1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-01-26 18:58:21 +02:00

Finished WebDAV driver

This commit is contained in:
Laurent Cozic 2018-01-23 20:10:20 +00:00
parent 7bee25599d
commit 5393a1399c
3 changed files with 65 additions and 88 deletions

View File

@ -34,10 +34,6 @@ class WebDavApi {
return this.baseUrl_; return this.baseUrl_;
} }
davNs(json) {
}
async xmlToJson(xml) { async xmlToJson(xml) {
const nameProcessor = (name) => { const nameProcessor = (name) => {
@ -56,7 +52,7 @@ class WebDavApi {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
parseXmlString(xml, options, (error, result) => { parseXmlString(xml, options, (error, result) => {
if (error) { if (error) {
reject(error); resolve(null); // Error handled by caller which will display the XML text (or plain text) if null is returned from this function
return; return;
} }
resolve(result); resolve(result);
@ -87,36 +83,12 @@ class WebDavApi {
stringFromJson(json, keys) { stringFromJson(json, keys) {
return this.valueFromJson(json, keys, 'string'); return this.valueFromJson(json, keys, 'string');
// let output = json;
// for (let i = 0; i < keys.length; i++) {
// const key = keys[i];
// if (!output || !output[key]) return null;
// output = output[key];
// }
// if (typeof output !== 'string') return null;
// return output;
} }
objectFromJson(json, keys) { objectFromJson(json, keys) {
return this.valueFromJson(json, keys, 'object'); return this.valueFromJson(json, keys, 'object');
// let output = json;
// for (let i = 0; i < keys.length; i++) {
// const key = keys[i];
// if (!output || !output[key]) return null;
// output = output[key];
// }
// if (!Array.isArray(output) && typeof output === 'object') return output;
// return null;
} }
// isDirectory(propStat) {
// try {
// return propStat[0]['d:prop'][0]['d:resourcetype'][0]['d:collection'];
// } catch (error) {
// return false;
// }
// }
async execPropFind(path, fields = null) { async execPropFind(path, fields = null) {
if (fields === null) fields = ['d:getlastmodified']; if (fields === null) fields = ['d:getlastmodified'];
@ -127,17 +99,17 @@ class WebDavApi {
// To find all available properties: // To find all available properties:
// //
const body=`<?xml version="1.0" encoding="utf-8" ?> // const body=`<?xml version="1.0" encoding="utf-8" ?>
<propfind xmlns="DAV:"> // <propfind xmlns="DAV:">
<propname/> // <propname/>
</propfind>`; // </propfind>`;
// const body = `<?xml version="1.0" encoding="UTF-8"?> const body = `<?xml version="1.0" encoding="UTF-8"?>
// <d:propfind xmlns:d="DAV:"> <d:propfind xmlns:d="DAV:">
// <d:prop xmlns:oc="http://owncloud.org/ns"> <d:prop xmlns:oc="http://owncloud.org/ns">
// ` + fieldsXml + ` ` + fieldsXml + `
// </d:prop> </d:prop>
// </d:propfind>`; </d:propfind>`;
return this.exec('PROPFIND', path, body); return this.exec('PROPFIND', path, body);
} }
@ -149,8 +121,11 @@ class WebDavApi {
// </d:prop> // </d:prop>
// </d:propfind>' // </d:propfind>'
async exec(method, path = '', body = null, headers = null) { async exec(method, path = '', body = null, headers = null, options = null) {
if (headers === null) headers = {}; if (headers === null) headers = {};
if (options === null) options = {};
if (!options.responseFormat) options.responseFormat = 'json';
if (!options.target) options.target = 'string';
const authToken = this.authToken(); const authToken = this.authToken();
@ -159,50 +134,50 @@ class WebDavApi {
const fetchOptions = {}; const fetchOptions = {};
fetchOptions.headers = headers; fetchOptions.headers = headers;
fetchOptions.method = method; fetchOptions.method = method;
if (options.path) fetchOptions.path = options.path;
if (body) fetchOptions.body = body; if (body) fetchOptions.body = body;
const url = this.baseUrl() + '/' + path; const url = this.baseUrl() + '/' + path;
const response = await shim.fetch(url, fetchOptions); let response = null;
const responseText = await response.text();
const responseJson = await this.xmlToJson(responseText);
if (!responseJson) throw new Error('Could not parse response: ' + responseText); if (options.source == 'file' && (method == 'POST' || method == 'PUT')) {
response = await shim.uploadBlob(url, fetchOptions);
if (responseJson['d:error']) { } else if (options.target == 'string') {
const code = responseJson['d:error']['s:exception'] ? responseJson['d:error']['s:exception'].join(' ') : null; response = await shim.fetch(url, fetchOptions);
const message = responseJson['d:error']['s:message'] ? responseJson['d:error']['s:message'].join("\n") : responseText; } else { // file
throw new JoplinError(message, code); response = await shim.fetchBlob(url, fetchOptions);
} }
return responseJson; const responseText = await response.text();
let responseJson_ = null;
const loadResponseJson = async () => {
if (!responseText) return null;
if (responseJson_) return responseJson_;
responseJson_ = await this.xmlToJson(responseText);
if (!responseJson_) throw new JoplinError('Cannot parse JSON response: ' + responseText, response.status);
return responseJson_;
}
// //console.info(JSON.stringify(responseJson['d:multistatus']['d:response'])); if (!response.ok) {
// When using fetchBlob we only get a string (not xml or json) back
if (options.target === 'file') throw new JoplinError(responseText, response.status);
const json = await loadResponseJson();
// body = `<?xml version="1.0" encoding="UTF-8"?> if (json['d:error']) {
// <d:propfind xmlns:d="DAV:"> const code = json['d:error']['s:exception'] ? json['d:error']['s:exception'].join(' ') : response.status;
// <d:prop xmlns:oc="http://owncloud.org/ns"> const message = json['d:error']['s:message'] ? json['d:error']['s:message'].join("\n") : responseText;
// <d:getlastmodified/> throw new JoplinError(message + ' (' + code + ')', response.status);
// </d:prop> }
// </d:propfind>`;
// const authToken = this.authToken(); throw new JoplinError(responseText, response.status);
// const url = 'http://nextcloud.local/remote.php/dav/files/admin/Joplin'; }
// const fetchOptions = {
// method: 'PROPFIND', if (options.responseFormat === 'text') return responseText;
// headers: {
// 'Authorization': 'Basic ' + authToken,
// },
// body: body,
// };
// console.info(url, fetchOptions); return await loadResponseJson();
// const response = await shim.fetch(url, fetchOptions);
// console.info(await response.text());
} }
} }

View File

@ -59,23 +59,37 @@ class FileApiDriverWebDav {
} }
async get(path, options) { async get(path, options) {
if (!options) options = {};
if (!options.responseFormat) options.responseFormat = 'text';
try {
return await this.api().exec('GET', path, null, null, options);
} catch (error) {
if (error.code !== 404) throw error;
}
} }
async mkdir(path) { async mkdir(path) {
try {
await this.api().exec('MKCOL', path);
} catch (error) {
if (error.code !== 405) throw error; // 405 means that the collection already exists (Method Not Allowed)
}
} }
async put(path, content, options = null) { async put(path, content, options = null) {
await this.api().exec('PUT', path, content, null, options);
} }
async delete(path) { async delete(path) {
try {
await this.api().exec('DELETE', path);
} catch (error) {
if (error.code !== 404) throw error;
}
} }
async move(oldPath, newPath) { async move(oldPath, newPath) {
throw new Error('Not implemented');
} }
format() { format() {

View File

@ -289,18 +289,6 @@ class Synchronizer {
} }
if (action == 'createRemote' || action == 'updateRemote') { if (action == 'createRemote' || action == 'updateRemote') {
// Make the operation atomic by doing the work on a copy of the file
// and then copying it back to the original location.
// let tempPath = this.syncDirName_ + '/' + path + '_' + time.unixMs();
//
// Atomic operation is disabled for now because it's not possible
// to do an atomic move with OneDrive (see file-api-driver-onedrive.js)
// await this.api().put(tempPath, content);
// await this.api().setTimestamp(tempPath, local.updated_time);
// await this.api().move(tempPath, path);
let canSync = true; let canSync = true;
try { try {
if (this.testingHooks_.indexOf('rejectedByTarget') >= 0) throw new JoplinError('Testing rejectedByTarget', 'rejectedByTarget'); if (this.testingHooks_.indexOf('rejectedByTarget') >= 0) throw new JoplinError('Testing rejectedByTarget', 'rejectedByTarget');