Files
cloudron-box/webadmin/src/views/appstore.html
2018-03-12 15:49:11 -07:00

291 lines
23 KiB
HTML

<!-- Modal install app -->
<div class="modal fade appstore-install" id="appInstallModal" tabindex="-1" role="dialog" aria-labelledby="updateModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<img ng-src="{{appInstall.app.iconUrl}}" onerror="this.onerror=null;this.src='img/appicon_fallback.png'" class="app-icon"/>
<h3 class="appstore-install-title" title="Version {{ appInstall.app.manifest.version }}">{{ appInstall.app.manifest.title }} <span class="badge badge-danger" ng-show="appInstall.app.publishState === 'testing'">Testing</span></h3>
<br/>
<span class="appstore-install-meta"><a href="{{ appInstall.app.manifest.website }}" target="_blank">{{ appInstall.app.manifest.author }}</a></span>
<br/>
<span class="appstore-install-meta">Last updated {{ appInstall.app.creationDate | prettyDate }}</span>
<br/>
<span class="appstore-install-meta hand">Requires atleast {{ appInstall.app.manifest.memoryLimit | prettyMemory }}MB memory</span>
</div>
<div class="modal-body">
<div class="collapse" id="collapseInstallForm" data-toggle="false">
<form role="form" name="appInstallForm" ng-submit="appInstall.submit()" autocomplete="off">
<div class="has-error text-center" ng-show="appInstall.error.other" ng-bind-html="appInstall.error.other"></div>
<div class="form-group" ng-class="{ 'has-error': (appInstallForm.location.$dirty && appInstallForm.location.$invalid) || (!appInstallForm.location.$dirty && appInstall.error.location) }">
<label class="control-label" for="appInstallLocationInput">Location {{ appInstall.error.location }} </label>
<div class="input-group form-inline">
<input type="text" class="form-control" ng-model="appInstall.location" id="appInstallLocationInput" 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">
{{ (appInstall.location ? (appInstall.domain.provider !== 'caas' ? '.' : '-') : '') + appInstall.domain.domain }}
<span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right" role="menu">
<li ng-repeat="domain in domains">
<a href="" ng-click="appInstall.domain = domain">{{ domain.domain }}</a>
</li>
</ul>
</div>
</div>
</div>
<p class="text-center" ng-show="appInstall.location && dnsConfig.provider === 'manual' && !dnsConfig.wildcard">
<b>Add an A record manually for {{ appInstall.location }} to this Cloudron's public IP</b>
<br>
</p>
<div class="has-error text-center" ng-show="appInstall.error.port">{{ appInstall.error.port }}</div>
<div ng-repeat="(env, info) in appInstall.portBindingsInfo">
<ng-form name="portInfo_form">
<div class="form-group" ng-class="{ 'has-error': (!appInstallForm.itemName{{$index}}.$dirty && appInstall.error.port) || (portInfo_form.itemName{{$index}}.$dirty && portInfo_form.itemName{{$index}}.$invalid) }">
<label class="control-label" for="inputPortInfo{{env}}"><input type="checkbox" ng-model="appInstall.portBindingsEnabled[env]"> {{ info.description }} ({{ HOST_PORT_MIN }} - {{ HOST_PORT_MAX }})</label>
<input type="number" class="form-control" ng-model="appInstall.portBindings[env]" ng-disabled="!appInstall.portBindingsEnabled[env]" id="inputPortInfo{{env}}" later-name="itemName{{$index}}" min="{{hostPortMin}}" max="{{hostPortMax}}" required>
</div>
</ng-form>
</div>
<div class="form-group" ng-show="appInstall.customAuth && !appInstall.app.manifest.addons.email">
<label class="control-label">User management</label>
<p>
This app has it's own user management.
</p>
</div>
<div class="form-group" ng-show="appInstall.app.manifest.addons.email">
<label class="control-label">User management</label>
<p>
All users with a mailbox on this Cloudron have access.
</p>
</div>
<div class="form-group" ng-hide="appInstall.customAuth || appInstall.app.manifest.addons.email">
<label class="control-label">User management</label>
<div class="radio" ng-show="appInstall.optionalSso">
<label>
<input type="radio" ng-model="appInstall.accessRestrictionOption" value="nosso">
Leave user management to the app
</label>
</div>
<div class="radio">
<label>
<input type="radio" ng-model="appInstall.accessRestrictionOption" value="any">
Allow all users from this Cloudron
</label>
</div>
<div class="radio">
<label>
<input type="radio" ng-model="appInstall.accessRestrictionOption" value="groups">
Only allow the following users and groups <span class="label label-danger" ng-show="appInstall.accessRestrictionOption === 'groups' && !appInstall.isAccessRestrictionValid()">Select at least one user or group</span>
</label>
</div>
<div>
<div style="margin-left: 20px;">
<div class="col-md-5">
Users: &nbsp;
<multiselect ng-model="appInstall.accessRestriction.users" ng-disabled="appInstall.accessRestrictionOption !== 'groups'" options="user.username for user in users" data-multiple="true"></multiselect>
</div>
<div class="col-md-5">
Groups: &nbsp;
<multiselect ng-model="appInstall.accessRestriction.groups" ng-disabled="appInstall.accessRestrictionOption !== 'groups'" options="group.name for group in (groups | ignoreAdminGroup)" data-multiple="true"></multiselect>
</div>
</div>
</div>
<br/>
<br/>
</div>
<p ng-show="appInstall.app.manifest.addons.email" class="text-info">
This app is pre-configured for use with <a href="https://cloudron.io/documentation/email/" target="_blank">Cloudron Email</a>.
</p>
<div class="hide">
<label class="control-label" for="appInstallCertificateInput" ng-show="appInstall.domain.provider !== 'caas'">Certificate (optional)</label>
<div class="has-error text-center" ng-show="appInstall.error.cert && appInstall.domain.provider !== 'caas'">{{ appInstall.error.cert }}</div>
<div class="form-group" ng-class="{ 'has-error': !appInstallForm.certificate.$dirty && appInstall.error.cert }" ng-show="appInstall.domain.provider !== 'caas'">
<div class="input-group">
<input type="file" id="appInstallCertificateFileInput" style="display:none"/>
<input type="text" class="form-control" placeholder="Certificate" ng-model="appInstall.certificateFileName" id="appInstallCertificateInput" name="certificate" onclick="getElementById('appInstallCertificateFileInput').click();" style="cursor: pointer;" ng-required="appInstall.keyFileName">
<span class="input-group-addon">
<i class="fa fa-upload" onclick="getElementById('appInstallCertificateFileInput').click();"></i>
</span>
</div>
</div>
<div class="form-group" ng-class="{ 'has-error': !appInstallForm.key.$dirty && appInstall.error.cert }" ng-show="appInstall.domain.provider !== 'caas'">
<div class="input-group">
<input type="file" id="appInstallKeyFileInput" style="display:none"/>
<input type="text" class="form-control" placeholder="Key" ng-model="appInstall.keyFileName" id="appInstallKeyInput" name="key" onclick="getElementById('appInstallKeyFileInput').click();" style="cursor: pointer;" ng-required="appInstall.certificateFileName">
<span class="input-group-addon">
<i class="fa fa-upload" onclick="getElementById('appInstallKeyFileInput').click();"></i>
</span>
</div>
</div>
</div>
<input class="ng-hide" type="submit" ng-disabled="appInstallForm.$invalid || busy"/>
</form>
</div>
<div class="collapse" id="collapseMediaLinksCarousel" data-toggle="false">
<div ng-repeat="mediaLink in appInstall.mediaLinks" class="slick-item" style="background-image: url('{{mediaLink}}');" ng-show="appInstall.mediaLinks.length == 1"></div>
<slick init-onload="true" current-index="0" autoplay="true" arrows="false" autoplay-speed="2000" data="appInstall.mediaLinks" ng-show="appInstall.mediaLinks.length > 1">
<div ng-repeat="mediaLink in appInstall.mediaLinks" class="slick-item" style="background-image: url('{{mediaLink}}');"></div>
</slick>
<div class="appstore-install-description">
<div ng-bind-html="appInstall.app.manifest.description | markdown2html"></div>
</div>
</div>
<div class="collapse" id="collapseResourceConstraint" data-toggle="false">
<h4 class="text-danger">This Cloudron is running low on resources.</h4>
<p ng-show="config.provider === 'caas'">Please upgrade to a bigger plan. Alternately, free up resources by uninstalling unused applications.</p>
<p ng-hide="config.provider === 'caas'">Please upgrade to a server instance with more memory. Alternately, free up resources by uninstalling unused applications.</p>
</div>
<div class="collapse" id="postInstallMessage" data-toggle="false">
<div class="appstore-install-description">
<div ng-bind-html="appInstall.app.manifest.postInstallMessage | postInstallMessage:appInstall.app | markdown2html"></div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" ng-show="appInstall.state !== 'postInstall'" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-default" ng-show="appInstall.state === 'postInstall'" data-dismiss="modal" ng-click="appInstall.switchToAppsView()">Got it</button>
<button type="button" class="btn btn-success" ng-show="config.provider === 'caas' && user.admin && appInstall.state === 'resourceConstraint'" ng-click="showView('/settings')">Upgrade Cloudron</button>
<button type="button" class="btn btn-danger" ng-show="config.provider !== 'caas' && user.admin && appInstall.state === 'resourceConstraint'" ng-click="appInstall.showForm(true)">Install anyway</button>
<button type="button" class="btn btn-success" ng-show="appInstall.state === 'appInfo' && user.admin" ng-click="appInstall.showForm()">Install</button>
<button type="button" class="btn btn-success" ng-show="appInstall.state === 'installForm' && user.admin" ng-click="appInstall.submit()" ng-disabled="appInstallForm.$invalid || appInstall.busy"><i class="fa fa-circle-o-notch fa-spin" ng-show="appInstall.busy"></i> Install</button>
</div>
</div>
</div>
</div>
<!-- Modal app not found -->
<div class="modal fade" id="appNotFoundModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">App not found</h4>
</div>
<div class="modal-body">
There is no such app <b>{{ appNotFound.appId }}</b><span ng-show="appNotFound.version"> with version <b>{{ appNotFound.version }}</b></span>.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">OK</button>
</div>
</div>
</div>
</div>
<div ng-show="!ready" class="loading-banner">
<h1><i class="fa fa-circle-o-notch fa-spin"></i></h1>
</div>
<!-- appstore login -->
<div ng-show="ready && !validAppstoreAccount" class="container card card-small appstore-login ng-cloak">
<div class="col-md-12">
<h1 ng-show="appstoreLogin.register">Sign up with Cloudron App Store</h1>
<h1 ng-hide="appstoreLogin.register">Login to Cloudron App Store</h1>
</div>
<div class="col-md-12" style="margin-bottom: 10px;">
<p>An App Store account gives you access to apps and updates.</p>
<br/>
<small class="text-danger" ng-show="appstoreLogin.error.generic">{{ appstoreLogin.error.generic }}</small>
</div>
<div class="col-md-12">
<form name="appstoreLoginForm" role="form" novalidate ng-submit="appstoreLogin.submit()" autocomplete="off">
<input type="password" style="display: none;">
<div class="form-group" ng-class="{ 'has-error': (appstoreLoginForm.email.$dirty && appstoreLoginForm.email.$invalid) || appstoreLogin.error.generic }">
<label class="control-label">Email Address</label>
<input type="email" class="form-control" ng-model="appstoreLogin.email" id="inputAppstoreLoginEmail" name="email" required autofocus>
<div class="control-label" ng-show="(!appstoreLoginForm.email.$dirty && appstoreLogin.error.email) || (appstoreLoginForm.email.$dirty && appstoreLoginForm.email.$invalid) || appstoreLogin.error.email">
<small class="text-danger" ng-show="appstoreLogin.error.email">{{ appstoreLogin.error.email }}</small>
</div>
</div>
<div class="form-group" ng-class="{ 'has-error': (!appstoreLoginForm.password.$dirty && appstoreLogin.error.password) || (appstoreLoginForm.password.$dirty && appstoreLoginForm.password.$invalid) || appstoreLogin.error.generic }">
<label class="control-label">Password</label>
<input type="password" class="form-control" ng-model="appstoreLogin.password" id="inputAppstoreLoginPassword" name="password" required>
<div class="control-label" ng-show="(!appstoreLoginForm.password.$dirty && appstoreLogin.error.password) || (appstoreLoginForm.password.$dirty && appstoreLoginForm.password.$invalid)">
<small ng-show="!appstoreLoginForm.password.$dirty && appstoreLogin.error.password">Wrong password</small>
</div>
</div>
<div class="checkbox" ng-show="appstoreLogin.register">
<label>
<input type="checkbox" ng-model="appstoreLogin.termsAccepted">I accept the Cloudron <a href="https://cloudron.io/legal/license.html" target="_blank">license</a>
</label>
</div>
<br/>
<a class="pull-left" href="" ng-click="appstoreLogin.register = true" ng-hide="appstoreLogin.register">Don't have an Account yet?</a>
<a class="pull-left" href="" ng-click="appstoreLogin.register = false" ng-show="appstoreLogin.register">Already have an Account?</a>
<button type="submit" class="btn btn-success pull-right" ng-disabled="appstoreLoginForm.$invalid || appstoreLogin.busy || (appstoreLogin.register && !appstoreLogin.termsAccepted)">
<i class="fa fa-circle-o-notch fa-spin" ng-show="appstoreLogin.busy"></i> <span ng-hide="appstoreLogin.register">Login</span><span ng-show="appstoreLogin.register">Sign up</span>
</button>
</form>
</div>
</div>
<div ng-show="ready && validAppstoreAccount" class="ng-cloak" id="appstoreGrid">
<div class="col-md-2">
<br/>
<div>
<form ng-submit="search()">
<div class="input-group">
<input type="text" id="appstoreSearch" class="form-control" style="height: 40px" placeholder="Search" ng-model="searchString" ng-change="search()" autofocus>
</div>
</form>
</div>
<br/>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === '' }" category="">All</a>
<br/>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'analytics' }" category="analytics"><i class="fa fa-bar-chart"></i> Analytics</a>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'blog' }" category="blog"><i class="fa fa-font"></i> Blog</a>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'chat' }" category="chat"><i class="fa fa-comments-o"></i> Chat</a>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'git' }" category="git"><i class="fa fa-code-fork"></i> Code Hosting</a>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'email' }" category="email"><i class="fa fa-envelope-o"></i> Email</a>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'sync' }" category="sync"><i class="fa fa-refresh"></i> File Sync</a>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'finance' }" category="finance"><i class="fa fa-dollar"></i> Finance</a>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'forum' }" category="forum"><i class="fa fa-users"></i> Forum</a>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'gallery' }" category="gallery"><i class="fa fa-picture-o"></i> Gallery</a>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'notes' }" category="notes"><i class="fa fa-sticky-note-o"></i> Notes</a>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'project' }" category="project"><i class="fa fa-line-chart"></i> Project Management</a>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'vpn' }" category="vpn"><i class="fa fa-user-secret"></i> VPN</a>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'hosting' }" category="hosting"><i class="fa fa-bars"></i> Web Hosting</a>
<a href="" class="appstore-category-link" ng-click="showCategory($event);" ng-class="{'category-active': category === 'wiki' }" category="wiki"><i class="fa fa-wikipedia-w"></i> Wiki</a>
<br/>
<br/>
<a href="https://forum.cloudron.io/category/5/app-requests" target="_blank">Missing an app? Let us know.</a>
</div>
<div class="col-md-10" ng-show="apps.length">
<div class="row-no-margin">
<div class="col-sm-1 appstore-item" ng-repeat="app in apps | orderBy:'installCount':true">
<div class="appstore-item-content highlight" ng-click="gotoApp(app)" ng-class="{ 'appstore-item-content-testing': (app.publishState === 'testing' || app.publishState === 'pending_approval') }">
<span class="badge badge-danger appstore-item-badge-testing" ng-show="app.publishState === 'testing'">Testing</span>
<span class="badge badge-warning appstore-item-badge-testing" ng-show="app.publishState === 'pending_approval'">Pending Approval</span>
<div class="appstore-item-content-icon col-same-height">
<img ng-src="{{app.iconUrl}}" onerror="this.onerror=null;this.src='img/appicon_fallback.png'" class="app-icon"/>
</div>
<div class="appstore-item-content-description col-same-height">
<h4 class="appstore-item-content-title">{{ app.manifest.title }}</h4>
<div class="appstore-item-content-tagline text-muted">{{ app.manifest.tagline }}</div>
<!-- <div class="appstore-item-rating"><i class="fa fa-star"></i><i class="fa fa-star"></i><i class="fa fa-star"></i><i class="fa fa-star-half-o"></i><i class="fa fa-star-o"></i></div> -->
</div>
</div>
</div>
</div>
</div>
<div class="col-md-10 animateMeOpacity loading-banner" ng-show="!apps.length">
<h3 class="text-muted">No apps found.</h3>
<a href="https://forum.cloudron.io/category/5/app-requests" target="_blank"><h3>Request an app or vote for one in our forum.</h3></a>
</div>
</div>