Browse Source

feat: add webdav sync service

Gerald 6 years ago
parent
commit
f16ebfdc36

+ 12 - 0
src/_locales/cs/messages.yml

@@ -332,6 +332,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: ''
   message: ''
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: ''
   message: ''
@@ -341,6 +344,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: ''
   message: ''
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: ''
   message: ''
@@ -351,9 +357,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: ''
   message: ''
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: ''
   message: ''
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Překladatel: '
   message: 'Překladatel: '

+ 12 - 0
src/_locales/de/messages.yml

@@ -334,6 +334,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Sync
   message: Sync
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Autorisieren
   message: Autorisieren
@@ -343,6 +346,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: Keines
   message: Keines
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: Abgelaufene Autorisierung automatisch erneuern
   message: Abgelaufene Autorisierung automatisch erneuern
@@ -353,9 +359,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Skriptstatus-Sync
   message: Skriptstatus-Sync
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Sync mit
   message: Sync mit
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Übersetzer: '
   message: 'Übersetzer: '

+ 12 - 0
src/_locales/el/messages.yml

@@ -338,6 +338,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Συγχρονισμός
   message: Συγχρονισμός
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Εξουσιοδότησε
   message: Εξουσιοδότησε
@@ -347,6 +350,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: Κανένας
   message: Κανένας
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: Αυτόματη επανεξουσιοδότηση όταν λήξει
   message: Αυτόματη επανεξουσιοδότηση όταν λήξει
@@ -357,9 +363,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Κατάσταση συγχρονισμού σεναρίου
   message: Κατάσταση συγχρονισμού σεναρίου
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Συγχρονισμός σε
   message: Συγχρονισμός σε
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Μεταφραστής: '
   message: 'Μεταφραστής: '

+ 12 - 0
src/_locales/en/messages.yml

@@ -332,6 +332,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Sync
   message: Sync
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: Use anonymous account
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Authorize
   message: Authorize
@@ -341,6 +344,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: None
   message: None
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: 'Password: '
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: Reauthorize automatically when expired
   message: Reauthorize automatically when expired
@@ -351,9 +357,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Sync script status
   message: Sync script status
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: 'Server URL: '
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Sync to
   message: Sync to
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: 'Username: '
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Translator: '
   message: 'Translator: '

+ 12 - 0
src/_locales/es/messages.yml

@@ -338,6 +338,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Sincronizar
   message: Sincronizar
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Autorizar
   message: Autorizar
@@ -347,6 +350,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: Ninguna
   message: Ninguna
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: ''
   message: ''
@@ -357,9 +363,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Estado de la sincronización de scripts
   message: Estado de la sincronización de scripts
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Sincronizar a
   message: Sincronizar a
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Traductor: '
   message: 'Traductor: '

+ 12 - 0
src/_locales/fi/messages.yml

@@ -332,6 +332,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Synkronointi
   message: Synkronointi
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Anna lupa
   message: Anna lupa
@@ -341,6 +344,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: Ei mihinkään
   message: Ei mihinkään
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: Uudista lupa automaattisesti sen vanhettua
   message: Uudista lupa automaattisesti sen vanhettua
@@ -351,9 +357,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Synkronoi skriptien tila
   message: Synkronoi skriptien tila
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Synkronoi
   message: Synkronoi
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Kääntäjä: '
   message: 'Kääntäjä: '

+ 12 - 0
src/_locales/fr/messages.yml

@@ -339,6 +339,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Synchronisation
   message: Synchronisation
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Autoriser
   message: Autoriser
@@ -348,6 +351,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: Aucun
   message: Aucun
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: Réautoriser automatiquement à l’expiration
   message: Réautoriser automatiquement à l’expiration
@@ -358,9 +364,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Synchroniser le statut du script
   message: Synchroniser le statut du script
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Synchroniser sur
   message: Synchroniser sur
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Traducteur·ice·s: '
   message: 'Traducteur·ice·s: '

+ 12 - 0
src/_locales/hr/messages.yml

@@ -332,6 +332,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Sinkronizacija
   message: Sinkronizacija
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Autoriziraj
   message: Autoriziraj
@@ -341,6 +344,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: Nijedan
   message: Nijedan
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: ''
   message: ''
@@ -351,9 +357,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Sync script status
   message: Sync script status
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Sinkroniziraj na
   message: Sinkroniziraj na
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Prevoditelj: '
   message: 'Prevoditelj: '

+ 12 - 0
src/_locales/id/messages.yml

@@ -334,6 +334,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Sinkron
   message: Sinkron
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Izinkan
   message: Izinkan
@@ -343,6 +346,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: Tanpa
   message: Tanpa
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: ''
   message: ''
@@ -353,9 +359,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Status sinkron skrip
   message: Status sinkron skrip
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Sinkron ke
   message: Sinkron ke
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Penerjemah: '
   message: 'Penerjemah: '

+ 12 - 0
src/_locales/it/messages.yml

@@ -332,6 +332,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Sincronizzazione
   message: Sincronizzazione
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Autorizza
   message: Autorizza
@@ -341,6 +344,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: Nessuno
   message: Nessuno
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: ''
   message: ''
