katello 3.4.0.rc2 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of katello might be problematic. Click here for more details.

Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/katello/api/rhsm/candlepin_dynflow_proxy_controller.rb +43 -0
  3. data/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb +20 -14
  4. data/app/controllers/katello/api/v2/content_views_controller.rb +2 -2
  5. data/app/controllers/katello/api/v2/errata_controller.rb +1 -0
  6. data/app/controllers/katello/api/v2/packages_controller.rb +6 -2
  7. data/app/lib/actions/candlepin/async_hypervisors.rb +22 -0
  8. data/app/lib/actions/candlepin/import_pool_handler.rb +19 -23
  9. data/app/lib/actions/katello/capsule_content/sync.rb +1 -0
  10. data/app/lib/actions/katello/host/hypervisors.rb +16 -4
  11. data/app/lib/actions/katello/host/hypervisors_update.rb +2 -2
  12. data/app/lib/actions/katello/host/upload_package_profile.rb +13 -6
  13. data/app/lib/actions/katello/repository/remove_content.rb +8 -8
  14. data/app/lib/actions/middleware/backend_services_check.rb +2 -2
  15. data/app/lib/katello/capsule_content.rb +6 -0
  16. data/app/lib/katello/resources/candlepin.rb +12 -7
  17. data/app/models/katello/concerns/organization_extensions.rb +4 -0
  18. data/app/models/katello/events/import_pool.rb +17 -0
  19. data/app/models/katello/glue/candlepin/owner.rb +4 -0
  20. data/app/models/katello/host/subscription_facet.rb +9 -2
  21. data/app/models/katello/ping.rb +4 -4
  22. data/app/models/katello/subscription_status.rb +5 -2
  23. data/app/services/cert/certs.rb +7 -0
  24. data/config/routes/api/rhsm.rb +4 -2
  25. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/views/activation-key-details.html +2 -1
  26. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/views/activation-key-host-collections-table.html +4 -6
  27. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/views/activation-key-info.html +2 -2
  28. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/new/views/activation-key-new.html +1 -1
  29. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/views/content-host-details.html +1 -0
  30. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/views/content-host-info.html +1 -1
  31. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/details/filters/views/filter-repositories.html +0 -1
  32. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/new/views/content-view-new.html +1 -1
  33. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/docker-tags/details/views/docker-tag-environments.html +4 -11
  34. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/gpg-keys/new/views/new-gpg-key.html +1 -1
  35. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/host-collections/new/views/new-host-collection.html +1 -1
  36. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +1 -1
  37. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/views/product-details.html +1 -1
  38. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/new/new-sync-plan-modal.controller.js +46 -0
  39. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/new/product-form.controller.js +15 -2
  40. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/new/views/new-sync-plan-modal.html +18 -0
  41. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/new/views/product-new-form.html +40 -38
  42. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/products.routes.js +0 -19
  43. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/subscriptions/content-access-mode-banner.directive.js +22 -0
  44. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/subscriptions/details/views/subscription-details.html +2 -1
  45. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/subscriptions/manifest/views/manifest-import.html +1 -1
  46. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/subscriptions/manifest/views/manifest.html +2 -0
  47. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/subscriptions/views/content-access-mode-banner.html +5 -0
  48. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/sync-plans/details/views/sync-plan-info.html +0 -3
  49. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/sync-plans/new/new-sync-plan.controller.js +16 -24
  50. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/sync-plans/new/views/new-sync-plan-form.html +52 -48
  51. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/sync-plans/new/views/new-sync-plan.html +3 -1
  52. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/sync-plans/sync-plan-helper.service.js +66 -0
  53. data/lib/katello/engine.rb +10 -0
  54. data/lib/katello/params_parser_wrapper.rb +16 -0
  55. data/lib/katello/permissions/host_permissions.rb +7 -3
  56. data/lib/katello/plugin.rb +4 -4
  57. data/lib/katello/tasks/regenerate_ueber_certs.rake +1 -1
  58. data/lib/katello/tasks/repository.rake +23 -0
  59. data/lib/katello/version.rb +1 -1
  60. metadata +13 -5
  61. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/views/product-new-sync-plan.html +0 -2
