commit b09b451583194c3bae7cafc2b5331f1319c4681a
Author: Ohad Levy <ohadlevy@gmail.com>
Date:   Wed Apr 28 17:12:53 2010 +0800

    Fixes #193 - move hosts controller away from AS
    * Remove active scaffold from hosts controller.
    * changed default host graphs from one day for up to one week
    * added dynamic dropdown boxes for host creation / edit page (e.g. for puppet classes, operating systems etc).
    * converted puppet class selection to a template, using it in hostgroups and hosts edit page.
    * Renamed puppetgroups to hostgroups for consistency
    * Added search option to list, this include search by facts as well

diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index d0ec81671..6c61054cf 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -15,7 +15,6 @@ class ApplicationController < ActionController::Base
   def self.active_scaffold_controller_for(klass)
     return FactNamesController if klass == Puppet::Rails::FactName
     return FactValuesController if klass == Puppet::Rails::FactValue
-    return HostsController if klass == Puppet::Rails::Host
     return "#{klass}ScaffoldController".constantize rescue super
   end
 
diff --git a/app/controllers/hosts_controller.rb b/app/controllers/hosts_controller.rb
index 4a7fb4b31..c580f5e99 100644
--- a/app/controllers/hosts_controller.rb
+++ b/app/controllers/hosts_controller.rb
@@ -2,52 +2,17 @@ class HostsController < ApplicationController
   before_filter :require_login, :except => [ :query, :externalNodes, :lookup ]
   before_filter :require_ssl, :except => [ :query, :externalNodes, :lookup ]
   before_filter :find_hosts, :only => :query
-
+  before_filter :ajax_methods, :only => [:environment_selected, :architecture_selected, :os_selected]
   helper :hosts
 
-  active_scaffold :host do |config|
-    list.empty_field_text ='N/A'
-    list.per_page = 15
-    list.sorting = {:name => 'ASC' }
-    config.actions.exclude :show
-    config.list.columns = [:name, :operatingsystem, :environment, :last_report ]
-    config.columns = %w{ name hostgroup puppetclasses environment domain puppetmaster comment host_parameters}
-    config.columns[:hostgroup].form_ui  = :select
-    config.columns[:domain].form_ui  = :select
-    config.columns[:environment].form_ui  = :select
-    config.columns[:puppetclasses].form_ui  = :select
-    config.columns[:puppetclasses].options = { :draggable_lists => {}}
-    config.columns[:fact_values].association.reverse = :host
-    config.nested.add_link("Inventory", [:fact_values])
-    config.columns[:puppetmaster].description = "leave empty if it is #{SETTINGS[:puppet_server] || "puppet"}"
-    # do not show these fields if unattended mode is disabled
-    if SETTINGS[:unattended].nil? or SETTINGS[:unattended]
-      config.columns = %w{ name ip mac hostgroup puppetclasses operatingsystem environment architecture media domain model root_pass serial puppetmaster ptable disk comment host_parameters}
-      config.columns[:architecture].form_ui  = :select
-      config.columns[:operatingsystem].form_ui  = :select
-      config.columns[:operatingsystem].options = { :update_column => :media }
-      config.columns[:media].form_ui  = :select
-      config.columns[:model].form_ui  = :select
-      config.columns[:ptable].form_ui  = :select
-      config.columns[:serial].description = "unsed for now"
-      config.columns[:disk].description = "the disk layout to use"
-      config.columns[:build].form_ui  = :checkbox
-      config.action_links.add 'setBuild', :label => 'Build', :inline => false,
-        :type => :record, :confirm => "This actions recreates all needed settings for host installation, if the host is
-         already running, it will disable certain functions.\n
-         Are you sure you want to reinstall this host?"
-    end
-    config.action_links.add 'rrdreport', :label => 'RRDReport', :inline => true,
-      :type => :record,  :position => :after if SETTINGS[:rrd_report_url]
-    config.action_links.add 'externalNodes', :label => 'YAML', :inline => true,
-      :type => :record, :position => :after
-    config.action_links.add 'puppetrun', :label => 'Run', :inline => true,
-      :type => :record, :position => :after if SETTINGS[:puppetrun]
+  def index
+    @search = Host.search(params[:search])
+    @hosts = @search.paginate :page => params[:page]
   end
 
   def show
     # filter graph time range
