Skip to content

Commit 1fac686

Browse files
committed
restyle the run list editor for clarity, other w.i.p.
1 parent fdfd1a7 commit 1fac686

7 files changed

Lines changed: 256 additions & 90 deletions

File tree

‎chef-server-webui/app/controllers/roles.rb‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ def edit
7474
@available_roles = Chef::Role.list.keys.sort
7575
@environments = Chef::Environment.list.keys.sort
7676
@current_env = session[:environment] || "_default"
77-
@run_list = @role.env_run_lists[@current_env]
77+
@run_lists = @environments.inject({}) { |run_lists, env| run_lists[env] = @role.env_run_lists[env] ; run_lists}
78+
@existing_run_list_environments = @role.env_run_lists.keys
79+
# merb select helper has no :include_blank => true, so fix the view in the controller.
80+
@existing_run_list_environments.unshift('')
7881
@available_recipes = list_available_recipes_for(@current_env)
7982
rescue => e
8083
Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
@@ -115,7 +118,7 @@ def create
115118
def update
116119
begin
117120
@role = Chef::Role.load(params[:id])
118-
@role.run_list(params[:for_role] ? params[:for_role] : [])
121+
@role.env_run_lists(params[:env_run_lists])
119122
@role.description(params[:description]) if params[:description] != ''
120123
@role.default_attributes(Chef::JSON.from_json(params[:default_attributes])) if params[:default_attributes] != ''
121124
@role.override_attributes(Chef::JSON.from_json(params[:override_attributes])) if params[:override_attributes] != ''

‎chef-server-webui/app/views/nodes/_form.html.erb‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
<div class="group form" id="run_list_editor_container">
1212
<div id="availableRunListItemContainer" class="runListEditorColumn">
13-
<div id="available_nodes_container" >
14-
<span class="runListEditorLabel">Available Roles</span>
13+
<div id="available_roles_container" class="runListItemListContainer">
14+
<div class="runListEditorLabel">Available Roles</div>
1515
<div class="sortable runListAvailableItemContainer">
1616
<ul id="node_available_roles" class="connectedSortable runListAvailableItemList">
1717
<% @available_roles.each do |role| %>
@@ -20,8 +20,8 @@
2020
</ul>
2121
</div>
2222
</div>
23-
<div id="available_recipes_container">
24-
<span class="runListEditorLabel">Available Recipes</span>
23+
<div id="available_recipes_container" class="runListItemListContainer">
24+
<div class="runListEditorLabel">Available Recipes</div>
2525
<div class="sortable runListAvailableItemContainer">
2626
<ul id="node_available_recipes" class="connectedSortable runListAvailableItemList">
2727
<% @available_recipes.each do |recipe| %>
@@ -32,15 +32,15 @@
3232
</div>
3333
</div>
3434
<div id="run_list_container" class="runListEditorColumn">
35-
<div id="environment_run_list_selector">
36-
<span class="runListEditorLabel">Run List</span>
37-
</div>
38-
<div class="sortable runListContainer">
39-
<ul class="ui-sortable connectedSortable runListItemList" id="for_node">
40-
<% @run_list.each do |entry| %>
41-
<li id="<%=h(entry)%>" class="<%= class_for_run_list_item(entry) -%> runListItem"><%=h(entry.name) -%></li>
42-
<% end %>
43-
</ul>
35+
<div class="runListItemListContainer">
36+
<div class="runListEditorLabel">Run List</div>
37+
<div class="sortable runListContainer">
38+
<ul class="ui-sortable connectedSortable runListItemList" id="for_node">
39+
<% @run_list.each do |entry| %>
40+
<li id="<%=h(entry)%>" class="<%= class_for_run_list_item(entry) -%> runListItem"><%=h(entry.name) -%></li>
41+
<% end %>
42+
</ul>
43+
</div>
4444
</div>
4545
</div>
4646
</div>
Lines changed: 66 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<div class="content roleFormContainer">
2-
<%= form(:action => form_url, :method => :post, :id => form_id, :class => 'form') do -%>
2+
<%= form(:action => form_url, :method => (method || :post), :id => form_id, :class => 'form roleForm') do -%>
33
<% unless form_for == 'edit' %>
44
<div class="group form">
55
<label class="label" for="role_name">Name</label>
@@ -16,8 +16,8 @@
1616