@@ -154,6 +154,10 @@ module Katello
154
154
  end
155
155
  discovery
156
156
  end
157
+
158
+ def regenerate_ueber_cert
159
+ ::Katello::Resources::Candlepin::Owner.generate_ueber_cert(self.label)
160
+ end
157
161
  end
158
162
  end
159
163
  end
@@ -0,0 +1,17 @@
1
+ module Katello
2
+ module Events
3
+ class ImportPool
4
+ EVENT_TYPE = 'import_pool'.freeze
5
+
6
+ def initialize(pool_id)
7
+ @pool = ::Katello::Pool.find_by(:id => pool_id)
8
+ end
9
+
10
+ def run
11
+ @pool.try(:import_data)
12
+ rescue RestClient::ResourceNotFound
13
+ Rails.logger.warn "skipped re-index of non-existent pool #{@pool.cp_id}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -34,6 +34,10 @@ module Katello
34
34
  Resources::Candlepin::Owner.update(self.label, :defaultServiceLevel => level)
35
35
  end
36
36
 
37
+ def content_access_mode
38
+ self.owner_details['contentAccessMode']
39
+ end
40
+
37
41
  def pools(consumer_uuid = nil)
38
42
  if consumer_uuid
39
43
  Resources::Candlepin::Owner.pools self.label, :consumer => consumer_uuid
@@ -106,8 +106,15 @@ module Katello
106
106
  end
107
107
  end
108
108
 
109
- def update_subscription_status
110
- host.get_status(::Katello::SubscriptionStatus).refresh!
109
+ def update_subscription_status(status_override = nil)
110
+ status = host.get_status(::Katello::SubscriptionStatus)
111
+ if status_override
112
+ status.status = status.to_status(:status_override => status_override)
113
+ status.save!
114
+ else
115
+ host.get_status(::Katello::SubscriptionStatus).refresh!
116
+ end
117
+
111
118
  host.refresh_global_status!
112
119
  end
113
120
 
@@ -115,19 +115,19 @@ module Katello
115
115
  def pulp_without_auth(url)
116
116
  body = backend_status(url, :pulp)
117
117
 
118
- fail _("Pulp does not appear to be running.") if body.empty?
118
+ fail _("Pulp does not appear to be running at %s.") % url if body.empty?
119
119
  json = JSON.parse(body)
120
120
 
121
121
  if json['database_connection'] && json['database_connection']['connected'] != true
122
- fail _("Pulp database connection issue.")
122
+ fail _("Pulp database connection issue at %s.") % url
123
123
  end
124
124
 
125
125
  if json['messaging_connection'] && json['messaging_connection']['connected'] != true
126
- fail _("Pulp message bus connection issue.")
126
+ fail _("Pulp message bus connection issue at %s.") % url
127
127
  end
128
128
 
129
129
  unless all_pulp_workers_present?(json)
130
- fail _("Not all necessary pulp workers running.")
130
+ fail _("Not all necessary pulp workers running at %s.") % url
131
131
  end
132
132
 
133
133
  json
@@ -35,9 +35,12 @@ module Katello
35
35
  end
36
36
  end
37
37
 
38
- def to_status(_options = {})
38
+ def to_status(options = {})
39
39
  return UNKNOWN unless host.subscription_facet.try(:uuid)
40
- case Katello::Candlepin::Consumer.new(host.subscription_facet.uuid, host.organization.label).entitlement_status
40
+ status_override = options.fetch(:status_override, nil)
41
+ status = status_override || Katello::Candlepin::Consumer.new(host.subscription_facet.uuid, host.organization.label).entitlement_status
42
+
43
+ case status
41
44
  when Katello::Candlepin::Consumer::ENTITLEMENTS_VALID
42
45
  VALID
43
46
  when Katello::Candlepin::Consumer::ENTITLEMENTS_PARTIAL
@@ -15,5 +15,12 @@ module Cert
15
15
  def self.ssl_client_key
16
16
  @ssl_client_key ||= OpenSSL::PKey::RSA.new(File.open(Setting['pulp_client_key'], 'r').read)
17
17
  end
