Hi,
this is first shoot of selection model with similar behaviour as packagekit. First patch is Mo's rebased patch called "remove template creation on 'new template form' until it is submitted (rev 3)".

Purpose of this patch is only for testing - if you think that this behaviour is not suitable for beta1, stop me, please.

Packages are sorted into "metagroups" (metagroup list is taken from packagekit), packages which are not listed in any metagroup are accessible through search field. "Collections" is list of groups.

Jan
>From 266e9eab248b7d4fde4dffdefefce68506de65ee Mon Sep 17 00:00:00 2001
From: Mohammed Morsi <[email protected]>
Date: Tue, 19 Oct 2010 16:23:47 -0400
Subject: [PATCH aggregator 1/2] BZ #641956: remove template creation on 'new 
template form' until it is submitted (rev 3)

  Currently when a user adds a package to a new template, a temporary
  template gets created for use until the form is submitted. If the user
  cancels the form, the template still exists when it should not. This
  patch simply changes the create template logic to only create the
  template when the form is submitted

  This patch now works both when javascript is enabled and disabled.

  Also took care of some more edge cases since the last revision.
---
 src/app/controllers/templates_controller.rb    |  128 ++++++++++++++----------
 src/app/models/template.rb                     |   18 +--
 src/app/util/image_descriptor_xml.rb           |    4 +
 src/app/views/templates/_hidden_fields.haml    |    8 ++
 src/app/views/templates/_managed_content.haml  |   16 +--
 src/app/views/templates/add_software_form.haml |    1 -
 src/app/views/templates/content_selection.haml |   45 ++++++++
 src/app/views/templates/managed_content.haml   |    1 +
 src/app/views/templates/new.haml               |   68 ++++++++-----
 9 files changed, 186 insertions(+), 103 deletions(-)
 create mode 100644 src/app/views/templates/_hidden_fields.haml
 delete mode 100644 src/app/views/templates/add_software_form.haml
 create mode 100644 src/app/views/templates/content_selection.haml
 create mode 100644 src/app/views/templates/managed_content.haml

diff --git a/src/app/controllers/templates_controller.rb 
b/src/app/controllers/templates_controller.rb
index 8d97c05..9c77c9b 100644
--- a/src/app/controllers/templates_controller.rb
+++ b/src/app/controllers/templates_controller.rb
@@ -38,71 +38,92 @@ class TemplatesController < ApplicationController
     end
   end
 
+  # Since the template form submission can mean multiple things,
+  # we dispatch based on form parameters here
+  def dispatch
+    if params[:save]
+      create
+
+    elsif params[:cancel]
+      redirect_to :action => 'index'
+
+    elsif params[:add_software_form]
+      content_selection
+
+    elsif pkg = params.keys.find { |k| k =~ /^remove_package_(.*)$/ }
+      # actually remove the package from list
+      params[:packages].delete($1) if params[:packages]
+      new
+
+    elsif params[:add_selected]
+      new
+
+    elsif params[:cancel_add_software]
+      params[:packages] = params[:selected_packages]
+      new
+
+    end
+  end
+
+  # FIXME at some point split edit/update out from new/create
+  # to conform to web standards
   def new
     # can't use @template variable - is used by compass (or something other)
-    @tpl = Template.find_or_create(params[:id])
-    @repository_manager = RepositoryManager.new
-    @groups = @repository_manager.all_groups(params[:repository])
+    @id  = params[:id]
+    @tpl = @id.nil? ? Template.new : Template.find(@id)
+    @tpl.attributes = params[:tpl] unless params[:tpl].nil?
+    get_selected_packages(@tpl)
+    render :action => :new
   end
 
   def create
-    if params[:cancel]
-      redirect_to :action => 'index'
-      return
-    end
-
-    @tpl = (params[:tpl] && !params[:tpl][:id].to_s.empty?) ? 
Template.find(params[:tpl][:id]) : Template.new(params[:tpl])
+    @id  = params[:tpl][:id]
+    @tpl = @id.nil? || @id == "" ? Template.new(params[:tpl]) : 
Template.find(@id)
 
-    unless params[:add_software_form] and request.xhr?
-      # this is crazy, but we have most attrs in xml and also in model,
-      # synchronize it at first to xml
-      @tpl.update_xml_attributes!(params[:tpl])
-    end
+    @tpl.xml.clear_packages
 
-    # if remove pkg, we only update xml and render 'new' template
-    # again
-    params.keys.each do |param|
-      if param =~ /^remove_package_(.*)$/
-        update_group_or_package(:remove_package, $1)
-        render :action => 'new'
-        return
-      end
-    end
+    params[:groups].to_a.each   { |group| @tpl.xml.add_group(group) }
+    params[:packages].to_a.each { |pkg|   @tpl.xml.add_package(pkg) }
 
-    if params[:add_software_form]
-      @repository_manager = RepositoryManager.new
-      @groups = 
@repository_manager.all_groups_with_tagged_selected_packages(@tpl.xml.packages, 
params[:repository])
-      render :action => 'add_software_form'
-      return
-    end
+    # this is crazy, but we have most attrs in xml and also in model,
+    # synchronize it at first to xml
+    @tpl.update_xml_attributes(params[:tpl])
 
     if @tpl.save
       flash[:notice] = "Template saved."
       @tpl.set_complete
       redirect_to :action => 'index'
     else
-      @repository_manager = RepositoryManager.new
-      @groups = @repository_manager.all_groups(params[:repository])
+      get_selected_packages(@tpl)
       render :action => 'new'
     end
   end
 
-  def add_software
-    @tpl = params[:template_id].to_s.empty? ? Template.new : 
Template.find(params[:template_id])
+  def content_selection
     @repository_manager = RepositoryManager.new
