From 5a8d6bc6c1d71a08347f1a675f48cb14cba59703 Mon Sep 17 00:00:00 2001 From: Deddy Syefria Date: Thu, 5 Oct 2017 15:28:53 +0700 Subject: [PATCH 1/3] fix group challenge with read-only access --- .../facade/contest/ContestServiceFacade.java | 12 +++++ .../contest/ejb/ContestServiceFacadeBean.java | 22 ++++++++ .../contest/launch/GetContestAction.java | 32 ++++++----- .../launch/SaveDraftContestAction.java | 23 ++++++-- .../SoftwareCompetitionBeanProcessor.java | 6 +++ .../services/view/util/DirectUtils.java | 54 ++++++++++++++++--- .../scripts/launch/contestDetailSoftware.js | 41 +++++++------- src/web/scripts/launch/main.js | 7 ++- 8 files changed, 154 insertions(+), 43 deletions(-) diff --git a/services/contest_service_facade/src/java/main/com/topcoder/service/facade/contest/ContestServiceFacade.java b/services/contest_service_facade/src/java/main/com/topcoder/service/facade/contest/ContestServiceFacade.java index fe060e81a..70d9ce871 100644 --- a/services/contest_service_facade/src/java/main/com/topcoder/service/facade/contest/ContestServiceFacade.java +++ b/services/contest_service_facade/src/java/main/com/topcoder/service/facade/contest/ContestServiceFacade.java @@ -31,6 +31,8 @@ import com.topcoder.project.service.ScorecardReviewData; import com.topcoder.search.builder.SearchBuilderException; import com.topcoder.security.TCSubject; +import com.topcoder.service.contest.eligibility.ContestEligibility; +import com.topcoder.service.contest.eligibility.dao.ContestEligibilityPersistenceException; import com.topcoder.service.facade.contest.notification.ProjectNotification; import com.topcoder.service.payment.CreditCardPaymentData; import com.topcoder.service.payment.TCPurhcaseOrderPaymentData; @@ -1666,4 +1668,14 @@ Set updatePreRegister(TCSubject tcSubject, SoftwareCompetition contest, * @since 1.8.6 */ public ProjectGroup[] getAllProjectGroups(TCSubject tcSubject) throws ContestServiceException; + + /** + * Get group for a contest + * + * @param contestId contestId + * @param isStudio false + * @return + * @throws ContestServiceException + */ + public List getGroupForContest(long contestId, boolean isStudio) throws ContestServiceException; } diff --git a/services/contest_service_facade/src/java/main/com/topcoder/service/facade/contest/ejb/ContestServiceFacadeBean.java b/services/contest_service_facade/src/java/main/com/topcoder/service/facade/contest/ejb/ContestServiceFacadeBean.java index 05212d639..56c2decc4 100644 --- a/services/contest_service_facade/src/java/main/com/topcoder/service/facade/contest/ejb/ContestServiceFacadeBean.java +++ b/services/contest_service_facade/src/java/main/com/topcoder/service/facade/contest/ejb/ContestServiceFacadeBean.java @@ -9592,4 +9592,26 @@ private ProjectPayment addManualCopilotPayment(com.topcoder.management.resource. newPayment.setResourceId(copilotResource.getId()); return projectPaymentManager.create(newPayment, String.valueOf(tcSubject.getUserId())); } + + /** + * Get group for a contest + * + * @param contestId contestId + * @param isStudio false + * @return + * @throws ContestServiceException + */ + public List getGroupForContest(long contestId, boolean isStudio) throws ContestServiceException { + try { + List ces = contestEligibilityManager.getContestEligibility(contestId, isStudio); + List groupList = new ArrayList(); + for (ContestEligibility ce : ces) { + groupList.add(new ProjectGroup(((GroupContestEligibility) ce).getGroupId(), "")); + } + return groupList; + } catch (ContestEligibilityPersistenceException ce) { + logger.error("Failed to get security group for challenge id:" + contestId); + throw new ContestServiceException("Failed to get security group for challenge id:" + contestId); + } + } } diff --git a/src/java/main/com/topcoder/direct/services/view/action/contest/launch/GetContestAction.java b/src/java/main/com/topcoder/direct/services/view/action/contest/launch/GetContestAction.java index fa56b25be..fcff77211 100644 --- a/src/java/main/com/topcoder/direct/services/view/action/contest/launch/GetContestAction.java +++ b/src/java/main/com/topcoder/direct/services/view/action/contest/launch/GetContestAction.java @@ -24,6 +24,7 @@ import com.topcoder.direct.services.view.util.SessionData; import com.topcoder.management.deliverable.Submission; import com.topcoder.management.project.Prize; +import com.topcoder.management.project.ProjectGroup; import com.topcoder.management.resource.Resource; import com.topcoder.management.resource.ResourceRole; import com.topcoder.security.TCSubject; @@ -342,11 +343,11 @@ public class GetContestAction extends ContestAction { private boolean admin; /** - * The registration end date. - */ - private String regEndDate; - - /** + * The registration end date. + */ + private String regEndDate; + + /** * The submission end date. * @since 1.5 */ @@ -452,11 +453,16 @@ protected void executeAction() throws Exception { if (DirectUtils.isStudio(softwareCompetition)) { softwareCompetition.setType(CompetionType.STUDIO); } + softwareCompetition.getProjectHeader().setGroups(DirectUtils.getGroupIdAndName( + softwareCompetition.getProjectHeader().getGroups())); + setResult(softwareCompetition); - regEndDate = DirectUtils.getDateString(DirectUtils.getRegistrationEndDate(softwareCompetition)); + regEndDate = DirectUtils.getDateString(DirectUtils.getRegistrationEndDate(softwareCompetition)); subEndDate = DirectUtils.getDateString(DirectUtils.getSubmissionEndDate(softwareCompetition)); contestEndDate = DirectUtils.getDateString(DirectUtils.getEndDate(softwareCompetition)); + + // depends on the type : // 1. if contest, store softwareCompetition in session // 2. if json data for contest, stops here since we are getting it @@ -955,13 +961,13 @@ public void setType(TYPE type) { this.type = type; } - /** - * Gets the registration end date. - */ - public String getRegEndDate() { - return regEndDate; - } - + /** + * Gets the registration end date. + */ + public String getRegEndDate() { + return regEndDate; + } + /** * Gets the submission end date. * @since 1.5 diff --git a/src/java/main/com/topcoder/direct/services/view/action/contest/launch/SaveDraftContestAction.java b/src/java/main/com/topcoder/direct/services/view/action/contest/launch/SaveDraftContestAction.java index e14f9f7b8..c9a474534 100644 --- a/src/java/main/com/topcoder/direct/services/view/action/contest/launch/SaveDraftContestAction.java +++ b/src/java/main/com/topcoder/direct/services/view/action/contest/launch/SaveDraftContestAction.java @@ -506,6 +506,17 @@ public class SaveDraftContestAction extends ContestAction { */ private static final long APPIRIO_MANAGER_METADATA_KEY_ID = 15L; + /** + * Private constant specifying administrator role. + */ + private static final String ADMIN_ROLE = "Cockpit Administrator"; + + /** + * Private constant specifying administrator role. + */ + private static final String TC_STAFF_ROLE = "TC Staff"; + + /** *

* @@ -1056,8 +1067,13 @@ public boolean evaluate(Object object) { } //set groups + List groupsList = new ArrayList(); + //read-only group we need to set it with current group + if (!DirectUtils.isRole(getCurrentUser(), ADMIN_ROLE) && !DirectUtils.isRole(getCurrentUser(), TC_STAFF_ROLE) && projectId > 0) { + groupsList = getContestServiceFacadeWithISE().getGroupForContest(projectId, false); + groups = null; + } if (groups != null && groups.size() > 0) { - List groupsList = new ArrayList(); // get the TCSubject from session ProjectGroup[] allProjectGroups = getContestServiceFacade().getAllProjectGroups(DirectStrutsActionsHelper.getTCSubjectFromSession()); for (String groupId : groups) { @@ -1066,12 +1082,9 @@ public boolean evaluate(Object object) { groupsList.add(projectGroup); } } - } - softwareCompetition.getProjectHeader().setGroups(groupsList); - } else { - softwareCompetition.getProjectHeader().setGroups(new ArrayList()); } + softwareCompetition.getProjectHeader().setGroups(groupsList); // remove the thurgood information if needed if(softwareCompetition.getProjectHeader().getProperties().containsKey(THURGOOD_PLATFORM_KEY)) { diff --git a/src/java/main/com/topcoder/direct/services/view/ajax/SoftwareCompetitionBeanProcessor.java b/src/java/main/com/topcoder/direct/services/view/ajax/SoftwareCompetitionBeanProcessor.java index 1e6ab9eed..5b9fba888 100644 --- a/src/java/main/com/topcoder/direct/services/view/ajax/SoftwareCompetitionBeanProcessor.java +++ b/src/java/main/com/topcoder/direct/services/view/ajax/SoftwareCompetitionBeanProcessor.java @@ -319,6 +319,12 @@ public Object transform(Object object) { } })); + result.put("groupNames", CollectionUtils.collect(bean.getProjectHeader().getGroups(), new Transformer() { + public Object transform(Object object) { + return ((ProjectGroup) object).getName(); + } + })); + // documentation result.put("documentation", CollectionUtils.collect(assetDTO.getDocumentation(), new Transformer() { public Object transform(Object object) { diff --git a/src/java/main/com/topcoder/direct/services/view/util/DirectUtils.java b/src/java/main/com/topcoder/direct/services/view/util/DirectUtils.java index 40cfb52dd..f6ca747fd 100644 --- a/src/java/main/com/topcoder/direct/services/view/util/DirectUtils.java +++ b/src/java/main/com/topcoder/direct/services/view/util/DirectUtils.java @@ -36,12 +36,7 @@ import com.topcoder.management.deliverable.Submission; import com.topcoder.management.deliverable.Upload; import com.topcoder.management.deliverable.persistence.UploadPersistenceException; -import com.topcoder.management.project.CopilotContestExtraInfo; -import com.topcoder.management.project.CopilotContestExtraInfoType; -import com.topcoder.management.project.Prize; -import com.topcoder.management.project.ProjectCopilotType; -import com.topcoder.management.project.ProjectPropertyType; -import com.topcoder.management.project.ProjectType; +import com.topcoder.management.project.*; import com.topcoder.management.resource.Resource; import com.topcoder.management.resource.ResourceRole; import com.topcoder.management.review.data.Comment; @@ -954,6 +949,9 @@ public final class DirectUtils { private static final String QUERY_GET_USERS_FROM_ID = "SELECT user_id, handle FROM user WHERE user_id in ("; + private static final String QUERY_GET_SECURITY_GROUP_FROM_ID = "SELECT group_id, description FROM security_groups " + + " WHERE group_id in ("; + /** *

* Default Constructor. @@ -3790,4 +3788,48 @@ public static Map> getUnAgreedProjectTermByUser(long proje DatabaseUtils.close(con); } } + + /** + * Get security Groups id and name from given projectGroup id + * + * @param projectGroups project group + * @return List of security group + * @throws Exception + */ + public static List getGroupIdAndName(List projectGroups) throws Exception{ + Connection con = null; + PreparedStatement ps = null; + ResultSet rs = null; + if (projectGroups == null || projectGroups.size() < 1){ + return null; + } + + try{ + con = DatabaseUtils.getDatabaseConnection(DBMS.COMMON_OLTP_DATASOURCE_NAME); + StringBuilder sbQueryGids = new StringBuilder(QUERY_GET_SECURITY_GROUP_FROM_ID); + for (ProjectGroup pg : projectGroups){ + sbQueryGids.append(" ?,"); + } + sbQueryGids.setCharAt(sbQueryGids.length() - 1, ')'); + + ps = con.prepareStatement(sbQueryGids.toString()); + + for (int i = 0; i < projectGroups.size(); i++){ + ps.setString(i + 1, String.valueOf(projectGroups.get(i).getId())); + } + rs = ps.executeQuery(); + List result = new ArrayList(); + while (rs.next()){ + ProjectGroup pg = new ProjectGroup(); + pg.setId(rs.getLong("group_id")); + pg.setName(rs.getString("description")); + result.add(pg); + } + return result; + }finally { + DatabaseUtils.close(rs); + DatabaseUtils.close(ps); + DatabaseUtils.close(con); + } + } } \ No newline at end of file diff --git a/src/web/scripts/launch/contestDetailSoftware.js b/src/web/scripts/launch/contestDetailSoftware.js index de3ddcc0c..b9ec36b0c 100644 --- a/src/web/scripts/launch/contestDetailSoftware.js +++ b/src/web/scripts/launch/contestDetailSoftware.js @@ -207,14 +207,13 @@ $(document).ready(function(){ }); $(".cancel_text").click(function(){ + groupCancel = true; jQuery_1_11_1("#groups").magicSuggest().clear(); - if (mainWidget.softwareCompetition.groups.length > 0){ - jQuery_1_11_1("#groups").magicSuggest().setValue(mainWidget.softwareCompetition.groups); - } + jQuery_1_11_1("#groups").magicSuggest().setValue(mainWidget.softwareCompetition.groups); + groupCancel = false; + jQuery_1_11_1("#preRegisterUsers").magicSuggest().clear(); - if (mainWidget.softwareCompetition.registrants.length > 0) { - jQuery_1_11_1("#preRegisterUsers").magicSuggest().setValue(mainWidget.softwareCompetition.registrants); - } + jQuery_1_11_1("#preRegisterUsers").magicSuggest().setValue(mainWidget.softwareCompetition.registrants); populateTypeSection(); showTypeSectionDisplay(); }); @@ -875,6 +874,7 @@ function initContest(contestJson) { } mainWidget.softwareCompetition.groups = contestJson.groupIds; + mainWidget.softwareCompetition.groupNames = contestJson.groupNames; var projectHeader = mainWidget.softwareCompetition.projectHeader; projectHeader.tcDirectProjectId = contestJson.tcDirectProjectId; @@ -1212,6 +1212,17 @@ function initContest(contestJson) { } $(".drHide").hide(); + + if (securityGroups.length < 1 && mainWidget.softwareCompetition.groups.length > 0) { + var allGroups = []; + $.each(mainWidget.softwareCompetition.groups, function(i, val){ + allGroups.push({id: val, name: mainWidget.softwareCompetition.groupNames[i]}); + }); + jQuery_1_11_1("#groups").magicSuggest().setData(allGroups); + } + + jQuery_1_11_1("#groups").magicSuggest().setValue(mainWidget.softwareCompetition.groups); + } @@ -1270,8 +1281,6 @@ function populateTypeSection() { $('#rProjectName').text(mainWidget.softwareCompetition.projectHeader.tcDirectProjectName); } - jQuery_1_11_1("#groups").magicSuggest().setValue(mainWidget.softwareCompetition.groups); - if (isF2F() || isDesignF2F()) { var privateProject = p[TASK_FLAG]; var registrants = []; @@ -1387,16 +1396,7 @@ function populateTypeSection() { } - var groupMap = {}; - $.each(securityGroups, function(i, val){ - groupMap[''+val.id]=val.name; - }); - var selectedGroupName = []; - $.each(mainWidget.softwareCompetition.groups, function(i, val){ - selectedGroupName.push(groupMap[val]); - }); - - $('#rswGroups').html(selectedGroupName.join(", ")); + $('#rswGroups').html(mainWidget.softwareCompetition.groupNames.join(", ")); } /** @@ -1453,6 +1453,11 @@ function saveTypeSection() { success: function (jsonResult) { handleSaveAsDraftContestResult(jsonResult); mainWidget.softwareCompetition.groups = jQuery_1_11_1("#groups").magicSuggest().getValue(); + mainWidget.softwareCompetition.groupNames=[]; + $.each(jQuery_1_11_1("#groups").magicSuggest().getSelection(), function(i, val){ + mainWidget.softwareCompetition.groupNames.push(val.name); + }) + mainWidget.softwareCompetition.registrants = jQuery_1_11_1("#preRegisterUsers").magicSuggest().getSelection().slice(); populateTypeSection(); populateRoundSection(); if (mainWidget.competitionType == "SOFTWARE") { diff --git a/src/web/scripts/launch/main.js b/src/web/scripts/launch/main.js index 4a230fb39..a4709bf80 100644 --- a/src/web/scripts/launch/main.js +++ b/src/web/scripts/launch/main.js @@ -203,6 +203,7 @@ var swDocuments = []; var REPORTING_ID = "36"; var securityGroups = []; +var groupCancel = false; /** * Configuration/General Set up */ @@ -232,9 +233,13 @@ $(document).ready(function() { var ms_group = jQuery_1_11_1("#groups").magicSuggest({ placeholder: 'Type group name here', allowFreeEntries: false, - data: securityGroups + data: securityGroups, + disabled: securityGroups.length > 0 ? false : true }); jQuery_1_11_1(ms_group).on('selectionchange', function(e,m){ + if (groupCancel){ + return; + } if (this.getValue().length > 0 && jQuery_1_11_1("#preRegisterUsers").magicSuggest().getValue().length > 0){ displayWarning("#yesNoConfirmation", "Confirmation", "Changing group will remove all assigned members.\n" + "Do you want to proceed?", "OK", function(){ From d49c967b8b756124d53873524a83aad776779bdd Mon Sep 17 00:00:00 2001 From: Deddy Syefria Date: Thu, 12 Oct 2017 01:24:36 +0700 Subject: [PATCH 2/3] use group api to get groups --- conf/web/WEB-INF/applicationContext.xml | 1 + conf/web/WEB-INF/struts.xml | 5 ++ .../action/contest/launch/CommonAction.java | 86 +++++++++++++++++-- src/web/scripts/launch/main.js | 68 +++++++++------ token.properties.docker | 1 + token.properties.example | 5 +- 6 files changed, 133 insertions(+), 33 deletions(-) diff --git a/conf/web/WEB-INF/applicationContext.xml b/conf/web/WEB-INF/applicationContext.xml index 5cd52d15a..53dd3fd32 100644 --- a/conf/web/WEB-INF/applicationContext.xml +++ b/conf/web/WEB-INF/applicationContext.xml @@ -356,6 +356,7 @@ + + + + + + diff --git a/src/java/main/com/topcoder/direct/services/view/action/contest/launch/CommonAction.java b/src/java/main/com/topcoder/direct/services/view/action/contest/launch/CommonAction.java index 04056ac11..606fe4336 100644 --- a/src/java/main/com/topcoder/direct/services/view/action/contest/launch/CommonAction.java +++ b/src/java/main/com/topcoder/direct/services/view/action/contest/launch/CommonAction.java @@ -3,11 +3,8 @@ */ package com.topcoder.direct.services.view.action.contest.launch; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.net.URI; +import java.util.*; import com.topcoder.clients.model.Project; import com.topcoder.clients.model.ProjectContestFee; @@ -29,11 +26,21 @@ import com.topcoder.direct.services.view.util.AuthorizationProvider; import com.topcoder.direct.services.view.util.DataProvider; import com.topcoder.direct.services.view.util.DirectUtils; +import com.topcoder.direct.services.view.util.JwtTokenUpdater; import com.topcoder.direct.services.view.util.challenge.CostCalculationService; import com.topcoder.security.TCSubject; import com.topcoder.service.facade.contest.ContestServiceException; import com.topcoder.service.facade.project.DAOFault; +import com.topcoder.util.log.Level; import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHeaders; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.impl.client.DefaultHttpClient; +import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.ObjectMapper; import com.topcoder.management.project.ProjectGroup; @@ -130,6 +137,16 @@ public class CommonAction extends BaseContestFeeAction { private long categoryId; + private String userGroupsApiEndpoint; + + /** + * The jackson object mapping which is used to deserialize json return from API to domain model. + */ + protected static final ObjectMapper objectMapper; + static { + objectMapper = new ObjectMapper(); + } + /** *

* Executes the action. @@ -325,7 +342,6 @@ public String getContestConfigs() throws Exception { configs.put("copilotFees", ConfigUtils.getCopilotFees()); configs.put("billingInfos", getBillingProjectInfos()); - configs.put("groups", getAllProjectGroups()); configs.put("platforms", getReferenceDataBean().getPlatforms()); configs.put("technologies", getReferenceDataBean().getTechnologies()); setResult(configs); @@ -552,4 +568,62 @@ public long getCategoryId() { public void setCategoryId(long categoryId) { this.categoryId = categoryId; } + + /** + * Get Accessible security groups from group Api + * + * @return + */ + public String getGroups() { + try { + TCSubject tcSubject = DirectUtils.getTCSubjectFromSession(); + URIBuilder uri = new URIBuilder(userGroupsApiEndpoint); + + if (!DirectUtils.isCockpitAdmin(tcSubject) && !DirectUtils.isTcStaff(tcSubject)) { + uri.setParameter("memberId", String.valueOf(tcSubject.getUserId())); + } + + DefaultHttpClient httpClient = new DefaultHttpClient(); + + HttpGet getRequest = new HttpGet(uri.build()); + getLogger().log(Level.INFO, "Getting Group with thi uri: " + uri.build().toString()); + + String v3Token = new JwtTokenUpdater().check().getToken(); + + getRequest.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + v3Token); + + getRequest.addHeader(HttpHeaders.ACCEPT, "application/json"); + HttpResponse httpResponse = httpClient.execute(getRequest); + + HttpEntity entity = httpResponse.getEntity(); + + if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + throw new Exception("Unable to get groups from the API:" + httpResponse.getStatusLine().getReasonPhrase()); + } + + JsonNode result = objectMapper.readTree(entity.getContent()); + JsonNode groups = result.path("result").path("content"); + Set> groupResults = new HashSet>(); + for (JsonNode group : groups) { + Map gr = new HashMap(); + gr.put("id", group.get("id").asText()); + gr.put("name", group.get("name").asText()); + groupResults.add(gr); + } + setResult(groupResults); + } catch (Throwable e) { + if (getModel() != null) { + setResult(e); + } + } + return SUCCESS; + } + + public String getUserGroupsApiEndpoint() { + return userGroupsApiEndpoint; + } + + public void setUserGroupsApiEndpoint(String userGroupsApiEndpoint) { + this.userGroupsApiEndpoint = userGroupsApiEndpoint; + } } diff --git a/src/web/scripts/launch/main.js b/src/web/scripts/launch/main.js index 2ce7ea686..ce71f9a7c 100644 --- a/src/web/scripts/launch/main.js +++ b/src/web/scripts/launch/main.js @@ -215,7 +215,6 @@ $(document).ready(function() { data: {}, cache: false, dataType: 'json', - async : false, success: function (jsonResult) { handleJsonResult(jsonResult, function(result) { @@ -228,30 +227,6 @@ $(document).ready(function() { billingInfos = result.billingInfos; copilotFees = result.copilotFees; if (typeof jQuery_1_11_1 !== 'undefined' && jQuery_1_11_1 !== null) { - securityGroups = result.groups; - securityGroups.sort(sortByname); - var ms_group = jQuery_1_11_1("#groups").magicSuggest({ - placeholder: 'Type group name here', - allowFreeEntries: false, - data: securityGroups, - disabled: securityGroups.length > 0 ? false : true - }); - jQuery_1_11_1(ms_group).on('selectionchange', function(e,m){ - if (groupCancel){ - return; - } - if (this.getValue().length > 0 && jQuery_1_11_1("#preRegisterUsers").magicSuggest().getValue().length > 0){ - displayWarning("#yesNoConfirmation", "Confirmation", "Changing group will remove all assigned members.\n" + - "Do you want to proceed?", "OK", function(){ - jQuery_1_11_1("#preRegisterUsers").magicSuggest().clear(); - closeModal(); - }, "CANCEL", function(){ - jQuery_1_11_1("#groups").magicSuggest().clear(); - closeModal(); - }); - - } - }); platforms = result.platforms; platforms.sort(sortByname); jQuery_1_11_1("#platforms").magicSuggest({ @@ -325,7 +300,48 @@ $(document).ready(function() { }) } }); - + $.ajax({ + type: 'POST', + url: ctx+"/launch/getGroups", + cache: false, + dataType: 'json', + success: function (jsonResult) { + handleJsonResult(jsonResult, + function(result) { + if (typeof jQuery_1_11_1 !== 'undefined' && jQuery_1_11_1 !== null) { + console.log(result); + securityGroups = $.map(result, function(val){ + return {id: Number(val["id"]), name: val["name"]}; + }); + securityGroups.sort(sortByname); + var ms_group = jQuery_1_11_1("#groups").magicSuggest({ + placeholder: 'Type group name here', + allowFreeEntries: false, + data: securityGroups, + disabled: securityGroups.length > 0 ? false : true + }); + jQuery_1_11_1(ms_group).on('selectionchange', function(e,m){ + if (groupCancel){ + return; + } + if (this.getValue().length > 0 && jQuery_1_11_1("#preRegisterUsers").magicSuggest().getValue().length > 0){ + displayWarning("#yesNoConfirmation", "Confirmation", "Changing group will remove all assigned members.\n" + + "Do you want to proceed?", "OK", function(){ + jQuery_1_11_1("#preRegisterUsers").magicSuggest().clear(); + closeModal(); + }, "CANCEL", function(){ + jQuery_1_11_1("#groups").magicSuggest().clear(); + closeModal(); + }); + } + }); + } + }, + function(errorMessage) { + showServerError(errorMessage); + }); + } + }); // multiple prizes add for studio $('.prizesInner .studioAdd').click(function(){ if($('#extraPrizes').is( ":hidden ")){ diff --git a/token.properties.docker b/token.properties.docker index 6994c98bf..38d6d1a34 100644 --- a/token.properties.docker +++ b/token.properties.docker @@ -365,3 +365,4 @@ @directChallengeServicesApiUrl@=http://api.topcoder-dev.com/v3/direct/challenges @authorizationUrl@=http://api.topcoder-dev.com/v3/authorizations @ssoLoginUrl@=https://topcoder-dev.com/login/ +@userGroupsApiEndpoint@=http://172.18.0.1:8080/v3/groups \ No newline at end of file diff --git a/token.properties.example b/token.properties.example index 337430927..f9ae88877 100644 --- a/token.properties.example +++ b/token.properties.example @@ -417,6 +417,9 @@ @member.profile.url.base@=http://tc.cloud.topcoder.com @memberSearchApiUrl@=https://tc-api.cloud.topcoder.com:8443/v3/members/_suggest/ +@groupMemberSearchApiUrl@=https://cockpit.cloud.topcoder.com/direct/group/member?handle= +@groupMemberApiUrl@=http://172.18.0.1:8080/v3/groups/%d/members @directChallengeServicesApiUrl@=http://api.topcoder-dev.com/v3/direct/challenges @authorizationUrl@=http://api.topcoder-dev.com/v3/authorizations -@ssoLoginUrl@=https://topcoder-dev.com/login/ \ No newline at end of file +@ssoLoginUrl@=https://topcoder-dev.com/login/ +@userGroupsApiEndpoint@=http://172.18.0.1:8080/v3/groups \ No newline at end of file From 5c14a9351f5e6b0955f98f0d7de8cdfcb7955aa6 Mon Sep 17 00:00:00 2001 From: Deddy Syefria Date: Fri, 13 Oct 2017 01:55:06 +0700 Subject: [PATCH 3/3] improve group selection --- conf/web/WEB-INF/applicationContext.xml | 1 + .../action/contest/launch/CommonAction.java | 67 ++------- .../launch/SaveDraftContestAction.java | 33 +++-- .../services/view/util/DirectUtils.java | 129 +++++++++++++----- .../scripts/launch/contestDetailSoftware.js | 29 ++-- src/web/scripts/launch/main.js | 68 +++++---- src/web/scripts/magicsuggest.js | 11 +- 7 files changed, 185 insertions(+), 153 deletions(-) diff --git a/conf/web/WEB-INF/applicationContext.xml b/conf/web/WEB-INF/applicationContext.xml index 53dd3fd32..431f8f5dd 100644 --- a/conf/web/WEB-INF/applicationContext.xml +++ b/conf/web/WEB-INF/applicationContext.xml @@ -425,6 +425,7 @@ + @@ -137,15 +123,10 @@ public class CommonAction extends BaseContestFeeAction { private long categoryId; - private String userGroupsApiEndpoint; - /** - * The jackson object mapping which is used to deserialize json return from API to domain model. + * Endpoint to group of a user */ - protected static final ObjectMapper objectMapper; - static { - objectMapper = new ObjectMapper(); - } + private String userGroupsApiEndpoint; /** *

@@ -577,40 +558,8 @@ public void setCategoryId(long categoryId) { public String getGroups() { try { TCSubject tcSubject = DirectUtils.getTCSubjectFromSession(); - URIBuilder uri = new URIBuilder(userGroupsApiEndpoint); - - if (!DirectUtils.isCockpitAdmin(tcSubject) && !DirectUtils.isTcStaff(tcSubject)) { - uri.setParameter("memberId", String.valueOf(tcSubject.getUserId())); - } - - DefaultHttpClient httpClient = new DefaultHttpClient(); - - HttpGet getRequest = new HttpGet(uri.build()); - getLogger().log(Level.INFO, "Getting Group with thi uri: " + uri.build().toString()); - - String v3Token = new JwtTokenUpdater().check().getToken(); - - getRequest.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + v3Token); - - getRequest.addHeader(HttpHeaders.ACCEPT, "application/json"); - HttpResponse httpResponse = httpClient.execute(getRequest); - - HttpEntity entity = httpResponse.getEntity(); - - if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { - throw new Exception("Unable to get groups from the API:" + httpResponse.getStatusLine().getReasonPhrase()); - } - - JsonNode result = objectMapper.readTree(entity.getContent()); - JsonNode groups = result.path("result").path("content"); - Set> groupResults = new HashSet>(); - for (JsonNode group : groups) { - Map gr = new HashMap(); - gr.put("id", group.get("id").asText()); - gr.put("name", group.get("name").asText()); - groupResults.add(gr); - } - setResult(groupResults); + Set projectGroups = DirectUtils.getGroups(tcSubject, userGroupsApiEndpoint); + setResult(projectGroups); } catch (Throwable e) { if (getModel() != null) { setResult(e); diff --git a/src/java/main/com/topcoder/direct/services/view/action/contest/launch/SaveDraftContestAction.java b/src/java/main/com/topcoder/direct/services/view/action/contest/launch/SaveDraftContestAction.java index 3ec0534ef..c72093d02 100644 --- a/src/java/main/com/topcoder/direct/services/view/action/contest/launch/SaveDraftContestAction.java +++ b/src/java/main/com/topcoder/direct/services/view/action/contest/launch/SaveDraftContestAction.java @@ -742,6 +742,11 @@ public class SaveDraftContestAction extends ContestAction { */ private Double customCopilotFee; + /** + * Endpoint to group of a user + */ + private String userGroupsApiEndpoint; + /** *

* Creates a SaveDraftContestAction instance. @@ -1066,25 +1071,15 @@ public boolean evaluate(Object object) { populateSoftwareCompetition(softwareCompetition); } - //set groups - List groupsList = new ArrayList(); - //read-only group we need to set it with current group - if (!DirectUtils.isRole(getCurrentUser(), ADMIN_ROLE) && !DirectUtils.isRole(getCurrentUser(), TC_STAFF_ROLE) && projectId > 0) { - groupsList = getContestServiceFacadeWithISE().getGroupForContest(projectId, false); - groups = null; - } + //do backend validation for groups here + List projectGroups = new ArrayList(); if (groups != null && groups.size() > 0) { - // get the TCSubject from session - ProjectGroup[] allProjectGroups = getContestServiceFacade().getAllProjectGroups(DirectStrutsActionsHelper.getTCSubjectFromSession()); for (String groupId : groups) { - for (ProjectGroup projectGroup : allProjectGroups) { - if (Long.valueOf(groupId).equals(projectGroup.getId())) { - groupsList.add(projectGroup); - } - } + projectGroups.add(new ProjectGroup(Long.valueOf(groupId), "")); } } - softwareCompetition.getProjectHeader().setGroups(groupsList); + + softwareCompetition.getProjectHeader().setGroups(projectGroups); // remove the thurgood information if needed if(softwareCompetition.getProjectHeader().getProperties().containsKey(THURGOOD_PLATFORM_KEY)) { @@ -2565,6 +2560,14 @@ public void setPreRegisterUsers(String preRegisterUsers) { this.preRegisterUsers = preRegisterUsers; } + public String getUserGroupsApiEndpoint() { + return userGroupsApiEndpoint; + } + + public void setUserGroupsApiEndpoint(String userGroupsApiEndpoint) { + this.userGroupsApiEndpoint = userGroupsApiEndpoint; + } + /** * Pre-Register users to a given project * diff --git a/src/java/main/com/topcoder/direct/services/view/util/DirectUtils.java b/src/java/main/com/topcoder/direct/services/view/util/DirectUtils.java index f6ca747fd..118ef2057 100644 --- a/src/java/main/com/topcoder/direct/services/view/util/DirectUtils.java +++ b/src/java/main/com/topcoder/direct/services/view/util/DirectUtils.java @@ -16,18 +16,9 @@ import com.topcoder.direct.services.project.metadata.entities.dao.DirectProjectMetadata; import com.topcoder.direct.services.view.action.AbstractAction; import com.topcoder.direct.services.view.action.BaseDirectStrutsAction; -import com.topcoder.direct.services.view.dto.contest.TermOfUse; import com.topcoder.direct.services.view.action.specreview.ViewSpecificationReviewActionResultData; import com.topcoder.direct.services.view.dto.IdNamePair; -import com.topcoder.direct.services.view.dto.contest.BaseContestCommonDTO; -import com.topcoder.direct.services.view.dto.contest.ContestBriefDTO; -import com.topcoder.direct.services.view.dto.contest.ContestDashboardDTO; -import com.topcoder.direct.services.view.dto.contest.ContestRoundType; -import com.topcoder.direct.services.view.dto.contest.ContestStatsDTO; -import com.topcoder.direct.services.view.dto.contest.ContestStatus; -import com.topcoder.direct.services.view.dto.contest.PhasedContestDTO; -import com.topcoder.direct.services.view.dto.contest.ProjectPhaseDTO; -import com.topcoder.direct.services.view.dto.contest.ProjectPhaseType; +import com.topcoder.direct.services.view.dto.contest.*; import com.topcoder.direct.services.view.dto.cost.CostDTO; import com.topcoder.direct.services.view.dto.project.ProjectBriefDTO; import com.topcoder.direct.services.view.interceptor.SecurityGroupsAccessInterceptor; @@ -73,15 +64,26 @@ import com.topcoder.shared.util.DBMS; import com.topcoder.shared.util.dwload.CacheClearer; import com.topcoder.web.common.CachedDataAccess; +import com.topcoder.web.common.cache.CacheClient; +import com.topcoder.web.common.cache.CacheClientFactory; import com.topcoder.web.common.cache.MaxAge; import eu.medsea.mimeutil.MimeType; import eu.medsea.mimeutil.MimeUtil; import org.apache.axis.encoding.Base64; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHeaders; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.impl.client.DefaultHttpClient; import org.apache.log4j.Logger; import org.apache.struts2.ServletActionContext; import org.apache.struts2.util.TokenHelper; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.ObjectMapper; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; @@ -95,14 +97,7 @@ import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.nio.channels.FileLock; import java.sql.Connection; import java.sql.PreparedStatement; @@ -110,23 +105,7 @@ import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; +import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; @@ -952,6 +931,14 @@ public final class DirectUtils { private static final String QUERY_GET_SECURITY_GROUP_FROM_ID = "SELECT group_id, description FROM security_groups " + " WHERE group_id in ("; + /** + * The jackson object mapping which is used to deserialize json return from API to domain model. + */ + protected static final ObjectMapper objectMapper; + static { + objectMapper = new ObjectMapper(); + } + /** *

* Default Constructor. @@ -3832,4 +3819,76 @@ public static List getGroupIdAndName(List projectGro DatabaseUtils.close(con); } } + + /** + * Get group from group API. + * + * @param tcSubject tcSubject of user + * @param endpoint endpoint url + * @return set of group + * @throws Exception + */ + public static Set getGroupsFromApi(TCSubject tcSubject, String endpoint) throws Exception { + URIBuilder uri = new URIBuilder(endpoint); + + if (!DirectUtils.isCockpitAdmin(tcSubject) && !DirectUtils.isTcStaff(tcSubject)) { + uri.setParameter("memberId", String.valueOf(tcSubject.getUserId())); + } + + DefaultHttpClient httpClient = new DefaultHttpClient(); + + HttpGet getRequest = new HttpGet(uri.build()); + logger.info("Getting Group with thi uri: " + uri.build().toString()); + + String v3Token = new JwtTokenUpdater().check().getToken(); + + getRequest.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + v3Token); + + getRequest.addHeader(HttpHeaders.ACCEPT, "application/json"); + HttpResponse httpResponse = httpClient.execute(getRequest); + + HttpEntity entity = httpResponse.getEntity(); + + if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + throw new Exception("Unable to get groups from the API:" + httpResponse.getStatusLine().getReasonPhrase()); + } + + JsonNode result = objectMapper.readTree(entity.getContent()); + JsonNode groups = result.path("result").path("content"); + Set groupResults = new HashSet(); + for (JsonNode group : groups) { + ProjectGroup pg = new ProjectGroup(group.get("id").asLong(), group.get("name").asText()); + groupResults.add(pg); + } + return groupResults; + } + + /** + * Get groups. Get from cache first if none then get from api + * + * @param tcSubject tcSubject of user + * @param endpoint endpoint url + * @return set of groupfor user + * @throws Exception + */ + public static Set getGroups(TCSubject tcSubject, String endpoint) throws Exception { + CacheClient cc = null; + Set projectGroups = null; + SortedCacheAddress cacheAddress = new SortedCacheAddress(tcSubject.getUserId()); + try { + cc = CacheClientFactory.create(); + projectGroups = (Set) cc.get(cacheAddress); + } catch (Exception e) { + logger.info("Can't get group for user " + tcSubject.getUserId() + " from cache"); + } + if (projectGroups == null) { + projectGroups = DirectUtils.getGroupsFromApi(tcSubject, endpoint); + try { + cc.set(cacheAddress, projectGroups, MaxAge.HOUR); + } catch (Exception e) { + logger.error("Failed to put user group into cache ", e); + } + } + return projectGroups; + } } \ No newline at end of file diff --git a/src/web/scripts/launch/contestDetailSoftware.js b/src/web/scripts/launch/contestDetailSoftware.js index bd64d5fb6..2a928d85a 100644 --- a/src/web/scripts/launch/contestDetailSoftware.js +++ b/src/web/scripts/launch/contestDetailSoftware.js @@ -345,6 +345,21 @@ $(document).ready(function(){ updateMCEPlaceHolderCtl(); }; setTimeout(waitForMCE, 500); + //update group + var currentData = jQuery_1_11_1("#groups").magicSuggest().getData(); + var currentGroupIds = currentData.map(function(val){return val.id}); + var additionGroups = mainWidget.softwareCompetition.groups.filter(function(val){ + return !(currentGroupIds.indexOf(val) > 0)}); + + $(mainWidget.softwareCompetition.groups).each(function(i, val){ + if (additionGroups.indexOf(val) > -1){ + currentData.unshift({id: val, name: mainWidget.softwareCompetition.groupNames[i], disabled: true, selected: true}) + } + }); + groupCancel = true; + jQuery_1_11_1("#groups").magicSuggest().setData(currentData); + jQuery_1_11_1("#groups").magicSuggest().setValue(mainWidget.softwareCompetition.groups); + groupCancel = false; }, function(errorMessage) { showServerError(errorMessage); @@ -1212,20 +1227,8 @@ function initContest(contestJson) { } $(".drHide").hide(); - - if (securityGroups.length < 1 && mainWidget.softwareCompetition.groups.length > 0) { - var allGroups = []; - $.each(mainWidget.softwareCompetition.groups, function(i, val){ - allGroups.push({id: val, name: mainWidget.softwareCompetition.groupNames[i]}); - }); - jQuery_1_11_1("#groups").magicSuggest().setData(allGroups); - } - - jQuery_1_11_1("#groups").magicSuggest().setValue(mainWidget.softwareCompetition.groups); - } - /** * Retrieve contest cost without admin fee. */ @@ -2690,6 +2693,7 @@ function populateSpecSection(initFlag) { //technlogies jQuery_1_11_1("#technologies").magicSuggest().setValue(mainWidget.softwareCompetition.assetDTO.directjsTechnologies); var technologyMap = {}; + var technologies = jQuery_1_11_1("#technologies").magicSuggest().getData(); $.each(technologies, function(i, val){ technologyMap[''+val.id]=val.name; }); @@ -2705,6 +2709,7 @@ function populateSpecSection(initFlag) { //platforms jQuery_1_11_1("#platforms").magicSuggest().setValue(mainWidget.softwareCompetition.platforms); var platformMap = {}; + var platforms=jQuery_1_11_1("#platforms").magicSuggest().getData(); $.each(platforms, function(i, val){ platformMap[''+val.id]=val.name; }); diff --git a/src/web/scripts/launch/main.js b/src/web/scripts/launch/main.js index ce71f9a7c..2f36a90a5 100644 --- a/src/web/scripts/launch/main.js +++ b/src/web/scripts/launch/main.js @@ -202,12 +202,37 @@ var swDocuments = []; // represents project id of reporting contest type. var REPORTING_ID = "36"; -var securityGroups = []; var groupCancel = false; +var members = []; /** * Configuration/General Set up */ $(document).ready(function() { +//configured group input first + if (typeof jQuery_1_11_1 !== 'undefined' && jQuery_1_11_1 !== null) { + var ms_group = jQuery_1_11_1("#groups").magicSuggest({ + placeholder: 'Type group name here', + allowFreeEntries: false, + data: [], + disabled: true + }); + jQuery_1_11_1(ms_group).on('selectionchange', function(e,m){ + if (groupCancel){ + return; + } + if (this.getValue().length > 0 && jQuery_1_11_1("#preRegisterUsers").magicSuggest().getValue().length > 0){ + displayWarning("#yesNoConfirmation", "Confirmation", "Changing group will remove all assigned members.\n" + + "Do you want to proceed?", "OK", function(){ + jQuery_1_11_1("#preRegisterUsers").magicSuggest().clear(); + closeModal(); + }, "CANCEL", function(){ + jQuery_1_11_1("#groups").magicSuggest().clear(); + jQuery_1_11_1("#groups").magicSuggest().setValue(mainWidget.softwareCompetition.groups) + closeModal(); + }); + } + }); + } // loading some configuration data $.ajax({ type: 'POST', @@ -215,6 +240,7 @@ $(document).ready(function() { data: {}, cache: false, dataType: 'json', + async: false, success: function (jsonResult) { handleJsonResult(jsonResult, function(result) { @@ -227,14 +253,14 @@ $(document).ready(function() { billingInfos = result.billingInfos; copilotFees = result.copilotFees; if (typeof jQuery_1_11_1 !== 'undefined' && jQuery_1_11_1 !== null) { - platforms = result.platforms; + var platforms = result.platforms; platforms.sort(sortByname); jQuery_1_11_1("#platforms").magicSuggest({ placeholder: 'Type platform name here', allowFreeEntries: false, data: platforms }); - technologies = result.technologies; + var technologies = result.technologies; technologies.sort(sortByname); jQuery_1_11_1("#technologies").magicSuggest({ placeholder: 'Type technology name here', @@ -246,7 +272,7 @@ $(document).ready(function() { allowFreeEntries: false, hideTrigger: true, data: function (q) { - var members = []; + members=[]; var url, data; if (jQuery_1_11_1("#groups").magicSuggest().getValue().length > 0){ url = group_member_api_url; @@ -290,7 +316,8 @@ $(document).ready(function() { } }) } - return members.sort(sortByname); + members.sort(sortByname); + return members; } }); } @@ -301,40 +328,23 @@ $(document).ready(function() { } }); $.ajax({ - type: 'POST', + type: 'GET', url: ctx+"/launch/getGroups", cache: false, dataType: 'json', + contentType: 'application/json; charset=utf-8', success: function (jsonResult) { handleJsonResult(jsonResult, function(result) { if (typeof jQuery_1_11_1 !== 'undefined' && jQuery_1_11_1 !== null) { - console.log(result); - securityGroups = $.map(result, function(val){ + var securityGroups = result.map(function(val){ return {id: Number(val["id"]), name: val["name"]}; }); securityGroups.sort(sortByname); - var ms_group = jQuery_1_11_1("#groups").magicSuggest({ - placeholder: 'Type group name here', - allowFreeEntries: false, - data: securityGroups, - disabled: securityGroups.length > 0 ? false : true - }); - jQuery_1_11_1(ms_group).on('selectionchange', function(e,m){ - if (groupCancel){ - return; - } - if (this.getValue().length > 0 && jQuery_1_11_1("#preRegisterUsers").magicSuggest().getValue().length > 0){ - displayWarning("#yesNoConfirmation", "Confirmation", "Changing group will remove all assigned members.\n" + - "Do you want to proceed?", "OK", function(){ - jQuery_1_11_1("#preRegisterUsers").magicSuggest().clear(); - closeModal(); - }, "CANCEL", function(){ - jQuery_1_11_1("#groups").magicSuggest().clear(); - closeModal(); - }); - } - }); + if (securityGroups.length>0){ + jQuery_1_11_1("#groups").magicSuggest().setData(securityGroups); + jQuery_1_11_1("#groups").magicSuggest().enable(); + } } }, function(errorMessage) { diff --git a/src/web/scripts/magicsuggest.js b/src/web/scripts/magicsuggest.js index 3ff463df7..e23b37af1 100644 --- a/src/web/scripts/magicsuggest.js +++ b/src/web/scripts/magicsuggest.js @@ -357,7 +357,9 @@ /** * type to validate against */ - vtype: null + vtype: null, + + selected: 'selected' }; var conf = $.extend({},options); @@ -1040,8 +1042,11 @@ 'class': 'ms-sel-item ' + cfg.selectionCls + validCls, html: selectedItemHtml }).data('json', value); - - if(cfg.disabled === false){ + var canDelete = true; + if (typeof(value[cfg.selected]) !== 'undefined' && value[cfg.selected] === true) { + canDelete = false; + } + if(cfg.disabled === false && canDelete){ // small cross img delItemEl = $('', { 'class': 'ms-close-btn'