1717
<div class="group form" id="run_list_editor_container">
1818
<div id="availableRunListItemContainer" class="runListEditorColumn">
19-
<div id="available_roles_container" >
20-
<span class="runListEditorLabel">Available Roles</span>
19+
<div id="available_roles_container" class="runListItemListContainer">
20+
<div class="runListEditorLabel">Available Roles</div>
2121
<div class="sortable runListAvailableItemContainer">
2222
<ul id="role_available_roles" class="connectedSortable runListAvailableItemList">
2323
<% @available_roles.each do |role| %>
@@ -26,41 +26,74 @@
2626
</ul>
2727
</div>
2828
</div>
29-
<div id="available_recipes_container">
30-
<span class="runListEditorLabel">Available Recipes</span>
29+
<div id="available_recipes_container" class="runListItemListContainer">
30+
<div class="runListEditorLabel">Available Recipes</div>
3131
<div class="sortable runListAvailableItemContainer">
32-
<%= image_tag('indicator.gif', :class => 'spinner') -%>
32+
<div class="spinner">
33+
<%= image_tag('indicator.gif') -%>
34+
</div>
3335
<ul id="role_available_recipes" class="connectedSortable runListAvailableItemList">
3436
</ul>
3537
</div>
3638
</div>
3739
</div>
3840
<div id="run_list_container" class="runListEditorColumn">
39-
<div id="environment_run_list_selector">
40-
<span class="runListEditorLabel">Run List for Environment:</span>
41-
<span id="environment_run_list_selection_control"><%= select :environment, :collection => @environments, :selected => @current_env%></span>
42-
</div>
43-
<div class="sortable runListContainer">
44-
<% if @run_list %>
45-
<ul class="ui-sortable connectedSortable runListItemList" id="for_role">
46-
<% @run_list.each do |item| %>
47-
<li id="<%=h(item)%>" class="<%= class_for_run_list_item(item) -%> runListItem"><%=h display_run_list_item(item)-%></li>
48-
<% end %>
49-
</ul>
50-
<% else %>
51-
<span class="todo">Run List Creator goes here</span>
41+
<div class="runListItemListContainer">
42+
<div id="environment_run_list_selector">
43+
<div class="runListEditorLabel">
44+
<span id="selectedRunListEditorLabel">
45+
<% if @current_env == '_default'%>
46+
Default Run List
47+
<% else %>
48+
Run List for <%= @current_env -%>
49+
<% end %>
50+
</span>
51+
<span id="environment_run_list_selection_control" currentEnvironment="">
52+
<%= select :environment, :collection => [['noop','Other Environments']].concat(@environments.map {|e| [e,e]}), :id => 'activeEnvironment'%>
53+
</span>
54+
</div>
55+
</div>
56+
<% @run_lists.each do |environment, run_list| %>
57+
<div class="runListWithControlsContainer <%= environment == @current_env ? 'active' : 'inactive' -%>" id="<%= environment %>">
58+
<div class="sortable runListContainer">
59+
<% if run_list %>
60+
<ul class="ui-sortable connectedSortable runListItemList" id="<%= environment %>">
61+
<% run_list.each do |item| %>
62+
<li id="<%=h(item)%>" class="<%= class_for_run_list_item(item) -%> runListItem"><%=h display_run_list_item(item)-%></li>
63+
<% end %>
64+
</ul>
65+
<% else %>
66+
<div class="emptyRunListControlsContainer" id="<%=environment -%>">
67+
<div class="cloneRunListTitle">This role has no environment specific run list for the (<%= environment -%>) environment.</div>
68+
<div class="cloneRunListOption">
69+
<a href="javascript:void(0)" class="createEmptyRunListControl" id="<%= environment -%>">Create a new empty run list</a>
70+
</div>
71+
<div class="cloneRunListOption">
72+
Or, clone an existing run list: <%= select :environment_to_clone, :collection => @existing_run_list_environments, :id => 'environmentToClone'%>
73+
</div>
74+
</div>
75+
<% end %>
76+
</div>
77+
<div class="runListAdditionalControls">
78+
<% if run_list && (environment != '_default')%>
79+
<a href="javascript:void(0);" class="runList" id="<%= environment %>">Remove environment specific run list for <%= environment %></a>
80+
<% end %>
81+
</div>
82+
</div>
5283
<% end %>
5384
</div>
5485
</div>
5586
<div id="current_run_list_display_container" class="runListEditorColumn">
56-
<span class="runListEditorLabel">Active Run List for <%= h(@current_env) -%></span>
57-
<div class="sortable activeRunListContainer">
58-
<div id="activeRunListName" class="todo">PREPROD</div>
59-
<ul class="runListItemList" id="active_run_list">
60-
<% @role.run_list_for(@current_env).each do |item| %>
61-
<li class="displayRunListItem"><%= h(item) -%></li>
62-
<% end %>
63-
</ul>
87+
<div class="runListItemListContainer">
88+
<div class="runListEditorLabel activeRunListLabel">Active Run List for <%= h(@current_env) -%></div>
89+
<div class="sortable activeRunListContainer">
90+
<div id="activeRunListName"><%= @role.active_run_list_for(@current_env).gsub('_default', 'default') -%></div>
91+
<ul class="runListItemList" id="active_run_list">
92+
<% @role.run_list_for(@current_env).each do |item| %>
93+
<li class="displayRunListItem"><%= h(item) -%></li>
94+
<% end %>
95+
</ul>
96+
</div>
6497
</div>
6598
</div>
6699
</div>
@@ -69,7 +102,7 @@
69102
<label class="label">Default and Override Attributes</label>
70103
<%= partial 'layout/jsonedit', :json => { :defaults => @role.default_attributes, :overrides => @role.override_attributes }.to_json%>
71104
<span class="description">
72-
A JSON hash for default attributes for nodes of this role. These attributes will only be applied if the node does not already have a value for the attributes.
105+
A JSON hash for default attributes for nodes of this role. These attributes will be applied according to the attribute precedence rules.
73106
</span>
74107
</div>
75108