18
+
19
+ def self.verify_ueber_cert(organization)
20
+ ueber_cert = OpenSSL::X509::Certificate.new(self.ueber_cert(organization)[:cert])
21
+ cert_store = OpenSSL::X509::Store.new
22
+ cert_store.add_file Setting[:ssl_ca_file]
23
+ organization.regenerate_ueber_cert unless cert_store.verify ueber_cert
24
+ end
18
25
  end
19
26
  end
@@ -16,6 +16,7 @@ Katello::Engine.routes.draw do
16
16
  end
17
17
  match '/consumers' => 'candlepin_proxies#consumer_create', :via => :post
18
18
  match '/hypervisors' => 'candlepin_proxies#hypervisors_update', :via => :post
19
+ match '/hypervisors/:owner/' => 'candlepin_proxies#async_hypervisors_update', :via => :post
19
20
  match '/owners/:organization_id/environments' => 'candlepin_proxies#rhsm_index', :via => :get
20
21
  match '/owners/:organization_id/pools' => 'candlepin_proxies#get', :via => :get, :as => :proxy_owner_pools_path
21
22
  match '/owners/:organization_id/servicelevels' => 'candlepin_proxies#get', :via => :get, :as => :proxy_owner_servicelevels_path
@@ -39,8 +40,8 @@ Katello::Engine.routes.draw do
39
40
  match '/deleted_consumers' => 'candlepin_proxies#get', :via => :get, :as => :proxy_deleted_consumers_path
40
41
  match '/entitlements/:id' => 'candlepin_proxies#get', :via => :get, :as => :proxy_entitlements_path
41
42
  match '/subscriptions' => 'candlepin_proxies#post', :via => :post, :as => :proxy_subscriptions_post_path
42
- match '/consumers/:id/profile/' => 'candlepin_proxies#upload_package_profile', :via => :put
43
- match '/consumers/:id/packages/' => 'candlepin_proxies#upload_package_profile', :via => :put
43
+ match '/consumers/:id/profile/' => 'candlepin_dynflow_proxy#upload_package_profile', :via => :put
44
+ match '/consumers/:id/packages/' => 'candlepin_dynflow_proxy#upload_package_profile', :via => :put
44
45
  match '/consumers/:id/tracer/' => 'candlepin_proxies#upload_tracer_profile', :via => :put
45
46
  match '/consumers/:id/checkin/' => 'candlepin_proxies#checkin', :via => :put
46
47
  match '/consumers/:id' => 'candlepin_proxies#facts', :via => :put
@@ -54,6 +55,7 @@ Katello::Engine.routes.draw do
54
55
  match '/consumers/:id/content_overrides/' => 'candlepin_proxies#delete', :via => :delete, :as => :proxy_consumer_content_overrides_delete_path
55
56
  match '/consumers/:id/available_releases' => 'candlepin_proxies#available_releases', :via => :get
56
57
  match '/systems/:id/enabled_repos' => 'candlepin_proxies#enabled_repos', :via => :put
58
+ match '/jobs/:jobId' => 'candlepin_proxies#get', :via => :get, :as => :proxy_jobs_get_path
57
59
  match '/status' => 'candlepin_proxies#server_status', :via => :get
58
60
  end
59
61
  end
@@ -40,6 +40,7 @@
40
40
  </div>
41
41
 
42
42
  <nav data-block="navigation">
43
+ <div content-access-mode-banner></div>
43
44
  <ul class="nav nav-tabs details-nav">
44
45
  <li ng-class="{active: isState('activation-key.info')}">
45
46
  <a ui-sref="activation-key.info">
@@ -89,4 +90,4 @@
89
90
  <section data-block="content">
90
91
  <div ui-view></div>
91
92
  </section>
92
- </div>
93
+ </div>
@@ -3,12 +3,10 @@
3
3
 
4
4
  <div data-extend-template="layouts/partials/table.html">
5
5
  <div data-block="search">
6
- <div class="input-group">
7
- <input type="text"
8
- class="form-control"
9
- placeholder="{{ 'Filter' | translate }}"
10
- ng-model="hostCollectionFilter"/>
11
- </div>
6
+ <input type="text"
7
+ class="form-control"
8
+ placeholder="{{ 'Filter' | translate }}"
9
+ ng-model="hostCollectionFilter"/>
12
10
  </div>
