mirror of
https://github.com/octoleo/syncthing.git
synced 2024-12-23 11:28:59 +00:00
63de838f27
* lib/locations: Fix enum values camelCase. * lib/locations: Remove unused FailuresFile. * cmd/syncthing: Turn around role of locations storage. Previously the locations package was used to provide default paths, possibly with an overridden home directory. Extra paths supplied on the command line were handled and passed around in the options object. To make the changed paths available to any other interested package, override the location setting from the option if supplied, instead of vice versa when not supplied. Adapt code using this to read from the locations package instead of passing through the options object. * lib/locations: Refactor showPaths to locations package. Generate a reusable string in locations.PrettyPrintPaths(). Enumerating all possible locations in different packages is error prone, so add a new public function to generate the listing as a string in the locations package. Adapt cmd/syncthing --paths to use that instead of its own console output. * lib/locations: Include CSRF token in pretty printed paths. * lib/api: New endpoint /rest/system/paths. The paths should be available for troubleshooting from a running instance. Using the --paths CLI option is not easy in some environments, so expose the locations mapping to a JSON endpoint. Add utility function ListExpandedPaths() that also filters out any entries which still contain variable placeholders. * gui: List runtime paths in separate log viewer tab. * Wrap paths. * lib/syncthing: Utilize locations.Get() instead of passing an arg. * Include base directories, move label to table caption. * gui: Switch to hard-coded paths instead of iterating over all. * gui: Break aboutModalView into tabs. Use tabs to separate authors from included third-party software. * gui: Move paths from log viewer to about modal. * lib/locations: Adjust pretty print output order to match GUI. * gui, authors: Remove additional bot names and fix indent. The indentation changed because of the tabbed about dialog, fix the authors script to respect that. Skip Syncthing*Automation in authors list as well. * Update AUTHORS list to remove bot names. * Revert AUTHORS email order change. * Do not emphasize DB and log file locations. * Review line wrapping. * review part 1: strings.Builder, naming * Rename and extend locations.Set() with error handling. Remodel the Override() function along the existing SetBaseDir() and rename it to simply Set(). Make sure to use absolute paths when given log file or GUI assets override options. Add proper error reporting if that goes wrong. * Remove obsolete comment about empty logfile option. * Don't filter out unexpanded baseDir placeholders, only ${timestamp}. * Restore behavior regarding special "-" logfile argument. If the option is given, but with empty value, assume the no log file (same as "-"). Don't try to convert the special value to an absolute path though and document this fact in a comment for the Set() function. * Use template to check for location key validity. * Don't filter out timestamp placeholders. * lib/api: Remove paths from /rest/system/status. * lib/ur: Properly initialize map in failure data (fixes #8479) Co-authored-by: Jakob Borg <jakob@kastelo.net>
1009 lines
71 KiB
HTML
1009 lines
71 KiB
HTML
<!DOCTYPE html>
|
|
<!--
|
|
// Copyright (C) 2014 The Syncthing Authors.
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
-->
|
|
<html lang="en" ng-app="syncthing" ng-controller="SyncthingController">
|
|
<head>
|
|
<meta charset="utf-8"/>
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
<link rel="shortcut icon" href="assets/img/favicon-{{syncthingStatus()}}.png"/>
|
|
<link rel="mask-icon" href="assets/img/safari-pinned-tab.svg" color="#0882c8"/>
|
|
|
|
<title ng-bind="thisDeviceName() + ' | Syncthing'"></title>
|
|
<link href="vendor/bootstrap/css/bootstrap.css" rel="stylesheet"/>
|
|
<link href="vendor/daterangepicker/daterangepicker.css" rel="stylesheet"/>
|
|
<link href="assets/font/raleway.css" rel="stylesheet"/>
|
|
<link href="vendor/fork-awesome/css/fork-awesome.css" rel="stylesheet"/>
|
|
<link href="vendor/fork-awesome/css/v5-compat.css" rel="stylesheet"/>
|
|
<link href="assets/css/tree.css" rel="stylesheet"/>
|
|
<link href="assets/css/overrides.css" rel="stylesheet"/>
|
|
<link href="assets/css/theme.css" rel="stylesheet"/>
|
|
</head>
|
|
|
|
<body>
|
|
<noscript>
|
|
<nav class="navbar navbar-top navbar-default" role="navigation">
|
|
<div class="container">
|
|
<span class="navbar-brand" aria-hidden="true">
|
|
<img class="logo hidden-xs" src="assets/img/logo-horizontal.svg" height="32" width="117" alt=""/>
|
|
<img class="logo hidden visible-xs" src="assets/img/favicon-default.png" height="32" alt=""/>
|
|
</span>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="container content">
|
|
<div class="row">
|
|
<div class="col-md-12">
|
|
<div class="panel panel-danger">
|
|
<div class="panel-heading">
|
|
<h3 class="panel-title">
|
|
<div class="panel-icon">
|
|
<span class="fas fa-exclamation-circle"></span>
|
|
</div>
|
|
Warning!
|
|
</h3>
|
|
</div>
|
|
<div class="panel-body">
|
|
<p>
|
|
The Syncthing admin interface requires JavaScript. Please enable JavaScript in your web browser and try again.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</noscript>
|
|
|
|
<div class="ng-cloak">
|
|
<script type="text/javascript" src="syncthing/development/logbar.js"></script>
|
|
<div ng-if="version.isBeta" ng-include="'syncthing/development/logbar.html'"></div>
|
|
<!-- Top bar -->
|
|
|
|
<nav class="navbar navbar-top navbar-default" role="navigation">
|
|
<div class="container">
|
|
<span class="navbar-brand" aria-hidden="true">
|
|
<img class="logo hidden-xs" src="assets/img/logo-horizontal.svg" height="32" width="117" alt=""/>
|
|
<img class="logo hidden visible-xs" src="assets/img/favicon-default.png" height="32" alt=""/>
|
|
</span>
|
|
<p class="navbar-text hidden-xs" ng-class="{'hidden-sm':upgradeInfo && upgradeInfo.newer}">{{thisDeviceName()}}</p>
|
|
<ul class="nav navbar-nav navbar-right">
|
|
<li ng-if="upgradeInfo && upgradeInfo.newer" class="upgrade-newer">
|
|
<button type="button" class="btn navbar-btn btn-primary btn-sm" data-toggle="modal" data-target="#upgrade">
|
|
<span class="fas fa-arrow-circle-up"></span>
|
|
<span class="hidden-xs" translate translate-value-version="{{upgradeInfo.latest}}">Upgrade To {%version%}</span>
|
|
</button>
|
|
</li>
|
|
<li ng-if="upgradeInfo && upgradeInfo.majorNewer" class="upgrade-newer-major">
|
|
<button type="button" class="btn navbar-btn btn-danger btn-sm" data-toggle="modal" data-target="#majorUpgrade">
|
|
<span class="fas fa-arrow-circle-up"></span>
|
|
<span class="hidden-xs" translate translate-value-version="{{upgradeInfo.latest}}">Upgrade To {%version%}</span>
|
|
</button>
|
|
</li>
|
|
<li class="dropdown" language-select></li>
|
|
<li>
|
|
<a class="navbar-link" href="{{docsURL('intro/gui')}}" target="_blank">
|
|
<span class="fas fa-question-circle"></span>
|
|
<span class="hidden-xs" translate>Help</span>
|
|
</a>
|
|
</li>
|
|
<li class="dropdown action-menu">
|
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
|
<span class="fas fa-cog"></span>
|
|
<span class="hidden-xs" translate>Actions</span>
|
|
<span class="caret"></span>
|
|
</a>
|
|
<ul class="dropdown-menu">
|
|
<li><a href="" ng-click="showSettings()"><span class="fas fa-fw fa-cog"></span> <span translate>Settings</span></a></li>
|
|
<li><a href="" ng-click="showDeviceIdentification(thisDevice())"><span class="fas fa-fw fa-qrcode"></span> <span translate>Show ID</span></a></li>
|
|
<li class="divider" aria-hidden="true"></li>
|
|
<li><a href="" ng-click="shutdown()"><span class="fas fa-fw fa-power-off"></span> <span translate>Shutdown</span></a></li>
|
|
<li><a href="" ng-click="restart()"><span class="fas fa-fw fa-refresh"></span> <span translate>Restart</span></a></li>
|
|
<li class="divider" aria-hidden="true"></li>
|
|
<li class="visible-xs">
|
|
<a href="{{docsURL('intro/gui')}}" target="_blank">
|
|
<span class="fas fa-fw fa-question-circle"></span> <span translate>Help</span>
|
|
</a>
|
|
</li>
|
|
<li><a href="" ng-click="about.show()"><span class="far fa-fw fa-heart"></span> <span translate>About</span></a></li>
|
|
<li class="divider" aria-hidden="true"></li>
|
|
<li><a href="" ng-click="advanced()"><span class="fas fa-fw fa-cogs"></span> <span translate>Advanced</span></a></li>
|
|
<li><a href="" ng-click="logging.show()"><span class="far fa-fw fa-file-alt"></span> <span translate>Logs</span></a></li>
|
|
<li class="divider" aria-hidden="true" ng-if="config.gui.debugging"></li>
|
|
<li><a href="/rest/debug/support" target="_blank" ng-if="config.gui.debugging"><span class="fa fa-user-md"></span> <span translate>Support Bundle</span></a></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="container content">
|
|
|
|
<!-- Panel: Open, no auth -->
|
|
|
|
<div ng-if="openNoAuth" class="row">
|
|
<div class="col-md-12">
|
|
<div class="panel panel-danger">
|
|
<div class="panel-heading">
|
|
<h3 class="panel-title">
|
|
<div class="panel-icon">
|
|
<span class="fas fa-exclamation-circle"></span>
|
|
</div>
|
|
<span translate>Danger!</span>
|
|
</h3>
|
|
</div>
|
|
<div class="panel-body">
|
|
<p>
|
|
<span translate>The Syncthing admin interface is configured to allow remote access without a password.</span>
|
|
<b><span translate>This can easily give hackers access to read and change any files on your computer.</span></b>
|
|
<span translate>Please set a GUI Authentication User and Password in the Settings dialog.</span>
|
|
</p>
|
|
</div>
|
|
<div class="panel-footer">
|
|
<button type="button" class="btn btn-sm btn-default pull-right" ng-click="showSettings()">
|
|
<span class="fas fa-cog"></span> <span translate>Settings</span>
|
|
</button>
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Panel: Restart Needed -->
|
|
|
|
<div ng-if="!configInSync" class="row">
|
|
<div class="col-md-12">
|
|
<div class="panel panel-warning">
|
|
<div class="panel-heading">
|
|
<h3 class="panel-title">
|
|
<div class="panel-icon">
|
|
<span class="fas fa-exclamation-circle"></span>
|
|
</div>
|
|
<span translate>Restart Needed</span>
|
|
</h3>
|
|
</div>
|
|
<div class="panel-body">
|
|
<p translate>The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.</p>
|
|
</div>
|
|
<div class="panel-footer">
|
|
<button type="button" class="btn btn-sm btn-default pull-right" ng-click="restart()">
|
|
<span class="fas fa-refresh"></span> <span translate>Restart</span>
|
|
</button>
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div ng-if="config">
|
|
|
|
<!-- Panel: Notifications -->
|
|
|
|
<div ng-if="config.options && config.options.unackedNotificationIDs" ng-include="'syncthing/core/notifications.html'"></div>
|
|
|
|
<!-- Panel: New Device -->
|
|
|
|
<div ng-repeat="(deviceID, pendingDevice) in pendingDevices" class="row">
|
|
<div class="col-md-12">
|
|
<div class="panel panel-warning">
|
|
<div class="panel-heading">
|
|
<h3 class="panel-title">
|
|
<identicon class="panel-icon" data-value="device"></identicon>
|
|
<span translate>New Device</span>
|
|
<span class="pull-right">{{ pendingDevice.time | date:"yyyy-MM-dd HH:mm:ss" }}</span>
|
|
</h3>
|
|
</div>
|
|
<div class="panel-body">
|
|
<p>
|
|
<span translate translate-value-device="{{ deviceID }}" translate-value-address="{{ pendingDevice.address }}" translate-value-name="{{ pendingDevice.name }}">
|
|
Device "{%name%}" ({%device%} at {%address%}) wants to connect. Add new device?
|
|
</span>
|
|
</p>
|
|
</div>
|
|
<div class="panel-footer clearfix">
|
|
<div class="pull-right">
|
|
<button type="button" class="btn btn-sm btn-success" ng-click="addDevice(deviceID, pendingDevice.name)">
|
|
<span class="fas fa-plus"></span> <span translate>Add Device</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-danger" ng-click="ignoreDevice(deviceID, pendingDevice)" tooltip data-original-title="{{'Permanently add it to the ignore list, suppressing further notifications.' | translate}}">
|
|
<span class="fas fa-times"></span> <span translate>Ignore</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-default" ng-click="dismissPendingDevice(deviceID)" tooltip data-original-title="{{'Do not add it to the ignore list, so this notification may recur.' | translate}}">
|
|
<span class="far fa-clock"></span> <span translate>Dismiss</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Panel: New Folder -->
|
|
<div ng-repeat="(folderID, pendingFolder) in pendingFolders">
|
|
<div ng-repeat="(deviceID, offeringDevice) in pendingFolder.offeredBy" class="row reject">
|
|
<div class="col-md-12">
|
|
<div class="panel panel-warning">
|
|
<div class="panel-heading">
|
|
<h3 class="panel-title">
|
|
<div class="panel-icon">
|
|
<span class="fas fa-folder"></span>
|
|
</div>
|
|
<span translate ng-if="!folders[folderID]">New Folder</span>
|
|
<span translate ng-if="folders[folderID]">Share Folder</span>
|
|
<span class="pull-right">{{ offeringDevice.time | date:"yyyy-MM-dd HH:mm:ss" }}</span>
|
|
</h3>
|
|
</div>
|
|
<div class="panel-body">
|
|
<p>
|
|
<span ng-if="offeringDevice.label.length == 0" translate translate-value-device="{{ deviceName(devices[deviceID]) }}" translate-value-folder="{{ folderID }}">
|
|
{%device%} wants to share folder "{%folder%}".
|
|
</span>
|
|
<span ng-if="offeringDevice.label.length != 0" translate translate-value-device="{{ deviceName(devices[deviceID]) }}" translate-value-folder="{{ folderID }}" translate-value-folderlabel="{{ offeringDevice.label }}">
|
|
{%device%} wants to share folder "{%folderlabel%}" ({%folder%}).
|
|
</span>
|
|
<span translate ng-if="folders[folderID]">Share this folder?</span>
|
|
<span translate ng-if="!folders[folderID]">Add new folder?</span>
|
|
</p>
|
|
</div>
|
|
<div class="panel-footer clearfix">
|
|
<div class="pull-right">
|
|
<button type="button" class="btn btn-sm btn-success" ng-click="addFolderAndShare(folderID, pendingFolder, deviceID)" ng-if="!folders[folderID]">
|
|
<span class="fas fa-check"></span> <span translate>Add</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-success" ng-click="shareFolderWithDevice(folderID, deviceID)" ng-if="folders[folderID]">
|
|
<span class="fas fa-check"></span> <span translate>Share</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-danger" ng-click="ignoreFolder(deviceID, folderID, offeringDevice)" tooltip data-original-title="{{'Permanently add it to the ignore list, suppressing further notifications.' | translate}}">
|
|
<span class="fas fa-times"></span> <span translate>Ignore</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-default" ng-click="dismissPendingFolder(folderID, deviceID)" tooltip data-original-title="{{'Do not add it to the ignore list, so this notification may recur.' | translate}}">
|
|
<span class="far fa-clock"></span> <span translate>Dismiss</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- Panel: Notice -->
|
|
|
|
<div ng-if="errorList().length > 0" class="row">
|
|
<div class="col-md-12">
|
|
<div class="panel panel-warning">
|
|
<div class="panel-heading">
|
|
<h3 class="panel-title">
|
|
<div class="panel-icon">
|
|
<span class="fas fa-exclamation-circle"></span>
|
|
</div>
|
|
<span translate>Notice</span>
|
|
</h3>
|
|
</div>
|
|
<div class="panel-body">
|
|
<p ng-repeat="err in errorList()">
|
|
<small>{{err.when | date:"yyyy-MM-dd HH:mm:ss"}}:</small>
|
|
<span ng-bind-html="friendlyDevices(err.message) | linky: '_blank'"></span>
|
|
</p>
|
|
</div>
|
|
<div class="panel-footer">
|
|
<button type="button" class="btn btn-sm btn-default pull-right" ng-click="clearErrors()">
|
|
<span class="fas fa-check"></span> <span translate>OK</span>
|
|
</button>
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Panel: FS watcher errors -->
|
|
|
|
<div ng-if="sizeOf(fsWatcherErrorMap()) > 0" class="row">
|
|
<div class="col-md-12">
|
|
<div class="panel panel-warning">
|
|
<div class="panel-heading">
|
|
<h3 class="panel-title">
|
|
<div class="panel-icon">
|
|
<span class="fas fa-exclamation-circle"></span>
|
|
</div>
|
|
<span translate>Filesystem Watcher Errors</span>
|
|
</h3>
|
|
</div>
|
|
<div class="panel-body">
|
|
<p>
|
|
<span translate>For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.</span> <a href="https://forum.syncthing.net" target="_blank"><span class="fas fa-question-circle"></span> <span translate>Support</span></a>
|
|
</p>
|
|
<table>
|
|
<tr ng-repeat="(id, err) in fsWatcherErrorMap()">
|
|
<td>{{folderLabel(id)}}</td><td>{{err}}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- First regular row -->
|
|
|
|
<div class="row">
|
|
|
|
<!-- Folder list (top left) -->
|
|
|
|
<div class="col-md-6" aria-labelledby="folder_list" role="region" >
|
|
<h3 id="folder_list" translate>Folders</h3>
|
|
<div class="panel-group" id="folders">
|
|
<div class="panel panel-default" ng-repeat="folder in folderList()">
|
|
<button class="btn panel-heading" data-toggle="collapse" data-parent="#folders" data-target="#folder-{{$index}}" aria-expanded="false">
|
|
<div class="panel-progress" ng-show="folderStatus(folder) == 'syncing'" ng-attr-style="width: {{syncPercentage(folder.id) | percent}}"></div>
|
|
<div class="panel-progress" ng-show="folderStatus(folder) == 'scanning' && scanProgress[folder.id] != undefined" ng-attr-style="width: {{scanPercentage(folder.id) | percent}}"></div>
|
|
<h4 class="panel-title">
|
|
<div class="panel-icon hidden-xs">
|
|
<span ng-if="folder.type == 'sendreceive'" class="fas fa-fw fa-folder"></span>
|
|
<span ng-if="folder.type == 'sendonly'" class="fas fa-fw fa-upload"></span>
|
|
<span ng-if="folder.type == 'receiveonly'" class="fas fa-fw fa-download"></span>
|
|
<span ng-if="folder.type == 'receiveencrypted'" class="fas fa-fw fa-lock"></span>
|
|
</div>
|
|
<div class="panel-status pull-right text-{{folderClass(folder)}}" ng-switch="folderStatus(folder)">
|
|
<span ng-switch-when="paused"><span class="hidden-xs" translate>Paused</span><span class="visible-xs" aria-label="{{'Paused' | translate}}"><i class="fas fa-fw fa-pause"></i></span></span>
|
|
<span ng-switch-when="unknown"><span class="hidden-xs" translate>Unknown</span><span class="visible-xs" aria-label="{{'Unknown' | translate}}"><i class="fas fa-fw fa-question-circle"></i></span></span>
|
|
<span ng-switch-when="unshared"><span class="hidden-xs" translate>Unshared</span><span class="visible-xs" aria-label="{{'Unshared' | translate}}"><i class="fas fa-fw fa-unlink"></i></span></span>
|
|
<span ng-switch-when="scan-waiting"><span class="hidden-xs" translate>Waiting to Scan</span><span class="visible-xs" aria-label="{{'Waiting to Scan' | translate}}"><i class="fas fa-fw fa-hourglass-half"></i></span></span>
|
|
<span ng-switch-when="cleaning"><span class="hidden-xs" translate>Cleaning Versions</span><span class="visible-xs" aria-label="{{'Cleaning Versions' | translate}}"><i class="fas fa-fw fa-recycle"></i></span></span>
|
|
<span ng-switch-when="clean-waiting"><span class="hidden-xs" translate>Waiting to Clean</span><span class="visible-xs" aria-label="{{'Waiting to Clean' | translate}}"><i class="fas fa-fw fa-hourglass-half"></i></span></span>
|
|
<span ng-switch-when="stopped"><span class="hidden-xs" translate>Stopped</span><span class="visible-xs" aria-label="{{'Stopped' | translate}}"><i class="fas fa-fw fa-stop"></i></span></span>
|
|
<span ng-switch-when="scanning">
|
|
<span class="hidden-xs" translate>Scanning</span>
|
|
<span class="hidden-xs" ng-if="scanPercentage(folder.id) != undefined">
|
|
({{scanPercentage(folder.id) | percent}})
|
|
</span>
|
|
<span class="visible-xs" aria-label="{{'Scanning' | translate}}"><i class="fas fa-fw fa-search"></i></span>
|
|
</span>
|
|
<span ng-switch-when="idle"><span class="hidden-xs" translate>Up to Date</span><span class="visible-xs" aria-label="{{'Up to Date' | translate}}"><i class="fas fa-fw fa-check"></i></span></span>
|
|
<span ng-switch-when="localadditions"><span class="hidden-xs" translate>Local Additions</span><span class="visible-xs" aria-label="{{'Local Additions' | translate}}"><i class="fas fa-fw fa-check"></i></span></span>
|
|
<span ng-switch-when="sync-waiting">
|
|
<span class="hidden-xs" translate>Waiting to Sync</span>
|
|
<span class="visible-xs" aria-label="{{'Waiting to Sync' | translate}}"><i class="fas fa-fw fa-hourglass-half"></i></span>
|
|
</span>
|
|
<span ng-switch-when="sync-preparing">
|
|
<span class="hidden-xs" translate>Preparing to Sync</span>
|
|
<span class="visible-xs" aria-label="{{'Preparing to Sync' | translate}}"><i class="fas fa-fw fa-hourglass-half"></i></span>
|
|
</span>
|
|
<span ng-switch-when="syncing">
|
|
<span class="hidden-xs" translate>Syncing</span>
|
|
<span>({{syncPercentage(folder.id) | percent}}, {{model[folder.id].needBytes | binary}}B)</span>
|
|
</span>
|
|
<span ng-switch-when="outofsync"><span class="hidden-xs" translate>Out of Sync</span><span class="visible-xs" aria-label="{{'Out of Sync' | translate}}"><i class="fas fa-fw fa-exclamation-circle"></i></span></span>
|
|
<span ng-switch-when="faileditems"><span class="hidden-xs" translate>Failed Items</span><span class="visible-xs" aria-label="{{'Failed Items' | translate}}"><i class="fas fa-fw fa-exclamation-circle"></i></span></span>
|
|
<span ng-switch-when="localunencrypted"><span class="hidden-xs">{{'Unexpected Items' | translate}}</span><span class="visible-xs" aria-label="{{'Unexpected Items' | translate}}"><i class="fas fa-fw fa-exclamation-circle"></i></span></span>
|
|
</div>
|
|
<div class="panel-title-text">
|
|
<span tooltip data-original-title="{{folder.label.length != 0 ? folder.id : ''}}">{{folder.label.length != 0 ? folder.label : folder.id}}</span>
|
|
</div>
|
|
</h4>
|
|
</button>
|
|
<div id="folder-{{$index}}" class="panel-collapse collapse">
|
|
<div class="panel-body">
|
|
<table class="table table-condensed table-striped table-auto">
|
|
<tbody>
|
|
<tr ng-show="folder.label != undefined && folder.label.length > 0">
|
|
<th><span class="fas fa-fw fa-info-circle"></span> <span translate>Folder ID</span></th>
|
|
<td class="text-right no-overflow-ellipse">{{folder.id}}</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="fas fa-fw fa-folder-open"></span> <span translate>Folder Path</span></th>
|
|
<td class="text-right">
|
|
<span tooltip data-original-title="{{folder.path}}">{{folder.path}}</span>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="!folder.paused && (model[folder.id].invalid || model[folder.id].error)">
|
|
<th><span class="fas fa-fw fa-exclamation-triangle"></span> <span translate>Error</span></th>
|
|
<td class="text-right">{{model[folder.id].invalid || model[folder.id].error}}</td>
|
|
</tr>
|
|
<tr ng-if="!folder.paused">
|
|
<th><span class="fas fa-fw fa-globe"></span> <span translate>Global State</span></th>
|
|
<td class="text-right">
|
|
<span tooltip data-original-title="{{model[folder.id].globalFiles | alwaysNumber | localeNumber}} {{'files' | translate}}, {{model[folder.id].globalDirectories | alwaysNumber | localeNumber}} {{'directories' | translate}}, ~{{model[folder.id].globalBytes | binary}}B">
|
|
<span class="far fa-copy"></span> {{model[folder.id].globalFiles | alwaysNumber | localeNumber}} 
|
|
<span class="far fa-folder"></span> {{model[folder.id].globalDirectories | alwaysNumber | localeNumber}} 
|
|
<span class="far fa-hdd"></span> ~{{model[folder.id].globalBytes | binary}}B
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="!folder.paused">
|
|
<th><span class="fas fa-fw fa-home"></span> <span translate>Local State</span></th>
|
|
<td class="text-right">
|
|
<div>
|
|
<span tooltip data-original-title="{{model[folder.id].localFiles | alwaysNumber | localeNumber}} {{'files' | translate}}, {{model[folder.id].localDirectories | alwaysNumber | localeNumber}} {{'directories' | translate}}, ~{{model[folder.id].localBytes | binary}}B">
|
|
<span class="far fa-copy"></span> {{model[folder.id].localFiles | alwaysNumber | localeNumber}} 
|
|
<span class="far fa-folder"></span> {{model[folder.id].localDirectories | alwaysNumber | localeNumber}} 
|
|
<span class="far fa-hdd"></span> ~{{model[folder.id].localBytes | binary}}B
|
|
</span>
|
|
</div>
|
|
<div ng-if="model[folder.id].ignorePatterns">
|
|
<a href="" ng-click="editFolderExisting(folder, '#folder-ignores')"><i class="small" translate>Reduced by ignore patterns</i></a>
|
|
</div>
|
|
<div ng-if="folder.ignoreDelete">
|
|
<i class="small">
|
|
<span translate>Altered by ignoring deletes.</span>
|
|
<a href="{{docsURL('advanced/folder-ignoredelete')}}" target="_blank">
|
|
<span class="fas fa-question-circle"></span> <span translate>Help</span>
|
|
</a>
|
|
</i>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="model[folder.id].needTotalItems > 0">
|
|
<th><span class="fas fa-fw fa-cloud-download-alt"></span> <span translate>Out of Sync Items</span></th>
|
|
<td class="text-right">
|
|
<a href="" ng-click="showNeed(folder.id)">{{model[folder.id].needTotalItems | alwaysNumber | localeNumber}} <span translate>items</span>, ~{{model[folder.id].needBytes | binary}}B</a>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="folderStatus(folder) === 'scanning' && scanRate(folder.id) > 0">
|
|
<th><span class="fas fa-fw fa-hourglass-half"></span> <span translate>Scan Time Remaining</span></th>
|
|
<td class="text-right">
|
|
<span tooltip data-original-title="{{scanRate(folder.id) | binary}}B/s">~ {{scanRemaining(folder.id)}}</span>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="hasFailedFiles(folder.id)">
|
|
<th><span class="fas fa-fw fa-exclamation-circle"></span> <span translate>Failed Items</span></th>
|
|
<!-- Show the number of failed items as a link to bring up the list. -->
|
|
<td class="text-right">
|
|
<a href="" ng-click="showFailed(folder.id)">{{model[folder.id].pullErrors | alwaysNumber | localeNumber}} <span translate>items</span></a>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="hasReceiveOnlyChanged(folder)">
|
|
<th><span class="fas fa-fw fa-exclamation-circle"></span> <span translate>Locally Changed Items</span></th>
|
|
<td class="text-right">
|
|
<a href="" ng-click="showLocalChanged(folder.id, folder.type)">{{model[folder.id].receiveOnlyTotalItems | alwaysNumber | localeNumber}} <span translate>items</span>, ~{{model[folder.id].receiveOnlyChangedBytes | binary}}B</a>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="folder.type != 'sendreceive'">
|
|
<th><span class="fas fa-fw fa-folder"></span> <span translate>Folder Type</span></th>
|
|
<td class="text-right">
|
|
<span ng-if="folder.type == 'sendonly'" translate>Send Only</span>
|
|
<span ng-if="folder.type == 'receiveonly'" translate>Receive Only</span>
|
|
<span ng-if="folder.type == 'receiveencrypted'" translate>Receive Encrypted</span>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="folder.ignorePerms">
|
|
<th><span class="far fa-fw fa-minus-square"></span> <span translate>Ignore Permissions</span></th>
|
|
<td class="text-right">
|
|
<span translate>Yes</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="fas fa-fw fa-refresh"></span> <span translate>Rescans</span></th>
|
|
<td class="text-right">
|
|
<div ng-if="folder.rescanIntervalS > 0">
|
|
<span ng-if="!folder.fsWatcherEnabled" tooltip data-original-title="{{'Periodic scanning at given interval and disabled watching for changes' | translate}}">
|
|
<span class="far fa-clock"></span> {{folder.rescanIntervalS | duration}} 
|
|
<span class="fas fa-eye-slash"></span> <span translate>Disabled</span>
|
|
</span>
|
|
<span ng-if="folder.fsWatcherEnabled && (!model[folder.id].watchError || folder.paused || folderStatus(folder) === 'stopped')" tooltip data-original-title="{{'Periodic scanning at given interval and enabled watching for changes' | translate}}">
|
|
<span class="far fa-clock"></span> {{folder.rescanIntervalS | duration}} 
|
|
<span class="fas fa-eye"></span> <span translate>Enabled</span>
|
|
</span>
|
|
<span ng-if="folder.fsWatcherEnabled && !folder.paused && folderStatus(folder) !== 'stopped' && model[folder.id].watchError" tooltip data-original-title="{{'Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:' | translate}}<br/>{{model[folder.id].watchError}}">
|
|
<span class="far fa-clock"></span> {{folder.rescanIntervalS | duration}} 
|
|
<span class="fas fa-eye-slash"></span> <span translate>Failed to setup, retrying</span>
|
|
</span>
|
|
</div>
|
|
<div ng-if="folder.rescanIntervalS <= 0">
|
|
<span ng-if="!folder.fsWatcherEnabled" tooltip data-original-title="{{'Disabled periodic scanning and disabled watching for changes' | translate}}">
|
|
<span class="far fa-clock"></span> <span translate>Disabled</span> 
|
|
<span class="fas fa-eye-slash"></span> <span translate>Disabled</span>
|
|
</span>
|
|
<span ng-if="folder.fsWatcherEnabled && (!model[folder.id].watchError || folder.paused || folderStatus(folder) === 'stopped')" tooltip data-original-title="{{'Disabled periodic scanning and enabled watching for changes' | translate}}">
|
|
<span class="far fa-clock"></span> <span translate>Disabled</span> 
|
|
<span class="fas fa-eye"></span> <span translate>Enabled</span>
|
|
</span>
|
|
<span ng-if="folder.fsWatcherEnabled && !folder.paused && folderStatus(folder) !== 'stopped' && model[folder.id].watchError" tooltip data-original-title="{{'Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:' | translate}}<br/>{{model[folder.id].watchError}}">
|
|
<span class="far fa-clock"></span> <span translate>Disabled</span> 
|
|
<span class="fas fa-eye-slash"></span> <span translate>Failed to setup, retrying</span>
|
|
</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="folder.order != 'random' && folder.type != 'sendonly'">
|
|
<th><span class="fas fa-fw fa-sort"></span> <span translate>File Pull Order</span></th>
|
|
<td class="text-right" ng-switch="folder.order">
|
|
<span ng-switch-when="random" translate>Random</span>
|
|
<span ng-switch-when="alphabetic" translate>Alphabetic</span>
|
|
<span ng-switch-when="smallestFirst" translate>Smallest First</span>
|
|
<span ng-switch-when="largestFirst" translate>Largest First</span>
|
|
<span ng-switch-when="oldestFirst" translate>Oldest First</span>
|
|
<span ng-switch-when="newestFirst" translate>Newest First</span>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="folder.versioning.type">
|
|
<th><span class="fa fa-fw fa-file"></span> <span translate>File Versioning</span></th>
|
|
<td class="text-right">
|
|
<span ng-switch="folder.versioning.type">
|
|
<span ng-switch-when="trashcan" translate>Trash Can</span>
|
|
<span ng-switch-when="simple" translate>Simple</span>
|
|
<span ng-switch-when="staggered" translate>Staggered</span>
|
|
<span ng-switch-when="external" tooltip data-original-title="<span class='text-monospace'>{{folder.versioning.params.command}}</span>" translate>External</span>
|
|
</span>
|
|
<span ng-if="folder.versioning.type != 'external'">
|
|
<span ng-if="(folder.versioning.type == 'trashcan' || folder.versioning.type == 'simple') && folder.versioning.params.cleanoutDays != versioningDefaults.trashcanClean" tooltip data-original-title="{{'Clean out after' | translate}}">
|
|
 <span class="fa fa-calendar"></span> {{folder.versioning.params.cleanoutDays * 86400 | duration:"d"}}
