Browse Source

Replaces multi select combos with transfer tables

Implements an angular-python bridge that allows django/horizon forms
to use transfer tables (as seen in other parts of horizon, e.g.:
computes launch instance dialog) as form fields. These fields are
then used to replace the multi select combos boxes in the different
GBPUI dialogs/forms.

Note 1: The add and remove policy rule set actions in group details
"Provided Policy Rule Set" and "Consumed Policy Rule Set" are currently
unaffected. These two tabs do not follow the "standard" horizon method
of adding and removing items through one transfer table; instead,
it uses two different dialogs to carry out each operation separately.
This should be addressed in a separate patchset.

Note 2: This is a bit of a stop gap measure, as horizon is slowly moving
away from native django based dialogs and wizards to AngularJS. The goal
should ultimately be to do the same in GBPUI.

Change-Id: I01c9dc08b1bc35309d62eb3da0bd26f3795867ab
Partial-Bug: 1712814
Marek Lycka 1 year ago
parent
commit
cf76c87cc7

+ 3
- 0
gbpui/_1550_gbp_project_add_panel_group.py View File

@@ -14,3 +14,6 @@ ADD_INSTALLED_APPS = ['gbpui', ]
14 14
 PANEL_GROUP = 'GroupPolicyPanels'
15 15
 PANEL_GROUP_NAME = 'Policy'
16 16
 PANEL_GROUP_DASHBOARD = 'project'
17
+
18
+AUTO_DISCOVER_STATIC_FILES = True
19
+ADD_ANGULAR_MODULES = ['gbpui', ]

+ 80
- 0
gbpui/fields.py View File

@@ -16,6 +16,11 @@ from django.forms import TextInput
16 16
 from django.forms import widgets
17 17
 from django.utils.safestring import mark_safe
18 18
 
19
+from django.forms.utils import flatatt
20
+from django.utils.html import format_html
21
+
22
+from django.utils.translation import ugettext_lazy as _
23
+
19 24
 
20 25
 class DynamicMultiSelectWidget(widgets.SelectMultiple):
21 26
 
@@ -84,3 +89,78 @@ class DropdownEditWidget(TextInput):
84 89
             data_list += '<option value="%s">' % item
85 90
         data_list += '</datalist>'
86 91
         return mark_safe(text_html + data_list)
92
+
93
+
94
+class TransferTableWidget(widgets.SelectMultiple):
95
+    actions_list = []
96
+    add_item_link = None
97
+    max_items = None
98
+    allocated_filter = False
99
+
100
+    allocated_help_text = None
101
+    available_help_text = None
102
+    no_allocated_text = None
103
+    no_available_text = None
104
+
105
+    def render(self, name, value, attrs=None, choices=()):
106
+        # css class currently breaks the layout for some reason,
107
+        self.attrs.pop('class', None)
108
+
109
+        final_attrs = self.build_attrs(attrs, name=name)
110
+
111
+        selected = [] if value is None else value
112
+
113
+        options = self.render_options(choices, selected)
114
+
115
+        if self.add_item_link is not None:
116
+            final_attrs['add_item_link'] = urlresolvers.reverse(
117
+                self.add_item_link
118
+            )
119
+
120
+        if self.max_items is not None:
121
+            final_attrs['max_items'] = self.max_items
122
+
123
+        if self.allocated_filter:
124
+            final_attrs['allocated_filter'] = "True"
125
+
126
+        final_attrs['allocated_help_text'] = self.allocated_help_text
127
+        final_attrs['available_help_text'] = self.available_help_text
128
+        final_attrs['no_allocated_text'] = self.no_allocated_text
129
+        final_attrs['no_available_text'] = self.no_available_text
130
+
131
+        open_tag = format_html('<d-table {}>', flatatt(final_attrs))
132
+
133
+        output = [open_tag, options, '</d-table>']
134
+
135
+        return mark_safe('\n'.join(output))
136
+
137
+    # ...this adds the 'add item button' just by existing and returning a
138
+    # true-y value
139
+    def get_add_item_url(self):
140
+        return None
141
+
142
+
143
+class TransferTableField(fields.MultipleChoiceField):
144
+    widget = TransferTableWidget
145
+
146
+    def __init__(self, add_item_link=None, max_items=-1,
147
+                 allocated_filter=False,
148
+                 allocated_help_text="",
149
+                 available_help_text="",
150
+                 no_allocated_text=_("Select items from bellow"),
151
+                 no_available_text=_("No available items"),
152
+                 *args, **kwargs):
153
+        super(TransferTableField, self).__init__(*args, **kwargs)
154
+
155
+        self.widget.add_item_link = add_item_link
156
+        self.widget.max_items = max_items
157
+        self.widget.allocated_filter = allocated_filter
158
+
159
+        self.widget.allocated_help_text = allocated_help_text
160
+        self.widget.available_help_text = available_help_text
161
+
162
+        self.widget.no_allocated_text = no_allocated_text
163
+        self.widget.no_available_text = no_available_text
164
+
165
+    def validate(self, *args, **kwargs):
166
+        return True