-    @range = (params["range"].empty? ? 1 : params["range"].to_i)
+    @range = (params["range"].empty? ? 7 : params["range"].to_i)
     range = @range.days.ago
 
     @host = Host.find params[:id]
@@ -67,6 +32,74 @@ class HostsController < ApplicationController
     @report_summary = Report.summarise(range, @host)
   end
 
+  def new
+    @host = Host.new
+    @host.host_parameters.build
+  end
+
+  def create
+    @host = Host.new(params[:host])
+    if @host.save
+      flash[:foreman_notice] = "Successfully created host."
+      redirect_to @host
+    else
+      render :action => 'new'
+    end
+  end
+
+  def edit
+    @host = Host.find(params[:id])
+    @environment = @host.environment
+    @architecture = @host.architecture
+    @operatingsystem = @host.operatingsystem
+  end
+
+  def update
+    @host = Host.find(params[:id])
+    if @host.update_attributes(params[:host])
+      flash[:foreman_notice] = "Successfully updated host."
+      redirect_to @host
+    else
+      render :action => 'edit'
+    end
+  end
+
+  def destroy
+    @host = Host.find(params[:id])
+    if @host.destroy
+      flash[:foreman_notice] = "Successfully destroyed host."
+    else
+      flash[:foreman_error] = @host.errors.full_messages.join("<br>")
+    end
+    redirect_to hosts_url
+  end
+
+  # form AJAX methods
+
+  def environment_selected
+    if params[:environment_id].to_i > 0 and @environment = Environment.find(params[:environment_id])
+      render :partial => 'puppetclasses/class_selection', :locals => {:obj => (@host ||= Host.new)}
+    else
+      return head(:not_found)
+    end
+  end
+
+  def architecture_selected
+    assign_parameter "architecture"
+  end
+
+  def os_selected
+    assign_parameter "operatingsystem"
+  end
+
+ # list AJAX methods
+  def fact_selected
+    @fact_name_id = params[:search_fact_values_fact_name_id_eq].to_i
+    @fact_values = FactValue.find(:all, :select => 'DISTINCT value', :conditions => {
+      :fact_name_id => @fact_name_id }, :order => 'value ASC') if @fact_name_id > 0
+    render :partial => 'fact_selected', :layout => false
+  end
+
   #returns a yaml file ready to use for puppet external nodes script
   #expected a fqdn parameter to provide hostname to lookup
   #see example script in extras directory
@@ -133,8 +166,8 @@ class HostsController < ApplicationController
 
   def query
     respond_to do |format|
-        format.html
-        format.yml { render :text => @hosts.to_yaml }
+      format.html
+      format.yml { render :text => @hosts.to_yaml }
     end
   end
 
@@ -148,6 +181,7 @@ class HostsController < ApplicationController
     if fact.empty? and klass.empty?
       render :text => '404 Not Found', :status => 404 and return
     end
+
     @hosts = []
     counter = 0
 
@@ -182,4 +216,17 @@ class HostsController < ApplicationController
     render :text => '404 Not Found', :status => 404 and return if @hosts.empty?
   end
 
+  def ajax_methods
+    return head(:method_not_allowed) unless request.xhr?
+    @host = Host.find(params[:id]) unless params[:id].empty?
+  end
+
+  def assign_parameter name
+    if params["#{name}_id"].to_i > 0 and eval("@#{name} = #{name.capitalize}.find(params['#{name}_id'])")
+      render :partial => name, :locals => {:host => (@host ||= Host.new)}
+    else
+      return head(:not_found)
+    end
+  end
+
 end
diff --git a/app/helpers/hosts_helper.rb b/app/helpers/hosts_helper.rb
index 655e4413b..941236e11 100644
--- a/app/helpers/hosts_helper.rb
+++ b/app/helpers/hosts_helper.rb
@@ -6,10 +6,6 @@ module HostsHelper
       link_to_if(Report.maximum('id', :conditions => {:host_id => record.id}), time, report_host_path(record))
   end
 
-  def root_pass_form_column(record, field_name)
-      password_field_tag field_name, record.root_pass
-  end
-
 # method that reformats the hostname column by adding the status icons
   def name_column(record)
     if record.build and not record.installed_at.nil?
@@ -26,27 +22,12 @@ module HostsHelper
       link_to(record.shortname, host_path(record))
   end
 
