mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-11-26 19:47:20 +00:00
Add support for importing encrypted TiddlyWiki documents
This commit is contained in:
parent
2f4932fefc
commit
299e9d15fb
@ -58,3 +58,7 @@ Error message and password prompt
|
|||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tw-password-wrapper input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
37
boot/boot.js
37
boot/boot.js
@ -448,11 +448,13 @@ Adds a new password prompt. Options are:
|
|||||||
submitText: text to use for submit button (defaults to "Login")
|
submitText: text to use for submit button (defaults to "Login")
|
||||||
serviceName: text of the human readable service name
|
serviceName: text of the human readable service name
|
||||||
noUserName: set true to disable username prompt
|
noUserName: set true to disable username prompt
|
||||||
|
canCancel: set true to enable a cancel button (callback called with null)
|
||||||
callback: function to be called on submission with parameter of object {username:,password:}. Callback must return `true` to remove the password prompt
|
callback: function to be called on submission with parameter of object {username:,password:}. Callback must return `true` to remove the password prompt
|
||||||
*/
|
*/
|
||||||
$tw.utils.PasswordPrompt.prototype.createPrompt = function(options) {
|
$tw.utils.PasswordPrompt.prototype.createPrompt = function(options) {
|
||||||
// Create and add the prompt to the DOM
|
// Create and add the prompt to the DOM
|
||||||
var submitText = options.submitText || "Login",
|
var self = this,
|
||||||
|
submitText = options.submitText || "Login",
|
||||||
dm = $tw.utils.domMaker,
|
dm = $tw.utils.domMaker,
|
||||||
children = [dm("h1",{text: options.serviceName})];
|
children = [dm("h1",{text: options.serviceName})];
|
||||||
if(!options.noUserName) {
|
if(!options.noUserName) {
|
||||||
@ -465,6 +467,19 @@ $tw.utils.PasswordPrompt.prototype.createPrompt = function(options) {
|
|||||||
attributes: {type: "password", name: "password", placeholder: "Password"},
|
attributes: {type: "password", name: "password", placeholder: "Password"},
|
||||||
"class": "input-small"
|
"class": "input-small"
|
||||||
}));
|
}));
|
||||||
|
if(options.canCancel) {
|
||||||
|
children.push(dm("button",{
|
||||||
|
text: "Cancel",
|
||||||
|
"class": "btn",
|
||||||
|
eventListeners: [{
|
||||||
|
name: "click",
|
||||||
|
handlerFunction: function(event) {
|
||||||
|
self.removePrompt(promptInfo);
|
||||||
|
options.callback(null);
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}));
|
||||||
|
}
|
||||||
children.push(dm("button",{
|
children.push(dm("button",{
|
||||||
attributes: {type: "submit"},
|
attributes: {type: "submit"},
|
||||||
text: submitText,
|
text: submitText,
|
||||||
@ -492,12 +507,7 @@ $tw.utils.PasswordPrompt.prototype.createPrompt = function(options) {
|
|||||||
// Call the callback
|
// Call the callback
|
||||||
if(options.callback(data)) {
|
if(options.callback(data)) {
|
||||||
// Remove the prompt if the callback returned true
|
// Remove the prompt if the callback returned true
|
||||||
var i = self.passwordPrompts.indexOf(promptInfo);
|
self.removePrompt(promptInfo);
|
||||||
if(i !== -1) {
|
|
||||||
self.passwordPrompts.splice(i,1);
|
|
||||||
promptInfo.form.parentNode.removeChild(promptInfo.form);
|
|
||||||
self.setWrapperDisplay();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Clear the password if the callback returned false
|
// Clear the password if the callback returned false
|
||||||
$tw.utils.each(form.elements,function(element) {
|
$tw.utils.each(form.elements,function(element) {
|
||||||
@ -520,6 +530,15 @@ $tw.utils.PasswordPrompt.prototype.createPrompt = function(options) {
|
|||||||
this.setWrapperDisplay();
|
this.setWrapperDisplay();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$tw.utils.PasswordPrompt.prototype.removePrompt = function(promptInfo) {
|
||||||
|
var i = this.passwordPrompts.indexOf(promptInfo);
|
||||||
|
if(i !== -1) {
|
||||||
|
this.passwordPrompts.splice(i,1);
|
||||||
|
promptInfo.form.parentNode.removeChild(promptInfo.form);
|
||||||
|
this.setWrapperDisplay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Crypto helper object for encrypted content. It maintains the password text in a closure, and provides methods to change
|
Crypto helper object for encrypted content. It maintains the password text in a closure, and provides methods to change
|
||||||
the password, and to encrypt/decrypt a block of text
|
the password, and to encrypt/decrypt a block of text
|
||||||
@ -530,7 +549,9 @@ $tw.utils.Crypto = function() {
|
|||||||
callSjcl = function(method,inputText) {
|
callSjcl = function(method,inputText) {
|
||||||
var outputText;
|
var outputText;
|
||||||
try {
|
try {
|
||||||
outputText = sjcl[method](password,inputText);
|
if(password) {
|
||||||
|
outputText = sjcl[method](password,inputText);
|
||||||
|
}
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
console.log("Crypto error:" + ex);
|
console.log("Crypto error:" + ex);
|
||||||
outputText = null;
|
outputText = null;
|
||||||
|
@ -1071,17 +1071,70 @@ exports.readFile = function(file,callback) {
|
|||||||
// Onload
|
// Onload
|
||||||
reader.onload = function(event) {
|
reader.onload = function(event) {
|
||||||
// Deserialise the file contents
|
// Deserialise the file contents
|
||||||
var tiddlerFields = {title: file.name || "Untitled", type: type};
|
var text = event.target.result,
|
||||||
|
tiddlerFields = {title: file.name || "Untitled", type: type};
|
||||||
// Are we binary?
|
// Are we binary?
|
||||||
if(isBinary) {
|
if(isBinary) {
|
||||||
// The base64 section starts after the first comma in the data URI
|
// The base64 section starts after the first comma in the data URI
|
||||||
var commaPos = event.target.result.indexOf(",");
|
var commaPos = text.indexOf(",");
|
||||||
if(commaPos !== -1) {
|
if(commaPos !== -1) {
|
||||||
tiddlerFields.text = event.target.result.substr(commaPos+1);
|
tiddlerFields.text = text.substr(commaPos+1);
|
||||||
callback([tiddlerFields]);
|
callback([tiddlerFields]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
callback(self.deserializeTiddlers(type,event.target.result,tiddlerFields));
|
// Check whether this is an encrypted TiddlyWiki file
|
||||||
|
var encryptedStoreAreaStartMarker = "<pre id=\"encryptedStoreArea\" type=\"text/plain\" style=\"display:none;\">",
|
||||||
|
encryptedStoreAreaStart = text.indexOf(encryptedStoreAreaStartMarker),
|
||||||
|
encryptedStoreAreaEnd = encryptedStoreAreaStart !== -1 ? text.indexOf("</pre>",encryptedStoreAreaStart) : -1;
|
||||||
|
if(encryptedStoreAreaStart !== -1 && encryptedStoreAreaEnd !== -1) {
|
||||||
|
var encryptedJson = $tw.utils.htmlDecode(text.substring(encryptedStoreAreaStart + encryptedStoreAreaStartMarker.length,encryptedStoreAreaEnd-1)),
|
||||||
|
attemptDecryption = function() {
|
||||||
|
var decryptedText = $tw.crypto.decrypt(encryptedJson);
|
||||||
|
if(decryptedText) {
|
||||||
|
var json = JSON.parse(decryptedText),
|
||||||
|
tiddlers = [];
|
||||||
|
for(var title in json) {
|
||||||
|
tiddlers.push(json[title]);
|
||||||
|
}
|
||||||
|
return tiddlers;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Try to decrypt with the current password
|
||||||
|
var tiddlers = attemptDecryption();
|
||||||
|
if(tiddlers) {
|
||||||
|
callback(tiddlers);
|
||||||
|
} else {
|
||||||
|
// Prompt for a new password and keep trying
|
||||||
|
$tw.passwordPrompt.createPrompt({
|
||||||
|
serviceName: "Enter a password to decrypt the imported TiddlyWiki",
|
||||||
|
noUserName: true,
|
||||||
|
canCancel: true,
|
||||||
|
submitText: "Decrypt",
|
||||||
|
callback: function(data) {
|
||||||
|
// Exit if the user cancelled
|
||||||
|
if(!data) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Attempt to decrypt the tiddlers
|
||||||
|
$tw.crypto.setPassword(data.password);
|
||||||
|
var tiddlers = attemptDecryption();
|
||||||
|
if(tiddlers) {
|
||||||
|
callback(tiddlers);
|
||||||
|
// Exit and remove the password prompt
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// We didn't decrypt everything, so continue to prompt for password
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Try to deserialise any tiddlers in the file
|
||||||
|
callback(self.deserializeTiddlers(type,text,tiddlerFields));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Kick off the read
|
// Kick off the read
|
||||||
|
@ -16,6 +16,7 @@ type: text/vnd.tiddlywiki
|
|||||||
|
|
||||||
!! Improvements
|
!! Improvements
|
||||||
|
|
||||||
|
* Added support for importing encrypted TiddlyWiki documents
|
||||||
* Added the [[highlight.js|http://highlightjs.org/]] syntax highlighting plugin: http://tiddlywiki.com/highlightdemo.html (thanks to João Bolila, @jbolila on GitHub)
|
* Added the [[highlight.js|http://highlightjs.org/]] syntax highlighting plugin: http://tiddlywiki.com/highlightdemo.html (thanks to João Bolila, @jbolila on GitHub)
|
||||||
* Added the first export option to the ''Tools'' tab of the [[control panel|$:/ControlPanel]]
|
* Added the first export option to the ''Tools'' tab of the [[control panel|$:/ControlPanel]]
|
||||||
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/ffcc215e8f8896be96093579abc5bcfb76335e66]] an ellipsis for [[advanced search|$:/AdvancedSearch]] next to the search box in the sidebar
|
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/ffcc215e8f8896be96093579abc5bcfb76335e66]] an ellipsis for [[advanced search|$:/AdvancedSearch]] next to the search box in the sidebar
|
||||||
|
Loading…
Reference in New Issue
Block a user