@@ -351,9 +357,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Sincronizza lo stato degli script
   message: Sincronizza lo stato degli script
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Sincronizza con
   message: Sincronizza con
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Traduttore: '
   message: 'Traduttore: '

+ 12 - 0
src/_locales/ja/messages.yml

@@ -333,6 +333,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: 同期
   message: 同期
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: アクセス許可
   message: アクセス許可
@@ -342,6 +345,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: なし
   message: なし
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: 期限が切れたとき、自動的に再認証する
   message: 期限が切れたとき、自動的に再認証する
@@ -352,9 +358,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: スクリプトのステータスも同期する
   message: スクリプトのステータスも同期する
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: '同期に利用するサービス:'
   message: '同期に利用するサービス:'
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: '翻訳者: '
   message: '翻訳者: '

+ 12 - 0
src/_locales/ko/messages.yml

@@ -330,6 +330,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: 동기화
   message: 동기화
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: 인증
   message: 인증
@@ -339,6 +342,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: 없음
   message: 없음
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: 만료시 자동으로 재인증
   message: 만료시 자동으로 재인증
@@ -349,9 +355,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: 스크립트 동기화 상태
   message: 스크립트 동기화 상태
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: 동기화
   message: 동기화
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: '번역자: '
   message: '번역자: '

+ 12 - 0
src/_locales/pl/messages.yml

@@ -337,6 +337,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Synchronizacja
   message: Synchronizacja
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Autoryzuj
   message: Autoryzuj
@@ -346,6 +349,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: Żaden
   message: Żaden
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: Automatycznie reautoryzuj po wygaśnięciu
   message: Automatycznie reautoryzuj po wygaśnięciu
@@ -356,9 +362,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Synchronizuj stan skryptu
   message: Synchronizuj stan skryptu
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Synchronizuj z
   message: Synchronizuj z
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Tłumacze: '
   message: 'Tłumacze: '

+ 12 - 0
src/_locales/pt_BR/messages.yml

@@ -334,6 +334,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Sincronizar
   message: Sincronizar
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Autorizar
   message: Autorizar
@@ -343,6 +346,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: Nenhum
   message: Nenhum
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: Reautorizar automaticamente quando expirar
   message: Reautorizar automaticamente quando expirar
@@ -353,9 +359,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Sincronizar estados dos scripts
   message: Sincronizar estados dos scripts
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Sincronizar em
   message: Sincronizar em
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Tradução: '
   message: 'Tradução: '

+ 12 - 0
src/_locales/pt_PT/messages.yml

@@ -334,6 +334,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Sincronizar
   message: Sincronizar
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Autorizar
   message: Autorizar
@@ -343,6 +346,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: Nenhum
   message: Nenhum
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: Reautorizar automaticamente quando expirar
   message: Reautorizar automaticamente quando expirar
@@ -353,9 +359,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Sincronizar estados dos scripts
   message: Sincronizar estados dos scripts
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Sincronizar em
   message: Sincronizar em
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Tradução: '
   message: 'Tradução: '

+ 12 - 0
src/_locales/ro/messages.yml

@@ -337,6 +337,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Sincronizare
   message: Sincronizare
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Autorizează
   message: Autorizează
@@ -346,6 +349,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: Niciunul
   message: Niciunul
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: Reautorizează automat la expirare
   message: Reautorizează automat la expirare
@@ -356,9 +362,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Sincronizează starea scripturilor
   message: Sincronizează starea scripturilor
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Sincronizează cu
   message: Sincronizează cu
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Traducător: '
   message: 'Traducător: '

+ 12 - 0
src/_locales/ru/messages.yml

@@ -339,6 +339,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Синхронизация
   message: Синхронизация
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Подключить
   message: Подключить
@@ -348,6 +351,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: ничем
   message: ничем
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: Повторная автоматическая авторизация по истечении срока действия
   message: Повторная автоматическая авторизация по истечении срока действия
@@ -358,9 +364,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Синхронизировать статус скриптов.
   message: Синхронизировать статус скриптов.
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Синхронизировать с
   message: Синхронизировать с
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Переводчики: '
   message: 'Переводчики: '

+ 12 - 0
src/_locales/sk/messages.yml

@@ -334,6 +334,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Synchronizácia
   message: Synchronizácia
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Autorizácia
   message: Autorizácia
@@ -343,6 +346,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: Zakázané
   message: Zakázané
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: Po exsiprovaní automaticky reautorizovať
   message: Po exsiprovaní automaticky reautorizovať
@@ -353,9 +359,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Stav synchronizácie skriptu
   message: Stav synchronizácie skriptu
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Synchronizovať na
   message: Synchronizovať na
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Prekladateľ: '
   message: 'Prekladateľ: '

+ 12 - 0
src/_locales/sr/messages.yml

@@ -332,6 +332,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Синхронизација
   message: Синхронизација
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: ''
   message: ''
@@ -341,6 +344,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: ''
   message: ''
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: ''
   message: ''
@@ -351,9 +357,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: ''
   message: ''
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: ''
   message: ''
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Преводилац:'
   message: 'Преводилац:'

+ 12 - 0
src/_locales/tr/messages.yml

@@ -334,6 +334,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Eşitle
   message: Eşitle
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Yetkilendirme
   message: Yetkilendirme
@@ -343,6 +346,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: Yok
   message: Yok
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: ''
   message: ''
