Add translation for app configure dialogs

This commit is contained in:
Johannes Zellner
2020-11-17 13:58:05 +01:00
parent e6814ca4d2
commit a46f7341e7
3 changed files with 244 additions and 210 deletions

View File

@@ -15,46 +15,69 @@
<img ng-src="{{app.iconUrl}}" onerror="this.onerror=null;this.src='img/appicon_fallback.png'" class="app-info-icon"/>
<h5 class="app-info-title">
{{ app.manifest.title }}
<span class="app-info-meta text-small">Package <a ng-href="/#/appstore/{{ app.manifest.id }}?version={{ app.manifest.version }}">v{{ app.manifest.version }}</a> </span>
<span class="app-info-meta text-small">{{ 'app.appInfo.package' | tr }} <a ng-href="/#/appstore/{{ app.manifest.id }}?version={{ app.manifest.version }}">v{{ app.manifest.version }}</a> </span>
<br/>
<span ng-show="app.manifest.documentationUrl"><a target="_blank" ng-href="{{ app.manifest.documentationUrl }}">Documentation</a> </span>
<span ng-show="app.manifest.documentationUrl"><a target="_blank" ng-href="{{ app.manifest.documentationUrl }}">{{ 'app.docsAction' | tr }}</a> </span>
<br/>
</h5>
</div>
<div class="modal-body">
<p ng-show="app.manifest.addons.email">This app is set up to allow all users with a mailbox on this Cloudron. Login with the email and Cloudron password to access the mailbox.</p>
<p ng-show="app.sso && !app.manifest.addons.email"> This app is set up to authenticate with the Cloudron User Directory. Cloudron users can login and use {{ appPostInstallConfirm.app.manifest.title }}.</p>
<p ng-show="app.manifest.addons.email">{{ 'app.appInfo.ssoEmail' | tr }}</p>
<p ng-show="app.sso && !app.manifest.addons.email">{{ 'app.appInfo.sso' | tr }}</p>
<div ng-bind-html="app.manifest.postInstallMessage | markdown2html"></div>
<div ng-show="app.manifest.documentationUrl">
Please see the <a target="_blank" ng-href="{{ app.manifest.documentationUrl }}">documentation</a> for more information.
</div>
<div ng-show="app.manifest.documentationUrl" ng-bind-html="'app.appInfo.appDocsUrl' | tr:{ docsUrl: app.manifest.documentationUrl, title: app.manifest.title, forumUrl: (app.manifest.forumUrl || 'https://forum.cloudron.io') }"></div>
</div>
<div class="modal-footer">
<div class="form-group pull-left">
<input type="checkbox" id="postInstallConfirmCheckbox" ng-model="postInstallConfirm.confirmed">
<label class="control-label" for="postInstallConfirmCheckbox">Acknowledge instructions</label>
<label class="control-label" for="postInstallConfirmCheckbox">{{ 'app.appInfo.postInstallConfirmCheckbox' | tr }}</label>
</div>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
<a class="btn btn-success" ng-href="{{ postInstallConfirm.confirmed ? ('https://' + app.fqdn) : '' }}" target="_blank" ng-disabled="!postInstallConfirm.confirmed" ng-click="postInstallConfirm.submit()"><i class="fas fa-external-link-alt"></i> Open App</a>
</div>
</div>
</div>
</div>
<!-- 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.manifest.title }}</h4>
</div>
<div class="modal-body">
<p ng-show="app.manifest.addons.email">{{ 'app.appInfo.ssoEmail' | tr }}</p>
<p ng-show="app.sso && !app.manifest.addons.email">{{ '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">
<div class="modal-header">
<h4 class="modal-title">Uninstall {{ app.label || app.fqdn }}</h4>
<h4 class="modal-title">{{ 'app.uninstallDialog.title' | tr:{ app: (app.label || app.fqdn) } }}</h4>
</div>
<div class="modal-body">
<p>This will immediately uninstall <b>{{ app.label || app.fqdn }}</b> and remove all it's data.</p>
<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">Cancel</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> Uninstall</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>
@@ -65,18 +88,18 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Domain Collision</h4>
<h4 class="modal-title">{{ 'app.domainCollisionDialog.title' | tr }}</h4>
</div>
<div class="modal-body">
<p>The following domains already exist in your DNS:</p>
<p>{{ 'app.domainCollisionDialog.collisionListTitle' | tr }}</p>
<ul>
<li ng-repeat="domain in location.domainCollisions">{{ domain.subdomain + '.' + domain.domain }}</li>
</ul>
<p>As a precautionary measure, Cloudron does not overwrite existing DNS records. Please confirm that the above domains are not in use for services external to Cloudron.</p>
<p>{{ 'app.domainCollisionDialog.description' | tr }}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger" ng-click="location.submit(true)">Overwrite existing DNS Records</button>
<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>
@@ -87,19 +110,19 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Repair {{ app.fqdn }}</h4>
<h4 class="modal-title">{{ 'app.repairDialog.title' | tr:{ app: app.fqdn } }}</h4>
</div>
<div class="modal-body">
<div ng-if="!app.error">
<p>Cloudron will re-install the app in-place with existing configuration. Existing data will be retained.</p>
<p>{{ 'app.repairDialog.description' | tr }}</p>
</div>
<div ng-if="app.error">
<p>The <b>{{ app.error.installationState | taskName }}</b> operation failed with the following error:</p>
<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>Cloudron will repair the app to use the following domains:</p>
<label class="control-label">Location</label>
<p>{{ 'app.repairDialog.domainDescription' | tr }}</p>
<label class="control-label">{{ 'app.repairDialog.location' | tr }}</label>
<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>
@@ -124,16 +147,16 @@
</p>
</div>
<div ng-show="repair.backups.length">
<label class="control-label">Restore from Backup:</label>
<label class="control-label">{{ 'app.repairDialog.fromBackup' | tr }}</label>
<select class="form-control" ng-model="repair.backupId">
<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">Cancel</button>
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<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> Retry {{ app.error.installationState | taskName }}
<i class="fa fa-circle-notch fa-spin" ng-show="repair.retryBusy"></i> {{ 'app.repairDialog.retryAction' | tr:{ task: (app.error.installationState | taskName) } }}
</button>
</div>
</div>
@@ -145,157 +168,128 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Import Backup</h4>
<h4 class="modal-title">{{ 'app.importBackupDialog.title' | tr }}</h4>
</div>
<div class="modal-body">
<p class="text-info">Any data generated between now and the last known backup will be irrevocably lost.
It is recommended to create a backup of the current data before attempting an import.
</p>
<p class="text-info">{{ 'app.importBackupDialog.description' | tr }}</p>
<form name="importBackupForm" role="form" novalidate ng-submit="importBackup.submit()" autocomplete="off">
<fieldset>
<p class="has-error text-center" ng-show="backups.error">{{ importBackup.error.generic }}</p>
<p class="has-error text-center" ng-show="backups.error">{{ importBackup.error.generic }}</p>
<div class="form-group">
<label class="control-label" for="storageProvider">Storage provider <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>
<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>
<!-- 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">Endpoint</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>
<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>
<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">Bucket name</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>
<div class="form-group" ng-class="{ 'has-error': importBackup.error.prefix }" ng-show="importBackup.provider !== 'filesystem' && importBackup.provider !== ''">
<label class="control-label" for="inputimportBackupPrefix">Prefix</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>
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 's3'">
<label class="control-label" for="inputImportBackupS3Region">Region</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>
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 's3-v4-compat'">
<label class="control-label" for="inputImportBackupS3V4CompatRegion">Region</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 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>
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 'digitalocean-spaces'">
<label class="control-label" for="inputImportBackupDORegion">Region</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 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>
<!-- 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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
</div>
</div>
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 'exoscale-sos'">
<label class="control-label" for="inputimportBackupExoscaleRegion">Region</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>
<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>
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 'wasabi'">
<label class="control-label" for="inputimportBackupWasabiRegion">Region</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>
<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>
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 'scaleway-objectstorage'">
<label class="control-label" for="inputimportBackupScalewayRegion">Region</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>
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 'linode-objectstorage'">
<label class="control-label" for="inputimportBackupLinodeRegion">Region</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>
<div class="form-group" ng-class="{ 'has-error': importBackup.error.region }" ng-show="importBackup.provider === 'ovh-objectstorage'">
<label class="control-label" for="inputimportBackupOvhRegion">Region</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>
<div class="form-group" ng-class="{ 'has-error': importBackup.error.accessKeyId }" ng-show="s3like(importBackup.provider)">
<label class="control-label" for="inputImportBackupAccessKeyId">Access key id</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>
<div class="form-group" ng-class="{ 'has-error': importBackup.error.secretAccessKey }" ng-show="s3like(importBackup.provider)">
<label class="control-label" for="inputImportBackupSecretAccessKey">Secret access key</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>
<div class="form-group" ng-class="{ 'has-error': importBackup.error.gcsKeyInput }" ng-show="importBackup.provider === 'gcs'">
<label class="control-label" for="gcsKeyInput">Service Account Key</label>
<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>
</div>
</div>
<div class="form-group">
<label class="control-label" for="storageFormat">Storage Format <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>
<div class="form-group" ng-class="{ 'has-error': importBackup.error.password }">
<label class="control-label" for="inputImportBackupPassword">Encryption password (optional) <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>
<input class="ng-hide" type="submit" ng-disabled="importBackupForm.$invalid"/>
</fieldset>
<input class="ng-hide" type="submit" ng-disabled="importBackupForm.$invalid"/>
</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();">Upload Backup Config</button>
<button type="button" class="btn btn-default pull-left" onclick="getElementById('backupConfigFileInput').click();">{{ 'app.importBackupDialog.uploadAction' | tr }}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</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> Import</button>
</div>
</div>
</div>
</div>
<!-- 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>First Time Setup</h4>
</div>
<div class="modal-body">
<p ng-show="app.manifest.addons.email">This app is set up to allow all users with a mailbox on this Cloudron. Login with the email and Cloudron password to access the mailbox.</p>
<p ng-show="app.sso && !app.manifest.addons.email"> This app is set up to authenticate with the Cloudron User Directory. Cloudron users can login and use {{ appPostInstallConfirm.app.manifest.title }}.</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">Acknowledge instructions</label>
</div>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</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">Open {{ app.manifest.title }}</a>
<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>
@@ -306,27 +300,25 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Update {{ app.fqdn }}</h4>
<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.apps[app.id].unstable">
This update is a pre-release and not considered stable yet. Please update at your own risk.
</p>
<p>Changes for new version <b>{{ config.update.apps[app.id].manifest.version}}</b>:</p>
<p class="text-danger" ng-show="config.update.apps[app.id].unstable">{{ 'app.updateDialog.unstableWarning' | tr }}</p>
<p>{{ 'app.updateDialog.changelogHeader' | tr:{ version: config.update.apps[app.id].manifest.version } }}</p>
<div ng-bind-html="config.update.apps[app.id].manifest.changelog | markdown2html"></div>
<p class="text-danger text-bold" ng-show="!config.update.apps[app.id].manifest.dockerImage">
<br/>
Your Cloudron subscription has expired. Please setup a subscription to update the app.
{{ 'app.updateDialog.subscriptionExpired' | tr }}
</p>
</div>
<div class="modal-footer">
<label class="checkbox-inline pull-left" ng-show="config.update.apps[app.id].manifest.dockerImage">
<input type="checkbox" ng-model="updates.skipBackup"><b>Skip backup</b>
<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.apps[app.id].manifest.dockerImage && user.isAtLeastOwner" ng-click="openSubscriptionSetup()">Setup Subscription</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" ng-class="config.update.apps[app.id].unstable ? 'btn btn-danger' : 'btn btn-success'" ng-click="updates.confirmUpdate()" ng-disabled="!config.update.apps[app.id].manifest.dockerImage || updates.busyUpdate"><i class="fa fa-circle-notch fa-spin" ng-show="updates.busyUpdate"></i> Update</button>
<button type="button" class="btn btn-primary pull-left" ng-show="!config.update.apps[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.apps[app.id].unstable ? 'btn btn-danger' : 'btn btn-success'" ng-click="updates.confirmUpdate()" ng-disabled="!config.update.apps[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>
@@ -337,20 +329,16 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">
Restore - {{ app.fqdn }}
</h4>
<h4 class="modal-title">{{ 'app.restoreDialog.title' | tr:{ app: app.fqdn } }}</h4>
</div>
<div class="modal-body" style="padding: 0 15px">
<p>This will restore this app to the data from <b>{{ restore.backup.creationTime | prettyDate }}</b>.</p>
<p class="text-danger">Any data generated between now and the last known backup will be irrevocably lost.
It is recommended to create a backup of the current data before attempting a restore.
</p>
<p>{{ 'app.restoreDialog.description' | tr:{ creationTime: (restore.backup.creationTime | prettyDate) } }}</p>
<p class="text-danger">{{ 'app.restoreDialog.warning' | tr }}</p>
<br/>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</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> Restore</button>
<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>
</div>
</div>
</div>
@@ -361,19 +349,17 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">
Clone - {{ app.fqdn }}
</h4>
<h4 class="modal-title">{{ 'app.cloneDialog.title' | tr:{ app: app.fqdn } }}</h4>
</div>
<div class="modal-body" style="padding: 0 15px">
<p>Using backup from <b>{{ clone.backup.creationTime | prettyDate }}</b> and version <b>v{{ clone.backup.packageVersion }}</b></p>
<p ng-bind-html="'app.cloneDialog.description' | tr:{ creationTime: (clone.backup.creationTime | prettyDate), packageVersion: clone.backup.packageVersion }"></p>
<form role="form" ng-submit="clone.submit()" autocomplete="off">
<fieldset>
<div class="form-group" ng-class="{ 'has-error': clone.error.location }">
<label class="control-label" for="cloneLocationInput">Location</label>
<label class="control-label" for="cloneLocationInput">{{ 'app.cloneDialog.location' | tr }}</label>
<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="Leave empty to use bare domain" autofocus>
<input type="text" class="form-control" ng-model="clone.location" id="cloneLocationInput" name="location" placeholder="{{ 'appstore.installDialog.locationPlaceholder' | tr }}" autofocus>
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span>{{ (!clone.location ? '' : '.') + clone.domain.domain }}</span>
@@ -388,10 +374,8 @@
</div>
</div>
<p class="text-center" ng-show="clone.location && clone.domain.provider === 'manual'">
<b>Add an A record manually for {{ clone.location }} to this Cloudron's public IP</b>
<br>
</p>
<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>
<div class="has-error text-center" ng-show="clone.error.port">{{ clone.error.port }}</div>
<div ng-repeat="(env, info) in clone.portBindingsInfo">
@@ -412,8 +396,8 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</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> Clone</button>
<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>
</div>
</div>
</div>