-  def disk_form_column(record, field_name)
-    text_area_tag field_name, record.disk, :cols => 120, :rows => 10
-  end
-
-  def comment_form_column(record, field_name)
-    text_area_tag field_name, record.comment, :cols => 120, :rows => 10
-  end
-
-  def options_for_association_conditions(association)
-    case association.name
-    when :media
-      {'medias.operatingsystem_id' => @record.operatingsystem_id}
-    when :ptable
-      {'ptables.operatingsystem_id' => @record.operatingsystem_id}
-    else
-      super
-    end
-  end
-
   def days_ago time
     ((Time.now - time) / 1.day).round.to_i
   end
 
+  def searching?
+    params[:search].empty?
+  end
+
 end
diff --git a/app/models/domain.rb b/app/models/domain.rb
index 892848872..949c75d26 100644
--- a/app/models/domain.rb
+++ b/app/models/domain.rb
@@ -12,6 +12,8 @@ class Domain < ActiveRecord::Base
     :allow_blank => true, :allow_nil => true
   validates_presence_of :name
 
+  default_scope :order => 'name'
+
   before_destroy Ensure_not_used_by.new(:hosts, :subnets)
 
   def to_label
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 3f826f518..77ed5764f 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -4,6 +4,7 @@ class Environment < ActiveRecord::Base
   validates_presence_of :name
   validates_uniqueness_of :name
   validates_format_of   :name, :with => /^\S+$/, :message => "Name cannot contain spaces"
+  default_scope :order => 'name'
 
   def to_label
     name
diff --git a/app/models/host.rb b/app/models/host.rb
index fd115ffc5..86f328642 100644
--- a/app/models/host.rb
+++ b/app/models/host.rb
@@ -11,6 +11,7 @@ class Host < Puppet::Rails::Host
   belongs_to :hostgroup
   has_many :reports, :dependent => :destroy
   has_many :host_parameters, :dependent => :destroy
+  accepts_nested_attributes_for :host_parameters, :reject_if => lambda { |a| a[:value].blank? }, :allow_destroy => true
 
   named_scope :recent, lambda { |*args| {:conditions => ["last_report > ?", (args.first || (SETTINGS[:run_interval] + 5.minutes).ago)]} }
   named_scope :out_of_sync, lambda { |*args| {:conditions => ["last_report < ?", (args.first || (SETTINGS[:run_interval] + 5.minutes).ago)]} }
@@ -183,13 +184,12 @@ class Host < Puppet::Rails::Host
 
   # returns the list of puppetclasses a host is in.
   def puppetclasses_names
-    if hostgroup.nil?
-      return puppetclasses.collect {|c| c.name}
-    else
-      return (hostgroup.puppetclasses.collect {|c| c.name} + puppetclasses.collect {|c| c.name}).uniq
-    end
+      return all_puppetclasses.collect {|c| c.name}
   end
 
+  def all_puppetclasses
+     return hostgroup.nil? ? puppetclasses : (hostgroup.puppetclasses + puppetclasses).uniq
+  end
 
   # provide information about each node, mainly used for puppet external nodes
   # TODO: remove hard coded default parameters into some selectable values in the database.
@@ -198,7 +198,7 @@ class Host < Puppet::Rails::Host
     param = {}
     # maybe these should be moved to the common parameters, leaving them in for now
     param["puppetmaster"] = puppetmaster
-    param["domainname"] = domain.fullname unless domain.fullname.empty?
+    param["domainname"] = domain.fullname unless domain.nil? or domain.fullname.nil?
     param.update self.params
     return Hash['classes' => self.puppetclasses_names, 'parameters' => param, 'environment' => environment.to_s]
   end
@@ -208,7 +208,7 @@ class Host < Puppet::Rails::Host
     # read common parameters
     CommonParameter.find_each {|p| parameters.update Hash[p.name => p.value] }
     # read domain parameters
-    domain.domain_parameters.each {|p| parameters.update Hash[p.name => p.value] }
+    domain.domain_parameters.each {|p| parameters.update Hash[p.name => p.value] } unless domain.nil?
     # read group parameters only if a host belongs to a group
     hostgroup.group_parameters.each {|p| parameters.update Hash[p.name => p.value] } unless hostgroup.nil?
     # and now read host parameters, override if required
