FoxToPhone 1.2beta3. Improved detection of errors with authentication. Allow the management of several accounts

This commit is contained in:
amla70
2011-08-04 15:09:24 +00:00
parent 11ae8e6ba9
commit b497fd22d5
12 changed files with 294 additions and 38 deletions

View File

@@ -10,7 +10,7 @@
</stringbundleset>
<popupset id="mainPopupSet">
<menupopup id="sendtophoneContextMenu" onpopupshowing="sendtophone.initPopup();">
<menupopup id="sendtophoneContextMenu" onpopupshowing="if (event.target == this) sendtophone.initPopup();">
<menuitem label="&sendtophoneContextSendClipboard.label;"
accesskey="&sendtophoneContextSendClipboard.accesskey;"
oncommand="sendtophone.sendClipboard()" id="sendtophoneContextMenuSendClipboard"/>
@@ -27,6 +27,10 @@
<menuitem type="checkbox" id="telcheck" label="tel:" oncommand="sendtophone.onToggleOption(this)" option="tel"/>
</menupopup>
</menu>
<menu label="&sendtophonePhoneAccounts.label;">
<menupopup id="sendtophoneContextMenuAccountsPane" onpopupshowing="sendtophone.fillAccountsMenu(this)">
</menupopup>
</menu>
<menuitem label="&sendtophoneContextLogout.label;"
accesskey="&sendtophoneContextLogout.accesskey;"
oncommand="sendtophone.logout()" id="sendtophoneContextMenuLogout"/>

View File

@@ -46,10 +46,123 @@ let foxToPhonePreferences =
document.getElementById("hboxFileServerUrl").hidden = ( fileserverMenuList.value != 'Custom');
window.sizeToContent();
// Accounts
var accountsList = document.getElementById("accountsList");
// Clear all the items
while (accountsList.getRowCount() >0)
accountsList.removeItemAt(0);
this.prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService)
.getBranch("extensions.sendtophone.") ;
var accounts = this.prefs.getCharPref("accounts").split(";");
for (var i=0; i<accounts.length ; i++)
{
var account = accounts[i];
var title = this.prefs.getCharPref( account + ".title" );
accountsList.appendItem( title, account );
}
window.sizeToContent();
},
getString: function(name)
{
if (!this.strings)
{
this.strings = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService)
.createBundle("chrome://sendtophone/locale/overlay.properties");
}
return this.strings.GetStringFromName(name);
},
onAccountSelected: function() {
var accountsList = document.getElementById("accountsList"),
count = accountsList.selectedCount;
document.getElementById("btnRenameAccount").disabled = (count === 0);
document.getElementById("btnRemoveAccount").disabled = ((count === 0) || (accountsList.getRowCount()==1));
},
addAccount: function() {
var n=1,
accounts = this.prefs.getCharPref("accounts"),
accountsArray = accounts.split(";");
while (accountsArray.indexOf("account" + n)>=0)
n++;
var input = {value:"Phone " + n};
if (Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService)
.prompt( null, this.getString("SendToPhoneTitle"), this.getString("PhoneAccountPromptAdd"), input, null, {value: false}))
{
var account = "account" + n;
this.prefs.setCharPref( account + ".title", input.value);
this.prefs.setCharPref( "accounts", accounts + ";" + account );
document.getElementById("accountsList").appendItem( input.value, account );
}
},
renameAccount: function() {
var accountsList = document.getElementById("accountsList");
for (var i= accountsList.selectedItems.length-1; i>=0; i--)
{
var item = accountsList.selectedItems[i],
input = {value:item.label},
ok = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService)
.prompt( null, this.getString("SendToPhoneTitle"), this.getString("PhoneAccountPromptRename"), input, null, {value: false});
if (!ok)
return;
item.label = input.value;
this.prefs.setCharPref( item.value + ".title", input.value);
}
},
removeAccount: function() {
var accountsList = document.getElementById("accountsList"),
removedAccount = "",
accounts = [],
i;
for (i = accountsList.selectedItems.length-1; i>=0; i--)
{
var item = accountsList.selectedItems[i],
title = item.label;
if (!Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService)
.confirm( null, this.getString("SendToPhoneTitle"), this.getString("PhoneAccountPromptRemove") + "\r\n" + title))
return;
removedAccount = item.value;
accountsList.removeItemAt( accountsList.getIndexOfItem( item ) );
this.prefs.deleteBranch( removedAccount );
}
for(i=0; i<accountsList.itemCount; i++)
{
var item = accountsList.getItemAtIndex( i );
accounts.push( item.value );
}
this.prefs.setCharPref( "accounts", accounts.join(";"));
if (this.prefs.getCharPref("currentAccount")==removedAccount)
{
if (typeof sendtophoneCore == "undefined")
Components.utils.import("resource://sendtophone/sendtophone.js");
sendtophoneCore.setCurrentAccount( accounts[0] );
}
}
} ;
this.addEventListener("load", function () {foxToPhonePreferences.load(); }, false);