|
|
</span>
|
|
<span ng-if="folder.versioning.type == 'simple' && folder.versioning.params.keep != versioningDefaults.simpleKeep" tooltip data-original-title="{{'Keep Versions' | translate}}">
|
|
 <span class="fa fa-file-archive-o"></span> {{folder.versioning.params.keep}}
|
|
</span>
|
|
<span ng-if="folder.versioning.type == 'staggered' && folder.versioning.params.maxAge / 86400 != versioningDefaults.staggeredMaxAge" tooltip data-original-title="{{'Maximum Age' | translate}}">
|
|
 <span class="fa fa-calendar"></span> <span ng-if="folder.versioning.params.maxAge == 0" translate>Forever</span><span ng-if="folder.versioning.params.maxAge > 0">{{folder.versioning.params.maxAge | duration}}</span>
|
|
</span>
|
|
<span ng-if="folder.versioning.cleanupIntervalS != versioningDefaults.cleanupIntervalS" tooltip data-original-title="{{'Cleanup Interval' | translate}}">
|
|
 <span class="fa fa-recycle"></span> <span ng-if="folder.versioning.cleanupIntervalS == 0" translate>Disabled</span><span ng-if="folder.versioning.cleanupIntervalS > 0">{{folder.versioning.cleanupIntervalS | duration}}</span>
