Files
cloudron-box/src/views/app.html

1122 lines
75 KiB
HTML
Raw Normal View History

<script>
function imageErrorHandler(elem) {
'use strict';
elem.src = elem.getAttribute('fallback-icon');
elem.onerror = null; // avoid retry after default icon cannot be loaded
}
</script>
<!-- Modal postinstall -->
<div class="modal fade" id="postInstallModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4>{{ 'app.appInfo.firstTimeTitle' | tr }}</h4>
</div>
<div class="modal-body">
<p ng-show="app.manifest.addons.email && postInstallMessage.openApp">{{ 'app.appInfo.ssoEmail' | tr }}</p>
<p ng-show="app.sso && !app.manifest.addons.email && postInstallMessage.openApp">{{ 'app.appInfo.sso' | tr }}</p>
<div ng-bind-html="app.manifest.postInstallMessage | markdown2html"></div>
</div>
<div class="modal-footer">
<div class="form-group pull-left" ng-show="postInstallMessage.openApp">
<input type="checkbox" id="appPostInstallConfirmCheckbox" ng-model="postInstallMessage.confirmed">
<label class="control-label" for="appPostInstallConfirmCheckbox">{{ 'app.appInfo.postInstallConfirmCheckbox' | tr }}</label>
</div>
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
<a class="btn btn-success" ng-href="{{ postInstallMessage.confirmed ? ('https://' + app.fqdn) : '' }}" target="_blank" ng-disabled="!postInstallMessage.confirmed" ng-click="postInstallMessage.submit()" ng-show="postInstallMessage.openApp">{{ 'app.appInfo.openAction' | tr:{ app: app.manifest.title } }}</a>
</div>
</div>
</div>
</div>
<!-- Modal uninstall app -->
<div class="modal fade" id="uninstallModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
2019-09-19 10:28:17 -07:00
<div class="modal-header">
<h4 class="modal-title">{{ 'app.uninstallDialog.title' | tr:{ app: (app.label || app.fqdn) } }}</h4>
2019-09-19 10:28:17 -07:00
</div>
<div class="modal-body">
<p ng-bind-html="'app.uninstallDialog.description' | tr:{ app: (app.label || app.fqdn) }"></p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<button type="button" class="btn btn-danger" ng-click="uninstall.submit()" ng-disabled="uninstall.busy"><i class="fa fa-circle-notch fa-spin" ng-show="uninstall.busy"></i> {{ 'app.uninstallDialog.uninstallAction' | tr }}</button>
</div>
</div>
</div>
</div>
<!-- Modal domain collision -->
<div class="modal fade" id="domainCollisionsModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'app.domainCollisionDialog.title' | tr }}</h4>
</div>
<div class="modal-body">
<p>{{ 'app.domainCollisionDialog.collisionListTitle' | tr }}</p>
<ul>
<li ng-repeat="domain in location.domainCollisions">{{ domain.subdomain + '.' + domain.domain }}</li>
</ul>
<p>{{ 'app.domainCollisionDialog.description' | tr }}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<button type="button" class="btn btn-danger" ng-click="location.submit(true)">{{ 'app.domainCollisionDialog.overwriteAction' | tr }}</button>
</div>
</div>
</div>
</div>
<!-- Modal repair -->
<div class="modal fade" id="repairModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'app.repairDialog.title' | tr:{ app: app.fqdn } }}</h4>
</div>
<div class="modal-body">
<div ng-if="!app.error">
<p>{{ 'app.repairDialog.description' | tr }}</p>
</div>
<div ng-if="app.error">
<p ng-bind-html="'app.repairDialog.taskError' | tr:{ task: (app.error.installationState | taskName) }"></p>
<p class="text-danger">{{ app.error.reason + ': ' + app.error.message }}</p>
</div>
<div class="form-group" ng-show="repair.location && repair.domain">
<p>{{ 'app.repairDialog.domainDescription' | tr }}</p>
<label class="control-label">{{ 'app.repairDialog.location' | tr }}</label>
2019-09-26 22:23:08 -07:00
<div class="input-group form-inline">
<input type="text" class="form-control" ng-model="repair.location" name="location" placeholder="{{ 'Leave empty to use bare domain' }}" autofocus>
2019-09-26 22:23:08 -07:00
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span>{{ (!repair.location ? '' : '.') + repair.domain.domain }}</span>
2019-09-26 22:23:08 -07:00
<span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right" role="menu">
<li ng-repeat="domain in domains">
<a href="" ng-click="repair.domain = domain">{{ domain.domain }}</a>
</li>
</ul>
</div>
</div>
2019-09-26 22:23:08 -07:00
</div>
2021-01-18 17:55:48 -08:00
<div ng-show="repair.aliasDomains.length">
<p ng-repeat="aliasDomain in repair.aliasDomains">
<label class="control-label"><input type="checkbox" ng-model="aliasDomain.enabled">
{{ aliasDomain.subdomain + (!aliasDomain.subdomain ? '' : '.') + aliasDomain.domain.domain }}
</label>
</p>
</div>
<div ng-show="repair.alternateDomains.length">
<p ng-repeat="alternateDomain in repair.alternateDomains">
<label class="control-label"><input type="checkbox" ng-model="alternateDomain.enabled">
{{ alternateDomain.subdomain + (!alternateDomain.subdomain ? '' : '.') + alternateDomain.domain.domain }}
</label>
</p>
</div>
2019-09-23 15:01:44 +02:00
<div ng-show="repair.backups.length">
<label class="control-label">{{ 'app.repairDialog.fromBackup' | tr }}</label>
<select class="form-control" ng-model="repair.backupId">
2020-05-13 22:37:11 -07:00
<option ng-repeat="backup in repair.backups" value="{{ backup.id }}">{{ backup.creationTime | prettyDate }} - v{{ backup.packageVersion }}</option>
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
2019-12-20 15:38:40 -08:00
<button type="button" class="btn btn-success" ng-click="repair.submit()" ng-disabled="repair.retryBusy">
<i class="fa fa-circle-notch fa-spin" ng-show="repair.retryBusy"></i> {{ 'app.repairDialog.retryAction' | tr:{ task: (app.error.installationState | taskName) } }}
2019-12-20 15:38:40 -08:00
</button>
</div>
</div>
</div>
</div>
2020-02-06 16:08:22 -08:00
<!-- modal import backup -->
<div class="modal fade" id="importBackupModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'app.importBackupDialog.title' | tr }}</h4>
2020-02-06 16:08:22 -08:00
</div>
<div class="modal-body">
<p class="text-info">{{ 'app.importBackupDialog.description' | tr }}</p>
2020-02-06 16:08:22 -08:00
<form name="importBackupForm" role="form" novalidate ng-submit="importBackup.submit()" autocomplete="off">
<p class="has-error text-center" ng-show="backups.error">{{ importBackup.error.generic }}</p>
2020-02-06 16:08:22 -08:00
<div class="form-group">
<label class="control-label" for="storageProvider">{{ 'backups.configureBackupStorage.provider' | tr }} <sup><a ng-href="https://docs.cloudron.io/backups/#storage-providers" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
<select class="form-control" id="storageProvider" ng-model="importBackup.provider" ng-options="a.value as a.name for a in storageProvider" ng-change="importBackup.clearForm()" ng-disabled="importBackup.busy"></select>
</div>
2020-02-06 16:08:22 -08:00
<div class="form-group" ng-class="{ 'has-error': importBackup.error.key }">
<label ng-show="importBackup.provider !== 'filesystem'" class="control-label" for="inputImportBackupId">Backup ID</label>
<label ng-show="importBackup.provider === 'filesystem'" class="control-label" for="inputImportBackupId">Backup Path</label>
<input type="text" class="form-control" ng-model="importBackup.backupId" id="inputImportBackupId" ng-disabled="importBackup.busy" required>
</div>
2020-02-06 16:08:22 -08:00
<!-- S3/Minio/SOS/GCS -->
<div class="form-group" ng-class="{ 'has-error': importBackup.error.endpoint }" ng-show="importBackup.provider === 'minio' || importBackup.provider === 'backblaze-b2' || importBackup.provider === 's3-v4-compat'">
<label class="control-label" for="inputimportBackupEndpoint">{{ 'backups.configureBackupStorage.s3Endpoint' | tr }}</label>
<input type="text" class="form-control" ng-model="importBackup.endpoint" id="inputimportBackupEndpoint" name="endpoint" ng-disabled="importBackup.busy" placeholder="URL" ng-required="importBackup.provider === 'minio' || importBackup.provider === 'backblaze-b2' || importBackup.provider === 's3-v4-compat'">
</div>
2020-02-06 16:08:22 -08:00
<div class="checkbox" ng-show="importBackup.provider === 'minio' || importBackup.provider === 's3-v4-compat'" >
<label>
<input type="checkbox" ng-model="importBackup.acceptSelfSignedCerts" id="inputimportBackupSelfSigned">Accept Self-signed certificate</input>
</label>
</div>
2020-02-06 16:08:22 -08:00
<div class="form-group" ng-class="{ 'has-error': importBackup.error.bucket }" ng-show="s3like(importBackup.provider) || importBackup.provider === 'gcs'">
<label class="control-label" for="inputImportBackupBucket">{{ 'backups.configureBackupStorage.bucketName' | tr }}</label>
<input type="text" class="form-control" ng-model="importBackup.bucket" id="inputImportBackupBucket" name="bucket" ng-disabled="importBackup.busy" ng-required="s3like(importBackup.provider)">
</div>
2020-02-06 16:08:22 -08:00
<!-- SSHFS/CIFS/NFS -->
2021-01-07 18:40:51 +01:00
<div class="form-group" ng-class="{ 'has-error': importBackup.error.mountPoint }" ng-show="mountlike(importBackup.provider)">
<label class="control-label" for="inputConfigureMountPoint">{{ 'backups.configureBackupStorage.mountPoint' | tr }}</label>
<input type="text" class="form-control" ng-model="importBackup.mountPoint" id="inputConfigureMountPoint" name="mountPoint" ng-disabled="importBackup.busy" placeholder="Folder where filesystem is mounted" ng-required="mountlike(importBackup.provider)">
</div>
<!-- Filesystem -->
<div class="form-group" ng-class="{ 'has-error': importBackup.error.prefix }" ng-show="importBackup.provider !== 'filesystem' && importBackup.provider !== ''">
<label class="control-label" for="inputimportBackupPrefix">{{ 'backups.configureBackupStorage.prefix' | tr }}</label>
<input type="text" class="form-control" ng-model="importBackup.prefix" id="inputimportBackupPrefix" name="prefix" ng-disabled="importBackup.busy" placeholder="Prefix for backup file names">
</div>
2020-02-06 16:08:22 -08:00
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 's3'">
<label class="control-label" for="inputImportBackupS3Region">{{ 'backups.configureBackupStorage.region' | tr }}</label>
<select class="form-control" name="region" id="inputImportBackupS3Region" ng-model="importBackup.region" ng-options="a.value as a.name for a in s3Regions" ng-disabled="importBackup.busy" ng-required="importBackup.provider === 's3'"></select>
</div>
2020-02-06 16:08:22 -08:00
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 's3-v4-compat'">
<label class="control-label" for="inputImportBackupS3V4CompatRegion">{{ 'backups.configureBackupStorage.region' | tr }}</label>
<input class="form-control" type="text" name="region" id="inputImportBackupS3V4CompatRegion" ng-model="importBackup.region" ng-disabled="importBackup.busy" placeholder="Leave empty to use us-east-1 as default"></input>
</div>
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 'digitalocean-spaces'">
<label class="control-label" for="inputImportBackupDORegion">{{ 'backups.configureBackupStorage.region' | tr }}</label>
<select class="form-control" name="region" id="inputImportBackupDORegion" ng-model="importBackup.endpoint" ng-options="a.value as a.name for a in doSpacesRegions" ng-disabled="importBackup.busy" ng-required="importBackup.provider === 'digitalocean-spaces'"></select>
</div>
2020-02-06 16:08:22 -08:00
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 'exoscale-sos'">
<label class="control-label" for="inputimportBackupExoscaleRegion">{{ 'backups.configureBackupStorage.region' | tr }}</label>
<select class="form-control" name="region" id="inputimportBackupExoscaleRegion" ng-model="importBackup.endpoint" ng-options="a.value as a.name for a in exoscaleSosRegions" ng-disabled="importBackup.busy" ng-required="importBackup.provider === 'exoscale-sos'"></select>
</div>
2020-02-06 16:08:22 -08:00
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 'wasabi'">
<label class="control-label" for="inputimportBackupWasabiRegion">{{ 'backups.configureBackupStorage.region' | tr }}</label>
<select class="form-control" name="region" id="inputimportBackupWasabiRegion" ng-model="importBackup.endpoint" ng-options="a.value as a.name for a in wasabiRegions" ng-disabled="importBackup.busy" ng-required="importBackup.provider === 'wasabi'"></select>
</div>
2020-02-06 16:08:22 -08:00
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 'scaleway-objectstorage'">
<label class="control-label" for="inputimportBackupScalewayRegion">{{ 'backups.configureBackupStorage.region' | tr }}</label>
<select class="form-control" name="region" id="inputimportBackupScalewayRegion" ng-model="importBackup.endpoint" ng-options="a.value as a.name for a in scalewayRegions" ng-disabled="importBackup.busy" ng-required="importBackup.provider === 'scaleway-objectstorage'"></select>
</div>
2020-02-06 16:08:22 -08:00
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 'linode-objectstorage'">
<label class="control-label" for="inputimportBackupLinodeRegion">{{ 'backups.configureBackupStorage.region' | tr }}</label>
<select class="form-control" name="region" id="inputimportBackupLinodeRegion" ng-model="importBackup.endpoint" ng-options="a.value as a.name for a in linodeRegions" ng-disabled="importBackup.busy" ng-required="importBackup.provider === 'linode-objectstorage'"></select>
</div>
2020-03-05 11:24:42 -08:00
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 'ovh-objectstorage'">
<label class="control-label" for="inputimportBackupOvhRegion">{{ 'backups.configureBackupStorage.region' | tr }}</label>
<select class="form-control" name="region" id="inputimportBackupOvhRegion" ng-model="importBackup.endpoint" ng-options="a.value as a.name for a in ovhRegions" ng-disabled="importBackup.busy" ng-required="importBackup.provider === 'ovh-objectstorage'"></select>
</div>
2020-04-29 12:54:19 -07:00
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 'ionos-objectstorage'">
<label class="control-label" for="inputimportBackupIonosRegion">{{ 'backups.configureBackupStorage.region' | tr }}</label>
<select class="form-control" name="region" id="inputimportBackupIonosRegion" ng-model="importBackup.endpoint" ng-options="a.value as a.name for a in ionosRegions" ng-disabled="importBackup.busy" ng-required="importBackup.provider === 'ionos-objectstorage'"></select>
</div>
<div class="form-group" ng-class="{ 'has-error': importBackup.error.accessKeyId }" ng-show="s3like(importBackup.provider)">
<label class="control-label" for="inputImportBackupAccessKeyId">{{ 'backups.configureBackupStorage.s3AccessKeyId' | tr }}</label>
<input type="text" class="form-control" ng-model="importBackup.accessKeyId" id="inputImportBackupAccessKeyId" name="accessKeyId" ng-disabled="importBackup.busy" ng-required="s3like(importBackup.provider)">
</div>
2020-02-06 16:08:22 -08:00
<div class="form-group" ng-class="{ 'has-error': importBackup.error.secretAccessKey }" ng-show="s3like(importBackup.provider)">
<label class="control-label" for="inputImportBackupSecretAccessKey">{{ 'backups.configureBackupStorage.s3SecretAccessKey' | tr }}</label>
<input type="text" class="form-control" ng-model="importBackup.secretAccessKey" id="inputImportBackupSecretAccessKey" name="secretAccessKey" ng-disabled="importBackup.busy" ng-required="s3like(importBackup.provider)">
</div>
2020-02-06 16:08:22 -08:00
<div class="form-group" ng-class="{ 'has-error': importBackup.error.gcsKeyInput }" ng-show="importBackup.provider === 'gcs'">
<label class="control-label" for="gcsKeyInput">{{ 'backups.configureBackupStorage.gcsServiceKey' | tr }}</label>
2020-02-06 16:08:22 -08:00
<div class="input-group">
<input type="file" id="gcsKeyFileInput" style="display:none"/>
<input type="text" class="form-control" placeholder="Service Account Key" ng-model="importBackup.gcsKey.keyFileName" id="gcsKeyInput" name="cert" onclick="getElementById('gcsKeyFileInput').click();" style="cursor: pointer;" ng-disabled="importBackup.busy" ng-required="importBackup.provider === 'gcs'">
<span class="input-group-addon">
<i class="fa fa-upload" onclick="getElementById('gcsKeyFileInput').click();"></i>
</span>
2020-02-06 16:08:22 -08:00
</div>
</div>
2020-02-06 16:08:22 -08:00
<div class="form-group">
<label class="control-label" for="storageFormat">{{ 'backups.configureBackupStorage.format' | tr }} <sup><a ng-href="https://docs.cloudron.io/backups/#backup-formats" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
<select class="form-control" id="storageFormat" ng-change="importBackup.password = ''" ng-model="importBackup.format" ng-options="a.value as a.name for a in formats" ng-disabled="importBackup.busy"></select>
</div>
2020-02-06 16:08:22 -08:00
<div class="form-group" ng-class="{ 'has-error': importBackup.error.password }">
<label class="control-label" for="inputImportBackupPassword">{{ 'backups.configureBackupStorage.encryptionPassword' | tr }} <sup><a ng-href="https://docs.cloudron.io/backups/#encryption" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
<input type="text" class="form-control" ng-model="importBackup.password" id="inputImportBackupPassword" ng-disabled="importBackup.busy" placeholder="Passphrase used to encrypt the backups">
</div>
2020-02-06 16:08:22 -08:00
<input class="ng-hide" type="submit" ng-disabled="importBackupForm.$invalid"/>
2020-02-06 16:08:22 -08:00
</form>
</div>
<div class="modal-footer ">
<input type="file" id="backupConfigFileInput" style="display:none"/>
<button type="button" class="btn btn-default pull-left" onclick="getElementById('backupConfigFileInput').click();">{{ 'app.importBackupDialog.uploadAction' | tr }}</button>
2020-02-06 16:08:22 -08:00
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<button type="submit" class="btn btn-outline btn-success pull-right" ng-click="importBackup.submit()" ng-disabled="importBackupForm.$invalid || importBackup.busy"><i class="fa fa-circle-notch fa-spin" ng-show="importBackup.busy"></i> {{ 'app.importBackupDialog.importAction' | tr }}</button>
</div>
</div>
</div>
</div>
<!-- Modal update app -->
<div class="modal fade" id="updateModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'app.updateDialog.title' | tr:{ app: app.fqdn } }}</h4>
</div>
<div class="modal-body">
<p class="text-danger" ng-show="config.update[app.id].unstable">{{ 'app.updateDialog.unstableWarning' | tr }}</p>
<p>{{ 'app.updateDialog.changelogHeader' | tr:{ version: config.update[app.id].manifest.version } }}</p>
<div ng-bind-html="config.update[app.id].manifest.changelog | markdown2html"></div>
<p class="text-danger text-bold" ng-show="!config.update[app.id].manifest.dockerImage">
<br/>
{{ 'app.updateDialog.subscriptionExpired' | tr }}
</p>
</div>
<div class="modal-footer">
<label class="checkbox-inline pull-left" ng-show="config.update[app.id].manifest.dockerImage">
<input type="checkbox" ng-model="updates.skipBackup"><b>{{ 'app.updateDialog.skipBackupCheckbox' | tr }}</b>
</label>
<button type="button" class="btn btn-primary pull-left" ng-show="!config.update[app.id].manifest.dockerImage && user.isAtLeastOwner" ng-click="openSubscriptionSetup()">{{ 'app.updateDialog.setupSubscriptionAction' | tr }}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<button type="button" ng-class="config.update[app.id].unstable ? 'btn btn-danger' : 'btn btn-success'" ng-click="updates.confirmUpdate()" ng-disabled="!config.update[app.id].manifest.dockerImage || updates.busyUpdate"><i class="fa fa-circle-notch fa-spin" ng-show="updates.busyUpdate"></i> {{ 'app.updateDialog.updateAction' | tr }}</button>
</div>
</div>
</div>
</div>
2019-10-24 10:01:23 -07:00
<!-- Modal restore app -->
<div class="modal fade" id="restoreModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'app.restoreDialog.title' | tr:{ app: app.fqdn } }}</h4>
2019-10-24 10:01:23 -07:00
</div>
<div class="modal-body" style="padding: 0 15px">
<p>{{ 'app.restoreDialog.description' | tr:{ creationTime: (restore.backup.creationTime | prettyDate) } }}</p>
<p class="text-danger">{{ 'app.restoreDialog.warning' | tr }}</p>
2019-10-24 10:07:34 -07:00
<br/>
2019-10-24 10:01:23 -07:00
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
<button type="button" class="btn btn-danger" ng-click="restore.submit()"><i class="fas fa-history" ng-hide="restore.busy"></i><i class="fa fa-circle-notch fa-spin" ng-show="clone.busy"></i> {{ 'app.restoreDialog.restoreAction' | tr }}</button>
2019-10-24 10:01:23 -07:00
</div>
</div>
</div>
</div>
2019-09-13 17:18:37 +02:00
<!-- Modal clone app -->
<div class="modal fade" id="cloneModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'app.cloneDialog.title' | tr:{ app: app.fqdn } }}</h4>
2019-09-13 17:18:37 +02:00
</div>
<div class="modal-body" style="padding: 0 15px">
<p ng-bind-html="'app.cloneDialog.description' | tr:{ creationTime: (clone.backup.creationTime | prettyDate), packageVersion: clone.backup.packageVersion }"></p>
2020-03-19 19:26:19 -07:00
<form role="form" ng-submit="clone.submit()" autocomplete="off">
<fieldset>
2019-09-13 17:18:37 +02:00
<div class="form-group" ng-class="{ 'has-error': clone.error.location }">
<label class="control-label" for="cloneLocationInput">{{ 'app.cloneDialog.location' | tr }}</label>
2019-09-13 17:18:37 +02:00
<div ng-show="clone.error.location"><small>{{ clone.error.location }}</small></div>
<div class="input-group form-inline">
<input type="text" class="form-control" ng-model="clone.location" id="cloneLocationInput" name="location" placeholder="{{ 'appstore.installDialog.locationPlaceholder' | tr }}" autofocus>
2019-09-13 17:18:37 +02:00
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span>{{ (!clone.location ? '' : '.') + clone.domain.domain }}</span>
2019-09-13 17:18:37 +02:00
<span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right" role="menu">
<li ng-repeat="domain in domains">
<a href="" ng-click="clone.domain = domain">{{ domain.domain }}</a>
</li>
</ul>
</div>
</div>
</div>
<p class="text-small text-warning" ng-show="clone.domain.provider === 'linode'" ng-bind-html="'appstore.installDialog.linodeWarning' | tr:{ linodeDocsLink: 'https://docs.cloudron.io/domains/#linode-dns' }"></p>
<p class="text-small text-warning" ng-show="clone.location && clone.domain.provider === 'manual'" ng-bind-html="'appstore.installDialog.manualWarning' | tr:{ location: (appInstall.location + '.' + appInstall.domain.domain) }"></p>
2019-09-13 17:18:37 +02:00
2019-09-24 18:50:52 +02:00
<div class="has-error text-center" ng-show="clone.error.port">{{ clone.error.port }}</div>
<div ng-repeat="(env, info) in clone.portBindingsInfo">
2019-09-13 17:18:37 +02:00
<ng-form name="portInfo_form">
2019-09-24 18:50:52 +02:00
<div class="form-group" ng-class="{ 'has-error': (!clone.itemName{{$index}}.$dirty && clone.error.port) || (portInfo_form.itemName{{$index}}.$dirty && portInfo_form.itemName{{$index}}.$invalid) }">
<label class="control-label" for="inputPortInfo{{env}}"><input type="checkbox" ng-model="clone.portBindingsEnabled[env]">
2019-09-13 17:18:37 +02:00
{{ info.title }}
<sup>
<a popover-placement="top-right" popover-trigger="outsideClick" uib-popover="{{info.description}} ({{ HOST_PORT_MIN }} - {{ HOST_PORT_MAX }})"><i class="fa fa-question-circle"></i></a>
</sup>
</label>
2019-09-24 18:50:52 +02:00
<input type="number" class="form-control" ng-model="clone.portBindings[env]" ng-disabled="!clone.portBindingsEnabled[env]" id="inputPortInfo{{env}}" later-name="itemName{{$index}}" min="{{hostPortMin}}" max="{{hostPortMax}}" required>
2019-09-13 17:18:37 +02:00
</div>
</ng-form>
</div>
2020-03-19 19:26:19 -07:00
</fieldset>
</form>
2019-09-13 17:18:37 +02:00
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<button type="button" class="btn btn-success" ng-click="clone.submit()"><i class="far fa-clone" ng-hide="clone.busy"></i><i class="fa fa-circle-notch fa-spin" ng-show="clone.busy"></i> {{ 'app.cloneDialog.cloneAction' | tr }}</button>
2019-09-13 17:18:37 +02:00
</div>
</div>
</div>
</div>
<div class="content content-large app-configure">
<a href="/#/apps" class="back-to-view-link"><i class="fas fa-arrow-left"></i> {{ 'app.backAction' | tr }}</a>
2019-09-16 14:15:38 +02:00
2019-09-12 16:28:21 +02:00
<br/>
<div class="row" ng-show="view">
<div class="col-sm-2 text-center">
<img ng-src="{{ app.iconUrl || 'img/appicon_fallback.png' }}" fallback-icon="img/appicon_fallback.png" onerror="imageErrorHandler(this)" class="app-icon"/>
</div>
<div class="col-sm-8">
<div class="app-header-container">
<h1>
<a ng-href="{{ app | applicationLink }}" target="_blank" ng-class="{ 'hand': (app | appIsInstalledAndHealthy) }" ng-click="(app | appIsInstalledAndHealthy) && app.pendingPostInstallConfirmation && postInstallMessage.show(true)">{{ app.label || app.fqdn }} <sup ng-show="app | appIsInstalledAndHealthy"><i class="fas fa-external-link-alt" style="font-size: 12px;"></i></sup></a>
</h1>
<div>
<a class="btn btn-sm btn-default" ng-href="{{ '/logs.html?appId=' + app.id }}" target="_blank" uib-tooltip="{{ 'app.logsActionTooltip' | tr }}" tooltip-append-to-body="true" tooltip-placement="bottom"><i class="fas fa-align-left"></i></a>
<a class="btn btn-sm btn-default" ng-href="{{ '/terminal.html?id=' + app.id }}" target="_blank" uib-tooltip="{{ 'app.terminalActionTooltip' | tr }}" tooltip-append-to-body="true" tooltip-placement="bottom"><i class="fa fa-terminal"></i></a>
<a class="btn btn-sm btn-default" ng-href="{{ '/filemanager.html?appId=' + app.id }}" target="_blank" uib-tooltip="{{ 'app.filemanagerActionTooltip' | tr }}" tooltip-append-to-body="true" tooltip-placement="bottom"><i class="fas fa-folder"></i></a>
<div class="dropdown" style="display: inline-block">
<button class="btn btn-sm btn-default dropdown-toggle" type="button" data-toggle="dropdown" uib-tooltip="{{ 'app.docsActionTooltip' | tr }}" tooltip-append-to-body="true" tooltip-placement="bottom">
<i class="fas fa-book"></i>
<span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li ng-class="{ 'disabled': !app.manifest.postInstallMessage }"><a href="" ng-click="postInstallMessage.show(false)">{{ 'app.firstTimeSetupAction' | tr }}</a></li>
<li ng-class="{ 'disabled': !app.manifest.documentationUrl }"><a ng-href="{{ app.manifest.documentationUrl }}" target="_blank">{{ 'app.docsAction' | tr }}</a></li>
<li ng-class="{ 'disabled': (!app.manifest.configurePath || !(app | applicationLink)) }"><a ng-href="{{ (app.manifest.configurePath && (app | applicationLink)) ? ((app | applicationLink) + app.manifest.configurePath) : ''}}" target="_blank">{{ 'app.adminPageAction' | tr }}</a></li>
<li role="separator" class="divider"></li>
<li ng-class="{ 'disabled': !app.manifest.website }"><a ng-href="{{ app.manifest.website }}" target="_blank">{{ 'app.projectWebsiteAction' | tr }}</a></li>
</ul>
</div>
</div>
</div>
2020-11-23 12:30:39 +01:00
<div class="app-status-container">
<span class="text-small">{{ app | installationStateLabel }} {{ app.message ? ' - ' + app.message : '' }}</span>
<span ng-click="setView('repair')" class="text-small hand text-hover" ng-show="app.error"> : {{ app.error.reason + ' - ' + app.error.message }}</span>
</div>
</div>
</div>
<div class="row" ng-show="app.taskId">
2020-03-26 00:16:23 +01:00
<div class="col-sm-8 col-sm-offset-2" style="height: 10px; display: flex; align-items: center;">
<div class="progress progress-striped active animateMeOpacity" style="height: 10px; flex-grow: 1;">
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{ app.progress || 5 }}%"></div>
</div>
2021-02-26 11:47:48 -08:00
<div ng-show="app.taskMinutesActive >= 2" class="text-danger hand" style="margin: 0 4px;" ng-click="stopAppTask(app.taskId)" uib-tooltip="Cancel Task"><i class="fas fa-times"></i></div>
</div>
</div>
<div class="row" ng-hide="view">
<div class="col-md-12 text-center">
<br/><br/><h2><i class="fa fa-circle-notch fa-spin"></i></h2>
</div>
</div>
<div class="row app-configure-links-container" ng-show="view">
<div class="col-sm-2">
<div class="app-configure-links">
<div ng-click="setView('display')" ng-class="{ 'active': view === 'display' }">{{ 'app.displayTabTitle' | tr }}</div>
<div ng-click="setView('location')" ng-class="{ 'active': view === 'location' }">{{ 'app.locationTabTitle' | tr }}</div>
<div ng-click="setView('access')" ng-class="{ 'active': view === 'access' }">{{ 'app.accessControlTabTitle' | tr }}</div>
<div ng-click="setView('resources')" ng-class="{ 'active': view === 'resources' }">{{ 'app.resourcesTabTitle' | tr }}</div>
<div ng-click="setView('storage')" ng-class="{ 'active': view === 'storage' }">{{ 'app.storageTabTitle' | tr }}</div>
<div ng-click="setView('graphs')" ng-class="{ 'active': view === 'graphs' }">{{ 'app.graphsTabTitle' | tr }}</div>
<div ng-click="setView('security')" ng-class="{ 'active': view === 'security' }">{{ 'app.securityTabTitle' | tr }}</div>
<div ng-click="setView('email')" ng-class="{ 'active': view === 'email' }" ng-show="app.manifest.addons.sendmail || app.manifest.addons.recvmail">{{ 'app.emailTabTitle' | tr }}</div>
<div ng-click="setView('updates')" ng-class="{ 'active': view === 'updates' }">{{ 'app.updatesTabTitle' | tr }}</div>
<div ng-click="setView('backups')" ng-class="{ 'active': view === 'backups' }">{{ 'app.backupsTabTitle' | tr }}</div>
<div ng-click="setView('repair')" ng-class="{ 'active': view === 'repair' }">{{ 'app.repairTabTitle' | tr }}</div>
<div ng-click="setView('uninstall')" ng-class="{ 'active': view === 'uninstall' }">{{ 'app.uninstallTabTitle' | tr }}</div>
</div>
</div>
<div class="col-sm-8 card-container">
<div class="card" ng-show="view === 'display'">
<div class="row">
<div class="col-md-12">
2020-03-19 19:26:19 -07:00
<form role="form" name="displayForm" ng-submit="display.submit()" autocomplete="off">
<fieldset>
<div class="form-group" ng-class="{ 'has-error': !displayForm.label.$dirty && display.error.label }">
<label class="control-label">{{ 'app.display.label' | tr }}</label>
<div class="control-label" ng-show="display.error.label">{{display.error.label}}</div>
<input type="text" class="form-control" id="displayLabelInput" name="label" ng-model="display.label">
</div>
<div class="form-group">
<label class="control-label">{{ 'app.display.tags' | tr }}</label>
<tag-input class="form-control" placeholder="{{ 'app.display.tagsPlaceholder' | tr }}" taglist="display.tags" name="tags" uib-tooltip="{{ 'app.display.tagsTooltip' | tr }}"></tag-input>
</div>
<div class="form-group">
<div>
<label class="control-label">{{ 'app.display.icon' | tr }}</label>
</div>
<div id="previewIcon" class="app-custom-icon" ng-click="display.showCustomIconSelector()">
<img ng-src="{{ display.iconUrl() || 'img/appicon_fallback.png' }}" fallback-icon="img/appicon_fallback.png" onerror="imageErrorHandler(this)"/>
<div class="overlay"></div>
</div>
<a href="" style="font-weight: normal;" ng-click="display.resetCustomIcon()">{{ 'app.display.iconResetAction' | tr }}</a>
<input type="file" id="iconFileInput" style="display: none" accept="image/png"/>
</div>
<input class="ng-hide" type="submit" ng-disabled="(!display.icon.data && !displayForm.$dirty) || displayForm.$invalid || display.busy"/>
2020-03-19 19:26:19 -07:00
</fieldset>
</form>
</div>
</div>
<div class="row">
<div class="col-md-12 text-right">
<button class="btn btn-outline btn-primary pull-right" ng-click="display.submit()" ng-disabled="(!display.icon.data && !displayForm.$dirty) || display.$invalid || display.busy"><i class="fa fa-circle-notch fa-spin" ng-show="display.busy"></i> {{ 'app.display.saveAction' | tr }}</button>
</div>
</div>
</div>
<div class="card" ng-show="view === 'location'">
<div class="row">
<div class="col-md-12">
2020-03-19 19:26:19 -07:00
<form role="form" name="locationForm" ng-submit="location.submit()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': (locationForm.location.$dirty && locationForm.location.$invalid) || (!locationForm.location.$dirty && location.error.location) }">
<label class="control-label">{{ 'app.location.location' | tr }}</label>
<div class="has-error" ng-show="location.error.location">{{ location.error.location }}</div>
<div class="input-group form-inline">
<input type="text" class="form-control" ng-model="location.location" name="location" placeholder="{{ 'app.location.locationPlaceholder' | tr }}" autofocus>
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span>{{ (!location.location ? '' : '.') + location.domain.domain }}</span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right" role="menu">
<li ng-repeat="domain in domains">
<a href="" ng-click="location.domain = domain">{{ domain.domain }}</a>
</li>
</ul>
</div>
</div>
</div>
<p class="text-small text-bold text-warning" ng-show="location.location && location.domain.provider === 'manual'" ng-bind-html="'appstore.installDialog.manualWarning' | tr:{ location: (location.location + '.' + location.domain.domain) }"></p>
<!-- hidden submit has to be prior to other button elements, otherwise firefox will treat them as the "enter" key action, in this case the alternate domain delete button! -->
<input class="ng-hide" type="submit" ng-disabled="locationForm.$invalid || location.busy"/>
<div class="has-error text-center" ng-show="location.error.port">{{ location.error.port }}</div>
<div ng-repeat="(env, info) in location.portBindingsInfo">
<ng-form name="portInfo_form">
<div class="form-group" ng-class="{ 'has-error': (!locationForm.itemName{{$index}}.$dirty && location.error.port) || (portInfo_form.itemName{{$index}}.$dirty && portInfo_form.itemName{{$index}}.$invalid) }">
<label class="control-label" for="locationPortInput{{env}}"><input type="checkbox" ng-model="location.portBindingsEnabled[env]">
{{ info.title }}
<sup>
<a popover-placement="top-right" popover-trigger="outsideClick" uib-popover="{{info.description}} ({{ HOST_PORT_MIN }} - {{ HOST_PORT_MAX }})"><i class="fa fa-question-circle"></i></a>
</sup>
</label>
<input type="number" class="form-control" ng-model="location.portBindings[env]" ng-disabled="!location.portBindingsEnabled[env]" id="locationPortInput{{env}}" later-name="itemName{{$index}}" min="{{HOST_PORT_MIN}}" max="{{HOST_PORT_MAX}}" required>
</div>
</ng-form>
</div>
2021-01-18 17:55:48 -08:00
<div class="form-group alias-domains" ng-show="app.manifest.multiDomain">
<label class="control-label">{{ 'app.location.aliases' | tr }} <sup><a ng-href="https://docs.cloudron.io/apps/#aliases" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
<div class="has-error" ng-show="location.error.aliasDomains">{{ location.error.aliasDomains }}</div>
<div class="row" ng-repeat="aliasDomain in location.aliasDomains">
<div class="col col-lg-11">
<div class="input-group">
<input type="text" class="form-control" ng-model="aliasDomain.subdomain" placeholder="{{ 'app.location.aliasesPlaceholder' | tr }}">
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span>{{ (!aliasDomain.subdomain ? '' : '.') + aliasDomain.domain.domain }}</span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right" role="menu">
<li ng-repeat="domain in domains">
<a href="" ng-click="aliasDomain.domain = domain">{{ domain.domain }}</a>
</li>
</ul>
</div>
</div>
</div>
<div class="col col-lg-1">
<button class="btn btn-danger btn-sm" ng-click="location.delAliasDomain($event, $index)"><i class="far fa-trash-alt"></i></button>
</div>
</div>
<div ng-show="location.aliasDomains.length === 0">{{ 'app.location.noAliases' | tr }}</div>
<div style="margin-top: 5px;"><a href="" ng-click="location.addAliasDomain($event)">{{ 'app.location.addAliasAction' | tr }}</a></div>
</div>
<div class="form-group alternate-domains">
<label class="control-label">{{ 'app.location.redirections' | tr }} <sup><a ng-href="https://docs.cloudron.io/apps/#redirections" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
<div class="has-error" ng-show="location.error.alternateDomains">{{ location.error.alternateDomains }}</div>
<div class="row" ng-repeat="alternateDomain in location.alternateDomains">
<div class="col col-lg-11">
<div class="input-group">
<input type="text" class="form-control" ng-model="alternateDomain.subdomain" placeholder="{{ 'app.location.redirectionsPlaceholder' | tr }}">
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span>{{ (!alternateDomain.subdomain ? '' : '.') + alternateDomain.domain.domain }}</span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right" role="menu">
<li ng-repeat="domain in domains">
<a href="" ng-click="alternateDomain.domain = domain">{{ domain.domain }}</a>
</li>
</ul>
</div>
</div>
</div>
<div class="col col-lg-1">
<button class="btn btn-danger btn-sm" ng-click="location.delAlternateDomain($event, $index)"><i class="far fa-trash-alt"></i></button>
</div>
</div>
<div ng-show="location.alternateDomains.length === 0">{{ 'app.location.noRedirections' | tr }}</div>
<div style="margin-top: 5px;"><a href="" ng-click="location.addAlternateDomain($event)">{{ 'app.location.addRedirectionAction' | tr }}</a></div>
</div>
2020-03-19 19:26:19 -07:00
</form>
</div>
</div>
<div class="row">
<div class="col-md-12 text-right">
<button class="btn btn-outline btn-primary pull-right" ng-click="location.submit()" ng-disabled="location.$invalid || location.busy || app.error || app.taskId" tooltip-enable="app.error || app.taskId" uib-tooltip="{{ app.error ? 'App is in error state' : 'App is busy' }}">
<i class="fa fa-circle-notch fa-spin" ng-show="location.busy"></i> {{ 'app.location.saveAction' | tr }}
</button>
</div>
</div>
</div>
<div class="card" ng-show="view === 'access'">
<div class="row">
<div class="col-md-12">
2020-03-19 19:26:19 -07:00
<form role="form" name="accessForm" ng-submit="access.submit()" autocomplete="off">
<div class="form-group">
<div class="form-group" ng-show="app.manifest.addons.email">
<label class="control-label">{{ 'app.accessControl.userManagement.title' | tr }}</label>
<p>{{ 'appstore.installDialog.userManagementMailbox' | tr }}
<span ng-bind-html="'appstore.installDialog.configuredForCloudronEmail' | tr:{ emailDocsLink: 'https://docs.cloudron.io/email/' }">
</p>
</div>
<div ng-show="access.ssoAuth && !app.manifest.addons.email">
2021-03-02 20:49:47 -08:00
<label class="control-label">{{ 'app.accessControl.userManagement.title' | tr }} <sup><a ng-href="https://docs.cloudron.io/apps/#access-restriction" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
<p>{{ 'app.accessControl.userManagement.description' | tr }} <span class="text-small text-warning" ng-show="access.ftp">{{ 'app.accessControl.userManagement.descriptionSftp' | tr }}</span></p>
</div>
<div ng-show="!access.ssoAuth || app.manifest.addons.email">
2021-03-02 20:49:47 -08:00
<label class="control-label">{{ 'app.accessControl.userManagement.dashboardVisibility' | tr }} <sup><a ng-href="https://docs.cloudron.io/apps/#dashboard-visibility" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
<p ng-show="!app.manifest.addons.email">{{ 'appstore.installDialog.userManagementNone' | tr }} <span ng-show="access.ftp">{{ 'app.accessControl.userManagement.sftpAccessControl' | tr }}</span></p>
</div>
<div class="radio">
<label>
<input type="radio" ng-model="access.accessRestrictionOption" value="any">
<span ng-show="access.ssoAuth">{{ 'appstore.installDialog.userManagementAllUsers' | tr }}</span>
<span ng-show="!access.ssoAuth">{{ 'app.accessControl.userManagement.visibleForAllUsers' | tr }}</span>
</label>
</div>
<div class="radio">
<label>
<input type="radio" ng-model="access.accessRestrictionOption" value="groups">
<span ng-show="access.ssoAuth">{{ 'appstore.installDialog.userManagementSelectUsers' | tr }}</span>
<span ng-show="!access.ssoAuth">{{ 'app.accessControl.userManagement.visibleForSelected' | tr }}</span>
<span class="label label-danger" ng-show="access.accessRestrictionOption === 'groups' && !access.isAccessRestrictionValid()">{{ 'appstore.installDialog.errorUserManagementSelectAtLeastOne' | tr }}</span>
</label>
</div>
<div>
<div style="margin-left: 20px;">
<div class="col-md-5">
{{ 'appstore.installDialog.users' | tr }}: <multiselect class="input-sm stretch" ng-model="access.accessRestriction.users" ng-disabled="access.accessRestrictionOption !== 'groups'" options="user.display for user in users" data-multiple="true" filter-after-rows="5" scroll-after-rows="10"></multiselect>
</div>
<div class="col-md-5">
{{ 'appstore.installDialog.groups' | tr }}: <multiselect class="input-sm stretch" ng-model="access.accessRestriction.groups" ng-disabled="access.accessRestrictionOption !== 'groups'" options="group.name for group in groups" data-multiple="true" filter-after-rows="5" scroll-after-rows="10"></multiselect>
</div>
</div>
</div>
<input class="ng-hide" type="submit" ng-disabled="(access.accessRestrictionOption === 'groups' && !access.isAccessRestrictionValid()) || accessForm.$invalid || access.busy"/>
</div>
2020-03-19 19:26:19 -07:00
</form>
</div>
</div>
<div class="row">
<div class="col-md-12 text-right">
<button class="btn btn-outline btn-primary pull-right" ng-click="access.submit()" ng-disabled="(access.accessRestrictionOption === 'groups' && !access.isAccessRestrictionValid()) || access.$invalid || access.busy"><i class="fa fa-circle-notch fa-spin" ng-show="access.busy"></i> {{ 'main.dialog.save' | tr }}</button>
</div>
</div>
<div class="row" ng-show="app.manifest.addons.localstorage.ftp">
2019-09-19 09:35:25 -07:00
<hr/>
<div class="col-md-12">
<label>{{ 'app.accessControl.sftp.title' | tr }} <sup><a ng-href="https://docs.cloudron.io/apps/#ftp-access" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
<br/>
<div class="row">
<div class="col-xs-6">
<span class="text-muted">{{ 'app.accessControl.sftp.server' | tr }}</span>
</div>
<div class="col-xs-6 text-right">
<span>{{ config.adminFqdn }}</span>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<span class="text-muted">{{ 'app.accessControl.sftp.port' | tr }}</span>
</div>
<div class="col-xs-6 text-right">
<span>222</span>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<span class="text-muted">{{ 'app.accessControl.sftp.username' | tr }}</span>
</div>
<div class="col-xs-6 text-right">
2020-08-08 19:15:21 -07:00
<span>{{ user.username }}@{{ app.fqdn }}</span>
</div>
</div>
</div>
</div>
</div>
<div class="card" ng-show="view === 'resources'">
<div class="row">
<div class="col-md-12">
2020-03-19 19:26:19 -07:00
<form role="form" name="resourcesForm" ng-submit="resources.submitMemoryLimit()" autocomplete="off">
<div class="form-group">
<label class="control-label" for="memoryLimit">{{ 'app.resources.memory.title' | tr }} <sup><a ng-href="https://docs.cloudron.io/apps/#memory-limit" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup> : <b>{{ resources.memoryLimit | prettyByteSize:'Default (256 MB)' }}</b></label>
<div style="padding: 0 10px;">
<slider id="memoryLimit" ng-model="resources.memoryLimit" step="134217728" tooltip="hide" ticks="resources.memoryTicks" ticks-snap-bounds="67108864"></slider>
</div>
</div>
<input class="ng-hide" type="submit" ng-disabled="resources.memoryLimit === resources.currentMemoryLimit || resourcesForm.$invalid || resources.busy"/>
2020-03-19 19:26:19 -07:00
</form>
2019-09-18 17:12:10 +02:00
</div>
</div>
<div class="row">
2020-10-21 13:27:31 +02:00
<div class="col-md-8">
<span ng-show="resources.error.memoryLimit" class="text-danger">{{ 'app.resources.memory.error' | tr }}</span>
2020-10-21 13:27:31 +02:00
</div>
<div class="col-md-4 text-right">
<button class="btn btn-outline btn-primary pull-right" ng-click="resources.submitMemoryLimit()" ng-disabled="resources.memoryLimit === resources.currentMemoryLimit || resourcesForm.$invalid || resources.busy || app.error || app.taskId" tooltip-enable="app.error || app.taskId" uib-tooltip="{{ app.error ? 'App is in error state' : 'App is busy' }}">
<i class="fa fa-circle-notch fa-spin" ng-show="resources.busy"></i> {{ 'app.resources.memory.resizeAction' | tr }}
</button>
2019-09-18 17:12:10 +02:00
</div>
</div>
<hr/>
2020-01-28 22:05:06 -08:00
<div class="row">
<div class="col-md-12">
2020-03-19 19:26:19 -07:00
<form role="form" name="resourcesForm" ng-submit="resources.submitCpuShares()" autocomplete="off">
<fieldset>
2020-01-28 22:05:06 -08:00
<div class="form-group">
<label class="control-label" for="cpuShares">{{ 'app.resources.cpu.title' | tr }} <sup><a ng-href="https://docs.cloudron.io/apps/#cpu-shares" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup> : <b>{{ (resources.cpuShares * 100 / 1024 | number:0) + ' %' }}</b></label>
<p>{{ 'app.resources.cpu.description' | tr }}</p>
2020-01-28 22:05:06 -08:00
<div style="padding: 0 10px;">
2020-03-06 10:40:21 -08:00
<slider id="cpuShares" ng-model="resources.cpuShares" ticks="[32, 256, 512, 768, 1024]" step="32" ticks-snap-bounds="32" min="32" max="1024" tooltip="hide"></slider>
2020-01-28 22:05:06 -08:00
</div>
</div>
<input class="ng-hide" type="submit" ng-disabled="resources.cpuShares === resources.currentCpuShares || resourcesForm.$invalid || resources.busyCpuShares"/>
2020-03-19 19:26:19 -07:00
</fieldset>
</form>
2020-01-28 22:05:06 -08:00
</div>
</div>
<div class="row">
<div class="col-md-12 text-right">
<button class="btn btn-outline btn-primary pull-right" ng-click="resources.submitCpuShares()" ng-disabled="resources.cpuShares === resources.currentCpuShares || resourcesForm.$invalid || resources.busyCpuShares || app.error || app.taskId" tooltip-enable="app.error || app.taskId" uib-tooltip="{{ app.error ? 'App is in error state' : 'App is busy' }}">
<i class="fa fa-circle-notch fa-spin" ng-show="resources.busyCpuShares"></i> {{ 'app.resources.cpu.setAction' | tr }}
2020-01-28 22:05:06 -08:00
</button>
</div>
</div>
2020-10-28 22:11:05 -07:00
</div>
<div class="card" ng-show="view === 'storage'">
2019-09-18 17:12:10 +02:00
<div class="row">
<div class="col-md-12">
<label class="control-label">{{ 'app.storage.appdata.title' | tr }} <sup><a ng-href="https://docs.cloudron.io/storage/#app-data-directory" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
<p ng-bind-html="'app.storage.appdata.description' | tr:{ storagePath: ('/home/yellowtent/appsdata/' + app.id) }"></p>
2020-10-28 22:11:05 -07:00
<form role="form" name="storageDataDirForm" ng-submit="storage.submitDataDir()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': storageDataDirForm.$dirty && storage.error.dataDir }">
<div ng-show="storage.error.dataDir">{{ storage.error.dataDir }}</div>
<input type="text" class="form-control" name="dataDir" placeholder="{{ 'app.storage.appdata.dataDirPlaceholder' | tr }}" ng-model="storage.dataDir">
</div>
<input class="ng-hide" type="submit" ng-disabled="!storageDataDirForm.$dirty || storageDataDirForm.$invalid || storage.busyDataDir"/>
2020-03-19 19:26:19 -07:00
</form>
</div>
</div>
<div class="row">
<div class="col-md-12 text-right">
2020-10-28 22:11:05 -07:00
<button class="btn btn-outline btn-primary pull-right" ng-click="storage.submitDataDir()" ng-disabled="!storageDataDirForm.$dirty || storageDataDirForm.$invalid || storage.busyDataDir || app.error || app.taskId" tooltip-enable="app.error || app.taskId" uib-tooltip="{{ app.error ? 'App is in error state' : 'App is busy' }}">
<i class="fa fa-circle-notch fa-spin" ng-show="storage.busyDataDir"></i> {{ 'app.storage.appdata.moveAction' | tr }}
</button>
</div>
</div>
2020-05-22 12:05:23 -07:00
2020-09-24 14:32:00 -07:00
<hr>
2020-04-29 22:18:44 -07:00
2020-10-28 22:11:05 -07:00
<div class="form-group mounts">
<label class="control-label">{{ 'app.storage.mounts.title' | tr }} <sup><a ng-href="https://docs.cloudron.io/apps/#mounts" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
2020-10-28 22:11:05 -07:00
<div class="has-error" ng-show="storage.error.mounts">{{ storage.error.mounts }}</div>
2020-11-23 17:25:13 -08:00
<div ng-show="storage.mounts.length === 0">{{ 'app.storage.mounts.noMounts' | tr }}</div>
<p ng-bind-html="'storage.mounts.volumeLocation' | tr" ng-show="storage.mounts.length > 0"></p>
<table ng-show="storage.mounts.length > 0" class="table table-hover" style="margin-top: 10px;">
2020-10-28 22:11:05 -07:00
<thead>
<tr>
<th style="width: 40%">{{ 'app.storage.mounts.volume' | tr }}</th>
<th class="text-left hidden-xs hidden-sm">{{ 'app.storage.mounts.readOnly' | tr }}</th>
2020-11-11 22:50:57 +01:00
<th style="width: 100px" class="text-right">{{ 'main.actions' | tr }}</th>
2020-10-28 22:11:05 -07:00
</tr>
</thead>
<tbody>
<tr ng-repeat="mount in storage.mounts">
<td>
<multiselect ng-model="mount.volume" data-compare-by="hostPath" options="volume.name for volume in volumes" data-multiple="false" filter-after-rows="5" scroll-after-rows="10"></multiselect>
2020-10-28 22:11:05 -07:00
</td>
<td class="text-left" style="vertical-align: middle;">
<input type="checkbox" ng-model="mount.readOnly" style="margin-top: initial;"/>
</td>
<td class="text-right no-wrap" style="vertical-align: middle">
2020-10-28 22:11:05 -07:00
<button class="btn btn-danger btn-xs" ng-click="storage.delMount($event, $index)"><i class="far fa-trash-alt"></i></button>
</td>
</tr>
</tbody>
</table>
<div style="margin-top: 5px;"><a href="" ng-click="storage.addMount($event)">{{ 'app.storage.mounts.addMountAction' | tr }}</a></div>
2020-04-29 22:18:44 -07:00
</div>
2020-09-24 14:32:00 -07:00
<div class="row">
2020-04-29 22:18:44 -07:00
<div class="col-md-12 text-right">
2020-10-28 22:11:05 -07:00
<button class="btn btn-outline btn-primary pull-right" ng-click="storage.submitMounts()" ng-disabled="storage.busyMounts || app.error || app.taskId" tooltip-enable="app.error || app.taskId" uib-tooltip="{{ app.error ? 'App is in error state' : 'App is busy' }}">
<i class="fa fa-circle-notch fa-spin" ng-show="storage.busyMounts"></i> {{ 'app.storage.mounts.saveAction' | tr }}
2020-04-29 22:18:44 -07:00
</button>
</div>
</div>
</div>
2020-05-13 00:42:27 +02:00
<div class="card" ng-show="view === 'graphs'">
<div class="row">
<div class="col-md-12">
<div class="dropdown pull-right">
2020-05-13 01:12:13 +02:00
<button class="btn btn-sm btn-primary dropdown-toggle" type="button" data-toggle="dropdown">
{{ 'app.graphs.selectPeriod' | tr:{ period: graphs.periodLabel } }}
2020-05-13 00:42:27 +02:00
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="" ng-click="graphs.setPeriod(12, '12 hours')">{{ 'app.graphs.period.12h' | tr }}</a></li>
<li><a href="" ng-click="graphs.setPeriod(24, '24 hours')">{{ 'app.graphs.period.24h' | tr }}</a></li>
<li><a href="" ng-click="graphs.setPeriod(24*7, '7 days')">{{ 'app.graphs.period.7d' | tr }}</a></li>
<li><a href="" ng-click="graphs.setPeriod(24*30, '30 days')">{{ 'app.graphs.period.30d' | tr }}</a></li>
2020-05-13 00:42:27 +02:00
</ul>
</div>
<label style="margin-top: 10px;">{{ 'app.graphs.memoryTitle' | tr }}</label>
2020-05-13 01:12:13 +02:00
<canvas id="graphsMemoryChart" style="width: 100%;"></canvas>
2020-05-13 00:42:27 +02:00
</div>
</div>
</div>
<div class="card" ng-show="view === 'email'">
<div class="row">
<div class="col-md-12">
2021-03-17 15:03:20 -07:00
<label class="control-label">{{ 'app.email.from.title' | tr }} <sup><a ng-href="https://docs.cloudron.io/apps/#mail-from-address" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
2021-03-17 15:03:20 -07:00
<div class="radio" ng-show="app.manifest.addons.sendmail.optional">
<label>
<input type="radio" ng-model="email.enableMailbox" value="1"> {{ 'app.email.from.enable' | tr }}
</label>
</div>
<div style="padding-left: 20px;">
<p ng-bind-html="'app.email.from.enableDescription' | tr:{ domain: app.domain, domainConfigLink: ('/#/email/' + app.domain) }"></p>
<form role="form" name="emailForm" ng-submit="email.submit()" autocomplete="off">
<fieldset ng-disabled="!email.enableMailbox">
<div class="form-group" ng-class="{ 'has-error': emailForm.$dirty && email.error.mailboxName }">
<div ng-show="email.error.mailboxName">{{ email.error.mailboxName }}</div>
<div class="input-group form-inline" ng-class="{ 'has-error': !emailForm.mailboxName.$dirty && email.error.mailboxName }">
<input type="text" class="form-control" name="mailboxName" placeholder="{{ 'app.email.from.mailboxPlaceholder' | tr }}" ng-model="email.mailboxName">
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span>{{ '@' + email.mailboxDomain.domain }}</span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right" role="menu">
<li ng-repeat="domain in domains">
<a href="" ng-click="email.mailboxDomain = domain">{{ domain.domain }}</a>
</li>
</ul>
</div>
2021-03-16 22:41:25 -07:00
</div>
<br/>
</div>
</fieldset>
<input class="ng-hide" type="submit" ng-disabled="(email.currentMailboxDomainName === email.mailboxDomain.domain && email.currentMailboxName === email.mailboxName) || email.busy || app.error || app.taskId"/>
</form>
</div>
2021-03-17 15:03:20 -07:00
<div class="radio" ng-show="app.manifest.addons.sendmail.optional">
<label>
<input type="radio" ng-model="email.enableMailbox" value="0"> {{ 'app.email.from.disable' | tr }}
</label>
</div>
<div style="padding-left: 20px;">
<p ng-show="app.manifest.addons.sendmail.optional">{{ 'app.email.from.disableDescription' | tr }}</p>
</div>
</div>
</div>
<div class="row">
2019-09-17 15:09:39 +02:00
<div class="col-md-12 text-right">
<br/>
2021-03-17 15:03:20 -07:00
<button class="btn btn-outline btn-primary pull-right" ng-click="email.submit()" ng-disabled="(app.enableMailbox === email.enableMailbox && email.currentMailboxDomainName === email.mailboxDomain.domain && email.currentMailboxName === email.mailboxName) || email.busy || app.error || app.taskId" tooltip-enable="app.error || app.taskId" uib-tooltip="{{ app.error ? 'App is in error state' : 'App is busy' }}">
<i class="fa fa-circle-notch fa-spin" ng-show="email.busy"></i> {{ 'app.email.from.saveAction' | tr }}
</button>
</div>
</div>
</div>
<div class="card" ng-show="view === 'security'">
<div class="row">
<div class="col-md-12">
2020-03-19 19:26:19 -07:00
<form role="form" name="securityForm" ng-submit="security.submit()" autocomplete="off">
<div class="form-group">
<label class="control-label" style="width: 100%">{{ 'app.security.robots.title' | tr }} <sup><a ng-href="https://docs.cloudron.io/apps/#robotstxt" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup> <a href="" class="pull-right" style="font-weight: normal;" ng-click="security.robotsTxt = ROBOTS_DISABLE_INDEXING_TEMPLATE">{{ 'app.security.robots.disableIndexingAction' | tr }}</a></label>
<textarea ng-trim="false" style="white-space: pre-wrap" ng-model="security.robotsTxt" placeholder="{{ 'app.security.robots.txtPlaceholder' | tr }}" class="form-control" rows="4"></textarea>
</div>
<div class="form-group">
<label class="control-label" style="width: 100%">{{ 'app.security.csp.title' | tr }} <sup><a ng-href="https://docs.cloudron.io/apps/#custom-csp" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup> </label>
<p>{{ 'app.security.csp.description' | tr }}</p>
<textarea ng-model="security.csp" placeholder="default-src 'self'; frame-ancestors 'none';" class="form-control" rows="2"></textarea>
</div>
2019-10-14 15:20:48 -07:00
<input class="ng-hide" type="submit" ng-disabled="securityForm.$invalid || security.busy"/>
2020-03-19 19:26:19 -07:00
</form>
</div>
</div>
2019-10-14 15:20:48 -07:00
<br/>
<div class="row">
<div class="col-md-12 text-right">
<button class="btn btn-outline btn-primary pull-right" ng-click="security.submit()" ng-disabled="security.$invalid || security.busy || app.error" tooltip-enable="app.error" uib-tooltip="App is in error state">
<i class="fa fa-circle-notch fa-spin" ng-show="security.busy"></i> {{ 'app.security.csp.saveAction' | tr }}
</button>
</div>
</div>
</div>
<div class="card" ng-show="view === 'updates'">
<p><label class="control-label">{{ 'app.updates.info.title' | tr }}</label></p>
<div class="row">
2020-05-27 22:31:10 -07:00
<div class="col-xs-4">
<span class="text-muted">{{ 'app.updates.info.description' | tr }}</span>
</div>
2020-05-27 22:31:10 -07:00
<div class="col-xs-8 text-right">
<span ng-show="app.appStoreId">{{ app.manifest.title }} {{ app.upstreamVersion }}</span>
<span ng-show="!app.appStoreId">{{ app.manifest.dockerImage }}</span>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<span class="text-muted">{{ 'app.updates.info.appId' | tr }}</span>
</div>
<div class="col-xs-6 text-right">
<span>{{ app.id }}</span>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<span class="text-muted">{{ 'app.updates.info.packageVersion' | tr }}</span>
</div>
<div class="col-xs-6 text-right">
2020-12-09 17:13:52 -08:00
<span ng-show="app.appStoreId"><a ng-href="/#/appstore/{{app.manifest.id}}?version={{app.manifest.version}}">{{ app.manifest.id }}@{{ app.manifest.version }}</a></span>
<span ng-show="!app.appStoreId">{{ app.manifest.version }}</span>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<span class="text-muted">{{ 'app.updates.info.lastUpdated' | tr }}</span>
</div>
<div class="col-xs-6 text-right">
<span>{{ app.updateTime | prettyDate }}</span>
</div>
</div>
<br/>
<div class="row">
<div class="col-md-12" ng-show="!config.update[app.id].manifest.version || config.update[app.id].manifest.version === app.manifest.version">
<button class="btn btn-primary pull-right" ng-show="app.appStoreId" ng-click="updates.check()" ng-disabled="updates.busyCheck"><i class="fa fa-circle-notch fa-spin" ng-show="updates.busyCheck"></i> {{ 'app.updates.info.checkForUpdatesAction' | tr }}</button>
<span ng-show="!app.appStoreId" class="text-danger pull-right">{{ 'app.updates.info.customAppUpdateInfo' | tr }}</span>
</div>
<div class="col-md-12" ng-show="config.update[app.id].manifest.version && config.update[app.id].manifest.version !== app.manifest.version && app.installationState !== 'pending_update'">
<button type="button" class="btn btn-success pull-right" ng-click="updates.askUpdate()" ng-disabled="app.taskId || app.error || app.runState === 'stopped'" tooltip-enable="app.error || app.taskId || app.runState === 'stopped'" uib-tooltip="{{ app.error ? 'App is in error state' : 'App is not running' }}">{{ 'app.updates.info.updateAvailableAction' | tr }}</button>
</div>
</div>
2019-09-19 11:58:06 -07:00
<hr/>
<div class="row">
2019-09-17 16:16:48 +02:00
<div class="col-md-12">
<label class="control-label">{{ 'app.updates.auto.title' | tr }}</label>
<p>{{ 'app.updates.auto.description' | tr }}</p>
<p class="text-success" ng-show="updates.enableAutomaticUpdate">{{ 'app.updates.auto.enabled' | tr }}</p>
<p class="text-danger" ng-hide="updates.enableAutomaticUpdate">{{ 'app.updates.auto.disabled' | tr }}</p>
<button class="btn btn-primary pull-right" uib-tooltip="{{ app.appStoreId ? '' : 'Not available for custom apps' }}" ng-class="{ 'btn-danger': updates.enableAutomaticUpdate }" ng-click="updates.toggleAutomaticUpdates()" ng-disabled="updates.busyAutomaticUpdates || !app.appStoreId"><i class="fa fa-circle-notch fa-spin" ng-show="updates.busyAutomaticUpdates"></i> {{ updates.enableAutomaticUpdate ? ('app.updates.auto.disableAction' | tr) : ('app.updates.auto.enableAction' | tr) }} </button>
</div>
</div>
</div>
<div class="card" ng-show="view === 'backups'">
2019-09-19 12:24:22 -07:00
<div class="row">
<div class="col-md-12">
<label class="control-label">{{ 'app.backups.backups.title' | tr }}</label>
<div>{{ 'app.backups.backups.description' | tr }}</div>
2019-12-17 10:15:38 -08:00
<br/>
2019-09-19 12:24:22 -07:00
<table ng-hide="!backups.backups.length" class="table table-hover" style="margin: 0;">
<thead>
<tr>
<th width="25px">&nbsp;</th>
<th>{{ 'app.backups.backups.packageVersion' | tr }}</th>
<th>{{ 'app.backups.backups.time' | tr }}</th>
2020-11-11 22:50:57 +01:00
<th class="text-right" width="180px">{{ 'main.actions' | tr }}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="backup in backups.backups">
<td><div class="hand clipboard" data-clipboard-text="{{ backup.id }}" uib-tooltip="{{ copyBackupIdDone ? ('main.clipboard.copied' | tr) : ('main.clipboard.clickToCopyBackupId' | tr) }}" tooltip-placement="right"><i class="fa fa-copy"></i></div></td>
<td><div>v{{ backup.packageVersion }}</div></td>
<td><div uib-tooltip="{{ backup.creationTime | prettyLongDate }}">{{ backup.creationTime | prettyDate }}</div></td>
<td class="text-right no-wrap" style="vertical-align: bottom">
<button class="btn btn-xs btn-default" ng-click="downloadConfig(backup)" uib-tooltip="{{ 'app.backups.backups.downloadConfigTooltip' | tr }}"><i class="fas fa-file-alt"></i></button>
<button class="btn btn-xs btn-default" ng-click="clone.show(backup)" uib-tooltip="{{ 'app.backups.backups.cloneTooltip' | tr }}"><i class="far fa-clone"></i></button>
<button class="btn btn-xs btn-danger" ng-click="restore.show(backup)" ng-disabled="app.taskId || app.runState === 'stopped'" uib-tooltip="{{ 'app.backups.backups.restoreTooltip' | tr }}"><i class="fas fa-history"></i></button>
</td>
</tr>
</tbody>
</table>
<br/>
<button type="button" class="btn btn-primary pull-right" ng-click="backups.createBackup()" ng-disabled="app.taskId || backups.busyCreate || app.error || app.runState === 'stopped'" tooltip-enable="app.error || app.taskId || app.runState === 'stopped'" uib-tooltip="{{ app.error ? 'App is in error state' : 'App is not running' }}">
<i class="fa fa-circle-notch fa-spin" ng-show="app.installationState === 'pending_backup' || backups.busyCreate"></i> {{ 'app.backups.backups.createBackupAction' | tr }}
</button>
</div>
</div>
2019-09-17 16:16:48 +02:00
<hr/>
2020-02-06 16:08:22 -08:00
<div class="row">
<div class="col-md-12">
<label class="control-label">{{ 'app.backups.import.title' | tr }}</label>
<p>{{ 'app.backups.import.description' | tr }}</p>
<button class="btn btn-primary pull-right" class="btn-primary" ng-click="importBackup.show()" ng-disabled="importBackup.busy || app.taskId || app.runState === 'stopped'" tooltip-enable="app.taskId" uib-tooltip="App is not running">
<i class="fa fa-circle-notch fa-spin" ng-show="backups.busy"></i> {{ 'app.backups.backups.importAction' | tr }}
2020-02-07 10:22:52 -08:00
</button>
2020-02-06 16:08:22 -08:00
</div>
</div>
<hr/>
<div class="row">
2019-09-17 16:16:48 +02:00
<div class="col-md-12">
<label class="control-label">{{ 'app.backups.auto.title' | tr }}</label>
2019-09-19 12:24:22 -07:00
<p ng-bind-html="'app.backups.auto.description' | tr:{ backupLink: '/#/backups' }"></p>
<p class="text-success" ng-show="backups.enableBackup">{{ 'app.backups.auto.enabled' | tr }}</p>
<p class="text-danger" ng-hide="backups.enableBackup">{{ 'app.backups.auto.disabled' | tr }}</p>
2019-09-19 12:24:22 -07:00
<button class="btn btn-primary pull-right" ng-class="{ 'btn-danger': backups.enableBackup }" ng-click="backups.toggleAutomaticBackups()" ng-disabled="backups.busyAutomaticBackups"><i class="fa fa-circle-notch fa-spin" ng-show="backups.busyAutomaticBackups"></i> {{ backups.enableBackup ? ('app.backups.auto.disableAction' | tr) : ('app.backups.auto.enableAction' | tr) }}</button>
2019-09-17 16:16:48 +02:00
</div>
</div>
</div>
2019-12-16 13:30:51 -08:00
<div class="card" ng-show="view === 'repair'">
2019-09-23 17:27:40 -07:00
<div class="row">
<div class="col-md-12">
<label class="control-label">{{ 'app.repair.recovery.title' | tr }}</label>
<p ng-bind-html="'app.repair.recovery.description' | tr:{ docsLink: 'https://docs.cloudron.io/troubleshooting/#unresponsive-app' }"></p>
<button class="btn btn-primary pull-right" ng-click="repair.pauseAppBegin()" ng-show="!app.debugMode" ng-disabled="repair.pauseBusy || app.error || app.taskId" tooltip-enable="app.error || app.taskId" uib-tooltip="{{ app.error ? 'App is in error state' : 'App is busy' }}">{{ 'app.repair.recovery.enableRecoveryModeAction' | tr }}</button>
<button class="btn btn-primary pull-right" ng-click="repair.pauseAppDone()" ng-show="app.debugMode" ng-disabled="repair.pauseBusy || app.error || app.taskId" tooltip-enable="app.error || app.taskId" uib-tooltip="{{ app.error ? 'App is in error state' : 'App is busy' }}">{{ 'app.repair.recovery.disableRecoveryModeAction' | tr }}</button>
<button class="btn btn-primary pull-right" ng-click="repair.restartApp()" ng-disabled="repair.restartBusy || app.error || app.taskId" tooltip-enable="app.error || app.taskId" uib-tooltip="{{ app.error ? 'App is in error state' : 'App is busy' }}"><i ng-show="repair.restartBusy" class="fa fa-circle-notch fa-spin"></i> {{ 'app.repair.recovery.restartAction' | tr }}</button>
2019-12-16 18:18:22 -08:00
</div>
</div>
<hr/>
<div class="row">
<div class="col-md-12">
<label class="control-label">{{ 'app.repair.taskError.title' | tr }}</label>
<p>{{ 'app.repair.taskError.description' | tr }}</p>
2019-11-23 17:49:13 -08:00
<p ng-show="app.error">An error occurred during the <b>{{ app.error.installationState | taskName }}</b> operation: <span class="text-danger"><b>{{ app.error.reason + ': ' + app.error.message }}</b></span></p>
<button class="btn btn-primary pull-right" ng-click="repair.confirm()" ng-disabled="app.taskId || !app.error" tooltip-enable="app.taskId" uib-tooltip="{{ 'app.repair.appIsBusyTooltip' | tr }}">{{ 'app.repair.taskError.retryAction' | tr:{ task: (app.error.installationState | taskName) } }}</button>
2019-09-19 12:50:06 -07:00
</div>
</div>
2019-09-13 10:34:12 +02:00
</div>
2019-09-17 15:40:04 +02:00
<div class="card" ng-show="view === 'uninstall'">
2020-11-16 14:42:02 +01:00
<div class="row">
<div class="col-md-12">
<label class="control-label">{{ 'app.uninstall.startStop.title' | tr }}</label>
<p>{{ 'app.uninstall.startStop.description' | tr }}</p>
<button class="btn btn-default pull-right" ng-class="{ 'btn-primary': uninstall.startButton }" ng-click="uninstall.toggleRunState()" ng-disabled="app.taskId || app.error || app.installationState === 'pending_start' || app.installationState === 'pending_stop'">
2020-11-16 14:42:02 +01:00
<i ng-show="app.installationState === 'pending_start' || app.installationState === 'pending_stop'" class="fa fa-circle-notch fa-spin"></i>
{{ uninstall.startButton ? ('app.uninstall.startStop.startAction' | tr) : ('app.uninstall.startStop.stopAction' | tr) }}
2020-11-16 14:42:02 +01:00
</button>
</div>
</div>
<hr/>
2019-12-16 12:54:24 -08:00
<div class="row">
<div class="col-md-12">
<label class="control-label">{{ 'app.uninstall.uninstall.title' | tr }}</label>
<p>{{ 'app.uninstall.uninstall.description' | tr }}</p>
<p ng-bind-html="'app.uninstall.uninstall.backupWarning' | tr:{ importBackupDocsLink: 'https://docs.cloudron.io/backups/#import-app-backup' }"></p>
<button class="btn btn-danger pull-right" ng-click="uninstall.ask()">{{ 'app.uninstall.uninstall.uninstallAction' | tr }}</button>
2019-09-17 15:40:04 +02:00
</div>
</div>
2019-12-16 12:54:24 -08:00
</div>
2019-09-17 15:40:04 +02:00
2019-09-13 10:34:12 +02:00
</div>
</div>
</div>