+ 9
- 3
gbpui/panels/application_policy/forms.py View File

@@ -55,7 +55,7 @@ class BaseUpdateForm(forms.SelfHandlingForm):
55 55
 class UpdatePolicyRuleSetForm(BaseUpdateForm):
56 56
     name = forms.CharField(label=_("Name"))
57 57
     description = forms.CharField(label=_("Description"), required=False)
58
-    policy_rules = forms.MultipleChoiceField(label=_("Policy Rules"),)
58
+    policy_rules = fields.TransferTableField(label=_("Policy Rules"), )
59 59
     shared = forms.BooleanField(label=_("Shared"), required=False)
60 60
 
61 61
     def __init__(self, request, *args, **kwargs):
@@ -298,7 +298,11 @@ class UpdatePolicyRuleForm(BaseUpdateForm):
298 298
     name = forms.CharField(max_length=80, label=_("Name"), required=False)
299 299
     description = forms.CharField(label=_("Description"), required=False)
300 300
     policy_classifier_id = forms.ChoiceField(label=_("Policy Classifier"))
301
-    policy_actions = forms.MultipleChoiceField(label=_("Policy Actions"))
301
+    policy_actions = fields.TransferTableField(
302
+        label=_("Policy Actions"),
303
+        required=False,
304
+    )
305
+
302 306
     shared = forms.BooleanField(label=_("Shared"), required=False)
303 307
 
304 308
     def __init__(self, request, *args, **kwargs):
@@ -306,17 +310,19 @@ class UpdatePolicyRuleForm(BaseUpdateForm):
306 310
         try:
307 311
             policyrule_id = self.initial['policyrule_id']
308 312
             rule = client.policyrule_get(request, policyrule_id)
313
+
309 314
             for item in ['name', 'description',
310 315
                          'policy_classifier_id', 'policy_actions', 'shared']:
311 316
                 self.fields[item].initial = getattr(rule, item)
317
+
312 318
             actions = client.policyaction_list(request,
313 319
                 tenant_id=request.user.tenant_id)
314
-            action_list = [a.id for a in actions]
315 320
             for action in actions:
316 321
                 action.set_id_as_name_if_empty()
317 322
             actions = sorted(actions, key=lambda action: action.name)
318 323
             action_list = [(a.id, a.name) for a in actions]
319 324
             self.fields['policy_actions'].choices = action_list
325
+
320 326
             classifiers = client.policyclassifier_list(request,
321 327
                 tenant_id=request.user.tenant_id)
322 328
             classifier_list = [(c.id, c.name) for c in classifiers]

+ 13
- 8
gbpui/panels/application_policy/workflows.py View File

@@ -21,6 +21,7 @@ from horizon import workflows
21 21
 from gbpui import client
22 22
 from gbpui import fields
23 23
 
