foreman_content 0.3 → 0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -2
- data/app/assets/javascripts/content/content.js +23 -12
- data/app/assets/javascripts/content/repository.js +19 -0
- data/app/controllers/content/content_views_controller.rb +19 -6
- data/app/controllers/content/products_controller.rb +1 -1
- data/app/controllers/content/repositories_controller.rb +9 -3
- data/app/helpers/content/content_views_helper.rb +24 -4
- data/app/helpers/content/repositories_helper.rb +28 -0
- data/app/models/concerns/content/host_extensions.rb +1 -1
- data/app/models/concerns/content/operatingsystem_extensions.rb +1 -1
- data/app/models/concerns/content/orchestration/pulp.rb +1 -1
- data/app/models/concerns/content/orchestration/pulp/sync.rb +37 -2
- data/app/models/concerns/content/redhat_extensions.rb +19 -3
- data/app/models/concerns/content/repository_common.rb +13 -0
- data/app/models/content/content_view.rb +21 -15
- data/app/models/content/content_view_repository_clone.rb +2 -2
- data/app/models/content/product.rb +1 -1
- data/app/models/content/repository.rb +24 -8
- data/app/models/content/repository/operating_system.rb +11 -2
- data/app/models/content/repository/product.rb +8 -3
- data/app/models/content/repository_clone.rb +5 -10
- data/app/services/content/content_view_factory.rb +46 -25
- data/app/services/content/pulp/repository.rb +13 -0
- data/app/services/content/pulp/repository_sync_history.rb +8 -2
- data/app/views/content/content_views/{_step2.html.erb → _composite.html.erb} +1 -1
- data/app/views/content/content_views/_form.html.erb +17 -14
- data/app/views/content/content_views/index.html.erb +4 -3
- data/app/views/content/content_views/new.html.erb +1 -7
- data/app/views/content/content_views/show.erb +22 -0
- data/app/views/content/products/_form.html.erb +3 -9
- data/app/views/content/repositories/_fields.html.erb +9 -0
- data/app/views/content/repositories/_os_form.html.erb +6 -11
- data/app/views/content/repositories/_product_form.html.erb +7 -11
- data/app/views/content/repositories/_repository.html.erb +4 -0
- data/app/views/content/repositories/_sync_schedule.erb +10 -0
- data/app/views/content/repositories/edit.html.erb +2 -0
- data/app/views/content/repositories/index.html.erb +14 -16
- data/app/views/content/repositories/new.html.erb +2 -0
- data/app/views/content/repositories/show.html.erb +51 -37
- data/db/migrate/20130807121629_create_content_content_views.rb +1 -1
- data/db/migrate/20130828173006_change_content_view_index_name.rb +10 -0
- data/db/migrate/20130901070945_add_publish_boolean_to_content_repository.rb +6 -0
- data/db/migrate/20130902130445_change_repository_to_polymorphic.rb +42 -0
- data/db/migrate/20130902142326_add_polymorphic_repo_to_content_view_repository_clone.rb +8 -0
- data/lib/content/version.rb +1 -1
- metadata +14 -18
@@ -1,5 +1,5 @@
|
|
1
1
|
class Content::ContentViewRepositoryClone < ActiveRecord::Base
|
2
2
|
belongs_to :content_view
|
3
|
-
belongs_to :
|
4
|
-
validate :content_view_id, :
|
3
|
+
belongs_to :repository, :polymorphic => true
|
4
|
+
validate :content_view_id, :repository_id, :repository_type, :presence => true
|
5
5
|
end
|
@@ -2,24 +2,29 @@ module Content
|
|
2
2
|
class Repository < ActiveRecord::Base
|
3
3
|
include Content::Orchestration::Pulp::Sync
|
4
4
|
include Foreman::STI
|
5
|
+
include Content::RepositoryCommon
|
5
6
|
|
6
7
|
YUM_TYPE = 'yum'
|
7
8
|
KICKSTART_TYPE = 'kickstart'
|
8
9
|
FILE_TYPE = 'iso'
|
9
10
|
TYPES = [YUM_TYPE, KICKSTART_TYPE, FILE_TYPE]
|
10
11
|
|
11
|
-
belongs_to :
|
12
|
-
belongs_to :operatingsystem
|
12
|
+
belongs_to :originator, :polymorphic => true
|
13
13
|
belongs_to :gpg_key
|
14
14
|
belongs_to :architecture
|
15
15
|
has_many :repository_clones
|
16
|
+
has_many :content_view_repository_clones, :dependent => :destroy, :as => :repository
|
16
17
|
|
18
|
+
before_validation :set_originator_type
|
17
19
|
before_destroy EnsureNotUsedBy.new(:repository_clones)
|
18
20
|
|
19
21
|
validates_presence_of :type # can't create this object, only child
|
20
22
|
|
23
|
+
validates_presence_of :originator_id, :originator_type
|
24
|
+
|
21
25
|
validates :name, :presence => true
|
22
|
-
validates_uniqueness_of :name, :scope => [:
|
26
|
+
validates_uniqueness_of :name, :scope => [:originator_type, :originator_id]
|
27
|
+
validates :feed, :presence => true, :uniqueness => true
|
23
28
|
validates_inclusion_of :content_type,
|
24
29
|
:in => TYPES,
|
25
30
|
:allow_blank => false,
|
@@ -27,19 +32,30 @@ module Content
|
|
27
32
|
|
28
33
|
scoped_search :on => [:name, :enabled], :complete_value => :true
|
29
34
|
scoped_search :in => :architecture, :on => :name, :rename => :architecture, :complete_value => :true
|
30
|
-
|
31
|
-
|
35
|
+
belongs_to :search_operatingsystems, :class_name => 'Operatingsystem', :foreign_key => :originator_id,
|
36
|
+
:conditions => '"content_repositories"."originator_type" = "Operatingsystem"'
|
37
|
+
belongs_to :search_products, :class_name => 'Content::Product', :foreign_key => :originator_id,
|
38
|
+
:conditions => '"content_repositories"."originator_type" = "Content::Product"'
|
39
|
+
scoped_search :in => :search_products, :on => :name, :rename => :product,
|
40
|
+
:complete_value => :true, :only_explicit => true
|
41
|
+
scoped_search :in => :search_operatingsystems, :on => :name, :rename => :operatingsystem,
|
42
|
+
:complete_value => :true, :only_explicit => true
|
32
43
|
|
33
44
|
scope :kickstart, where(:content_type => KICKSTART_TYPE)
|
34
45
|
scope :yum, where(:content_type => YUM_TYPE)
|
35
46
|
|
47
|
+
scope :for_content_views, lambda { |ids|
|
48
|
+
joins(:content_view_repository_clones).
|
49
|
+
where('content_content_view_repository_clones' => {:content_view_id => ids})
|
50
|
+
}
|
51
|
+
|
36
52
|
def content_types
|
37
53
|
TYPES
|
38
54
|
end
|
39
55
|
|
40
56
|
# The label is used as a repository label in a yum repo file.
|
41
57
|
def to_label
|
42
|
-
"#{entity_name}
|
58
|
+
"#{entity_name}/#{name}"
|
43
59
|
end
|
44
60
|
|
45
61
|
#inhariters are expected to override this method
|
@@ -47,11 +63,11 @@ module Content
|
|
47
63
|
''
|
48
64
|
end
|
49
65
|
|
50
|
-
def
|
66
|
+
def clone content_view
|
51
67
|
repository_clones.create!(
|
52
68
|
:content_views => [content_view],
|
53
69
|
:name => self.name + "_clone",
|
54
|
-
:relative_path => "content_views/#{
|
70
|
+
:relative_path => "content_views/#{entity_name}/#{name}/#{Foreman.uuid}"
|
55
71
|
)
|
56
72
|
end
|
57
73
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module Content
|
2
2
|
class Repository::OperatingSystem < Repository
|
3
|
-
|
4
|
-
validates_presence_of :operatingsystem
|
3
|
+
alias_method :operatingsystem, :originator
|
5
4
|
|
6
5
|
def self.model_name
|
7
6
|
Repository.model_name
|
@@ -10,5 +9,15 @@ module Content
|
|
10
9
|
def entity_name
|
11
10
|
operatingsystem.to_label
|
12
11
|
end
|
12
|
+
|
13
|
+
def description
|
14
|
+
entity_name
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def set_originator_type
|
20
|
+
self.originator_type ||= 'Operatingsystem'
|
21
|
+
end
|
13
22
|
end
|
14
23
|
end
|
@@ -1,10 +1,9 @@
|
|
1
1
|
module Content
|
2
2
|
class Repository::Product < Repository
|
3
|
-
|
3
|
+
alias_method :product, :originator
|
4
4
|
has_many :operatingsystem_repositories, :foreign_key => :repository_id, :dependent => :destroy, :uniq => true
|
5
5
|
has_many :operatingsystems, :through => :operatingsystem_repositories
|
6
|
-
|
7
|
-
validates_presence_of :product
|
6
|
+
delegate :description, :to => :product
|
8
7
|
|
9
8
|
def self.model_name
|
10
9
|
Repository.model_name
|
@@ -17,5 +16,11 @@ module Content
|
|
17
16
|
def entity_name
|
18
17
|
product.name
|
19
18
|
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def set_originator_type
|
23
|
+
self.originator_type = 'Content::Product'
|
24
|
+
end
|
20
25
|
end
|
21
26
|
end
|
@@ -1,29 +1,24 @@
|
|
1
1
|
module Content
|
2
2
|
class RepositoryClone < ActiveRecord::Base
|
3
3
|
include Content::Orchestration::Pulp::Clone
|
4
|
-
|
5
|
-
REPO_PREFIX = '/pulp/repos/'
|
4
|
+
include Content::RepositoryCommon
|
6
5
|
|
7
6
|
belongs_to :repository
|
8
|
-
has_many :content_view_repository_clones, :dependent => :destroy
|
7
|
+
has_many :content_view_repository_clones, :dependent => :destroy, :as => :repository
|
9
8
|
has_many :content_views, :through => :content_view_repository_clones
|
10
9
|
before_destroy EnsureNotUsedBy.new(:content_views)
|
11
10
|
|
12
11
|
attr_accessible :description, :last_published, :name, :pulp_id, :relative_path, :status, :content_views
|
13
12
|
validate :relative_path, :repository_id, :presence => true
|
14
13
|
|
15
|
-
delegate :content_type, :architecture, :unprotected, :gpg_key, :product, :to => :repository
|
14
|
+
delegate :content_type, :architecture, :unprotected, :gpg_key, :product, :operatingsystem, :enabled, :to => :repository
|
16
15
|
|
17
16
|
scope :for_content_views, lambda { |ids|
|
18
17
|
joins(:content_view_repository_clones).
|
19
18
|
where('content_content_view_repository_clones' => {:content_view_id => ids})
|
20
19
|
}
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
scheme = (unprotected ? 'http' : 'https')
|
25
|
-
port = (pulp_url.port == 443 || pulp_url.port == 80 ? "" : ":#{pulp_url.port}")
|
26
|
-
"#{scheme}://#{pulp_url.host}#{port}#{REPO_PREFIX}#{relative_path}"
|
27
|
-
end
|
21
|
+
default_scope { includes(:repository) }
|
22
|
+
|
28
23
|
end
|
29
24
|
end
|
@@ -1,32 +1,11 @@
|
|
1
1
|
module Content
|
2
2
|
class ContentViewFactory
|
3
3
|
include ActiveModel::Conversion
|
4
|
-
|
4
|
+
include ActiveModel::Validations
|
5
|
+
extend ActiveModel::Naming
|
5
6
|
|
6
|
-
attr_accessor :
|
7
|
-
|
8
|
-
def self.create_product_content_view(product_id)
|
9
|
-
product = Product.find_by_id(product_id)
|
10
|
-
ContentView.new(:source_repositories => product.repositories, :originator => product)
|
11
|
-
end
|
12
|
-
|
13
|
-
# create a content view of an operating system
|
14
|
-
def self.create_os_content_view(operatingsystem_id)
|
15
|
-
os = Operatingsystem.find_by_id(operatingsystem_id)
|
16
|
-
ContentView.new(:source_repositories => os.repositories, :originator => os)
|
17
|
-
end
|
18
|
-
|
19
|
-
# create a composite content view of a hostgroup (the hostgroup may have a list of products) and a parent
|
20
|
-
# content view. The parent content view is one of the hostgroup parent content views.
|
21
|
-
# A hostgroup may have number of content views representing different versions of that hostgroup content.
|
22
|
-
def self.create_composite_content_view(options = {})
|
23
|
-
factory = new(options)
|
24
|
-
hostgroup = Hostgroup.find_by_id(factory.originator_id)
|
25
|
-
clone_ids = Content::RepositoryClone.for_content_views([factory.product_cv, factory.os_cv]).
|
26
|
-
pluck(:id)
|
27
|
-
ContentView.new(:source_repositories=> [], :originator => hostgroup,
|
28
|
-
:repository_clone_ids => clone_ids, :parent_id => factory.parent_cv)
|
29
|
-
end
|
7
|
+
attr_accessor :originator_type, :originator_id, :parent_cv, :product_cv, :os_cv, :source_repositories
|
8
|
+
validates_presence_of :originator_type, :originator_id
|
30
9
|
|
31
10
|
def initialize(attributes = {})
|
32
11
|
attributes.each do |name, value|
|
@@ -37,5 +16,47 @@ module Content
|
|
37
16
|
def persisted?
|
38
17
|
false
|
39
18
|
end
|
19
|
+
|
20
|
+
def content_view
|
21
|
+
case originator_type
|
22
|
+
when 'Hostgroup'
|
23
|
+
create_composite_content_view
|
24
|
+
else
|
25
|
+
ContentView.new(:repository_ids_to_clone => originator.repositories, :originator => originator)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def repositories
|
30
|
+
originator.repositories
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def originator
|
36
|
+
originator ||= case originator_type
|
37
|
+
when 'Content::Product'
|
38
|
+
Product.find(originator_id)
|
39
|
+
when 'Operatingsystem'
|
40
|
+
Operatingsystem.find(originator_id)
|
41
|
+
when 'Hostgroup'
|
42
|
+
Hostgroup.find(originator_id)
|
43
|
+
else
|
44
|
+
raise _('Unknown type')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# create a composite content view of a hostgroup (the hostgroup may have a list of products) and a parent
|
49
|
+
# content view. The parent content view is one of the hostgroup parent content views.
|
50
|
+
# A hostgroup may have number of content views representing different versions of that hostgroup content.
|
51
|
+
def create_composite_content_view
|
52
|
+
clone_ids = Content::RepositoryClone.for_content_views([product_cv, os_cv]).pluck(:id)
|
53
|
+
source_ids = Content::Repository.for_content_views([product_cv, os_cv]).pluck(:id)
|
54
|
+
ContentView.new(:originator => originator,
|
55
|
+
:repository_clone_ids => clone_ids,
|
56
|
+
:repository_source_ids => source_ids,
|
57
|
+
:parent_id => parent_cv)
|
58
|
+
end
|
59
|
+
|
60
|
+
|
40
61
|
end
|
41
62
|
end
|
@@ -126,6 +126,19 @@ class Content::Pulp::Repository
|
|
126
126
|
true
|
127
127
|
end
|
128
128
|
|
129
|
+
def sync_schedule=(schedule)
|
130
|
+
type = Runcible::Extensions::YumImporter::ID
|
131
|
+
if schedule.present?
|
132
|
+
Runcible::Resources::RepositorySchedule.create(pulp_id, type, schedule)
|
133
|
+
else
|
134
|
+
Runcible::Extensions::Repository.remove_schedules(pulp_id, type)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def sync_schedule
|
139
|
+
Runcible::Resources::RepositorySchedule.list(pulp_id, Runcible::Extensions::YumImporter::ID)
|
140
|
+
end
|
141
|
+
|
129
142
|
private
|
130
143
|
|
131
144
|
def create_repository pulp_distributors = []
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class Content::Pulp::RepositorySyncHistory
|
2
|
-
attr_reader :exception, :removed_count, :started, :completed,
|
2
|
+
attr_reader :exception, :removed_count, :started, :completed,
|
3
3
|
:error_message, :added_count, :updated_count, :details, :result
|
4
4
|
|
5
5
|
def initialize(attrs)
|
@@ -12,7 +12,7 @@ class Content::Pulp::RepositorySyncHistory
|
|
12
12
|
{
|
13
13
|
:comps => summary[:time_total_sec],
|
14
14
|
:errata => summary[:errata_time_total_sec],
|
15
|
-
:packages => summary[:packages][:time_total_sec]
|
15
|
+
:packages => (summary[:packages][:time_total_sec] rescue 0)
|
16
16
|
}
|
17
17
|
end
|
18
18
|
|
@@ -32,4 +32,10 @@ class Content::Pulp::RepositorySyncHistory
|
|
32
32
|
}
|
33
33
|
end
|
34
34
|
|
35
|
+
private
|
36
|
+
|
37
|
+
def summary
|
38
|
+
@summary ||= Hash.new(0)
|
39
|
+
end
|
40
|
+
|
35
41
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<%= wizard_header 2, _("Select source type"), _("Select content"), _("Create") %>
|
2
|
-
<%= form_for
|
2
|
+
<%= form_for @factory, :url => hash_for_new_content_view_path, :class => 'form-horizontal well', :method => :get do |f| %>
|
3
3
|
<%= f.hidden_field :originator_id, :value => @hostgroup.id %>
|
4
4
|
<%= f.hidden_field :originator_type, :value => @hostgroup.class.name %>
|
5
5
|
<%= select_f f, :parent_cv, @hostgroup.parent.try(:content_views) || [], "id", "name",
|
@@ -1,3 +1,5 @@
|
|
1
|
+
<%= wizard_header 2, _('Select source type'), _('Select content'), _('Create') %>
|
2
|
+
|
1
3
|
<%= form_for @content_view, :url => (@content_view.new_record? ? content_views_path : content_view_path(@content_view)) do |f| %>
|
2
4
|
<%= base_errors_for @content_view %>
|
3
5
|
|
@@ -11,30 +13,31 @@
|
|
11
13
|
<%= text_f f, :name, :value => @content_view.to_label %>
|
12
14
|
|
13
15
|
<% if @content_view.new_record? %>
|
14
|
-
<% @content_view.source_repositories.each do |repo| %>
|
15
|
-
<%= f.hidden_field :source_repositories, :multiple => true, :value => repo.id %>
|
16
|
-
<% end if @content_view.source_repositories %>
|
17
|
-
<% @content_view.repository_clones.each do |repo| %>
|
18
|
-
<%= f.hidden_field :repository_clone_ids, :multiple => true, :value => repo.id %>
|
19
|
-
<% end if @content_view.repository_clones %>
|
20
|
-
|
21
16
|
<%= f.hidden_field :originator_id, :value => @content_view.originator_id %>
|
22
17
|
<%= f.hidden_field :originator_type, :value => @content_view.originator_type %>
|
23
18
|
<% end %>
|
24
19
|
|
25
20
|
<table class="table table-bordered">
|
26
21
|
<tr>
|
22
|
+
<th></th>
|
27
23
|
<th><%= _("Name") %></th>
|
28
24
|
<th><%= _("State") %></th>
|
29
|
-
<th><%= _(
|
25
|
+
<th><%= f.object.kind_of?(Content::Repository) ? _('Last Sync') : _('Last Publish') %></th>
|
30
26
|
<th><%= _("Content type") %></th>
|
31
27
|
</tr>
|
32
|
-
<% repositories
|
33
|
-
<tr>
|
34
|
-
<td
|
35
|
-
|
36
|
-
|
37
|
-
|
28
|
+
<% repositories.each do |repository| %>
|
29
|
+
<tr class='repository_selection'>
|
30
|
+
<td>
|
31
|
+
<% if @content_view.new_record? %>
|
32
|
+
<%= check_box_tag nil, repository.id, true %>
|
33
|
+
<%= f.hidden_field :repository_ids_to_clone, :value => repository.id, :multiple => true, :id => "clone-#{repository.id}",
|
34
|
+
:disabled => true %>
|
35
|
+
<%= f.hidden_field :repository_source_ids, :value => repository.id, :multiple => true, :id => "latest-#{repository.id}",
|
36
|
+
:disabled => true %>
|
37
|
+
<%= select_tag(nil, options_for_type_selection(repository), {:class => 'cv-repo-selection span2', :id => "select-#{repository.id}"}) %>
|
38
|
+
<% end %>
|
39
|
+
</td>
|
40
|
+
<%= render :partial => 'content/repositories/repository', :locals => {:repository => repository} %>
|
38
41
|
</tr>
|
39
42
|
<% end %>
|
40
43
|
</table>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<% title_actions select_action_button( _('New content view'),
|
3
3
|
display_link_if_authorized(_("Product view"), hash_for_new_content_view_path(:type=>'product')),
|
4
4
|
display_link_if_authorized(_("Operating system view"), hash_for_new_content_view_path(:type=>'operatingsystem')),
|
5
|
-
display_link_if_authorized(_("
|
5
|
+
display_link_if_authorized(_("Composite hostgroup view"), hash_for_new_content_view_path(:type=>'hostgroup'))) %>
|
6
6
|
|
7
7
|
|
8
8
|
<table class="table table-bordered table-striped">
|
@@ -14,11 +14,12 @@
|
|
14
14
|
</tr>
|
15
15
|
<% @content_views.each do |content_view| %>
|
16
16
|
<tr>
|
17
|
-
<td><%= link_to_if_authorized(h(content_view.name),
|
17
|
+
<td><%= link_to_if_authorized(h(content_view.name), hash_for_content_view_path(content_view)) %></td>
|
18
18
|
<td><%= @counter[content_view.id] || '0' %></td>
|
19
19
|
<td><%= _("%s ago") % time_ago_in_words(content_view.created_at) %></td>
|
20
20
|
<td><%= action_buttons(
|
21
|
-
|
21
|
+
display_link_if_authorized(_("Edit"), hash_for_edit_content_view_path(content_view)),
|
22
|
+
display_delete_if_authorized(hash_for_content_view_path(content_view), :confirm => "Delete #{content_view}?")
|
22
23
|
)%></td>
|
23
24
|
</tr>
|
24
25
|
<% end %>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<% title @content_view.to_label %>
|
2
|
+
|
3
|
+
<%= title_actions(button_group(
|
4
|
+
link_to_if_authorized(_("Edit"), hash_for_edit_content_view_path(@content_view)),
|
5
|
+
display_delete_if_authorized(hash_for_content_view_path(@content_view), :class => 'btn-danger', :confirm => "Delete #{@content_view.name}?")
|
6
|
+
)) %>
|
7
|
+
|
8
|
+
<h3><%= _('Repositories') %></h3>
|
9
|
+
<table class="table table-bordered table-striped">
|
10
|
+
<tr>
|
11
|
+
<th><%= sort :name, :as => s_("Name") %></th>
|
12
|
+
<th><%= _("State") %></th>
|
13
|
+
<th><%= _("Last sync status") %></th>
|
14
|
+
<th><%= _("Content type") %></th>
|
15
|
+
</tr>
|
16
|
+
<% @content_view.repository_clones.each do |repository| %>
|
17
|
+
<tr>
|
18
|
+
<%= render :partial => 'content/repositories/repository', :locals => { :repository => repository } %>
|
19
|
+
</tr>
|
20
|
+
<% end %>
|
21
|
+
</table>
|
22
|
+
|
@@ -1,15 +1,9 @@
|
|
1
1
|
<%= javascript 'content/content.js' %>
|
2
2
|
<%= form_for @product, :url => (@product.new_record? ? products_path : product_path(@product)) do |f| %>
|
3
3
|
<%= base_errors_for @product %>
|
4
|
-
<ul class="nav nav-tabs" data-tabs="tabs">
|
5
|
-
<li class="active"><a href="#primary" data-toggle="tab"><%= _("Product") %></a></li>
|
6
|
-
</ul>
|
7
|
-
<div class="tab-content">
|
8
|
-
<div class="tab-pane active" id="primary">
|
9
|
-
<%= text_f f, :name %>
|
10
|
-
<%= textarea_f f, :description, :class => "input-xlarge" , :rows=> '3' %>
|
11
|
-
</div>
|
12
4
|
|
13
|
-
|
5
|
+
<%= text_f f, :name %>
|
6
|
+
<%= textarea_f f, :description, :class => "input-xlarge", :rows => '3' %>
|
7
|
+
|
14
8
|
<%= submit_or_cancel f %>
|
15
9
|
<% end %>
|