Bug 271863 - Support exporting the current profile to a zip file. r=darktrojan
- Add an Export menu item to the main menu and app menu - Add an exportDialog.xhtml Depends on D119024. Differential Revision: https://phabricator.services.mozilla.com/D119168
This commit is contained in:
Родитель
03872db0e6
Коммит
5abaf55a6c
|
@ -1,4 +1,4 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
/* -*- Mode: JS; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
@ -518,6 +518,14 @@ function toImport() {
|
|||
);
|
||||
}
|
||||
|
||||
function toExport() {
|
||||
window.openDialog(
|
||||
"chrome://messenger/content/exportDialog.xhtml",
|
||||
"exportDialog",
|
||||
"chrome, modal, titlebar, centerscreen"
|
||||
);
|
||||
}
|
||||
|
||||
function toSanitize() {
|
||||
let sanitizerScope = {};
|
||||
Services.scriptloader.loadSubScript(
|
||||
|
|
|
@ -1094,6 +1094,9 @@
|
|||
<menuitem id="menu_import" label="&importCmd.label;"
|
||||
accesskey="&importCmd.accesskey;"
|
||||
oncommand="toImport();"/>
|
||||
<menuitem id="menu_export" label="&exportCmd.label;"
|
||||
accesskey="&exportCmd.accesskey;"
|
||||
oncommand="toExport();"/>
|
||||
#ifdef MOZ_OPENPGP
|
||||
<menuitem id="manageKeysOpenPGP" class="openpgp-item"
|
||||
data-l10n-id="openpgp-manage-keys-openpgp-cmd"
|
||||
|
|
|
@ -1669,6 +1669,10 @@
|
|||
class="subviewbutton subviewbutton-iconic"
|
||||
label="&importCmd.label;"
|
||||
oncommand="toImport();"/>
|
||||
<toolbarbutton id="appmenu_export"
|
||||
class="subviewbutton subviewbutton-iconic"
|
||||
label="&exportCmd.label;"
|
||||
oncommand="toExport();"/>
|
||||
#ifdef MOZ_OPENPGP
|
||||
<toolbarbutton id="appmenu_manageKeysOpenPGP"
|
||||
class="subviewbutton subviewbutton-iconic openpgp-item"
|
||||
|
|
|
@ -494,6 +494,8 @@
|
|||
<!ENTITY deleteJunk.accesskey "D">
|
||||
<!ENTITY importCmd.label "Import…">
|
||||
<!ENTITY importCmd.accesskey "m">
|
||||
<!ENTITY exportCmd.label "Export…">
|
||||
<!ENTITY exportCmd.accesskey "x">
|
||||
<!ENTITY clearRecentHistory.label "Clear Recent History…">
|
||||
<!ENTITY clearRecentHistory.accesskey "H">
|
||||
<!ENTITY accountManagerCmd2.label "Account Settings">
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
export-dialog-brand-name = { -brand-product-name }
|
||||
|
||||
export-dialog-window =
|
||||
.title = Export
|
||||
|
||||
export-dialog =
|
||||
.buttonlabelaccept = Next
|
||||
|
||||
export-dialog-button-finish = Finish
|
||||
|
||||
export-dialog-file-picker = Export to a zip file
|
||||
|
||||
export-dialog-desc1 = Export mail accounts, mail messages, address books, preferences to a zip file.
|
||||
|
||||
export-dialog-desc2 = When needed, you can import the zip file to restore your profile.
|
||||
|
||||
export-dialog-exporting = Exporting…
|
||||
|
||||
export-dialog-exported = Exported!
|
|
@ -0,0 +1,145 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
var { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
var nsFile = Components.Constructor(
|
||||
"@mozilla.org/file/local;1",
|
||||
"nsIFile",
|
||||
"initWithPath"
|
||||
);
|
||||
|
||||
// No need to backup those paths, they are not used when importing.
|
||||
const IGNORE_PATHS = [
|
||||
"cache2",
|
||||
"chrome_debugger_profile",
|
||||
"crashes",
|
||||
"datareporting",
|
||||
"extensions",
|
||||
"extension-store",
|
||||
"logs",
|
||||
"lock",
|
||||
"minidumps",
|
||||
"saved-telemetry-pings",
|
||||
"security_state",
|
||||
"storage",
|
||||
"xulstore",
|
||||
];
|
||||
|
||||
var zipW;
|
||||
|
||||
document.addEventListener("dialogaccept", async event => {
|
||||
if (zipW) {
|
||||
// This will close the dialog.
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not close the dialog, but open a FilePicker to set the output location.
|
||||
event.preventDefault();
|
||||
|
||||
let [filePickerTitle, brandName] = await document.l10n.formatValues([
|
||||
"export-dialog-file-picker",
|
||||
"export-dialog-brand-name",
|
||||
]);
|
||||
let filePicker = Components.Constructor(
|
||||
"@mozilla.org/filepicker;1",
|
||||
"nsIFilePicker"
|
||||
)();
|
||||
filePicker.init(window, filePickerTitle, Ci.nsIFilePicker.modeSave);
|
||||
filePicker.defaultString = `${brandName}_profile_backup.zip`;
|
||||
filePicker.defaultExtension = "zip";
|
||||
filePicker.appendFilter("", "*.zip");
|
||||
filePicker.open(rv => {
|
||||
if (
|
||||
[Ci.nsIFilePicker.returnOK, Ci.nsIFilePicker.returnReplace].includes(
|
||||
rv
|
||||
) &&
|
||||
filePicker.file
|
||||
) {
|
||||
exportCurrentProfile(filePicker.file);
|
||||
} else {
|
||||
window.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Export the current profile to the specified target zip file.
|
||||
* @param {nsIFile} targetFile - A target zip file to write to.
|
||||
*/
|
||||
async function exportCurrentProfile(targetFile) {
|
||||
let [
|
||||
progressExporting,
|
||||
progressExported,
|
||||
buttonLabelFinish,
|
||||
] = await document.l10n.formatValues([
|
||||
"export-dialog-exporting",
|
||||
"export-dialog-exported",
|
||||
"export-dialog-button-finish",
|
||||
]);
|
||||
document.getElementById("progressBar").hidden = false;
|
||||
let progressStatus = document.getElementById("progressStatus");
|
||||
progressStatus.value = progressExporting;
|
||||
let buttonAccept = document.querySelector("dialog").getButton("accept");
|
||||
buttonAccept.disabled = true;
|
||||
document.querySelector("dialog").getButton("cancel").hidden = true;
|
||||
|
||||
zipW = Components.Constructor("@mozilla.org/zipwriter;1", "nsIZipWriter")();
|
||||
// MODE_WRONLY (0x02) and MODE_CREATE (0x08)
|
||||
zipW.open(targetFile, 0x02 | 0x08);
|
||||
let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
let rootPath = profileDir.parent.path;
|
||||
let zipEntryMap = new Map();
|
||||
await collectFilesToZip(zipEntryMap, rootPath, profileDir);
|
||||
|
||||
let progressElement = document.getElementById("progress");
|
||||
progressElement.max = zipEntryMap.size;
|
||||
let i = 0;
|
||||
for (let [path, file] of zipEntryMap) {
|
||||
zipW.addEntryFile(
|
||||
path,
|
||||
0, // no compression, bigger file but much faster
|
||||
file,
|
||||
false
|
||||
);
|
||||
if (++i % 10 === 0) {
|
||||
progressElement.value = i;
|
||||
await new Promise(resolve => setTimeout(resolve));
|
||||
}
|
||||
}
|
||||
progressElement.value = progressElement.max;
|
||||
zipW.close();
|
||||
|
||||
progressStatus.value = progressExported;
|
||||
buttonAccept.disabled = false;
|
||||
buttonAccept.label = buttonLabelFinish;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively collect files to be zipped, save the entries into zipEntryMap.
|
||||
* @param {Map<string, nsIFile>} zipEntryMap - Collection of files to be zipped.
|
||||
* @param {string} rootPath - The rootPath to zip from.
|
||||
* @param {nsIFile} folder - The folder to search for files to zip.
|
||||
*/
|
||||
async function collectFilesToZip(zipEntryMap, rootPath, folder) {
|
||||
let entries = await IOUtils.getChildren(folder.path);
|
||||
for (let entry of entries) {
|
||||
let file = nsFile(entry);
|
||||
if (file.isDirectory()) {
|
||||
await collectFilesToZip(zipEntryMap, rootPath, file);
|
||||
} else {
|
||||
// We don't want to include the rootPath part in the zip file.
|
||||
let path = entry.slice(rootPath.length + 1);
|
||||
// path now looks like this: profile-default/lock.
|
||||
let parts = path.split("/");
|
||||
if (IGNORE_PATHS.includes(parts[1])) {
|
||||
continue;
|
||||
}
|
||||
zipEntryMap.set(path, file);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://messenger/skin/messenger.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://messenger/skin/input-fields.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://messenger/skin/themeableDialog.css" type="text/css"?>
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
data-l10n-id="export-dialog-window"
|
||||
lightweightthemes="true"
|
||||
width="500">
|
||||
<dialog buttons="accept,cancel" style="min-height: 12em"
|
||||
data-l10n-id="export-dialog"
|
||||
data-l10n-attrs="buttonlabelaccept">
|
||||
<html:link rel="localization" href="branding/brand.ftl" />
|
||||
<html:link rel="localization" href="messenger/exportDialog.ftl"/>
|
||||
<script src="chrome://messenger/content/exportDialog.js"/>
|
||||
|
||||
<description data-l10n-id="export-dialog-desc1"></description>
|
||||
<description data-l10n-id="export-dialog-desc2"></description>
|
||||
|
||||
<separator/>
|
||||
<vbox id="progressBar" hidden="true">
|
||||
<html:progress id="progress" value="0" max="100" style="flex:1;"/>
|
||||
<label id="progressStatus" crop="center"/>
|
||||
</vbox>
|
||||
|
||||
</dialog>
|
||||
</window>
|
|
@ -105,6 +105,8 @@ messenger.jar:
|
|||
content/messenger/messengercompose/askSendFormat.xhtml (compose/content/askSendFormat.xhtml)
|
||||
content/messenger/messengercompose/sendProgress.xhtml (compose/content/sendProgress.xhtml)
|
||||
content/messenger/messengercompose/sendProgress.js (compose/content/sendProgress.js)
|
||||
content/messenger/exportDialog.js (export/content/exportDialog.js)
|
||||
content/messenger/exportDialog.xhtml (export/content/exportDialog.xhtml)
|
||||
content/messenger/importDialog.js (import/content/importDialog.js)
|
||||
* content/messenger/importDialog.xhtml (import/content/importDialog.xhtml)
|
||||
* content/messenger/fieldMapImport.xhtml (import/content/fieldMapImport.xhtml)
|
||||
|
|
Загрузка…
Ссылка в новой задаче