13
11
 
14
12
  <div data-block="list-actions">
@@ -1,9 +1,9 @@
1
1
  <span page-title ng-model="activationKey">{{ 'Details for Activation Key:' | translate }} {{ activationKey.name }}</span>
2
2
 
3
3
  <div bst-alert="info">
4
- <p translate>
4
+ <span translate>
5
5
  This activation key may be used during system registration. For example:
6
- </p>
6
+ </span>
7
7
  <p translate>
8
8
  subscription-manager register --org="{{ activationKey.organization.label }}" --activationkey="{{ activationKey.name }}"
9
9
  </p>
@@ -5,7 +5,7 @@
5
5
  <h2 translate>New Activation Key</h2>
6
6
  </header>
7
7
 
8
- <div data-block="content">
8
+ <div data-block="content" class="row">
9
9
  <form name="activationKeyForm" class="col-sm-5" novalidate role="form">
10
10
 
11
11
  <div bst-form-group label="{{ 'Name' | translate }}">
@@ -45,6 +45,7 @@
45
45
  </nav>
46
46
 
47
47
  <nav data-block="navigation">
48
+ <div content-access-mode-banner></div>
48
49
  <ul class="nav nav-tabs">
49
50
  <li ng-class="{active: isState('content-host.info')}">
50
51
  <a translate
@@ -55,7 +55,6 @@
55
55
  ng-class="{'table-mask': table.working}">
56
56
  <thead>
57
57
  <tr bst-table-head row-select>
58
- <th bst-table-column="affectedRepos">Affected?</th>
59
58
  <th bst-table-column="name" translate>Name</th>
60
59
  <th bst-table-column="product" translate>Product</th>
61
60
  <th bst-table-column="type" translate>Type</th>
@@ -27,7 +27,7 @@
27
27
  </form>
28
28
  </div>
29
29
 
30
- <div data-block="content">
30
+ <div data-block="content" class="row">
31
31
  <form name="contentViewForm" class="col-sm-5" novalidate role="form">
32
32
  <div bst-form-group label="{{ 'Name' | translate }}">
33
33
  <input id="name"
@@ -1,17 +1,10 @@
1
1
 
2
2
  <div data-extend-template="layouts/partials/table.html">
3
3
  <div data-block="search">
4
- <div class="col-sm-6">
5
- <form class="form-inline">
6
- <div class="form-group">
7
- <input type="text"
8
- class="form-control"
9
- placeholder="{{ 'Filter' | translate }}"
10
- ng-model="dockerTagFilter"/>
11
-
12
- </div>
13
- </form>
14
- </div>
4
+ <input type="text"
5
+ class="form-control"
6
+ placeholder="{{ 'Filter' | translate }}"
7
+ ng-model="dockerTagFilter"/>
15
8
  </div>
16
9
 
17
10
  <span data-block="no-rows-message" translate>
@@ -3,7 +3,7 @@
3
3
  <h2 translate>Create GPG Key</h2>
4
4
  </header>
5
5
 
6
- <div data-block="content">
6
+ <div data-block="content" class="row">
7
7
  <form name="gpgKeyForm" class="col-sm-5" novalidate role="form"
8
8
  action="{{ uploadURL }}"
9
9
  ng-upload="uploadContent(content)"
@@ -5,7 +5,7 @@
5
5
  <h2 translate>Create Host Collection</h2>
6
6
  </header>
7
7
 
8
- <div data-block="content">
8
+ <div data-block="content" class="row">
9
9
  <form name="hostCollectionForm" class="col-sm-5" novalidate role="form">
10
10
  <div bst-form-group label="{{ 'Name' | translate }}">
11
11
  <input id="name"
@@ -4,7 +4,7 @@
4
4
 
5
5
  <div bst-alerts error-messages="errorMessages" success-messages="successMessages"></div>
6
6
 
7
- <div data-block="content">
7
+ <div data-block="content" class="row">
8
8
  <form name="repositoryForm" class="col-sm-5" novalidate role="form">
9
9
  <div>
10
10
  <h4 translate> General Information </h4>
@@ -23,7 +23,7 @@
23
23
  </li>
