-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes #3890 I think it would be useful to have a simple tutorial for setting up saving via GitHub pages.
- Loading branch information
Showing
8 changed files
with
307 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
/*\ | ||
title: $:/core/modules/savers/github.js | ||
type: application/javascript | ||
module-type: saver | ||
Saves wiki by pushing a commit to the GitHub v3 REST API | ||
\*/ | ||
(function(){ | ||
|
||
/*jslint node: true, browser: true */ | ||
/*global $tw: false */ | ||
"use strict"; | ||
|
||
var base64utf8 = require("$:/core/modules/utils/base64-utf8/base64-utf8.module.js"); | ||
|
||
/* | ||
Select the appropriate saver module and set it up | ||
*/ | ||
var GitHubSaver = function(wiki) { | ||
this.wiki = wiki; | ||
}; | ||
|
||
GitHubSaver.prototype.save = function(text,method,callback) { | ||
var self = this, | ||
username = this.wiki.getTiddlerText("$:/GitHub/Username"), | ||
password = $tw.utils.getPassword("github"), | ||
repo = this.wiki.getTiddlerText("$:/GitHub/Repo"), | ||
path = this.wiki.getTiddlerText("$:/GitHub/Path"), | ||
filename = this.wiki.getTiddlerText("$:/GitHub/Filename"), | ||
branch = this.wiki.getTiddlerText("$:/GitHub/Branch") || "master", | ||
headers = { | ||
"Accept": "application/vnd.github.v3+json", | ||
"Content-Type": "application/json;charset=UTF-8", | ||
"Authorization": "Basic " + window.btoa(username + ":" + password) | ||
}; | ||
// Make sure the path start and ends with a slash | ||
if(path.substring(0,1) !== "/") { | ||
path = "/" + path; | ||
} | ||
if(path.substring(path.length - 1) !== "/") { | ||
path = path + "/"; | ||
} | ||
// Compose the base URI | ||
var uri = "https://api.github.com/repos/" + repo + "/contents" + path; | ||
// Bail if we don't have everything we need | ||
if(!username || !password || !repo || !path || !filename) { | ||
return false; | ||
} | ||
// Perform a get request to get the details (inc shas) of files in the same path as our file | ||
$tw.utils.httpRequest({ | ||
url: uri, | ||
type: "GET", | ||
headers: headers, | ||
data: { | ||
ref: branch | ||
}, | ||
callback: function(err,getResponseDataJson,xhr) { | ||
if(err) { | ||
return callback(err); | ||
} | ||
var getResponseData = JSON.parse(getResponseDataJson), | ||
sha = ""; | ||
$tw.utils.each(getResponseData,function(details) { | ||
if(details.name === filename) { | ||
sha = details.sha; | ||
} | ||
}); | ||
var data = { | ||
message: "Saved by TiddlyWiki", | ||
content: base64utf8.base64.encode.call(base64utf8,text), | ||
branch: branch, | ||
sha: sha | ||
}; | ||
// Perform a PUT request to save the file | ||
$tw.utils.httpRequest({ | ||
url: uri + filename, | ||
type: "PUT", | ||
headers: headers, | ||
data: JSON.stringify(data), | ||
callback: function(err,putResponseDataJson,xhr) { | ||
if(err) { | ||
return callback(err); | ||
} | ||
var putResponseData = JSON.parse(putResponseDataJson); | ||
callback(null); | ||
} | ||
}); | ||
} | ||
}); | ||
return true; | ||
}; | ||
|
||
/* | ||
Information about this saver | ||
*/ | ||
GitHubSaver.prototype.info = { | ||
name: "github", | ||
priority: 2000, | ||
capabilities: ["save", "autosave"] | ||
}; | ||
|
||
/* | ||
Static method that returns true if this saver is capable of working | ||
*/ | ||
exports.canSave = function(wiki) { | ||
return true; | ||
}; | ||
|
||
/* | ||
Create an instance of this saver | ||
*/ | ||
exports.create = function(wiki) { | ||
return new GitHubSaver(wiki); | ||
}; | ||
|
||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// From https://gist.github.com/Nijikokun/5192472 | ||
// | ||
// UTF8 Module | ||
// | ||
// Cleaner and modularized utf-8 encoding and decoding library for javascript. | ||
// | ||
// copyright: MIT | ||
// author: Nijiko Yonskai, @nijikokun, nijikokun@gmail.com | ||
(function (name, definition, context, dependencies) { | ||
if (typeof context['module'] !== 'undefined' && context['module']['exports']) { if (dependencies && context['require']) { for (var i = 0; i < dependencies.length; i++) context[dependencies[i]] = context['require'](dependencies[i]); } context['module']['exports'] = definition.apply(context); } | ||
else if (typeof context['define'] !== 'undefined' && context['define'] === 'function' && context['define']['amd']) { define(name, (dependencies || []), definition); } | ||
else { context[name] = definition.apply(context); } | ||
})('utf8', function () { | ||
return { | ||
encode: function (string) { | ||
if (typeof string !== 'string') return string; | ||
else string = string.replace(/\r\n/g, "\n"); | ||
var output = "", i = 0, charCode; | ||
|
||
for (i; i < string.length; i++) { | ||
charCode = string.charCodeAt(i); | ||
|
||
if (charCode < 128) | ||
output += String.fromCharCode(charCode); | ||
else if ((charCode > 127) && (charCode < 2048)) | ||
output += String.fromCharCode((charCode >> 6) | 192), | ||
output += String.fromCharCode((charCode & 63) | 128); | ||
else | ||
output += String.fromCharCode((charCode >> 12) | 224), | ||
output += String.fromCharCode(((charCode >> 6) & 63) | 128), | ||
output += String.fromCharCode((charCode & 63) | 128); | ||
} | ||
|
||
return output; | ||
}, | ||
|
||
decode: function (string) { | ||
if (typeof string !== 'string') return string; | ||
var output = "", i = 0, charCode = 0; | ||
|
||
while (i < string.length) { | ||
charCode = string.charCodeAt(i); | ||
|
||
if (charCode < 128) | ||
output += String.fromCharCode(charCode), | ||
i++; | ||
else if ((charCode > 191) && (charCode < 224)) | ||
output += String.fromCharCode(((charCode & 31) << 6) | (string.charCodeAt(i + 1) & 63)), | ||
i += 2; | ||
else | ||
output += String.fromCharCode(((charCode & 15) << 12) | ((string.charCodeAt(i + 1) & 63) << 6) | (string.charCodeAt(i + 2) & 63)), | ||
i += 3; | ||
} | ||
|
||
return output; | ||
} | ||
}; | ||
}, this); | ||
|
||
// Base64 Module | ||
// | ||
// Cleaner, modularized and properly scoped base64 encoding and decoding module for strings. | ||
// | ||
// copyright: MIT | ||
// author: Nijiko Yonskai, @nijikokun, nijikokun@gmail.com | ||
(function (name, definition, context, dependencies) { | ||
if (typeof context['module'] !== 'undefined' && context['module']['exports']) { if (dependencies && context['require']) { for (var i = 0; i < dependencies.length; i++) context[dependencies[i]] = context['require'](dependencies[i]); } context['module']['exports'] = definition.apply(context); } | ||
else if (typeof context['define'] !== 'undefined' && context['define'] === 'function' && context['define']['amd']) { define(name, (dependencies || []), definition); } | ||
else { context[name] = definition.apply(context); } | ||
})('base64', function (utf8) { | ||
var $this = this; | ||
var $utf8 = utf8 || this.utf8; | ||
var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; | ||
|
||
return { | ||
encode: function (input) { | ||
if (typeof $utf8 === 'undefined') throw { error: "MissingMethod", message: "UTF8 Module is missing." }; | ||
if (typeof input !== 'string') return input; | ||
else input = $utf8.encode(input); | ||
var output = "", a, b, c, d, e, f, g, i = 0; | ||
|
||
while (i < input.length) { | ||
a = input.charCodeAt(i++); | ||
b = input.charCodeAt(i++); | ||
c = input.charCodeAt(i++); | ||
d = a >> 2; | ||
e = ((a & 3) << 4) | (b >> 4); | ||
f = ((b & 15) << 2) | (c >> 6); | ||
g = c & 63; | ||
|
||
if (isNaN(b)) f = g = 64; | ||
else if (isNaN(c)) g = 64; | ||
|
||
output += map.charAt(d) + map.charAt(e) + map.charAt(f) + map.charAt(g); | ||
} | ||
|
||
return output; | ||
}, | ||
|
||
decode: function (input) { | ||
if (typeof $utf8 === 'undefined') throw { error: "MissingMethod", message: "UTF8 Module is missing." }; | ||
if (typeof input !== 'string') return input; | ||
else input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); | ||
var output = "", a, b, c, d, e, f, g, i = 0; | ||
|
||
while (i < input.length) { | ||
d = map.indexOf(input.charAt(i++)); | ||
e = map.indexOf(input.charAt(i++)); | ||
f = map.indexOf(input.charAt(i++)); | ||
g = map.indexOf(input.charAt(i++)); | ||
|
||
a = (d << 2) | (e >> 4); | ||
b = ((e & 15) << 4) | (f >> 2); | ||
c = ((f & 3) << 6) | g; | ||
|
||
output += String.fromCharCode(a); | ||
if (f != 64) output += String.fromCharCode(b); | ||
if (g != 64) output += String.fromCharCode(c); | ||
} | ||
|
||
return $utf8.decode(output); | ||
} | ||
} | ||
}, this, [ "utf8" ]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"tiddlers": [ | ||
{ | ||
"file": "base64-utf8.module.js", | ||
"fields": { | ||
"type": "application/javascript", | ||
"title": "$:/core/modules/utils/base64-utf8/base64-utf8.module.js", | ||
"module-type": "library" | ||
}, | ||
"prefix": "(function(){", | ||
"suffix": "}).call(exports);" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
title: $:/core/ui/ControlPanel/Saving/GitHub | ||
tags: $:/tags/ControlPanel/Saving | ||
caption: {{$:/language/ControlPanel/Saving/GitHub/Caption}} | ||
|
||
\define lingo-base() $:/language/ControlPanel/Saving/GitHub/ | ||
|
||
<<lingo Description>> | ||
|
||
|<<lingo UserName>> |<$edit-text tiddler="$:/GitHub/Username" default="" tag="input"/> | | ||
|<<lingo Password>> |<$password name="github"/> | | ||
|<<lingo Repo>> |<$edit-text tiddler="$:/GitHub/Repo" default="" tag="input"/> | | ||
|<<lingo Branch>> |<$edit-text tiddler="$:/GitHub/Branch" default="" tag="input"/> | | ||
|<<lingo Path>> |<$edit-text tiddler="$:/GitHub/Path" default="" tag="input"/> | | ||
|<<lingo Filename>> |<$edit-text tiddler="$:/GitHub/Filename" default="" tag="input"/> | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
created: 20190408173002622 | ||
modified: 20190408173002622 | ||
tags: Saving Android Chrome Firefox InternetExplorer iOS Linux Mac Opera Safari Windows | ||
title: Saving to GitHub | ||
type: text/vnd.tiddlywiki | ||
delivery: Service | ||
method: save | ||
caption: GitHub Saver | ||
description: Save changes directly to a GitHub repository | ||
|
||
TiddlyWiki can save changes directly to a GitHub repository in the single file configuration. | ||
|
||
Saving to GitHub is configured in the [[$:/ControlPanel]] in the ''~GitHub Saver'' tab under the ''Saving'' tab. The following settings are supported: | ||
|
||
* ''Username'' - (mandatory) the username for the GitHub account used for saving changes | ||
* ''Password'' - (mandatory) the password, OAUTH token or personal access token for the specified account. Note that GitHub permits [[several different mechanisms|https://developer.github.com/v3/#authentication]] for authentication | ||
* ''Repository'' - (mandatory) the name of the GitHub repository. Both the owner name and the repository name must be specified. For example `Jermolene/TiddlyWiki5` | ||
* ''Branch'' - (optional) the name of the branch to be used within the GitHub repository. Defaults to `master` | ||
* ''Path'' - (optional) the path to the target file. Defaults to `/` | ||
* ''Filename'' - (mandatory) the filename of the target file | ||
|
||
Notes | ||
|
||
* The GitHub password is stored persistently in browser local storage. Be sure to clear the password if using a shared machine. Using a [[personal access token|]] for authentication offers an extra layer of security: if the access token is accidentally exposed it can be revoked without needing to reset the account password |
aa5eaa9
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EDIT: I cannot reliably reproduce the following issue. One guess is that it happens when the save button is clicked twice.
I copied the pre-release build in order to test the GitHub saver by this commit. All seems to function correctly with one annoyance:
After every successful saving to my gh repo (should be enough to reproduce the error), the following floating box appears:
Error while saving: XMLHttpRequest error code: 409
Error:
GitHub saver config:
Response headers:
Request headers
aa5eaa9
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @pesho-ivanov that does indeed sound like a timing issue. This stuff is quite new so your help with testing is much appreciated.