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
This commit is contained in:
parent
f14de1c22a
commit
cf76c87cc7
|
@ -14,3 +14,6 @@ ADD_INSTALLED_APPS = ['gbpui', ]
|
|||
PANEL_GROUP = 'GroupPolicyPanels'
|
||||
PANEL_GROUP_NAME = 'Policy'
|
||||
PANEL_GROUP_DASHBOARD = 'project'
|
||||
|
||||
AUTO_DISCOVER_STATIC_FILES = True
|
||||
ADD_ANGULAR_MODULES = ['gbpui', ]
|
||||
|
|
|
@ -16,6 +16,11 @@ from django.forms import TextInput
|
|||
from django.forms import widgets
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from django.forms.utils import flatatt
|
||||
from django.utils.html import format_html
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class DynamicMultiSelectWidget(widgets.SelectMultiple):
|
||||
|
||||
|
@ -84,3 +89,78 @@ class DropdownEditWidget(TextInput):
|
|||
data_list += '<option value="%s">' % item
|
||||
data_list += '</datalist>'
|
||||
return mark_safe(text_html + data_list)
|
||||
|
||||
|
||||
class TransferTableWidget(widgets.SelectMultiple):
|
||||
actions_list = []
|
||||
add_item_link = None
|
||||
max_items = None
|
||||
allocated_filter = False
|
||||
|
||||
allocated_help_text = None
|
||||
available_help_text = None
|
||||
no_allocated_text = None
|
||||
no_available_text = None
|
||||
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
# css class currently breaks the layout for some reason,
|
||||
self.attrs.pop('class', None)
|
||||
|
||||
final_attrs = self.build_attrs(attrs, name=name)
|
||||
|
||||
selected = [] if value is None else value
|
||||
|
||||
options = self.render_options(choices, selected)
|
||||
|
||||
if self.add_item_link is not None:
|
||||
final_attrs['add_item_link'] = urlresolvers.reverse(
|
||||
self.add_item_link
|
||||
)
|
||||
|
||||
if self.max_items is not None:
|
||||
final_attrs['max_items'] = self.max_items
|
||||
|
||||
if self.allocated_filter:
|
||||
final_attrs['allocated_filter'] = "True"
|
||||
|
||||
final_attrs['allocated_help_text'] = self.allocated_help_text
|
||||
final_attrs['available_help_text'] = self.available_help_text
|
||||
final_attrs['no_allocated_text'] = self.no_allocated_text
|
||||
final_attrs['no_available_text'] = self.no_available_text
|
||||
|
||||
open_tag = format_html('<d-table {}>', flatatt(final_attrs))
|
||||
|
||||
output = [open_tag, options, '</d-table>']
|
||||
|
||||
return mark_safe('\n'.join(output))
|
||||
|
||||
# ...this adds the 'add item button' just by existing and returning a
|
||||
# true-y value
|
||||
def get_add_item_url(self):
|
||||
return None
|
||||
|
||||
|
||||
class TransferTableField(fields.MultipleChoiceField):
|
||||
widget = TransferTableWidget
|
||||
|
||||
def __init__(self, add_item_link=None, max_items=-1,
|
||||
allocated_filter=False,
|
||||
allocated_help_text="",
|
||||
available_help_text="",
|
||||
no_allocated_text=_("Select items from bellow"),
|
||||
no_available_text=_("No available items"),
|
||||
*args, **kwargs):
|
||||
super(TransferTableField, self).__init__(*args, **kwargs)
|
||||
|
||||
self.widget.add_item_link = add_item_link
|
||||
self.widget.max_items = max_items
|
||||
self.widget.allocated_filter = allocated_filter
|
||||
|
||||
self.widget.allocated_help_text = allocated_help_text
|
||||
self.widget.available_help_text = available_help_text
|
||||
|
||||
self.widget.no_allocated_text = no_allocated_text
|
||||
self.widget.no_available_text = no_available_text
|
||||
|
||||
def validate(self, *args, **kwargs):
|
||||
return True
|
||||
|
|
|
@ -55,7 +55,7 @@ class BaseUpdateForm(forms.SelfHandlingForm):
|
|||
class UpdatePolicyRuleSetForm(BaseUpdateForm):
|
||||
name = forms.CharField(label=_("Name"))
|
||||
description = forms.CharField(label=_("Description"), required=False)
|
||||
policy_rules = forms.MultipleChoiceField(label=_("Policy Rules"),)
|
||||
policy_rules = fields.TransferTableField(label=_("Policy Rules"), )
|
||||
shared = forms.BooleanField(label=_("Shared"), required=False)
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
|
@ -298,7 +298,11 @@ class UpdatePolicyRuleForm(BaseUpdateForm):
|
|||
name = forms.CharField(max_length=80, label=_("Name"), required=False)
|
||||
description = forms.CharField(label=_("Description"), required=False)
|
||||
policy_classifier_id = forms.ChoiceField(label=_("Policy Classifier"))
|
||||
policy_actions = forms.MultipleChoiceField(label=_("Policy Actions"))
|
||||
policy_actions = fields.TransferTableField(
|
||||
label=_("Policy Actions"),
|
||||
required=False,
|
||||
)
|
||||
|
||||
shared = forms.BooleanField(label=_("Shared"), required=False)
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
|
@ -306,17 +310,19 @@ class UpdatePolicyRuleForm(BaseUpdateForm):
|
|||
try:
|
||||
policyrule_id = self.initial['policyrule_id']
|
||||
rule = client.policyrule_get(request, policyrule_id)
|
||||
|
||||
for item in ['name', 'description',
|
||||
'policy_classifier_id', 'policy_actions', 'shared']:
|
||||
self.fields[item].initial = getattr(rule, item)
|
||||
|
||||
actions = client.policyaction_list(request,
|
||||
tenant_id=request.user.tenant_id)
|
||||
action_list = [a.id for a in actions]
|
||||
for action in actions:
|
||||
action.set_id_as_name_if_empty()
|
||||
actions = sorted(actions, key=lambda action: action.name)
|
||||
action_list = [(a.id, a.name) for a in actions]
|
||||
self.fields['policy_actions'].choices = action_list
|
||||
|
||||
classifiers = client.policyclassifier_list(request,
|
||||
tenant_id=request.user.tenant_id)
|
||||
classifier_list = [(c.id, c.name) for c in classifiers]
|
||||
|
|
|
@ -21,6 +21,7 @@ from horizon import workflows
|
|||
from gbpui import client
|
||||
from gbpui import fields
|
||||
|
||||
|
||||
ADD_POLICY_ACTION_URL = "horizon:project:application_policy:addpolicyaction"
|
||||
ADD_POLICY_CLASSIFIER_URL = "horizon:project:application_policy:"
|
||||
ADD_POLICY_CLASSIFIER_URL = ADD_POLICY_CLASSIFIER_URL + "addpolicyclassifier"
|
||||
|
@ -28,11 +29,12 @@ ADD_POLICY_RULE_URL = "horizon:project:application_policy:addpolicyrule"
|
|||
|
||||
|
||||
class SelectPolicyRuleAction(workflows.Action):
|
||||
policy_rules = fields.DynamicMultiChoiceField(
|
||||
policy_rules = fields.TransferTableField(
|
||||
label=_("Policy Rules"),
|
||||
required=False,
|
||||
add_item_link=ADD_POLICY_RULE_URL,
|
||||
help_text=_("Create a policy rule set with selected rules."))
|
||||
help_text=_("Create a policy rule set with selected rules.")
|
||||
)
|
||||
|
||||
class Meta(object):
|
||||
name = _("Rules")
|
||||
|
@ -162,11 +164,17 @@ class SelectPolicyClassifierAction(workflows.Action):
|
|||
|
||||
|
||||
class SelectPolicyActionAction(workflows.Action):
|
||||
actions = fields.DynamicMultiChoiceField(
|
||||
actions = fields.TransferTableField(
|
||||
label=_("Policy Action"),
|
||||
required=False,
|
||||
help_text=_("Create a policy-rule with selected action."),
|
||||
add_item_link=ADD_POLICY_ACTION_URL)
|
||||
add_item_link=ADD_POLICY_ACTION_URL,
|
||||
help_text=_("Create a policy-rule with selected action.")
|
||||
)
|
||||
|
||||
def __init__(self, request, context, *args, **kwargs):
|
||||
super(SelectPolicyActionAction, self).__init__(
|
||||
request, context, *args, **kwargs
|
||||
)
|
||||
|
||||
class Meta(object):
|
||||
name = _("actions")
|
||||
|
@ -176,14 +184,11 @@ class SelectPolicyActionAction(workflows.Action):
|
|||
try:
|
||||
actions = client.policyaction_list(request,
|
||||
tenant_id=request.user.tenant_id)
|
||||
action_list = [a.id for a in actions]
|
||||
for action in actions:
|
||||
action.set_id_as_name_if_empty()
|
||||
actions = sorted(actions,
|
||||
key=lambda action: action.name)
|
||||
action_list = [(a.id, a.name) for a in actions]
|
||||
if len(action_list) > 0:
|
||||
self.fields['actions'].initial = action_list[0]
|
||||
except Exception as e:
|
||||
action_list = []
|
||||
exceptions.handle(request,
|
||||
|
|
|
@ -63,10 +63,11 @@ class AddL3PolicyForm(forms.SelfHandlingForm):
|
|||
label=_("Subnet Prefix Length"),
|
||||
help_text=_("Between 2 - 30 for IP4"
|
||||
"and 2-127 for IP6."),)
|
||||
external_segments = \
|
||||
fields.CustomMultiChoiceField(label=_("External Segments"),
|
||||
add_item_link=EXT_SEG_PARAM_URL,
|
||||
required=False)
|
||||
external_segments = fields.TransferTableField(
|
||||
label=_("External Segments"),
|
||||
add_item_link=EXT_SEG_PARAM_URL,
|
||||
required=False
|
||||
)
|
||||
shared = forms.BooleanField(label=_("Shared"),
|
||||
initial=False,
|
||||
required=False)
|
||||
|
@ -296,9 +297,11 @@ class CreateServicePolicyForm(forms.SelfHandlingForm):
|
|||
name = forms.CharField(max_length=80, label=_("Name"))
|
||||
description = forms.CharField(
|
||||
max_length=80, label=_("Description"), required=False)
|
||||
network_service_params = fields.CustomMultiChoiceField(label=_(
|
||||
"Network Service Parameters"), add_item_link=NETWORK_PARAM_URL,
|
||||
required=False)
|
||||
network_service_params = fields.TransferTableField(
|
||||
label=_("Network Service Parameters"),
|
||||
add_item_link=NETWORK_PARAM_URL,
|
||||
required=False
|
||||
)
|
||||
shared = forms.BooleanField(label=_("Shared"),
|
||||
initial=False, required=False)
|
||||
|
||||
|
@ -555,9 +558,11 @@ class CreateExternalConnectivityForm(forms.SelfHandlingForm):
|
|||
"(e.g. 192.168.0.0/24,"
|
||||
"2001:DB8::/48)"),
|
||||
version=forms.IPv4 | forms.IPv6, mask=True)
|
||||
external_routes = fields.CustomMultiChoiceField(
|
||||
label=_("External Routes"), add_item_link=ROUTE_URL,
|
||||
required=False)
|
||||
external_routes = fields.TransferTableField(
|
||||
label=_("External Routes"),
|
||||
add_item_link=ROUTE_URL,
|
||||
required=False
|
||||
)
|
||||
subnet_id = forms.ChoiceField(label=_("Subnet ID"), required=False)
|
||||
port_address_translation = forms.BooleanField(
|
||||
label=_("Port Address Translation"),
|
||||
|
|
|
@ -22,6 +22,7 @@ from horizon import forms
|
|||
from horizon import messages
|
||||
|
||||
from gbpui import client
|
||||
from gbpui import fields
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -31,10 +32,12 @@ class UpdatePolicyTargetForm(forms.SelfHandlingForm):
|
|||
label=_("Name"), required=False)
|
||||
description = forms.CharField(max_length=80,
|
||||
label=_("Description"), required=False)
|
||||
provided_policy_rule_sets = forms.MultipleChoiceField(
|
||||
label=_("Provided Policy Rule Set"), required=False)
|
||||
consumed_policy_rule_sets = forms.MultipleChoiceField(
|
||||
label=_("Consumed Policy Rule Set"), required=False)
|
||||
provided_policy_rule_sets = fields.TransferTableField(
|
||||
label=_("Provided Policy Rule Set"), required=False
|
||||
)
|
||||
consumed_policy_rule_sets = fields.TransferTableField(
|
||||
label=_("Consumed Policy Rule Set"), required=False
|
||||
)
|
||||
l2_policy_id = forms.ChoiceField(
|
||||
label=_("Network Policy"),
|
||||
required=False,
|
||||
|
@ -141,9 +144,9 @@ class UpdateExternalPolicyTargetForm(forms.SelfHandlingForm):
|
|||
label=_("Name"), required=False)
|
||||
description = forms.CharField(max_length=80,
|
||||
label=_("Description"), required=False)
|
||||
provided_policy_rule_sets = forms.MultipleChoiceField(
|
||||
provided_policy_rule_sets = fields.TransferTableField(
|
||||
label=_("Provided Policy Rule Set"), required=False)
|
||||
consumed_policy_rule_sets = forms.MultipleChoiceField(
|
||||
consumed_policy_rule_sets = fields.TransferTableField(
|
||||
label=_("Consumed Policy Rule Set"), required=False)
|
||||
external_segments = forms.MultipleChoiceField(
|
||||
label=_("External Connectivity"), required=True,
|
||||
|
|
|
@ -48,12 +48,12 @@ ADD_EXTERNAL_CONNECTIVITY = \
|
|||
|
||||
|
||||
class SelectPolicyRuleSetAction(workflows.Action):
|
||||
provided_policy_rule_set = fields.DynamicMultiChoiceField(
|
||||
provided_policy_rule_set = fields.TransferTableField(
|
||||
label=_("Provided Policy Rule Set"),
|
||||
help_text=_("Choose a policy rule set for an Group."),
|
||||
add_item_link=POLICY_RULE_SET_URL,
|
||||
required=False)
|
||||
consumed_policy_rule_set = fields.DynamicMultiChoiceField(
|
||||
consumed_policy_rule_set = fields.TransferTableField(
|
||||
label=_("Consumed Policy Rule Set"),
|
||||
help_text=_("Select consumed policy rule set for Group."),
|
||||
add_item_link=POLICY_RULE_SET_URL,
|
||||
|
@ -64,8 +64,10 @@ class SelectPolicyRuleSetAction(workflows.Action):
|
|||
help_text = _("Select Policy Rule Set for Group.")
|
||||
|
||||
def _policy_rule_set_list(self, request):
|
||||
policy_rule_sets = client.policy_rule_set_list(request,
|
||||
tenant_id=request.user.tenant_id)
|
||||
policy_rule_sets = client.policy_rule_set_list(
|
||||
request,
|
||||
tenant_id=request.user.tenant_id
|
||||
)
|
||||
for c in policy_rule_sets:
|
||||
c.set_id_as_name_if_empty()
|
||||
policy_rule_sets = sorted(policy_rule_sets,
|
||||
|
@ -75,12 +77,8 @@ class SelectPolicyRuleSetAction(workflows.Action):
|
|||
def populate_provided_policy_rule_set_choices(self, request, context):
|
||||
policy_rule_set_list = []
|
||||
try:
|
||||
rsets = self._policy_rule_set_list(request)
|
||||
if len(rsets) == 0:
|
||||
rsets.extend([('None', 'No Provided Policy Rule Sets')])
|
||||
policy_rule_set_list = rsets
|
||||
policy_rule_set_list = self._policy_rule_set_list(request)
|
||||
except Exception as e:
|
||||
policy_rule_set_list = []
|
||||
msg = _('Unable to retrieve policy rule set. %s.') % (str(e))
|
||||
exceptions.handle(request, msg)
|
||||
return policy_rule_set_list
|
||||
|
@ -88,9 +86,7 @@ class SelectPolicyRuleSetAction(workflows.Action):
|
|||
def populate_consumed_policy_rule_set_choices(self, request, context):
|
||||
policy_rule_set_list = []
|
||||
try:
|
||||
policy_rule_set_list = [('None', 'No Consumed Policy Rule Sets')]
|
||||
policy_rule_set_list =\
|
||||
self._policy_rule_set_list(request)
|
||||
policy_rule_set_list = self._policy_rule_set_list(request)
|
||||
except Exception as e:
|
||||
msg = _('Unable to retrieve policy rule set. %s.') % (str(e))
|
||||
exceptions.handle(request, msg)
|
||||
|
@ -342,6 +338,7 @@ class SetAccessControlsAction(workflows.Action):
|
|||
help_text=_("Key pair to use for "
|
||||
"authentication."),
|
||||
add_item_link=KEYPAIR_IMPORT_URL)
|
||||
|
||||
admin_pass = forms.RegexField(
|
||||
label=_("Admin Password"),
|
||||
required=False,
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License. You may obtain
|
||||
* a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
(function () {
|
||||
angular
|
||||
.module('gbpui', ['gbpui.transfer-table-bridge'])
|
||||
.config(module_config);
|
||||
|
||||
module_config.$inject = ["$provide","$windowProvider"];
|
||||
|
||||
function module_config($provide, $windowProvider) {
|
||||
var path = $windowProvider.$get().STATIC_URL + 'dashboard/gbpui/';
|
||||
$provide.constant('gbpui.basePath', path);
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License. You may obtain
|
||||
* a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
(function () {
|
||||
angular
|
||||
.module('gbpui.transfer-table-bridge')
|
||||
.directive('dSelect', function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: true,
|
||||
link: function ($scope, $elem, $attrs, $ctrl) {
|
||||
|
||||
|
||||
$.each($scope.tableData.available, function (index, optionObject) {
|
||||
var option = $("<option></option>");
|
||||
option.attr("value", optionObject.id);
|
||||
option.append(optionObject.name);
|
||||
$elem.append(option);
|
||||
});
|
||||
|
||||
// This change listener watches for changes to the raw
|
||||
// HTML select element; since the select should be hidden,
|
||||
// the only possible change is the creation of a new
|
||||
// option using the horizon add button.
|
||||
$elem.change(function () {
|
||||
// Find the last option in the select, since the
|
||||
// addition is done by Horizon appending the a new
|
||||
// option element
|
||||
var option = $(this).find("option").last();
|
||||
|
||||
// Create a valid option object and make it available
|
||||
// at the end of the available list
|
||||
var val = {
|
||||
'id': option.attr('value'),
|
||||
'name': option.text()
|
||||
};
|
||||
$scope.tableData.available.push(val);
|
||||
|
||||
// Deallocate all the objects using the built in
|
||||
// transfer table controller deallocation method
|
||||
var toDeallocate = $scope.tableData.allocated.slice();
|
||||
$.each(toDeallocate, function (index, object) {
|
||||
$scope.trCtrl.deallocate(object);
|
||||
});
|
||||
// Notify the scope of the deallocations
|
||||
$scope.$apply();
|
||||
|
||||
// Allocate the new option; this mimicks te behaviour
|
||||
// of the normal Horizon based adding
|
||||
$scope.trCtrl.allocate(val);
|
||||
|
||||
// Notify the scope of the allocation changes
|
||||
$scope.$apply();
|
||||
});
|
||||
|
||||
// The directive watches for a changes in the allocated
|
||||
// list to dynamically set values for the hidden element.
|
||||
$scope.$watchCollection(
|
||||
function (scope) {
|
||||
return $scope.tableData.allocated;
|
||||
},
|
||||
function (newValue, oldValue) {
|
||||
var values = $.map(
|
||||
newValue, function (value, index) {
|
||||
return value.id;
|
||||
});
|
||||
$elem.val(values);
|
||||
}
|
||||
);
|
||||
|
||||
// Sets initial values as allocated when appropriate
|
||||
$.each($scope.initial, function (index, initialObject) {
|
||||
$scope.trCtrl.allocate(initialObject);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License. You may obtain
|
||||
* a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
(function () {
|
||||
angular
|
||||
.module('gbpui.transfer-table-bridge')
|
||||
.directive('dTable', ['gbpui.basePath', function(basePath){
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: true,
|
||||
templateUrl: basePath +
|
||||
"transfer-table-bridge/transfer-table-bridge.html",
|
||||
transclude: true,
|
||||
link: function($scope, $elem, $attrs, $ctrl, $transclude) {
|
||||
|
||||
var initial = [];
|
||||
var available = [];
|
||||
|
||||
var transcluded = $transclude();
|
||||
|
||||
transcluded.each(function(index, element) {
|
||||
if(element.localName=="option") {
|
||||
var val = {
|
||||
'id': $(element).attr('value'),
|
||||
'name': $(element).text()
|
||||
};
|
||||
available.push(val);
|
||||
|
||||
if($(element).prop('selected')) {
|
||||
initial.push(val);
|
||||
}
|
||||
}
|
||||
});
|
||||
$scope.initial = initial;
|
||||
|
||||
var allocated = [];
|
||||
|
||||
$scope.tableData = {
|
||||
available: available,
|
||||
allocated: allocated,
|
||||
displayedAvailable: [],
|
||||
displayedAllocated: [],
|
||||
minItems: -1
|
||||
};
|
||||
|
||||
var maxAllocation = "maxItems" in $attrs
|
||||
? Number($attrs["maxItems"])
|
||||
: -1;
|
||||
$scope.tableLimits = {
|
||||
maxAllocation: maxAllocation
|
||||
};
|
||||
|
||||
$scope.tableHelpText = {
|
||||
allocHelpText: $attrs['allocatedHelpText'],
|
||||
availHelpText: $attrs['availableHelpText'],
|
||||
noAllocText: $attrs['noAllocatedText'],
|
||||
noAvailText: $attrs['noAvailableText']
|
||||
};
|
||||
|
||||
$scope.facets = [{
|
||||
label: gettext("Name"),
|
||||
name: "name",
|
||||
singleton: true
|
||||
}];
|
||||
|
||||
if("addItemLink" in $attrs) {
|
||||
$scope.addItemLink = $attrs["addItemLink"];
|
||||
}
|
||||
|
||||
if("allocatedFilter" in $attrs) {
|
||||
$scope.allocatedFilter = true;
|
||||
}
|
||||
|
||||
$scope.id = $attrs["id"];
|
||||
$scope.name = $attrs["name"];
|
||||
|
||||
}
|
||||
}
|
||||
}])
|
||||
|
||||
})();
|
|
@ -0,0 +1,114 @@
|
|||
<transfer-table tr-model="tableData" limits="tableLimits" help-text="tableHelpText">
|
||||
|
||||
<allocated ng-model="tableData.allocated.length">
|
||||
<table st-table="tableData.displayedAllocated"
|
||||
st-safe-src="tableData.allocated"
|
||||
hz-table
|
||||
class="table table-striped table-rsp table-detail table-condensed">
|
||||
<thead>
|
||||
<tr ng-if="allocatedFilter">
|
||||
<th colspan="2">
|
||||
<hz-magic-search-bar filter-facets="facets"></hz-magic-search-bar>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="2">Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-if="tableData.allocated.length === 0">
|
||||
<td colspan="{{ addItemLink ? 2 : 1 }}">
|
||||
<div class="no-rows-help">
|
||||
{$ ::tableHelpText.noAllocText $}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat="row in tableData.displayedAllocated track by row.id">
|
||||
<td>
|
||||
{$ row.name $}
|
||||
</td>
|
||||
<td class="actions_column">
|
||||
<action-list>
|
||||
<button tabIndex="0"
|
||||
ng-class="'btn btn-default'"
|
||||
ng-click="trCtrl.deallocate(row)"
|
||||
type="button">
|
||||
<span class="fa fa-arrow-down"></span>
|
||||
</button>
|
||||
</action-list>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</allocated>
|
||||
|
||||
<available>
|
||||
<table
|
||||
st-table="tableData.displayedAvailable"
|
||||
st-safe-src="tableData.available"
|
||||
hz-table
|
||||
class="table table-striped table-rsp table-detail table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="{$ addItemLink ? 1 : 2 $}">
|
||||
<hz-magic-search-bar filter-facets="facets"></hz-magic-search-bar>
|
||||
</th>
|
||||
<th ng-if="addItemLink">
|
||||
<span class="input-group-btn">
|
||||
<a href="{$ addItemLink $}" data-add-to-field="{$ id $}_select" class="btn btn-default ajax-add ajax-modal">
|
||||
<span class="fa fa-plus"></span>
|
||||
</a>
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="2">Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr ng-if="trCtrl.numAvailable() === 0">
|
||||
<td colspan="{{ addItemLink ? 2 : 1 }}">
|
||||
<div class="no-rows-help">
|
||||
{$ ::tableHelpText.noAvailText $}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr ng-repeat="row in tableData.displayedAvailable track by row.id"
|
||||
ng-if="!trCtrl.allocatedIds[row.id]"
|
||||
>
|
||||
<td>{$ row.name $}</td>
|
||||
<td class="actions_column">
|
||||
<action-list button-tooltip="row.warningMessage"
|
||||
bt-model="ctrl.tooltipModel"
|
||||
bt-disabled="!row.disabled"
|
||||
warning-classes="'invalid'">
|
||||
<notifications>
|
||||
<span class="fa fa-exclamation-circle invalid"
|
||||
ng-show="row.disabled"></span>
|
||||
</notifications>
|
||||
<button tabIndex="0"
|
||||
ng-class="'btn btn-default'"
|
||||
ng-click="trCtrl.allocate(row)"
|
||||
type="button">
|
||||
<span class="fa fa-arrow-up"></span>
|
||||
</button>
|
||||
</action-list>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="display:None">
|
||||
<select
|
||||
d-select
|
||||
id="{$ id $}_select"
|
||||
data-add-item-url
|
||||
multiple
|
||||
name="{$ name $}" >
|
||||
</select>
|
||||
</div>
|
||||
</available>
|
||||
|
||||
</transfer-table>
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License. You may obtain
|
||||
* a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
(function () {
|
||||
angular
|
||||
.module('gbpui.transfer-table-bridge', ['horizon.app.core.workflow']);
|
||||
})();
|
Loading…
Reference in New Issue