@@ -353,9 +359,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Eşitleme script durumu
   message: Eşitleme script durumu
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Eşitleyen
   message: Eşitleyen
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Çeviren: '
   message: 'Çeviren: '

+ 12 - 0
src/_locales/uk/messages.yml

@@ -336,6 +336,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Синхронізація з хмарним сховищем
   message: Синхронізація з хмарним сховищем
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Підключити
   message: Підключити
@@ -345,6 +348,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: нічим
   message: нічим
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: ''
   message: ''
@@ -355,9 +361,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Синхронізувати статус скриптів.
   message: Синхронізувати статус скриптів.
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Синхронізувати з
   message: Синхронізувати з
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Перекладачі: '
   message: 'Перекладачі: '

+ 12 - 0
src/_locales/vi/messages.yml

@@ -332,6 +332,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: Đồng bộ
   message: Đồng bộ
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: Xác thực
   message: Xác thực
@@ -341,6 +344,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: Không có
   message: Không có
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: Tự động xác thực lại khi hết hạn
   message: Tự động xác thực lại khi hết hạn
@@ -351,9 +357,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: Trạng thái đồng bộ script
   message: Trạng thái đồng bộ script
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: Đồng bộ tới
   message: Đồng bộ tới
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 'Dịch bởi: '
   message: 'Dịch bởi: '

+ 12 - 0
src/_locales/zh_CN/messages.yml

@@ -330,6 +330,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: 同步
   message: 同步
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: 使用匿名账号
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: 授权
   message: 授权
@@ -339,6 +342,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: 无
   message: 无
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: 密码:
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: 授权失效后自动重新授权
   message: 授权失效后自动重新授权
@@ -349,9 +355,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: 同步脚本状态
   message: 同步脚本状态
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: 服务器地址:
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: 同步到
   message: 同步到
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: 用户名:
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 翻译者:
   message: 翻译者:

+ 12 - 0
src/_locales/zh_TW/messages.yml

@@ -330,6 +330,9 @@ labelShowBadge:
 labelSync:
 labelSync:
   description: Label for sync options.
   description: Label for sync options.
   message: 同步
   message: 同步
+labelSyncAnonymous:
+  description: Label for using anonymous account.
+  message: ''
 labelSyncAuthorize:
 labelSyncAuthorize:
   description: Label for button to authorize a service.
   description: Label for button to authorize a service.
   message: 授權
   message: 授權
@@ -339,6 +342,9 @@ labelSyncAuthorizing:
 labelSyncDisabled:
 labelSyncDisabled:
   description: Label for option to disable sync service.
   description: Label for option to disable sync service.
   message: 無
   message: 無
+labelSyncPassword:
+  description: Label for input to hold password.
+  message: ''
 labelSyncReauthorize:
 labelSyncReauthorize:
   description: Option to reauthorize sync service when expired.
   description: Option to reauthorize sync service when expired.
   message: ''
   message: ''
@@ -349,9 +355,15 @@ labelSyncRevoke:
 labelSyncScriptStatus:
 labelSyncScriptStatus:
   description: Label for option to sync script status.
   description: Label for option to sync script status.
   message: 同步腳本狀態
   message: 同步腳本狀態
+labelSyncServerUrl:
+  description: Label for input to hold server URL.
+  message: ''
 labelSyncService:
 labelSyncService:
   description: Label for sync service select.
   description: Label for sync service select.
   message: 同步到
   message: 同步到
+labelSyncUsername:
+  description: Label for input to hold username.
+  message: ''
 labelTranslator:
 labelTranslator:
   description: Label of translator.
   description: Label of translator.
   message: 翻譯者:
   message: 翻譯者:

+ 1 - 0
src/background/index.js

@@ -188,6 +188,7 @@ const commands = {
   SyncAuthorize: sync.authorize,
   SyncAuthorize: sync.authorize,
   SyncRevoke: sync.revoke,
   SyncRevoke: sync.revoke,
   SyncStart: sync.sync,
   SyncStart: sync.sync,
+  SyncSetConfig: sync.setConfig,
   CacheLoad(data) {
   CacheLoad(data) {
     return cache.get(data) || null;
     return cache.get(data) || null;
   },
   },

+ 49 - 13
src/background/sync/base.js

@@ -1,5 +1,6 @@
 import {
 import {
   debounce, normalizeKeys, request, noop,
   debounce, normalizeKeys, request, noop,
+  encodeFilename, decodeFilename,
 } from '#/common';
 } from '#/common';
 import {
 import {
   objectGet, objectSet, objectPick, objectPurify,
   objectGet, objectSet, objectPick, objectPurify,
@@ -20,17 +21,28 @@ const autoSync = debounce(sync, 60 * 60 * 1000);
 let working = Promise.resolve();
 let working = Promise.resolve();
 let syncConfig;
 let syncConfig;
 
 
-export function getItemFilename({ name: filename, uri }) {
-  return uri ? getFilename(uri) : filename;
+export function getItemFilename({ name, uri }) {
+  // When get or remove, current name should be prefered
+  // otherwise uri derived name should be prefered
+  return name || getFilename(uri);
 }
 }
 export function getFilename(uri) {
 export function getFilename(uri) {
-  return `vm-${encodeURIComponent(uri)}`;
+  return `vm@2-${encodeFilename(uri)}`;
 }
 }
 export function isScriptFile(name) {
 export function isScriptFile(name) {
-  return /^vm-/.test(name);
+  return /^vm(?:@\d+)?-/.test(name);
 }
 }
 export function getURI(name) {
 export function getURI(name) {
-  return decodeURIComponent(name.slice(3));
+  const i = name.indexOf('-');
+  const [, version] = name.slice(0, i).split('@');
+  if (version === '2') {
+    return decodeFilename(name.slice(i + 1));
+  }
+  try {
+    return decodeURIComponent(name.slice(3));
+  } catch (err) {
+    return name.slice(3);
+  }
 }
 }
 
 
 function initConfig() {
 function initConfig() {
@@ -110,6 +122,8 @@ export function getStates() {
       syncState: service.syncState.get(),
       syncState: service.syncState.get(),
       lastSync: service.config.get('meta', {}).lastSync,
       lastSync: service.config.get('meta', {}).lastSync,
       progress: service.progress,
       progress: service.progress,
+      properties: service.properties,
+      userConfig: service.getUserConfig(),
     };
     };
   });
   });
 }
 }