-    @groups = @repository_manager.all_groups(params[:repository])
-    if params[:add_selected]
-      params[:groups].to_a.each { |group| @tpl.xml.add_group(group) }
-      params[:packages].to_a.each { |pkg| @tpl.xml.add_package(pkg) }
-      @tpl.save_xml!
-    end
-    if params[:ajax]
-      render :partial => 'managed_content'
+    @id  = params[:id] if params[:id]
+    @tpl = @id.nil? ? Template.new : Template.find(@id)
+    @tpl.attributes = params[:tpl] unless params[:tpl].nil?
+    @packages = []
+    @packages = params[:packages].collect{ |p| { :name => p } } if 
params[:packages]
+    @groups = 
@repository_manager.all_groups_with_tagged_selected_packages(@packages, 
params[:repository])
+    @embed  = params[:embed]
+    if @embed
+      render :layout => false
     else
-      render :action => 'new'
+      render :action => :content_selection
     end
   end
 
+  def managed_content
+    @repository_manager = RepositoryManager.new
+    @groups   = @repository_manager.all_groups(params[:repository])
+    @selected_packages = params[:selected_packages] || []
+    @selected_packages.uniq!
+    @selected_groups   = params[:selected_groups]   || []
+    render :layout => false
+  end
+
   def build_form
     raise "select template to build" unless params[:image] and 
params[:image][:template_id]
     @image = Image.new(params[:image])
@@ -162,15 +183,6 @@ class TemplatesController < ApplicationController
 
   private
 
-  def update_group_or_package(method, *args)
-    @repository_manager = RepositoryManager.new
-    @groups = @repository_manager.all_groups(params[:repository])
-    @tpl.xml.send(method, *args)
-    # we save template w/o validation (we can add package before name,... is
-    # set)
-    @tpl.save_xml!
-  end
-
   def check_permission
     require_privilege(Privilege::IMAGE_MODIFY)
   end
@@ -183,4 +195,18 @@ class TemplatesController < ApplicationController
     end
     return ids.first
   end
+
+  def get_selected_packages(tpl)
+    @repository_manager = RepositoryManager.new
+    @groups = @repository_manager.all_groups(params[:repository])
+
+    if params[:packages]
+      @selected_packages = params[:packages]
+      @selected_packages.uniq!
+    else
+      @selected_packages = tpl.xml.packages.collect { |p| p[:name] }
+    end
+
+    @selected_groups   = []
+  end
 end
diff --git a/src/app/models/template.rb b/src/app/models/template.rb
index 6457f7a..2e76a53 100644
--- a/src/app/models/template.rb
+++ b/src/app/models/template.rb
@@ -18,21 +18,15 @@ class Template < ActiveRecord::Base
   validates_presence_of :platform_version
   validates_presence_of :architecture
 
-  def update_xml_attributes!(opts = {})
-    doc = xml
-    doc.name = opts[:name] if opts[:name]
-    doc.platform = opts[:platform] if opts[:platform]
-    doc.description = opts[:summary] if opts[:summary]
-    doc.platform_version = opts[:platform_version] if opts[:platform_version]
-    doc.architecture = opts[:architecture] if opts[:architecture]
-    save_xml!
-  end
-
-  def save_xml!
+  def update_xml_attributes(opts = {})
+    xml.name = opts[:name] if opts[:name]
+    xml.platform = opts[:platform] if opts[:platform]
+    xml.description = opts[:summary] if opts[:summary]
+    xml.platform_version = opts[:platform_version] if opts[:platform_version]
+    xml.architecture = opts[:architecture] if opts[:architecture]
     self[:xml] = xml.to_xml
     @xml = nil
     update_attrs
-    save_without_validation!
   end
 
   def xml
diff --git a/src/app/util/image_descriptor_xml.rb 
b/src/app/util/image_descriptor_xml.rb
index e8fe552..034d943 100644
--- a/src/app/util/image_descriptor_xml.rb
+++ b/src/app/util/image_descriptor_xml.rb
@@ -141,6 +141,10 @@ class ImageDescriptorXML
     end
   end
 
+  def clear_packages
+    @root.xpath('/image/packages').each { |s| s.remove }
+  end
+
   private
 
   def add_group_node(parent, group)
diff --git a/src/app/views/templates/_hidden_fields.haml 
b/src/app/views/templates/_hidden_fields.haml
new file mode 100644
index 0000000..3fcab65
--- /dev/null
+++ b/src/app/views/templates/_hidden_fields.haml
@@ -0,0 +1,8 @@
+- # the template fields as hidden form inputs
+<input type="hidden" id="id" name="id" value="#...@id}" />
+= hidden_field :tpl, :id
+= hidden_field :tpl, :name
+= hidden_field :tpl, :summary
+= hidden_field :tpl, :platform
+= hidden_field :tpl, :platform_version
+= hidden_field :tpl, :architecture
diff --git a/src/app/views/templates/_managed_content.haml 
b/src/app/views/templates/_managed_content.haml
index f6c8f7b..97c4046 100644
--- a/src/app/views/templates/_managed_content.haml
+++ b/src/app/views/templates/_managed_content.haml
@@ -1,26 +1,20 @@
 #selected_packages
-  / we place template id into this partial, because
-  / if we want to add software with ajax, it's possible
-  / that template is not saved in db yet -> in this case
-  / template is saved when software is added and we have
-  / to pass template id back to new form
-  = hidden_field :tpl, :id
   %h3.gap Managed Content to Bundle
   %hr
   %label.header.alpha.prefix_2.grid_7 Name:
   %label.header.omega.grid_2.suffix_5 Repository:
   %label.grid_2.alpha.clear Managed:
   .grid_14.omega
-    - if @tpl.xml.packages.empty?
+    - if @selected_packages.empty?
       No selected packages
     - else
       - repos = @repository_manager.repositories_hash