diff --git a/app/models/hostgroup.rb b/app/models/hostgroup.rb
index 095994c1e..313950882 100644
--- a/app/models/hostgroup.rb
+++ b/app/models/hostgroup.rb
@@ -6,6 +6,7 @@ class Hostgroup < ActiveRecord::Base
   accepts_nested_attributes_for :group_parameters, :reject_if => lambda { |a| a[:value].blank? }, :allow_destroy => true
   has_many :hosts
   before_destroy Ensure_not_used_by.new(:hosts)
+  default_scope :order => 'name'
 
   acts_as_audited
 
@@ -14,4 +15,13 @@ class Hostgroup < ActiveRecord::Base
  def to_s
    name
  end
+
+ def all_puppetclasses
+   puppetclasses
+ end
+
+ def hostgroup
+   self
+ end
+
 end
diff --git a/app/models/model.rb b/app/models/model.rb
index baf478714..6dfa3edbb 100644
--- a/app/models/model.rb
+++ b/app/models/model.rb
@@ -3,6 +3,7 @@ class Model < ActiveRecord::Base
   before_destroy Ensure_not_used_by.new(:hosts)
   validates_uniqueness_of :name
   validates_presence_of :name
+  default_scope :order => 'name'
 
   def to_label
     name
diff --git a/app/views/home/settings.erb b/app/views/home/settings.erb
index 4113906f0..2450791c4 100644
--- a/app/views/home/settings.erb
+++ b/app/views/home/settings.erb
@@ -1,7 +1,7 @@
 <div id="settings">
   <ul>
     <li><%= link_to 'Puppet Classes',     puppetclasses_path %>      </li>
-    <li><%= link_to 'Puppet Groups',      hostgroups_path %>         </li>
+    <li><%= link_to 'Host Groups',        hostgroups_path %>         </li>
     <li><%= link_to 'Domains',            domains_path %>            </li>
     <li><%= link_to 'Operating Systems',  operatingsystems_path %>   </li>
     <li><%= link_to 'Installation Medias',medias_path %>             </li>
diff --git a/app/views/hostgroups/_form.html.erb b/app/views/hostgroups/_form.html.erb
index 1d64e8ccf..0169b5af7 100644
--- a/app/views/hostgroups/_form.html.erb
+++ b/app/views/hostgroups/_form.html.erb
@@ -5,23 +5,8 @@
   <%= f.text_field :name %>
   </p>
 
-  <table>
-    <th>Included services</th>
-    <th colspan=3>Available Services</th>
-    <tr>
-      <td id=hostclasses>
-        <div id=selected_classes>
-          <%# hidden field to ensure that classes gets removed if none are defined -%>
-          <%= hidden_field_tag "hostgroup[puppetclass_ids][]" %>
-          <%= render :partial => "puppetclasses/selectedClasses",
-            :collection => @hostgroup.puppetclasses,:as => :klass,
-            :locals => { :type => Hostgroup } %>
-        </div>
-      </td>
 
-      <%= render :partial => "puppetclasses/classes", :locals => {:puppetclasses => Puppetclass.all - @hostgroup.puppetclasses, :type => Hostgroup } %>
-    </tr>
-  </table>
+  <%= render 'puppetclasses/class_selection', :obj => @hostgroup %>
 
   <%= render :partial => "common_parameters/parameters", :locals => { :f => f, :type => :group_parameters } %>
 