24
24
 
25
25
  <li role="menuitem" ng-hide="denied('create_sync_plans')">
26
- <a ui-sref="product.new-sync-plan">
26
+ <a ui-sref="openProductModal()">
27
27
  <span translate>New Sync Plan</span>
28
28
  </a>
29
29
  </li>
@@ -0,0 +1,46 @@
1
+ /**
2
+ * @ngdoc object
3
+ * @name Bastion.proudcts.controller:NewSyncPlanModalController
4
+ *
5
+ * @requires $scope
6
+ * @requires $uibModalInstance
7
+ * @requires SyncPlan
8
+ * @requires SyncPlanHelper
9
+ *
10
+ * @description
11
+ * A controller for creating a new sync plan in a modal.
12
+ */
13
+ angular.module('Bastion.products').controller('NewSyncPlanModalController',
14
+ ['$scope', '$uibModalInstance', 'SyncPlan', 'SyncPlanHelper',
15
+ function ($scope, $uibModalInstance, SyncPlan, SyncPlanHelper) {
16
+ function success(syncPlan) {
17
+ $uibModalInstance.close(syncPlan);
18
+ }
19
+
20
+ function error(response) {
21
+ var form = SyncPlanHelper.getForm();
22
+
23
+ angular.forEach(response.data.errors, function (errors, field) {
24
+ form[field].$setValidity('server', false);
25
+ form[field].$error.messages = errors;
26
+ });
27
+ }
28
+
29
+ $scope.ok = function (syncPlan) {
30
+ SyncPlanHelper.createSyncPlan(syncPlan, success, error);
31
+ };
32
+
33
+ $scope.cancel = function () {
34
+ $uibModalInstance.dismiss('cancel');
35
+ };
36
+
37
+ $scope.isFormDisabled = function () {
38
+ var form = SyncPlanHelper.getForm();
39
+ return form && !form.$valid;
40
+ };
41
+
42
+ $scope.intervals = SyncPlanHelper.getIntervals();
43
+ $scope.syncPlan = new SyncPlan();
44
+ $scope.syncPlan.interval = $scope.intervals[0].id;
45
+ }]
46
+ );
@@ -4,19 +4,21 @@
4
4
  *
5
5
  * @requires $scope
6
6
  * @requires $q
7
+ * @requires $uibModal
7
8
  * @requires Product
8
9
  * @requires GPGKey
9
10
  * @requires SyncPlan
10
11
  * @requires FormUtils
11
12
  *
13
+ *
12
14
  * @description
13
15
  * Provides the functionality specific to Products for use with the Nutupane UI pattern.
14
16
  * Defines the columns to display and the transform function for how to generate each row
15
17
  * within the table.
16
18
  */
