<rd-header id="view-top"> <rd-header-title title-text="Application templates list"> <a data-toggle="tooltip" title="Refresh" ui-sref="docker.templates" ui-sref-opts="{reload: true}"> <i class="fa fa-sync" aria-hidden="true"></i> </a> </rd-header-title> <rd-header-content>Templates</rd-header-content> </rd-header> <div class="row"> <!-- stack-form --> <div class="col-sm-12" ng-if="state.selectedTemplate && state.filters.Type === 'stack'"> <rd-widget> <rd-widget-custom-header icon="state.selectedTemplate.Logo" title-text="state.selectedTemplate.Title"> <div class="pull-right"> <button type="button" class="btn btn-sm btn-primary" ng-click="unselectTemplate()">Hide</button> </div> </rd-widget-custom-header> <rd-widget-body classes="padding"> <form class="form-horizontal"> <!-- description --> <div ng-if="state.selectedTemplate.Note"> <div class="col-sm-12 form-section-title"> Information </div> <div class="form-group"> <div class="col-sm-12"> <div class="template-note" ng-if="state.selectedTemplate.Note" ng-bind-html="state.selectedTemplate.Note"></div> </div> </div> </div> <!-- !description --> <div class="col-sm-12 form-section-title"> Configuration </div> <!-- name-input --> <div class="form-group"> <label for="container_name" class="col-sm-2 control-label text-left">Name</label> <div class="col-sm-10"> <input type="text" name="container_name" class="form-control" ng-model="formValues.name" placeholder="e.g. myStack" required> </div> </div> <!-- !name-input --> <!-- env --> <div ng-repeat="var in state.selectedTemplate.Env" ng-if="var.label && !var.set" class="form-group"> <label for="field_{{ $index }}" class="col-sm-2 control-label text-left">{{ var.label }}<portainer-tooltip ng-if="var.description" position="bottom" message="{{ var.description }}"></portainer-tooltip></label> <div class="col-sm-10"> <!-- <input ng-if="!var.values && (!var.type || !var.type === 'container')" type="text" class="form-control" ng-model="var.value" id="field_{{ $index }}"> --> <input type="text" class="form-control" ng-if="!var.select" ng-model="var.value" id="field_{{ $index }}"> <select class="form-control" ng-if="var.select" ng-model="var.value" id="field_{{ $index }}"> <option selected disabled hidden value="">Select value</option> <option ng-repeat="choice in var.select" value="{{ choice.value }}">{{ choice.text }}</option> </select> </div> </div> <!-- !env --> <!-- access-control --> <por-access-control-form form-data="formValues.AccessControlData" ng-if="applicationState.application.authentication"></por-access-control-form> <!-- !access-control --> <!-- actions --> <div class="col-sm-12 form-section-title"> Actions </div> <div class="form-group"> <div class="col-sm-12"> <button type="button" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !formValues.name" ng-click="createTemplate()" button-spinner="state.actionInProgress"> <span ng-hide="state.actionInProgress">Deploy the stack</span> <span ng-show="state.actionInProgress">Deployment in progress...</span> </button> <span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span> </div> </div> <!-- !actions --> </form> </rd-widget-body> </rd-widget> </div> <!-- !stack-form --> <!-- container-form --> <div class="col-sm-12" ng-if="state.selectedTemplate && state.filters.Type === 'container'"> <rd-widget> <rd-widget-custom-header icon="state.selectedTemplate.Logo" title-text="state.selectedTemplate.Image"> <div class="pull-right"> <button type="button" class="btn btn-sm btn-primary" ng-click="unselectTemplate()">Hide</button> </div> </rd-widget-custom-header> <rd-widget-body classes="padding"> <form class="form-horizontal"> <!-- description --> <div ng-if="state.selectedTemplate.Note"> <div class="col-sm-12 form-section-title"> Information </div> <div class="form-group"> <div class="col-sm-12"> <div class="template-note" ng-if="state.selectedTemplate.Note" ng-bind-html="state.selectedTemplate.Note"></div> </div> </div> </div> <!-- !description --> <div class="col-sm-12 form-section-title"> Configuration </div> <!-- name-input --> <div class="form-group"> <label for="container_name" class="col-sm-2 control-label text-left">Name</label> <div class="col-sm-10"> <input type="text" name="container_name" class="form-control" ng-model="formValues.name" placeholder="e.g. web (optional)"> </div> </div> <!-- !name-input --> <!-- network-input --> <div class="form-group"> <label for="container_network" class="col-sm-2 control-label text-left">Network</label> <div class="col-sm-10"> <select class="form-control" ng-options="net.Name for net in availableNetworks" ng-model="formValues.network"> <option disabled hidden value="">Select a network</option> </select> </div> </div> <!-- !network-input --> <!-- env --> <div ng-repeat="var in state.selectedTemplate.Env" ng-if="!var.set" class="form-group"> <label for="field_{{ $index }}" class="col-sm-2 control-label text-left">{{ var.label }}</label> <div class="col-sm-10"> <select ng-if="var.type === 'container'" ng-options="container|containername for container in runningContainers" class="form-control" ng-model="var.value"> <option selected disabled hidden value="">Select a container</option> </select> <input ng-if="!var.type || !var.type === 'container'" type="text" class="form-control" ng-model="var.value" id="field_{{ $index }}"> </div> </div> <!-- !env --> <!-- access-control --> <por-access-control-form form-data="formValues.AccessControlData" ng-if="applicationState.application.authentication"></por-access-control-form> <!-- !access-control --> <div class="form-group"> <div class="col-sm-12"> <a class="small interactive" ng-if="!state.showAdvancedOptions" ng-click="state.showAdvancedOptions = true;"> <i class="fa fa-plus space-right" aria-hidden="true"></i> Show advanced options </a> <a class="small interactive" ng-if="state.showAdvancedOptions" ng-click="state.showAdvancedOptions = false;"> <i class="fa fa-minus space-right" aria-hidden="true"></i> Hide advanced options </a> </div> </div> <div ng-if="state.showAdvancedOptions"> <!-- port-mapping --> <div class="form-group" > <div class="col-sm-12" style="margin-top: 5px;"> <label class="control-label text-left">Port mapping</label> <span class="label label-default interactive" style="margin-left: 10px;" ng-click="addPortBinding()"> <i class="fa fa-plus-circle" aria-hidden="true"></i> map additional port </span> </div> <div class="col-sm-12" style="margin-top: 10px" ng-if="state.selectedTemplate.Ports.length > 0"> <span class="small text-muted">Portainer will automatically assign a port if you leave the host port empty.</span> </div> <!-- !port-mapping --> <!-- port-mapping-input-list --> <div class="col-sm-12"> <div class="col-sm-12 form-inline" style="margin-top: 10px;"> <div ng-repeat="portBinding in state.selectedTemplate.Ports" style="margin-top: 2px;"> <!-- host-port --> <div class="input-group col-sm-4 input-group-sm"> <span class="input-group-addon">host</span> <input type="text" class="form-control" ng-model="portBinding.hostPort" placeholder="e.g. 80 or 1.2.3.4:80 (optional)"> </div> <!-- !host-port --> <span style="margin: 0 10px 0 10px;"> <i class="fa fa-long-arrow-alt-right" aria-hidden="true"></i> </span> <!-- container-port --> <div class="input-group col-sm-4 input-group-sm"> <span class="input-group-addon">container</span> <input type="text" class="form-control" ng-model="portBinding.containerPort" placeholder="e.g. 80"> </div> <!-- !container-port --> <!-- protocol-actions --> <div class="input-group col-sm-3 input-group-sm"> <div class="btn-group btn-group-sm"> <label class="btn btn-primary" ng-model="portBinding.protocol" uib-btn-radio="'tcp'">TCP</label> <label class="btn btn-primary" ng-model="portBinding.protocol" uib-btn-radio="'udp'">UDP</label> </div> <button class="btn btn-sm btn-danger" type="button" ng-click="removePortBinding($index)"> <i class="fa fa-trash" aria-hidden="true"></i> </button> </div> <!-- !protocol-actions --> </div> </div> </div> </div> <!-- !port-mapping-input-list --> <!-- volume-mapping --> <div class="form-group" > <div class="col-sm-12" style="margin-top: 5px;"> <label class="control-label text-left">Volume mapping</label> <span class="label label-default interactive" style="margin-left: 10px;" ng-click="addVolume()"> <i class="fa fa-plus-circle" aria-hidden="true"></i> map additional volume </span> </div> <div class="col-sm-12" style="margin-top: 10px" ng-if="state.selectedTemplate.Volumes.length > 0"> <span class="small text-muted">Portainer will automatically create and map a local volume when using the <b>auto</b> option.</span> </div> <div ng-repeat="volume in state.selectedTemplate.Volumes"> <div class="col-sm-12" style="margin-top: 10px;"> <!-- volume-line1 --> <div class="col-sm-12 form-inline"> <!-- container-path --> <div class="input-group input-group-sm col-sm-6"> <span class="input-group-addon">container</span> <input type="text" class="form-control" ng-model="volume.containerPath" placeholder="e.g. /path/in/container"> </div> <!-- !container-path --> <!-- volume-type --> <div class="input-group col-sm-5" style="margin-left: 5px;"> <div class="btn-group btn-group-sm"> <label class="btn btn-primary" ng-model="volume.type" uib-btn-radio="'auto'" ng-click="volume.name = ''">Auto</label> <label class="btn btn-primary" ng-model="volume.type" uib-btn-radio="'volume'" ng-click="volume.name = ''">Volume</label> <label class="btn btn-primary" ng-model="volume.type" uib-btn-radio="'bind'" ng-click="volume.name = ''" ng-if="isAdmin || allowBindMounts">Bind</label> </div> <button class="btn btn-sm btn-danger" type="button" ng-click="removeVolume($index)"> <i class="fa fa-trash" aria-hidden="true"></i> </button> </div> <!-- !volume-type --> </div> <!-- !volume-line1 --> <!-- volume-line2 --> <div class="col-sm-12 form-inline" style="margin-top: 5px;" ng-if="volume.type !== 'auto'"> <i class="fa fa-long-arrow-alt-right" aria-hidden="true"></i> <!-- volume --> <div class="input-group input-group-sm col-sm-6" ng-if="volume.type === 'volume'"> <span class="input-group-addon">volume</span> <select class="form-control" ng-model="volume.name"> <option selected disabled hidden value="">Select a volume</option> <option ng-repeat="vol in availableVolumes" ng-value="vol.Name">{{ vol.Name|truncate:30}}</option> </select> </div> <!-- !volume --> <!-- bind --> <div class="input-group input-group-sm col-sm-6" ng-if="volume.type === 'bind'"> <span class="input-group-addon">host</span> <input type="text" class="form-control" ng-model="volume.name" placeholder="e.g. /path/on/host"> </div> <!-- !bind --> <!-- read-only --> <div class="input-group input-group-sm col-sm-5" style="margin-left: 5px;"> <div class="btn-group btn-group-sm"> <label class="btn btn-primary" ng-model="volume.readOnly" uib-btn-radio="false">Writable</label> <label class="btn btn-primary" ng-model="volume.readOnly" uib-btn-radio="true">Read-only</label> </div> </div> <!-- !read-only --> </div> <!-- !volume-line2 --> </div> </div> </div> <!-- !volume-mapping --> <!-- extra-host --> <div class="form-group" > <div class="col-sm-12" style="margin-top: 5px;"> <label class="control-label text-left">Hosts file entries</label> <span class="label label-default interactive" style="margin-left: 10px;" ng-click="addExtraHost()"> <i class="fa fa-plus-circle" aria-hidden="true"></i> add additional entry </span> </div> <!-- extra-host-input-list --> <div class="col-sm-12"> <div class="col-sm-12 form-inline" style="margin-top: 10px;"> <div ng-repeat="(idx, host) in state.selectedTemplate.Hosts track by $index" style="margin-top: 2px;"> <div class="input-group col-sm-5 input-group-sm"> <span class="input-group-addon">value</span> <input type="text" class="form-control" ng-model="state.selectedTemplate.Hosts[idx]" placeholder="e.g. host:IP"> </div> <button class="btn btn-sm btn-danger" type="button" ng-click="removeExtraHost($index)"> <i class="fa fa-trash" aria-hidden="true"></i> </button> </div> </div> </div> </div> <!-- !extra-host --> <!-- Label --> <div class="form-group" > <div class="col-sm-12" style="margin-top: 5px;"> <label class="control-label text-left">Labels</label> <span class="label label-default interactive" style="margin-left: 10px;" ng-click="addLabel()"> <i class="fa fa-plus-circle" aria-hidden="true"></i> add label </span> </div> <!-- labels-input-list --> <div class="col-sm-12"> <div class="col-sm-12 form-inline" style="margin-top: 10px;"> <div ng-repeat="label in state.selectedTemplate.Labels" style="margin-top: 2px;"> <div class="input-group col-sm-5 input-group-sm"> <span class="input-group-addon">name</span> <input type="text" class="form-control" ng-model="label.name" placeholder="e.g. com.example.foo"> </div> <div class="input-group col-sm-5 input-group-sm"> <span class="input-group-addon">value</span> <input type="text" class="form-control" ng-model="label.value" placeholder="e.g. bar"> </div> <button class="btn btn-sm btn-danger" type="button" ng-click="removeLabel($index)"> <i class="fa fa-trash" aria-hidden="true"></i> </button> </div> </div> </div> <!-- !labels-input-list --> </div> <!-- !Label --> <!-- hostname --> <div class="form-group"> <label for="container_hostname" class="col-sm-2 control-label text-left">Hostname</label> <div class="col-sm-10"> <input type="text" name="container_hostname" class="form-control" ng-model="state.selectedTemplate.Hostname" placeholder="leave empty to use docker default"> </div> </div> <!-- !hostname --> </div> <!-- !advanced-options --> <!-- actions --> <div class="col-sm-12 form-section-title"> Actions </div> <div class="form-group"> <div class="col-sm-12"> <button type="button" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !formValues.network" ng-click="createTemplate()" button-spinner="state.actionInProgress"> <span ng-hide="state.actionInProgress">Deploy the container</span> <span ng-show="state.actionInProgress">Deployment in progress...</span> </button> <span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span> </div> </div> <!-- !actions --> </form> </rd-widget-body> </rd-widget> </div> <!-- container-form --> </div> <div class="row"> <div class="col-sm-12" style="height: 100%"> <rd-template-widget> <rd-widget-header icon="fa-rocket" title-text="Templates"> <div ng-if="availableCategories.length > 0" class="pull-right"> Category <select ng-model="state.filters.Categories"> <option value="!">All</option> <option ng-repeat="category in availableCategories" value="{{ category }}">{{ category }}</option> </select> </div> </rd-widget-header> <rd-widget-taskbar> <div> <!-- Platform --> <span class="btn-group btn-group-sm" style="margin-right: 15px;"> <label class="btn btn-primary" ng-model="state.filters.Platform" uib-btn-radio="'!'"> All </label> <label class="btn btn-primary" ng-model="state.filters.Platform" uib-btn-radio="'windows'"> <i class="fab fa-windows" aria-hidden="true"></i> Windows </label> <label class="btn btn-primary" ng-model="state.filters.Platform" uib-btn-radio="'linux'"> <i class="fab fa-linux" aria-hidden="true"></i> Linux </label> </span> </div> </rd-widget-taskbar> <rd-widget-body classes="padding template-widget-body"> <form class="form-horizontal"> <div ng-if="templatesKey !== 'linuxserver.io' && state.showDeploymentSelector"> <div class="col-sm-12 form-section-title"> Deployment method </div> <div class="form-group"></div> <div class="form-group" style="margin-bottom: 0"> <div class="boxselector_wrapper"> <div ng-click="updateCategories(templates, state.filters.Type)"> <input type="radio" id="template_stack" ng-model="state.filters.Type" value="stack"> <label for="template_stack"> <div class="boxselector_header"> <i class="fa fa-th-list" aria-hidden="true" style="margin-right: 2px;"></i> Stack </div> <p>Multi-containers deployment</p> </label> </div> <div ng-click="updateCategories(templates, state.filters.Type)"> <input type="radio" id="template_container" ng-model="state.filters.Type" value="container"> <label for="template_container"> <div class="boxselector_header"> <i class="fa fa-server" aria-hidden="true" style="margin-right: 2px;"></i> Container </div> <p>Single container deployment</p> </label> </div> </div> </div> </div> <div ng-if="templatesKey !== 'linuxserver.io' && state.showDeploymentSelector"> <div class="col-sm-12 form-section-title"> Templates </div> <div class="form-group"></div> </div> <div class="template-list"> <!-- template --> <div ng-repeat="tpl in templates | filter:state.filters:true" class="template-container" id="template_{{ tpl.index }}" ng-click="selectTemplate(tpl.index, $index)"> <div class="template-main"> <!-- template-image --> <span class=""> <img class="template-logo" ng-src="{{ tpl.Logo }}" /> </span> <!-- !template-image --> <!-- template-details --> <span class="col-sm-12"> <!-- template-line1 --> <div class="template-line"> <span class="template-title"> {{ tpl.Title }} </span> <span> <i class="fab fa-windows" aria-hidden="true" ng-if="tpl.Platform === 'windows'"></i> <i class="fab fa-linux" aria-hidden="true" ng-if="tpl.Platform === 'linux'"></i> <!-- Arch / Platform --> </span> </div> <!-- !template-line1 --> <!-- template-line2 --> <div class="template-line"> <span class="template-description"> {{ tpl.Description }} </span> <span class="small text-muted" ng-if="tpl.Categories.length > 0"> {{ tpl.Categories.join(', ') }} </span> </div> <!-- !template-line2 --> </span> <!-- !template-details --> </div> <!-- !template --> </div> <div ng-if="!templates" class="text-center text-muted"> Loading... </div> <div ng-if="(templates | filter:state.filters:true).length == 0" class="text-center text-muted"> No templates available. </div> </div> </form> </rd-widget-body> </rd-template-widget> </div> </div>