Files
cloudron-box/src/views/apps.html
2019-09-13 16:07:55 +02:00

278 lines
15 KiB
HTML

<!-- Modal restore app -->
<div class="modal fade" id="appRestoreModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">
Backups - {{ appRestore.app.fqdn }}
<button type="button" class="btn btn-sm btn-primary pull-right" ng-click="appRestore.createBackup()" ng-hide="appRestore.busyFetching" ng-disabled="appRestore.app.installationState === 'pending_backup'"><i class="fa fa-circle-notch fa-spin" ng-show="appRestore.app.installationState === 'pending_backup'"></i> Create Backup</button>
</h4>
</div>
<div class="modal-body" style="padding: 0 15px">
<p class="text-center" ng-show="appRestore.busyFetching"><i class="fa fa-circle-notch fa-spin"></i> Fetching backups</p>
<p ng-hide="appRestore.backups.length || appRestore.creatingBackup">This app has no backups yet.</p>
<div ng-show="appRestore.creatingBackup">
<div class="progress progress-striped active animateMe" style="margin-bottom: 10px;">
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{appRestore.app.progress}}%"></div>
</div>
<div><center>{{ appRestore.app.message }}</center></div>
<br>
</div>
<!-- backup id copy helper -->
<input type="text" class="offscreen" aria-hidden="true" id="appRestoreBackupIdHelper" value="">
<table class="table table-hover" style="margin: 0;" ng-show="appRestore.backups.length">
<thead>
<tr>
<th width="25px">&nbsp;</th>
<th>Created</th>
<th>Version</th>
<th class="text-right" width="180px">Actions</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="backup in appRestore.backups">
<td><div ng-click="appRestore.copyBackupId(backup)" class="hand" uib-tooltip="{{ appRestore.copyBackupIdDone ? 'Copied to clipboard' : 'Click to copy backup id' }}" tooltip-placement="right"><i class="fa fa-copy"></i></div></td>
<td>{{ backup.creationTime | prettyDate }}</td>
<td>v{{ backup.version }}</td>
<td class="text-right no-wrap" style="vertical-align: bottom">
<button class="btn btn-xs btn-default" ng-hide="backup.ackRestore" ng-click="appClone.show(appRestore.app, backup)" uib-tooltip="Clone from this Backup"><i class="far fa-clone"></i></button>
<button class="btn btn-xs btn-danger" ng-hide="backup.ackRestore" ng-click="backup.ackRestore = true" uib-tooltip="Restore to this Backup"><i class="fas fa-history"></i></button>
<button class="btn btn-xs btn-danger" ng-show="backup.ackRestore" ng-click="appRestore.restore(backup)">Yes restore now</button>
<button class="btn btn-xs btn-default" ng-show="backup.ackRestore" ng-click="backup.ackRestore = false">Back</button>
</td>
</tr>
</tbody>
</table>
<br/>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Modal clone app -->
<div class="modal fade" id="appCloneModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">
Clone - {{ appClone.app.fqdn }}
</h4>
</div>
<div class="modal-body" style="padding: 0 15px">
<p>Using backup from <b>{{ appClone.backup.creationTime | prettyDate }}</b> and version <b>v{{ appClone.backup.version }}</b></p>
<fieldset>
<form role="form" ng-submit="appClone.submit()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': appClone.error.location }">
<label class="control-label" for="appCloneLocationInput">Location</label>
<div ng-show="appClone.error.location"><small>{{ appClone.error.location }}</small></div>
<div class="input-group form-inline">
<input type="text" class="form-control" ng-model="appClone.location" id="appCloneLocationInput" name="location" placeholder="Leave empty to use bare domain" autofocus>
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span>{{ (!appClone.location ? '' : (appClone.domain.config.hyphenatedSubdomains ? '-' : '.')) + appClone.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="appClone.domain = domain">{{ domain.domain }}</a>
</li>
</ul>
</div>
</div>
</div>
<p class="text-center" ng-show="appClone.location && appClone.domain.provider === 'manual'">
<b>Add an A record manually for {{ appClone.location }} to this Cloudron's public IP</b>
<br>
</p>
<div class="has-error text-center" ng-show="appClone.error.port">{{ appClone.error.port }}</div>
<div ng-repeat="(env, info) in appClone.portBindingsInfo">
<ng-form name="portInfo_form">
<div class="form-group" ng-class="{ 'has-error': (!appClone.itemName{{$index}}.$dirty && appClone.error.port) || (portInfo_form.itemName{{$index}}.$dirty && portInfo_form.itemName{{$index}}.$invalid) }">
<label class="control-label" for="inputPortInfo{{env}}"><input type="checkbox" ng-model="appClone.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="appClone.portBindings[env]" ng-disabled="!appClone.portBindingsEnabled[env]" id="inputPortInfo{{env}}" later-name="itemName{{$index}}" min="{{hostPortMin}}" max="{{hostPortMax}}" required>
</div>
</ng-form>
</div>
</form>
</fieldset>
</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="appClone.submit()"><i class="far fa-clone"></i> Clone</button>
</div>
</div>
</div>
</div>
<!-- Modal postinstall confirm -->
<div class="modal fade" id="appPostInstallConfirmModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<img ng-src="{{appPostInstallConfirm.app.iconUrl}}" onerror="this.onerror=null;this.src='img/appicon_fallback.png'" class="app-info-icon"/>
<h5 class="app-info-title">
{{ appPostInstallConfirm.app.manifest.title }}
<span class="app-info-meta text-small">Package <a ng-href="/#/appstore/{{appPostInstallConfirm.app.manifest.id}}?version={{appPostInstallConfirm.app.manifest.version}}">v{{ appPostInstallConfirm.app.manifest.version }}</a> </span>
<br/>
<span ng-show="appPostInstallConfirm.app.manifest.documentationUrl"><a target="_blank" ng-href="{{appPostInstallConfirm.app.manifest.documentationUrl}}">Documentation</a> </span>
<br/>
</h5>
</div>
<div class="modal-body">
<div ng-bind-html="appPostInstallConfirm.app.manifest.postInstallMessage | postInstallMessage:appPostInstallConfirm.app | markdown2html"></div>
<div ng-show="appPostInstallConfirm.app.manifest.documentationUrl">
Please see the <a target="_blank" ng-href="{{appPostInstallConfirm.app.manifest.documentationUrl}}">documentation</a> for more information.
</div>
</div>
<div class="modal-footer">
<div class="form-group pull-left">
<input type="checkbox" id="appPostInstallConfirmCheckbox" ng-model="appPostInstallConfirm.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="{{ appPostInstallConfirm.confirmed ? ('https://' + appPostInstallConfirm.app.fqdn) : '' }}" target="_blank" ng-disabled="!appPostInstallConfirm.confirmed" ng-click="appPostInstallConfirm.submit()">Open {{ appPostInstallConfirm.app.manifest.title }}</a>
</div>
</div>
</div>
</div>
<!-- Modal error app -->
<div class="modal fade" id="appErrorModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Error for {{ appError.app.fqdn }}</h4>
</div>
<div class="modal-body">
<p>{{ appError.app | prettyAppErrorMessage }}</p>
</div>
<div class="modal-footer">
<a type="button" class="btn btn-primary pull-left" ng-href="{{ '/#/app/' + appError.app.id }}" autofocus>Repair</a>
<a type="button" class="btn btn-default pull-left" ng-href="{{ '/logs.html?appId=' + appError.app.id }}" target="_blank">Logs</a>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Modal update app -->
<div class="modal fade" id="appUpdateModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Update {{ appUpdate.app.fqdn }}</h4>
</div>
<div class="modal-body">
<p>Recent Changes for new version <b>{{ appUpdate.manifest.version}}</b>:</p>
<div ng-bind-html="appUpdate.manifest.changelog | markdown2html"></div>
</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="appUpdate.submit()" ng-disabled="appUpdate.busy"><i class="fa fa-circle-notch fa-spin" ng-show="appUpdate.busy"></i> Update</button>
</div>
</div>
</div>
</div>
<script>
function imageErrorHandler(elem) {
'use strict';
elem.src = elem.getAttribute('fallback-icon');
elem.onerror = null; // avoid retry after default icon cannot be loaded
}
</script>
<div class="content content-large">
<!-- Workaround for select-all issue, see commit message -->
<div style="font-size: 1px;">&nbsp;</div>
<div class="animateMeOpacity ng-hide" ng-show="installedApps.length === 0 && user.admin">
<div class="col-md-12" style="text-align: center;">
<br/><br/><br/><br/>
<h1><i class="fa fa-cloud-download fa-fw"></i> No apps installed yet!</h1>
<br/></br>
<h3>How about installing some? Check out the <a href="#/appstore">App Store</a></h3>
</div>
</div>
<div class="animateMeOpacity ng-hide" ng-show="installedApps.length === 0 && !user.admin">
<div class="col-md-12" style="text-align: center;">
<br/><br/><br/><br/>
<h1>You don't have access to any apps on this Cloudron yet!</h1>
<br/></br>
<h3>Once you do, they will show up here.</h3>
</div>
</div>
<div class="animateMeOpacity ng-hide" ng-show="installedApps.length > 0">
<h1 class="view-header">
Your Apps
<div class="pull-right">
<multiselect ng-model="selectedTags" ng-show="tags.length > 0" ms-header="All Tags" ms-selected="Tags: {{ selectedTags.join(', ') }}" options="tag for tag in tags" data-multiple="true"></multiselect>
<multiselect ng-model="selectedDomain" ng-show="filterDomains.length > 2" data-compare-by="domain" ms-selected="{{ selectedDomain.domain }}" options="domain.domain for domain in filterDomains" data-multiple="false"></multiselect>
</div>
</h1>
</div>
<div class="animateMeOpacity ng-hide" ng-show="installedApps.length > 0">
<div class="app-grid">
<div class="grid-item" ng-repeat="app in installedApps | selectedTagFilter:selectedTags | selectedDomainFilter:selectedDomain | orderBy:'location'">
<a ng-href="{{ app | applicationLink }}" ng-click="user.admin && ((app | installError) === true && appError.show(app)) || ((app | appIsInstalledAndHealthy) && app.pendingPostInstallConfirmation && appPostInstallConfirm.show(app))" target="_blank" ng-class="{ 'hand': (app | appIsInstalledAndHealthy) || (app | installError) }">
<div style="background-color: white;" class="highlight grid-item-content" uib-tooltip="{{ app.fqdn }}">
<div class="grid-item-top">
<div class="row">
<div class="col-xs-12 text-center" style="padding-left: 5px; padding-right: 5px;">
<br/>
<img ng-src="{{ app.iconUrl || 'img/appicon_fallback.png' }}" fallback-icon="img/appicon_fallback.png" onerror="imageErrorHandler(this)" class="app-icon"/>
</div>
</div>
<br/>
<div class="row">
<div class="col-xs-12 text-center">
<div class="grid-item-top-title" data-fittext>{{ app.label || app.location || app.fqdn }}</div>
<div class="text-muted status" style="text-overflow: ellipsis; white-space: nowrap; overflow: hidden" uib-tooltip="{{ app | appProgressMessage }}">
{{ app | installationStateLabel:user }}
</div>
<div class="status" ng-style="{ 'visibility': (app | installationActive) ? 'visible' : 'hidden' }">
<div class="progress progress-striped active">
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{ app.progress }}%"></div>
</div>
</div>
</div>
</div>
</div>
<div class="grid-item-actions" ng-show="user.admin">
<a href="" ng-hide="!backupsEnabled || (app | activeTask)" ng-click="appRestore.show(app)" uib-tooltip="Backups" tooltip-placement="right" tooltip-class="app-tooltip"><i class="fa fa-archive scale"></i></a>
<a ng-href="#/app/{{ app.id}}" uib-tooltip="Configure" tooltip-placement="right" tooltip-class="app-tooltip"><i class="fa fa-wrench scale"></i></a>
</div>
<!-- we check the version here because the box updater does not know when an app gets updated -->
<div class="app-update-badge" ng-click="appUpdate.show(app, config.update.apps[app.id].manifest)" ng-show="config.update.apps[app.id].manifest.version && config.update.apps[app.id].manifest.version !== app.manifest.version && (app | installSuccess)">
<i class="fa fa-arrow-up fa-inverse"></i>
</div>
</div>
</a>
</div>
</div>
</div>
</div>