@@ -79,4 +112,9 @@
79112
</div>
80113
</div>
81114
<% end =%>
115+
<a class="todo" href="javascript:void(0);" id="debugFormBuild">BUILD TEH HIDDEN FORMZ</a>
82116
</div>
117+
118+
<script type="text/javascript" charset="utf-8">
119+
window.initialCurrentEnv = '<%= @current_env %>';
120+
</script>

‎chef-server-webui/app/views/roles/edit.html.haml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
%h2.title= "Role #{h @role.name}"
44
.inner
55
= partial('navigation', :active => 'edit')
6-
= partial('form', :header => "Edit Role #{@role.name}", :form_id => 'edit_role', :submit_name => "Save Role", :submit_id => "edit_role_button", :form_for => 'edit', :form_url => url(:role, @role.name) )
6+
= partial('form', :header => "Edit Role #{@role.name}", :form_id => 'edit_role', :submit_name => "Save Role", :submit_id => "edit_role_button", :form_for => 'edit', :form_url => url(:role, @role.name), :method => 'put')

‎chef-server-webui/public/javascripts/chef.js‎

Lines changed: 83 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,29 @@
1919

2020
$(document).ready(function(){
2121

22-
$('form#edit_role, form#create_role').submit(function(event) {
23-
var form = $(this);
24-
var to_role = $('ul#for_role').sortable('toArray');
25-
if (form.attr('id') == 'edit_role') {
26-
form.append('<input type="hidden" name="_method" value="put">');
27-
}
28-
form.append($('input#role_name')).css('display', 'none');
29-
form.append($('textarea#role_description')).css('display', 'none');
22+
var buildHiddenFormFromDragDrop = function(form, runListSet) {
23+
runListSet.each(function(i, envRunListUl) {
24+
$(envRunListUl).find('li.runListItem').each(function(i, field) {
25+
form.append('<input type="hidden" name="env_run_lists[' + envRunListUl.id + '][]" value="' + field.id + '"/>');
26+
});
27+
});
28+
};
29+
30+
var buildHiddenFormFromJSONEditor = function(form) {
3031
form.append('<input type="hidden" id="default_attributes" name="default_attributes"/>');
31-
$('input#default_attributes').attr('value', BCJTEP.save('json["defaults"]'))
32+
$('input#default_attributes').attr('value', BCJTEP.save('json["defaults"]'));
3233
form.append('<input type="hidden" id="override_attributes" name="override_attributes"/>');
3334
$('input#override_attributes').attr('value', BCJTEP.save('json["overrides"]'));
34-
jQuery.each(to_role, function(i, field) {
35-
form.append('<input type="hidden" name="for_role[]" value="' + field + '"/>');
36-
});
37-
});
35+
};
3836

37+
$('a#debugFormBuild').click(function(event) {buildHiddenFormFromDragDrop(event)});
38+
39+
40+
$('form.roleForm').submit(function(event) {
41+
buildHiddenFormFromDragDrop($(this), $('ul.runListItemList'));
42+
buildHiddenFormFromJSONEditor($(this));
43+
});
44+
3945
$('form#edit_node, form#create_node').submit(function(event) {
4046
var form = $(this);
4147
var to_node = $('ul#for_node').sortable('toArray');
@@ -108,10 +114,10 @@ $(document).ready(function(){
108114
return;
109115
}
110116

111-
if (method == 'post' || method == 'put' || method == 'delete') {
117+
if (method === 'post' || method === 'put' || method === 'delete') {
112118
event.preventDefault();
113119
var form = $("<form/>").attr('method', 'post').attr('action', this.href).attr('style', 'display: none');
114-
if (method != "post") {
120+
if (method !== "post") {
115121
form.append($('<input type="hidden" name="_method"/>').attr('value', method));
116122
}
117123
form.insertAfter(this).submit();
@@ -129,10 +135,18 @@ $(document).ready(function(){
129135
$('a[rel*=facebox]').facebox();
130136

131137

132-
$('.connectedSortable').sortable({
133-
placeholder: 'ui-state-highlight',
134-
connectWith: $('.connectedSortable')
135-
}).disableSelection();
138+
var enableDragDropBehavior = function() {
139+
$('.connectedSortable').sortable({
140+
placeholder: 'ui-state-highlight',
141+
connectWith: $('.connectedSortable')
142+
}).disableSelection();
143+
};
144+
145+
var disableDragDropBehavior = function() {
146+
$('.connectedSortable').sortable("destroy");
147+
};
148+
149+
enableDragDropBehavior();
136150

137151
// The table tree!
138152
$('table.tree').treeTable({ expandable: true });
@@ -158,17 +172,62 @@ $(document).ready(function(){
158172
$("#sidebar_block").fadeIn();
159173
}
160174

175+
// Run list editor with per-env run lists for roles
161176

162-
163-
if ($('ul#role_available_recipes').size() !== 0) {
164-
var currentEnvironment = $('#environment_run_list_selection_control select').val();
177+
var populateAvailableRecipesForEnv = function(currentEnvironment, callback) {
165178
$.getJSON('/environments/' + currentEnvironment + '/recipes', function(data) {
166-
$('div#available_recipes_container img.spinner').remove();
167-
for (var i = data['recipes'].length - 1; i >= 0; i--){
179+
$('div#available_recipes_container .spinner').hide();
180+
for (var i=0; i < data['recipes'].length; i++) {
168181
var recipe = data['recipes'][i];
169182
$('ul#role_available_recipes').append('<li id="recipe[' + recipe + ']" class="ui-state-default runListItem">' + recipe + '</li>');
170183
};
171184
})
185+
if (callback) { callback(); }
186+
};
187+
188+
var depopulateAvailableRecipesForEnv = function() {
189+
$('ul#role_available_recipes li').remove();
190+
$('div#available_recipes_container .spinner').show();
172191
};
173192

193+
if ($('ul#role_available_recipes').size() !== 0) {
194+
populateAvailableRecipesForEnv(window.initialCurrentEnv);
195+
}
196+
197+
$('a.createEmptyRunListControl').each(function(i) {
198+
var environment = $(this).attr('id');
199+
$(this).click(function(event) {
200+
$('div.emptyRunListControlsContainer#' + environment).remove();
201+
var runListContainerForEnv = $('div.runListContainer#' + environment + 'RunListContainer');
202+
runListContainerForEnv.append('<ul class="ui-sortable connectedSortable runListItemList ohai" id="' + environment + '"></ul>');
203+
// remove all previous drag/drop events/behavior/whatever, then re-add it for the new run list container
204+
disableDragDropBehavior();
205+
enableDragDropBehavior();
206+
});
207+
});
208+
209+
var setActiveRunListLabelFor = function(environment) {
210+
var labelElement = $('span#selectedRunListEditorLabel');
211+
if (environment === '_default') {
212+
labelElement.html('Default Run List');
213+
}
214+
else {
215+
labelElement.html('Run List for ' + environment);
216+
}
217+
};
218+
219+
220+
$('select#activeEnvironment').change(function() {
221+
// set the active run list editor
222+
var newActiveEnvironment = $(this).val();
223+
if (newActiveEnvironment !== 'noop') {
224+
$('div.runListWithControlsContainer.active').removeClass('active').addClass('inactive');
225+
$('div.runListWithControlsContainer#' + newActiveEnvironment).addClass('active', 10000).removeClass('inactive');
226+
setActiveRunListLabelFor(newActiveEnvironment);
227+
depopulateAvailableRecipesForEnv();
228+
var selector = $(this);
229+
populateAvailableRecipesForEnv(newActiveEnvironment, function() { selector.val('noop');});
230+
}
231+
});
232+
174233
});

0 commit comments

Comments
 (0)