2018-01-22 13:01:38 -08:00
<!-- Modal restore app -->
< div class = "modal fade" id = "appRestoreModal" tabindex = "-1" role = "dialog" >
2019-07-12 17:18:21 +02:00
< div class = "modal-dialog" >
< div class = "modal-content" >
< div class = "modal-header" >
< h4 class = "modal-title" >
Backups - {{ appRestore.app.fqdn }}
2019-09-05 11:30:27 -07:00
< 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 >
2019-07-12 17:18:21 +02:00
< / 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 >
2019-09-06 15:10:57 -07:00
< p ng-hide = "appRestore.backups.length || appRestore.creatingBackup" > This app has no backups yet.< / p >
2019-07-12 17:18:21 +02:00
2019-09-06 15:10:57 -07:00
< div ng-show = "appRestore.creatingBackup" >
2019-09-05 11:30:27 -07:00
< 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 >
2019-07-12 17:18:21 +02:00
<!-- 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" > < / 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 >
2018-01-22 13:01:38 -08:00
< / div >
2018-05-23 20:36:54 -07:00
2019-07-12 17:18:21 +02:00
< 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 >
2018-01-22 13:01:38 -08:00
< / div >
2019-07-12 17:18:21 +02:00
< / 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 >
2018-01-22 13:01:38 -08:00
< / div >
2019-07-12 17:18:21 +02:00
< / div >
2018-01-22 13:01:38 -08:00
< / div >
<!-- Modal information of app -->
< div class = "modal fade" id = "appInfoModal" tabindex = "-1" role = "dialog" >
< div class = "modal-dialog" >
< div class = "modal-content" >
< div class = "modal-header" >
< img ng-src = "{{appInfo.app.iconUrl}}" onerror = "this.onerror=null;this.src='img/appicon_fallback.png'" class = "app-info-icon" / >
< h5 class = "app-info-title" >
{{ appInfo.app.manifest.title }}
2019-03-20 09:22:54 -07:00
< span class = "app-info-meta text-small" > {{ appInfo.app.upstreamVersion }} (Package < a ng-href = "/#/appstore/{{appInfo.app.manifest.id}}?version={{appInfo.app.manifest.version}}" > v{{ appInfo.app.manifest.version }}< / a > ) < / span >
2018-08-05 21:10:18 -07:00
< br / >
App ID < span class = "app-info-meta text-small" > {{ appInfo.app.id }}< / a > < / span >
< br / >
Last updated < span class = "app-info-meta text-small" > {{ appInfo.app.updateTime | prettyDate }}< / span >
2018-01-22 13:01:38 -08:00
< / h5 >
< / div >
< div class = "modal-body" >
< div class = "app-postinstall-message" ng-hide = "appInfo.app.manifest && appInfo.app.manifest.postInstallMessage" >
This package has no special usage information.
< / div >
< div class = "app-postinstall-message" ng-show = "appInfo.app.manifest && appInfo.app.manifest.postInstallMessage" >
< div ng-bind-html = "appInfo.message | postInstallMessage:appInfo.app | markdown2html" > < / div >
< / div >
2019-03-20 21:29:24 -07:00
< div ng-show = "appInfo.app.manifest.addons.localstorage.ftp" >
< br / >
< b > SFTP< / b > < sup > < a ng-href = "{{ config.webServerOrigin }}/documentation/apps/#ftp-access" class = "help" target = "_blank" > < i class = "fa fa-question-circle" > < / i > < / a > < / sup > < br / >
Server: {{ config.adminFqdn }}< br / >
Port: 222< br / >
Username: {{ user.username }}@{{ appInfo.app.fqdn }}< br / >
< / div >
2018-01-22 13:01:38 -08:00
< / div >
< div class = "modal-footer" >
2019-03-20 09:32:33 -07:00
< a ng-show = "appInfo.app.manifest.documentationUrl" target = "_blank" ng-href = "{{appInfo.app.manifest.documentationUrl}}" class = "btn btn-info pull-left" > Documentation< / a >
< button type = "button" class = "btn btn-default" data-dismiss = "modal" autofocus > Close< / button >
2018-01-22 13:01:38 -08:00
< / div >
< / div >
< / div >
< / div >
2018-06-14 15:46:55 +02:00
<!-- 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 >
2018-08-05 21:36:40 -07:00
< br / >
< span ng-show = "appPostInstallConfirm.app.manifest.documentationUrl" > < a target = "_blank" ng-href = "{{appPostInstallConfirm.app.manifest.documentationUrl}}" > Documentation< / a > < / span >
< br / >
2018-06-14 15:46:55 +02:00
< / h5 >
< / div >
< div class = "modal-body" >
< div ng-bind-html = "appPostInstallConfirm.app.manifest.postInstallMessage | postInstallMessage:appPostInstallConfirm.app | markdown2html" > < / div >
2018-08-21 19:12:04 -07:00
< 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 >
2018-06-14 15:46:55 +02:00
< / div >
< div class = "modal-footer" >
< div class = "form-group pull-left" >
< input type = "checkbox" id = "appPostInstallConfirmCheckbox" ng-model = "appPostInstallConfirm.confirmed" >
2018-08-05 21:09:16 -07:00
< label class = "control-label" for = "appPostInstallConfirmCheckbox" > Acknowledge instructions< / label >
2018-06-14 15:46:55 +02:00
< / 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 >
2018-01-22 13:01:38 -08:00
<!-- 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" >
2019-09-07 09:24:39 +02:00
< p > {{ appError.app | prettyAppErrorMessage }}< / p >
2018-01-22 13:01:38 -08:00
< / div >
< div class = "modal-footer" >
2019-09-13 11:29:19 +02:00
< a type = "button" class = "btn btn-primary pull-left" ng-href = "{{ '/#/app/' + appError.app.id }}" autofocus > Repair< / a >
2018-12-08 21:45:49 -08:00
< a type = "button" class = "btn btn-default pull-left" ng-href = "{{ '/logs.html?appId=' + appError.app.id }}" target = "_blank" > Logs< / a >
2018-11-20 11:39:32 +01:00
< button type = "button" class = "btn btn-default" data-dismiss = "modal" > Close< / button >
2018-01-22 13:01:38 -08:00
< / div >
< / div >
< / div >
< / div >
2019-08-29 14:28:45 -07:00
<!-- Modal cancel app -->
< div class = "modal fade" id = "appCancelModal" tabindex = "-1" role = "dialog" >
< div class = "modal-dialog" >
< div class = "modal-content" >
< div class = "modal-header" >
< h4 class = "modal-title" > Cancel {{ appCancel.app.fqdn }}< / h4 >
< / div >
< div class = "modal-body" >
The app at {{ appCancel.app.fqdn }} is < b > {{ appCancel.app | installationStateCancelTask }}< / b > . Do you want to cancel this operation?
< br / >
< br / >
You can Restore or Repair the app to get it running again.
< / 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 = "appCancel.submit()" ng-disabled = "appCancel.busy" > < i class = "fa fa-circle-notch fa-spin" ng-show = "appCancel.busy" > < / i > Stop {{ appCancel.app | installationStateCancelTooltip }}< / button >
< / div >
< / div >
< / div >
< / div >
2018-01-22 13:01:38 -08:00
<!-- 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 >
2019-08-29 11:33:22 -07:00
< 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 >
2018-01-22 13:01:38 -08:00
< / div >
< / div >
< / div >
< / div >
< script >
function imageErrorHandler(elem) {
'use strict';
2019-04-03 14:43:20 +02:00
elem.src = elem.getAttribute('fallback-icon');
elem.onerror = null; // avoid retry after default icon cannot be loaded
2018-01-22 13:01:38 -08:00
}
< / script >
< div class = "content content-large" >
<!-- Workaround for select - all issue, see commit message -->
< div style = "font-size: 1px;" > < / div >
2019-05-04 18:40:10 -07:00
< div class = "animateMeOpacity ng-hide" ng-show = "installedApps.length === 0 && user.admin" >
2018-01-22 13:01:38 -08:00
< 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 >
2019-05-04 18:40:10 -07:00
< div class = "animateMeOpacity ng-hide" ng-show = "installedApps.length === 0 && !user.admin" >
2018-01-22 13:01:38 -08:00
< 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" >
2019-04-15 14:31:12 +02:00
< h1 class = "view-header" >
Your Apps
< div class = "pull-right" >
2019-04-15 14:38:35 +02:00
< 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 >
2019-05-20 23:40:02 +02:00
< 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 >
2019-04-15 14:31:12 +02:00
< / div >
< / h1 >
2019-04-12 11:06:56 +02:00
< / div >
2018-01-22 13:01:38 -08:00
< div class = "animateMeOpacity ng-hide" ng-show = "installedApps.length > 0" >
2019-04-15 14:31:12 +02:00
< div class = "app-grid" >
2019-05-20 23:40:02 +02:00
< div class = "grid-item" ng-repeat = "app in installedApps | selectedTagFilter:selectedTags | selectedDomainFilter:selectedDomain | orderBy:'location'" >
2019-09-05 17:50:10 -07:00
< 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) }" >
2019-04-15 14:31:12 +02:00
< 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;" >
2019-03-25 10:42:31 +01:00
< br / >
2019-04-15 14:31:12 +02:00
< 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" >
2019-04-24 14:31:52 +02:00
< div class = "grid-item-top-title" data-fittext > {{ app.label || app.location || app.fqdn }}< / div >
2019-09-05 10:05:01 -07:00
< div class = "text-muted status" style = "text-overflow: ellipsis; white-space: nowrap; overflow: hidden" uib-tooltip = "{{ app | appProgressMessage }}" >
2019-08-30 21:21:44 +02:00
{{ app | installationStateLabel:user }}
2019-04-15 14:31:12 +02:00
< / 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 >
2019-03-25 10:42:31 +01:00
< / div >
2018-01-22 13:01:38 -08:00
< / div >
2019-03-25 10:42:31 +01:00
< / div >
< / div >
2019-04-15 14:31:12 +02:00
< / div >
2018-01-22 13:01:38 -08:00
2019-05-04 18:40:10 -07:00
< div class = "grid-item-actions" ng-show = "user.admin" >
2019-08-29 14:28:45 -07:00
< a href = "" ng-show = "app | activeTask" ng-click = "appCancel.show(app)" uib-tooltip = "Cancel {{app | installationStateCancelTooltip}}" tooltip-placement = "right" tooltip-class = "app-tooltip" > < i class = "fa fa-ban scale" > < / i > < / a >
< 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 >
2019-09-12 14:40:16 +02:00
< a ng-href = "#/app/{{ app.id}}" uib-tooltip = "Configure" tooltip-placement = "right" tooltip-class = "app-tooltip" > < i class = "fa fa-wrench scale" > < / i > < / a >
2019-04-15 14:31:12 +02:00
< a href = "" ng-click = "showInformation(app)" uib-tooltip = "Information" tooltip-placement = "right" tooltip-class = "app-tooltip" > < i class = "fa fa-info-circle scale" > < / i > < / a >
< / div >
2018-01-22 13:01:38 -08:00
2019-04-15 14:31:12 +02:00
<!-- we check the version here because the box updater does not know when an app gets updated -->
2019-08-29 11:33:22 -07:00
< 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)" >
2019-04-16 09:21:09 +02:00
< i class = "fa fa-arrow-up fa-inverse" > < / i >
2019-03-25 10:42:31 +01:00
< / div >
2019-04-15 14:31:12 +02:00
< / div >
< / a >
2018-01-22 13:01:38 -08:00
< / div >
2019-03-25 10:42:31 +01:00
< / div >
2018-01-22 13:01:38 -08:00
< / div >
< / div >