24
+
24 25
 ADD_POLICY_ACTION_URL = "horizon:project:application_policy:addpolicyaction"
25 26
 ADD_POLICY_CLASSIFIER_URL = "horizon:project:application_policy:"
26 27
 ADD_POLICY_CLASSIFIER_URL = ADD_POLICY_CLASSIFIER_URL + "addpolicyclassifier"
@@ -28,11 +29,12 @@ ADD_POLICY_RULE_URL = "horizon:project:application_policy:addpolicyrule"
28 29
 
29 30
 
30 31
 class SelectPolicyRuleAction(workflows.Action):
31
-    policy_rules = fields.DynamicMultiChoiceField(
32
+    policy_rules = fields.TransferTableField(
32 33
         label=_("Policy Rules"),
33 34
         required=False,
34 35
         add_item_link=ADD_POLICY_RULE_URL,
35
-        help_text=_("Create a policy rule set with selected rules."))
36
+        help_text=_("Create a policy rule set with selected rules.")
37
+    )
36 38
 
37 39
     class Meta(object):
38 40
         name = _("Rules")
@@ -162,11 +164,17 @@ class SelectPolicyClassifierAction(workflows.Action):
162 164
 
163 165
 
164 166
 class SelectPolicyActionAction(workflows.Action):
165
-    actions = fields.DynamicMultiChoiceField(
167
+    actions = fields.TransferTableField(
166 168
         label=_("Policy Action"),
167 169
         required=False,
168
-        help_text=_("Create a policy-rule with selected action."),
169
-        add_item_link=ADD_POLICY_ACTION_URL)
170
+        add_item_link=ADD_POLICY_ACTION_URL,
171
+        help_text=_("Create a policy-rule with selected action.")
172
+    )
173
+
174
+    def __init__(self, request, context, *args, **kwargs):
175
+        super(SelectPolicyActionAction, self).__init__(
176
+            request, context, *args, **kwargs
177
+        )
170 178
 
171 179
     class Meta(object):
172 180
         name = _("actions")
@@ -176,14 +184,11 @@ class SelectPolicyActionAction(workflows.Action):
176 184
         try:
177 185
             actions = client.policyaction_list(request,
178 186
                 tenant_id=request.user.tenant_id)
179
-            action_list = [a.id for a in actions]
180 187
             for action in actions:
181 188
                 action.set_id_as_name_if_empty()
182 189
             actions = sorted(actions,
183 190
                              key=lambda action: action.name)
184 191
             action_list = [(a.id, a.name) for a in actions]
185
-            if len(action_list) > 0:
186
-                self.fields['actions'].initial = action_list[0]
187 192
         except Exception as e:
188 193
             action_list = []
189 194
             exceptions.handle(request,

+ 15
- 10
gbpui/panels/network_policy/forms.py View File

@@ -63,10 +63,11 @@ class AddL3PolicyForm(forms.SelfHandlingForm):
63 63
                                            label=_("Subnet Prefix Length"),
64 64
                                            help_text=_("Between 2 - 30 for IP4"
65 65
                                                        "and 2-127 for IP6."),)
66
-    external_segments = \
67
-        fields.CustomMultiChoiceField(label=_("External Segments"),
68
-                                      add_item_link=EXT_SEG_PARAM_URL,
69
-                                      required=False)
66
+    external_segments = fields.TransferTableField(
67
+        label=_("External Segments"),
68
+        add_item_link=EXT_SEG_PARAM_URL,
69
+        required=False
70
+    )
70 71
     shared = forms.BooleanField(label=_("Shared"),
71 72
                                 initial=False,
72 73
                                 required=False)
@@ -296,9 +297,11 @@ class CreateServicePolicyForm(forms.SelfHandlingForm):
296 297
     name = forms.CharField(max_length=80, label=_("Name"))
297 298
     description = forms.CharField(
298 299
         max_length=80, label=_("Description"), required=False)
