You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-11-26 22:41:17 +02:00
All: Improved WebDAV driver compatibility with some services (eg. Seafile)
This commit is contained in:
@@ -244,7 +244,7 @@ The following commands are available in [command-line mode](#command-line-mode):
|
||||
Possible values: HH:mm (20:30), h:mm A (8:30 PM).
|
||||
Default: "HH:mm"
|
||||
|
||||
uncompletedTodosOnTop Show uncompleted todos on top of the lists.
|
||||
uncompletedTodosOnTop Show uncompleted to-dos on top of the lists.
|
||||
Type: bool.
|
||||
Default: true
|
||||
|
||||
|
||||
@@ -133,6 +133,42 @@ class WebDavApi {
|
||||
return this.valueFromJson(json, keys, 'array');
|
||||
}
|
||||
|
||||
resourcePropByName(resource, outputType, propName) {
|
||||
const propStats = resource['d:propstat'];
|
||||
let output = null;
|
||||
if (!Array.isArray(propStats)) throw new Error('Missing d:propstat property');
|
||||
for (let i = 0; i < propStats.length; i++) {
|
||||
const props = propStats[i]['d:prop'];
|
||||
if (!Array.isArray(props) || !props.length) continue;
|
||||
const prop = props[0];
|
||||
if (Array.isArray(prop[propName])) {
|
||||
output = prop[propName];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (outputType === 'string') {
|
||||
// If the XML has not attribute the value is directly a string
|
||||
// If the XML node has attributes, the value is under "_".
|
||||
// Eg for this XML, the string will be under {"_":"Thu, 01 Feb 2018 17:24:05 GMT"}:
|
||||
// <a:getlastmodified b:dt="dateTime.rfc1123">Thu, 01 Feb 2018 17:24:05 GMT</a:getlastmodified>
|
||||
// For this XML, the value will be "Thu, 01 Feb 2018 17:24:05 GMT"
|
||||
// <a:getlastmodified>Thu, 01 Feb 2018 17:24:05 GMT</a:getlastmodified>
|
||||
|
||||
output = output[0];
|
||||
|
||||
if (typeof output === 'object' && '_' in output) output = output['_'];
|
||||
if (typeof output !== 'string') return null;
|
||||
return output;
|
||||
}
|
||||
|
||||
if (outputType === 'array') {
|
||||
return output;
|
||||
}
|
||||
|
||||
throw new Error('Invalid output type: ' + outputType);
|
||||
}
|
||||
|
||||
async execPropFind(path, depth, fields = null, options = null) {
|
||||
if (fields === null) fields = ['d:getlastmodified'];
|
||||
|
||||
|
||||
@@ -65,6 +65,11 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
||||
fontSize: theme.fontSize,
|
||||
flex: 1,
|
||||
},
|
||||
descriptionText: {
|
||||
color: theme.color,
|
||||
fontSize: theme.fontSize,
|
||||
flex: 1,
|
||||
},
|
||||
settingControl: {
|
||||
color: theme.color,
|
||||
flex: 1,
|
||||
@@ -181,8 +186,8 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
||||
const messages = shared.checkSyncConfigMessages(this);
|
||||
const statusComp = !messages.length ? null : (
|
||||
<View style={{flex:1, marginTop: 10}}>
|
||||
<Text>{messages[0]}</Text>
|
||||
{messages.length >= 1 ? (<Text style={{marginTop:10}}>{messages[1]}</Text>) : null}
|
||||
<Text style={this.styles().descriptionText}>{messages[0]}</Text>
|
||||
{messages.length >= 1 ? (<View style={{marginTop:10}}><Text style={this.styles().descriptionText}>{messages[1]}</Text></View>) : null}
|
||||
</View>);
|
||||
|
||||
settingComps.push(
|
||||
|
||||
@@ -27,7 +27,7 @@ class FileApiDriverWebDav {
|
||||
const result = await this.api().execPropFind(path, 0, [
|
||||
'd:getlastmodified',
|
||||
'd:resourcetype',
|
||||
'd:getcontentlength', // Remove this once PUT call issue is sorted out
|
||||
// 'd:getcontentlength', // Remove this once PUT call issue is sorted out
|
||||
]);
|
||||
|
||||
const resource = this.api().objectFromJson(result, ['d:multistatus', 'd:response', 0]);
|
||||
@@ -39,22 +39,40 @@ class FileApiDriverWebDav {
|
||||
}
|
||||
|
||||
statFromResource_(resource, path) {
|
||||
const isCollection = this.api().stringFromJson(resource, ['d:propstat', 0, 'd:prop', 0, 'd:resourcetype', 0, 'd:collection', 0]);
|
||||
const lastModifiedString = this.api().stringFromJson(resource, ['d:propstat', 0, 'd:prop', 0, 'd:getlastmodified', 0]);
|
||||
// WebDAV implementations are always slighly different from one server to another but, at the minimum,
|
||||
// a resource should have a propstat key - if not it's probably an error.
|
||||
const propStat = this.api().arrayFromJson(resource, ['d:propstat']);
|
||||
if (!Array.isArray(propStat)) throw new Error('Invalid WebDAV resource format: ' + JSON.stringify(resource));
|
||||
|
||||
const resourceTypes = this.api().resourcePropByName(resource, 'array', 'd:resourcetype');
|
||||
let isDir = false;
|
||||
if (Array.isArray(resourceTypes)) {
|
||||
for (let i = 0; i < resourceTypes.length; i++) {
|
||||
const t = resourceTypes[i];
|
||||
if (typeof t === 'object' && 'd:collection' in t) {
|
||||
isDir = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const lastModifiedString = this.api().resourcePropByName(resource, 'string', 'd:getlastmodified');
|
||||
|
||||
// const sizeDONOTUSE = Number(this.api().stringFromJson(resource, ['d:propstat', 0, 'd:prop', 0, 'd:getcontentlength', 0]));
|
||||
// if (isNaN(sizeDONOTUSE)) throw new Error('Cannot get content size: ' + JSON.stringify(resource));
|
||||
|
||||
if (!lastModifiedString) throw new Error('Could not get lastModified date: ' + JSON.stringify(resource));
|
||||
|
||||
const lastModifiedDate = new Date(lastModifiedString);
|
||||
// Note: Not all WebDAV servers return a getlastmodified date (eg. Seafile, which doesn't return the
|
||||
// property for folders) so we can only throw an error if it's a file.
|
||||
if (!lastModifiedString && !isDir) throw new Error('Could not get lastModified date for resource: ' + JSON.stringify(resource));
|
||||
const lastModifiedDate = lastModifiedString ? new Date(lastModifiedString) : new Date();
|
||||
if (isNaN(lastModifiedDate.getTime())) throw new Error('Invalid date: ' + lastModifiedString);
|
||||
|
||||
return {
|
||||
path: path,
|
||||
// created_time: lastModifiedDate.getTime(),
|
||||
updated_time: lastModifiedDate.getTime(),
|
||||
isDir: isCollection === '',
|
||||
isDir: isDir,
|
||||
// sizeDONOTUSE: sizeDONOTUSE, // This property is used only for the WebDAV PUT hack (see below) so mark it as such so that it can be removed with the hack later on.
|
||||
};
|
||||
}
|
||||
@@ -260,7 +278,7 @@ class FileApiDriverWebDav {
|
||||
]);
|
||||
|
||||
const resources = this.api().arrayFromJson(result, ['d:multistatus', 'd:response']);
|
||||
const stats = this.statsFromResources_(resources)
|
||||
const stats = this.statsFromResources_(resources);
|
||||
|
||||
return {
|
||||
items: stats,
|
||||
|
||||
@@ -58,7 +58,7 @@ class Setting extends BaseModel {
|
||||
// recent: _('Non-completed and recently completed ones'),
|
||||
// nonCompleted: _('Non-completed ones only'),
|
||||
// })},
|
||||
'uncompletedTodosOnTop': { value: true, type: Setting.TYPE_BOOL, public: true, label: () => _('Show uncompleted todos on top of the lists') },
|
||||
'uncompletedTodosOnTop': { value: true, type: Setting.TYPE_BOOL, public: true, label: () => _('Show uncompleted to-dos on top of the lists') },
|
||||
'trackLocation': { value: true, type: Setting.TYPE_BOOL, public: true, label: () => _('Save geo-location with notes') },
|
||||
'newTodoFocus': { value: 'title', type: Setting.TYPE_STRING, isEnum: true, public: true, appTypes: ['desktop'], label: () => _('When creating a new to-do:'), options: () => {
|
||||
return {
|
||||
|
||||
@@ -391,14 +391,14 @@ $$
|
||||
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/hr.png" alt=""></td>
|
||||
<td>Croatian</td>
|
||||
<td>hr_HR</td>
|
||||
<td>Hrvoje Mandić <a href="mailto:trbuhom@net.hr">trbuhom@net.hr</a></td>
|
||||
<td>Hrvoje Mandić <a href="mailto:trbuhom@net.hr">trbuhom@net.hr</a></td>
|
||||
<td>72%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/de.png" alt=""></td>
|
||||
<td>Deutsch</td>
|
||||
<td>de_DE</td>
|
||||
<td>Tobias Strobel <a href="mailto:git@strobeltobias.de">git@strobeltobias.de</a></td>
|
||||
<td>Tobias Strobel <a href="mailto:git@strobeltobias.de">git@strobeltobias.de</a></td>
|
||||
<td>91%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -454,14 +454,14 @@ $$
|
||||
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/ru.png" alt=""></td>
|
||||
<td>Русский</td>
|
||||
<td>ru_RU</td>
|
||||
<td>Artyom Karlov <a href="mailto:artyom.karlov@gmail.com">artyom.karlov@gmail.com</a></td>
|
||||
<td>Artyom Karlov <a href="mailto:artyom.karlov@gmail.com">artyom.karlov@gmail.com</a></td>
|
||||
<td>94%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/cn.png" alt=""></td>
|
||||
<td>中文 (简体)</td>
|
||||
<td>zh_CN</td>
|
||||
<td>RCJacH <a href="mailto:RCJacH@outlook.com">RCJacH@outlook.com</a></td>
|
||||
<td>RCJacH <a href="mailto:RCJacH@outlook.com">RCJacH@outlook.com</a></td>
|
||||
<td>75%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -395,7 +395,7 @@ Possible keys/values:
|
||||
Possible values: HH:mm (20:30), h:mm A (8:30 PM).
|
||||
Default: "HH:mm"
|
||||
|
||||
uncompletedTodosOnTop Show uncompleted todos on top of the lists.
|
||||
uncompletedTodosOnTop Show uncompleted to-dos on top of the lists.
|
||||
Type: bool.
|
||||
Default: true
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"*.min.js",
|
||||
"ElectronClient/app/gui/note-viewer/highlight/*.pack.js",
|
||||
"ElectronClient/app/css/font-awesome.min.css",
|
||||
"docs/*.html",
|
||||
"docs/*.svg",
|
||||
],
|
||||
"folder_exclude_patterns":
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user