-      - @tpl.xml.packages.each do |pkg|
-        - pkg_group = @groups.keys.find {|g| @groups[g][:packages][pkg[:name]]}
+      - @selected_packages.each do |pkg|
+        - pkg_group = @groups.keys.find {|g| @groups[g][:packages][pkg]}
         %fieldset.clearfix
-          = text_field_tag 'packages[]', pkg[:name], :disabled => true, :id => 
"selected_package_#{pkg[:name]}", :class => "alpha grid_7 packagename"
+          = text_field_tag 'packages[]', pkg, :id => 
"selected_package_#{pkg}", :class => "alpha grid_7 packagename"
           .grid_2= (pkg_group and repo = 
rep...@groups[pkg_group][:repository_id]]) ? repo.name.to_s : '&nbsp;'
           .grid_5.omega
             %button{:type => 'button', :disabled => 'disabled'} Config
             %button{:type => 'button', :disabled => 'disabled'} Metadata
-            = submit_tag "Remove", :name => "remove_package_#{pkg[:name]}", 
:id => "remove_package_#{pkg[:name]}"
+            = submit_tag "Remove", :name => "remove_package_#{pkg}", :id => 
"remove_package_#{pkg}", :class => 'remove_package'
diff --git a/src/app/views/templates/add_software_form.haml 
b/src/app/views/templates/add_software_form.haml
deleted file mode 100644
index d32729a..0000000
--- a/src/app/views/templates/add_software_form.haml
+++ /dev/null
@@ -1 +0,0 @@
-= render :partial => 'content_selection'
diff --git a/src/app/views/templates/content_selection.haml 
b/src/app/views/templates/content_selection.haml
new file mode 100644
index 0000000..ddd104e
--- /dev/null
+++ b/src/app/views/templates/content_selection.haml
@@ -0,0 +1,45 @@
+.grid_16
+  %h3 Managed Content Selection
+
+  - form_tag :action => "dispatch" do
+    - unless @embed
+      = render :partial => 'hidden_fields'
+    - @packages.each do |p|
+      <input type="hidden" id="selected_package_#{p[:name]}" 
name="selected_packages[]" value="#{p[:name]}" />
+    %fieldset.clearfix
+      .search.grid_4.alpha
+        %input{:type => "search", :placeholder => "Search for package", 
:disabled => "disabled"}
+        %button.action
+      .grid_8
+        %p
+          Repositories to Search:
+          %a (Check all)
+        %fieldset
+          - @repository_manager.repositories.each do |repo|
+            = check_box_tag 'repositories[]', repo.id, true
+            = label_tag 'repositories[]', repo.name
+      %a.grid_4.omega Advanced Search
+
+    %ul.softwaregroups
+      - groups = @groups.keys.sort
+      - unsorted = groups.delete('unsorted')
+      - groups.push('unsorted') if params[:show_unsorted] and unsorted
+      - groups.each do |group|
+        - group_sel = @groups[group][:selected]
+        - group_id = group.gsub(/\s/, '_')
+        %li
+          = check_box_tag 'groups[]', group, group_sel, :id => 
"group_#{group_id}"
+          = label_tag "group_#{group_id}", group
+          %ul{:class => "packages group_#{group_id}"}
+            - pkgs = @groups[group][:packages]
+            - pkgs.keys.sort.each do |pkg|
+              - pkg_sel = pkgs[pkg][:selected] ? true : false
+              - pkg_id = pkg.gsub(/\s/, '_')
+              %li
+                = check_box_tag 'packages[]', pkg, pkg_sel, :id => 
"package_#{pkg_id}"
+                = label_tag "package_#{pkg_id}", pkg
+      = link_to "Show unsorted packages", {:action => 'create', 
:add_software_form => true, :show_unsorted => true, 'tpl[id]' => @tpl.id}, {:id 
=> 'switch_all_link'} unless params[:show_unsorted]
+
+    %fieldset.clearfix
+      = submit_tag "Add Selected", :name => "add_selected", :class => "grid_2 
alpha", :id => "do_add_software"
+      = submit_tag "Cancel", :name => "cancel_add_software", :class => 
"grid_2", :id => "cancel_add_software"
diff --git a/src/app/views/templates/managed_content.haml 
b/src/app/views/templates/managed_content.haml
new file mode 100644
index 0000000..926142d
--- /dev/null
+++ b/src/app/views/templates/managed_content.haml
@@ -0,0 +1 @@
+= render :partial => 'managed_content'
diff --git a/src/app/views/templates/new.haml b/src/app/views/templates/new.haml
index f11c650..fbbe6de 100644
--- a/src/app/views/templates/new.haml
+++ b/src/app/views/templates/new.haml
@@ -1,39 +1,45 @@
 :javascript
   $(document).ready(function() {
-    var $container = $('#package_selection_list'),
-    $submit = $('#add_software_button');
-    $submit.click(function(e, show_all) {
-      var list_url = '#{url_for :action => 'create', :add_software_form => 
true}';
-      var list_all_url = '#{url_for :action => 'create', :add_software_form => 
true, :show_unsorted => true}';
-      var list_data = {'tpl[id]': $("input[name='tpl[id]']").val() || '', 
ajax: true};
+    var $content_container = $('#managed_content');
+    var $sel_pkg_container = $('#package_selection_list');
+    var $submit            = $('#add_software_button');
+    $submit.click(function(e) {
       e.preventDefault();
-      $(this).hide();
-      $container.empty().show().addClass('loading');
-      $container.load(show_all ? list_all_url : list_url, list_data, 
function() {
-        var $groups = $('.softwaregroups .disclosure', this);
-        $container.removeClass('loading');
+      $submit.hide();
+      var data = {
+        'packages[]': $("input[name='packages[]']").map(function() {return 
$(this).val()}).get()
+      }
+      var url = '#{url_for :action => 'content_selection', :embed => true, :id 
=> @id}';
+      $content_container.empty().show();
+      $sel_pkg_container.empty().show().addClass('loading');
+      $sel_pkg_container.load(url, data, function(){
+        $sel_pkg_container.removeClass('loading');;
         $('#do_add_software').click(function(e) {
-          var url = '#{url_for :action => 'add_software', :ajax => true, 
:add_selected => true}';
+          e.preventDefault();
+          var url = '#{url_for :action => 'managed_content'}';
           var data = {
-            'packages[]': $("input:checked[name='packages[]']").map(function() 
{return $(this).val()}).get(),
-            'groups[]': $("input:checked[name='groups[]']").map(function() 
{return $(this).val()}).get(),
-            'template_id': $("input[name='tpl[id]']").val() || ''
+            'selected_packages[]': 
$("input:checked[name='packages[]']").map(function() {return 
$(this).val()}).get(),
+            'selected_groups[]': 
$("input:checked[name='groups[]']").map(function() {return 
$(this).val()}).get(),
+            'template_id'  : '#[email protected]? ? nil : @id}'
           };
-          e.preventDefault();
-          $(this).replaceWith('<span class="loading grid_2 alpha">Adding 
Packages</span>');
-          $('#selected_packages').load(url, data, function() {
-            $container.hide();
+          $content_container.load(url, data, function(){
+            $sel_pkg_container.empty().show();
             $submit.show();
+            $('.remove_package').click(function() { 
$(this).parent().parent().remove(); });
           });
         });
-        $('#switch_all_link').click(function(e) {
-          e.preventDefault();
-          $submit.trigger('click', [true]);
-        });
         $('#cancel_add_software').click(function(e) {
           e.preventDefault();
-          $container.hide();
-          $submit.show();
+          var data = {
+            'selected_packages[]': 
$("input[name='selected_packages[]']").map(function() {return 
$(this).val()}).get(),
+            'selected_groups[]': 
$("input:checked[name='groups[]']").map(function() {return 
$(this).val()}).get(),
+            'template_id'  : '#[email protected]? ? nil : @id}'
+          }
+          var url = '#{url_for :action => 'managed_content'}';
+          $content_container.load(url, data, function(){
+            $sel_pkg_container.empty().show();
+            $submit.show();
+          });
         });
         //select all packages in group
         $(".softwaregroups input[type='checkbox']").click(function() {
@@ -53,12 +59,15 @@
         });
       });
     });
+    $('.remove_package').click(function() { 
$(this).parent().parent().remove(); });
   });
 
 .grid_16
   %h2 Template
-  - form_for @tpl, :url => { :action => "create" } do
-    = error_messages_for 'tpl'
+  - form_for @tpl, :url => { :action => "dispatch" } do
+    <input type="hidden" id="id" name="id" value="#...@id}" />
+    = hidden_field :tpl, :id
+    = error_messages_for :tpl
     = render :partial => 'basics'
 
     %h3.disabled.gap Local Content to Bundle
@@ -84,12 +93,15 @@
         %a{:href => '#'} Remove
         )
 
-    = render :partial => 'managed_content'
+    #managed_content
+      = render :partial => 'managed_content'
     .clearfix
       .grid_14.alpha.prefix_2
         = submit_tag "Add Software", :name => "add_software_form", :id => 
"add_software_button", :class => "iconbutton"
 
+
     #package_selection_list{:style => 'display: none'}
+
     %h3.gap.clear Preboot Configuration
     %hr
     %fieldset.clearfix
-- 
1.7.2.3

From 367ab878e9ce8796fdab30467db4c4be60a91db6 Mon Sep 17 00:00:00 2001
From: Jan Provaznik <[email protected]>
Date: Wed, 20 Oct 2010 10:00:05 +0200
Subject: [PATCH aggregator 2/2] selection model - POC, for trying

---
 src/app/controllers/templates_controller.rb        |   51 ++++++++++++++---
 src/app/util/repository_manager.rb                 |   59 ++++++++++++++++++++
 .../util/repository_manager/comps_repository.rb    |   58 +++++++++++++++++---
 src/app/util/repository_manager/pulp_repository.rb |   25 ++++++++-
 src/app/views/templates/_collections.haml          |    6 ++
 src/app/views/templates/_managed_content.haml      |    4 +-
 src/app/views/templates/_metagroup_packages.haml   |    6 ++
 src/app/views/templates/content_selection.haml     |   47 +++++++++-------
 src/app/views/templates/new.haml                   |    6 +-
 .../image_descriptor_package_metagroups.conf       |   20 +++++++
 10 files changed, 237 insertions(+), 45 deletions(-)
 create mode 100644 src/app/views/templates/_collections.haml
 create mode 100644 src/app/views/templates/_metagroup_packages.haml
 create mode 100644 src/config/image_descriptor_package_metagroups.conf

diff --git a/src/app/controllers/templates_controller.rb 
b/src/app/controllers/templates_controller.rb
index 9c77c9b..dc06046 100644
--- a/src/app/controllers/templates_controller.rb
+++ b/src/app/controllers/templates_controller.rb
@@ -62,6 +62,13 @@ class TemplatesController < ApplicationController
       params[:packages] = params[:selected_packages]
       new
 
+    elsif params.include?('show_metagroup')
+      @metagroup = params['show_metagroup']
+      content_selection
+
+    elsif not params[:package_search].blank?
+      content_selection
+
     end
   end
 
@@ -70,7 +77,7 @@ class TemplatesController < ApplicationController
   def new
     # can't use @template variable - is used by compass (or something other)
     @id  = params[:id]
-    @tpl = @id.nil? ? Template.new : Template.find(@id)
+    @tpl = @id.blank? ? Template.new : Template.find(@id)
     @tpl.attributes = params[:tpl] unless params[:tpl].nil?
     get_selected_packages(@tpl)
     render :action => :new
@@ -78,7 +85,7 @@ class TemplatesController < ApplicationController
 
   def create
     @id  = params[:tpl][:id]
-    @tpl = @id.nil? || @id == "" ? Template.new(params[:tpl]) : 
Template.find(@id)
+    @tpl = @id.blank? ? Template.new(params[:tpl]) : Template.find(@id)
 
     @tpl.xml.clear_packages
 
@@ -101,12 +108,33 @@ class TemplatesController < ApplicationController
 
   def content_selection
     @repository_manager = RepositoryManager.new
-    @id  = params[:id] if params[:id]
-    @tpl = @id.nil? ? Template.new : Template.find(@id)
+    @id  = params[:id]
+    @tpl = @id.blank? ? Template.new : Template.find(@id)
     @tpl.attributes = params[:tpl] unless params[:tpl].nil?
     @packages = []
     @packages = params[:packages].collect{ |p| { :name => p } } if 
params[:packages]
-    @groups = 
@repository_manager.all_groups_with_tagged_selected_packages(@packages, 
params[:repository])
+    @packages += params[:selected_packages].collect{ |p| { :name => p } } 
unless params[:selected_packages].blank?
+    @metagroups = @repository_manager.metagroups
+
+    if not params[:package_search].blank?
+      @metagroup_packages = 
@repository_manager.search_package(params[:package_search], params[:repository])
+    elsif not @metagroup.blank?
+      if @metagroup == 'Collections'
+        # TODO: if we remember selected groups, this could be done much more 
simply
+        @collections = 
@repository_manager.all_groups_with_tagged_selected_packages(@packages, 
params[:repository])
+      else
+        @metagroup_packages = 
@repository_manager.metagroup_packages(@metagroup, params[:repository])
+      end
+    end
+    if request.xhr?
+      if @metagroup_packages
+        render :partial => 'metagroup_packages' and return
+      end
+      if @collections
+        render :partial => 'collections' and return
+      end
+    end
+
     @embed  = params[:embed]
     if @embed
       render :layout => false
@@ -118,9 +146,12 @@ class TemplatesController < ApplicationController
   def managed_content
     @repository_manager = RepositoryManager.new
     @groups   = @repository_manager.all_groups(params[:repository])
-    @selected_packages = params[:selected_packages] || []
-    @selected_packages.uniq!
-    @selected_groups   = params[:selected_groups]   || []
+    @selected_packages = params[:selected_packages].to_a.uniq
+    @selected_groups   = params[:selected_groups].to_a
+    @selected_groups.each do |g|
+      next unless @groups[g]
+      @selected_packages += @groups[g][:packages].keys
+    end
     render :layout => false
   end
 
@@ -200,8 +231,8 @@ class TemplatesController < ApplicationController
     @repository_manager = RepositoryManager.new
     @groups = @repository_manager.all_groups(params[:repository])
 
-    if params[:packages]
-      @selected_packages = params[:packages]
+    if params[:packages] or params[:selected_packages]
+      @selected_packages = params[:packages].to_a + 
params[:selected_packages].to_a
       @selected_packages.uniq!
     else
       @selected_packages = tpl.xml.packages.collect { |p| p[:name] }
diff --git a/src/app/util/repository_manager.rb 
b/src/app/util/repository_manager.rb
index fbe7a8b..64de179 100644
--- a/src/app/util/repository_manager.rb
+++ b/src/app/util/repository_manager.rb
@@ -47,6 +47,61 @@ class RepositoryManager
     return @all_groups
   end
 
+  def packages(repository = nil)
+  end
+
+  def categories(repository = nil)
+    unless @all_categories
+      @all_categories = {}
+      repositories.each do |r|
+        next if repository and repository != 'all' and repository != r.id
+        r.categories.each do |id, data|
+          if @all_categories[id]
+            @all_categories[id][:groups] += data[:groups]
+          else
+            @all_categories[id] = data
+          end
+        end
+      end
+    end
+    return @all_categories
+  end
+
+  def packages(repository = nil)
+    unless @packages
+      @packages = []
+      repositories.each do |r|
+        next if repository and repository != 'all' and repository != r.id
+        @packages += r.packages
+      end
+    end
+    return @packages
+  end
+
+  # TODO: this is temporary solution for categorizing packages
+  def metagroups
+    unless @metagroups
+      @metagroups = {}
+      File.readlines('config/image_descriptor_package_metagroups.conf').each 
do |line|
+        group, entries_str = line.chomp.split('=')
+        next unless group and entries_str
+        @metagroups[group] = entries_str.split(',')
+      end
+    end
+    @metagroups
+  end
+
+  def metagroup_packages(category, repository = nil)
+    res = []
+    groups = all_groups(repository)
+    metagroups[category].to_a.each do |entry|
+      cat, group = entry.split(';')
+      next unless c = categories[cat] and c[:groups].include?(group) and 
groups[group]
+      res += groups[group][:packages].keys
+    end
+    res.sort.uniq
+  end
+
   def all_groups_with_tagged_selected_packages(pkgs, repository = nil)
     groups = all_groups(repository)
     groups.each_value do |group|
@@ -67,6 +122,10 @@ class RepositoryManager
     res
   end
 
+  def search_package(str, repository = nil)
+    packages.select {|p| p =~ /#{Regexp.escape(str)}/i}
+  end
+
   private
 
   # returns true if all non-optional packages are selected
diff --git a/src/app/util/repository_manager/comps_repository.rb 
b/src/app/util/repository_manager/comps_repository.rb
index 80391ef..86c4da7 100644
--- a/src/app/util/repository_manager/comps_repository.rb
+++ b/src/app/util/repository_manager/comps_repository.rb
@@ -11,11 +11,19 @@ class CompsRepository < AbstractRepository
   end
 
   def groups
-    begin
-      @groups ||= Marshal.load(File.open(@cache_file, 'r'))
-    rescue Errno::ENOENT
-      raise "failed to read cached packages info, run 'rake dc:prepare_repos'"
+    @groups ||= load_data[:groups]
+  end
+
+  def categories
+    @categories ||= load_data[:categories]
+  end
+
+  def packages
+    pkgs = []
+    groups.each_value do |g|
+      pkgs += g[:packages].keys
     end
+    pkgs.uniq
   end
 
   def prepare_repo
@@ -25,7 +33,8 @@ class CompsRepository < AbstractRepository
       pkgs = group_packages(g)
       next if pkgs.empty?
       name = g.at_xpath('name').text
-      grps[name] = {
+      id = g.at_xpath('id').text
+      grps[id] = {
         :name => name,
         :repository_id => @id,
         :packages => pkgs
@@ -45,18 +54,51 @@ class CompsRepository < AbstractRepository
       }
     end
 
+    categories = {}
+    category_nodes.each do |cat|
+      id = cat.at_xpath('id').text
+      categories[id] = {
+        :name => cat.at_xpath('name').text,
+        :groups => cat.xpath('./grouplist/groupid').map {|g| g.text}
+      }
+    end
+
     Dir.mkdir(@cache_dir) unless File.directory?(@cache_dir)
-    Marshal.dump(grps, File.open(@cache_file, 'w'))
+    Marshal.dump({:groups => grps,
+                  :categories => categories}, File.open(@cache_file, 'w'))
   end
 
   private
 
+  def load_data
+    unless @load_data
+      begin
+        @load_data = Marshal.load(File.open(@cache_file, 'r'))
+      rescue Errno::ENOENT
+        raise "failed to read cached packages info, run 'rake 
dc:prepare_repos'"
+      end
+    end
+    @load_data
+  end
+
+  def parsed_group_xml
+    unless @xml
+      return nil unless data = group_xml
+      @xml = Nokogiri::XML(data)
+    end
+    @xml
+  end
+
   def group_nodes
-    return [] unless data = group_xml
-    xml = Nokogiri::XML(data)
+    return [] unless xml = parsed_group_xml
     xml.xpath('/comps/group')
   end
 
+  def category_nodes
+    return [] unless xml = parsed_group_xml
+    xml.xpath('/comps/category')
+  end
+
   def pkg_names
     pkgs = {}
     xml = Nokogiri::XML(primary_xml)
diff --git a/src/app/util/repository_manager/pulp_repository.rb 
b/src/app/util/repository_manager/pulp_repository.rb
index a0ea005..051bc47 100644
--- a/src/app/util/repository_manager/pulp_repository.rb
+++ b/src/app/util/repository_manager/pulp_repository.rb
@@ -30,6 +30,8 @@ class PulpRepository < AbstractRepository
   def initialize(conf)
     super
     @groups_url = File.join(strip_path(@baseurl), conf['packagegroups'])
+    @categories_url = File.join(strip_path(@baseurl), 
conf['packagegroupcategories'])
+    @packages_url = File.join(strip_path(@baseurl), conf['packages'])
   end
 
   def groups
@@ -41,8 +43,8 @@ class PulpRepository < AbstractRepository
       info['mandatory_package_names'].each {|p| pkgs[p] = {:type => 
'mandatory'}}
       next if pkgs.empty?
       name = info['name']
-      groups[name] = {
-        :name => name,
+      groups[info['id']] = {
+        :name => info['name'],
         :description => info['description'].to_s,
         :repository_id => @id,
         :packages => pkgs,
@@ -51,6 +53,25 @@ class PulpRepository < AbstractRepository
     return groups
   end
 
+  def categories
+    categories = {}
+    WrappedRestClient.get(@categories_url, HTTP_OPTS).each do |id, info|
+      categories[info['id']] = {
+        :name => info['name'],
+        :groups => info['packagegroupids'],
+      }
+    end
+    return categories
+  end
+
+  def packages
+    packages = []
+    WrappedRestClient.get(@packages_url, HTTP_OPTS).each do |info|
+      packages << info['name']
+    end
+    return packages
+  end
+
   private
 
   def strip_path(url)
diff --git a/src/app/views/templates/_collections.haml 
b/src/app/views/templates/_collections.haml
new file mode 100644
index 0000000..1b4604b
--- /dev/null
+++ b/src/app/views/templates/_collections.haml
@@ -0,0 +1,6 @@
+%ul.metagrouppackages
+  - @collections.keys.sort.each do |id|
+    %li
+      - selected = @collections[id][:selected] ? true : false
+      = check_box_tag 'groups[]', id, selected, {:disabled => selected, :id => 
"group_#{id}"}
+      = label_tag "group_#{id}", @collections[id][:name]
diff --git a/src/app/views/templates/_managed_content.haml 
b/src/app/views/templates/_managed_content.haml
index 97c4046..0657ff1 100644
--- a/src/app/views/templates/_managed_content.haml
+++ b/src/app/views/templates/_managed_content.haml
@@ -9,10 +9,10 @@
       No selected packages
     - else
       - repos = @repository_manager.repositories_hash
-      - @selected_packages.each do |pkg|
+      - @selected_packages.sort.each do |pkg|
         - pkg_group = @groups.keys.find {|g| @groups[g][:packages][pkg]}
         %fieldset.clearfix
-          = text_field_tag 'packages[]', pkg, :id => 
"selected_package_#{pkg}", :class => "alpha grid_7 packagename"
+          = text_field_tag 'packages[]', pkg, :id => "package_#{pkg}", :class 
=> "alpha grid_7 packagename"
           .grid_2= (pkg_group and repo = 
rep...@groups[pkg_group][:repository_id]]) ? repo.name.to_s : '&nbsp;'
           .grid_5.omega
             %button{:type => 'button', :disabled => 'disabled'} Config
diff --git a/src/app/views/templates/_metagroup_packages.haml 
b/src/app/views/templates/_metagroup_packages.haml
new file mode 100644
index 0000000..c8fc367
--- /dev/null
+++ b/src/app/views/templates/_metagroup_packages.haml
@@ -0,0 +1,6 @@
+%ul.metagrouppackages
+  - @metagroup_packages.sort.each do |pkg|
+    %li
+      - selected = @selected_packages.to_a.include?(pkg)
+      = check_box_tag 'packages[]', pkg, selected, {:disabled => selected, :id 
=> "package_#{pkg}"}
+      = label_tag "package_#{pkg}", pkg
diff --git a/src/app/views/templates/content_selection.haml 
b/src/app/views/templates/content_selection.haml
index ddd104e..234366d 100644
--- a/src/app/views/templates/content_selection.haml
+++ b/src/app/views/templates/content_selection.haml
@@ -1,3 +1,14 @@
+:javascript
+  $(document).ready(function() {
+    var $metagrouppackages = $('#metagrouppackages');
+    $('input.metagroup').click(function(e) {
+      e.preventDefault();
+      var data = { 'show_metagroup': e.currentTarget.value };
+      var url = '#{url_for :action => 'dispatch', :id => @id}';
+      $metagrouppackages.load(url, data);
+    });
+  });
+
 .grid_16
   %h3 Managed Content Selection
 
@@ -5,10 +16,10 @@
     - unless @embed
       = render :partial => 'hidden_fields'
     - @packages.each do |p|
-      <input type="hidden" id="selected_package_#{p[:name]}" 
name="selected_packages[]" value="#{p[:name]}" />
+      %input{:type => 'hidden', :id => "selected_package_#{p[:name]}", :name 
=> 'selected_packages[]', :value => "#{p[:name]}"}
     %fieldset.clearfix
       .search.grid_4.alpha
-        %input{:type => "search", :placeholder => "Search for package", 
:disabled => "disabled"}
+        %input{:type => "search", :placeholder => "Search for package", :name 
=> 'package_search'}
         %button.action
       .grid_8
         %p
@@ -19,26 +30,20 @@
             = check_box_tag 'repositories[]', repo.id, true
             = label_tag 'repositories[]', repo.name
       %a.grid_4.omega Advanced Search
-
-    %ul.softwaregroups
-      - groups = @groups.keys.sort
-      - unsorted = groups.delete('unsorted')
-      - groups.push('unsorted') if params[:show_unsorted] and unsorted
-      - groups.each do |group|
-        - group_sel = @groups[group][:selected]
-        - group_id = group.gsub(/\s/, '_')
+    %ul.metagroups{:style => 'float:left'}
+      %li
+        = submit_tag 'Collections', :name => 'show_metagroup', :class => 
'metagroup'
+      - @metagroups.keys.sort.each do |cat|
         %li
-          = check_box_tag 'groups[]', group, group_sel, :id => 
"group_#{group_id}"
-          = label_tag "group_#{group_id}", group
-          %ul{:class => "packages group_#{group_id}"}
-            - pkgs = @groups[group][:packages]
-            - pkgs.keys.sort.each do |pkg|
-              - pkg_sel = pkgs[pkg][:selected] ? true : false
-              - pkg_id = pkg.gsub(/\s/, '_')
-              %li
-                = check_box_tag 'packages[]', pkg, pkg_sel, :id => 
"package_#{pkg_id}"
-                = label_tag "package_#{pkg_id}", pkg
-      = link_to "Show unsorted packages", {:action => 'create', 
:add_software_form => true, :show_unsorted => true, 'tpl[id]' => @tpl.id}, {:id 
=> 'switch_all_link'} unless params[:show_unsorted]
+          = submit_tag cat, :name => "show_metagroup", :class => 'metagroup'
+    #metagrouppackages
+      - if @collections
+        = render :partial => 'collections'
+      - else
+        - if @metagroup_packages.blank?
+          No group selected
+        - else
+          = render :partial => 'metagroup_packages'
 
     %fieldset.clearfix
       = submit_tag "Add Selected", :name => "add_selected", :class => "grid_2 
alpha", :id => "do_add_software"
diff --git a/src/app/views/templates/new.haml b/src/app/views/templates/new.haml
index fbbe6de..916ca0b 100644
--- a/src/app/views/templates/new.haml
+++ b/src/app/views/templates/new.haml
@@ -10,15 +10,17 @@
         'packages[]': $("input[name='packages[]']").map(function() {return 
$(this).val()}).get()
       }
       var url = '#{url_for :action => 'content_selection', :embed => true, :id 
=> @id}';
-      $content_container.empty().show();
+      //$content_container.empty().show();
       $sel_pkg_container.empty().show().addClass('loading');
       $sel_pkg_container.load(url, data, function(){
         $sel_pkg_container.removeClass('loading');;
         $('#do_add_software').click(function(e) {
           e.preventDefault();
           var url = '#{url_for :action => 'managed_content'}';
+          var all_pkgs = $("input:checked[name='packages[]']").map(function() 
{return $(this).val()}).get();
+          all_pkgs = 
all_pkgs.concat($("input:hidden[name='selected_packages[]']").map(function() 
{return $(this).val()}).get());
           var data = {
-            'selected_packages[]': 
$("input:checked[name='packages[]']").map(function() {return 
$(this).val()}).get(),
+            'selected_packages[]': all_pkgs,
             'selected_groups[]': 
$("input:checked[name='groups[]']").map(function() {return 
$(this).val()}).get(),
             'template_id'  : '#[email protected]? ? nil : @id}'
           };
diff --git a/src/config/image_descriptor_package_metagroups.conf 
b/src/config/image_descriptor_package_metagroups.conf
new file mode 100644
index 0000000..7218e5c
--- /dev/null
+++ b/src/config/image_descriptor_package_metagroups.conf
@@ -0,0 +1,20 @@
+admin-tools=base-system;system-tools,base-system;admin-tools,rpmfusion_free;hardware-support
+desktop-gnome=rpmfusion_free;gnome-desktop,desktops;gnome-desktop
+desktop-kde=desktops;kde-desktop,rpmfusion_free;kde-desktop
+desktop-xfce=desktops;xfce-desktop
+desktop-other=desktops;window-managers,desktops;sugar-desktop,desktops;lxde-desktop,desktops;moblin-desktop
+education=apps;education,development;electronic-lab
+fonts=base-system;fonts,base-system;legacy-fonts
+games=apps;games,rpmfusion_free;games,rpmfusion_nonfree;games
+graphics=apps;graphics
+internet=apps;text-internet,apps;graphical-internet,rpmfusion_free;internet
+legacy=base-system;legacy-software-support
+localization=base-system;input-methods,language-support;khmer-support,language-support;persian-support,language-support;georgian-support,language-support;malay-support,language-support;tonga-support,language-support;portuguese-support,language-support;japanese-support,language-support;hungarian-support,language-support;somali-support,language-support;punjabi-support,language-support;bhutanese-support,language-support;british-support,language-support;korean-support,language-support;lao-support,language-support;inuktitut-support,language-support;german-support,language-support;hindi-support,language-support;faeroese-support,language-support;swedish-support,language-support;tsonga-support,language-support;russian-support,language-support;serbian-support,language-support;latvian-support,language-support;samoan-support,language-support;sinhala-support,language-support;catalan-support,language-support;lithuanian-support,language-support;turkish-support,language-support;arabic-support,language-support;vietnamese-support,language-support;mongolian-support,language-support;tswana-support,language-support;irish-support,language-support;italian-support,language-support;slovak-support,language-support;slovenian-support,language-support;belarusian-support,language-support;northern-sotho-support,language-support;kannada-support,language-support;malayalam-support,language-support;swati-support,language-support;breton-support,language-support;romanian-support,language-support;greek-support,language-support;tagalog-support,language-support;zulu-support,language-support;tibetan-support,language-support;danish-support,language-support;afrikaans-support,language-support;southern-sotho-support,language-support;bosnian-support,language-support;brazilian-support,language-support;basque-support,language-support;welsh-support,language-support;thai-support,language-support;telugu-support,language-support;low-saxon-support,language-support;urdu-support,language-support;tamil-support,language-support;indonesian-support,language-support;gujarati-support,language-support;xhosa-support,language-support;chinese-support,language-support;czech-support,language-support;venda-support,language-support;bulgarian-support,language-support;albanian-support,language-support;galician-support,language-support;armenian-support,language-support;dutch-support,language-support;oriya-support,language-support;maori-support,language-support;nepali-support,language-support;icelandic-support,language-support;ukrainian-support,language-support;assamese-support,language-support;bengali-support,language-support;spanish-support,language-support;hebrew-support,language-support;estonian-support,language-support;french-support,language-support;croatian-support,language-support;filipino-support,language-support;finnish-support,language-support;norwegian-support,language-support;southern-ndebele-support,language-support;polish-support,language-support;gaelic-support,language-support;marathi-support,language-support;ethiopic-support,language-support;esperanto-support,language-support;northern-sami-support,language-support;macedonian-support,language-support;walloon-support,language-support;kashubian-support,language-support;kashmiri-support,language-support;konkani-support,language-support;tajik-support,language-support;sindhi-support,language-support;uzbek-support,language-support;burmese-support,language-support;maithili-support,language-support;kazakh-support,language-support;azerbaijani-support,language-support;fijian-support,language-support;tetum-support,language-support;upper-sorbian-support,language-support;sanskrit-support,language-support;sardinian-support,language-support;swahili-support,language-support;maltese-support,language-support;friulian-support,language-support;latin-support,language-support;interlingua-support,language-support;occitan-support,language-support;hiligaynon-support,language-support;malagasy-support,language-support;amazigh-support,language-support;manx-support,language-support;turkmen-support,language-support;kinyarwanda-support,language-support;kurdish-support,language-support;coptic-support,language-support;luxembourgish-support,language-support;frisian-support,language-support;chichewa-support
+multimedia=apps;sound-and-video,rpmfusion_free;sound-and-video
+office=apps;office,apps;editors
+other=apps;engineering-and-scientific,rpmfusion_free;misc-libs,rpmfusion_nonfree;emulators
+programming=development;haskell,development;kde-software-development,development;gnome-software-development,development;development-tools,development;eclipse,development;development-libs,development;x-software-development,development;web-development,development;legacy-software-development,development;ruby,development;java-development,development;xfce-software-development,development;fedora-packager,development;mingw32,development;ocaml;development;perl;development;books
+publishing=apps;authoring-and-publishing,apps;font-design
+servers=servers;clustering,servers;dns-server,servers;server-cfg,servers;news-server,servers;web-server,servers;smb-server,servers;sql-server,servers;ftp-server,servers;printing,servers;mysql,servers;mail-server,servers;network-server,servers;legacy-network-server;servers;directory-server
+system=base-system;java,base-system;base-x,base-system;hardware-support,base-system;dial-up,base-system;base,rpmfusion_free;base,rpmfusion_free;system-tools,rpmfusion_nonfree;hardware-support,rpmfusion_nonfree;misc-tools,rpmfusion_nonfree;base
+virtualization=base-system;virtualization
-- 
1.7.2.3

_______________________________________________
deltacloud-devel mailing list
[email protected]
https://fedorahosted.org/mailman/listinfo/deltacloud-devel

Reply via email to