1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-30 13:29:56 +00:00
TiddlyWiki5/core/modules/savers/put.js
Simon Baird 39e4e69ae7
Show server response as error message in put saver (#6589)
* Show server response as error message in put saver

I'd like to use this on Tiddlyhost so users can get more informative
error messages if the put save fails for whatever reason.

This would make the put saver a viable replacement for the legacy
upload saver, which is what Tiddlyhost uses currently.

I'm not sure what standard WebDAV servers do, but I would guess they
don't provide any response body for put requests, and hence this
patch would have no impact for a standard WebDAV server. (That said,
it would be a good idea to test it to make sure there aren't any
unexpected regressions for WebDAV or other put saver compatible
services.)

* Access http response status directly in put saver

There's no need to extract it from the error string created inside
tw.utils.httpRequest if we can get it directly from the xhr object.

* Add 'Save starting' notification for put saver

There are two related changes here:

1. Add a 'Save starting' notification for the put saver, similar to
   the upload saver. Not sure if it was intentionally omitted for
   the put saver, but it seems reasonable to have the two be
   consistent.

2. Send the 'Save starting' notifications in both upload and put
   save right before the actual request is sent. While testing I
   noticed that the save might have failed before the "Save
   starting" notification appeared which doesn't seem useful.
2022-04-05 17:06:56 +01:00

141 lines
3.1 KiB
JavaScript

/*\
title: $:/core/modules/savers/put.js
type: application/javascript
module-type: saver
Saves wiki by performing a PUT request to the server
Works with any server which accepts a PUT request
to the current URL, such as a WebDAV server.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Retrieve ETag if available
*/
var retrieveETag = function(self) {
var headers = {
Accept: "*/*;charset=UTF-8"
};
$tw.utils.httpRequest({
url: self.uri(),
type: "HEAD",
headers: headers,
callback: function(err,data,xhr) {
if(err) {
return;
}
var etag = xhr.getResponseHeader("ETag");
if(!etag) {
return;
}
self.etag = etag.replace(/^W\//,"");
}
});
};
/*
Select the appropriate saver module and set it up
*/
var PutSaver = function(wiki) {
this.wiki = wiki;
var self = this;
var uri = this.uri();
// Async server probe. Until probe finishes, save will fail fast
// See also https://github.com/Jermolene/TiddlyWiki5/issues/2276
$tw.utils.httpRequest({
url: uri,
type: "OPTIONS",
callback: function(err,data,xhr) {
// Check DAV header http://www.webdav.org/specs/rfc2518.html#rfc.section.9.1
if(!err) {
self.serverAcceptsPuts = xhr.status === 200 && !!xhr.getResponseHeader("dav");
}
}
});
retrieveETag(this);
};
PutSaver.prototype.uri = function() {
return document.location.toString().split("#")[0];
};
// TODO: in case of edit conflict
// Prompt: Do you want to save over this? Y/N
// Merging would be ideal, and may be possible using future generic merge flow
PutSaver.prototype.save = function(text,method,callback) {
if(!this.serverAcceptsPuts) {
return false;
}
var self = this;
var headers = {
"Content-Type": "text/html;charset=UTF-8"
};
if(this.etag) {
headers["If-Match"] = this.etag;
}
$tw.notifier.display("$:/language/Notifications/Save/Starting");
$tw.utils.httpRequest({
url: this.uri(),
type: "PUT",
headers: headers,
data: text,
callback: function(err,data,xhr) {
if(err) {
var status = xhr.status,
errorMsg = err;
if(status === 412) { // file changed on server
errorMsg = $tw.language.getString("Error/PutEditConflict");
} else if(status === 401) { // authentication required
errorMsg = $tw.language.getString("Error/PutUnauthorized");
} else if(status === 403) { // permission denied
errorMsg = $tw.language.getString("Error/PutForbidden");
}
if (xhr.responseText) {
// treat any server response like a plain text error explanation
errorMsg = errorMsg + "\n\n" + xhr.responseText;
}
callback(errorMsg); // fail
} else {
self.etag = xhr.getResponseHeader("ETag");
if(self.etag == null) {
retrieveETag(self);
}
callback(null); // success
}
}
});
return true;
};
/*
Information about this saver
*/
PutSaver.prototype.info = {
name: "put",
priority: 2000,
capabilities: ["save","autosave"]
};
/*
Static method that returns true if this saver is capable of working
*/
exports.canSave = function(wiki) {
return /^https?:/.test(location.protocol);
};
/*
Create an instance of this saver
*/
exports.create = function(wiki) {
return new PutSaver(wiki);
};
})();