17
19
  angular.module('Bastion.products').controller('ProductFormController',
18
- ['$scope', '$q', 'Product', 'GPGKey', 'SyncPlan', 'FormUtils', 'GlobalNotification',
19
- function ($scope, $q, Product, GPGKey, SyncPlan, FormUtils, GlobalNotification) {
20
+ ['$scope', '$q', '$uibModal', 'Product', 'GPGKey', 'SyncPlan', 'FormUtils', 'GlobalNotification',
21
+ function ($scope, $q, $uibModal, Product, GPGKey, SyncPlan, FormUtils, GlobalNotification) {
20
22
 
21
23
  function fetchGpgKeys() {
22
24
  return GPGKey.queryUnpaged(function (gpgKeys) {
@@ -62,5 +64,16 @@ angular.module('Bastion.products').controller('ProductFormController',
62
64
  $q.all([fetchSyncPlans().$promise, fetchGpgKeys().$promise]).finally(function () {
63
65
  $scope.page.loading = false;
64
66
  });
67
+
68
+ $scope.openSyncPlanModal = function () {
69
+ $uibModal.open({
70
+ templateUrl: 'products/new/views/new-sync-plan-modal.html',
71
+ controller: 'NewSyncPlanModalController'
72
+ }).result.then(function ($value) {
73
+ fetchSyncPlans().$promise.then(function () {
74
+ $scope.product['sync_plan_id'] = $value.id;
75
+ });
76
+ });
77
+ };
65
78
  }]
66
79
  );
@@ -0,0 +1,18 @@
1
+ <div data-extend-template="components/views/bst-modal.html" ng-controller="NewSyncPlanController">
2
+ <h4 data-block="modal-header" translate>
3
+ New Sync Plan
4
+ </h4>
5
+
6
+ <div data-block="modal-body">
7
+ <div ng-include="'sync-plans/new/views/new-sync-plan-form.html'"></div>
8
+ </div>
9
+
10
+ <span data-block="modal-confirm-button">
11
+ <button class="btn btn-primary" ng-click="ok(syncPlan)"
12
+ ng-disabled="isFormDisabled()">
13
+ <span translate>Save</span>
14
+ </button>
15
+ </span>
16
+ </div>
17
+
18
+
@@ -1,30 +1,31 @@
1
- <form name="productForm" class="col-sm-5" novalidate role="form">
1
+ <div class="row">
2
+ <form name="productForm" class="col-sm-5" novalidate role="form">
2
3
 
3
- <div bst-form-group label="{{ 'Name' | translate }}">
4
- <input id="name"
5
- name="name"
6
- ng-model="product.name"
7
- type="text"
8
- autofocus
9
- required/>
10
- </div>
4
+ <div bst-form-group label="{{ 'Name' | translate }}">
5
+ <input id="name"
6
+ name="name"
7
+ ng-model="product.name"
8
+ type="text"
9
+ autofocus
10
+ required/>
11
+ </div>
11
12
 
12
- <div bst-form-group label="{{ 'Label' | translate }}">
13
- <input id="label"
14
- name="label"
15
- ng-model="product.label"
16
- type="text"
17
- required/>
18
- </div>
13
+ <div bst-form-group label="{{ 'Label' | translate }}">
14
+ <input id="label"
15
+ name="label"
16
+ ng-model="product.label"
17
+ type="text"
18
+ required/>
19
+ </div>
19
20
 
20
- <div bst-form-group label="{{ 'GPG Key' | translate }}">
21
- <select id="gpg_key_id"
22
- name="gpg_key_id"
23
- ng-model="product.gpg_key_id"
24
- ng-options="gpg_key.id as gpg_key.name for gpg_key in gpgKeys">
25
- <option value=""></option>
26
- </select>
27
- </div>
21
+ <div bst-form-group label="{{ 'GPG Key' | translate }}">
22
+ <select id="gpg_key_id"
23
+ name="gpg_key_id"
24
+ ng-model="product.gpg_key_id"
25
+ ng-options="gpg_key.id as gpg_key.name for gpg_key in gpgKeys">
26
+ <option value=""></option>
27
+ </select>
28
+ </div>
28
29
 
29
30
  <div bst-form-group label="{{ 'Sync Plan' | translate }}">
30
31
  <select id="sync_plan_id"
@@ -33,23 +34,24 @@
33
34
  ng-options="syncPlan.id as syncPlan.name for syncPlan in syncPlans">
34
35
  <option value=""></option>
35
36
  </select>
36
- <a ui-sref="products.new.sync-plan" translate>
37
+ <a ng-click="openSyncPlanModal()">
37
38
  <i class="pficon pficon-add-circle-o"></i>
38
- Create Sync Plan
39
+ <span translate>Create Sync Plan</span>
39
40
  </a>
40
41
  </div>
41
42
 
42
- <div bst-form-group label="{{ 'Description' | translate }}">
43
- <textarea id="description"
44
- name="description"
45
- ng-model="product.description">
46
- </textarea>
47
- </div>
43
+ <div bst-form-group label="{{ 'Description' | translate }}">
44
+ <textarea id="description"
45
+ name="description"
46
+ ng-model="product.description">
47
+ </textarea>
48
+ </div>
48
49
 
49
- <div bst-form-buttons
50
- on-cancel="transitionTo('products')"
51
- on-save="save(product)"
52
- working="working">
53
- </div>
50
+ <div bst-form-buttons
51
+ on-cancel="transitionTo('products')"
52
+ on-save="save(product)"
53
+ working="working">
54
+ </div>
54
55
 
55
- </form>
56
+ </form>
57
+ </div>