diff --git a/app/views/hosts/_architecture.html.erb b/app/views/hosts/_architecture.html.erb
new file mode 100644
index 000000000..7c8c3247e
--- /dev/null
+++ b/app/views/hosts/_architecture.html.erb
@@ -0,0 +1,11 @@
+<%= label :host, :operatingsystem, "Operating system" %>
+<%= collection_select :host, :operatingsystem_id, @architecture.operatingsystems, :id, :to_label, :include_blank => true %>
+<span id=os_sub_select>
+  <% if @operatingsystem -%>
+    <%= render :partial => 'operatingsystem' %>
+  <% end -%>
+</span>
+<%= observe_field :host_operatingsystem_id,
+    :url => { :action => :os_selected, :id => @host.id },
+    :update => :os_sub_select,
+    :with => :operatingsystem_id %>
diff --git a/app/views/hosts/_fact_selected.html.erb b/app/views/hosts/_fact_selected.html.erb
new file mode 100644
index 000000000..093633481
--- /dev/null
+++ b/app/views/hosts/_fact_selected.html.erb
@@ -0,0 +1,17 @@
+<span id=fact_search>
+  <%= label_tag "Fact Name" %>
+  <%= collection_select :search, :fact_values_fact_name_id_eq,
+    Puppet::Rails::FactName.find(:all, :order => "name ASC"),
+    :id, :name, :selected => @fact_name_id, :include_blank => true
+  %>
+  <% if @fact_values %>
+    <%= label :fact_values_value_eq, "Value" %>
+    <%= collection_select :search, :fact_values_value_eq, @fact_values, :value, :value %>
+  <% end %>
+</span>
+
+<%= observe_field(:search_fact_values_fact_name_id_eq,
+                  :url => { :action => :fact_selected },
+                  :update => :fact_search,
+                  :with => :search_fact_values_fact_name_id_eq) %>
+
diff --git a/app/views/hosts/_form.html.erb b/app/views/hosts/_form.html.erb
new file mode 100644
index 000000000..1be461709
--- /dev/null
+++ b/app/views/hosts/_form.html.erb
@@ -0,0 +1,42 @@
+<% form_for @host do |f| %>
+  <%= f.error_messages %>
+
+  <% field_set_tag 'Primary settings' do -%>
+    <%= f.label :name %>
+    <%= f.text_field :name, :size => 16, :value => @host.shortname %>
+    <%= f.label :hostgroup %>
+    <%= f.collection_select :hostgroup_id, Hostgroup.all, :id, :name, :include_blank => true %>
+    <%= f.label :environment %>
+    <%= f.collection_select :environment_id, Environment.all, :id, :to_label, :include_blank => true %>
+    <%= f.label :puppetmaster %>
+    <%= f.text_field :puppetmaster, :size => 10, :value => @host.puppetmaster %>
+
+   <%= link_to_function "Additional Classes", toggle_div(:classlist) %>
+
+   <div id="classlist", style=display:none;>
+     <% if @environment -%>
+       <%= render 'puppetclasses/class_selection', :obj => @host %>
+     <% else -%>
+       <b>Please select an Environment first</b>
+     <% end -%>
+   </div>
+
+  <% end -%>
+
+  <%= observe_field :host_environment_id,
+                    :url => { :action => :environment_selected, :id => @host.id },
+                    :update => :classlist,
+                    :with => :environment_id %>
+
+  <% if SETTINGS[:unattended].nil? or SETTINGS[:unattended] -%>
+    <%= render 'unattended', :f => f %>
+  <% end -%>
+
+  <% field_set_tag 'Additional Information' do -%>
+    <%= f.text_area :comment, :size => "120x5" %>
+  <% end -%>
+
+  <%= render :partial => "common_parameters/parameters", :locals => {:f => f, :type => :host_parameters} %>
+
+  <p><%= f.submit "Submit" %></p>
+<% end %>
diff --git a/app/views/hosts/_minilist.html.erb b/app/views/hosts/_minilist.html.erb
index 60f470bb6..c297453d5 100644
--- a/app/views/hosts/_minilist.html.erb
+++ b/app/views/hosts/_minilist.html.erb
@@ -1,17 +1,27 @@
-<div>
+<div id="hostlist">
   <% if hosts.size > 0 -%>
     <table class="list">
-      <caption> <%= header -%> </caption>
+      <caption> <%= header ||= "" -%> </caption>
       <tr>
-        <th>Name</th><th>Operatingsystem</th><th>Environment</th><th>Last Report</th><th>Edit</th>
+        <th><%= order @search, :by => :name %></th>
+        <th>Operating system</th>
+        <th>Environment</th>
+        <th>Model</th>
+        <th>Host Group</th>
+        <th><%= order @search, :by => :last_report %></th>
+        <th>Edit</th>
+        <th>Delete</th>
       </tr>
       <% hosts.each do |host| -%>
         <tr class="<%= cycle("even", "odd") -%>">
           <td><%=name_column(host) %></td>
           <td><%=h host.try(:os) %></td>
           <td><%=h host.try(:environment) %></td>
+          <td><%=h host.try(:model) %></td>
+          <td><%=h host.try(:hostgroup) %></td>
           <td><%=last_report_column(host) %></td>
           <td><%= link_to  'Edit', edit_host_url(host)  %></td>
+          <td><%= link_to "Destroy", host, :confirm => 'Are you sure?', :method => :delete %></td>
         <% end -%>
       </tr>
     </table>
