Index: lams_admin/src/java/org/lamsfoundation/lams/admin/web/action/OrgPasswordChangeAction.java =================================================================== diff -u -rf47e5f0e21f1ff69300ea79194ea9a3278af3f33 -r9d983eac609d45a939706e34834cfed8c7249810 --- lams_admin/src/java/org/lamsfoundation/lams/admin/web/action/OrgPasswordChangeAction.java (.../OrgPasswordChangeAction.java) (revision f47e5f0e21f1ff69300ea79194ea9a3278af3f33) +++ lams_admin/src/java/org/lamsfoundation/lams/admin/web/action/OrgPasswordChangeAction.java (.../OrgPasswordChangeAction.java) (revision 9d983eac609d45a939706e34834cfed8c7249810) @@ -36,6 +36,7 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; @@ -178,19 +179,28 @@ Boolean isLearnerChange = (Boolean) passForm.get("isLearnerChange"); // get data needed for each group if (isStaffChange) { - JSONArray excludedStaff = new JSONArray((String) passForm.get("excludedStaff")); + String staffString = (String) passForm.get("excludedStaff"); + JSONArray excludedStaff = StringUtils.isBlank(staffString) ? null : new JSONArray(staffString); + staffString = (String) passForm.get("includedStaff"); + JSONArray includedStaff = StringUtils.isBlank(staffString) ? null : new JSONArray(staffString); + String staffPass = (String) passForm.get("staffPass"); Collection users = getUsersByRole(organisationID, true); - Collection changedUserIDs = changePassword(staffPass, users, excludedStaff, force); + Collection changedUserIDs = changePassword(staffPass, users, includedStaff, excludedStaff, force); if (email && !changedUserIDs.isEmpty()) { notifyOnPasswordChange(changedUserIDs, staffPass); } } if (isLearnerChange) { - JSONArray excludedLearners = new JSONArray((String) passForm.get("excludedLearners")); + String learnersString = (String) passForm.get("excludedLearners"); + JSONArray excludedLearners = StringUtils.isBlank(learnersString) ? null : new JSONArray(learnersString); + learnersString = (String) passForm.get("includedLearners"); + JSONArray includedLearners = StringUtils.isBlank(learnersString) ? null : new JSONArray(learnersString); + String learnerPass = (String) passForm.get("learnerPass"); Collection users = getUsersByRole(organisationID, false); - Collection changedUserIDs = changePassword(learnerPass, users, excludedLearners, force); + Collection changedUserIDs = changePassword(learnerPass, users, includedLearners, excludedLearners, + force); if (email && !changedUserIDs.isEmpty()) { notifyOnPasswordChange(changedUserIDs, learnerPass); } @@ -208,29 +218,48 @@ messageService.getMessage("admin.org.password.change.email.body", new String[] { password }), false); } - private Set changePassword(String password, Collection users, JSONArray excludedUsers, boolean force) - throws JSONException { + private Set changePassword(String password, Collection users, JSONArray includedUsers, + JSONArray excludedUsers, boolean force) throws JSONException { if (!ValidationUtil.isPasswordValueValid(password, password)) { // this should have been picked up by JS validator on the page! throw new InvalidParameterException("Password does not pass validation"); } + if (includedUsers != null && excludedUsers != null) { + throw new IllegalArgumentException("Both included and excluded users arrays must not be passed together"); + } Set changedUserIDs = new TreeSet(); IUserManagementService userManagementService = AdminServiceProxy.getService(getServlet().getServletContext()); UserDTO currentUserDTO = getUserDTO(); User currentUser = (User) userManagementService.findById(User.class, currentUserDTO.getUserID()); for (User user : users) { - boolean excluded = false; - // skip excluded (unchecked on the page) users - for (int index = 0; index < excludedUsers.length(); index++) { - Integer excludedUserID = excludedUsers.getInt(index); - if (user.getUserId().equals(excludedUserID)) { - excluded = true; - break; + // either we work with white list or black list + if (includedUsers == null) { + boolean excluded = false; + // skip excluded (unchecked on the page) users + for (int index = 0; index < excludedUsers.length(); index++) { + Integer excludedUserID = excludedUsers.getInt(index); + if (user.getUserId().equals(excludedUserID)) { + excluded = true; + break; + } } + if (excluded) { + continue; + } + } else { + boolean included = false; + for (int index = 0; index < includedUsers.length(); index++) { + Integer includedUserID = includedUsers.getInt(index); + if (user.getUserId().equals(includedUserID)) { + included = true; + break; + } + } + if (!included) { + continue; + } } - if (excluded) { - continue; - } + // change password String salt = HashUtil.salt(); user.setSalt(salt); @@ -239,6 +268,7 @@ user.setChangePassword(true); } userManagementService.saveUser(user); + log.info("Changed password for user ID " + user.getUserId()); userManagementService.logPasswordChanged(user, currentUser); changedUserIDs.add(user.getUserId()); } Index: lams_admin/web/WEB-INF/struts-config.xml =================================================================== diff -u -rb26f90eccf164c7058201360d2ca771adcfb597a -r9d983eac609d45a939706e34834cfed8c7249810 --- lams_admin/web/WEB-INF/struts-config.xml (.../struts-config.xml) (revision b26f90eccf164c7058201360d2ca771adcfb597a) +++ lams_admin/web/WEB-INF/struts-config.xml (.../struts-config.xml) (revision 9d983eac609d45a939706e34834cfed8c7249810) @@ -165,7 +165,9 @@ + + Index: lams_admin/web/orgPasswordChange.jsp =================================================================== diff -u -r376e99b362b0ea18df73f848d310201270b26239 -r9d983eac609d45a939706e34834cfed8c7249810 --- lams_admin/web/orgPasswordChange.jsp (.../orgPasswordChange.jsp) (revision 376e99b362b0ea18df73f848d310201270b26239) +++ lams_admin/web/orgPasswordChange.jsp (.../orgPasswordChange.jsp) (revision 9d983eac609d45a939706e34834cfed8c7249810) @@ -44,10 +44,6 @@ vertical-align: top; } - .jqgh_cbox { - visibility: hidden; - } - .ui-jqgrid-btable tr[role="row"] { cursor: pointer; } @@ -70,10 +66,8 @@ var mustHaveUppercase = ${mustHaveUppercase}, mustHaveNumerics = ${mustHaveNumerics}, mustHaveLowercase = ${mustHaveLowercase}, - mustHaveSymbols = ${mustHaveSymbols}, - excludedLearners = JSON.parse(""), - excludedStaff = JSON.parse(""); - + mustHaveSymbols = ${mustHaveSymbols}; + $.validator.addMethod("pwcheck", function(value) { return (!mustHaveUppercase || /[A-Z]/.test(value)) && // has uppercase letters (!mustHaveNumerics || /\d/.test(value)) && // has a digit @@ -171,8 +165,17 @@ }, submitHandler : function(form) { - $('#excludedLearners').val(JSON.stringify(excludedLearners)); - $('#excludedStaff').val(JSON.stringify(excludedStaff)); + // see which mode are we in (included/excluded) and serialize JSON arrays into strings + var learnerGrid = $('#learnerGrid'), + staffGrid = $('#staffGrid'), + includedLearners = learnerGrid.data('included'), + excludedLearners = learnerGrid.data('excluded'), + includedStaff = staffGrid.data('included'), + excludedStaff = staffGrid.data('excluded'); + $('#includedLearners').val(includedLearners === null ? '' : JSON.stringify(includedLearners)); + $('#excludedLearners').val(excludedLearners === null ? '' : JSON.stringify(excludedLearners)); + $('#includedStaff').val(includedStaff === null ? '' : JSON.stringify(includedStaff)); + $('#excludedStaff').val(excludedStaff === null ? '' : JSON.stringify(excludedStaff)); form.submit(); } }); @@ -217,31 +220,84 @@ ], onSelectRow : function(id, status, event) { var grid = $(this), + included = grid.data('included'), excluded = grid.data('excluded'), - index = excluded.indexOf(+id); - // if row is deselected, add it to excluded array - if (index < 0) { - if (!status) { - excluded.push(+id); + selectAllChecked = grid.closest('.ui-jqgrid-view').find('.jqgh_cbox .cbox').prop('checked'); + if (selectAllChecked) { + var index = excluded.indexOf(+id); + // if row is deselected, add it to excluded array + if (index < 0) { + if (!status) { + excluded.push(+id); + } + } else if (status) { + excluded.splice(index, 1); } - } else if (status) { - excluded.splice(index, 1); + } else { + var index = included.indexOf(+id); + // if row is selected, add it to included array + if (index < 0) { + if (status) { + included.push(+id); + } + } else if (!status) { + included.splice(index, 1); + } } }, gridComplete : function(){ var grid = $(this), - // get excludedLearners or excludedStaff - excluded = grid.data('excluded'); - // go through each loaded row - $('[role="row"]', grid).each(function(){ - var id = +$(this).attr('id'), - selected = $(this).hasClass('success'); - // if row is not selected and is not excluded, select it - if (!selected && !excluded.includes(id)) { - // select without triggering onSelectRow - grid.jqGrid('setSelection', id, false); - } - }); + included = grid.data('included'), + // cell containing "(de)select all" button + selectAllCell = grid.closest('.ui-jqgrid-view').find('.jqgh_cbox > div'); + // remove the default button provided by jqGrid + $('.cbox', selectAllCell).remove(); + // create own button which follows own rules + var selectAllCheckbox = $('') + .prop('checked', included === null) + .prependTo(selectAllCell) + .change(function(){ + // start with deselecting everyone on current page + grid.resetSelection(); + if ($(this).prop('checked')){ + // on select all change mode and select all on current page + grid.data('included', null); + grid.data('excluded', []); + $('[role="row"]', grid).each(function(){ + grid.jqGrid('setSelection', +$(this).attr('id'), false); + }); + } else { + // on deselect all just change mode + grid.data('excluded', null); + grid.data('included', []); + } + }); + + grid.resetSelection(); + if (selectAllCheckbox.prop('checked')) { + var excluded = grid.data('excluded'); + // go through each loaded row + $('[role="row"]', grid).each(function(){ + var id = +$(this).attr('id'), + selected = $(this).hasClass('success'); + // if row is not selected and is not excluded, select it + if (!selected && !excluded.includes(id)) { + // select without triggering onSelectRow + grid.jqGrid('setSelection', id, false); + } + }); + } else { + // go through each loaded row + $('[role="row"]', grid).each(function(){ + var id = +$(this).attr('id'), + selected = $(this).hasClass('success'); + // if row is not selected and is included, select it + if (!selected && included.includes(id)) { + // select without triggering onSelectRow + grid.jqGrid('setSelection', id, false); + } + }); + } changeCheckboxes.trigger('change'); }, @@ -251,11 +307,20 @@ ''); } }; - + + + var includedLearners = "", + excludedLearners = "", + includedStaff = "", + excludedStaff = ""; jqGridSettings.url = jqGridURL + 'learner' - $("#learnerGrid").data('excluded', excludedLearners).jqGrid(jqGridSettings); + $("#learnerGrid").data('excluded', excludedLearners ? JSON.parse(excludedLearners) : null) + .data('included', includedLearners ? JSON.parse(includedLearners) : null) + .jqGrid(jqGridSettings); jqGridSettings.url = jqGridURL + 'staff' - $("#staffGrid").data('excluded', excludedStaff).jqGrid(jqGridSettings); + $("#staffGrid").data('excluded', excludedStaff ? JSON.parse(excludedStaff) : null) + .data('included', includedStaff ? JSON.parse(includedStaff) : null) + .jqGrid(jqGridSettings); }); @@ -303,7 +368,9 @@ + +