299
-    network_service_params = fields.CustomMultiChoiceField(label=_(
300
-        "Network Service Parameters"), add_item_link=NETWORK_PARAM_URL,
301
-        required=False)
300
+    network_service_params = fields.TransferTableField(
301
+        label=_("Network Service Parameters"),
302
+        add_item_link=NETWORK_PARAM_URL,
303
+        required=False
304
+    )
302 305
     shared = forms.BooleanField(label=_("Shared"),
303 306
                                 initial=False, required=False)
304 307
 
@@ -555,9 +558,11 @@ class CreateExternalConnectivityForm(forms.SelfHandlingForm):
555 558
                                         "(e.g. 192.168.0.0/24,"
556 559
                                         "2001:DB8::/48)"),
557 560
                             version=forms.IPv4 | forms.IPv6, mask=True)
558
-    external_routes = fields.CustomMultiChoiceField(
559
-        label=_("External Routes"), add_item_link=ROUTE_URL,
560
-        required=False)
561
+    external_routes = fields.TransferTableField(
562
+        label=_("External Routes"),
563
+        add_item_link=ROUTE_URL,
564
+        required=False
565
+    )
561 566
     subnet_id = forms.ChoiceField(label=_("Subnet ID"), required=False)
562 567
     port_address_translation = forms.BooleanField(
563 568
         label=_("Port Address Translation"),

+ 9
- 6
gbpui/panels/policytargets/forms.py View File

@@ -22,6 +22,7 @@ from horizon import forms
22 22
 from horizon import messages
23 23
 
24 24
 from gbpui import client
25
+from gbpui import fields
25 26
 
26 27
 LOG = logging.getLogger(__name__)
27 28
 
@@ -31,10 +32,12 @@ class UpdatePolicyTargetForm(forms.SelfHandlingForm):
31 32
                            label=_("Name"), required=False)
32 33
     description = forms.CharField(max_length=80,
33 34
                                   label=_("Description"), required=False)
34
-    provided_policy_rule_sets = forms.MultipleChoiceField(
35
-        label=_("Provided Policy Rule Set"), required=False)
36
-    consumed_policy_rule_sets = forms.MultipleChoiceField(
37
-        label=_("Consumed Policy Rule Set"), required=False)
35
+    provided_policy_rule_sets = fields.TransferTableField(
36
+        label=_("Provided Policy Rule Set"), required=False
37
+    )
38
+    consumed_policy_rule_sets = fields.TransferTableField(
39
+        label=_("Consumed Policy Rule Set"), required=False
40
+    )
38 41
     l2_policy_id = forms.ChoiceField(
39 42
         label=_("Network Policy"),
40 43
         required=False,
@@ -141,9 +144,9 @@ class UpdateExternalPolicyTargetForm(forms.SelfHandlingForm):
141 144
                            label=_("Name"), required=False)
142 145
     description = forms.CharField(max_length=80,
143 146
                                   label=_("Description"), required=False)