diff --git a/app/views/hosts/_operatingsystem.html.erb b/app/views/hosts/_operatingsystem.html.erb
new file mode 100644
index 000000000..c9fa6db7e
--- /dev/null
+++ b/app/views/hosts/_operatingsystem.html.erb
@@ -0,0 +1,4 @@
+<%= label :host, :media %>
+<%= collection_select :host, :media_id, @operatingsystem.medias, :id, :to_label %>
+<%= label :host, :ptable, "Partition Table" %>
+<%= collection_select :host, :ptable_id, @operatingsystem.ptables, :id, :to_label %>
diff --git a/app/views/hosts/_unattended.html.erb b/app/views/hosts/_unattended.html.erb
new file mode 100644
index 000000000..30902f004
--- /dev/null
+++ b/app/views/hosts/_unattended.html.erb
@@ -0,0 +1,40 @@
+<% field_set_tag 'Unattended settings', :id => "unattended" do -%>
+  <% field_set_tag 'Network settings', :id => "network" do -%>
+    <%= f.label :domain %>
+    <%= f.collection_select :domain_id, Domain.all, :id, :to_label %>
+    <%= f.label :ip %>
+    <%= f.text_field :ip, :size => 16 %>
+    <%= f.label :mac %>
+    <%= f.text_field :mac, :size => 17 %>
+  <% end -%>
+
+  <% field_set_tag 'Operating system settings', :id => "operatingsystem" do -%>
+    <p><span id="architecture">
+      <%= f.label :architecture %>
+      <%= f.collection_select :architecture_id, Architecture.all, :id, :to_label, :include_blank => true %>
+      <span id="host_os">
+        <% if @architecture -%>
+          <%= render "architecture" -%>
+        <% end -%>
+      </span>
+    </span></p>
+    <%= f.label :root_pass %>
+    <%= f.password_field :root_pass %>
+    <%= f.label :model %>
+    <%= f.collection_select :model_id, Model.all, :id, :to_label, :include_blank => true %>
+    <%= f.label :serial %>
+    <%= f.select :serial, ["","0,9600n8","0,19200n8","1,9600n8","1,19200n8"] %>
+    <p>
+    <%= link_to_function "Switch to custom disk layout", toggle_div("custom_disk") %>
+    <div id="custom_disk", style=display:none;>
+      <%= f.label :disk, "Custom Disk layout" %>
+      <%= f.text_area :disk, :size => "50x10", :disabled => true %>
+    </div>
+  <% end -%>
+
+<% end -%>
+
+<%= observe_field :host_architecture_id,
+    :url => { :action => :architecture_selected, :id => @host.id },
+    :update => :host_os,
+    :with => :architecture_id %>
diff --git a/app/views/hosts/edit.html.erb b/app/views/hosts/edit.html.erb
new file mode 100644
index 000000000..5fe28775c
--- /dev/null
+++ b/app/views/hosts/edit.html.erb
@@ -0,0 +1,8 @@
+<% title "Edit Host" %>
+
+<%= render :partial => 'form' %>
+
+<p>
+  <%= link_to "Show", @host %> |
+  <%= link_to "View All", hosts_path %>
+</p>
diff --git a/app/views/hosts/index.html.erb b/app/views/hosts/index.html.erb
new file mode 100644
index 000000000..c7107a4e4
--- /dev/null
+++ b/app/views/hosts/index.html.erb
@@ -0,0 +1,18 @@
+<%= link_to_function "Search", toggle_div(:search) %>
+<%= link_to "New Host", new_host_path %>
+
+<div id="search" style="display: <%= searching? ? 'none':'inline' %>;">
+  <% form_for @search do |f|-%>
+    <%= f.label :name_like, "Name" %>
+    <%= f.text_field :name_like, :size => 15 %>
+    <%= f.label :environment_id_equals, "Environment" %>
+    <%= f.collection_select :environment_id_equals, Environment.all, :id, :name, :include_blank => true %>
+    <%= f.label :hostgroup, "Role" %>
+    <%= f.collection_select :hostgroup_id_eq, Hostgroup.all, :id, :name, :include_blank => true %>
+    <%= render :partial => 'fact_selected' %>
+    <p>
+    <%= f.submit "Search" %>
+  <% end %>
+</div>
+
+<%= render 'minilist', :hosts => @hosts, :header => "Hosts" %>
diff --git a/app/views/hosts/new.html.erb b/app/views/hosts/new.html.erb
new file mode 100644
index 000000000..a5cb2a687
--- /dev/null
+++ b/app/views/hosts/new.html.erb
@@ -0,0 +1,5 @@
+<% title "New Host" %>
+
+<%= render :partial => 'form' %>
+
+<p><%= link_to "Back to List", hosts_path %></p>
diff --git a/app/views/hosts/show.html.erb b/app/views/hosts/show.html.erb
index eb4fec632..3ee24ccba 100644
--- a/app/views/hosts/show.html.erb
+++ b/app/views/hosts/show.html.erb
@@ -3,7 +3,7 @@
   <%= link_to "Reports", reports_host_path(@host) %>
   <%= link_to "YAML", :action=>"externalNodes", :id => @host %>
   <%= link_to_if @host.build == false, "Build", {:action=>"setBuild", :id => @host} %>