View File

@@ -15,7 +15,7 @@
<preference id="extensions.sendtophone.fileServerUrl" name="extensions.sendtophone.fileServerUrl" type="string"/>
</preferences>
<groupbox style="padding:1em;" label="ProtocolsGroup">
<groupbox style="padding:1em;">
<caption label="&sendtophoneProtocols.label;"/>
<checkbox preference="extensions.sendtophone.protocols.market"
@@ -30,7 +30,38 @@
label="tel:" />
</groupbox>
<groupbox style="padding:1em; min-height:10em;" label="FileServer">
<groupbox style="padding:1em;">
<caption label="&sendtophonePhoneAccounts.label;"/>
<description style="width:400px; height:2em;">
&sendtophonePhoneAccounts.description;
</description>
<hbox>
<listbox id="accountsList" flex="1" style="height: 8em; "
seltype="single"
onselect="foxToPhonePreferences.onAccountSelected();"
>
</listbox>
<vbox align="end">
<button id="btnAddAccount"
icon="add" label="&sendtophonePhoneAccounts.add;"
oncommand="foxToPhonePreferences.addAccount();"/>
<button id="btnRenameAccount" disabled="true"
label="&sendtophonePhoneAccounts.rename;"
oncommand="foxToPhonePreferences.renameAccount();"/>
<button id="btnRemoveAccount" disabled="true"
icon="remove" label="&sendtophonePhoneAccounts.remove;"
oncommand="foxToPhonePreferences.removeAccount();"/>
</vbox>
</hbox>
</groupbox>
<groupbox style="padding:1em; min-height:10em;">
<caption label="&sendtophoneFileTransfers.label;" />
<hbox align="center">
<label value="&sendtophoneFileServer.label;"

View File