144
-    provided_policy_rule_sets = forms.MultipleChoiceField(
147
+    provided_policy_rule_sets = fields.TransferTableField(
145 148
         label=_("Provided Policy Rule Set"), required=False)
146
-    consumed_policy_rule_sets = forms.MultipleChoiceField(
149
+    consumed_policy_rule_sets = fields.TransferTableField(
147 150
         label=_("Consumed Policy Rule Set"), required=False)
148 151
     external_segments = forms.MultipleChoiceField(
149 152
         label=_("External Connectivity"), required=True,

+ 9
- 12
gbpui/panels/policytargets/workflows.py View File

@@ -48,12 +48,12 @@ ADD_EXTERNAL_CONNECTIVITY = \
48 48
 
49 49
 
50 50
 class SelectPolicyRuleSetAction(workflows.Action):
51
-    provided_policy_rule_set = fields.DynamicMultiChoiceField(
51
+    provided_policy_rule_set = fields.TransferTableField(
52 52
         label=_("Provided Policy Rule Set"),
53 53
         help_text=_("Choose a policy rule set for an Group."),
54 54
         add_item_link=POLICY_RULE_SET_URL,
55 55
         required=False)
56
-    consumed_policy_rule_set = fields.DynamicMultiChoiceField(
56
+    consumed_policy_rule_set = fields.TransferTableField(
57 57
         label=_("Consumed Policy Rule Set"),
58 58
         help_text=_("Select consumed policy rule set for Group."),
59 59
         add_item_link=POLICY_RULE_SET_URL,
@@ -64,8 +64,10 @@ class SelectPolicyRuleSetAction(workflows.Action):
64 64
         help_text = _("Select Policy Rule Set for Group.")
65 65
 
66 66
     def _policy_rule_set_list(self, request):
67
-        policy_rule_sets = client.policy_rule_set_list(request,
68
-            tenant_id=request.user.tenant_id)
67
+        policy_rule_sets = client.policy_rule_set_list(
68
+            request,
69
+            tenant_id=request.user.tenant_id
70
+        )
69 71
         for c in policy_rule_sets:
70 72
             c.set_id_as_name_if_empty()
71 73
         policy_rule_sets = sorted(policy_rule_sets,
@@ -75,12 +77,8 @@ class SelectPolicyRuleSetAction(workflows.Action):
75 77
     def populate_provided_policy_rule_set_choices(self, request, context):
76 78
         policy_rule_set_list = []
77 79
         try:
78
-            rsets = self._policy_rule_set_list(request)
79
-            if len(rsets) == 0:
80
-                rsets.extend([('None', 'No Provided Policy Rule Sets')])
81
-            policy_rule_set_list = rsets
80
+            policy_rule_set_list = self._policy_rule_set_list(request)
82 81
         except Exception as e:
83
-            policy_rule_set_list = []
84 82
             msg = _('Unable to retrieve policy rule set. %s.') % (str(e))
85 83
             exceptions.handle(request, msg)
86 84
         return policy_rule_set_list
@@ -88,9 +86,7 @@ class SelectPolicyRuleSetAction(workflows.Action):
88 86
     def populate_consumed_policy_rule_set_choices(self, request, context):
89 87
         policy_rule_set_list = []
90 88
         try:
91
-            policy_rule_set_list = [('None', 'No Consumed Policy Rule Sets')]
92
-            policy_rule_set_list =\
93
-                self._policy_rule_set_list(request)
89
+            policy_rule_set_list = self._policy_rule_set_list(request)
94 90
         except Exception as e:
95 91
             msg = _('Unable to retrieve policy rule set. %s.') % (str(e))
96 92
             exceptions.handle(request, msg)
@@ -342,6 +338,7 @@ class SetAccessControlsAction(workflows.Action):
342 338
                                        help_text=_("Key pair to use for "
343 339
                                                    "authentication."),
344 340
                                        add_item_link=KEYPAIR_IMPORT_URL)
341
+
345 342
     admin_pass = forms.RegexField(
346 343
         label=_("Admin Password"),
347 344
         required=False,

+ 25
- 0
gbpui/static/dashboard/gbpui/gbpui.module.js View File

@@ -0,0 +1,25 @@
1
+/**
2
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
3
+ * not use this file except in compliance with the License. You may obtain
4
+ * a copy of the License at
5
+ *
6
+ *    http://www.apache.org/licenses/LICENSE-2.0
7
+ *
8
+ * Unless required by applicable law or agreed to in writing, software
9
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
+ * License for the specific language governing permissions and limitations
12
+ * under the License.
13
+ */
14
+(function () {
15
+    angular
16
+        .module('gbpui', ['gbpui.transfer-table-bridge'])
17
+        .config(module_config);
18
+
19
+    module_config.$inject = ["$provide","$windowProvider"];
20
+
21
+    function module_config($provide, $windowProvider) {
22
+        var path = $windowProvider.$get().STATIC_URL + 'dashboard/gbpui/';
23
+        $provide.constant('gbpui.basePath', path);
24
+    }
25
+})();

+ 90
- 0
gbpui/static/dashboard/gbpui/transfer-table-bridge/d-select.directive.js View File

@@ -0,0 +1,90 @@
1
+/**
2
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
3
+ * not use this file except in compliance with the License. You may obtain
4
+ * a copy of the License at
5
+ *
6
+ *    http://www.apache.org/licenses/LICENSE-2.0
7
+ *
8
+ * Unless required by applicable law or agreed to in writing, software
9
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
+ * License for the specific language governing permissions and limitations
12
+ * under the License.
13
+ */
14
+(function () {
15
+    angular
16
+        .module('gbpui.transfer-table-bridge')
17
+        .directive('dSelect', function () {
18
+            return {
19
+                restrict: 'A',
20
+                scope: true,
21
+                link: function ($scope, $elem, $attrs, $ctrl) {
22
+
23
+
24
+                    $.each($scope.tableData.available, function (index, optionObject) {
25
+                        var option = $("<option></option>");
26
+                        option.attr("value", optionObject.id);
27
+                        option.append(optionObject.name);
28
+                        $elem.append(option);
29
+                    });
30
+
31
+                    // This change listener watches for changes to the raw
32
+                    // HTML select element; since the select should be hidden,
33
+                    // the only possible change is the creation of a new
34
+                    // option using the horizon add button.
35
+                    $elem.change(function () {
36
+                        // Find the last option in the select, since the
37
+                        // addition is done by Horizon appending the a new
38
+                        // option element
39
+                        var option = $(this).find("option").last();
40
+
41
+                        // Create a valid option object and make it available
42
+                        // at the end of the available list
43
+                        var val = {
44
+                            'id': option.attr('value'),
45
+                            'name': option.text()
46
+                        };
47
+                        $scope.tableData.available.push(val);
48
+
49
+                        // Deallocate all the objects using the built in
50
+                        // transfer table controller deallocation method
51
+                        var toDeallocate = $scope.tableData.allocated.slice();
52
+                        $.each(toDeallocate, function (index, object) {
53
+                            $scope.trCtrl.deallocate(object);
54
+                        });
55
+                        // Notify the scope of the deallocations
56
+                        $scope.$apply();
57
+
58
+                        // Allocate the new option; this mimicks te behaviour
59
+                        // of the normal Horizon based adding
60
+                        $scope.trCtrl.allocate(val);
61
+
62
+                        // Notify the scope of the allocation changes
63
+                        $scope.$apply();
64
+                    });
65
+
66
+                    // The directive watches for a changes in the allocated
67
+                    // list to dynamically set values for the hidden element.
68
+                    $scope.$watchCollection(
69
+                        function (scope) {
70
+                            return $scope.tableData.allocated;
71
+                        },
72
+                        function (newValue, oldValue) {
73
+                            var values = $.map(
74
+                                newValue, function (value, index) {
75
+                                    return value.id;
76
+                                });
77
+                            $elem.val(values);
78
+                        }
79
+                    );
80
+
81
+                    // Sets initial values as allocated when appropriate
82
+                    $.each($scope.initial, function (index, initialObject) {
83
+                        $scope.trCtrl.allocate(initialObject);
84
+                    });
85
+
86
+
87
+                }
88
+            }
89
+        });
90
+})();

+ 91
- 0
gbpui/static/dashboard/gbpui/transfer-table-bridge/d-table.directive.js View File

@@ -0,0 +1,91 @@
1
+/**
2
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
3
+ * not use this file except in compliance with the License. You may obtain
4
+ * a copy of the License at
5
+ *
6
+ *    http://www.apache.org/licenses/LICENSE-2.0
7
+ *
8
+ * Unless required by applicable law or agreed to in writing, software
9
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
+ * License for the specific language governing permissions and limitations
12
+ * under the License.
13
+ */
14
+(function () {
15
+    angular
16
+        .module('gbpui.transfer-table-bridge')
17
+        .directive('dTable', ['gbpui.basePath', function(basePath){
18
+            return {
19
+                restrict: 'E',
20
+                scope: true,
21
+                templateUrl: basePath +
22
+                    "transfer-table-bridge/transfer-table-bridge.html",
23
+                transclude: true,
24
+                link: function($scope, $elem, $attrs, $ctrl, $transclude) {
25
+
26
+                    var initial = [];
27
+                    var available = [];
28
+
29
+                    var transcluded = $transclude();
30
+
31
+                    transcluded.each(function(index, element) {
32
+                        if(element.localName=="option") {
33
+                            var val = {
34
+                                'id': $(element).attr('value'),
35
+                                'name': $(element).text()
36
+                            };
37
+                            available.push(val);
38
+
39
+                            if($(element).prop('selected')) {
40
+                                initial.push(val);
41
+                            }
42
+                        }
43
+                    });
44
+                    $scope.initial = initial;
45
+
46
+                    var allocated = [];
47
+
48
+                    $scope.tableData = {
49
+                        available: available,
50
+                        allocated: allocated,
51
+                        displayedAvailable: [],
52
+                        displayedAllocated: [],
53
+                        minItems: -1
54
+                    };
55
+
56
+                    var maxAllocation =  "maxItems" in $attrs
57
+                        ? Number($attrs["maxItems"])
58
+                        : -1;
59
+                    $scope.tableLimits = {
60
+                        maxAllocation: maxAllocation
61
+                    };
62
+
63
+                    $scope.tableHelpText = {
64
+                        allocHelpText: $attrs['allocatedHelpText'],
65
+                        availHelpText: $attrs['availableHelpText'],
66
+                        noAllocText: $attrs['noAllocatedText'],
67
+                        noAvailText: $attrs['noAvailableText']
68
+                    };
69
+
70
+                    $scope.facets = [{
71
+                            label: gettext("Name"),
72
+                            name: "name",
73
+                            singleton: true
74
+                    }];
75
+
76
+                    if("addItemLink" in $attrs) {
77
+                        $scope.addItemLink = $attrs["addItemLink"];
78
+                    }
79
+
80
+                    if("allocatedFilter" in $attrs) {
81
+                        $scope.allocatedFilter = true;
82
+                    }
83
+
84
+                    $scope.id = $attrs["id"];
85
+                    $scope.name = $attrs["name"];
86
+
87
+                }
88
+            }
89
+        }])
90
+
91
+})();

+ 114
- 0
gbpui/static/dashboard/gbpui/transfer-table-bridge/transfer-table-bridge.html View File

@@ -0,0 +1,114 @@
1
+<transfer-table tr-model="tableData" limits="tableLimits" help-text="tableHelpText">
2
+
3
+    <allocated ng-model="tableData.allocated.length">
4
+        <table  st-table="tableData.displayedAllocated"
5
+                st-safe-src="tableData.allocated"
6
+                hz-table
7
+                class="table table-striped table-rsp table-detail table-condensed">
8
+            <thead>
9
+                <tr ng-if="allocatedFilter">
10
+                    <th colspan="2">
11
+                        <hz-magic-search-bar filter-facets="facets"></hz-magic-search-bar>
12
+                    </th>
13
+                </tr>
14
+                <tr>
15
+                    <th colspan="2">Name</th>
16
+                </tr>
17
+            </thead>
18
+            <tbody>
19
+                <tr ng-if="tableData.allocated.length === 0">
20
+                    <td colspan="{{ addItemLink ? 2 : 1 }}">
21
+                         <div class="no-rows-help">
22
+                             {$ ::tableHelpText.noAllocText $}
23
+                         </div>
24
+                    </td>
25
+                </tr>
26
+                <tr ng-repeat="row in tableData.displayedAllocated track by row.id">
27
+                    <td>
28
+                        {$ row.name $}
29
+                    </td>
30
+                    <td class="actions_column">
31
+                        <action-list>
32
+                            <button tabIndex="0"
33
+                                    ng-class="'btn btn-default'"
34
+                                    ng-click="trCtrl.deallocate(row)"
35
+                                    type="button">
36
+                                <span class="fa fa-arrow-down"></span>
37
+                            </button>
38
+                        </action-list>
39
+                    </td>
40
+                </tr>
41
+            </tbody>
42
+        </table>
43
+    </allocated>
44
+
45
+    <available>
46
+        <table
47
+               st-table="tableData.displayedAvailable"
48
+               st-safe-src="tableData.available"
49
+               hz-table
50
+               class="table table-striped table-rsp table-detail table-condensed">
51
+            <thead>
52
+                <tr>
53
+                    <th colspan="{$ addItemLink ? 1 : 2 $}">
54
+                        <hz-magic-search-bar filter-facets="facets"></hz-magic-search-bar>
55
+                    </th>
56
+                    <th ng-if="addItemLink">
57
+                        <span class="input-group-btn">
58
+                            <a href="{$ addItemLink $}" data-add-to-field="{$ id $}_select" class="btn btn-default ajax-add ajax-modal">
59
+                                <span class="fa fa-plus"></span>
60
+                            </a>
61
+                        </span>
62
+                    </th>
63
+                </tr>
64
+                <tr>
65
+                    <th colspan="2">Name</th>
66
+                </tr>
67
+            </thead>
68
+            <tbody>
69
+
70
+                <tr ng-if="trCtrl.numAvailable() === 0">
71
+                    <td colspan="{{ addItemLink ? 2 : 1 }}">
72
+                        <div class="no-rows-help">
73
+                             {$ ::tableHelpText.noAvailText $}
74
+                        </div>
75
+                    </td>
76
+                </tr>
77
+
78
+                <tr ng-repeat="row in tableData.displayedAvailable track by row.id"
79
+                    ng-if="!trCtrl.allocatedIds[row.id]"
80
+                >
81
+                    <td>{$ row.name $}</td>
82
+                    <td class="actions_column">
83
+                        <action-list button-tooltip="row.warningMessage"
84
+                                     bt-model="ctrl.tooltipModel"
85
+                                     bt-disabled="!row.disabled"
86
+                                     warning-classes="'invalid'">
87
+                            <notifications>
88
+                                <span class="fa fa-exclamation-circle invalid"
89
+                                      ng-show="row.disabled"></span>
90
+                            </notifications>
91
+                            <button tabIndex="0"
92
+                                    ng-class="'btn btn-default'"
93
+                                    ng-click="trCtrl.allocate(row)"
94
+                                    type="button">
95
+                                <span class="fa fa-arrow-up"></span>
96
+                            </button>
97
+                        </action-list>
98
+                    </td>
99
+                </tr>
100
+            </tbody>
101
+        </table>
102
+
103
+        <div style="display:None">
104
+            <select
105
+                    d-select
106
+                    id="{$ id $}_select"
107
+                    data-add-item-url
108
+                    multiple
109
+                    name="{$ name $}" >
110
+            </select>
111
+        </div>
112
+    </available>
113
+
114
+</transfer-table>

+ 17
- 0
gbpui/static/dashboard/gbpui/transfer-table-bridge/transfer-table.module.js View File

@@ -0,0 +1,17 @@
1
+/**
2
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
3
+ * not use this file except in compliance with the License. You may obtain
4
+ * a copy of the License at
5
+ *
6
+ *    http://www.apache.org/licenses/LICENSE-2.0
7
+ *
8
+ * Unless required by applicable law or agreed to in writing, software
9
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
+ * License for the specific language governing permissions and limitations
12
+ * under the License.
13
+ */
14
+(function () {
15
+    angular
16
+        .module('gbpui.transfer-table-bridge', ['horizon.app.core.workflow']);
17
+})();

Loading…
Cancel
Save