-  <%= link_to_if SETTINGS[:puppetrun], "Puppet Run", {:action=>"puppetrun", :id => @host} %>
+  <%= link_to_if SETTINGS[:puppetrun], "Run Puppet", {:action=>"puppetrun", :id => @host} %>
   <%= link_to "Delete", host_path(@host), :confirm => 'Are you sure?', :method => :delete%>
 </div>
 
diff --git a/app/views/puppetclasses/_class_selection.html.erb b/app/views/puppetclasses/_class_selection.html.erb
new file mode 100644
index 000000000..0a0471441
--- /dev/null
+++ b/app/views/puppetclasses/_class_selection.html.erb
@@ -0,0 +1,25 @@
+<% field_set_tag 'Puppet classes' do -%>
+  <table>
+    <th>Included classes</th>
+    <th colspan=3>Available classes</th>
+    <tr>
+      <td id=hostclasses>
+        <div id=selected_classes>
+          <%# hidden field to ensure that classes gets removed if none are defined -%>
+          <%= hidden_field_tag obj.class.to_s.downcase + "[puppetclass_ids][]" %>
+          <%= render :partial => "puppetclasses/selectedClasses",
+            :collection => obj.puppetclasses ,:as => :klass,
+            :locals => { :type => obj.class.to_s.downcase } %>
+          </div>
+          <% if (klasses = obj.hostgroup.try(:puppetclasses)).is_a?(Array) and obj.is_a?(Host) -%>
+            <% for klass in klasses -%>
+              <li><%= h klass.name %></li>
+            <% end -%>
+          <% end -%>
+        </td>
+
+        <%= render :partial => "puppetclasses/classes", :locals =>
+        {:puppetclasses => (obj.is_a?(Host) ? @environment.puppetclasses : Puppetclass.all) - obj.all_puppetclasses, :type => obj.class.to_s.downcase } %>
+    </tr>
+  </table>
+<% end -%>
diff --git a/app/views/puppetclasses/_selectedClasses.html.erb b/app/views/puppetclasses/_selectedClasses.html.erb
index c91bd04b0..e51077c87 100644
--- a/app/views/puppetclasses/_selectedClasses.html.erb
+++ b/app/views/puppetclasses/_selectedClasses.html.erb
@@ -1,4 +1,4 @@
 <% content_tag_for :div, klass, :selected do %>
   <li><%= link_to_remove_puppetclass klass %></li>
-  <%= hidden_field_tag "#{type.to_s.downcase}[puppetclass_ids][]", klass.id %>
+  <%= hidden_field_tag "#{type}[puppetclass_ids][]", klass.id %>
 <% end -%>
diff --git a/config/routes.rb b/config/routes.rb
index b9a944505..c3a85a5c7 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -5,9 +5,9 @@ ActionController::Routing::Routes.draw do |map|
     :requirements => { :name => /[^\.][\w\.-]+/ }
   map.connect "/hosts/query", :controller => 'hosts', :action => 'query'
   map.resources :hosts,
-                :member => {:report => :get, :reports => :get, :facts => :get},
-                :collection => { :show_search => :get},
-                :active_scaffold => true
+                :member => {:report => :get, :reports => :get, :facts => :get,
+                  :environment_selected => :post, :architecture_selected => :post, :os_selected => :post},
+                :collection => { :show_search => :get}
   map.dashboard '/dashboard', :controller => 'dashboard'
   map.audit '/audit', :controller => 'audit'
   map.statistics '/statistics', :controller => 'statistics'