@@ -233,6 +233,36 @@ var sendtophone = {
return (/^(https?|market|tel|sms(to)?|mailto|ftp):/i).test( uri );
},
fillAccountsMenu: function(menupopup)
{
var accounts = this.prefs.getCharPref("accounts").split(";"),
currentAccount = this.prefs.getCharPref('currentAccount');
while (menupopup.firstChild)
menupopup.removeChild(menupopup.firstChild);
for (var i=0; i<accounts.length ; i++)
{
var account = accounts[i],
title = this.prefs.getCharPref( account + '.title' );
var menuitem = document.createElement("menuitem");
menuitem.setAttribute("label", title);
menuitem.setAttribute("id", "sendtophone_" + account);
menuitem.setAttribute("type", "radio");
menuitem.setAttribute("name", "sendtophone_account");
if (currentAccount == account)
menuitem.setAttribute("checked", true);
// Select that account
menuitem.addEventListener("command", function(e) {
var id = e.target.getAttribute("id");
sendtophoneCore.setCurrentAccount( id.match(/_(.*)/)[1] );
}, false);
menupopup.appendChild(menuitem);
}
},
initPopup: function()
{
var fileServerUrl = this.prefs.getCharPref( "fileServerUrl" );
@@ -241,6 +271,8 @@ var sendtophone = {
document.getElementById("sendtophoneContextMenuSendClipboard").disabled = !this.clipboardHasText();
document.getElementById("sendtophoneContextMenuLogout").disabled = !sendtophoneCore.isLoggedIn();
// returning true will make the popup show
return true;
},

View File

@@ -27,3 +27,8 @@
<!ENTITY sendtophoneFileServer.None "None">
<!ENTITY sendtophoneFileServer.Custom "Custom">
<!ENTITY sendtophoneFileServer.Disclaimer "Disclaimer: We (FoxToPhone developers) are not affiliated with any of the third party hosting services listed here. You should read their Terms Of Service before using them.">
<!ENTITY sendtophonePhoneAccounts.label "Phone accounts">
<!ENTITY sendtophonePhoneAccounts.description "If you are using several Google accounts with your devices, you can switch between them after creating them here.">
<!ENTITY sendtophonePhoneAccounts.add "Add new">
<!ENTITY sendtophonePhoneAccounts.rename "Rename">
<!ENTITY sendtophonePhoneAccounts.remove "Remove">

View File

@@ -21,3 +21,6 @@ qrContextMenu=Send "%s" to Android
FileUploadsDisabled=It's not possible to upload files to the phone.
FileTooBig=The file is too big.
videoTitle=Video Link
PhoneAccountPromptAdd=Please, write the name for the new account
PhoneAccountPromptRename=Please, write the new name for this account
PhoneAccountPromptRemove=Are you sure that you want to delete this account?

View File

@@ -27,3 +27,8 @@
<!ENTITY sendtophoneFileServer.None "Ninguno">
<!ENTITY sendtophoneFileServer.Custom "Personalizado">
<!ENTITY sendtophoneFileServer.Disclaimer "Aviso: Nosotros (los desarrolladores de Fox to Phone) no estamos relacionados con ninguno de los servidores de alojamiento mostrados aquí. Debes leer sus Terminos de Servicio antes de usarlos.">
<!ENTITY sendtophonePhoneAccounts.label "Cuentas">
<!ENTITY sendtophonePhoneAccounts.description "Si usas varias cuentas de Google con tus aparatos, puedes cambiar entre ellas tras crearlas aquí.">
<!ENTITY sendtophonePhoneAccounts.add "Añadir">
<!ENTITY sendtophonePhoneAccounts.rename "Renombrar">
<!ENTITY sendtophonePhoneAccounts.remove "Quitar">

View File

@@ -21,3 +21,6 @@ qrContextMenu=Enviar "%s" a Android
FileUploadsDisabled=No se pueden enviar ficheros al teléfono.
FileTooBig=El fichero que quiere enviar es demasiado grande.
videoTitle=Video Link
PhoneAccountPromptAdd=Por favor, escriba un nombre para la nueva cuenta
PhoneAccountPromptRename=Por favor, escriba el nuevo nombre para la cuenta
PhoneAccountPromptRemove=¿Está seguro de querer quitar esta cuenta?

View File

@@ -6,6 +6,9 @@ pref("extensions.sendtophone.proxyUrl", "http://foxtophone.com/f2p.php?link=");
pref("extensions.sendtophone.fileServerUrl", "");
pref("extensions.sendtophone.fileUploadMaxKb", 50000);
pref("extensions.sendtophone.SearchQR", 0); // 0: search and prompt, 1: search and launch automatically, 2: don't search
pref("extensions.sendtophone.accounts", "account0");
pref("extensions.sendtophone.currentAccount", "account0");
pref("extensions.sendtophone.account0.title", "default");
pref("extensions.sendtophone.protocols.market", true);
pref("extensions.sendtophone.protocols.sms", true);

View File

@@ -4,7 +4,7 @@
<em:id>sendtophone@martinezdelizarrondo.com</em:id>
<em:type>2</em:type>
<em:name>Fox To Phone</em:name>
<em:version>1.2beta2</em:version>
<em:version>1.2beta3</em:version>
<em:creator>Alfonso &amp; Patrick</em:creator>
<em:description>Send links to your Android 2.2 phone using the new C2DM service. Based on the ChromeToPhone extension.</em:description>

View File

@@ -71,7 +71,7 @@ function openTab(url, successUrl, callback)
* @return {ChromeExOAuth} An initialized ChromeExOAuth object.
*/
var OAuthFactory = {
init : function(oauth_config)
init : function(oauth_config, preferences)
{
var chromeExOAuth = new ChromeExOAuth(
oauth_config['request_url'],
@@ -91,22 +91,19 @@ var OAuthFactory = {
/*
LocalStorage isn't working for extensions
http://farter.users.sourceforge.net/blog/2011/03/07/using-localstorage-in-firefox-extensions-for-persistent-data-storage/
Use the preferences system with a branch for each account
*/
var url = oauth_config['scope'];
var ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
.getService(Components.interfaces.nsIScriptSecurityManager);
var dsm = Components.classes["@mozilla.org/dom/storagemanager;1"]
.getService(Components.interfaces.nsIDOMStorageManager);
chromeExOAuth.localStorage = dsm.getLocalStorageForPrincipal(ssm.getCodebasePrincipal( ios.newURI(url, "", null) ) , "");
chromeExOAuth.setPreferencesBranch(preferences);
return chromeExOAuth;
}
};
ChromeExOAuth.prototype.setPreferencesBranch = function( branch ) {
this.prefs = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefService)
.getBranch(branch) ;
}
/**
* Constructor - no need to invoke directly, call initBackgroundPage instead.
@@ -156,8 +153,8 @@ function ChromeExOAuth(url_request_token, url_auth_token, url_access_token,
* "logout" of the configured OAuth API.
*/
ChromeExOAuth.prototype.clearTokens = function() {
delete this.localStorage[this.key_token + encodeURI(this.oauth_scope)];
delete this.localStorage[this.key_token_secret + encodeURI(this.oauth_scope)];
try { this.prefs.clearUserPref(this.key_token); } catch (e) {}
try { this.prefs.clearUserPref(this.key_token_secret); } catch (e) {}
};
/**
@@ -414,7 +411,7 @@ ChromeExOAuth.addURLParam = function(url, key, value) {
* @param {String} token The token to store.
*/
ChromeExOAuth.prototype.setToken = function(token) {
this.localStorage[this.key_token + encodeURI(this.oauth_scope)] = token;
this.prefs.setCharPref(this.key_token, token);
};
/**
@@ -422,7 +419,9 @@ ChromeExOAuth.prototype.setToken = function(token) {
* @return {String} The stored token.
*/
ChromeExOAuth.prototype.getToken = function() {
return this.localStorage[this.key_token + encodeURI(this.oauth_scope)];
if (!this.prefs.prefHasUserValue(this.key_token))
return "";
return this.prefs.getCharPref(this.key_token);
};
/**
@@ -430,7 +429,7 @@ ChromeExOAuth.prototype.getToken = function() {
* @param {String} secret The secret to store.
*/
ChromeExOAuth.prototype.setTokenSecret = function(secret) {
this.localStorage[this.key_token_secret + encodeURI(this.oauth_scope)] = secret;
this.prefs.setCharPref(this.key_token_secret, secret);
};
/**
@@ -438,12 +437,13 @@ ChromeExOAuth.prototype.setTokenSecret = function(secret) {
* @return {String} The stored secret.
*/
ChromeExOAuth.prototype.getTokenSecret = function() {
return this.localStorage[this.key_token_secret + encodeURI(this.oauth_scope)];
if (!this.prefs.prefHasUserValue(this.key_token_secret))
return "";
return this.prefs.getCharPref(this.key_token_secret);
};
/**
* Starts an OAuth authorization flow for the current page. If a token exists,
* no redirect is needed and the supplied callback is called immediately.
* Starts an OAuth authorization flow.
* If this method detects that a redirect has finished, it grabs the
* appropriate OAuth parameters from the URL and attempts to retrieve an
* access token. If no token exists and no redirect has happened, then
@@ -454,14 +454,21 @@ ChromeExOAuth.prototype.getTokenSecret = function() {
* secret {String} The OAuth access token secret.
*/
ChromeExOAuth.prototype.initOAuthFlow = function(callback) {
if (!this.hasToken()) {
// Clear any existing credentials as they have failed
this.clearTokens();
var request_params = {
'url_callback_param' : 'chromeexoauthcallback',
'url_callback': this.callback_page
}
var self = this;
this.getRequestToken(function(url) {
this.getRequestToken(function(url, error) {
if (error)
{
callback(error);
return;
}
openTab( url, request_params.url_callback, function( url )
{
var params = ChromeExOAuth.getQueryStringParams( url );
@@ -476,9 +483,6 @@ ChromeExOAuth.prototype.initOAuthFlow = function(callback) {
});
}, request_params);
} else {
callback(this.getToken(), this.getTokenSecret());
}
};
/**
@@ -547,7 +551,8 @@ ChromeExOAuth.prototype.onRequestToken = function(callback, xhr) {
}
callback(url);
} else {
throw new Error("Fetching request token failed. Status " + xhr.status);
// throw new Error("Fetching request token failed. Status " + xhr.status);
callback(null, "Fetching request token failed. Status " + xhr.status);
}
}
};

View File

@@ -53,6 +53,8 @@ var sendtophoneCore = {
if (typeof OAuthFactory == "undefined")
Components.utils.import("resource://sendtophone/OAuth.js");
var currentAccount = this.prefs.getCharPref('currentAccount');
this.oauth = OAuthFactory.init({
'request_url' : baseUrl + '/_ah/OAuthGetRequestToken',
'authorize_url' : baseUrl + '/_ah/OAuthAuthorizeToken',
@@ -62,7 +64,16 @@ var sendtophoneCore = {
'scope' : baseUrl,
'app_name' : 'Fox To Phone',
'callback_page': this.returnOAuthUrl
});
}, 'extensions.sendtophone.' + currentAccount + '.');
},
setCurrentAccount: function(account)
{
if (!this.prefs)
this.init();
this.prefs.setCharPref('currentAccount', account);
this.oauth.setPreferencesBranch('extensions.sendtophone.' + account + '.');
},
getString: function(name)
@@ -112,7 +123,7 @@ var sendtophoneCore = {
.logStringMessage( text );
},
processXHR: function(url, method, headers, data, callback)
processXHR: function(url, method, headers, data, callback, errorCallback)
{
var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest);
@@ -149,6 +160,11 @@ var sendtophoneCore = {
}
else
{
if (errorCallback)
{
errorCallback.call( sendtophoneCore, req);
return;
}
sendtophoneCore.alert(sendtophoneCore.getString("ErrorOnSend") + ' (status ' + req.status + ')\r\n' + body);
}
}
@@ -274,11 +290,47 @@ var sendtophoneCore = {
}
},
// Detect if the user is logged in
isLoggedIn: function()
{
if (!this.oauth)
this.init();
return (this.oauth.hasToken());
},
doLogin: function()
{
this.popupNotification( this.getString("LoginRequired") );
//Open Google login page and close tab when done
this.oauth.initOAuthFlow( function() {sendtophoneCore.loginSuccessful();} );
this.oauth.initOAuthFlow( function(error) {
var self = sendtophoneCore;
if (error)
{
// Try to guess if the domain might be blocked. Not bulletprof, but friendlier that stating anything about "request token"
if (error == "Fetching request token failed. Status 0")
{
var url = self.prefs.getCharPref( "appUrl" );
// The expected response isn't hardcoded here as we don't know what are the plans.
self.processXHR( url, "GET", null, "",
function(req) {
// Don't really know why the login failed.
self.alert( error );
},
function(req) {
if ( req.status == 0)
self.alert("Unable to connect with " + url + "\r\nCheck that it isn't blocked with a firewall");
else
self.alert( error );
});
}
else
self.alert(error);
return;
}
self.loginSuccessful();
} );
},
processSentData : function(body, req)