|
|
</span>
|
|
<!-- Keep the path last, so that it truncates without pushing other information out of the screen. -->
|
|
<span ng-if="folder.versioning.fsPath != ''" tooltip data-original-title="{{folder.versioning.fsPath}}">
|
|
 <span class="fa fa-folder-open-o"></span> {{folder.versioning.fsPath}}
|
|
</span>
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="fas fa-fw fa-share-alt"></span> <span translate>Shared With</span></th>
|
|
<td class="text-right">
|
|
<span tooltip data-original-title="{{sharesFolder(folder)}} {{folderHasUnacceptedDevices(folder) ? '<br/>(<sup>1</sup>' + ('The remote device has not accepted sharing this folder.' | translate) + ')' : ''}} {{folderHasPausedDevices(folder) ? '<br/>(<sup>2</sup>' + ('The remote device has paused this folder.' | translate) + ')' : ''}}" ng-bind-html="sharesFolder(folder)"></span>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="folderStats[folder.id].lastScan">
|
|
<th><span class="far fa-fw fa-clock"></span> <span translate>Last Scan</span></th>
|
|
<td translate ng-if="folderStats[folder.id].lastScanDays >= 365" class="text-right">Never</td>
|
|
<td ng-if="folderStats[folder.id].lastScanDays < 365" class="text-right">
|
|
<span>{{folderStats[folder.id].lastScan | date:'yyyy-MM-dd HH:mm:ss'}}</span>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="folder.type != 'sendonly' && folder.type != 'receiveencrypted' && folderStats[folder.id].lastFile && folderStats[folder.id].lastFile.filename">
|
|
<th><span class="fas fa-fw fa-exchange-alt"></span> <span translate>Latest Change</span></th>
|
|
<td class="text-right">
|
|
<span tooltip data-original-title="{{folderStats[folder.id].lastFile.filename}} @ {{folderStats[folder.id].lastFile.at | date:'yyyy-MM-dd HH:mm:ss'}}">
|
|
<span translate translate-value-file="{{folderStats[folder.id].lastFile.filename | basename}}" ng-if="!folderStats[folder.id].lastFile.deleted">Updated {%file%}</span>
|
|
<span translate translate-value-file="{{folderStats[folder.id].lastFile.filename | basename}}" ng-if="folderStats[folder.id].lastFile.deleted">Deleted {%file%}</span>
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="panel-footer">
|
|
<button type="button" class="btn btn-sm btn-danger pull-left" ng-click="revertOverrideConfirmationModal('override', folder.id)" ng-if="folderStatus(folder) == 'outofsync' && folder.type == 'sendonly'">
|
|
<span class="fas fa-arrow-circle-up"></span> <span translate>Override Changes</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-danger pull-left" ng-click="revertOverrideConfirmationModal('revert', folder.id)" ng-if="hasReceiveOnlyChanged(folder)">
|
|
<span class="fa fa-arrow-circle-down"></span> <span translate>Revert Local Changes</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-danger pull-left" ng-click="revertOverrideConfirmationModal('deleteEnc', folder.id)" ng-if="hasReceiveEncryptedItems(folder)">
|
|
<span class="fa fa-minus-circle"></span> <span translate>Delete Unexpected Items</span>
|
|
</button>
|
|
<span class="pull-right">
|
|
<button ng-if="!folder.paused" type="button" class="btn btn-sm btn-default" ng-click="setFolderPause(folder.id, true)">
|
|
<span class="fas fa-pause"></span> <span translate>Pause</span>
|
|
</button>
|
|
<button ng-if="folder.paused" type="button" class="btn btn-sm btn-default" ng-click="setFolderPause(folder.id, false)">
|
|
<span class="fas fa-play"></span> <span translate>Resume</span>
|
|
</button>
|
|
<button type="button" class="btn btn-default btn-sm" ng-click="restoreVersions.show(folder.id)" ng-if="folder.versioning.type && folder.versioning.type != 'external'" ng-disabled="folder.paused">
|
|
<span class="fas fa-undo"></span> <span translate>Versions</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-default" ng-click="rescanFolder(folder.id)" ng-disabled="['idle', 'stopped', 'unshared', 'outofsync', 'faileditems', 'localadditions'].indexOf(folderStatus(folder)) < 0">
|
|
<span class="fas fa-refresh"></span> <span translate>Rescan</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-default" ng-click="editFolderExisting(folder)">
|
|
<span class="fas fa-pencil-alt"></span> <span translate>Edit</span>
|
|
</button>
|
|
</span>
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<span class="pull-right">
|
|
<button type="button" class="btn btn-sm btn-default" ng-click="setAllFoldersPause(true)" ng-if="isAtleastOneFolderPausedStateSetTo(false)">
|
|
<span class="fas fa-pause"></span> <span translate>Pause All</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-default" ng-click="setAllFoldersPause(false)" ng-if="isAtleastOneFolderPausedStateSetTo(true)">
|
|
<span class="fas fa-play"></span> <span translate>Resume All</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-default" ng-click="rescanAllFolders()" ng-if="folderList().length > 0" ng-disabled="!isAtleastOneFolderPausedStateSetTo(false)">
|
|
<span class="fas fa-refresh"></span> <span translate>Rescan All</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-default" ng-click="addFolder()">
|
|
<span class="fas fa-plus"></span> <span translate>Add Folder</span>
|
|
</button>
|
|
</span>
|
|
<div class="clearfix"></div>
|
|
<hr class="visible-sm"/>
|
|
</div>
|
|
|
|
<!-- Device list (top right) -->
|
|
|
|
<!-- This device -->
|
|
|
|
<div class="col-md-6" aria-label="{{'Devices' | translate}}" role="region">
|
|
<h3 translate>This Device</h3>
|
|
<div class="panel panel-default" ng-repeat="deviceCfg in [thisDevice()]">
|
|
<button class="btn panel-heading" data-toggle="collapse" data-target="#device-this" aria-expanded="true">
|
|
<h4 class="panel-title">
|
|
<identicon class="panel-icon" data-value="deviceCfg.deviceID"></identicon>
|
|
<div class="panel-title-text">{{deviceName(deviceCfg)}}</div>
|
|
</h4>
|
|
</button>
|
|
<div id="device-this" class="panel-collapse collapse in">
|
|
<div class="panel-body">
|
|
<table class="table table-condensed table-striped table-auto">
|
|
<tbody>
|
|
<tr>
|
|
<th><span class="fas fa-fw fa-cloud-download-alt"></span> <span translate>Download Rate</span></th>
|
|
<td class="text-right">
|
|
<a href="#" class="toggler" ng-click="toggleUnits()">
|
|
<span ng-if="!metricRates">{{connectionsTotal.inbps | binary}}B/s</span>
|
|
<span ng-if="metricRates">{{connectionsTotal.inbps*8 | metric}}bps</span>
|
|
({{connectionsTotal.inBytesTotal | binary}}B)
|
|
<small ng-if="config.options.maxRecvKbps > 0"><br/>
|
|
<i class="text-muted"><span translate>Limit</span>:
|
|
<span ng-if="!metricRates">{{config.options.maxRecvKbps*1024 | binary}}B/s</span>
|
|
<span ng-if="metricRates">{{config.options.maxRecvKbps*1024*8 | metric}}bps</span>
|
|
</i>
|
|
</small>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="fas fa-fw fa-cloud-upload-alt"></span> <span translate>Upload Rate</span></th>
|
|
<td class="text-right">
|
|
<a href="#" class="toggler" ng-click="toggleUnits()">
|
|
<span ng-if="!metricRates">{{connectionsTotal.outbps | binary}}B/s</span>
|
|
<span ng-if="metricRates">{{connectionsTotal.outbps*8 | metric}}bps</span>
|
|
({{connectionsTotal.outBytesTotal | binary}}B)
|
|
<small ng-if="config.options.maxSendKbps > 0"><br/>
|
|
<i class="text-muted"><span translate>Limit</span>:
|
|
<span ng-if="!metricRates">{{config.options.maxSendKbps*1024 | binary}}B/s</span>
|
|
<span ng-if="metricRates">{{config.options.maxSendKbps*1024*8 | metric}}bps</span>
|
|
</i>
|
|
</small>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="fas fa-fw fa-home"></span> <span translate>Local State (Total)</span></th>
|
|
<td class="text-right">
|
|
<span tooltip data-original-title="{{localStateTotal.files | alwaysNumber | localeNumber}} {{'files' | translate}}, {{ localStateTotal.directories | alwaysNumber | localeNumber}} {{'directories' | translate}}, ~{{ localStateTotal.bytes | binary}}B">
|
|
<span class="far fa-copy"></span> {{localStateTotal.files | alwaysNumber | localeNumber}} 
|
|
<span class="far fa-folder"></span> {{localStateTotal.directories| alwaysNumber | localeNumber}} 
|
|
<span class="far fa-hdd"></span> ~{{localStateTotal.bytes | binary}}B
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="fas fa-fw fa-sitemap"></span> <span translate>Listeners</span></th>
|
|
<td class="text-right">
|
|
<span class="data" tooltip data-original-title="{{'Show detailed listener status' | translate}}.">
|
|
<a href="" ng-class="{'text-success': listenersTotal > 0 && listenersFailed.length == 0, 'text-danger': listenersTotal > 0 && listenersFailed.length == listenersTotal}" ng-click="showListenerStatus()">
|
|
{{listenersTotal-listenersFailed.length}}/{{listenersTotal}}
|
|
</a>
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="system.discoveryEnabled">
|
|
<th><span class="fas fa-fw fa-map-signs"></span> <span translate>Discovery</span></th>
|
|
<td class="text-right">
|
|
<span class="data" tooltip data-original-title="{{'Show detailed discovery status' | translate}}.">
|
|
<a href="" ng-class="{'text-success': discoveryFailed.length == 0, 'text-danger': discoveryFailed.length == discoveryTotal}" ng-click="showDiscoveryStatus()">
|
|
{{discoveryTotal-discoveryFailed.length}}/{{discoveryTotal}}
|
|
</a>
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="far fa-fw fa-clock"></span> <span translate>Uptime</span></th>
|
|
<td class="text-right">{{system.uptime | duration:"m"}}</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="fas fa-fw fa-qrcode"></span> <span translate>Identification</span></th>
|
|
<td class="text-right">
|
|
<span tooltip data-original-title="{{'Click to see full identification string and QR code.' | translate}}">
|
|
<a href="" ng-click="showDeviceIdentification(thisDevice())">{{deviceShortID(deviceCfg.deviceID)}}</a>
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="fas fa-fw fa-tag"></span> <span translate>Version</span></th>
|
|
<td class="text-right">
|
|
<span tooltip data-original-title="{{versionString()}}">{{versionString()}}</span>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Remote devices -->
|
|
<h3 translate>Remote Devices</h3>
|
|
<div class="panel-group" id="devices">
|
|
<div class="panel panel-default" ng-repeat="deviceCfg in otherDevices()">
|
|
<button class="btn panel-heading" data-toggle="collapse" data-parent="#devices" data-target="#device-{{$index}}" aria-expanded="false">
|
|
<div class="panel-progress" ng-show="deviceStatus(deviceCfg) == 'syncing'" ng-attr-style="width: {{completion[deviceCfg.deviceID]._total | percent}}"></div>
|
|
<h4 class="panel-title">
|
|
<identicon class="panel-icon" data-value="deviceCfg.deviceID"></identicon>
|
|
<span ng-switch="deviceStatus(deviceCfg)" class="pull-right text-{{deviceClass(deviceCfg)}}">
|
|
<span ng-switch-when="insync"><span class="hidden-xs" translate>Up to Date</span><span class="visible-xs" aria-label="{{'Up to Date' | translate}}"><i class="fas fa-fw fa-check"></i></span></span>
|
|
<span ng-switch-when="unused-insync"><span class="hidden-xs" translate>Connected (Unused)</span><span class="visible-xs" aria-label="{{'Connected (Unused)' | translate}}"><i class="fas fa-fw fa-unlink"></i></span></span>
|
|
<span ng-switch-when="syncing">
|
|
<span class="hidden-xs" translate>Syncing</span> ({{completion[deviceCfg.deviceID]._total | percent}}, {{completion[deviceCfg.deviceID]._needBytes | binary}}B)
|
|
</span>
|
|
<span ng-switch-when="paused"><span class="hidden-xs" translate>Paused</span><span class="visible-xs" aria-label="{{'Paused' | translate}}"><i class="fas fa-fw fa-pause"></i></span></span>
|
|
<span ng-switch-when="unused-paused"><span class="hidden-xs" translate>Paused (Unused)</span><span class="visible-xs" aria-label="{{'Paused (Unused)' | translate}}"><i class="fas fa-fw fa-unlink"></i></span></span>
|
|
<span ng-switch-when="disconnected"><span class="hidden-xs" translate>Disconnected</span><span class="visible-xs" aria-label="{{'Disconnected' | translate}}"><i class="fas fa-fw fa-power-off"></i></span></span>
|
|
<span ng-switch-when="unused-disconnected"><span class="hidden-xs" translate>Disconnected (Unused)</span><span class="visible-xs" aria-label="{{'Disconnected (Unused)' | translate}}"><i class="fas fa-fw fa-unlink"></i></span></span>
|
|
</span>
|
|
<div class="panel-title-text">{{deviceName(deviceCfg)}}</div>
|
|
</h4>
|
|
</button>
|
|
<div id="device-{{$index}}" class="panel-collapse collapse">
|
|
<div class="panel-body">
|
|
<table class="table table-condensed table-striped table-auto">
|
|
<tbody>
|
|
<tr ng-if="!connections[deviceCfg.deviceID].connected">
|
|
<th><span class="fas fa-fw fa-eye"></span> <span translate>Last seen</span></th>
|
|
<td translate ng-if="!deviceStats[deviceCfg.deviceID].lastSeenDays || deviceStats[deviceCfg.deviceID].lastSeenDays >= 365" class="text-right">Never</td>
|
|
<td ng-if="deviceStats[deviceCfg.deviceID].lastSeenDays < 365" class="text-right">{{deviceStats[deviceCfg.deviceID].lastSeen | date:"yyyy-MM-dd HH:mm:ss"}}</td>
|
|
</tr>
|
|
<tr ng-if="!connections[deviceCfg.deviceID].connected && deviceFolders(deviceCfg).length > 0">
|
|
<th><span class="fas fa-fw fa-cloud"></span> <span translate>Sync Status</span></th>
|
|
<td translate ng-if="completion[deviceCfg.deviceID]._total == 100" class="text-right">Up to Date</td>
|
|
<td ng-if="completion[deviceCfg.deviceID]._total < 100" class="text-right">
|
|
<span class="hidden-xs" translate>Out of Sync</span> ({{completion[deviceCfg.deviceID]._total | percent}}, {{completion[deviceCfg.deviceID]._needBytes | binary}}B)
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="connections[deviceCfg.deviceID].connected">
|
|
<th><span class="fas fa-fw fa-cloud-download-alt"></span> <span translate>Download Rate</span></th>
|
|
<td class="text-right">
|
|
<a href="#" class="toggler" ng-click="toggleUnits()">
|
|
<span ng-if="!metricRates">{{connections[deviceCfg.deviceID].inbps | binary}}B/s</span>
|
|
<span ng-if="metricRates">{{connections[deviceCfg.deviceID].inbps*8 | metric}}bps</span>
|
|
({{connections[deviceCfg.deviceID].inBytesTotal | binary}}B)
|
|
<small ng-if="deviceCfg.maxRecvKbps > 0"><br/>
|
|
<i class="text-muted"><span translate>Limit</span>:
|
|
<span ng-if="!metricRates">{{deviceCfg.maxRecvKbps*1024 | binary}}B/s</span>
|
|
<span ng-if="metricRates">{{deviceCfg.maxRecvKbps*1024*8 | metric}}bps</span>
|
|
</i>
|
|
</small>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="connections[deviceCfg.deviceID].connected">
|
|
<th><span class="fas fa-fw fa-cloud-upload-alt"></span> <span translate>Upload Rate</span></th>
|
|
<td class="text-right">
|
|
<a href="#" class="toggler" ng-click="toggleUnits()">
|
|
<span ng-if="!metricRates">{{connections[deviceCfg.deviceID].outbps | binary}}B/s</span>
|
|
<span ng-if="metricRates">{{connections[deviceCfg.deviceID].outbps*8 | metric}}bps</span>
|
|
({{connections[deviceCfg.deviceID].outBytesTotal | binary}}B)
|
|
<small ng-if="deviceCfg.maxSendKbps > 0"><br/>
|
|
<i class="text-muted"><span translate>Limit</span>:
|
|
<span ng-if="!metricRates">{{deviceCfg.maxSendKbps*1024 | binary}}B/s</span>
|
|
<span ng-if="metricRates">{{deviceCfg.maxSendKbps*1024*8 | metric}}bps</span>
|
|
</i>
|
|
</small>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="deviceStatus(deviceCfg) == 'syncing'">
|
|
<th><span class="fas fa-fw fa-exchange-alt"></span> <span translate>Out of Sync Items</span></th>
|
|
<td class="text-right">
|
|
<a href="" ng-click="showRemoteNeed(deviceCfg)">{{completion[deviceCfg.deviceID]._needItems | alwaysNumber | localeNumber}} <span translate>items</span>, ~{{completion[deviceCfg.deviceID]._needBytes | binary}}B</a>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="fas fa-fw fa-link"></span> <span translate>Address</span></th>
|
|
<td ng-if="connections[deviceCfg.deviceID].connected" class="text-right">
|
|
<span tooltip data-original-title="{{ connections[deviceCfg.deviceID].type.indexOf('Relay') > -1 ? '' : connections[deviceCfg.deviceID].type }} {{ connections[deviceCfg.deviceID].crypto }}">
|
|
{{deviceAddr(deviceCfg)}}
|
|
</span>
|
|
</td>
|
|
<td ng-if="!connections[deviceCfg.deviceID].connected" class="text-right">
|
|
<span ng-repeat="addr in deviceCfg.addresses">
|
|
<span tooltip data-original-title="{{'Configured' | translate}}">{{addr}}</span><br>
|
|
<small ng-if="system.lastDialStatus[addr].error" tooltip data-original-title="{{system.lastDialStatus[addr].error}}" class="text-danger">{{abbreviatedError(addr)}}<br></small>
|
|
</span>
|
|
<span ng-repeat="addr in discoveryCache[deviceCfg.deviceID].addresses">
|
|
<span tooltip data-original-title="{{'Discovered' | translate}}">{{addr}}</span><br>
|
|
<small ng-if="system.lastDialStatus[addr].error" tooltip data-original-title="{{system.lastDialStatus[addr].error}}" class="text-danger">{{abbreviatedError(addr)}}<br></small>
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="connections[deviceCfg.deviceID].connected && connections[deviceCfg.deviceID].type.indexOf('Relay') > -1" tooltip data-original-title="Connections via relays might be rate limited by the relay">
|
|
<th><span class="fas fa-fw fa-exclamation-triangle text-danger"></span> <span translate>Connection Type</span></th>
|
|
<td class="text-right">{{connections[deviceCfg.deviceID].type}}</td>
|
|
</tr>
|
|
<tr ng-if="deviceCfg.allowedNetworks.length > 0">
|
|
<th><span class="fas fa-fw fa-filter"></span> <span translate>Allowed Networks</span></th>
|
|
<td class="text-right">
|
|
<span>{{deviceCfg.allowedNetworks.join(", ")}}</span>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="deviceCfg.compression != 'metadata'">
|
|
<th><span class="fas fa-fw fa-compress"></span> <span translate>Compression</span></th>
|
|
<td class="text-right">
|
|
<span ng-if="deviceCfg.compression == 'always'" translate>All Data</span>
|
|
<span ng-if="deviceCfg.compression == 'never'" translate>Off</span>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="deviceCfg.introducer">
|
|
<th><span class="far fa-fw fa-thumbs-up"></span> <span translate>Introducer</span></th>
|
|
<td translate class="text-right">Yes</td>
|
|
</tr>
|
|
<tr ng-if="deviceCfg.introducedBy">
|
|
<th><span class="far fa-fw fa-handshake-o"></span> <span translate>Introduced By</span></th>
|
|
<td class="text-right">{{ deviceName(devices[deviceCfg.introducedBy]) || deviceCfg.introducedBy.substring(0, 5) }}</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="fas fa-fw fa-qrcode"></span> <span translate>Identification</span></th>
|
|
<td class="text-right">
|
|
<span tooltip data-original-title="{{'Click to see full identification string and QR code.' | translate}}">
|
|
<a href="" ng-click="showDeviceIdentification(deviceCfg)">{{deviceShortID(deviceCfg.deviceID)}}</a>
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="connections[deviceCfg.deviceID].clientVersion">
|
|
<th><span class="fas fa-fw fa-tag"></span> <span translate>Version</span></th>
|
|
<td class="text-right">{{connections[deviceCfg.deviceID].clientVersion}}</td>
|
|
</tr>
|
|
<tr ng-if="deviceFolders(deviceCfg).length > 0">
|
|
<th><span class="fas fa-fw fa-folder"></span> <span translate>Folders</span></th>
|
|
<td class="text-right">
|
|
<span tooltip data-original-title="{{sharedFolders(deviceCfg)}} {{deviceHasUnacceptedFolders(deviceCfg) ? '<br/>(<sup>1</sup>' + ('The remote device has not accepted sharing this folder.' | translate) + ')' : '' }} {{deviceHasPausedFolders(deviceCfg) ? '<br/>(<sup>2</sup>' + ('The remote device has paused this folder.' | translate) + ')' : '' }}" ng-bind-html="sharedFolders(deviceCfg)"></span>
|
|
</td>
|
|
</tr>
|
|
<tr ng-if="deviceCfg.remoteGUIPort > 0">
|
|
<th><span class="fas fa-fw fa-desktop"></span> <span translate>Remote GUI</span></th>
|
|
<td class="text-right" ng-attr-title="Port {{deviceCfg.remoteGUIPort}}">
|
|
<!-- Apply RFC6874 encoding for IPv6 link-local zone identifier -->
|
|
<a ng-if="hasRemoteGUIAddress(deviceCfg)" href="{{remoteGUIAddress(deviceCfg).replace('%', '%25')}}">{{remoteGUIAddress(deviceCfg)}}</a>
|
|
<span translate ng-if="!hasRemoteGUIAddress(deviceCfg)">Unknown</span>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="panel-footer">
|
|
<span class="pull-right">
|
|
<button ng-if="!deviceCfg.paused" type="button" class="btn btn-sm btn-default" ng-click="setDevicePause(deviceCfg.deviceID, true)">
|
|
<span class="fas fa-pause"></span> <span translate>Pause</span>
|
|
</button>
|
|
<button ng-if="deviceCfg.paused" type="button" class="btn btn-sm btn-default" ng-click="setDevicePause(deviceCfg.deviceID, false)">
|
|
<span class="fas fa-play"></span> <span translate>Resume</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-default" ng-click="editDeviceExisting(deviceCfg)">
|
|
<span class="fas fa-pencil-alt"></span> <span translate>Edit</span>
|
|
</button>
|
|
</span>
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<span class="pull-right">
|
|
<button type="button" class="btn btn-sm btn-default" ng-click="setAllDevicesPause(true)" ng-if="isAtleastOneDevicePausedStateSetTo(false)">
|
|
<span class="fas fa-pause"></span> <span translate>Pause All</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-default" ng-click="setAllDevicesPause(false)" ng-if="isAtleastOneDevicePausedStateSetTo(true)">
|
|
<span class="fas fa-play"></span> <span translate>Resume All</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-default" ng-click="globalChanges()">
|
|
<span class="fas fa-fw fa-info-circle"></span> <span translate>Recent Changes</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-default" ng-click="addDevice()">
|
|
<span class="fas fa-plus"></span> <span translate>Add Remote Device</span>
|
|
</button>
|
|
</span>
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
</div>
|
|
</div> <!-- /row -->
|
|
|
|
</div> <!-- /container -->
|
|
</div> <!-- /ng-cloak -->
|
|
|
|
<!-- Bottom bar -->
|
|
|
|
<nav class="navbar navbar-default navbar-fixed-bottom">
|
|
<div class="container">
|
|
<ul class="nav navbar-nav">
|
|
<li><a class="navbar-link" href="https://syncthing.net/" target="_blank"><span class="fas fa-home"></span> <span translate>Home page</span></a></li>
|
|
<li><a class="navbar-link" href="{{docsURL()}}" target="_blank"><span class="fas fa-book"></span> <span translate>Documentation</span></a></li>
|
|
<li><a class="navbar-link" href="https://forum.syncthing.net" target="_blank"><span class="fas fa-question-circle"></span> <span translate>Support</span></a></li>
|
|
<li><a class="navbar-link" href="https://data.syncthing.net/" target="_blank"><span class="fas fa-bar-chart"></span> <span translate>Statistics</span></a></li>
|
|
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing/releases" target="_blank"><span class="far fa-file-alt"></span> <span translate>Changelog</span></a></li>
|
|
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing/issues" target="_blank"><span class="fas fa-bug"></span> <span translate>Bugs</span></a></li>
|
|
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing" target="_blank"><span class="fas fa-wrench"></span> <span translate>Source Code</span></a></li>
|
|
<li><a class="navbar-link" href="https://twitter.com/syncthing" target="_blank"><span class="fab fa-twitter"></span> <span translate>Twitter</span></a></li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
<ng-include src="'syncthing/core/networkErrorDialogView.html'"></ng-include>
|
|
<ng-include src="'syncthing/core/httpErrorDialogView.html'"></ng-include>
|
|
<ng-include src="'syncthing/core/restartingDialogView.html'"></ng-include>
|
|
<ng-include src="'syncthing/core/upgradingDialogView.html'"></ng-include>
|
|
<ng-include src="'syncthing/core/shutdownDialogView.html'"></ng-include>
|
|
<ng-include src="'syncthing/device/idqrModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/device/editDeviceModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/device/globalChangesModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/folder/editFolderModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/folder/restoreVersionsModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/folder/restoreVersionsConfirmation.html'"></ng-include>
|
|
<ng-include src="'syncthing/settings/settingsModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/settings/advancedSettingsModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/settings/discardChangesConfirmation.html'"></ng-include>
|
|
<ng-include src="'syncthing/usagereport/usageReportModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/usagereport/usageReportPreviewModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/transfer/neededFilesModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/transfer/failedFilesModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/transfer/remoteNeededFilesModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/transfer/localChangedFilesModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/core/upgradeModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/core/majorUpgradeModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/core/aboutModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/core/connectivityStatusModalView.html'"></ng-include>
|
|
<ng-include src="'syncthing/folder/removeFolderDialogView.html'"></ng-include>
|
|
<ng-include src="'syncthing/folder/revertOverrideView.html'"></ng-include>
|
|
<ng-include src="'syncthing/device/removeDeviceDialogView.html'"></ng-include>
|
|
<ng-include src="'syncthing/core/logViewerModalView.html'"></ng-include>
|
|
|
|
<!-- vendor scripts -->
|
|
<script type="text/javascript" src="vendor/jquery/jquery-2.2.2.js"></script>
|
|
<script type="text/javascript" src="vendor/angular/angular.js"></script>
|
|
<script type="text/javascript" src="vendor/angular/angular-sanitize.js"></script>
|
|
<script type="text/javascript" src="vendor/angular/angular-translate.js"></script>
|
|
<script type="text/javascript" src="vendor/angular/angular-translate-loader-static-files.js"></script>
|
|
<script type="text/javascript" src="vendor/angular/angular-dirPagination.js"></script>
|
|
<script type="text/javascript" src="vendor/moment/moment.js"></script>
|
|
<script type="text/javascript" src="vendor/bootstrap/js/bootstrap.js"></script>
|
|
<script type="text/javascript" src="vendor/daterangepicker/daterangepicker.js"></script>
|
|
<script type="text/javascript" src="vendor/fancytree/jquery.fancytree-all-deps.js"></script>
|
|
<!-- / vendor scripts -->
|
|
|
|
<!-- gui application code -->
|
|
<script type="text/javascript" src="syncthing/core/module.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/alwaysNumberFilter.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/basenameFilter.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/binaryFilter.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/localeNumberFilter.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/percentFilter.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/durationFilter.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/eventService.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/identiconDirective.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/languageSelectDirective.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/lastErrorComponentFilter.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/localeService.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/modalDirective.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/metricFilter.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/notificationDirective.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/pathIsSubDirDirective.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/popoverDirective.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/selectOnClickDirective.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/syncthingController.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/tooltipDirective.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/uncamelFilter.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/uniqueFolderDirective.js"></script>
|
|
<script type="text/javascript" src="syncthing/core/validDeviceidDirective.js"></script>
|
|
<script type="text/javascript" src="assets/lang/valid-langs.js"></script>
|
|
<script type="text/javascript" src="assets/lang/prettyprint.js"></script>
|
|
<script type="text/javascript" src="meta.js"></script>
|
|
<script type="text/javascript" src="syncthing/app.js"></script>
|
|
<!-- / gui application code -->
|
|
|
|
</body>
|
|
</html>
|