diff --git a/test/functional/hosts_controller_test.rb b/test/functional/hosts_controller_test.rb
index 60ec5f2df..63c783e77 100644
--- a/test/functional/hosts_controller_test.rb
+++ b/test/functional/hosts_controller_test.rb
@@ -3,51 +3,57 @@ require 'test_helper'
 class HostsControllerTest < ActionController::TestCase
   setup :initialize_host
 
-  test "should get index" do
+  def test_index
     get :index
-    assert_response :success
+    assert_template 'index'
+  end
+
+  def test_show
+    get :show, :id => Host.first
+    assert_template 'show'
   end
 
-  test "should get new" do
+  def test_new
     get :new
-    assert_response :success
+    assert_template 'new'
   end
 
-  test "should create new host" do
-    assert_difference 'Host.count' do
-      post :create, { :commit => "Create",
-        :record => {:name => "myotherfullhost",
-          :mac => "aabbecddee00",
-          :ip => "123.05.02.25",
-          :domain => {:id => Domain.find_or_create_by_name("othercompany.com").id.to_s},
-          :operatingsystem => {:id => Operatingsystem.first.id.to_s},
-          :architecture => {:id => Architecture.first.id.to_s},
-          :environment => {:id => Environment.first.id.to_s},
-          :disk => "empty partition"
-        }
-      }
-    end
-  end
-
-  test "should get edit" do
-    get :edit, :id => @host.id
-    assert_response :success
+  def test_create_invalid
+    Host.any_instance.stubs(:valid?).returns(false)
+    post :create
+    assert_template 'new'
   end
 
-  test "should update host" do
-    put :update, { :commit => "Update", :id => @host.id, :record => {:disk => "ntfs"} }
-    @host = Host.find_by_id(@host.id)
+  def test_create_valid
+    Host.any_instance.stubs(:valid?).returns(true)
+    post :create, :host => {:name => "test"}
+    assert_redirected_to host_url(assigns(:host))
+  end
+
+  def test_edit
+    get :edit, :id => Host.first
+    assert_template 'edit'
+  end
 
-    assert @host.disk == "ntfs"
+  def test_update_invalid
+    Host.any_instance.stubs(:valid?).returns(false)
+    put :update, :id => Host.first
+    assert_template 'edit'
   end
 
-  test "should destroy host" do
-    assert_difference('Host.count', -1) do
-      delete :destroy, :id => @host.id
-    end
+  def test_update_valid
+    Host.any_instance.stubs(:valid?).returns(true)
+    put :update, :id => Host.first
+    assert_redirected_to host_url(assigns(:host))
   end
 
-  test "externalNodes should render 404 when no params are given" do
+  def test_destroy
+    host = Host.first
+    delete :destroy, :id => host
+    assert_redirected_to hosts_url
+    assert !Host.exists?(host.id)
+  end
+test "externalNodes should render 404 when no params are given" do
     get :externalNodes
     assert_response :missing
     assert_template :text => '404 Not Found'
diff --git a/test/unit/host_test.rb b/test/unit/host_test.rb
index 785bccfd5..fd7d9149c 100644
--- a/test/unit/host_test.rb
+++ b/test/unit/host_test.rb
@@ -40,13 +40,14 @@ class HostTest < ActiveSupport::TestCase
   test "should import facts from yaml of a new host" do
     assert Host.importHostAndFacts(File.read(File.expand_path(File.dirname(__FILE__) + "/facts.yml")))
   end
-
-  test "should not save if both ptable and disk are not defined" do
-    host = Host.create :name => "myfullhost", :mac => "aabbecddeeff", :ip => "123.05.02.03",
-      :domain => Domain.find_or_create_by_name("company.com"), :operatingsystem => Operatingsystem.first,
-      :architecture => Architecture.first, :environment => Environment.first
-    assert true unless SETTINGS[:attended]
-    assert !host.valid?
+  if SETTINGS[:unattended].nil? or SETTINGS[:unattended]
+    test "should not save if both ptable and disk are not defined" do
+      host = Host.create :name => "myfullhost", :mac => "aabbecddeeff", :ip => "123.05.02.03",
+        :domain => Domain.find_or_create_by_name("company.com"), :operatingsystem => Operatingsystem.first,
+        :architecture => Architecture.first, :environment => Environment.first
+      assert true unless SETTINGS[:attended]
+      assert !host.valid?
+    end
   end
 
   test "should save if ptable is defined" do
