Project

General

Profile

« Previous | Next » 

Revision 09ce8a63

Added by Ori Rabin almost 9 years ago

Fixes #4419 - rearranging smart class parameters edit form

View differences:

app/assets/javascripts/application.js
//= require hidden_values
//= require select_on_click
//= require select2
//= require underscore
$(document).on('ContentLoad', function(){onContentLoad()});
Turbolinks.enableProgressBar();
app/assets/javascripts/lookup_keys.js
//on load
$(function() {
$(document).on("ContentLoad", function() {
//select the first tab
select_first_tab();
$(document).on('click', '.nav-tabs a[data-toggle="tab"]', function(){select_first_tab();});
......
$('.tabs-left .col-md-4').removeClass('col-md-4').addClass('col-md-8')
//remove variable click event
$(document).on('click', '.smart-var-tabs li a span', function(){ remove_node(this);});
fill_in_matchers();
$('.matchers').parents('form').on('submit', function(){
build_match();
})
$('.matcher_key').select2('destroy');
})
function select_first_tab(){
......
content = fix_template_context(content, context);
var new_id = new Date().getTime();
content = fix_template_names(content, assoc, new_id);
var field = '';
if (assoc == 'lookup_keys') {
$('#smart_vars .smart-var-tabs .active, #smart_vars .stacked-content .active').removeClass('active');
......
$('#smart_vars .smart-var-tabs').prepend(pill);
field = $('#smart_vars .stacked-content').prepend($(content).addClass('active'));
$('#smart_vars .smart-var-tabs li.active a').show('highlight', 500);
} else if (assoc == 'lookup_values') {
field = $(item).parent().find('tbody').first().append($(content).find('tr'));
$(item).parent().find('table').removeClass('hidden');
} else {
field = $(content).insertBefore($(item));
}
$(item).closest("form").trigger({type: 'nested:fieldAdded', field: field});
$('a[rel="popover"]').popover({html: true});
$('a[rel="twipsy"]').tooltip();
$(field).find('select').select2({ allowClear: true });
return new_id;
}
......
var mergeOverrides = fields.find("[id$='_merge_overrides']");
var avoidDuplicates = fields.find("[id$='_avoid_duplicates']");
var overrideMergeDiv = fields.find("[id$='lookup_key_override_merge']");
var validators = fields.find("[id^='optional_input_validators']");
changeCheckboxEnabledStatus(mergeOverrides, keyType == 'array' || keyType == 'hash');
changeCheckboxEnabledStatus(avoidDuplicates, keyType == 'array' && $(mergeOverrides).attr('checked') == 'checked');
overrideMergeDiv.toggle(keyType == 'array' || keyType == 'hash');
validators.collapse('show');
validators.parent().find('legend').removeClass('collapsed');
}
function mergeOverridesChanged(item) {
......
function filterByEnvironment(item){
if ($(item).val()=="") {
$('ul.smart-var-tabs li[data-used-environments] a').removeClass('text-muted');
$('ul.smart-var-tabs li[data-used-environments] a').removeClass('hidden');
return;
}
var selected = $(item).find('option:selected').text();
$('ul.smart-var-tabs li[data-used-environments] a').addClass('text-muted');
$('ul.smart-var-tabs li[data-used-environments*="'+selected+'"] a').removeClass('text-muted');
$('ul.smart-var-tabs li[data-used-environments] a').addClass('hidden');
$('ul.smart-var-tabs li[data-used-environments*="'+selected+'"] a').removeClass('hidden');
}
function filterByClassParam(item) {
var term = $(item).val().trim();
if (term.length > 0) {
$('ul.smart-var-tabs li[data-used-environments]').removeClass('search-marker').addClass('hide');
$('ul.smart-var-tabs li[data-used-environments] a[href*='+term+']:not(.selected-marker)').parent().addClass('search-marker').removeClass('hide');
$('ul.smart-var-tabs li[data-used-environments]').removeClass('search-marker').addClass('hidden');
$('ul.smart-var-tabs li[data-used-environments] a[href*='+term+']:not(.selected-marker)').parent().addClass('search-marker').removeClass('hidden');
} else{
$('ul.smart-var-tabs li[data-used-environments]:not(.selected-marker)').addClass('search-marker').removeClass('hide');
$('ul.smart-var-tabs li[data-used-environments]:not(.selected-marker)').addClass('search-marker').removeClass('hidden');
}
return false;
}
......
var validator_rule_field = $(item).closest('.fields').find("[id$='_validator_rule']");
validator_rule_field.attr('disabled', validatorType == "" ? 'disabled' : null);
}
const KEY_DELM = ",";
const EQ_DELM = "=";
function match_to_key_value(match){
var regex = new RegExp('[' + KEY_DELM + EQ_DELM + ']');
var keys = [], values = [],
split_matcher = match.replace(/(\s+,) | (,\s+)/g, '').split(regex);
$.each(split_matcher, function (index, value) {
if (index % 2 === 0) {
keys.push(value);
} else {
values.push(value);
}
});
return [keys.join(KEY_DELM), values.join(KEY_DELM)];
}
function key_value_to_match(keys, values){
var match = "";
keys.split(KEY_DELM).forEach(function (el, index) {
match += el + EQ_DELM + values.split(KEY_DELM)[index] + KEY_DELM;
});
return match.slice(0, -1);
}
function fill_in_matchers(){
$('.matchers').each(function () {
var matcher = $(this);
var match = matcher.find('.match').val();
var matcher_key = matcher.find('.matcher_key');
var matcher_value = matcher.find('.matcher_value');
var order = matcher.closest('.matcher-parent').find('#order').val().split('\n');
matcher_key.empty();
matcher_key.append("<option></option>");
$.each(order, function (index, value) {
matcher_key.append($("<option>", {value: _.escape(value), html: _.escape(value)}));
});
if (match) {
var key_value = match_to_key_value(match);
matcher_key.find("option[value='" + key_value[0] + "']").attr('selected', 'selected');
matcher_value.val(key_value[1]);
}
});
}
function build_match() {
$('.matchers').each(function () {
var match = $(this).find('.match');
var matcher_key = $(this).find('.matcher_key');
var matcher_value = $(this).find('.matcher_value');
match.val(key_value_to_match(matcher_key.val(), matcher_value.val()));
});
}
app/assets/stylesheets/application.scss
height: 100vh !important;
width: 100vw !important;
}
.expander {
cursor: pointer;
*cursor: hand;
.caret{
border-width: 8px;
transform: rotate(0deg);
transition-duration: 0.8s;
}
&.collapsed .caret{
transform: rotate(-90deg);
}
}
.btn-fullscreen{
margin-right: 10px;
}
app/assets/stylesheets/bootstrap_and_overrides.scss
// help-inline is not defined in Bootstrap3
.help-inline{
margin: 0 5px;
line-height: 2.428571;
margin: 0;
display: inline;
color: #737373;
}
.well {
......
.puppetca-filters {
min-width: 150px;
}
legend {
margin-bottom: 10px;
}
app/assets/stylesheets/puppetclasses.scss
.fields {
border-top: 1px dotted lightgrey;
padding-top: 10px;
&:nth-child(3) {
border-top: none;
}
}
}
.lookup-keys-container {
h6 {
margin-top: 0px;
}
.help-block {
margin: 0px;
}
input[type=checkbox]{
margin-top: 11px;
}
}
app/helpers/common_parameters_helper.rb
end
end
def use_puppet_default_help link_title = _("Explain use Puppet default"), title = _("Use Puppet default")
def use_puppet_default_help link_title = nil, title = _("Use Puppet default")
popover(link_title, _("Do not send this parameter via the ENC.<br>Puppet will use the value defined in the manifest."), :title => title)
end
end
app/helpers/layout_helper.rb
text = options.delete(:help_text)
inline = options.delete(:help_inline)
field(f, attr, options) do
help_inline = inline.blank? ? '' : content_tag(:span, inline, :class => "help-block")
help_inline = inline.blank? ? '' : content_tag(:span, inline, :class => "help-inline")
f.check_box(attr, options, checked_value, unchecked_value) + " #{text} " + help_inline.html_safe
end
end
......
end
def field(f, attr, options = {})
error = f.object.errors[attr] if f && f.object.respond_to?(:errors)
table_field = options.delete(:table_field)
error = f.object.errors[attr] if f && f.object.respond_to?(:errors)
help_inline = help_inline(options.delete(:help_inline), error)
help_block = content_tag(:span, options.delete(:help_block), :class => "help-block")
size_class = options.delete(:size) || "col-md-4"
content_tag(:div, :class=> "clearfix") do
content_tag :div, :class => "form-group #{error.empty? ? "" : 'has-error'}",
:id => options.delete(:control_group_id) do
required = options.delete(:required) # we don't want to use html5 required attr so we delete the option
required_mark = ' *' if required.nil? ? is_required?(f, attr) : required
label = options[:label] == :none ? '' : options.delete(:label)
label ||= ((clazz = f.object.class).respond_to?(:gettext_translation_for_attribute_name) &&
s_(clazz.gettext_translation_for_attribute_name attr)) if f
label = label.present? ? label_tag(attr, "#{label}#{required_mark}".html_safe, :class => "col-md-2 control-label") : ''
fullscreen = options[:fullscreen] ? fullscreen_button("$(this).prev().find('textarea')") : ""
label.html_safe +
content_tag(:div, :class => size_class) do
yield.html_safe + help_block.html_safe
end.html_safe + fullscreen + help_inline.html_safe
size_class = options.delete(:size) || "col-md-4"
label = add_label(options, f, attr)
if table_field
add_help_to_label(size_class, label, help_inline, '') do
yield
end.html_safe
else
help_block = content_tag(:span, options.delete(:help_block), :class => "help-block")
content_tag(:div, :class => "clearfix") do
content_tag :div, :class => "form-group #{error.empty? ? "" : 'has-error'}",
:id => options.delete(:control_group_id) do
fullscreen = options[:fullscreen] ? fullscreen_button("$(this).prev().find('textarea')") : ""
add_help_to_label(size_class, label, help_inline, fullscreen) do
yield.html_safe + help_block.html_safe
end
end.html_safe
end
end
end
def add_help_to_label(size_class, label, help_inline, fullscreen)
label.html_safe +
content_tag(:div, :class => size_class) do
yield
end.html_safe + fullscreen + help_inline.html_safe
end
# The target should have class="collapse [out|in]" out means collapsed on load and in means expanded.
# Target must also have a unique id.
def collapsing_legend(title, target, collapsed = '')
content_tag(:legend, :class => "expander #{collapsed}", :data => {:toggle => 'collapse', :target => target}) do
content_tag(:span, '', :class => 'caret') + title
end
end
def check_required options, f, attr
required = options.delete(:required) # we don't want to use html5 required attr so we delete the option
return ' *' if required.nil? ? is_required?(f, attr) : required
end
def add_label options, f, attr
label_size = options.delete(:label_size) || "col-md-2"
required_mark = check_required(options, f, attr)
label = options[:label] == :none ? '' : options.delete(:label)
label ||= ((clazz = f.object.class).respond_to?(:gettext_translation_for_attribute_name) &&
s_(clazz.gettext_translation_for_attribute_name attr)) if f
label = label.present? ? label_tag(attr, "#{label}#{required_mark}".html_safe, :class => label_size + " control-label") : ''
label
end
def is_required?(f, attr)
return false unless f && f.object.class.respond_to?(:validators_on)
f.object.class.validators_on(attr).any? do |validator|
......
end
def fullscreen_button(element = "$(this).prev()")
button_tag(:type => 'button', :class => 'btn btn-default btn-sm', :onclick => "set_fullscreen(#{element})", :title => _("Full screen")) do
button_tag(:type => 'button', :class => 'btn btn-default btn-sm btn-fullscreen', :onclick => "set_fullscreen(#{element})", :title => _("Full screen")) do
icon_text('resize-full')
end
end
app/helpers/lookup_keys_helper.rb
def param_type_selector(f, options = {})
selectable_f f, :key_type, options_for_select(LookupKey::KEY_TYPES.map { |e| [_(e),e] }, f.object.key_type),{},
options.merge({ :disabled => (f.object.is_param && !f.object.override), :size => "col-md-8",
:help_block => popover(_("Parameter types"),_("<dl>" +
:help_inline => popover("",_("<dl>" +
"<dt>String</dt> <dd>Everything is taken as a string.</dd>" +
"<dt>Boolean</dt> <dd>Common representation of boolean values are accepted.</dd>" +
"<dt>Integer</dt> <dd>Integer numbers only, can be negative.</dd>" +
......
selectable_f f, :validator_type, options_for_select(LookupKey::VALIDATOR_TYPES.map { |e| [_(e),e] }, f.object.validator_type),{:include_blank => _("None")},
{ :disabled => (f.object.is_param && !f.object.override), :size => "col-md-8",
:onchange => 'validatorTypeSelected(this)',
:help_block => popover(_("Validator type"),_("<dl>" +
:help_inline => popover("",_("<dl>" +
"<dt>List</dt> <dd>A list of the allowed values, specified in the Validator rule field.</dd>" +
"<dt>Regexp</dt> <dd>Validates the input with the regular expression in the Validator rule field.</dd>" +
"</dl>"), :title => _("Validation types")).html_safe}
app/models/lookup_key.rb
KEY_DELM = ","
EQ_DELM = "="
VALUE_REGEX =/\A[^#{KEY_DELM}]+#{EQ_DELM}[^#{KEY_DELM}]+(#{KEY_DELM}[^#{KEY_DELM}]+#{EQ_DELM}[^#{KEY_DELM}]+)*\Z/
audited :associated_with => :audit_class, :allow_mass_assignment => true, :except => :lookup_values_count
validates_lengths_from_database
......
has_many :lookup_values, :dependent => :destroy, :inverse_of => :lookup_key
accepts_nested_attributes_for :lookup_values,
:reject_if => lambda { |a| a[:value].blank? && (a[:use_puppet_default].nil? || a[:use_puppet_default] == "0")},
:reject_if => :reject_invalid_lookup_values,
:allow_destroy => true
before_validation :validate_and_cast_default_value, :unless => Proc.new{|p| p.use_puppet_default }
......
write_attribute(:path, using_default ? nil : v)
end
def reject_invalid_lookup_values(attributes)
attributes[:match].empty? ||
(attributes[:value].blank? &&
(attributes[:use_puppet_default].nil? || attributes[:use_puppet_default] == "0"))
end
def default_value_before_type_cast
value_before_type_cast default_value
end
app/models/lookup_value.rb
audited :associated_with => :lookup_key, :allow_mass_assignment => true
belongs_to :lookup_key, :counter_cache => true
validates :match, :presence => true, :uniqueness => {:scope => :lookup_key_id}
validates :match, :presence => true, :uniqueness => {:scope => :lookup_key_id}, :format => LookupKey::VALUE_REGEX
validate :value_present?
delegate :key, :to => :lookup_key
before_validation :sanitize_match
app/views/lookup_keys/_fields.html.erb
<div id='<%= (f.object.key || 'new_lookup_keys').to_s.gsub(' ','_') + '_' + f.object.id.to_s %>' class='tab-pane fields' >
<div id='<%= (f.object.key || 'new_lookup_keys').to_s.gsub(' ', '_') + '_' + f.object.id.to_s %>' class='tab-pane fields'>
<% is_param = f.object.is_param %>
<%= text_f(f, :environment_classes, :value => f.object.environment_classes.map(&:environment).to_sentence, :label => _('Puppet Environments'),:size => "col-md-8", :disabled=>true) if is_param%>
<%= remove_child_link(_("Remove %s?") % (f.object.new_record? ? _("Variable") : f.object), f , {:class => 'btn btn-danger hide'}) unless controller_name == "lookup_keys" %>
<%= text_f f, :key, :disabled => f.object.is_param, :size => "col-md-8" %>
<%= f.hidden_field :key if is_param %>
<%= textarea_f f, :description, :rows => :auto, :size => "col-md-8" %>
<%= show_puppet_class f %>
<%= checkbox_f(f, :override, :onchange => 'toggleOverrideValue(this)', :size => "col-md-8",
:help_block => _('Whether the smart variable value is managed by Foreman')
) if is_param%>
<%= param_type_selector(f, :onchange => 'keyTypeChange(this)') %>
<%= checkbox_f(f, :use_puppet_default, :label => _('Use Puppet default'), :size => "col-md-8",
:help_block => use_puppet_default_help,
:onchange=>'toggleUsePuppetDefaultValue(this, "default_value")',
:disabled => (f.object.is_param && !f.object.override)) if is_param %>
<%= textarea_f f, :default_value, :value => f.object.default_value_before_type_cast,:size => "col-md-8",
:disabled => (f.object.is_param && (!f.object.override || f.object.use_puppet_default)),
:fullscreen => :true,
:rows => :auto, :help_block => _("Value to use when there is no match") %>
</br>
<div <%= "id=#{(f.object.key || 'new_lookup_keys').to_s.gsub(' ','_')}_lookup_key_override_value" %> style=<%= "display:none;" if (f.object.is_param && !f.object.override) %>>
<legend><%= _("Optional input validator") %></legend>
<p class="help-block">
<%= _('Note that if you use ERB as a value of parameter, value will be validated during ENC evaluation. If value is invalid, ENC evaluation will fail.') %>
</p>
<%= checkbox_f(f, :required, :size => "col-md-8", :disabled => !f.object.override,
:help_block => _("If checked, will raise an error if there is no default value and no matcher provide a value.")
<%= remove_child_link(_("Remove %s?") % (f.object.new_record? ? _("Variable") : f.object), f, { :class => 'btn btn-danger hide' }) unless controller_name == "lookup_keys" %>
<fieldset>
<legend><%= _("Parameter details") %></legend>
<%= text_f f, :key, :disabled => f.object.is_param, :size => "col-md-8" %>
<%= f.hidden_field :key if is_param %>
<%= textarea_f f, :description, :rows => :auto, :size => "col-md-8" %>
<%= text_f(f, :environment_classes, :value => f.object.environment_classes.map(&:environment).to_sentence, :label => _('Puppet Environments'), :size => "col-md-8", :disabled => true) if is_param %>
<%= show_puppet_class f %>
</fieldset>
<fieldset>
<legend><%= _("Default behavior") %></legend>
<h6><%= _("Override the default value of the Puppet class parameter.") if f.object.is_param%></h6>
<%= checkbox_f(f, :override, :onchange => 'toggleOverrideValue(this)', :size => "col-md-8",
:help_inline => popover('', _('Whether the class parameter value is managed by Foreman.'))
) if is_param %>
<%= validator_type_selector f %>
<%= text_f f, :validator_rule, :size => "col-md-8", :disabled => f.object.validator_type.blank? %>
<div <%= "id=#{(f.object.key || 'new_lookup_keys').to_s.gsub(' ','_')}_lookup_key_override_merge" %> style=<%= "display:none;" if (!f.object.merge_overrides) %>>
<legend><%= _("Override merging options") %></legend>
<%= checkbox_f(f, :merge_overrides, :onchange => 'mergeOverridesChanged(this)',
:disabled => !f.object.supports_merge?, :size => "col-md-8",
:help_block => _("Should the matchers continue to look for matches after first find (only array/hash type). Note: merging overrides ignores all matchers that use Puppet default")) %>
<%= checkbox_f(f, :avoid_duplicates, :disabled => (!f.object.supports_uniq? || !f.object.merge_overrides), :size => "col-md-8",
:help_block => _("Should the matched result avoid duplicate values (only array type).")) %>
</div>
<legend><%= _("Override value for specific hosts") %></legend>
<%= textarea_f f, :path, :rows => :auto, :label => _("Order"), :size => "col-md-8", :value => f.object.path,
:help_block => popover(_("The order in which values are resolved"),
_("The order in which matchers keys are processed, first match wins.<br> You may use multiple attributes as a matcher key, for example, an order of <code>host group, environment</code> would expect a matcher such as <code>hostgroup = \"web servers\", environment = production</code>"), :title => _("The order in which values are resolved")).html_safe
%>
<div class="children_fields lookup_values">
<%= new_child_fields_template(f, :lookup_values, {:partial => "lookup_keys/value", :form_builder_attrs => {:is_param => is_param}}) %>
<%= f.fields_for :lookup_values do |lookup_values| %>
<%= render 'lookup_keys/value', :f => lookup_values, :is_param => is_param %>
<% end %>
<%= add_child_link '+ ' + _("Add Matcher-Value"), :lookup_values, { :title => _('add a new matcher-value pair')} %>
<%= documentation_button('4.2.6SmartMatchers') %>
<%= param_type_selector(f, :onchange => 'keyTypeChange(this)') %>
<%= textarea_f f, :default_value, :value => f.object.default_value_before_type_cast, :size => "col-md-8",
:disabled => (f.object.is_param && (!f.object.override || f.object.use_puppet_default)),
:fullscreen => :true,
:rows => :auto, :help_inline => popover('', _("Value to use when there is no match.")) %>
<%= checkbox_f(f, :use_puppet_default, :label => _('Use Puppet default'), :size => "col-md-8",
:help_inline => use_puppet_default_help,
:onchange => 'toggleUsePuppetDefaultValue(this, "default_value")',
:disabled => (f.object.is_param && !f.object.override)) if is_param %>
</br>
</fieldset>
<fieldset>
<%= collapsing_legend _("Optional input validator"), "#optional_input_validators_#{f.object.id}", "collapsed" %>
<div id="optional_input_validators_<%= f.object.id %>" class="collapse out">
<p class="help-block">
<%= _('Note that if you use ERB as a value of parameter, value will be validated during ENC evaluation. If value is invalid, ENC evaluation will fail.') %>
</p>
<%= checkbox_f(f, :required, :size => "col-md-8", :disabled => !f.object.override,
:help_inline => popover('', _("If checked, will raise an error if there is no default value and no matcher provide a value."))
) if is_param %>
<%= validator_type_selector f %>
<%= text_f f, :validator_rule, :size => "col-md-8", :disabled => f.object.validator_type.blank? %>
</div>
</fieldset>
</br>
<div class="matcher-parent" <%= "id=#{(f.object.key || 'new_lookup_keys').to_s.gsub(' ', '_')}_lookup_key_override_value" %> style=<%= "display:none;" if (f.object.is_param && !f.object.override) %>>
<fieldset>
<legend><%= _("Prioritize attribute order") %></legend>
<h6><%= popover("", _("The order in which matchers keys are processed, first match wins.<br> You may use multiple attributes as a matcher key, for example, an order of <code>host group, environment</code> would expect a matcher such as <code>hostgroup = \"web servers\", environment = production</code>"),
:title => _("The order in which values are resolved.")).html_safe %> <%= _("Set the order in which values are resolved.") %></h6>
<%= textarea_f f, :path, :rows => :auto, :label => _("Order"), :id => 'order', :size => "col-md-8", :onchange => 'fill_in_matchers()', :value => f.object.path %>
<%= checkbox_f(f, :merge_overrides, :onchange => 'mergeOverridesChanged(this)', :table_field => true,
:disabled => !f.object.supports_merge?, :size => "col-md-1", :label_size => "col-md-3",
:help_inline => popover("", _("Continue to look for matches after first find (only array/hash type)? Note: merging overrides ignores all matchers that use Puppet default."))) %>
<%= checkbox_f(f, :avoid_duplicates, :disabled => (!f.object.supports_uniq? || !f.object.merge_overrides),
:size => "col-md-1", :label_size => "col-md-4", :table_field => true,
:help_inline => popover("", _("Avoid duplicate values when merging them (only array type)?"))) %>
</fieldset>
</br>
<fieldset>
<legend><%= _("Specify matchers") %> <%= documentation_button('4.2.6SmartMatchers') %></legend>
<div class="children_fields lookup_values">
<%= render 'lookup_keys/values', :f => f, :is_param => is_param %>
</div>
</fieldset>
</div>
</div>
app/views/lookup_keys/_value.html.erb
<div class="fields">
<%= text_f f, :match, :size => "col-md-8",
:help_block => popover(_("Explain matchers"), _("Matcher is a combination of an attribute and its value, if they match, the value below would be provided.<br> You may use any attribute foreman knows about, such as facts etc for example: <code> domain = example.com </code> or <code> is_virtual = true</code>"),
:title => _("Matcher"))
%>
<%= checkbox_f(f, :use_puppet_default, :label => _('Use Puppet default'), :size => "col-md-8",
:help_block => use_puppet_default_help,
:onchange=>'toggleUsePuppetDefaultValue(this, "value")') if is_param %>
<%= textarea_f f, :value, :rows => :auto, :value => f.object.value_before_type_cast, :size => "col-md-8",
<% if is_template %>
<table>
<% end %>
<tr class="fields matchers form-group <%= 'has-error' if f.object.errors.any? %>">
<td>
<%= f.hidden_field :match, :class => 'match' %>
<%= select_tag '', nil, :class => 'matcher_key' %>
=
<%= text_field_tag '', nil, :class => 'matcher_value' %>
</td>
<td>
<%= f.text_area :value, :rows => line_count(f, :value), :class => 'form-control', :'data-property' => 'value',
:disabled => f.object.use_puppet_default,
:fullscreen => :true,
:help_inline => remove_child_link(icon_text("remove"), f, {:title => _('remove value')}) %>
</div>
:placeholder => _("Value") %>
</td>
<td>
<%= fullscreen_button("$(this).parent().prev().find('textarea')") %>
</td>
<td class="ca">
<%= f.check_box :use_puppet_default, :'data-property' => 'use_puppet_default',
:disabled => !is_param,
:onchange => 'toggleUsePuppetDefaultValue(this, "value")' if is_param %>
</td>
<td>
<span class="help-block">
<%= link_to_remove_fields('', f) %>
</span>
</td>
</tr>
<% if f.object.errors.any? %>
<tr class="has-error">
<td colspan="5">
<span class="help-block">
<%= f.object.errors.full_messages.to_sentence %>
</span>
</td>
</tr>
<% end %>
<% if is_template %>
</table>
<% end %>
app/views/lookup_keys/_values.html.erb
<table class="table table-condensed <%= 'hidden' unless f.object.lookup_values.present? %>">
<thead>
<tr>
<th colspan='2' class='col-md-6'><%= _('Attribute type') %>
<span class="help-inline">
<%= popover("", _("Matcher is a combination of an attribute and its value, if they match, the value below would be provided.<br> You may use any attribute Foreman knows about, such as facts etc for example: <code> domain = example.com </code> or <code> is_virtual = true</code>."),
:title => _("Explain matchers")).html_safe %>
</span></th>
<th class='col-md-4'><%= _('Value') %></th>
<th></th>
<% if is_param %>
<th class='col-md-2'><%= _('Use Puppet default') %>
<span class="help-inline"> <%= use_puppet_default_help() %></span>
</th>
<% end %>
<th></th>
</tr>
</thead>
<tbody>
<%= f.fields_for :lookup_values do |lookup_values| %>
<%= render 'lookup_keys/value', :f => lookup_values, :is_param => is_param, :is_template => false %>
<% end %>
</tbody>
</table>
<%= new_child_fields_template(f, :lookup_values, { :partial => "lookup_keys/value", :form_builder_attrs => { :is_param => is_param, :is_template => true } }) %>
<%= add_child_link '+ ' + _("Add Matcher"), :lookup_values, { :title => _('add a new matcher') } %>
app/views/puppetclasses/_form.html.erb
<div class='input-group'>
<span class="input-group-addon">@</span>
<%= select_tag "environment_filter", options_from_collection_for_select(@puppetclass.environments, "id", "name"),
:include_blank => "All Environments - (Not filtered)",:class=>'form-control', :onchange=>'filterByEnvironment(this)'%>
:prompt => _("All environments - (not filtered)"),:class=>'form-control', :onchange=>'filterByEnvironment(this)'%>
</div>
</div>
<div class='col-md-6 text-right'>
bundler.d/assets.rb
gem 'jquery_pwstrength_bootstrap', '~> 1.2'
gem 'jquery-turbolinks', '~> 2.1'
gem 'select2-rails', '~> 3.5'
gem 'underscore-rails', '~> 1.8'
end
test/functional/api/v2/override_values_controller_test.rb
assert_response :success
end
[{ :value => 'xyz=10'}, { :match => 'string'}].each do |override_value|
[{ :value => 'xyz=10'}, { :match => 'os=string'}].each do |override_value|
test "should not create override value without #{override_value.keys.first}" do
lookup_key = FactoryGirl.create(:lookup_key, :puppetclass => puppetclasses(:two))
refute lookup_key.override
test/unit/lookup_value_test.rb
assert value.valid?
end
test "boolean lookup value should not allow for nil" do
test "boolean lookup value should not allow for nil value" do
#boolean key
key = lookup_keys(:three)
value = LookupValue.new(:value => nil, :match => "hostgroup=Common", :lookup_key_id => key.id)
refute value.valid?
end
test "lookup value should allow valid key" do
key = lookup_keys(:three)
value = LookupValue.new(:value => true, :match => "hostgroup=Common", :lookup_key_id => key.id)
assert_valid value
end
test "lookup value should allow valid multiple key" do
key = lookup_keys(:three)
value = LookupValue.new(:value => true, :match => "hostgroup=Common,domain=example.com", :lookup_key_id => key.id)
assert_valid value
end
test "lookup value should not allow for nil key" do
key = lookup_keys(:three)
value = LookupValue.new(:value => true, :match => "", :lookup_key_id => key.id)
refute_valid value
end
test "lookup value will be rejected for invalid key" do
key = lookup_keys(:three)
value = LookupValue.new(:value => true, :match => "hostgroup=", :lookup_key_id => key.id)
refute_valid value
assert_equal "is invalid", value.errors[:match].first
end
test "lookup value will be rejected for invalid multiple key" do
key = lookup_keys(:three)
value = LookupValue.new(:value => true, :match => "hostgroup=Common,domain=", :lookup_key_id => key.id)
refute_valid value
assert_equal "is invalid", value.errors[:match].first
end
context "when key is a boolean and default_value is a string" do
def setup
@key = FactoryGirl.create(:lookup_key, :as_smart_class_param,

Also available in: Unified diff