Einstellungen -> Konto und Synchronisation -> ganz unten...
Danke, das hatte ich noch nicht auf dem Schirm, muss ich gleich mal testen. Hast du das schon damit gemacht?
Einstellungen -> Konto und Synchronisation -> ganz unten...
Danke, das hatte ich noch nicht auf dem Schirm, muss ich gleich mal testen. Hast du das schon damit gemacht?
das hatte ich noch nicht auf dem Schirm
Im Nightly gibt es auch das noch:
chrome://browser/content/backup/debug.html
Ungetestet von mir.![]()
Im Nightly gibt es auch das noch:
Habe ich mir angesehen, ist nicht so mein Ding. Schuster bleib bei deinen Leisten, so heißt es wohl.![]()
Hast du das schon damit gemacht?
Sicherung ja - Rücksicherung noch nicht getetstet -> zu viele andere Baustellen... ![]()
![]()
Sicherung ja - Rücksicherung noch nicht getetstet
Ja, gut, dann werde ich mal probieren.![]()
dann werde ich mal probieren
Erstell dir vorher aber zur Sicherheit ein Backup![]()
Erstell dir vorher aber zur Sicherheit ein Backup
Das hätte ich glatt vergessen, danke.![]()
![]()
In dem Skript von hier #375 wird beim Entpacken des Archivs der gesamte Inhalt des Profilordners aufgeführt. Das habe ich in dem folgenden Skript dahingehend geändert, dass beim Entpacken der vollständige Profilordner mit seinem richtigen Namen erstellt wird. Ist sehr viel einfacher, wenn ein Archiv mal für die Herstellung des Profils genutzt werden muss.
// ==UserScript==
// @name ProfileBackupRestore@Fu_combo_zipRoot_withlog.uc.js
// @description Navbar: Backup(Ordner1 + ZIP2) + Restore(Backup1->Profil) + Log in Ordner1
// @version 2026.07-combo-zip-root-log
// ==/UserScript==
(function () {
"use strict";
if (location.href !== "chrome://browser/content/browser.xhtml") return;
if (typeof CustomizableUI === "undefined") return;
if (!window.gBrowser) return;
const PROFILES = [
{
label: "Reserve 3",
profilePath: "C:\\Users\\Old Man\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\Reserve 3",
profileName: "Reserve 3",
backup1Path: "G:\\BackupRestore Firefox\\Reserve 3",
archiveRootPath: "E:\\BackupRestore Firefox\\Reserve 3",
iconPath: "file:///C:/FoxIcons2/Finale.png"
},
{
label: "Portable.Firefox.Updater.3_Stable ",
profilePath: "G:\\Portable.Firefox.Updater.3\\Firefox Stable x64\\profile",
profileName: "profile",
backup1Path: "G:\\BackupRestore Firefox\\Portable.Firefox.Updater.3_Stable",
archiveRootPath: "E:\\BackupRestore Firefox\\Portable.Firefox.Updater.3_Stable",
iconPath: "file:///C:/FoxIcons2/Finale.png"
},
{
label: "Portable.Firefox.Updater.3_Nightly ",
profilePath: "G:\\Portable.Firefox.Updater.3\\Firefox Nightly x64\\profile",
profileName: "profile",
backup1Path: "G:\\BackupRestore Firefox\\Portable.Firefox.Updater.3_Nightly",
archiveRootPath: "E:\\BackupRestore Firefox\\Portable.Firefox.Updater.3_Nightly",
iconPath: "file:///C:/FoxIcons2/Nightly.png"
}
];
const BTN_ID = "profilebackup_restore_combo_ziproot_withlog";
const POPUP_ID = BTN_ID + "_popup";
const btnIconPath = "file:///C:/FoxIcons2/backup.png";
// =========================
// Logging helpers
// =========================
function pad2(n) {
return String(n).padStart(2, "0");
}
function timeHHMM(d) {
return "[" + pad2(d.getHours()) + ":" + pad2(d.getMinutes()) + "]";
}
function formatHeaderDateGermanLong(d) {
const weekdays = ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"];
const months = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"];
const wd = weekdays[d.getDay()];
const mn = months[d.getMonth()];
return "am " + wd + ", " +
pad2(d.getDate()) + ". " + mn + " " + d.getFullYear() + ", " +
pad2(d.getHours()) + ":" + pad2(d.getMinutes()) + ":" + pad2(d.getSeconds()) + " Uhr";
}
function getLogFile(profile) {
const f = new FileUtils.File(profile.backup1Path);
f.append("ProfileBackupRestore.log");
return f;
}
function ensureParentForFile(file) {
const parent = file.parent;
if (!parent || !parent.exists()) {
// backup1Path ist bei dir der Parent; falls nicht existiert, versuche zu erstellen
// (best effort)
try { parent.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755); } catch (_) {}
}
}
function appendTextToFile(file, text) {
try {
ensureParentForFile(file);
if (!file.exists()) {
file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o644);
}
const fos = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
fos.init(file, 0x02 | 0x08 | 0x10, 0o644, 0); // write | create | append
const conv = Cc["@mozilla.org/intl/converter-output-stream;1"].createInstance(Ci.nsIConverterOutputStream);
conv.init(fos, "UTF-8", 0, 0);
conv.writeString(text);
conv.close();
try { fos.close(); } catch (_) {}
} catch (e) {
console.error("Log Fehler:", e);
}
}
function logRestore(profile, destProfilePath) {
const dStart = new Date();
const header = "Profil in alten Zustand versetzt " + formatHeaderDateGermanLong(dStart) + "\n\n";
const lines =
timeHHMM(dStart) + " Restore gestartet (Firefox-Erkennung deaktiviert).\n" +
timeHHMM(dStart) + " Lösche vorhandenen Profilordner: " + destProfilePath + "\n" +
timeHHMM(dStart) + " Kopiere Sicherung nach: " + destProfilePath + "\n" +
timeHHMM(new Date()) + " Restore erfolgreich abgeschlossen.\n";
appendTextToFile(getLogFile(profile), header + "\n" + lines + "\n");
}
function logBackup(profile, backupTargetPath, zipPath) {
const dStart = new Date();
const header = "Profil gesichert " + formatHeaderDateGermanLong(dStart) + "\n\n";
const lines =
timeHHMM(dStart) + " Backup gestartet (Firefox-Erkennung deaktiviert).\n" +
timeHHMM(dStart) + " Lösche vorhandenen Profilordner: " + backupTargetPath + "\n" +
timeHHMM(dStart) + " Kopiere Profil nach: " + backupTargetPath + "\n" +
timeHHMM(new Date()) + " Erzeuge Archiv ZIP: " + zipPath + "\n" +
timeHHMM(new Date()) + " Backup erfolgreich abgeschlossen.\n";
appendTextToFile(getLogFile(profile), header + "\n" + lines + "\n");
}
// =========================
// Alert
// =========================
function showAlert(text) {
try {
let w = null;
try { w = Services.wm.getMostRecentWindow("navigator:browser"); } catch (_) {}
Services.prompt.alert(w || null, "Profilsicherung", text);
} catch (e) {
console.error("Alert Fehler:", e);
}
}
// =========================
// Date/Zähler ZIP2
// =========================
function pad2Str(n) {
const x = parseInt(n, 10);
if (isNaN(x)) return "00";
return pad2(x);
}
function getDateStrYYYYMMDD(d) {
return d.getFullYear() + "-" + pad2(d.getMonth() + 1) + "-" + pad2(d.getDate());
}
function escapeRegExp(s) {
return String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function getNextDailyZipIndex(archiveRootFile, profileName, dateStr) {
let max = 0;
const re = new RegExp("^" + escapeRegExp(profileName) + "_" + escapeRegExp(dateStr) + "_(\\d{2})\\.zip$");
let entries = archiveRootFile.directoryEntries;
while (entries.hasMoreElements()) {
const entry = entries.getNext().QueryInterface(Ci.nsIFile);
if (!entry.isFile()) continue;
const m = re.exec(entry.leafName);
if (m) {
const n = parseInt(m[1], 10);
if (!isNaN(n) && n > max) max = n;
}
}
return max + 1; // => startet bei 01
}
// =========================
// Skip-Dateien (WAL/SHM/Locks)
// =========================
function shouldSkipEntryByName(name) {
if (name === "parent.lock") return true;
if (name === "lock") return true;
if (name === "recovery.jsonlz4") return true;
if (name.endsWith(".sqlite-wal")) return true;
if (name.endsWith(".sqlite-shm")) return true;
return false;
}
// =========================
// Copy helpers
// =========================
function copyDirectoryContents(srcDir, destDir) {
let entries = srcDir.directoryEntries;
while (entries.hasMoreElements()) {
const entry = entries.getNext().QueryInterface(Ci.nsIFile);
if (shouldSkipEntryByName(entry.leafName)) continue;
const destEntry = destDir.clone();
destEntry.append(entry.leafName);
try {
if (entry.isDirectory()) {
if (!destEntry.exists()) destEntry.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
copyDirectoryContents(entry, destEntry);
} else {
if (destEntry.exists()) destEntry.remove(false);
entry.copyTo(destDir, entry.leafName);
}
} catch (e) {
console.warn("⚠️ Kopie fehlgeschlagen:", entry.path, e);
}
}
}
function clearDirectoryContents(dir) {
let entries = dir.directoryEntries;
while (entries.hasMoreElements()) {
const entry = entries.getNext().QueryInterface(Ci.nsIFile);
try { entry.remove(true); } catch (e) {
console.warn("⚠️ Clear fehlgeschlagen:", entry.path, e);
}
}
}
// =========================
// ZIP erstellen: Inhalt + Root = profile.profileName
// =========================
function zipDirectoryWithRoot(sourceDir, zipFile, rootNameInZip) {
const baseDirPath = sourceDir.path;
if (zipFile.exists()) zipFile.remove(true);
const zipWriter = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter);
const PR_WRONLY = 0x02;
const PR_CREATE_FILE = 0x08;
const PR_TRUNCATE = 0x20;
zipWriter.open(zipFile, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE);
function walk(dir) {
let entries = dir.directoryEntries;
while (entries.hasMoreElements()) {
const entry = entries.getNext().QueryInterface(Ci.nsIFile);
if (entry.path === zipFile.path) continue;
if (shouldSkipEntryByName(entry.leafName)) continue;
if (entry.isDirectory()) {
walk(entry);
continue;
}
// relPath relativ zum sourceDir, ohne führenden Slash
let relPath = entry.path.replace(baseDirPath, "");
if (!relPath) return;
if (relPath[0] === "\\" || relPath[0] === "/") relPath = relPath.substring(1);
const saveInZipAs = (rootNameInZip + "/" + relPath).replace(/\\/g, "/");
try {
zipWriter.addEntryFile(
saveInZipAs,
Ci.nsIZipWriter.COMPRESSION_FASTEST,
entry,
false
);
} catch (e) {
console.warn("⚠️ ZIP Eintrag fehlgeschlagen:", saveInZipAs, e);
}
}
}
walk(sourceDir);
zipWriter.close();
}
// =========================
// Backup (Ordner1 + ZIP2)
// =========================
function runBackup(profile) {
let backupTarget = null;
let zipFile = null;
try {
const now = new Date();
const profileDir = new FileUtils.File(profile.profilePath);
if (!profileDir.exists()) throw new Error("Profilordner existiert nicht: " + profile.profilePath);
const backupRoot = new FileUtils.File(profile.backup1Path);
if (!backupRoot.exists()) backupRoot.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
const archiveRoot = new FileUtils.File(profile.archiveRootPath);
if (!archiveRoot.exists()) archiveRoot.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
// BACKUP 1 Ziel: backupRoot/<profileName>
backupTarget = backupRoot.clone();
backupTarget.append(profile.profileName);
if (backupTarget.exists()) backupTarget.remove(true);
backupTarget.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
copyDirectoryContents(profileDir, backupTarget);
// BACKUP 2 ZIP: archiveRoot/<profileName>_YYYY-MM-DD_XX.zip (XX ab 01)
const dateStr = getDateStrYYYYMMDD(now);
const nextIdx = getNextDailyZipIndex(archiveRoot, profile.profileName, dateStr);
const idxStr = pad2Str(nextIdx);
const zipName = profile.profileName + "_" + dateStr + "_" + idxStr + ".zip";
zipFile = archiveRoot.clone();
zipFile.append(zipName);
// ZIP-Inhalt soll als Root den profile.profileName Ordner haben.
// Wir zippen den Inhalt von backupTarget, Root in der ZIP = profile.profileName.
zipDirectoryWithRoot(backupTarget, zipFile, profile.profileName);
if (!zipFile.exists()) throw new Error("ZIP wurde nicht erstellt: " + zipFile.path);
// Log in Ordner 1
try { logBackup(profile, backupTarget.path, zipFile.path); } catch (_) {}
setTimeout(() => {
showAlert(
"✅ Backup abgeschlossen!\n\n" +
"👤 Profil: " + profile.label + "\n\n" +
"📁 Backup Ordner 1: " + backupTarget.path + "\n" +
"📦 Archiv ZIP: " + zipFile.path
);
}, 50);
} catch (e) {
console.error("Backup Fehler:", e);
// Best effort Cleanup ZIP, falls vorhanden
try { if (zipFile && zipFile.exists()) zipFile.remove(false); } catch (_) {}
setTimeout(() => showAlert("❌ Backup Fehler: " + e.message), 0);
}
}
// =========================
// Restore (Backup1 -> Profil überschreibt; Wrapper fix!)
// =========================
function runRestore(profile) {
let profileDir = null;
let backupRootDir = null;
let backupProfileDir = null;
try {
profileDir = new FileUtils.File(profile.profilePath);
if (!profileDir.exists()) throw new Error("Zielprofil existiert nicht: " + profile.profilePath);
backupRootDir = new FileUtils.File(profile.backup1Path);
if (!backupRootDir.exists()) throw new Error("Backup1 Ordner existiert nicht: " + profile.backup1Path);
// Wrapper fix:
// Backup1 enthält i.d.R. <profileName>/... -> daraus restaurieren:
backupProfileDir = backupRootDir.clone();
backupProfileDir.append(profile.profileName);
// Fallback falls Backup1 doch direkt Root ist
if (!backupProfileDir.exists()) backupProfileDir = backupRootDir;
const ok = Services.prompt.confirm(
null,
"Profilsicherung (Restore)",
"Restore jetzt starten für: " + profile.label + "\n\n" +
"Quelle: " + backupProfileDir.path + "\n" +
"Ziel: " + profileDir.path + "\n\n" +
"WICHTIG: Firefox bitte vorher vollständig beenden.\nFortfahren?"
);
if (!ok) return;
clearDirectoryContents(profileDir);
copyDirectoryContents(backupProfileDir, profileDir);
try { logRestore(profile, profileDir.path); } catch (_) {}
setTimeout(() => {
showAlert(
"✅ Restore abgeschlossen!\n\n" +
"👤 Profil: " + profile.label + "\n\n" +
"📁 Quelle: " + backupProfileDir.path + "\n" +
"📁 Ziel: " + profileDir.path + "\n\n" +
"Hinweis: Starte Firefox danach neu."
);
}, 50);
} catch (e) {
console.error("Restore Fehler:", e);
setTimeout(() => showAlert("❌ Restore Fehler: " + e.message), 0);
}
}
// =========================
// Menü
// =========================
function createMenu(doc) {
const menupopup = doc.createXULElement("menupopup");
menupopup.setAttribute("id", POPUP_ID);
PROFILES.forEach((profile) => {
const backupItem = doc.createXULElement("menuitem");
backupItem.setAttribute("label", "Backup: " + profile.label);
backupItem.setAttribute("class", "menuitem-iconic");
if (profile.iconPath) backupItem.setAttribute("image", profile.iconPath);
backupItem.addEventListener("command", () => {
const ok = Services.prompt.confirm(
null,
"Profilsicherung",
"Backup jetzt starten für: " + profile.label + " ?"
);
if (ok) runBackup(profile);
});
menupopup.appendChild(backupItem);
});
const sep = doc.createXULElement("menuseparator");
menupopup.appendChild(sep);
PROFILES.forEach((profile) => {
const restoreItem = doc.createXULElement("menuitem");
restoreItem.setAttribute("label", "Restore: " + profile.label);
restoreItem.setAttribute("class", "menuitem-iconic");
if (profile.iconPath) restoreItem.setAttribute("image", profile.iconPath);
restoreItem.addEventListener("command", () => runRestore(profile));
menupopup.appendChild(restoreItem);
});
return menupopup;
}
// =========================
// Button Patch
// =========================
function patchButton(btn) {
if (!btn) return false;
const oldPopup = btn.querySelector("#" + POPUP_ID);
if (oldPopup) oldPopup.remove();
btn.setAttribute("type", "menu");
btn.setAttribute("aria-haspopup", "true");
btn.setAttribute("tooltiptext", "Backup & Restore");
btn.setAttribute("image", btnIconPath);
btn.appendChild(createMenu(btn.ownerDocument));
btn.addEventListener("click", (ev) => {
if (ev.button !== 0) return;
try {
const p = btn.querySelector("#" + POPUP_ID);
if (p) p.openPopup(btn, "after_start", 0, 0, false, false);
} catch (_) {}
}, false);
return true;
}
// =========================
// Widget erstellen
// =========================
try {
if (CustomizableUI.getWidget && CustomizableUI.getWidget(BTN_ID)) {
try { CustomizableUI.destroyWidget(BTN_ID); } catch (_) {}
}
} catch (_) {}
try {
const el = document.getElementById(BTN_ID);
if (el) el.remove();
} catch (_) {}
CustomizableUI.createWidget({
id: BTN_ID,
defaultArea: CustomizableUI.AREA_NAVBAR,
label: "Profilsicherung",
tooltiptext: "Backup & Restore (ZIP Root = profileName) + Log",
onCreated: (button) => setTimeout(() => patchButton(button), 0)
});
// =========================
// CSS (dein Layout)
// =========================
const css = `
#${BTN_ID} .toolbarbutton-icon {
width: 31px !important;
height: 31px !important;
padding: 5px !important;
}
#${BTN_ID}_popup {
max-width: 250px !important;
min-width: 250px !important;
}
#${BTN_ID}_popup menuitem.menuitem-iconic img.menu-icon {
margin-left: -28px !important;
}
#${BTN_ID}_popup menuitem.menuitem-iconic label.menu-text {
margin-left: 6px !important;
}
`;
const sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
const ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
const uri = ios.newURI("data:text/css;charset=utf-8," + encodeURIComponent(css), null, null);
sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
})();
Alles anzeigen