Bug fixes

This commit is contained in:
Vincent Fiduccia 2015-12-17 09:57:53 -07:00
parent b9478560eb
commit 93224c3997
46 changed files with 366 additions and 354 deletions

View File

@ -2,6 +2,6 @@ import Ember from 'ember';
export default Ember.Route.extend({ export default Ember.Route.extend({
beforeModel: function() { beforeModel: function() {
this.transitionTo('admin-tab.accounts'); this.transitionTo('admin-tab.audit-logs');
} }
}); });

View File

@ -1,5 +1,5 @@
<section class="header"> <section class="header">
{{#link-to "admin-tab.processes" tagName="h1" href=false}}<a>Proccesses</a>{{/link-to}} <h1>{{#link-to "admin-tab.processes"}}Proccesses:{{/link-to}}</h1>
{{model.processInstance.id}} {{model.processInstance.id}}
</section> </section>

View File

@ -3,32 +3,30 @@ import C from 'ui/utils/constants';
import {denormalizeName} from 'ui/services/settings'; import {denormalizeName} from 'ui/services/settings';
export default Ember.Route.extend({ export default Ember.Route.extend({
endpoint: Ember.inject.service(),
settings: Ember.inject.service(), settings: Ember.inject.service(),
model: function() { beforeModel() {
var store = this.get('store');
return Ember.RSVP.all([ return Ember.RSVP.all([
this.get('store').find('setting', denormalizeName(C.SETTING.API_HOST)), store.find('setting', denormalizeName(C.SETTING.API_HOST)),
this.get('store').find('setting', denormalizeName(C.SETTING.CATALOG_URL)), store.find('setting', denormalizeName(C.SETTING.CATALOG_URL)),
this.get('store').find('setting', denormalizeName(C.SETTING.VM_ENABLED)), store.find('setting', denormalizeName(C.SETTING.VM_ENABLED)),
]).then((/* response */) => { ]);
},
model() {
var settings = this.get('settings');
return Ember.Object.create({ return Ember.Object.create({
host: this.get('settings').get(C.SETTING.API_HOST), host: settings.get(C.SETTING.API_HOST),
catalog: this.get('settings').get(C.SETTING.CATALOG_URL), catalog: settings.get(C.SETTING.CATALOG_URL),
vm: this.get('settings').get(C.SETTING.VM_ENABLED) || false, vm: settings.get(C.SETTING.VM_ENABLED) || false,
});
}); });
}, },
setupController: function(controller, model) { resetController(controller, isExiting /*, transition*/ ) {
/*not sure we need this anymore except maybe to set error to null?*/
controller.set('model', model);
controller.set('error', null);
},
resetController: function(controller, isExiting /*, transition*/ ) {
if (isExiting) { if (isExiting) {
controller.set('backToAdd', false); controller.set('backToAdd', false);
controller.set('error', null);
} }
} }
}); });

View File

@ -18,28 +18,23 @@
</section> </section>
<section class="well"> <section class="well">
<h4>Virtual Machine</h4> <h4>Virtual Machines</h4>
<hr> <hr>
<h5>Here you can enable access to virtual machines.</h5> <h5>Enable Virtual Machine support in the UI. Requires hosts that have <a href="https://en.wikipedia.org/wiki/Kernel-based_Virtual_Machine" target="_blank">KVM</a> loaded.</h5>
<div class="row r-mt20"> <div class="r-pt10">
<div class="col-md-2">
<label>Virtual Machines Access</label>
</div>
<div class="col-md-10">
<div class="radio small"> <div class="radio small">
<label> <label>
{{radio-button selection=model.vm value=true}} {{radio-button selection=model.vm value=true}}
Enable Enabled
</label> </label>
</div> </div>
<div class="radio small"> <div class="radio small">
<label> <label>
{{radio-button selection=model.vm value=false}} {{radio-button selection=model.vm value=false}}
Disable Disabled
</label> </label>
</div> </div>
</div> </div>
</div>
</section> </section>
{{/unless}} {{/unless}}

View File

@ -1,7 +1,7 @@
import Ember from 'ember'; import Ember from 'ember';
export default Ember.Controller.extend({ export default Ember.Controller.extend({
queryParams: ['category', 'catalogid'], queryParams: ['category', 'catalogId'],
category: 'all', category: 'all',
catalogid: 'library' catalogId: 'all'
}); });

View File

@ -4,7 +4,12 @@ export default Ember.Controller.extend({
application: Ember.inject.controller(), application: Ember.inject.controller(),
catalogController: Ember.inject.controller('applications-tab.catalog'), catalogController: Ember.inject.controller('applications-tab.catalog'),
category: Ember.computed.alias('catalogController.category'), category: Ember.computed.alias('catalogController.category'),
selectedCatalog: Ember.computed.alias('catalogController.catalogid'), selectedCatalog: Ember.computed.alias('catalogController.catalogId'),
categories: Ember.computed.alias('model.categories'),
catalogIds: Ember.computed.alias('model.catalogIds'),
// > 2 because 'all' is one of them.
showCatalogDropdown: Ember.computed.gt('catalogIds.length',2),
search: '', search: '',
actions: { actions: {
@ -17,10 +22,6 @@ export default Ember.Controller.extend({
} }
}, },
categories: Ember.computed.alias('model.categories'),
catalogIds: Ember.computed.alias('model.catalogIds'),
arrangedContent: function() { arrangedContent: function() {
var search = this.get('search').toUpperCase(); var search = this.get('search').toUpperCase();
var result = []; var result = [];

View File

@ -3,18 +3,20 @@
Catalog Catalog
</h1> </h1>
{{#if showCatalogDropdown}}
<div class="btn-group pull-right left-divider"> <div class="btn-group pull-right left-divider">
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Catalog: <span class="text-capitalize">{{selectedCatalog}}</span> <i class="icon icon-chevron-down"></i></button> <button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Catalog: <span class="text-capitalize">{{selectedCatalog}}</span> <i class="icon icon-chevron-down"></i></button>
<ul class="dropdown-menu dropdown-menu-right"> <ul class="dropdown-menu dropdown-menu-right">
{{#each catalogIds as |catalog|}} {{#each catalogIds as |catalog|}}
<li> <li>
{{#link-to "applications-tab.catalog" (query-params catalogid=catalog)}} {{#link-to "applications-tab.catalog" (query-params catalogId=catalog)}}
<span class="text-capitalize">{{catalog}}</span> <span class="text-capitalize">{{catalog}}</span>
{{/link-to}} {{/link-to}}
</li> </li>
{{/each}} {{/each}}
</ul> </ul>
</div> </div>
{{/if}}
<div class="btn-group pull-right left-divider"> <div class="btn-group pull-right left-divider">
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Category: <span class="text-capitalize">{{category}}</span> <i class="icon icon-chevron-down"></i></button> <button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Category: <span class="text-capitalize">{{category}}</span> <i class="icon icon-chevron-down"></i></button>
@ -38,7 +40,12 @@
<section class="r-plr15"> <section class="r-plr15">
{{#each arrangedContent as |catalogItem|}} {{#each arrangedContent as |catalogItem|}}
<div class="container-catalog text-center"> <div class="container-catalog text-center">
<h5>{{catalogItem.name}}</h5> <h5>
{{catalogItem.name}}
{{#if showCatalogDropdown}}
<span class="small">in <span class="text-capitalize">{{catalogItem.catalogId}}</span></span>
{{/if}}
</h5>
<div class="catalog-icon"> <div class="catalog-icon">
<img src="{{catalogItem.links.icon}}"/> <img src="{{catalogItem.links.icon}}"/>
</div> </div>

View File

@ -17,7 +17,7 @@ export default Ember.Route.extend({
category: { category: {
refreshModel: true refreshModel: true
}, },
catalogid: { catalogId: {
refreshModel: true refreshModel: true
} }
}, },
@ -40,13 +40,13 @@ export default Ember.Route.extend({
var cache = this.get('cache'); var cache = this.get('cache');
// If the catalogIds dont match we need to go get the other catalog from the store since we do not cache all catalogs // If the catalogIds dont match we need to go get the other catalog from the store since we do not cache all catalogs
if ( cache && cache.catalogId === params.catalogid) if ( cache && cache.catalogId === params.catalogId)
{ {
return filter(cache, params.category, this.get('catalogIds')); return filter(cache, params.category, this.get('catalogIds'));
} }
if (params.catalogid) { if (params.catalogId) {
this.controllerFor('applications-tab.catalog.index').set('selectedCatalog', params.catalogid); this.controllerFor('applications-tab.catalog.index').set('selectedCatalog', params.catalogId);
} }
var version = this.get('settings.rancherVersion'); var version = this.get('settings.rancherVersion');
@ -54,8 +54,8 @@ export default Ember.Route.extend({
'category_ne': 'system', 'category_ne': 'system',
}; };
if (params.catalogid !== 'all') { if (params.catalogId !== 'all') {
qp['catalogId'] = params.catalogid; qp['catalogId'] = params.catalogId;
} }
if ( version ) if ( version )
@ -66,7 +66,7 @@ export default Ember.Route.extend({
var url = addQueryParams(this.get('app.catalogEndpoint')+'/templates', qp); var url = addQueryParams(this.get('app.catalogEndpoint')+'/templates', qp);
return this.get('store').request({url: url}).then((response) => { return this.get('store').request({url: url}).then((response) => {
response.catalogId = params.catalogid; response.catalogId = params.catalogId;
this.set('cache', response); this.set('cache', response);
return filter(response, params.category, this.get('catalogIds')); return filter(response, params.category, this.get('catalogIds'));
}); });
@ -77,7 +77,6 @@ export default Ember.Route.extend({
var out = Ember.Object.create({ var out = Ember.Object.create({
categories: uniqKeys(data, 'category'), categories: uniqKeys(data, 'category'),
catalogIds: catalogIds, catalogIds: catalogIds,
}); });
if ( category === 'all' ) { if ( category === 'all' ) {
@ -89,4 +88,12 @@ export default Ember.Route.extend({
return out; return out;
} }
}, },
resetController: function (controller, isExiting/*, transition*/) {
if (isExiting)
{
controller.set('category', 'all');
controller.set('catalogId', 'all');
}
}
}); });

View File

@ -3,7 +3,7 @@ import Ember from 'ember';
export default Ember.Controller.extend({ export default Ember.Controller.extend({
actions: { actions: {
changeCertificate(cert) { changeCertificate(cert) {
this.get('application').transitionToRoute('certificate.detail', cert.get('id')); this.transitionToRoute('certificates.detail', cert.get('id'));
}, },
}, },
}); });

View File

@ -1,19 +1,23 @@
<section class="header"> <section class="header">
<h1 class="right-divider"> <h1>{{#link-to "certificates"}}Certificate:{{/link-to}}</h1>
{{#link-to "certificates" tagName="h1" href=false}}<a>Certificates</a>{{/link-to}}
{{#power-select options=model.allCertificates selected=model.certificate onchange=(action "changeCertificate") searchField="displayName" as |obj|}} {{#power-select options=model.allCertificates selected=model.certificate onchange=(action "changeCertificate") searchField="displayName" as |obj|}}
<div class="clip"><i class="{{obj.stateIcon}} {{obj.stateColor}}" tooltip="{{obj.displayState}}" data-placement="left"></i> {{obj.displayName}}</div> <div class="clip"><i class="{{obj.stateIcon}} {{obj.stateColor}}" tooltip="{{obj.displayState}}" data-placement="left"></i> {{obj.displayName}}</div>
{{/power-select}} {{/power-select}}
</h1>
{{action-menu model=model.certificate size="sm"}} {{action-menu model=model.certificate size="sm"}}
</section> </section>
<section> {{#if model.certificate.description}}
{{partial "description"}} <section>
<div class="well description">
<label>Description</label>
<p>{{model.certificate.description}}</p>
</div>
</section>
{{/if}}
<section>
<div class="row"> <div class="row">
<div class="row-same-height row-full-height"> <div class="row-same-height row-full-height">
<div class="col-sm-12 col-md-6 col-md-height col-md-full-height col-top" style="margin-bottom: 20px;"> <div class="col-sm-12 col-md-6 col-md-height col-md-full-height col-top" style="margin-bottom: 20px;">

View File

@ -1,6 +1,6 @@
<h4>Catalog</h4> <h4>Catalog</h4>
<hr> <hr>
<h5>You can add and remove catalogs here. When adding ensure you give your catalog a name and git url. The correct format of git urls can be found <a href="https://git-scm.com/docs/git-clone#_git_urls_a_id_urls_a" target="_blank">here</a>.</h5> <h5>The catalog can retrieve templates from multiple sources. Each one needs a unique name and any URL that <code>git clone</code> can handle (see <a href="https://git-scm.com/docs/git-clone#_git_urls_a_id_urls_a" target="_blank">docs</a> for more info).</h5>
<div class=""> <div class="">
{{form-key-value {{form-key-value
nameLabel="Catalog URL" nameLabel="Catalog URL"

View File

@ -0,0 +1,28 @@
import Ember from 'ember';
export default Ember.Component.extend({
dockerCompose: null,
rancherCompose: null,
classNames: ['row'],
didReceiveAttrs() {
this.highlightAll();
},
highlightAll() {
Ember.run.next(() => {
this.$('CODE').each(function(idx, elem) {
Prism.highlightElement(elem);
});
});
},
// The highlighting breaks Ember's link to the content, so it's not automatically updated
// when the content change.. manually trigger that.
yamlChanged: function() {
this.$('CODE.docker-compose').html(this.get('dockerCompose'));
this.$('CODE.rancher-compose').html(this.get('rancherCompose'));
this.highlightAll();
}.observes('dockerCompose','rancherCompose'),
});

View File

@ -0,0 +1,8 @@
<div class="col-md-6">
docker-compose.yml {{zero-clipboard text=dockerCompose}}
<pre class="line-numbers"><code class="docker-compose language-yaml">{{dockerCompose}}</code></pre>
</div>
<div class="col-md-6">
rancher-compose.yml {{zero-clipboard text=rancherCompose}}
<pre class="line-numbers"><code class="rancher-compose language-yaml">{{rancherCompose}}</code></pre>
</div>

View File

@ -1,11 +1,12 @@
import NewAlias from 'ui/components/new-aliasservice/component'; import NewOrEdit from 'ui/mixins/new-or-edit';
import Ember from 'ember'; import Ember from 'ember';
export default NewAlias.extend({ export default Ember.Component.extend(NewOrEdit, {
existing: Ember.computed.alias('originalModel'), existing: Ember.computed.alias('originalModel'),
editing: true, editing: true,
service: null, service: null,
primaryResource: Ember.computed.alias('service'),
actions: { actions: {
done() { done() {
@ -21,4 +22,12 @@ export default NewAlias.extend({
var original = this.get('originalModel'); var original = this.get('originalModel');
this.set('service', original.clone()); this.set('service', original.clone());
}, },
doneSaving: function() {
this.send('done');
},
didInsertElement() {
this.$('INPUT')[0].focus();
},
}); });

View File

@ -1,14 +1,11 @@
<section class="header"> <section class="header">
{{#link-to "environments" tagName="h1" href=false}}<a>Stack:</a>{{/link-to}} <h1>{{#link-to "environments"}}Stack:{{/link-to}}</h1>
{{#power-select options=all selected=model onchange=(action "changeStack") searchField="displayName" as |obj|}} {{#power-select options=all selected=model onchange=(action "changeStack") searchField="displayName" as |obj|}}
<div class="clip r-pr15"><i class="{{obj.stateIcon}} {{obj.stateColor}}" tooltip="{{obj.displayState}}" data-placement="left"></i> {{obj.displayName}}</div> <div class="clip r-pr15"><i class="{{obj.stateIcon}} {{obj.stateColor}}" tooltip="{{obj.displayState}}" data-placement="left"></i> {{obj.displayName}}</div>
{{/power-select}} {{/power-select}}
<div class="btn-group pull-right"> {{header-state model=model}}
{{#link-to "environment.index" classNames="btn btn-sm btn-default"}}<i class="icon icon-tasks" data-placement="bottom" tooltip="List"></i>{{/link-to}}
{{#link-to "environment.graph" classNames="btn btn-sm btn-default"}}<i class="icon icon-share" data-placement="bottom" tooltip="Link Graph"></i>{{/link-to}}
{{#link-to "environment.code" classNames="btn btn-sm btn-default"}}<i class="icon icon-file" data-placement="bottom" tooltip="Compose YAML"></i>{{/link-to}}
</div>
<div class="left-divider"> <div class="left-divider">
{{#if outputs.length}} {{#if outputs.length}}
@ -43,13 +40,11 @@
{{action-menu model=model classNames="left-divider r-ml5" size="sm"}} {{action-menu model=model classNames="left-divider r-ml5" size="sm"}}
{{upgrade-btn environmentResource=model classNames="left-divider"}} <div class="btn-group left-divider">
{{#link-to "environment.index" classNames="btn btn-sm btn-default"}}<i class="icon icon-tasks" data-placement="bottom" tooltip="List"></i>{{/link-to}}
{{#if (and model.isError model.showTransitioningMessage)}} {{#link-to "environment.graph" classNames="btn btn-sm btn-default"}}<i class="icon icon-share" data-placement="bottom" tooltip="Link Graph"></i>{{/link-to}}
<div class="pull-right clip text-danger"> {{#link-to "environment.code" classNames="btn btn-sm btn-default"}}<i class="icon icon-file" data-placement="bottom" tooltip="Compose YAML"></i>{{/link-to}}
{{model.transitioningMessage}}
</div> </div>
{{/if}}
{{header-state model=model}} {{upgrade-btn environmentResource=model classNames="left-divider"}}
</section> </section>

View File

@ -43,7 +43,7 @@
{{form-key-value {{form-key-value
initialMap=machine.engineLabel initialMap=machine.engineLabel
changed=(action (mut machine.engineLabel)) changed=(action (mut machine.engineLabel))
nameLabel="Label" nameLabel="Engine Label"
keyLabel="Label" keyLabel="Label"
}} }}
</div> </div>

View File

@ -96,7 +96,7 @@
<div class="col-sm-12 col-md-8"> <div class="col-sm-12 col-md-8">
<div class="form-control-static"> <div class="form-control-static">
<button class="btn-circle" {{action "addDns"}}><i class="icon icon-plus-circle"/></button> <button class="btn-circle" {{action "addDnsResolver"}}><i class="icon icon-plus-circle"/></button>
</div> </div>
{{#if dnsResolverArray.length}} {{#if dnsResolverArray.length}}
<table class="table fixed no-lines no-top-padding tight"> <table class="table fixed no-lines no-top-padding tight">

View File

@ -1,6 +1,6 @@
import Ember from 'ember'; import Ember from 'ember';
export default Ember.Component.extend({ export default Ember.Component.extend({
classNames: ['pull-right','section','r-mt5'], classNames: ['header-state','pull-right','section','r-mt5'],
classNameBindings: ['model.stateColor'], classNameBindings: ['model.stateColor'],
}); });

View File

@ -1,6 +1,6 @@
<h4>Host</h4> <h4>Host Registration URL</h4>
<hr> <hr>
<h5>We need to know a little about how your environment is set up before you can register hosts. What base URL should hosts use to connect to the Rancher API?</h5> <h5>What base URL should hosts use to connect to the Rancher API?</h5>
<div style="margin-top: 15px;"> <div style="margin-top: 15px;">
<label>{{radio-button selection=customRadio value="no"}}&nbsp;This site's address:&nbsp;&nbsp;</label> <label>{{radio-button selection=customRadio value="no"}}&nbsp;This site's address:&nbsp;&nbsp;</label>

View File

@ -21,7 +21,6 @@ export default Ember.Component.extend(NewOrEdit, {
questionsArray: null, questionsArray: null,
selectedTemplateUrl: null, selectedTemplateUrl: null,
selectedTemplateModel: null, selectedTemplateModel: null,
showReadme: true,
readmeContent: null, readmeContent: null,
actions: { actions: {
@ -44,10 +43,6 @@ export default Ember.Component.extend(NewOrEdit, {
changeTemplate: function(tpl) { changeTemplate: function(tpl) {
this.get('application').transitionToRoute('applications-tab.catalog.launch', tpl.id); this.get('application').transitionToRoute('applications-tab.catalog.launch', tpl.id);
}, },
toggleReadMe: function() {
this.toggleProperty('showReadme');
}
}, },
didReceiveAttrs() { didReceiveAttrs() {

View File

@ -1,6 +1,5 @@
<section class="header"> <section class="header">
<h1>{{#link-to "applications-tab.catalog"}}Catalog:{{/link-to}}</h1>
{{#link-to "applications-tab.catalog" tagName="h1" href=false}}<a>Catalog</a>{{/link-to}}
{{#if editing}} {{#if editing}}
{{templateResource.name}} {{templateResource.name}}
@ -9,53 +8,38 @@
<div class="power-select-img"><img src="{{obj.links.icon}}"><div class="clip">{{obj.name}}</div></div> <div class="power-select-img"><img src="{{obj.links.icon}}"><div class="clip">{{obj.name}}</div></div>
{{/power-select}} {{/power-select}}
{{/if}} {{/if}}
</section> </section>
<div class="container"> <div class="container">
<section> <section>
<div class="well row"> <div class="well row">
<div class="col-sm-2 col-md-2"> <div class="col-sm-12 col-md-3">
{{#if templateResource.links.icon}} {{#if templateResource.links.icon}}
<img src={{templateResource.links.icon}} alt={{templateResource.name}} class="r-mr20" style="height:75px;max-width: 100%;"> <img src={{templateResource.links.icon}} alt={{templateResource.name}} class="r-mr20" style="height:75px;max-width: 100%;">
{{/if}} {{/if}}
<div>
<small><strong>Catalog:</strong> <span class="text-capitalize">{{templateResource.catalogId}}</span></small>
</div>
<div> <div>
<small><strong>Category:</strong> {{templateResource.category}}</small> <small><strong>Category:</strong> {{templateResource.category}}</small>
</div> </div>
<div> <div>
<small><strong>Release By:</strong> {{templateResource.maintainer}}</small> <small><strong>Maintainer:</strong> {{templateResource.maintainer}}</small>
</div> </div>
<div> <div>
<small><strong>Requires:</strong> {{templateResource.minimumRancherVersion}}</small> <small><strong>License:</strong> {{#if templateResource.license}}{{templateResource.license}}{{else}}<span class="text-muted">?</span>{{/if}}</small>
</div>
<div>
<small><strong>License:</strong> {{templateResource.license}}</small>
</div> </div>
</div> </div>
<div class="col-sm-10 col-md-10" style="min-height:160px;"> {{#if readmeContent}}
<div class="col-sm-12 col-md-9" style="overflow-y: auto; max-height: 500px;">
{{common-mark markdown=readmeContent}}
</div>
{{else}}
<h2 class="r-mb10"> <h2 class="r-mb10">
{{if editing "Upgrade" "Add"}} {{templateResource.name}} Stack {{#if editing}}&quot;{{environmentResource.name}}&quot;{{/if}} {{if editing "Upgrade" "Add"}} {{templateResource.name}} Stack {{#if editing}}&quot;{{environmentResource.name}}&quot;{{/if}}
</h2> </h2>
<p>{{templateResource.description}}</p> <p>{{templateResource.description}}</p>
</div>
{{#if readmeContent}}
<div class="col-sm-10 col-md-10 col-sm-offset-2 col-md-offset-2">
<div class="over-hr r-mt5 r-mb15">
<span style="text-transform: none;">
<nav>
<ul class="pagination pagination-sm r-m0">
<li class="">
<a href="#" {{action 'toggleReadMe'}}>{{if showReadme 'Hide' 'Show'}} Read Me</a>
</li>
</ul>
</nav>
</span>
</div>
{{#liquid-if showReadme}}
{{common-mark markdown=readmeContent}}
{{/liquid-if}}
</div>
{{/if}} {{/if}}
</div> </div>
</section> </section>

View File

@ -103,6 +103,7 @@
<ul class="nav nav-tabs nav-tabs-well shadowed" style="display: inline-block"> <ul class="nav nav-tabs nav-tabs-well shadowed" style="display: inline-block">
{{#if isVm}} {{#if isVm}}
<li role="presentation" class="tab" data-section="disks" {{action "selectTab" "disks"}}><a>Disks</a></li> <li role="presentation" class="tab" data-section="disks" {{action "selectTab" "disks"}}><a>Disks</a></li>
<li role="presentation" class="tab" data-section="userdata" {{action "selectTab" "userdata"}}><a>User Data</a></li>
{{else}} {{else}}
<li role="presentation" class="tab" data-section="command" {{action "selectTab" "command"}}><a>Command</a></li> <li role="presentation" class="tab" data-section="command" {{action "selectTab" "command"}}><a>Command</a></li>
<li role="presentation" class="tab" data-section="volumes" {{action "selectTab" "volumes"}}><a>Volumes</a></li> <li role="presentation" class="tab" data-section="volumes" {{action "selectTab" "volumes"}}><a>Volumes</a></li>
@ -110,7 +111,6 @@
<li role="presentation" class="tab" data-section="security" {{action "selectTab" "security"}}><a>Security/Host</a></li> <li role="presentation" class="tab" data-section="security" {{action "selectTab" "security"}}><a>Security/Host</a></li>
{{/if}} {{/if}}
<li role="presentation" class="tab" data-section="userdata" {{action "selectTab" "userdata"}}><a>User Data</a></li>
<li role="presentation" class="tab" data-section="healthcheck" {{action "selectTab" "healthcheck"}}><a>Health Check</a></li> <li role="presentation" class="tab" data-section="healthcheck" {{action "selectTab" "healthcheck"}}><a>Health Check</a></li>
<li role="presentation" class="tab" data-section="labels" {{action "selectTab" "labels"}}><a>Labels</a></li> <li role="presentation" class="tab" data-section="labels" {{action "selectTab" "labels"}}><a>Labels</a></li>
<li role="presentation" class="tab" data-section="scheduling" {{action "selectTab" "scheduling"}}><a>Scheduling</a></li> <li role="presentation" class="tab" data-section="scheduling" {{action "selectTab" "scheduling"}}><a>Scheduling</a></li>
@ -121,6 +121,10 @@
<div class="section container-fluid" data-section="disks"> <div class="section container-fluid" data-section="disks">
{{form-disks instance=launchConfig}} {{form-disks instance=launchConfig}}
</div> </div>
<div class="section container-fluid" data-section="userdata">
{{form-userdata instance=launchConfig}}
</div>
{{else}} {{else}}
<div class="section container-fluid" data-section="command"> <div class="section container-fluid" data-section="command">
{{form-command {{form-command
@ -152,10 +156,6 @@
</div> </div>
{{/if}} {{/if}}
<div class="section container-fluid" data-section="userdata">
{{form-userdata instance=launchConfig}}
</div>
<div class="section container-fluid" data-section="healthcheck"> <div class="section container-fluid" data-section="healthcheck">
{{form-healthcheck healthCheck=launchConfig.healthCheck errors=healthCheckErrors}} {{form-healthcheck healthCheck=launchConfig.healthCheck errors=healthCheckErrors}}
</div> </div>

View File

@ -133,11 +133,11 @@
</div> </div>
{{/if}} {{/if}}
{{#if isAdminTab}} {{#if isAdminTab}}
{{#link-to "admin-tab.audit-logs"}}<i class="icon icon-folder-open"></i>Audit Log{{/link-to}}
{{#link-to "admin-tab.processes"}}<i class="icon icon-processes"></i>Processes{{/link-to}}
{{#link-to "admin-tab.accounts"}}<i class="icon icon-users"></i>Accounts{{/link-to}} {{#link-to "admin-tab.accounts"}}<i class="icon icon-users"></i>Accounts{{/link-to}}
{{#link-to "admin-tab.auth"}}<i class="icon icon-key"></i>Access Control{{/link-to}} {{#link-to "admin-tab.auth"}}<i class="icon icon-key"></i>Access Control{{/link-to}}
{{#link-to "admin-tab.settings"}}<i class="icon icon-network"></i>Settings{{/link-to}} {{#link-to "admin-tab.settings"}}<i class="icon icon-network"></i>Settings{{/link-to}}
{{#link-to "admin-tab.processes"}}<i class="icon icon-processes"></i>Processes{{/link-to}}
{{#link-to "admin-tab.audit-logs"}}<i class="icon icon-folder-open"></i>Audit Logs{{/link-to}}
{{/if}} {{/if}}
</nav> </nav>
</div> </div>

View File

@ -1,32 +1,18 @@
<div class="clearfix {{if single 'single'}}"> {{#unless single}}
{{#unless single}} <div class="clearfix">
<div class="stack-state {{model.stateBackground}}"><i class="{{model.stateIcon}}"></i></div> <div class="stack-state {{model.stateBackground}}"><i class="{{model.stateIcon}}"></i></div>
{{/unless}}
<div class="header-left collapser"> <div class="header-left collapser">
{{#unless single}}
<a {{action "toggleCollapse"}} class="btn btn-link"><i class="icon {{if collapsed 'icon-plus' 'icon-minus'}}" alt="Toggle stack details" title=""></i></a> <a {{action "toggleCollapse"}} class="btn btn-link"><i class="icon {{if collapsed 'icon-plus' 'icon-minus'}}" alt="Toggle stack details" title=""></i></a>
{{/unless}}
</div> </div>
<div class="header-left"> <div class="header-left">
{{#if single}}
<label class="section" style="color:white;">
State: {{model.displayState~}}
{{#if model.showTransitioningMessage}}
<span class="force-wrap">
({{model.transitioningMessage}})
</span>
{{/if}}
</label>
{{else}}
<h4> <h4>
<span class="divider clip">{{#link-to "environment" model.id}}{{model.name}}{{/link-to}}</span> <span class="divider clip">{{#link-to "environment" model.id}}{{model.name}}{{/link-to}}</span>
</h4> </h4>
{{/if}}
</div> </div>
{{#if (and (not single) outputs.length)}} {{#if outputs.length}}
<div class="header-left"> <div class="header-left">
<div class="btn-group r-ml10"> <div class="btn-group r-ml10">
<button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@ -42,9 +28,7 @@
{{/if}} {{/if}}
<div class="header-right right-divider stack-actions wide"> <div class="header-right right-divider stack-actions wide">
{{#unless single}}
{{action-menu model=model}} {{action-menu model=model}}
{{/unless}}
</div> </div>
<div class="header-right right-divider count"> <div class="header-right right-divider count">
@ -58,7 +42,6 @@
</div> </div>
<div class="header-right right-divider stack-template"> <div class="header-right right-divider stack-template">
{{#unless single}}
{{upgrade-btn environmentResource=model}} {{upgrade-btn environmentResource=model}}
<div class="btn-group"> <div class="btn-group">
{{#link-to "service.new" (query-params environmentId=model.id) classNames="btn btn-default btn-sm"}}Add Service{{/link-to}} {{#link-to "service.new" (query-params environmentId=model.id) classNames="btn btn-default btn-sm"}}Add Service{{/link-to}}
@ -75,10 +58,9 @@
{{/if}} {{/if}}
</ul> </ul>
</div> </div>
{{/unless}}
</div> </div>
</div> </div>
{{/unless}}
{{#liquid-if (not collapsed)}} {{#liquid-if (not collapsed)}}
<div class="stack-body {{if single 'single-body'}}"> <div class="stack-body {{if single 'single-body'}}">

View File

@ -1,15 +1,13 @@
<section class="header"> <section class="header">
{{#link-to "containers" tagName="h1" href=false}}<a>Container:</a>{{/link-to}} <h1>{{#link-to "containers"}}Container:{{/link-to}}</h1>
{{#power-select options=model.primaryHost.instances selected=model onchange=(action "changeContainer") searchField="displayName" as |obj|}} {{#power-select options=model.primaryHost.instances selected=model onchange=(action "changeContainer") searchField="displayName" as |obj|}}
<div class="clip r-mr15"> <div class="clip r-mr15">
<i class="{{obj.stateIcon}} {{obj.stateColor}}" tooltip="{{obj.displayState}}" data-placement="left"></i> {{obj.displayName}} <i class="{{obj.stateIcon}} {{obj.stateColor}}" tooltip="{{obj.displayState}}" data-placement="left"></i> {{obj.displayName}}
</div> </div>
{{/power-select}} {{/power-select}}
<strong>on {{#link-to "host" model.primaryHost.id}}{{model.primaryHost.displayName}}{{/link-to}}</strong>
<strong>on {{#link-to "host" model.primaryHost.id href=false}}<a>{{model.primaryHost.displayName}}</a>{{/link-to}}</strong>
{{action-menu model=model size="sm"}} {{action-menu model=model size="sm"}}
{{header-state model=model}} {{header-state model=model}}
</section> </section>

View File

@ -6,13 +6,14 @@ export default Ember.Route.extend({
var stack = par.get('stack'); var stack = par.get('stack');
return stack.doAction('exportconfig').then((config) => { return stack.doAction('exportconfig').then((config) => {
// Windows needs CRLFs // Windows needs CRLFs
config.dockerComposeConfig = config.dockerComposeConfig.split(/\r?\n/).join('\r\n'); var dockerCompose = config.dockerComposeConfig.split(/\r?\n/).join('\r\n');
config.rancherComposeConfig = config.rancherComposeConfig.split(/\r?\n/).join('\r\n'); var rancherCompose = config.rancherComposeConfig.split(/\r?\n/).join('\r\n');
return Ember.Object.create({ return Ember.Object.create({
stack: stack, stack: stack,
all: par.get('all'), all: par.get('all'),
composeConfig: config dockerCompose: dockerCompose,
rancherCompose: rancherCompose,
}); });
}); });
}, },

View File

@ -1,16 +1,8 @@
{{environment-header model=model.stack all=model.all}} {{environment-header model=model.stack all=model.all}}
<section> <section>
<div class="row"> {{compose-yaml
<div class="col-md-6"> dockerCompose=model.dockerCompose
docker-compose.yml {{zero-clipboard text=model.composeConfig.dockerComposeConfig}} rancherCompose=model.rancherCompose
<pre class="line-numbers"><code class="language-yaml"> }}
{{model.composeConfig.dockerComposeConfig}}</code></pre>
</div>
<div class="col-md-6">
rancher-compose.yml {{zero-clipboard text=model.composeConfig.rancherComposeConfig}}
<pre class="line-numbers"><code class="language-yaml">
{{model.composeConfig.rancherComposeConfig}}</code></pre>
</div>
</div>
</section> </section>

View File

@ -1,18 +0,0 @@
import Ember from 'ember';
export default Ember.View.extend({
didInsertElement: function() {
this._super();
this.highlightAll();
},
highlightAll: function() {
this.$('CODE').each(function(idx, elem) {
Prism.highlightElement(elem);
});
},
yamlChanged: function() {
this.highlightAll();
}.observes('context.model.composeConfig.{dockerComposeConfig,rancherComposeConfig}'),
});

View File

@ -1,5 +1,5 @@
<section class="header"> <section class="header">
{{#link-to "hosts" tagName="h1" href=false}}<a>Host:</a>{{/link-to}} <h1>{{#link-to "hosts"}}Host:{{/link-to}}</h1>
{{#power-select options=model.all selected=host onchange=(action "changeHost") searchField="displayName" as |obj|}} {{#power-select options=model.all selected=host onchange=(action "changeHost") searchField="displayName" as |obj|}}
<div class="clip"><i class="{{obj.stateIcon}} {{obj.stateColor}}" tooltip="{{obj.displayState}}" data-placement="left"></i> {{obj.displayName}}</div> <div class="clip"><i class="{{obj.stateIcon}} {{obj.stateColor}}" tooltip="{{obj.displayState}}" data-placement="left"></i> {{obj.displayName}}</div>

View File

@ -1,6 +1,6 @@
<section class="header"> <section class="header">
<h1 class="right-divider"> <h1 class="right-divider">
{{#link-to "infrastructure-tab" tagName="li" href=false}}<a>Hosts</a>{{/link-to}} {{#link-to "infrastructure-tab"}}Hosts:{{/link-to}}
Add Host Add Host
</h1> </h1>
</section> </section>

View File

@ -19,14 +19,15 @@ function serialize(obj) {
} }
} }
var auditLog = Resource.extend({}); var AuditLog = Resource.extend({});
auditLog.reopenClass({ AuditLog.reopenClass({
headers: { headers: {
[C.HEADER.PROJECT]: undefined, // Requests for projects use the user's scope, not the project [C.HEADER.PROJECT]: undefined, // Requests for projects use the user's scope, not the project
}, },
mangleIn: function(data) { mangleIn: function(data) {
// request and responseObject should be plain JSON objects, not typeified.
if ( data.requestObject ) if ( data.requestObject )
{ {
data.requestObject = serialize(data.requestObject); data.requestObject = serialize(data.requestObject);
@ -42,4 +43,4 @@ auditLog.reopenClass({
}); });
export default auditLog; export default AuditLog;

View File

@ -106,7 +106,7 @@ var Container = Resource.extend({
var resource = this.get('state'); var resource = this.get('state');
var health = this.get('healthState'); var health = this.get('healthState');
if ( ['running','active','updating-active'].indexOf(resource) >= 0 ) if ( C.ACTIVEISH_STATES.indexOf(resource) >= 0 )
{ {
if ( health === null || health === 'healthy' ) if ( health === null || health === 'healthy' )
{ {

View File

@ -126,7 +126,7 @@ var Environment = Resource.extend({
unremoved++; unremoved++;
if ( ['running','active','updating-active'].indexOf(resource) >= 0 && health === 'healthy' ) if ( C.ACTIVEISH_STATES.indexOf(resource) >= 0 && health === 'healthy' )
{ {
healthy++; healthy++;
} }

View File

@ -306,8 +306,9 @@ var Service = Resource.extend({
instances.forEach((instance) => { instances.forEach((instance) => {
var resource = instance.get('state'); var resource = instance.get('state');
var health = instance.get('healthState'); var health = instance.get('healthState');
var startOnce = instance.get('labels')[C.LABEL.START_ONCE] === true;
if ( ['running','active','updating-active'].indexOf(resource) >= 0 && (health === 'healthy' || health === null) ) if ( C.ACTIVEISH_STATES.indexOf(resource) >= 0 && (health === 'healthy' || health === null) )
{ {
healthy++; healthy++;
} }

View File

@ -1,11 +1,9 @@
<section class="header"> <section class="header">
<h1> <h1>Service:</h1>
Service:
</h1>
{{#power-select options=model.stack.services selected=service onchange=(action "changeService") searchField="displayName" as |obj|}} {{#power-select options=model.stack.services selected=service onchange=(action "changeService") searchField="displayName" as |obj|}}
<div class="clip"><i class="{{obj.stateIcon}} {{obj.stateColor}}" tooltip="{{obj.displayState}}" data-placement="left"></i> {{obj.displayName}}</div> <div class="clip"><i class="{{obj.stateIcon}} {{obj.stateColor}}" tooltip="{{obj.displayState}}" data-placement="left"></i> {{obj.displayName}}</div>
{{/power-select}} {{/power-select}}
<strong>in {{#link-to "environment" model.stack.id}}{{model.stack.displayName}}{{/link-to}}</strong>
{{action-menu model=service classNames="r-ml5" size="sm"}} {{action-menu model=service classNames="r-ml5" size="sm"}}
{{header-state model=service}} {{header-state model=service}}

View File

@ -16,7 +16,7 @@ export default Ember.Service.extend(Ember.Evented, {
init() { init() {
this._super(); this._super();
this.set('all', this.get('store').allUnremoved('activesetting')); this.set('all', this.get('store').all('activesetting'));
}, },
unknownProperty(key) { unknownProperty(key) {

View File

@ -21,7 +21,7 @@ $lines-to-show: 4;
h5 { h5 {
border-bottom: solid thin $lightGray; border-bottom: solid thin $lightGray;
text-align: left; text-align: left;
padding: 15px; padding: 15px 0;
margin: 0 15px; margin: 0 15px;
} }

View File

@ -763,6 +763,10 @@ SECTION.header {
} }
} }
.header-state {
margin-left: 10px;
}
.sad-ie { .sad-ie {
background-image: url('images/sad-ie.png'); background-image: url('images/sad-ie.png');
width: 300px; width: 300px;

View File

@ -1,15 +1,16 @@
import Ember from 'ember'; import Ember from 'ember';
export default Ember.ArrayProxy.extend(Ember.SortableMixin, { export default Ember.ArrayProxy.extend({
sourceContent: null, sourceContent: null,
sortProperties: null,
init: function() { init: function() {
if ( !this.get('sortProperties') ) if ( !this.get('sortProperties') )
{ {
this.set('sortProperties', ['name','id']); this.set('sortProperties', ['displayName','name','id']);
} }
this.sourceContentChanged();
this._super(); this._super();
this.set('content', []);
}, },
sourceContentChanged: function() { sourceContentChanged: function() {
@ -17,5 +18,8 @@ export default Ember.ArrayProxy.extend(Ember.SortableMixin, {
return (Ember.get(item,'state')||'').toLowerCase() === 'active'; return (Ember.get(item,'state')||'').toLowerCase() === 'active';
}); });
this.set('content', x); this.set('content', x);
}.observes('sourceContent.@each.state').on('init'), }.observes('sourceContent.@each.state'),
// The array proxy reads this property
arrangedContent: Ember.computed.sort('content','sortProperties'),
}); });

View File

@ -1,40 +1,17 @@
var C = { var C = {
PREFS: {
ACCESS_WARNING: 'accessWarning',
PROJECT_DEFAULT: 'defaultProjectId',
I_HATE_SPINNERS: 'iHateSpinners',
EXPANDED_STACKS: 'expandedStacks',
SORT_STACKS_BY: 'sortStacksBy',
},
COOKIE: { COOKIE: {
TOKEN: 'token', TOKEN: 'token',
}, },
SESSION: { EXTERNALID: {
BACK_TO: 'backTo', KIND_SEPARATOR: '://',
USER_ID: 'user', GROUP_SEPARATOR: ':',
ACCOUNT_ID: 'accountId', KIND_ALL: 'all',
USER_TYPE: 'userType', KIND_USER: 'user',
PROJECT: 'projectId', KIND_CATALOG: 'catalog',
IDENTITY: 'userIdentity', KIND_SYSTEM: 'system',
IDENTITY_TYPE: 'userType', KIND_KUBERNETES: 'kubernetes',
GITHUB_CACHE: 'githubCache', CATALOG_DEFAULT_GROUP: 'library',
GITHUB_ORGS: 'orgs',
GITHUB_TEAMS: 'teams',
},
HEADER: {
PROJECT: 'x-api-project-id',
NO_CHALLENGE: 'x-api-no-challenge',
NO_CHALLENGE_VALUE: 'true',
ACCOUNT_ID: 'x-api-account-id',
},
USER: {
TYPE_NORMAL: 'user',
TYPE_ADMIN: 'admin',
BASIC_BEARER: 'x-api-bearer',
}, },
GITHUB: { GITHUB: {
@ -44,35 +21,11 @@ var C = {
SCOPE: 'read:org', SCOPE: 'read:org',
}, },
PROJECT: { HEADER: {
TYPE_RANCHER: 'rancher_id', PROJECT: 'x-api-project-id',
TYPE_GITHUB_USER: 'github_user', NO_CHALLENGE: 'x-api-no-challenge',
TYPE_GITHUB_TEAM: 'github_team', NO_CHALLENGE_VALUE: 'true',
TYPE_GITHUB_ORG: 'github_org', ACCOUNT_ID: 'x-api-account-id',
TYPE_LDAP_USER: 'ldap_user',
TYPE_LDAP_GROUP: 'ldap_group',
TYPE_OPENLDAP_USER: 'openldap_user',
TYPE_OPENLDAP_GROUP: 'openldap_group',
PERSON: 'person',
TEAM: 'team',
ORG: 'org',
ROLE_MEMBER: 'member',
ROLE_OWNER: 'owner',
},
SETTING: {
// Dots in key names do not mix well with Ember, so use $ in their place.
DOT_CHAR: '$',
VERSION_RANCHER: 'rancher$server$image',
VERSION_COMPOSE: 'rancher$compose$version',
VERSION_CATTLE: 'cattle$version',
VERSION_MACHINE: 'docker$machine$version',
VERSION_GMS: 'go$machine$service$version',
API_HOST: 'api$host',
CATALOG_URL: 'catalog$url',
VM_ENABLED: 'vm$enabled',
}, },
KEY: { KEY: {
@ -113,15 +66,62 @@ var C = {
KVM: 'io.rancher.host.kvm', KVM: 'io.rancher.host.kvm',
}, },
EXTERNALID: { PREFS: {
KIND_SEPARATOR: '://', ACCESS_WARNING: 'accessWarning',
GROUP_SEPARATOR: ':', PROJECT_DEFAULT: 'defaultProjectId',
KIND_ALL: 'all', I_HATE_SPINNERS: 'iHateSpinners',
KIND_USER: 'user', EXPANDED_STACKS: 'expandedStacks',
KIND_CATALOG: 'catalog', SORT_STACKS_BY: 'sortStacksBy',
KIND_SYSTEM: 'system', },
KIND_KUBERNETES: 'kubernetes',
CATALOG_DEFAULT_GROUP: 'library', PROJECT: {
TYPE_RANCHER: 'rancher_id',
TYPE_GITHUB_USER: 'github_user',
TYPE_GITHUB_TEAM: 'github_team',
TYPE_GITHUB_ORG: 'github_org',
TYPE_LDAP_USER: 'ldap_user',
TYPE_LDAP_GROUP: 'ldap_group',
TYPE_OPENLDAP_USER: 'openldap_user',
TYPE_OPENLDAP_GROUP: 'openldap_group',
PERSON: 'person',
TEAM: 'team',
ORG: 'org',
ROLE_MEMBER: 'member',
ROLE_OWNER: 'owner',
},
SESSION: {
BACK_TO: 'backTo',
USER_ID: 'user',
ACCOUNT_ID: 'accountId',
USER_TYPE: 'userType',
PROJECT: 'projectId',
IDENTITY: 'userIdentity',
IDENTITY_TYPE: 'userType',
GITHUB_CACHE: 'githubCache',
GITHUB_ORGS: 'orgs',
GITHUB_TEAMS: 'teams',
},
SETTING: {
// Dots in key names do not mix well with Ember, so use $ in their place.
DOT_CHAR: '$',
VERSION_RANCHER: 'rancher$server$image',
VERSION_COMPOSE: 'rancher$compose$version',
VERSION_CATTLE: 'cattle$version',
VERSION_MACHINE: 'docker$machine$version',
VERSION_GMS: 'go$machine$service$version',
API_HOST: 'api$host',
CATALOG_URL: 'catalog$url',
VM_ENABLED: 'vm$enabled',
},
USER: {
TYPE_NORMAL: 'user',
TYPE_ADMIN: 'admin',
BASIC_BEARER: 'x-api-bearer',
}, },
}; };
@ -148,4 +148,14 @@ C.SYSTEM_LABELS_WITH_CONTROL = [
C.LABEL.PULL_IMAGE, C.LABEL.PULL_IMAGE,
]; ];
C.ACTIVEISH_STATES = [
'running',
'active',
'updating-active',
'updating-running',
'healthy',
'degraded',
'unhealthy'
];
export default C; export default C;

View File

@ -1,5 +1,6 @@
import Ember from "ember"; import Ember from "ember";
import Socket from "ui/utils/socket"; import Socket from "ui/utils/socket";
import C from 'ui/utils/constants';
/* /*
Usage: Usage:
@ -27,7 +28,7 @@ export default Ember.Object.extend(Ember.Evented, {
}, },
available: function() { available: function() {
return ['running','updating-running','active','updating-active','unhealthy'].indexOf(this.get('resource.state')) >= 0; return C.ACTIVEISH_STATES.indexOf(this.get('resource.state')) >= 0;
}.property('resource.state'), }.property('resource.state'),
active: Ember.computed.and('available', 'connected'), active: Ember.computed.and('available', 'connected'),

View File

@ -1,15 +1,16 @@
import Ember from 'ember'; import Ember from 'ember';
export default Ember.ArrayProxy.extend(Ember.SortableMixin, { export default Ember.ArrayProxy.extend({
sourceContent: null, sourceContent: null,
sortProperties: null,
init: function() { init: function() {
if ( !this.get('sortProperties') ) if ( !this.get('sortProperties') )
{ {
this.set('sortProperties', ['name','id']); this.set('sortProperties', ['displayName','name','id']);
} }
this.sourceContentChanged();
this._super(); this._super();
this.set('content', []);
}, },
sourceContentChanged: function() { sourceContentChanged: function() {
@ -17,5 +18,8 @@ export default Ember.ArrayProxy.extend(Ember.SortableMixin, {
return (Ember.get(item,'state')||'').toLowerCase() !== 'purged'; return (Ember.get(item,'state')||'').toLowerCase() !== 'purged';
}); });
this.set('content', x); this.set('content', x);
}.observes('sourceContent.@each.state').on('init'), }.observes('sourceContent.@each.state'),
// The array proxy gets it's data from here
arrangedContent: Ember.computed.sort('content','sortProperties'),
}); });

View File

@ -1,21 +1,27 @@
import Ember from 'ember'; import Ember from 'ember';
export default Ember.ArrayProxy.extend(Ember.SortableMixin, { var undesireable = ['removed','purging','purged'];
export default Ember.ArrayProxy.extend({
sourceContent: null, sourceContent: null,
sortProperties: null,
init: function() { init: function() {
if ( !this.get('sortProperties') ) if ( !this.get('sortProperties') )
{ {
this.set('sortProperties', ['name','id']); this.set('sortProperties', ['displayName','name','id']);
} }
this.sourceContentChanged();
this._super(); this._super();
this.set('content', []);
}, },
sourceContentChanged: function() { sourceContentChanged: function() {
var x = (this.get('sourceContent')||[]).filter(function(item) { var x = (this.get('sourceContent')||[]).filter(function(item) {
return ['removed','purging','purged'].indexOf((Ember.get(item,'state')||'').toLowerCase()) === -1; return undesireable.indexOf((Ember.get(item,'state')||'').toLowerCase()) === -1;
}); });
this.set('content', x); this.set('content', x);
}.observes('sourceContent.@each.state').on('init'), }.observes('sourceContent.@each.state'),
// The array proxy reads this property
arrangedContent: Ember.computed.sort('content','sortProperties'),
}); });

View File

@ -6,7 +6,7 @@ export default Ember.Route.extend({
return store.find('virtualmachine', params.virtualmachine_id).then(function(vm) { return store.find('virtualmachine', params.virtualmachine_id).then(function(vm) {
var host = vm.get('primaryHost'); var host = vm.get('primaryHost');
if ( host.get('instances') ) if ( !host || host.get('instances') )
{ {
return vm; return vm;
} }

View File

@ -1,19 +1,16 @@
<section class="header"> <section class="header">
<h1 class="right-divider"> <h1>{{#link-to "virtualmachines"}}Virtual Machine:{{/link-to}}</h1>
{{#if model.vm.primaryHost}}
{{#link-to "hosts" tagName="li" href=false}}<a>Hosts</a>{{/link-to}}
{{#link-to "host" model.vm.primaryHost.id tagName="li" href=false}}<a>{{model.vm.primaryHost.displayName}}</a>{{/link-to}}
{{else}}
{{#link-to "virtualmachines" tagName="li" href=false}}<a>Virtual Machines</a>{{/link-to}}
{{/if}}
{{#power-select options=model.vm.primaryHost.instances selected=model.vm onchange=(action "changeVirtualMachine") searchField="displayName" as |obj|}} {{#power-select options=model.vm.primaryHost.instances selected=model.vm onchange=(action "changeVirtualMachine") searchField="displayName" as |obj|}}
<div class="clip r-mr15"><i class="{{obj.stateIcon}} {{obj.stateColor}}" tooltip="{{obj.displayState}}" data-placement="left"></i> {{obj.displayName}}</div> <div class="clip r-mr15"><i class="{{obj.stateIcon}} {{obj.stateColor}}" tooltip="{{obj.displayState}}" data-placement="left"></i> {{obj.displayName}}</div>
{{/power-select}} {{/power-select}}
</h1> {{#if model.vm.primaryHost}}
<strong>on {{#link-to "host" model.vm.primaryHost.id}}{{model.vm.primaryHost.displayName}}{{/link-to}}</strong>
{{/if}}
{{action-menu model=model.vm size="sm"}} {{action-menu model=model.vm size="sm"}}
{{header-state model=model.vm}}
</section> </section>
<section> <section>

View File

@ -908,9 +908,9 @@ $text-muted: #999;
//** Abbreviations and acronyms border color //** Abbreviations and acronyms border color
$abbr-border-color: $gray-light; $abbr-border-color: $gray-light;
//** Headings small color //** Headings small color
$headings-small-color: $gray-light; $headings-small-color: $gray;
//** Blockquote small color //** Blockquote small color
$blockquote-small-color: $gray-light; $blockquote-small-color: $gray;
//** Blockquote font size //** Blockquote font size
$blockquote-font-size: ($font-size-base * 1.25); $blockquote-font-size: ($font-size-base * 1.25);
//** Blockquote border color //** Blockquote border color