mirror of
				http://git.whoc.org.uk/git/password-manager.git
				synced 2025-10-31 19:27:34 +01:00 
			
		
		
		
	Implemented lock (to be reviewed)
This commit is contained in:
		| @@ -63,6 +63,7 @@ Clipperz_normalizedNewLine = '\x0d\x0a'; | ||||
| 	</div> | ||||
| 	<div class="page right" id="loginPage"></div> | ||||
| 	<div class="page right" id="registrationPage"></div> | ||||
| 	<div class="page left"  id="unlockPage"></div> | ||||
| 	<div class="page right" id="mainPage"></div> | ||||
| 	<div class="page right" id="cardDetailPage"></div> | ||||
| 	<div class="page right" id="errorPage"></div> | ||||
|   | ||||
| @@ -35,6 +35,8 @@ Clipperz.PM.DataModel.User.Header.Legacy = function(args) { | ||||
| 	this._records = null; | ||||
| //	this._directLogins = null; | ||||
|  | ||||
| Clipperz.log("Clipperz.PM.DataModel.User.Header.Legacy: Legacy header in use!"); | ||||
|  | ||||
| 	return this; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -33,7 +33,6 @@ Clipperz.PM.DataModel.User.Header.Preferences = function(args) { | ||||
| 	return this; | ||||
| } | ||||
|  | ||||
|  | ||||
| Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.Preferences, Clipperz.PM.DataModel.EncryptedRemoteObject, { | ||||
|  | ||||
| 	'toString': function() { | ||||
| @@ -41,8 +40,42 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.Preferences, Clipperz.PM. | ||||
| 	}, | ||||
|  | ||||
| 	//------------------------------------------------------------------------- | ||||
| 	 | ||||
| 	'mergeDefaultPreferences': function(somePreferences) { | ||||
| 		var result; | ||||
|  | ||||
| 		result = new Clipperz.KeyValueObjectStore(); | ||||
|  | ||||
| 		result.setValues(MochiKit.Base.updatetree( | ||||
| 			Clipperz.Base.deepClone(Clipperz.PM.DataModel.User.Header.Preferences.defaultPreferences), | ||||
| 			somePreferences | ||||
| 		)); | ||||
|  | ||||
| 		return result; | ||||
| 	}, | ||||
|  | ||||
| 	'getPreferences': function() { | ||||
| 		return Clipperz.Async.callbacks("User.Header.Preferences.getPreferences", [ | ||||
| 			MochiKit.Base.method(this, 'values'), | ||||
| 			MochiKit.Base.method(this, 'mergeDefaultPreferences') | ||||
| 		], {trace:false}); | ||||
| 	}, | ||||
|  | ||||
| 	'getPreference': function(aKey) { | ||||
| 		return Clipperz.Async.callbacks("User.Header.Preferences.getPreference", [ | ||||
| 			MochiKit.Base.method(this, 'getPreferences'), | ||||
| 			MochiKit.Base.methodcaller('getValue', aKey) | ||||
| 		], {trace:false}); | ||||
| 	}, | ||||
|  | ||||
| 	//========================================================================= | ||||
| 	__syntaxFix__: "syntax fix" | ||||
| }); | ||||
|  | ||||
|  | ||||
| Clipperz.PM.DataModel.User.Header.Preferences.defaultPreferences = { | ||||
| 	'lock': { | ||||
| 		'timeoutInMinutes': 10 | ||||
| 	}, | ||||
| 	'shouldShowDonationPanel': true | ||||
| }; | ||||
| @@ -394,7 +394,9 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, { | ||||
|  | ||||
| 	'lock': function () { | ||||
| 		return Clipperz.Async.callbacks("User.lock", [ | ||||
| 			MochiKit.Base.method(this, 'deleteAllCleanTextData') | ||||
| 			MochiKit.Base.method(this.connection(), 'logout'), | ||||
| 			MochiKit.Base.method(this, 'deleteAllCleanTextData'), | ||||
| 			MochiKit.Base.method(this, 'setPassphraseFunction', function() {throw("No passphrase set.")}) | ||||
| 		], {trace:false}); | ||||
| 	}, | ||||
|  | ||||
| @@ -906,6 +908,30 @@ console.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more t | ||||
| 		], {trace:false}); | ||||
| 	}, | ||||
|  | ||||
| 	//------------------------------------------------------------------------- | ||||
|  | ||||
| 	'getPreferences': function() { | ||||
| 		return Clipperz.Async.callbacks("User.getPreferences", [ | ||||
| 			MochiKit.Base.method(this, 'getHeaderIndex', 'preferences'), | ||||
| 			MochiKit.Base.methodcaller('getPreferences') | ||||
| 		], {trace:false}); | ||||
| 	}, | ||||
|  | ||||
| 	'getPreference': function(aKey) { | ||||
| 		return Clipperz.Async.callbacks("User.getPreference", [ | ||||
| 			MochiKit.Base.method(this, 'getHeaderIndex', 'preferences'), | ||||
| 			MochiKit.Base.methodcaller('getPreference', aKey) | ||||
| 		], {trace:false}); | ||||
| 	}, | ||||
|  | ||||
| 	'setPreference': function(aKey, aValue) { | ||||
| 		return Clipperz.Async.callbacks("User.setPreference", [ | ||||
| 			MochiKit.Base.method(this, 'getHeaderIndex', 'preferences'), | ||||
| 			MochiKit.Base.methodcaller('setValue', aKey, aValue), | ||||
| 			MochiKit.Base.method(this, 'saveChanges') | ||||
| 		], {trace:false}); | ||||
| 	}, | ||||
|  | ||||
| 	//========================================================================= | ||||
|  | ||||
| 	'hasPendingChanges': function () { | ||||
|   | ||||
| @@ -234,36 +234,44 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, { | ||||
| 	//========================================================================= | ||||
|  | ||||
| 	'processMessage': function (aFunctionName, someParameters) { | ||||
| 		var result; | ||||
| 		var	connection; | ||||
| 		var	deferredResult; | ||||
| 		 | ||||
| 		connection = this.getConnectionForRequest(aFunctionName, someParameters); | ||||
| 		try { | ||||
| 			var result; | ||||
| 			var	connection; | ||||
|  | ||||
| 		switch(aFunctionName) { | ||||
| 			case 'knock': | ||||
| 				result = this._knock(connection, someParameters); | ||||
| 				break; | ||||
| 			case 'registration': | ||||
| 				this.checkToll(aFunctionName, someParameters); | ||||
| 				result = this._registration(connection, someParameters.parameters); | ||||
| 				break; | ||||
| 			case 'handshake': | ||||
| 				this.checkToll(aFunctionName, someParameters); | ||||
| 				result = this._handshake(connection, someParameters.parameters); | ||||
| 				break; | ||||
| 			case 'message': | ||||
| 				this.checkToll(aFunctionName, someParameters); | ||||
| 				result = this._message(connection, someParameters.parameters); | ||||
| 				break; | ||||
| 			case 'logout': | ||||
| 				this._currentStaticConnection = null; | ||||
| 				result = this._logout(connection, someParameters.parameters); | ||||
| 				break; | ||||
| 			connection = this.getConnectionForRequest(aFunctionName, someParameters); | ||||
|  | ||||
| 			switch(aFunctionName) { | ||||
| 				case 'knock': | ||||
| 					result = this._knock(connection, someParameters); | ||||
| 					break; | ||||
| 				case 'registration': | ||||
| 					this.checkToll(aFunctionName, someParameters); | ||||
| 					result = this._registration(connection, someParameters.parameters); | ||||
| 					break; | ||||
| 				case 'handshake': | ||||
| 					this.checkToll(aFunctionName, someParameters); | ||||
| 					result = this._handshake(connection, someParameters.parameters); | ||||
| 					break; | ||||
| 				case 'message': | ||||
| 					this.checkToll(aFunctionName, someParameters); | ||||
| 					result = this._message(connection, someParameters.parameters); | ||||
| 					break; | ||||
| 				case 'logout': | ||||
| 					this._currentStaticConnection = null; | ||||
| 					result = this._logout(connection, someParameters.parameters); | ||||
| 					break; | ||||
| 			} | ||||
|  | ||||
| 			this.storeConnectionForRequestWithConnectionAndResponse(aFunctionName, someParameters, connection, result); | ||||
|  | ||||
| 			deferredResult = MochiKit.Async.succeed(result); | ||||
| 		} catch (exception) { | ||||
| 			deferredResult = MochiKit.Async.fail(exception); | ||||
| 		} | ||||
| 		 | ||||
| 		this.storeConnectionForRequestWithConnectionAndResponse(aFunctionName, someParameters, connection, result); | ||||
|  | ||||
| 		return MochiKit.Async.succeed(result); | ||||
| 		return deferredResult; | ||||
| 	}, | ||||
|  | ||||
| 	//========================================================================= | ||||
| @@ -395,6 +403,7 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, { | ||||
| 				); | ||||
| 				result['M2'] = M2; | ||||
| 				result['accountInfo'] = aConnection['userData']['accountInfo']; | ||||
| 				result['lock'] = '<<LOCK>>'; | ||||
| 			} else { | ||||
| 				throw new Error("Client checksum verification failed! Expected <" + M1 + ">, received <" + someParameters.parameters.M1 + ">.", "Error"); | ||||
| 			} | ||||
| @@ -438,7 +447,7 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, { | ||||
|  | ||||
| 		result = { | ||||
| 			result: result, | ||||
| 			toll:   this.getTollForRequestType(nextTollRequestType) | ||||
| 			toll:   this.getTollForRequestType(nextTollRequestType), | ||||
| 		} | ||||
|  | ||||
| 		return result; | ||||
| @@ -449,6 +458,10 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, { | ||||
| 	'_message': function(aConnection, someParameters) { | ||||
| 		var result; | ||||
|  | ||||
| 		if (MochiKit.Base.keys(aConnection).length==0) { | ||||
| 			throw('Trying to communicate without an active connection'); | ||||
| 		} | ||||
|  | ||||
| 		result = {}; | ||||
|  | ||||
| 		//===================================================================== | ||||
|   | ||||
| @@ -219,7 +219,8 @@ Clipperz.PM.UI.Components.Cards.ViewClass = React.createClass({ | ||||
| 				Clipperz.PM.UI.Components.Cards.TextArea({ | ||||
| //				React.DOM.textarea({ | ||||
| 					'readOnly': true, | ||||
| 					'onClick': function(e) { e.target.select(); }, | ||||
| 					// 'onMouseUp': function(e) { e.target.focus(); e.target.select(); e.stopPropagation(); e.preventDefault();}, | ||||
| 					'onClick': function(e) { e.target.focus(); e.target.select(); e.target.selectionStart = 0; e.target.selectionEnd = e.target.value.length; e.stopPropagation(); e.preventDefault(); }, | ||||
| 					'className':Clipperz.PM.UI.Components.classNames(cardFieldValueClasses), | ||||
| 					'value': aField['value'], | ||||
| 					'rows': 1 | ||||
|   | ||||
| @@ -26,8 +26,6 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures'); | ||||
|  | ||||
| Clipperz.PM.UI.Components.ExtraFeatures.OTPClass = React.createClass({ | ||||
|  | ||||
| 	//	TODO: add print button!!!! | ||||
|  | ||||
| 	getInitialState: function() { | ||||
| 		return { | ||||
| //			'selectedOTPs': [], | ||||
|   | ||||
| @@ -0,0 +1,150 @@ | ||||
| /* | ||||
|  | ||||
| Copyright 2008-2015 Clipperz Srl | ||||
|  | ||||
| This file is part of Clipperz, the online password manager. | ||||
| For further information about its features and functionalities please | ||||
| refer to http://www.clipperz.com. | ||||
|  | ||||
| * Clipperz is free software: you can redistribute it and/or modify it | ||||
|   under the terms of the GNU Affero General Public License as published | ||||
|   by the Free Software Foundation, either version 3 of the License, or  | ||||
|   (at your option) any later version. | ||||
|  | ||||
| * Clipperz is distributed in the hope that it will be useful, but  | ||||
|   WITHOUT ANY WARRANTY; without even the implied warranty of  | ||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||||
|   See the GNU Affero General Public License for more details. | ||||
|  | ||||
| * You should have received a copy of the GNU Affero General Public | ||||
|   License along with Clipperz. If not, see http://www.gnu.org/licenses/. | ||||
|  | ||||
| */ | ||||
|  | ||||
| "use strict"; | ||||
| Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures'); | ||||
|  | ||||
| Clipperz.PM.UI.Components.ExtraFeatures.PreferencesClass = React.createClass({ | ||||
|  | ||||
| 	getInitialState: function() { | ||||
| 		return { | ||||
| 			'preferenceBeingEdited': null, | ||||
| 			'preferenceValue': '', | ||||
| 		}; | ||||
| 	}, | ||||
|  | ||||
| 	propTypes: { | ||||
| 	}, | ||||
|  | ||||
| 	//========================================================================= | ||||
|  | ||||
| 	setEditedPreference: function(aKey, aValue) { | ||||
| 		this.setState({ | ||||
| 			'preferenceBeingEdited': aKey, | ||||
| 			'preferenceValue': aValue | ||||
| 		}); | ||||
| 	}, | ||||
|  | ||||
| 	handleChange: function(anEvent) { | ||||
| 		var newState = this.state; | ||||
|  | ||||
| 		newState['preferenceValue'] = anEvent.target.value; | ||||
|  | ||||
| 		this.setState(newState); | ||||
| 	}, | ||||
|  | ||||
| 	handleSave: function() { | ||||
| 		MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'setPreference', this.state['preferenceBeingEdited'], this.state['preferenceValue']); | ||||
| 		this.setEditedPreference(null, ''); | ||||
| 	}, | ||||
|  | ||||
| 	handleCancel: function() { | ||||
| 		this.setEditedPreference(null, ''); | ||||
| 	}, | ||||
|  | ||||
| 	handleKeyPressed: function(anEvent) { | ||||
| 		switch (anEvent.keyCode) { | ||||
| 			case  9: // tab | ||||
| 				this.handleSave(); | ||||
| 				// TODO: edit next preference | ||||
| 				break; | ||||
| 			case 13: // enter | ||||
| 				this.handleSave(); | ||||
| 				break; | ||||
| 			case 27: // escape | ||||
| 				this.handleCancel(); | ||||
| 				break; | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	//========================================================================= | ||||
|  | ||||
| 	renderPreferenceValueElement: function(aKey) { | ||||
| 		var result; | ||||
|  | ||||
| 		var preferenceValue = this.props.userInfo.preferences.getValue(aKey); | ||||
|  | ||||
| 		if (this.state.preferenceBeingEdited == aKey) { | ||||
| 			result = React.DOM.input({ | ||||
| 				'autoFocus': true, | ||||
| 				'key': aKey, | ||||
| 				'type': 'text', | ||||
| 				'value': this.state.preferenceValue, | ||||
| 				'onChange': this.handleChange, | ||||
| 				'onKeyDown': MochiKit.Base.method(this, 'handleKeyPressed'), | ||||
| 			}); | ||||
| 		} else { | ||||
| 			result = React.DOM.p({ | ||||
| 				'className': 'preferenceValue', | ||||
| 				'onClick': MochiKit.Base.method(this, 'setEditedPreference', aKey, preferenceValue) | ||||
| 			}, preferenceValue); | ||||
| 		} | ||||
|  | ||||
| 		return result; | ||||
| 	}, | ||||
|  | ||||
| 	renderPreferences: function() { | ||||
| 		var result; | ||||
| 		 | ||||
| 		result = [ | ||||
| 			React.DOM.li({'key': 'autoLockAfterMinutes'}, [ | ||||
| 				React.DOM.p({'className': 'preferenceName'}, "Automatic lock (minutes) - m"), | ||||
| 				React.DOM.p({'className': 'preferenceDescription'}, "Automatically lock Clipperz after N minutes. (0 = auto lock disabled)"), | ||||
| 				this.renderPreferenceValueElement('lock.timeoutInMinutes') | ||||
| 			]), | ||||
| 			React.DOM.li({'key': 'shouldShowDonationPanel'}, [ | ||||
| 				React.DOM.p({'className': 'preferenceName'}, "Donation Panel - m"), | ||||
| 				React.DOM.p({'className': 'preferenceDescription'}, "Select whether to display the donation panel or not"), | ||||
| 				this.renderPreferenceValueElement('shouldShowDonationPanel') | ||||
| 			]) | ||||
| 		]; | ||||
|  | ||||
| 		return result; | ||||
| 	}, | ||||
|  | ||||
| 	render: function () { | ||||
| 		var result; | ||||
|  | ||||
| 		if (! this.props.userInfo.preferences) { | ||||
| 			result = React.DOM.p({}, "spinner..."); | ||||
| 		} else { | ||||
| 			result = React.DOM.div({'className':'extraFeature preferences'}, [ | ||||
| 				React.DOM.div({'className':'header'}, [ | ||||
| 					React.DOM.h1({}, "Preferences"), | ||||
| 					React.DOM.div({'className':'description'}, [ | ||||
| 						React.DOM.p({}, "Insert copy here..."), | ||||
| 					]) | ||||
| 				]), | ||||
| 				React.DOM.div({'className':'content'}, [ | ||||
| 					React.DOM.ul({'className':'preferenceList'}, this.renderPreferences()), | ||||
| 				]) | ||||
| 			]); | ||||
| 		} | ||||
|  | ||||
| 		return result; | ||||
| 	}, | ||||
|  | ||||
| 	//========================================================================= | ||||
| }); | ||||
|  | ||||
| Clipperz.PM.UI.Components.ExtraFeatures.Preferences = React.createFactory(Clipperz.PM.UI.Components.ExtraFeatures.PreferencesClass); | ||||
| @@ -42,6 +42,7 @@ Clipperz.PM.UI.Components.Pages.MainPageClass = React.createClass({ | ||||
| 		'userInfo':			React.PropTypes.object.isRequired, | ||||
| 		'accountInfo':		React.PropTypes.object.isRequired, | ||||
| 		'style':			React.PropTypes.oneOf(Clipperz_PM_UI_availableStyles).isRequired, | ||||
| 		'locked':			React.PropTypes.bool, | ||||
| //		'mediaQueryStyle':	React.PropTypes.oneOf(['extra-short', 'narrow', 'wide', 'extra-wide']).isRequired, | ||||
| //		'cards':			React.PropTypes.deferred.isRequired | ||||
| 	}, | ||||
| @@ -55,17 +56,25 @@ Clipperz.PM.UI.Components.Pages.MainPageClass = React.createClass({ | ||||
| 	//========================================================================= | ||||
|  | ||||
| 	render: function () { | ||||
| 		var	classes = { | ||||
| 			'mainPage': true | ||||
| 		}; | ||||
| 		classes[this.props['style']] = true; | ||||
| 		var result; | ||||
|  | ||||
| 		return	React.DOM.div({'key':'mainPage', 'className':Clipperz.PM.UI.Components.classNames(classes)}, [ | ||||
| 			this.props['style'] != 'extra-wide' ? Clipperz.PM.UI.Components.Panels.SelectionPanel(this.props) : null, | ||||
| 			Clipperz.PM.UI.Components.Panels.MainPanel(this.props), | ||||
| 			Clipperz.PM.UI.Components.Panels.ExtraFeaturesPanel(this.props), | ||||
| 			this.props['ask'] ? Clipperz.PM.UI.Components.DialogBox(this.props['ask']) : null | ||||
| 		]); | ||||
| 		if (this.props['locked']) { | ||||
| 			result = null; | ||||
| 		} else { | ||||
| 			var	classes = { | ||||
| 				'mainPage': true | ||||
| 			}; | ||||
| 			classes[this.props['style']] = true; | ||||
|  | ||||
| 			result = React.DOM.div({'key':'mainPage', 'className':Clipperz.PM.UI.Components.classNames(classes)}, [ | ||||
| 				this.props['style'] != 'extra-wide' ? Clipperz.PM.UI.Components.Panels.SelectionPanel(this.props) : null, | ||||
| 				Clipperz.PM.UI.Components.Panels.MainPanel(this.props), | ||||
| 				Clipperz.PM.UI.Components.Panels.ExtraFeaturesPanel(this.props), | ||||
| 				this.props['ask'] ? Clipperz.PM.UI.Components.DialogBox(this.props['ask']) : null | ||||
| 			]); | ||||
| 		} | ||||
|  | ||||
| 		return result; | ||||
| 	} | ||||
|  | ||||
| 	//========================================================================= | ||||
|   | ||||
							
								
								
									
										174
									
								
								frontend/delta/js/Clipperz/PM/UI/Components/Pages/UnlockPage.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								frontend/delta/js/Clipperz/PM/UI/Components/Pages/UnlockPage.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| /* | ||||
|  | ||||
| Copyright 2008-2015 Clipperz Srl | ||||
|  | ||||
| This file is part of Clipperz, the online password manager. | ||||
| For further information about its features and functionalities please | ||||
| refer to http://www.clipperz.com. | ||||
|  | ||||
| * Clipperz is free software: you can redistribute it and/or modify it | ||||
|   under the terms of the GNU Affero General Public License as published | ||||
|   by the Free Software Foundation, either version 3 of the License, or  | ||||
|   (at your option) any later version. | ||||
|  | ||||
| * Clipperz is distributed in the hope that it will be useful, but  | ||||
|   WITHOUT ANY WARRANTY; without even the implied warranty of  | ||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||||
|   See the GNU Affero General Public License for more details. | ||||
|  | ||||
| * You should have received a copy of the GNU Affero General Public | ||||
|   License along with Clipperz. If not, see http://www.gnu.org/licenses/. | ||||
|  | ||||
| */ | ||||
|  | ||||
| "use strict"; | ||||
| Clipperz.Base.module('Clipperz.PM.UI.Components.Pages'); | ||||
|  | ||||
| Clipperz.PM.UI.Components.Pages.UnlockPageClass = React.createClass({ | ||||
|  | ||||
| 	propTypes: { | ||||
| 		mode:							React.PropTypes.oneOf(['CREDENTIALS','PIN']).isRequired, | ||||
| 		// isNewUserRegistrationAvailable:	React.PropTypes.bool.isRequired, | ||||
| 		disabled:						React.PropTypes.bool.isRequired | ||||
| 	}, | ||||
| /* | ||||
| 	getDefaultProps: function () { | ||||
| 		return { | ||||
| 			mode: 'CREDENTIALS', | ||||
| 			isNewUserRegistrationAvailable: false, | ||||
| 			disabled: false, | ||||
| //			template: Clipperz.PM.UI.Components.PageTemplate | ||||
| 		} | ||||
| 	}, | ||||
| */ | ||||
| 	getInitialState: function () { | ||||
| 		return { | ||||
| 			passphrase: '', | ||||
| 			pin: '' | ||||
| 		}; | ||||
| 	}, | ||||
|  | ||||
| 	//========================================================================= | ||||
|  | ||||
| 	handleChange: function (anEvent) { | ||||
| 		var newState = {}; | ||||
|  | ||||
| 		newState['passphrase'] = anEvent.target.value; | ||||
|  | ||||
| 	    this.setState(newState); | ||||
| 	}, | ||||
|  | ||||
| 	// pollForChanges: function() { | ||||
| 	// 	if (this.props.mode == 'CREDENTIALS') { | ||||
| 	// 		var newState; | ||||
|  | ||||
| 	// 		var usernameValue = this.refs['username'].getDOMNode().value; | ||||
| 	// 		var passphraseValue = this.refs['passphrase'].getDOMNode().value; | ||||
|  | ||||
| 	// 		newState = {}; | ||||
|  | ||||
| 	// 		newState['username'] = (usernameValue) ? usernameValue : ""; | ||||
| 	// 		newState['passphrase'] = (passphraseValue) ? passphraseValue : ""; | ||||
|  | ||||
| 	// 		this.setState(newState); | ||||
| 	// 	} | ||||
| 	// }, | ||||
|  | ||||
| 	//========================================================================= | ||||
|  | ||||
| 	handlePassphraseSubmit: function (event) { | ||||
| 		event.preventDefault(); | ||||
|  | ||||
| 		this.refs['passphrase'].getDOMNode().blur(); | ||||
|  | ||||
| 		MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'unlock', this.refs['passphrase'].getDOMNode().value); | ||||
|  | ||||
| 		 | ||||
| 	}, | ||||
|  | ||||
| 	resetUnlockForm: function() { | ||||
| 		this.refs['passphrase'].getDOMNode().value = ''; | ||||
| 		this.replaceState(this.getInitialState()); | ||||
| 	}, | ||||
|  | ||||
| 	//------------------------------------------------------------------------- | ||||
|  | ||||
| 	shouldEnableUnlockButton: function () { | ||||
| 		var result; | ||||
|  | ||||
| 		return	( | ||||
| 					(this.state['passphrase'] != '')  | ||||
| 					|| | ||||
| 					(this.state['pin'] != '') | ||||
| 				) | ||||
| 				&& | ||||
| 				!this.props['disabled']; | ||||
| 	}, | ||||
|  | ||||
| 	unlockForm: function () { | ||||
| 		return	React.DOM.form({'key':'form', 'className':'unlockForm credentials', 'autoComplete':'off', 'autoCorrect':'off', 'autoCapitalize':'off', 'onChange':this.handleChange, 'onSubmit':this.handlePassphraseSubmit}, [ | ||||
| 					React.DOM.div({'key':'fields'},[ | ||||
| 						React.DOM.label({'key':'passphrase-label', 'htmlFor' :'passphrase'}, "passphrase"), | ||||
| 						React.DOM.input({'key':'passphrase', 'type':'password', 'name':'passphrase', 'ref':'passphrase', 'placeholder':"passphrase"}) | ||||
| 					]), | ||||
| 					React.DOM.button({'key':'button', 'type':'submit', 'disabled':!this.shouldEnableUnlockButton(), 'className':'button'}, "unlock") | ||||
| 				]); | ||||
| 	}, | ||||
|  | ||||
| 	// handlePINSubmit: function (event) { | ||||
| 	// 	event.preventDefault(); | ||||
|  | ||||
| 	// 	this.refs['pin'].getDOMNode().blur(); | ||||
|  | ||||
| 	// 	var credentials = { | ||||
| 	// 		pin: this.refs['pin'].getDOMNode().value | ||||
| 	// 	} | ||||
|  | ||||
| 	// 	MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'doLogin', credentials); | ||||
| 	// }, | ||||
|  | ||||
| 	// pinForm: function () { | ||||
| 	// 	return	React.DOM.form({'className':'pinForm pin', 'autoComplete':'off', 'onChange':this.handleChange, 'onSubmit':this.handlePINSubmit}, [ | ||||
| 	// 				React.DOM.div({'key':'pinFormDiv'},[ | ||||
| 	// 					React.DOM.label({'for':'pin'}, "pin"), | ||||
| 	// 					React.DOM.input({'type':'text', 'name':'pin', 'ref':'pin', placeholder:"PIN", 'key':'pin', 'autocapitalize':'none'}) | ||||
| 	// 				]), | ||||
| 	// 				React.DOM.button({'key':'submitButton', 'type':'submit', 'disabled':this.props.disabled, 'className':'button'}, "login") | ||||
| 	// 			]); | ||||
| 	// }, | ||||
|  | ||||
| 	setInitialFocus: function () { | ||||
| 		if (this.props.mode == 'PIN') { | ||||
| 			this.refs['pin'].getDOMNode().select(); | ||||
| 		} else { | ||||
| 			this.refs['passphrase'].getDOMNode().select(); | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	// showUrl: function (anUrl) { | ||||
| 	// 	return function () { | ||||
| 	// 		window.open(anUrl, 'clipperz_about'); | ||||
| 	// 	} | ||||
| 	// }, | ||||
|  | ||||
| 	render: function() { | ||||
| //console.log("LOGIN PAGE", this.props); | ||||
| 		return React.DOM.div({'key':'unlockForm', 'className':'unlockForm ' + this.props['style']}, [ | ||||
| 			Clipperz.PM.UI.Components.AccountStatus(MochiKit.Base.update(this.props['proxyInfo'])), | ||||
| 			React.DOM.header({'key':'header'}, [ | ||||
| 				React.DOM.h3({}, 'Type your passphrase to unlock'), | ||||
| 			]), | ||||
| 			React.DOM.div({'key':'formWrapper', 'className':'form'}, [ | ||||
| 				this.props.mode == 'PIN' ? this.pinForm() : this.unlockForm(), | ||||
| 			]), | ||||
| 			React.DOM.footer({'key':'footer'}, [ | ||||
| 				React.DOM.div({'key':'applicationVersion', 'className':'applicationVersion'}, [ | ||||
| 					React.DOM.span({'key':'applicationVersionLabel'}, "application version"), | ||||
| 					React.DOM.a({'key':'applicationVersionLink', 'href':'https://github.com/clipperz/password-manager/commit/' + Clipperz_version, 'target':'github'}, Clipperz_version) | ||||
| 				]) | ||||
| 			]) | ||||
| 		]); | ||||
| 	} | ||||
| }); | ||||
|  | ||||
| Clipperz.PM.UI.Components.Pages.UnlockPage = React.createFactory(Clipperz.PM.UI.Components.Pages.UnlockPageClass); | ||||
| @@ -81,7 +81,7 @@ Clipperz.PM.UI.Components.Panels.ExtraFeaturesPanelClass = React.createClass({ | ||||
| 	}, | ||||
|  | ||||
| 	lock: function () { | ||||
| console.log("LOCK"); | ||||
| 		MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'lock'); | ||||
| 	}, | ||||
|  | ||||
| 	logout: function () { | ||||
| @@ -121,6 +121,9 @@ console.log("LOCK"); | ||||
| 		if (aComponentName == 'OTP') { | ||||
| 			MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'updateOTPListAndDetails'); | ||||
| 		} | ||||
| 		if (aComponentName == 'Preferences') { | ||||
| 			MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'updatePreferences'); | ||||
| 		} | ||||
| 		 | ||||
| 		this.setState({ | ||||
| 			'isFullyOpen':true, | ||||
| @@ -150,6 +153,9 @@ console.log("LOCK"); | ||||
| 					React.DOM.li({'key':'account', 'className':this.state['index']['account'] ? 'open' : 'closed'}, [ | ||||
| 						React.DOM.h1({'key':'accountH1', 'onClick':this.toggleIndexState('account')}, "Account"), | ||||
| 						React.DOM.ul({'key':'accountUL'}, [ | ||||
| 							React.DOM.li({'key':'account_preferences', 'onClick':this.toggleExtraFeatureComponent('Preferences'), 'className':(this.state['extraFeatureComponentName'] == 'Preferences') ? 'selected' : ''}, [ | ||||
| 								React.DOM.h2({'key':'account_preferences_h2'}, "Preferences"), | ||||
| 							]), | ||||
| 							React.DOM.li({'key':'account_1', 'onClick':this.toggleExtraFeatureComponent('Passphrase'), 'className':(this.state['extraFeatureComponentName'] == 'Passphrase') ? 'selected' : ''}, [ | ||||
| 								React.DOM.h2({'key':'account_1_h2'}, "Passphrase"), | ||||
| //								React.DOM.div({'key':'account_1_div'}, [ | ||||
| @@ -280,7 +286,7 @@ console.log("LOCK"); | ||||
| 						]) | ||||
| 					]), | ||||
| 					React.DOM.li({'key':'logout', 'className':'lock-logout'}, [ | ||||
| //						React.DOM.h2({'className':'lock',   'onClick':this.lock},   "Lock"), | ||||
| 						React.DOM.h2({'className':'lock',   'onClick':this.lock},   "Lock"), | ||||
| 						React.DOM.h2({'className':'logout', 'onClick':this.logout}, "Logout"), | ||||
| 					]) | ||||
| 				]) | ||||
|   | ||||
| @@ -51,10 +51,14 @@ Clipperz.PM.UI.MainController = function() { | ||||
|  | ||||
| 	this._closeMaskAction = null; | ||||
|  | ||||
| 	this._lockListener = null; | ||||
| 	this._lockTimeout = null; | ||||
|  | ||||
| 	this._pages = {}; | ||||
| 	this.renderPages([ | ||||
| 		'loginPage', | ||||
| 		'registrationPage', | ||||
| 		'unlockPage', | ||||
| 		'mainPage', | ||||
| 		'cardDetailPage', | ||||
| 		'errorPage', | ||||
| @@ -62,10 +66,12 @@ Clipperz.PM.UI.MainController = function() { | ||||
|  | ||||
| 	this.registerForNotificationCenterEvents([ | ||||
| 		'doLogin', 'registerNewUser', 'showRegistrationForm', 'goBack', | ||||
| 		'logout', | ||||
| 		'logout', 'unlock', | ||||
| 		'enableLock', 'disableLock', | ||||
| //		'lock', | ||||
| 		'changePassphrase', 'deleteAccount', | ||||
| 		'updatePreferences', 'setPreference', | ||||
| 		'updateOTPListAndDetails', 'createNewOTP', 'deleteOTPs', 'changeOTPLabel', | ||||
| //		'export', | ||||
| 		'importCards', | ||||
| 		'downloadExport', | ||||
| 		'updateProgress', | ||||
| @@ -170,7 +176,7 @@ MochiKit.Base.update(Clipperz.PM.UI.MainController.prototype, { | ||||
| 	//========================================================================= | ||||
|  | ||||
| 	showOfflineError: function () { | ||||
| console.log("THE BROWSER IS OFFLINE"); | ||||
| Clipperz.log("THE BROWSER IS OFFLINE"); | ||||
| 	}, | ||||
|  | ||||
| 	selectInitialProxy: function () { | ||||
| @@ -212,6 +218,7 @@ console.log("THE BROWSER IS OFFLINE"); | ||||
|  | ||||
| 		MochiKit.Iter.forEach(events, function (anEvent) { | ||||
| 			MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, anEvent, MochiKit.Base.method(self, anEvent + '_handler')); | ||||
| 			MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, anEvent, MochiKit.Base.method(self, 'resetLockTimeout')); | ||||
| 		}); | ||||
|  | ||||
| //		MochiKit.Signal.connect(window, 'onpopstate',		MochiKit.Base.method(this, 'historyGoBack')); | ||||
| @@ -383,6 +390,75 @@ console.log("THE BROWSER IS OFFLINE"); | ||||
| 		return deferredResult; | ||||
| 	}, | ||||
|  | ||||
| 	lock: function () { | ||||
| 		var deferredResult; | ||||
|  | ||||
| 		MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'disableLock'); | ||||
|  | ||||
| 		deferredResult = new Clipperz.Async.Deferred('MainController.lock_handler', {trace:false}); | ||||
| 		//	force exit edit state OR skip lock if pending changes are present | ||||
| 		deferredResult.addMethod(this, 'exitCurrentSelection'); | ||||
| 		deferredResult.addMethod(this, 'resetPanels'); | ||||
| 		deferredResult.addMethod(this, 'moveOutPage', this.currentPage(), 'unlockPage'); | ||||
| 		deferredResult.addMethod(this.user(), 'lock'); | ||||
| 		deferredResult.addMethod(this, 'deleteCleanTextData'); | ||||
| 		deferredResult.addMethod(this.pages()['unlockPage'], 'setInitialFocus'); | ||||
| 		deferredResult.addCallback(MochiKit.Async.callLater, 1, MochiKit.Base.method(this.pages()['mainPage'], 'replaceProps', {'locked': true})); | ||||
| 		 | ||||
| 		deferredResult.callback(); | ||||
|  | ||||
| 		return deferredResult; | ||||
| 	}, | ||||
|  | ||||
| 	unlock_handler: function(aPassphrase) { | ||||
| 		var deferredResult; | ||||
|  | ||||
| 		var user = this.user(); | ||||
| 		var unlockPage = this.pages()['unlockPage']; | ||||
| 		 | ||||
| 		deferredResult = new Clipperz.Async.Deferred('MainController.unlock_handler', {trace:false}); | ||||
| 		deferredResult.addMethod(unlockPage, 'setProps', {'disabled': true}); | ||||
|  | ||||
| 		deferredResult.addMethod(user, 'setPassphraseFunction', function() { return MochiKit.Async.succeed(aPassphrase); }); | ||||
| 		deferredResult.addMethod(user, 'getPreferences');		//	#ASK -> user.unlock | ||||
| //		deferredResult.addMethod(user, 'unlock', function() { return MochiKit.Async.succeed(aPassphrase); }); | ||||
| 		deferredResult.addErrback(function (aValue) { | ||||
| 			var innerDeferredResult; | ||||
|  | ||||
| 			innerDeferredResult = new Clipperz.Async.Deferred('MainController.unlock_handler <incorrect passphrase>', {trace:false}); | ||||
| 			innerDeferredResult.addMethod(user, 'getHeaderIndex', 'preferences'); | ||||
| 			innerDeferredResult.addMethodcaller('deleteAllCleanTextData'); | ||||
| 			innerDeferredResult.addMethod(unlockPage, 'setProps', {'disabled': false}); | ||||
| 			innerDeferredResult.addMethod(unlockPage, 'setInitialFocus'); | ||||
| 			innerDeferredResult.addCallback(MochiKit.Async.fail, aValue); | ||||
| 			innerDeferredResult.callback(); | ||||
|  | ||||
| 			return aValue; | ||||
| 		}); | ||||
|  | ||||
| 		deferredResult.addMethod(this, 'moveInPage', this.currentPage(), 'mainPage');	//	#ASK or cardDetailPage | ||||
| 		deferredResult.addMethod(this, 'refreshUI'); | ||||
| 		deferredResult.addMethod(unlockPage, 'setProps', {'disabled': false});	//	#ASK ? | ||||
| 		deferredResult.addMethod(unlockPage, 'resetUnlockForm');				//	#ASK ? | ||||
| //		deferredResult.addMethod(this, 'resetLockTimeout'); | ||||
| 		deferredResult.addCallback(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'enableLock'); | ||||
|  | ||||
| 		deferredResult.callback(); | ||||
|  | ||||
| 		return deferredResult; | ||||
|  | ||||
| 		// this.user().setPassphraseFunction(function(){return aPassphrase;}); | ||||
| // TODO: check if passphrase is correct by try/catch on decrypting something | ||||
| 		// this.moveOutPage(this.currentPage(), 'mainPage'); | ||||
| // TODO: check why the unlock form keeps the value stored (doesn't happen with the login form...) | ||||
| 	}, | ||||
|  | ||||
| 	deleteCleanTextData: function() { | ||||
| 		this._recordsInfo = null; | ||||
| 		this._selectedCards = null; | ||||
| 		this._selectedCardInfo = null; | ||||
| 	}, | ||||
|  | ||||
| 	//------------------------------------------------------------------------- | ||||
|  | ||||
| 	registerNewUser_handler: function (credentials) { | ||||
| @@ -489,6 +565,7 @@ console.log("THE BROWSER IS OFFLINE"); | ||||
| 	collectRecordInfo: function (aRecord) { | ||||
| 		var deferredResult; | ||||
|  | ||||
| //console.log("collectRecordInfo"); | ||||
| 		deferredResult = new Clipperz.Async.Deferred('MainController.collectRecordInfo', {trace:false}); | ||||
| 		deferredResult.setValue('_record'); | ||||
| 		deferredResult.addMethod(aRecord, 'reference'); | ||||
| @@ -551,7 +628,6 @@ console.log("THE BROWSER IS OFFLINE"); | ||||
| // deferredResult.addMethod(this, function(d) {console.log(d); return d;}); | ||||
|  | ||||
| 			deferredResult.addMethod(this, 'collectRecordInfo'); | ||||
|  | ||||
| 			deferredResult.addMethod(this, 'setPageProperties', 'mainPage', 'selectedCard'); | ||||
| 			if ((this.mediaQueryStyle() == 'narrow') && shouldShowCardDetail) { | ||||
| 				deferredResult.addMethod(this, 'showCardDetailInNarrowView'); | ||||
| @@ -730,6 +806,43 @@ console.log("THE BROWSER IS OFFLINE"); | ||||
|  | ||||
| 	//---------------------------------------------------------------------------- | ||||
|  | ||||
| 	enableLock_handler: function () { | ||||
| 		MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'lock', MochiKit.Base.method(this, 'lock')); | ||||
| 	}, | ||||
| 	 | ||||
| 	disableLock_handler: function () { | ||||
| 		MochiKit.Signal.disconnectAll(Clipperz.Signal.NotificationCenter, 'lock'); | ||||
| 	}, | ||||
| 	 | ||||
| //	clearLockTimeout: function () { | ||||
| //		clearTimeout(this._lockTimeout); | ||||
| //	}, | ||||
|  | ||||
| 	resetLockTimeout: function () { | ||||
| 		if (this.user()) { | ||||
| 			return Clipperz.Async.callbacks("MainController.resetLockTimeout", [ | ||||
| 				MochiKit.Base.method(this.user(), 'getPreference', 'lock.timeoutInMinutes'), | ||||
| 				MochiKit.Base.bind( function (aDelay) { | ||||
| 					var aDelay = aDelay * 60*1000; | ||||
|  | ||||
| 					if (this._lockTimeout) { | ||||
| // console.log("clearing previous lock timer"); | ||||
| 						clearTimeout(this._lockTimeout); | ||||
| 					} | ||||
|  | ||||
| 					if (aDelay > 0) { | ||||
| // console.log("setting lock timer", aDelay); | ||||
| 						this._lockTimeout = setTimeout(function() { | ||||
| 							MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'lock'); | ||||
| 						}, aDelay); | ||||
| 					} | ||||
| 				}, this) | ||||
| 			], {trace:false}); | ||||
| 		} | ||||
| 	}, | ||||
| 	 | ||||
| 	//---------------------------------------------------------------------------- | ||||
|  | ||||
| 	getAllCardsCount: function () { | ||||
| 		var	archivedCardsFilter =	this.shouldIncludeArchivedCards() | ||||
| 									?	MochiKit.Async.succeed | ||||
| @@ -813,6 +926,9 @@ console.log("THE BROWSER IS OFFLINE"); | ||||
|  | ||||
| 	runApplication: function (anUser) { | ||||
| 		this.moveInPage(this.currentPage(), 'mainPage'); | ||||
| 		MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'enableLock'); | ||||
| 		this.resetLockTimeout(); | ||||
|  | ||||
| 		return this.renderAccountData(); | ||||
| 	}, | ||||
|  | ||||
| @@ -1086,6 +1202,7 @@ console.log("THE BROWSER IS OFFLINE"); | ||||
| 				'featureSet':					this.featureSet(), | ||||
| 				'features':						this.features(), | ||||
| 				'proxyInfo':					this.proxyInfo(), | ||||
| 				'locked':						false | ||||
| //				'shouldIncludeArchivedCards':	this.shouldIncludeArchivedCards(), | ||||
| //				'cards':				…, | ||||
| //				'tags':					…, | ||||
| @@ -1302,6 +1419,7 @@ console.log("THE BROWSER IS OFFLINE"); | ||||
| 	}, | ||||
|  | ||||
| 	refreshCardEditDetail_handler: function (aRecordReference) { | ||||
| //console.log("refreshCardEditDetail_handler"); | ||||
| 		this.updateSelectedCard({'reference':aRecordReference}, false, true); | ||||
| 	}, | ||||
|  | ||||
| @@ -1371,6 +1489,36 @@ console.log("THE BROWSER IS OFFLINE"); | ||||
| 		return deferredResult; | ||||
| 	}, | ||||
| 	 | ||||
| 	updatePreferences: function() { | ||||
| 		return Clipperz.Async.callbacks("MainController.updatePreferences_handler", [ | ||||
| 			MochiKit.Base.method(this.user(), 'getPreferences'), | ||||
| 			MochiKit.Base.bind(function(somePreferences) { | ||||
| 				return MochiKit.Base.update(this.userInfo(), {'preferences': somePreferences}) | ||||
| 			}, this), | ||||
| 			MochiKit.Base.bind(function(someUserInfo) { | ||||
| 				this.pages()['mainPage'].setProps({ | ||||
| 					'userInfo': someUserInfo | ||||
| 				}) | ||||
| 			}, this), | ||||
| 		], {trace:false}); | ||||
| 	}, | ||||
|  | ||||
| 	updatePreferences_handler: function() { | ||||
| 		this.updatePreferences(); | ||||
| 	}, | ||||
|  | ||||
| 	setPreference_handler: function(aKey, aValue) { | ||||
| 		// return this.user().setPreference(aKey, aValue); | ||||
|  | ||||
| 		return Clipperz.Async.callbacks("MainController.setPreference_handler", [ | ||||
| 			MochiKit.Base.method(this.overlay(), 'show', "", true), | ||||
| 			MochiKit.Base.method(this.user(), 'setPreference', aKey, aValue), | ||||
| 			MochiKit.Base.method(this, 'refreshCurrentPage'), | ||||
| 			MochiKit.Base.method(this, 'updatePreferences'), | ||||
| 			MochiKit.Base.method(this.overlay(), 'done', "", 0.5), | ||||
| 		], {trace:false}); | ||||
| 	}, | ||||
|  | ||||
| 	importCards_handler: function(data) { | ||||
| 		return Clipperz.Async.callbacks("MainController.importCards_handler", [ | ||||
| 			MochiKit.Base.method(this.overlay(), 'show', "importing …", true), | ||||
| @@ -1407,7 +1555,7 @@ console.log("THE BROWSER IS OFFLINE"); | ||||
| 	updateOTPListAndDetails: function() { | ||||
|  | ||||
| 		return Clipperz.Async.callbacks("MainController.updateOTPListAndDetails", [ | ||||
| 			Clipperz.Async.collectResults("User.updateOTPListAndDetails <inner results>", { | ||||
| 			Clipperz.Async.collectResults("MainController.updateOTPListAndDetails <inner results>", { | ||||
| 				'userInfo':		MochiKit.Base.method(this, 'userInfo'), | ||||
| 				'otpDetails':	Clipperz.Async.collectResults("User.updateOTPListAndDetails <otpDetails>", { | ||||
| 					'otpList':		MochiKit.Base.method(this.user(),'getOneTimePasswords'), | ||||
| @@ -1478,6 +1626,7 @@ console.log("THE BROWSER IS OFFLINE"); | ||||
| 			MochiKit.Base.method(this, 'saveChanges'), | ||||
| 			MochiKit.Base.method(currentPage, 'setProps', {'mode':'view', 'showGlobalMask':false}), | ||||
| 			MochiKit.Base.method(this, 'refreshUI', aRecordReference), | ||||
| 			MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'enableLock'), | ||||
| 		], {trace:false}); | ||||
| 	}, | ||||
|  | ||||
| @@ -1523,6 +1672,7 @@ console.log("THE BROWSER IS OFFLINE"); | ||||
| 					this.goBackToMainPage(); | ||||
| 				} | ||||
| 			},this), | ||||
| 			MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'enableLock'), | ||||
| 		], {trace:false}); | ||||
| 	}, | ||||
|  | ||||
| @@ -1637,6 +1787,7 @@ console.log("THE BROWSER IS OFFLINE"); | ||||
| 		var	currentPage = this.pages()[this.currentPage()]; | ||||
|  | ||||
| 		currentPage.setProps({'mode': 'edit'}); | ||||
| 		MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'disableLock'); | ||||
|  | ||||
| 		return Clipperz.Async.callbacks("MainController.enterEditMode", [ | ||||
| 			MochiKit.Base.method(this, 'allTags', true), | ||||
|   | ||||
| @@ -178,6 +178,7 @@ | ||||
|  | ||||
| 		"Clipperz/PM/UI/Components/Pages/LoginPage.js", | ||||
| 		"Clipperz/PM/UI/Components/Pages/RegistrationPage.js", | ||||
| 		"Clipperz/PM/UI/Components/Pages/UnlockPage.js", | ||||
| 		"Clipperz/PM/UI/Components/Pages/MainPage.js", | ||||
| 		"Clipperz/PM/UI/Components/Pages/CardDetailPage.js", | ||||
| 		"Clipperz/PM/UI/Components/Pages/ErrorPage.js", | ||||
| @@ -187,6 +188,7 @@ | ||||
| 		"Clipperz/PM/UI/Components/Panels/ExtraFeaturesPanel.js", | ||||
|  | ||||
| 		"Clipperz/PM/UI/Components/ExtraFeatures/DevicePIN.js", | ||||
| 		"Clipperz/PM/UI/Components/ExtraFeatures/Preferences.js", | ||||
| 		"Clipperz/PM/UI/Components/ExtraFeatures/Passphrase.js", | ||||
| 		"Clipperz/PM/UI/Components/ExtraFeatures/OTP.js", | ||||
| 		"Clipperz/PM/UI/Components/ExtraFeatures/DeleteAccount.js", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Dario Chiappetta
					Dario Chiappetta