Bug 594620 - Sync: Implement quota UI. r=mconnor a=blocking-beta7

This commit is contained in:
Philipp von Weitershausen 2010-09-15 15:55:04 +02:00
Родитель 50765c34ea
Коммит c2d5ad7e79
16 изменённых файлов: 547 добавлений и 1 удалений

Просмотреть файл

@ -22,6 +22,7 @@
# Chris Beard <cbeard@mozilla.com>
# Dan Mosedale <dmose@mozilla.org>
# Paul OShannessy <paul@oshannessy.com>
# Philipp von Weitershausen <philipp@weitershausen.de>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
@ -56,6 +57,7 @@ let gSyncUI = {
"weave:service:sync:finish",
"weave:service:sync:error",
"weave:service:sync:delayed",
"weave:service:quota:remaining",
"weave:service:setup-complete",
"weave:service:login:start",
"weave:service:login:finish",
@ -218,6 +220,21 @@ let gSyncUI = {
this.updateUI();
},
onQuotaNotice: function onQuotaNotice(subject, data) {
let title = this._stringBundle.GetStringFromName("warning.sync.quota.label");
let description = this._stringBundle.GetStringFromName("warning.sync.quota.description");
let buttons = [];
buttons.push(new Weave.NotificationButton(
this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.label"),
this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.accesskey"),
function() { gSyncUI.openQuotaDialog(); return true; }
));
let notification = new Weave.Notification(
title, description, null, Weave.Notifications.PRIORITY_WARNING, buttons);
Weave.Notifications.replaceTitle(notification);
},
onNotificationAdded: function SUI_onNotificationAdded() {
if (!gBrowser)
return;
@ -308,6 +325,16 @@ let gSyncUI = {
}
},
openQuotaDialog: function SUI_openQuotaDialog() {
let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
if (win)
win.focus();
else
Services.ww.activeWindow.openDialog(
"chrome://browser/content/syncQuota.xul", "",
"centerscreen,chrome,dialog,modal");
},
openPrefs: function SUI_openPrefs() {
openPreferences("paneSync");
},
@ -352,7 +379,18 @@ let gSyncUI = {
let priority = Weave.Notifications.PRIORITY_WARNING;
let buttons = [];
if (!Weave.Status.enforceBackoff) {
if (Weave.Status.sync == Weave.OVER_QUOTA) {
description = this._stringBundle.GetStringFromName(
"error.sync.quota.description");
buttons.push(new Weave.NotificationButton(
this._stringBundle.GetStringFromName(
"error.sync.viewQuotaButton.label"),
this._stringBundle.GetStringFromName(
"error.sync.viewQuotaButton.accesskey"),
function() { gSyncUI.openQuotaDialog(); return true; } )
);
}
else if (!Weave.Status.enforceBackoff) {
priority = Weave.Notifications.PRIORITY_INFO;
buttons.push(new Weave.NotificationButton(
this._stringBundle.GetStringFromName("error.sync.tryAgainButton.label"),
@ -394,6 +432,9 @@ let gSyncUI = {
case "weave:service:sync:delayed":
this.onSyncDelay();
break;
case "weave:service:quota:remaining":
this.onQuotaNotice();
break;
case "weave:service:setup-complete":
this.onLoginFinish();
break;

Просмотреть файл

@ -0,0 +1,282 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Firefox Sync.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Philipp von Weitershausen <philipp@weitershausen.de>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
const Cu = Components.utils;
Cu.import("resource://services-sync/main.js");
Cu.import("resource://gre/modules/DownloadUtils.jsm");
let gSyncQuota = {
init: function init() {
this.bundle = document.getElementById("quotaStrings");
let caption = document.getElementById("treeCaption");
caption.firstChild.nodeValue = this.bundle.getString("quota.treeCaption.label");
gUsageTreeView.init();
this.tree = document.getElementById("usageTree");
this.tree.view = gUsageTreeView;
this.loadData();
},
loadData: function loadData() {
window.setTimeout(function() {
let usage = Weave.Service.getCollectionUsage();
gUsageTreeView.displayUsageData(usage);
}, 0);
let usageLabel = document.getElementById("usageLabel");
let bundle = this.bundle;
window.setTimeout(function() {
let quota = Weave.Service.getQuota();
if (!quota) {
usageLabel.value = bundle.getString("quota.usageError.label");
return;
}
let used = gSyncQuota.convertKB(quota[0]);
if (!quota[1]) {
// No quota on the server.
usageLabel.value = bundle.getFormattedString(
"quota.usageNoQuota.label", used);
return;
}
let percent = Math.round(100 * quota[0] / quota[1]);
let total = gSyncQuota.convertKB(quota[1]);
usageLabel.value = bundle.getFormattedString(
"quota.usagePercentage.label", [percent].concat(used).concat(total));
}, 0);
},
onAccept: function onAccept() {
let engines = gUsageTreeView.getEnginesToDisable();
for each (let engine in engines) {
Weave.Engines.get(engine).enabled = false;
}
if (engines.length) {
// The 'Weave' object will disappear once the window closes.
let Service = Weave.Service;
Weave.Utils.delay(function() Service.sync(), 0);
}
return true;
},
convertKB: function convertKB(value) {
return DownloadUtils.convertByteUnits(value * 1024);
}
};
let gUsageTreeView = {
_ignored: {keys: true,
meta: true,
clients: true},
/*
* Internal data structures underlaying the tree.
*/
_collections: [],
_byname: {},
init: function init() {
let retrievingLabel = gSyncQuota.bundle.getString("quota.retrieving.label");
for each (let engine in Weave.Engines.getEnabled()) {
if (this._ignored[engine.name])
continue;
// Some engines use the same pref, which means they can only be turned on
// and off together. We need to combine them here as well.
let existing = this._byname[engine.prefName];
if (existing) {
existing.engines.push(engine.name);
continue;
}
let obj = {name: engine.prefName,
title: this._collectionTitle(engine),
engines: [engine.name],
enabled: true,
sizeLabel: retrievingLabel};
this._collections.push(obj);
this._byname[engine.prefName] = obj;
}
},
_collectionTitle: function _collectionTitle(engine) {
try {
return gSyncQuota.bundle.getString(
"collection." + engine.prefName + ".label");
} catch (ex) {
return engine.Name;
}
},
/*
* Process the quota information as returned by info/collection_usage.
*/
displayUsageData: function displayUsageData(data) {
data = data || {};
for each (let coll in this._collections) {
coll.size = 0;
for each (let engineName in coll.engines)
coll.size += data[engineName] || 0;
let sizeLabel = "";
if (coll.size)
sizeLabel = gSyncQuota.bundle.getFormattedString(
"quota.sizeValueUnit.label", gSyncQuota.convertKB(coll.size));
coll.sizeLabel = sizeLabel;
}
let sizeColumn = this.treeBox.columns.getNamedColumn("size");
this.treeBox.invalidateColumn(sizeColumn);
},
/*
* Handle click events on the tree.
*/
onTreeClick: function onTreeClick(event) {
if (event.button == 2)
return;
let row = {}, col = {};
this.treeBox.getCellAt(event.clientX, event.clientY, row, col, {});
if (col.value && col.value.id == "enabled")
this.toggle(row.value);
},
/*
* Toggle enabled state of an engine.
*/
toggle: function toggle(row) {
// Update the tree
let collection = this._collections[row];
collection.enabled = !collection.enabled;
this.treeBox.invalidateRow(row);
// Display which ones will be removed
let freeup = 0;
let toremove = [];
for each (collection in this._collections) {
if (collection.enabled)
continue;
toremove.push(collection.name);
freeup += collection.size;
}
let caption = document.getElementById("treeCaption");
if (!toremove.length) {
caption.className = "";
caption.firstChild.nodeValue = gSyncQuota.bundle.getString(
"quota.treeCaption.label");
return;
}
toremove = [this._byname[coll].title for each (coll in toremove)];
toremove = toremove.join(gSyncQuota.bundle.getString("quota.list.separator"));
caption.firstChild.nodeValue = gSyncQuota.bundle.getFormattedString(
"quota.removal.label", [toremove]);
if (freeup)
caption.firstChild.nodeValue += gSyncQuota.bundle.getFormattedString(
"quota.freeup.label", gSyncQuota.convertKB(freeup));
caption.className = "captionWarning";
},
/*
* Return a list of engines (or rather their pref names) that should be
* disabled.
*/
getEnginesToDisable: function getEnginesToDisable() {
return [coll.name for each (coll in this._collections) if (!coll.enabled)];
},
// nsITreeView
get rowCount() {
return this._collections.length;
},
getRowProperties: function(index, properties) {},
getCellProperties: function(row, col, properties) {},
getColumnProperties: function(col, properties) {},
isContainer: function(index) { return false; },
isContainerOpen: function(index) { return false; },
isContainerEmpty: function(index) { return false; },
isSeparator: function(index) { return false; },
isSorted: function() { return false; },
canDrop: function(index, orientation, dataTransfer) { return false; },
drop: function(row, orientation, dataTransfer) {},
getParentIndex: function(rowIndex) {},
hasNextSibling: function(rowIndex, afterIndex) { return false; },
getLevel: function(index) { return 0; },
getImageSrc: function(row, col) {},
getCellValue: function(row, col) {
return this._collections[row].enabled;
},
getCellText: function getCellText(row, col) {
let collection = this._collections[row];
switch (col.id) {
case "collection":
return collection.title;
case "size":
return collection.sizeLabel;
default:
return "";
}
},
setTree: function setTree(tree) {
this.treeBox = tree;
},
toggleOpenState: function(index) {},
cycleHeader: function(col) {},
selectionChanged: function() {},
cycleCell: function(row, col) {},
isEditable: function(row, col) { return false; },
isSelectable: function (row, col) { return false; },
setCellValue: function(row, col, value) {},
setCellText: function(row, col, value) {},
performAction: function(action) {},
performActionOnRow: function(action, row) {},
performActionOnCell: function(action, row, col) {}
};

Просмотреть файл

@ -0,0 +1,97 @@
<?xml version="1.0"?>
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (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.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Firefox Sync.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Philipp von Weitershausen <philipp@weitershausen.de>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/syncQuota.css"?>
<!DOCTYPE dialog [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
<!ENTITY % syncQuotaDTD SYSTEM "chrome://browser/locale/syncQuota.dtd">
%brandDTD;
%syncBrandDTD;
%syncQuotaDTD;
]>
<dialog id="quotaDialog"
windowtype="Sync:ViewQuota"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
onload="gSyncQuota.init()"
buttons="accept,cancel"
title="&quota.dialogTitle.label;"
ondialogcancel="return true;"
ondialogaccept="return gSyncQuota.onAccept();">
<script type="application/javascript"
src="chrome://browser/content/syncQuota.js"/>
<stringbundleset id="stringbundleset">
<stringbundle id="quotaStrings"
src="chrome://browser/locale/syncQuota.properties"/>
</stringbundleset>
<vbox flex="1">
<label id="usageLabel"
value="&quota.retrievingInfo.label;"/>
<separator/>
<tree id="usageTree"
seltype="single"
hidecolumnpicker="true"
onclick="gUsageTreeView.onTreeClick(event);"
flex="1">
<treecols>
<treecol id="enabled"
type="checkbox"
fixed="true"/>
<splitter class="tree-splitter"/>
<treecol id="collection"
label="Type"
flex="1"/>
<splitter class="tree-splitter"/>
<treecol id="size"
label="Size"
flex="1"/>
</treecols>
<treechildren flex="1"/>
</tree>
<separator/>
<description id="treeCaption"> </description>
</vbox>
</dialog>

Просмотреть файл

@ -67,6 +67,8 @@ browser.jar:
content/browser/syncGenericChange.js (content/syncGenericChange.js)
* content/browser/syncKey.xhtml (content/syncKey.xhtml)
* content/browser/syncNotification.xml (content/syncNotification.xml)
* content/browser/syncQuota.xul (content/syncQuota.xul)
content/browser/syncQuota.js (content/syncQuota.js)
content/browser/syncUtils.js (content/syncUtils.js)
#endif
# XXX: We should exclude this one as well (bug 71895)

Просмотреть файл

@ -206,6 +206,15 @@ let gSyncPane = {
}
},
openQuotaDialog: function () {
let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
if (win)
win.focus();
else
window.openDialog("chrome://browser/content/syncQuota.xul", "",
"centerscreen,chrome,dialog,modal");
},
resetSync: function () {
this.openSetup(true);
}

Просмотреть файл

@ -117,6 +117,9 @@
<row id="manageAccountControls" hidden="true">
<spacer/>
<vbox class="indent">
<label class="text-link"
onclick="gSyncPane.openQuotaDialog(); return false;"
value="&viewQuota.label;"/>
<label class="text-link"
onclick="gSyncUtils.changePassword(); return false;"
value="&changePassword.label;"/>

Просмотреть файл

@ -14,6 +14,7 @@
<!-- Manage Account -->
<!ENTITY manageAccount.label "Manage Account">
<!ENTITY manageAccount.accesskey "A">
<!ENTITY viewQuota.label "View Quota">
<!ENTITY changePassword.label "Change Password">
<!ENTITY mySyncKey.label "My Sync Key">
<!ENTITY resetSync.label "Reset Sync">

Просмотреть файл

@ -0,0 +1,2 @@
<!ENTITY quota.dialogTitle.label "Server Quota">
<!ENTITY quota.retrievingInfo.label "Retrieving quota information…">

Просмотреть файл

@ -0,0 +1,37 @@
collection.bookmarks.label = Bookmarks
collection.history.label = History
collection.passwords.label = Passwords
collection.prefs.label = Preferences
collection.tabs.label = Tabs
# LOCALIZATION NOTE (quota.usageNoQuota.label): %1$S and %2$S are numeric value
# and unit (as defined in the download manager) of the amount of space occupied
# on the server
quota.usageNoQuota.label = You are currently using %1$S %2$S.
# LOCALIZATION NOTE (quota.usagePercentage.label):
# %1$S is the percentage of space used,
# %2$S and %3$S numeric value and unit (as defined in the download manager)
# of the amount of space used,
# %3$S and %4$S numeric value and unit (as defined in the download manager)
# of the total space available.
quota.usagePercentage.label = You are using %1$S%% (%2$S %3$S) of your allowed %4$S %5$S.
quota.usageError.label = Could not retrieve quota information.
quota.retrieving.label = Retrieving…
# LOCALIZATION NOTE (quota.sizeValueUnit.label): %1$S is the amount of space
# occupied by the engine, %2$K the corresponding unit (e.g. kB) as defined in
# the download manager.
quota.sizeValueUnit.label = %1$S %2$S
quota.remove.label = Remove
quota.treeCaption.label = Uncheck items to stop syncing them and free up space on the server.
# LOCALIZATION NOTE (quota.removal.label): %S is a list of engines that will be
# disabled and whose data will be removed once the user confirms.
quota.removal.label = Firefox Sync will remove the following data: %S.
# LOCALIZATION NOTE (quota.list.separator): This is the separator string used
# for the list of engines (incl. spaces where appropriate)
quota.list.separator = ,\u0020
# LOCALIZATION NOTE (quota.freeup.label): %1$S and %2$S are numeric value
# and unit (as defined in the download manager) of the amount of space freed
# up by disabling the unchecked engines. If displayed this string is
# concatenated directly to quota.removal.label and may need to start off with
# whitespace.
quota.freeup.label = \u0020This will free up %1$S %2$S.

Просмотреть файл

@ -75,6 +75,8 @@
locale/browser/syncSetup.dtd (%chrome/browser/syncSetup.dtd)
locale/browser/syncSetup.properties (%chrome/browser/syncSetup.properties)
locale/browser/syncGenericChange.properties (%chrome/browser/syncGenericChange.properties)
locale/browser/syncQuota.dtd (%chrome/browser/syncQuota.dtd)
locale/browser/syncQuota.properties (%chrome/browser/syncQuota.properties)
#endif
% locale browser-region @AB_CD@ %locale/browser-region/
locale/browser-region/region.properties (%chrome/browser-region/region.properties)

Просмотреть файл

@ -95,4 +95,5 @@ browser.jar:
skin/classic/browser/sync-usedNever.png
skin/classic/browser/syncSetup.css
skin/classic/browser/syncCommon.css
skin/classic/browser/syncQuota.css
#endif

Просмотреть файл

@ -0,0 +1,22 @@
#quotaDialog {
width: 33em;
height: 25em;
}
treechildren::-moz-tree-checkbox {
list-style-image: none;
}
treechildren::-moz-tree-checkbox(checked) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
}
treechildren::-moz-tree-checkbox(disabled) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif");
}
#treeCaption {
height: 4em;
}
.captionWarning {
font-weight: bold;
}

Просмотреть файл

@ -135,4 +135,5 @@ browser.jar:
skin/classic/browser/sync-usedNever.png
skin/classic/browser/syncSetup.css
skin/classic/browser/syncCommon.css
skin/classic/browser/syncQuota.css
#endif

Просмотреть файл

@ -0,0 +1,22 @@
#quotaDialog {
width: 33em;
height: 25em;
}
treechildren::-moz-tree-checkbox {
list-style-image: none;
}
treechildren::-moz-tree-checkbox(checked) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
}
treechildren::-moz-tree-checkbox(disabled) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif");
}
#treeCaption {
height: 4em;
}
.captionWarning {
font-weight: bold;
}

Просмотреть файл

@ -114,6 +114,7 @@ browser.jar:
skin/classic/browser/sync-usedNever.png
skin/classic/browser/syncSetup.css
skin/classic/browser/syncCommon.css
skin/classic/browser/syncQuota.css
#endif
#ifdef XP_WIN
@ -231,5 +232,6 @@ browser.jar:
skin/classic/aero/browser/sync-usedNever.png
skin/classic/aero/browser/syncSetup.css
skin/classic/aero/browser/syncCommon.css
skin/classic/aero/browser/syncQuota.css
#endif
#endif

Просмотреть файл

@ -0,0 +1,22 @@
#quotaDialog {
width: 33em;
height: 25em;
}
treechildren::-moz-tree-checkbox {
list-style-image: none;
}
treechildren::-moz-tree-checkbox(checked) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
}
treechildren::-moz-tree-checkbox(disabled) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif");
}
#treeCaption {
height: 4em;
}
.captionWarning {
font-weight: bold;
}