Skip to content

Commit

Permalink
Merge pull request #4913 from ZitaNemeckova/god_buttons
Browse files Browse the repository at this point in the history
GOD's button group can have buttons assign/unassign during create/edit
  • Loading branch information
himdel authored Aug 2, 2019
2 parents d165ff2 + 03d6c5f commit 1fc6e61
Show file tree
Hide file tree
Showing 11 changed files with 448 additions and 19 deletions.
59 changes: 59 additions & 0 deletions app/assets/javascripts/components/generic_object/assign-buttons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
ManageIQ.angular.app.component('assignButtons', {
bindings: {
assignedButtons: '<',
unassignedButtons: '<',
updateButtons: '<',
},
require: {
parent: '^^mainCustomButtonGroupForm',
},
controllerAs: 'vm',
controller: assignButtonsController,
templateUrl: '/static/generic_object/assign-buttons.html.haml',
});

function assignButtonsController() {
var vm = this;

vm.model = {
selectedAssignedButtons: [],
selectedUnassignedButtons: [],
};

vm.leftButtonClicked = function() {
var ret = ManageIQ.move.between({
from: [].concat(vm.assignedButtons),
to: [].concat(vm.unassignedButtons),
selected: vm.model.selectedAssignedButtons,
});

vm.updateButtons(ret.from, ret.to);
};

vm.rightButtonClicked = function() {
var ret = ManageIQ.move.between({
from: [].concat(vm.unassignedButtons),
to: [].concat(vm.assignedButtons),
selected: vm.model.selectedUnassignedButtons,
});

vm.updateButtons(ret.to, ret.from);
};

function wrap(fn) {
return function() {
var assigned = fn({
array: [].concat(vm.assignedButtons),
selected: vm.model.selectedAssignedButtons,
});

vm.updateButtons(assigned);
};
}

vm.topButtonClicked = wrap(ManageIQ.move.top);
vm.bottomButtonClicked = wrap(ManageIQ.move.bottom);

vm.upButtonClicked = wrap(ManageIQ.move.up);
vm.downButtonClicked = wrap(ManageIQ.move.down);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ ManageIQ.angular.app.component('mainCustomButtonGroupForm', {
templateUrl: '/static/generic_object/main_custom_button_group_form.html.haml',
});

mainCustomButtonGroupFormController.$inject = ['API', 'miqService'];
mainCustomButtonGroupFormController.$inject = ['API', 'miqService', '$http'];

function mainCustomButtonGroupFormController(API, miqService) {
function mainCustomButtonGroupFormController(API, miqService, $http) {
var vm = this;

vm.$onInit = function() {
Expand All @@ -26,20 +26,35 @@ function mainCustomButtonGroupFormController(API, miqService) {
button_icon: '',
button_color: '#4d5258',
set_data: {},
assigned_buttons: [],
unassigned_buttons: [],
};

if (vm.customButtonGroupRecordId) {
vm.newRecord = false;
miqService.sparkleOn();
API.get('/api/custom_button_sets/' + vm.customButtonGroupRecordId)
.then(getCustomButtonGroupFormData)
.catch(miqService.handleFailure);
} else {
vm.newRecord = true;
$http.get('/generic_object_definition/custom_buttons_in_set/?custom_button_set_id=' + vm.customButtonGroupRecordId + '&generic_object_definition_id=' + vm.genericObjectDefnRecordId)
.then(function(response) {
Object.assign(vm.customButtonGroupModel, response.data);
if (vm.customButtonGroupRecordId) {
vm.newRecord = false;
miqService.sparkleOn();
API.get('/api/custom_button_sets/' + vm.customButtonGroupRecordId)
.then(getCustomButtonGroupFormData)
.catch(miqService.handleFailure);
} else {
vm.newRecord = true;

API.get('/api/custom_button_sets?expand=resources&attributes=set_data')
.then(getCustomButtonSetGroupIndex)
.catch(miqService.handleFailure);
}
})
.catch(miqService.handleFailure);
};

vm.updateButtons = function(assignedButtons, unassignedButtons) {
vm.customButtonGroupModel.assigned_buttons = assignedButtons;

API.get('/api/custom_button_sets?expand=resources&attributes=set_data')
.then(getCustomButtonSetGroupIndex)
.catch(miqService.handleFailure);
if (unassignedButtons) {
vm.customButtonGroupModel.unassigned_buttons = unassignedButtons;
}
};

Expand All @@ -53,7 +68,7 @@ function mainCustomButtonGroupFormController(API, miqService) {
};

vm.resetClicked = function(angularForm) {
vm.customButtonGroupModel = Object.assign({}, vm.modelCopy);
vm.customButtonGroupModel = _.cloneDeep(vm.modelCopy);

angularForm.$setUntouched(true);
angularForm.$setPristine(true);
Expand All @@ -71,11 +86,19 @@ function mainCustomButtonGroupFormController(API, miqService) {
vm.saveWithAPI('post', '/api/custom_button_sets/', vm.prepSaveObject(), saveMsg);
};

vm.buttonOrder = function() {
var orderedButtons = [];
vm.customButtonGroupModel.assigned_buttons.forEach(function(button) {
orderedButtons.push(button.id);
});
return orderedButtons;
};

vm.prepSaveObject = function() {
vm.customButtonGroupModel.set_data = {
button_icon: vm.customButtonGroupModel.button_icon,
button_color: vm.customButtonGroupModel.button_color,
button_order: vm.customButtonGroupModel.button_order,
button_order: vm.buttonOrder(),
display: vm.customButtonGroupModel.display,
applies_to_class: 'GenericObjectDefinition',
applies_to_id: parseInt(vm.genericObjectDefnRecordId, 10),
Expand All @@ -94,7 +117,9 @@ function mainCustomButtonGroupFormController(API, miqService) {
vm.saveWithAPI = function(method, url, saveObject, saveMsg) {
miqService.sparkleOn();
API[method](url, saveObject)
.then(miqService.redirectBack.bind(vm, saveMsg, 'success', vm.redirectUrl))
.then(function() {
miqService.redirectBack(saveMsg, 'success', vm.redirectUrl);
})
.catch(miqService.handleFailure);
};

Expand All @@ -112,7 +137,8 @@ function mainCustomButtonGroupFormController(API, miqService) {

vm.customButtonGroupModel.set_data = {};

vm.modelCopy = Object.assign({}, vm.customButtonGroupModel);
vm.modelCopy = _.cloneDeep(vm.customButtonGroupModel);

vm.afterGet = true;
miqService.sparkleOff();
}
Expand All @@ -123,6 +149,6 @@ function mainCustomButtonGroupFormController(API, miqService) {
return setData.applies_to_class === 'GenericObject';
}).length;
vm.customButtonGroupModel.group_index = currentGroupIndex + 1;
vm.modelCopy = angular.copy( vm.customButtonGroupModel );
vm.modelCopy = _.cloneDeep(vm.customButtonGroupModel);
}
}
2 changes: 1 addition & 1 deletion app/assets/javascripts/directives/form_changed.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ ManageIQ.angular.app.directive('formChanged', function() {

var compare = function(original, copy, key) {
// add missing keys in copy from original so recursion works
if (_.isObject(copy) && _.isObject(original)) {
if (_.isObject(copy) && _.isObject(original) && !_.isArray(copy) && !_.isArray(original)) {
_.difference(Object.keys(original), Object.keys(copy)).forEach(function(k) {
copy[k] = undefined;
});
Expand Down
2 changes: 2 additions & 0 deletions app/assets/javascripts/miq_global.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ if (!window.ManageIQ) {
x: null, // mouse X coordinate for popup menu
y: null, // mouse Y coordinate for popup menu
},
move: { //methods to move elements between Arrays or in an Array
},
noCollapseEvent: false, // enable/disable events fired after collapsing an accordion
observe: { // keeping track of data-miq_observe requests
processing: false, // is a request currently being processed?
Expand Down
15 changes: 15 additions & 0 deletions app/controllers/generic_object_definition_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ def custom_button_group_edit
assert_privileges('ab_group_edit')
@custom_button_group = CustomButtonSet.find(params[:id])
@right_cell_text = _("Edit Custom Button Group '%{name}'") % {:name => @custom_button_group.name}
@generic_object_definition = find_record_with_rbac(GenericObjectDefinition, @custom_button_group.set_data[:applies_to_id])
render_form(@right_cell_text, 'custom_button_group_form')
end

Expand Down Expand Up @@ -148,6 +149,20 @@ def add_button_in_group
custom_button_set.save!
end

def custom_buttons_in_set
assigned_buttons = if params[:custom_button_set_id].present?
button_set = find_record_with_rbac(CustomButtonSet, params[:custom_button_set_id])
button_set.custom_buttons
else
[]
end
generic_object_definition = find_record_with_rbac(GenericObjectDefinition, params[:generic_object_definition_id])
unassigned_buttons = generic_object_definition.custom_buttons
assigned_buttons.map! { |button| {:name => button.name, :id => button.id} }
unassigned_buttons.map! { |button| {:name => button.name, :id => button.id} }
render :json => {:assigned_buttons => assigned_buttons, :unassigned_buttons => unassigned_buttons}
end

private

def node_type(node)
Expand Down
109 changes: 109 additions & 0 deletions app/javascript/helpers/move.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { find, findIndex, some, reject } from 'lodash';

// [{id: 123}], [123] => [0]
function idsToElements(arr, ids) {
return ids.map((id) => find(arr, { id }));
}

function idsToIndexes(arr, ids) {
return ids.map((id) => findIndex(arr, { id }));
}

function removeElements(arr, elements) {
return reject(arr, (elem) => some(elements, elem));
}

function filterIndexes(indexes) {
if (indexes[0] !== 0) {
return indexes;
}
var previous = 0;
var filteredIndexes = [];
indexes.forEach(function(index) {
if (index !== 0 && index - 1 !== previous) {
filteredIndexes.push(index);
} else {
previous = index;
}
});
return filteredIndexes;
}

function filterReverseIndexes(indexes, endIndex) {
if (indexes[0] !== endIndex) {
return indexes;
}
var previous = endIndex;
var filteredIndexes = [];
indexes.forEach(function(index) {
if (index !== endIndex && index + 1 !== previous) {
filteredIndexes.push(index);
} else {
previous = index;
}
});
return filteredIndexes;
}

function stepUp(array, index) {
if (index < 1) {
return;
}

[array[index], array[index - 1]] = [array[index - 1], array[index]];
}

function stepDown(array, index) {
if ((index < 0) || (index >= array.length - 1)) {
return;
}

[array[index], array[index + 1]] = [array[index + 1], array[index]];
}


// move selected elements between two arrays
export function between({from, to, selected}) {
var moved = idsToElements(from, selected);

return {
from: removeElements(from, moved),
to: to.concat(moved),
};
}

// move selected elements to the top of the array
export function top({array, selected}) {
var moved = idsToElements(array, selected);
array = removeElements(array, moved);

return moved.concat(array);
}

// move selected elements to the bottom of the array
export function bottom({array, selected}) {
var moved = idsToElements(array, selected);
array = removeElements(array, moved);

return array.concat(moved);
}

// move selected elements one position up
export function up({array, selected}) {
var indexes = idsToIndexes(array, selected);
indexes = filterIndexes(indexes);

indexes.forEach((index) => stepUp(array, index));

return array;
}

// move selected elements one position up
export function down({array, selected}) {
var indexes = idsToIndexes(array, selected).reverse();
indexes = filterReverseIndexes(indexes, array.length - 1);

indexes.forEach((index) => stepDown(array, index));

return array;
}
3 changes: 3 additions & 0 deletions app/javascript/packs/application-common.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,6 @@ window.Spinner = Spinner;

// Overview > Optimization
miqOptimizationInit();

import * as move from '../helpers/move.js';
ManageIQ.move = move;
55 changes: 55 additions & 0 deletions app/views/static/generic_object/assign-buttons.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
.form-horizontal
%hr
%h3
= _('Assign Buttons')
#column_lists
.col-md-5
= _('Unassigned:')
%select.form-control{:name => 'unassigned_buttons',
"ng-model" => "vm.model.selectedUnassignedButtons",
"ng-options" => "item.id as item.name for item in vm.unassignedButtons",
:multiple => true,
:style => "overflow-x: scroll;",
:size => 8,
:id => "available_fields"}
.col-md-1{:style => "padding: 10px"}
.spacer
.spacer
%button.btn.btn-default.btn-block{:title => _("Move selected fields right"),
"ng-click" => "vm.rightButtonClicked()"}
%i.fa.fa-angle-right.fa-lg.hidden-xs.hidden-sm
%i.fa.fa-lg.fa-angle-right.fa-rotate-90.hidden-md.hidden-lg
%button.btn.btn-default.btn-block{:title => _("Move selected fields left"),
"ng-click" => "vm.leftButtonClicked()"}
%i.fa.fa-angle-left.fa-lg.hidden-xs.hidden-sm
%i.fa.fa-lg.fa-angle-left.fa-rotate-90.hidden-md.hidden-lg
.spacer
.col-md-5
= _('Selected:')
%select.form-control{:name => 'assigned_buttons',
"ng-model" => "vm.model.selectedAssignedButtons",
"ng-options" => "item.id as item.name for item in vm.assignedButtons",
:multiple => true,
:style => "overflow-x: scroll;",
:size => 8,
:id => "selected_fields"}
.col-md-1{:style => "padding: 10px"}
.spacer
.spacer
%button.btn.btn-default.btn-block{:title => _("Move selected fields to top"),
"ng-enabled" => "vm.moveButtonsEnabled()",
"ng-click" => "vm.topButtonClicked()"}
%i.fa.fa-angle-double-up.fa-lg
%button.btn.btn-default.btn-block{:title => _("Move selected fields up"),
:enabled => "vm.moveButtonsEnabled()",
"ng-click" => "vm.upButtonClicked()"}
%i.fa.fa-angle-up.fa-lg
%button.btn.btn-default.btn-block{:title => _("Move selected fields down"),
"ng-enabled" => "vm.moveButtonsEnabled()",
"ng-click" => "vm.downButtonClicked()"}
%i.fa.fa-angle-down.fa-lg
%button.btn.btn-default.btn-block{:title => _("Move selected fields to bottom"),
"ng-enabled" => "vm.moveButtonsEnabled()",
"ng-click" => "vm.bottomButtonClicked()"}
%i.fa.fa-angle-double-down
.spacer
Loading

0 comments on commit 1fc6e61

Please sign in to comment.