@@ -188,6 +202,11 @@ export const BaseService = serviceFactory({
   delayTime: 1000,
   delayTime: 1000,
   urlPrefix: '',
   urlPrefix: '',
   metaFile: 'Violentmonkey',
   metaFile: 'Violentmonkey',
+  properties: {
+    authType: 'oauth',
+  },
+  getUserConfig: noop,
+  setUserConfig: noop,
   initialize() {
   initialize() {
     this.progress = {
     this.progress = {
       finished: 0,
       finished: 0,
@@ -275,13 +294,16 @@ export const BaseService = serviceFactory({
     .then(() => this.startSync());
     .then(() => this.startSync());
   },
   },
   user: noop,
   user: noop,
+  acquireLock: noop,
+  releaseLock: noop,
   handleMetaError(err) {
   handleMetaError(err) {
     throw err;
     throw err;
   },
   },
   getMeta() {
   getMeta() {
     return this.get({ name: this.metaFile })
     return this.get({ name: this.metaFile })
     .then(data => JSON.parse(data))
     .then(data => JSON.parse(data))
-    .catch(err => this.handleMetaError(err));
+    .catch(err => this.handleMetaError(err))
+    .then(data => data || {});
   },
   },
   initToken() {
   initToken() {
     this.prepareHeaders();
     this.prepareHeaders();
@@ -352,6 +374,7 @@ export const BaseService = serviceFactory({
     // Avoid simultaneous requests
     // Avoid simultaneous requests
     return this.prepare()
     return this.prepare()
     .then(() => this.getSyncData())
     .then(() => this.getSyncData())
+    .then(data => Promise.resolve(this.acquireLock()).then(() => data))
     .then(([remoteMeta, remoteData, localData]) => {
     .then(([remoteMeta, remoteData, localData]) => {
       const { data: remoteMetaData } = remoteMeta;
       const { data: remoteMetaData } = remoteMeta;
       const remoteMetaInfo = remoteMetaData.info || {};
       const remoteMetaInfo = remoteMetaData.info || {};
@@ -425,7 +448,7 @@ export const BaseService = serviceFactory({
       });
       });
       const promiseQueue = [
       const promiseQueue = [
         ...putLocal.map(({ remote, info }) => {
         ...putLocal.map(({ remote, info }) => {
-          this.log('Download script:', remote.uri);
+          this.log('Download script:', getFilename(remote.uri));
           return this.get(remote)
           return this.get(remote)
           .then(raw => {
           .then(raw => {
             const data = parseScriptData(raw);
             const data = parseScriptData(raw);
@@ -441,7 +464,7 @@ export const BaseService = serviceFactory({
           });
           });
         }),
         }),
         ...putRemote.map(({ local, remote }) => {
         ...putRemote.map(({ local, remote }) => {
-          this.log('Upload script:', local.props.uri);
+          this.log('Upload script:', getFilename(local.props.uri));
           return pluginScript.get(local.props.id)
           return pluginScript.get(local.props.id)
           .then(code => {
           .then(code => {
             // XXX use version 1 to be compatible with Violentmonkey on other platforms
             // XXX use version 1 to be compatible with Violentmonkey on other platforms
@@ -452,19 +475,22 @@ export const BaseService = serviceFactory({
             };
             };
             remoteChanged = true;
             remoteChanged = true;
             return this.put(
             return this.put(
-              Object.assign({}, remote, { uri: local.props.uri }),
+              Object.assign({}, remote, {
+                uri: local.props.uri,
+                name: null, // prefer using uri on PUT
+              }),
               JSON.stringify(data),
               JSON.stringify(data),
             );
             );
           });
           });
         }),
         }),
         ...delRemote.map(({ remote }) => {
         ...delRemote.map(({ remote }) => {
-          this.log('Remove remote script:', remote.uri);
+          this.log('Remove remote script:', getFilename(remote.uri));
           delete remoteMetaData.info[remote.uri];
           delete remoteMetaData.info[remote.uri];
           remoteChanged = true;
           remoteChanged = true;
           return this.remove(remote);
           return this.remove(remote);
         }),
         }),
         ...delLocal.map(({ local }) => {
         ...delLocal.map(({ local }) => {
-          this.log('Remove local script:', local.props.uri);
+          this.log('Remove local script:', getFilename(local.props.uri));
           return pluginScript.remove(local.props.id);
           return pluginScript.remove(local.props.id);
         }),
         }),
         ...updateLocal.map(({ local, info }) => {
         ...updateLocal.map(({ local, info }) => {
@@ -504,11 +530,13 @@ export const BaseService = serviceFactory({
     })
     })
     .then(() => {
     .then(() => {
       this.syncState.set('idle');
       this.syncState.set('idle');
+      this.log('Sync finished:', this.displayName);
     }, err => {
     }, err => {
       this.syncState.set('error');
       this.syncState.set('error');
-      this.log('Failed syncing:', this.name);
+      this.log('Failed syncing:', this.displayName);
       this.log(err);
       this.log(err);
-    });
+    })
+    .then(() => Promise.resolve(this.releaseLock()).catch(noop));
   },
   },
 });
 });
 
 
@@ -562,6 +590,14 @@ export function revoke() {
   if (service) service.revoke();
   if (service) service.revoke();
 }
 }
 
 
+export function setConfig(config) {
+  const service = getService();
+  if (service) {
+    service.setUserConfig(config);
+    service.checkSync();
+  }
+}
+
 hookOptions(data => {
 hookOptions(data => {
   const value = objectGet(data, 'sync.current');
   const value = objectGet(data, 'sync.current');
   if (value) initialize();
   if (value) initialize();

+ 2 - 2
src/background/sync/dropbox.js

@@ -29,8 +29,7 @@ const Dropbox = BaseService.extend({
     });
     });
   },
   },
   handleMetaError(res) {
   handleMetaError(res) {
-    if (res.status === 409) return {};
-    throw res;
+    if (res.status !== 409) throw res;
   },
   },
   list() {
   list() {
     return this.loadData({
     return this.loadData({
@@ -124,6 +123,7 @@ register(Dropbox);
 
 
 function normalize(item) {
 function normalize(item) {
   return {
   return {
+    name: item.name,
     size: item.size,
     size: item.size,
     uri: getURI(item.name),
     uri: getURI(item.name),
     // modified: new Date(item.server_modified).getTime(),
     // modified: new Date(item.server_modified).getTime(),

+ 3 - 4
src/background/sync/googledrive.js

@@ -1,7 +1,7 @@
 // Reference:
 // Reference:
 // - https://developers.google.com/drive/v3/reference/files
 // - https://developers.google.com/drive/v3/reference/files
 // - https://github.com/google/google-api-nodejs-client
 // - https://github.com/google/google-api-nodejs-client
-import { getUniqId } from '#/common';
+import { getUniqId, noop } from '#/common';
 import { objectGet } from '#/common/object';
 import { objectGet } from '#/common/object';
 import { dumpQuery, notify } from '../utils';
 import { dumpQuery, notify } from '../utils';
 import {
 import {
@@ -168,9 +168,7 @@ const GoogleDrive = BaseService.extend({
       }
       }
     });
     });
   },
   },
-  handleMetaError() {
-    return {};
-  },
+  handleMetaError: noop,
   list() {
   list() {
     throw new Error('Not supported');
     throw new Error('Not supported');
   },
   },
@@ -227,6 +225,7 @@ register(GoogleDrive);
 function normalize(item) {
 function normalize(item) {
   return {
   return {
     id: item.id,
     id: item.id,
+    name: item.name,
     size: +item.size,
     size: +item.size,
     uri: getURI(item.name),
     uri: getURI(item.name),
   };
   };

+ 9 - 1
src/background/sync/index.js

@@ -1,9 +1,16 @@
 import {
 import {
-  checkAuthUrl, initialize, sync, getStates, authorize, revoke,
+  checkAuthUrl,
+  initialize,
+  sync,
+  getStates,
+  authorize,
+  revoke,
+  setConfig,
 } from './base';
 } from './base';
 import './dropbox';
 import './dropbox';
 import './onedrive';
 import './onedrive';
 import './googledrive';
 import './googledrive';
+import './webdav';
 
 
 browser.tabs.onUpdated.addListener((tabId, changes) => {
 browser.tabs.onUpdated.addListener((tabId, changes) => {
   if (changes.url && checkAuthUrl(changes.url)) browser.tabs.remove(tabId);
   if (changes.url && checkAuthUrl(changes.url)) browser.tabs.remove(tabId);
@@ -15,4 +22,5 @@ export {
   getStates,
   getStates,
   authorize,
   authorize,
   revoke,
   revoke,
+  setConfig,
 };
 };

+ 2 - 1
src/background/sync/onedrive.js

@@ -54,7 +54,7 @@ const OneDrive = BaseService.extend({
       if (/^Bearer realm="OneDriveAPI"/.test(header)) {
       if (/^Bearer realm="OneDriveAPI"/.test(header)) {
         return this.refreshToken().then(() => this.getMeta());
         return this.refreshToken().then(() => this.getMeta());
       }
       }
-      return {};
+      return;
     }
     }
     throw res;
     throw res;
   },
   },
@@ -160,6 +160,7 @@ register(OneDrive);
 
 
 function normalize(item) {
 function normalize(item) {
   return {
   return {
+    name: item.name,
     size: item.size,
     size: item.size,
     uri: getURI(item.name),
     uri: getURI(item.name),
     // modified: new Date(item.lastModifiedDateTime).getTime(),
     // modified: new Date(item.lastModifiedDateTime).getTime(),

+ 301 - 0
src/background/sync/webdav.js

@@ -0,0 +1,301 @@
+import {
+  getURI, getItemFilename, BaseService, isScriptFile, register,
+} from './base';
+
+const KEY_CHILDREN = Symbol('children');
+
+class XNode {
+  constructor(node, nsMap) {
+    this.node = node;
+    this.nsMap = { ...nsMap };
+    this.parseAttrs();
+    this.parseName();
+  }
+
+  static fromXML(xml) {
+    const parser = new DOMParser();
+    const doc = parser.parseFromString(xml, 'application/xml');
+    return new XNode(doc);
+  }
+
+  parseAttrs() {
+    const { node, nsMap } = this;
+    const attrs = {};
+    const { attributes } = node;
+    if (attributes) {
+      for (const attr of node.attributes) {
+        const { name, value } = attr;
+        if (name === 'xmlns') nsMap.$ = value;
+        else if (name.startsWith('xmlns:')) nsMap[name.slice(6)] = value;
+        attrs[name] = value;
+      }
+    }
+    this.attrs = attrs;
+  }
+
+  parseName() {
+    const { node, nsMap } = this;
+    if (node.nodeType === 1) {
+      let name = node.tagName;
+      let ns = nsMap.$;
+      if (name.includes(':')) {
+        let prefix;
+        [prefix, name] = name.split(':');
+        ns = nsMap[prefix];
+        if (!ns) throw new Error(`Unknown namespace: ${prefix}`);
+      }
+      this.name = ns + name;
+    }
+  }
+
+  text() {
+    const { node } = this;
+    if (node) return (node.textContent || '').trim();
+  }
+
+  children() {
+    if (!this[KEY_CHILDREN]) {
+      const { node, nsMap } = this;
+      this[KEY_CHILDREN] = [...node.children]
+      .map(child => new XNode(child, nsMap));
+    }
+    return this[KEY_CHILDREN];
+  }
+
+  map(callback) {
+    return this.children().map(callback);
+  }
+
+  getCallback(callback) {
+    if (typeof callback === 'string') {
+      return (tagName => node => node.name === tagName)(callback);
+    }
+    return callback;
+  }
+
+  filter(callback) {
+    return this.children().filter(this.getCallback(callback));
+  }
+
+  find(callback) {
+    return this.children().find(this.getCallback(callback));
+  }
+
+  attr(key) {
+    return this.attrs[key];
+  }
+}
+
+const DEFAULT_CONFIG = {
+  serverUrl: '',
+  anonymous: false,
+  username: '',
+  password: '',
+};
+
+const WebDAV = BaseService.extend({
+  name: 'webdav',
+  displayName: 'WebDAV',
+  properties: {
+    authType: 'password',
+    serverUrl: null,
+  },
+  getUserConfig() {
+    if (!this.userConfig) {
+      this.userConfig = {
+        ...DEFAULT_CONFIG,
+        ...this.config.get('userConfig'),
+      };
+    }
+    return this.userConfig;
+  },
+  setUserConfig(config) {
+    Object.assign(this.userConfig, config);
+    this.config.set('userConfig', this.userConfig);
+  },
+  initToken() {
+    this.prepareHeaders();
+    const {
+      serverUrl, anonymous, username, password,
+    } = this.getUserConfig();
+    if (!/:\/\/.*?\/$/.test(serverUrl)) {
+      this.properties.serverUrl = null;
+      return false;
+    }
+    this.properties.serverUrl = `${serverUrl}Violentmonkey/`;
+    if (anonymous) return true;
+    if (!username || !password) return false;
+    const auth = window.btoa(`${username}:${password}`);
+    this.headers.Authorization = `Basic ${auth}`;
+    return true;
+  },
+  handleMetaError(res) {
+    if (![
+      404, // File not exists
+      409, // Directory not exists
+    ].includes(res.status)) throw res;
+  },
+  // Some WebDAV servers do not allow LOCK / UNLOCK
+  /*
+  acquireLock() {
+    const { serverUrl } = this.properties;
+    const createLock = () => {
+      this.log('Acquire lock...');
+      return this.loadData({
+        method: 'LOCK',
+        url: serverUrl,
+        headers: {
+          Timeout: `Second-${30 * 60}`,
+        },
+        body: `\
+<?xml version="1.0" encoding="utf-8" ?>
+<D:lockinfo xmlns:D='DAV:'>
+  <D:lockscope><D:exclusive/></D:lockscope>
+  <D:locktype><D:write/></D:locktype>
+</D:lockinfo>`,
+      })
+      .then(xml => {
+        const doc = XNode.fromXML(xml);
+        const lock = doc.find('DAV:prop')
+        .find('DAV:lockdiscovery')
+        .find('DAV:activelock')
+        .find('DAV:locktoken')
+        .find('DAV:href')
+        .text();
+        this.log('Acquired lock:', lock);
+        this.config.set({
+          lock,
+        });
+      });
+    };
+    const lock = this.config.get('lock');
+    if (lock) {
+      this.log('Refresh lock:', lock);
+      return this.loadData({
+        method: 'LOCK',
+        url: serverUrl,
+        headers: {
+          If: `(<${lock}>)`,
+        },
+      })
+      .then(() => {
+        this.log('Refreshed lock:', lock);
+      }, err => {
+        if (err.status === 412) {
+          this.log('Refresh lock error');
+          this.config.set({ lock: null });
+          // Precondition Failed
+          return createLock();
+        }
+        throw err;
+      });
+    }
+    return createLock();
+  },
+  releaseLock() {
+    const lock = this.config.get('lock');
+    if (lock) {
+      const { serverUrl } = this.properties;
+      this.log('Release lock:', lock);
+      return this.loadData({
+        method: 'UNLOCK',
+        url: serverUrl,
+        headers: {
+          'Lock-Token': `<${lock}>`,
+        },
+      })
+      .then(() => {
+        this.log('Released lock');
+      }, () => {
+        this.log('Release lock error');
+      })
+      .then(() => {
+        this.config.set({ lock: null });
+      });
+    }
+  },
+  */
+  list() {
+    const { serverUrl } = this.properties;
+    const mkdir = () => this.loadData({
+      method: 'MKCOL',
+      url: serverUrl,
+    });
+    const readdir = () => this.loadData({
+      method: 'PROPFIND',
+      url: serverUrl,
+      headers: {
+        depth: '1',
+      },
+    })
+    .then(xml => {
+      const doc = XNode.fromXML(xml);
+      const items = doc.children()[0]
+      .map(node => {
+        const prop = node.find('DAV:propstat').find('DAV:prop');
+        const type = prop.find('DAV:resourcetype').find('DAV:collection') ? 'directory' : 'file';
+        const displayName = prop.find('DAV:displayname').text();
+        if (type === 'file' && isScriptFile(displayName)) {
+          const size = prop.find('DAV:getcontentlength');
+          return normalize({
+            name: displayName,
+            size: size ? +size.text() : 0,
+          });
+        }
+        return null;
+      })
+      .filter(Boolean);
+      return items;
+    });
+    return readdir()
+    .catch(err => {
+      if (err.status === 404) {
+        return mkdir().then(readdir);
+      }
+      throw err;
+    });
+  },
+  get(item) {
+    const name = getItemFilename(item);
+    const { serverUrl } = this.properties;
+    return this.loadData({
+      url: serverUrl + name,
+    });
+  },
+  put(item, data) {
+    const name = getItemFilename(item);
+    const headers = {
+      'Content-Type': 'text/plain',
+    };
+    const lock = this.config.get('lock');
+    if (lock) headers.If = `(<${lock}>)`;
+    const { serverUrl } = this.properties;
+    return this.loadData({
+      method: 'PUT',
+      url: serverUrl + name,
+      body: data,
+      headers,
+    });
+  },
+  remove(item) {
+    const name = getItemFilename(item);
+    const headers = {};
+    const lock = this.config.get('lock');
+    if (lock) headers.If = `(<${lock}>)`;
+    const { serverUrl } = this.properties;
+    return this.loadData({
+      method: 'DELETE',
+      url: serverUrl + name,
+      headers,
+    });
+  },
+});
+register(WebDAV);
+
+function normalize(item) {
+  return {
+    name: item.name,
+    size: item.size,
+    uri: getURI(item.name),
+  };
+}

+ 2 - 1
src/background/utils/script.js

@@ -1,3 +1,4 @@
+import { encodeFilename } from '#/common';
 import { getOption } from './options';
 import { getOption } from './options';
 
 
 const metaStart = '==UserScript==';
 const metaStart = '==UserScript==';
@@ -106,7 +107,7 @@ export function newScript(data) {
 export function getNameURI(script) {
 export function getNameURI(script) {
   const ns = script.meta.namespace || '';
   const ns = script.meta.namespace || '';
   const name = script.meta.name || '';
   const name = script.meta.name || '';
-  let nameURI = `${escape(ns)}:${escape(name)}:`;
+  let nameURI = encodeFilename(`${ns}\n${name}\n`);
   if (!ns && !name) nameURI += script.props.id || '';
   if (!ns && !name) nameURI += script.props.id || '';
   return nameURI;
   return nameURI;
 }
 }

+ 13 - 0
src/common/index.js

@@ -200,3 +200,16 @@ export function cache2blobUrl(raw, { defaultType, type: overrideType } = {}) {
     return URL.createObjectURL(blob);
     return URL.createObjectURL(blob);
   }
   }
 }
 }
+
+export function encodeFilename(name) {
+  // `escape` generated URI has % in it
+  return name.replace(/[-\\/:*?"<>|%\s]/g, m => {
+    let code = m.charCodeAt(0).toString(16);
+    if (code.length < 2) code = `0${code}`;
+    return `-x${code}`;
+  });
+}
+
+export function decodeFilename(filename) {
+  return filename.replace(/-x([0-9a-f]{2})/g, (_m, g) => String.fromCharCode(+`0x${g}`));
+}

+ 13 - 1
src/common/ui/style/style.css

@@ -41,14 +41,20 @@ hr {
 input[disabled] ~ * {
 input[disabled] ~ * {
   color: gray;
   color: gray;
 }
 }
-input[type=text] {
+input[type=text],
+input[type=password] {
   line-height: 1.5rem;
   line-height: 1.5rem;
+  &[disabled] {
+    background: #eee;
+    cursor: not-allowed;
+  }
 }
 }
 textarea {
 textarea {
   display: block;
   display: block;
   width: 100%;
   width: 100%;
 }
 }
 input[type=text],
 input[type=text],
+input[type=password],
 textarea {
 textarea {
   padding: 0 .5rem;
   padding: 0 .5rem;
   border: 1px solid #ccc;
   border: 1px solid #ccc;
@@ -60,6 +66,12 @@ code {
   background: #f7e999;
   background: #f7e999;
 }
 }
 
 
+fieldset {
+  display: inline-block;
+  padding: .5rem;
+  border: 1px solid #ccc;
+}
+
 :focus {
 :focus {
   outline: none;
   outline: none;
 }
 }

+ 0 - 6
src/options/views/edit/settings.vue

@@ -133,12 +133,6 @@ export default {
       display: block;
       display: block;
       width: 100%;
       width: 100%;
     }
     }
-    > * {
-      flex: 1;
-    }
-    > .label {
-      flex: 0 0 8em;
-    }
     > textarea {
     > textarea {
       min-height: 5em;
       min-height: 5em;
     }
     }

+ 61 - 1
src/options/views/tab-settings/vm-sync.vue

@@ -11,13 +11,56 @@
           :value="service.name"
           :value="service.name"
         />
         />
       </select>
       </select>
-      <button v-text="state.label" v-if="service.name"
+      <button v-text="state.label" v-if="service.name && state.authType === 'oauth'"
       :disabled="!state.canAuthorize" @click="onAuthorize"></button>
       :disabled="!state.canAuthorize" @click="onAuthorize"></button>
       <button :disabled="!state.canSync" v-if="service.name" @click="onSync">
       <button :disabled="!state.canSync" v-if="service.name" @click="onSync">
         <icon name="refresh"></icon>
         <icon name="refresh"></icon>
       </button>
       </button>
     </div>
     </div>
     <p v-if="state" class="mt-1" v-text="state.message"></p>
     <p v-if="state" class="mt-1" v-text="state.message"></p>
+    <fieldset class="mt-1" v-if="state && state.authType === 'password'">
+      <div class="sync-server-url">
+        <label v-text="i18n('labelSyncServerUrl')"></label>
+        <input
+          type="text"
+          v-model="state.userConfig.serverUrl"
+          :disabled="!state.canAuthorize || state.userConfig.anonymous"
+        />
+      </div>
+      <div class="mt-1">
+        <div class="inline-block mr-2">
+          <label v-text="i18n('labelSyncUsername')"></label>
+          <input
+            type="text"
+            v-model="state.userConfig.username"
+            :disabled="!state.canAuthorize || state.userConfig.anonymous"
+          />
+        </div>
+        <div class="inline-block mr-2">
+          <label v-text="i18n('labelSyncPassword')"></label>
+          <input
+            type="password"
+            v-model="state.userConfig.password"
+            :disabled="!state.canAuthorize || state.userConfig.anonymous"
+          />
+        </div>
+        <label class="mr-2">
+          <input
+            type="checkbox"
+            v-model="state.userConfig.anonymous"
+            :disabled="!state.canAuthorize"
+          />
+          <span v-text="i18n('labelSyncAnonymous')"></span>
+        </label>
+      </div>
+      <div class="mt-1">
+        <button
+          v-text="i18n('buttonSave')"
+          @click.prevent="onSaveUserConfig"
+          :disabled="!state.canAuthorize"
+        />
+      </div>
+    </fieldset>
     <div class="mt-1">
     <div class="mt-1">
       <label>
       <label>
         <setting-check name="syncScriptStatus" />
         <setting-check name="syncScriptStatus" />
@@ -62,6 +105,7 @@ export default {
           {
           {
             displayName: this.i18n('labelSyncDisabled'),
             displayName: this.i18n('labelSyncDisabled'),
             name: '',
             name: '',
+            properties: {},
           },
           },
           ...states,
           ...states,
         ];
         ];
@@ -91,12 +135,20 @@ export default {
           label: this.getLabel(),
           label: this.getLabel(),
           canAuthorize,
           canAuthorize,
           canSync,
           canSync,
+          authType: service.properties.authType,
+          userConfig: service.userConfig || {},
         };
         };
       }
       }
       return null;
       return null;
     },
     },
   },
   },
   methods: {
   methods: {
+    onSaveUserConfig() {
+      sendMessage({
+        cmd: 'SyncSetConfig',
+        data: this.state.userConfig,
+      });
+    },
     onSyncChange(e) {
     onSyncChange(e) {
       const { value } = e.target;
       const { value } = e.target;
       options.set(SYNC_CURRENT, value);
       options.set(SYNC_CURRENT, value);
@@ -141,3 +193,11 @@ export default {
   },
   },
 };
 };
 </script>
 </script>
+
+<style>
+.sync-server-url {
+  > input {
+    width: 400px;
+  }
+}
+</style>