Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* This file is part of the Meeds project (https://meeds.io/).
*
* Copyright (C) 2020 - 2025 Meeds Lab contact@meedslab.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package io.meeds.crowdin.gamification.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class RemoteApproval implements Cloneable {

private long id;

private String userName;

private String translationId;

private String stringId;

private String languageId;


}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
package io.meeds.crowdin.gamification.plugin;

import io.meeds.crowdin.gamification.model.Event;
import io.meeds.crowdin.gamification.model.RemoteApproval;
import io.meeds.crowdin.gamification.services.CrowdinTriggerService;
import io.meeds.gamification.model.RealizationDTO;
import io.meeds.crowdin.gamification.services.WebhookService;
import io.meeds.gamification.service.RealizationService;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -41,6 +42,9 @@ public class SuggestionApprovedTriggerPlugin extends CrowdinTriggerPlugin {
@Autowired
private RealizationService realizationService;

@Autowired
private WebhookService webhookService;

@PostConstruct
public void init() {
crowdinTriggerService.addPlugin(this);
Expand All @@ -51,7 +55,7 @@ public List<Event> getEvents(String trigger, Map<String, Object> payload) {
String objectId = constructObjectIdAsJsonString(payload, TRANSLATION);

List<Event> eventList = new ArrayList<>();
eventList.add(new Event(APPROVE_SUGGESTION_EVENT_NAME,
eventList.add(new Event(SUGGESTION_APPROVED_EVENT_NAME,
extractSubItem(payload, TRANSLATION, USER, USERNAME),
extractSubItem(payload, TRANSLATION, USER, USERNAME),
objectId,
Expand All @@ -63,13 +67,14 @@ public List<Event> getEvents(String trigger, Map<String, Object> payload) {
trigger.equals(SUGGESTION_DISAPPROVED_TRIGGER),
countWords(extractSubItem(payload, TRANSLATION, STRING, TEXT))));

List<RealizationDTO> realizations = realizationService.findRealizationsByObjectIdAndObjectType(objectId, TRANSLATION);
// Retrieve who made that approval by calling Crowdin API
String translationId = extractSubItem(payload, TRANSLATION, ID);
RemoteApproval remoteApproval = webhookService.getApproval(getProjectId(payload), translationId);

if (!realizations.isEmpty()) {
String earnerId = realizations.get(0).getEarnerId();
eventList.add(new Event(SUGGESTION_APPROVED_EVENT_NAME,
earnerId,
earnerId,
if (remoteApproval != null) {
eventList.add(new Event(APPROVE_SUGGESTION_EVENT_NAME,
remoteApproval.getUserName(),
remoteApproval.getUserName(),
objectId,
TRANSLATION,
getProjectId(payload),
Expand All @@ -80,7 +85,6 @@ public List<Event> getEvents(String trigger, Map<String, Object> payload) {
countWords(extractSubItem(payload, TRANSLATION, STRING, TEXT))));

}

return eventList;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package io.meeds.crowdin.gamification.services;

import io.meeds.crowdin.gamification.model.RemoteApproval;
import io.meeds.crowdin.gamification.model.RemoteDirectory;
import io.meeds.crowdin.gamification.model.RemoteProject;
import io.meeds.crowdin.gamification.model.WebHook;
Expand Down Expand Up @@ -56,10 +57,18 @@ public List<RemoteProject> getProjects(String accessToken) throws IllegalAccessE
return crowdinConsumerStorage.getProjects(accessToken);
}

public RemoteApproval getApproval(String projectId, String translationId) {
WebHook webHook = webHookStorage.getWebhookByProjectId(Long.parseLong(projectId));
if (webHook != null) {
return crowdinConsumerStorage.getApproval(webHook.getToken(), projectId, translationId);
}
return null;
}

public WebHook createWebhook(long projectId,
String projectName,
String accessToken,
String currentUser) throws ObjectAlreadyExistsException, IllegalAccessException {
String projectName,
String accessToken,
String currentUser) throws ObjectAlreadyExistsException, IllegalAccessException {
if (!Utils.isRewardingManager(currentUser)) {
throw new IllegalAccessException("The user is not authorized to create Crowdin hook");
}
Expand All @@ -80,7 +89,7 @@ public WebHook createWebhook(long projectId,
}

public void updateWebHookAccessToken(long webHookId, String accessToken, String currentUser) throws IllegalAccessException,
ObjectNotFoundException {
ObjectNotFoundException {
if (!Utils.isRewardingManager(currentUser)) {
throw new IllegalAccessException(AUTHORIZED_TO_ACCESS_CROWDIN_HOOKS);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package io.meeds.crowdin.gamification.storage;

import io.meeds.crowdin.gamification.model.*;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
Expand All @@ -39,10 +40,6 @@
import org.exoplatform.services.log.Log;

import io.meeds.crowdin.gamification.exception.CrowdinConnectionException;
import io.meeds.crowdin.gamification.model.RemoteDirectory;
import io.meeds.crowdin.gamification.model.RemoteLanguage;
import io.meeds.crowdin.gamification.model.RemoteProject;
import io.meeds.crowdin.gamification.model.WebHook;

import org.json.JSONArray;
import org.json.JSONObject;
Expand Down Expand Up @@ -235,7 +232,7 @@ private String processDelete(URI uri, String accessToken) throws CrowdinConnecti
private String processRequest(HttpClient httpClient, HttpRequestBase request) throws IOException, CrowdinConnectionException {
HttpResponse response = httpClient.execute(request);
boolean isSuccess = response != null
&& (response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300);
&& (response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300);
if (isSuccess) {
return processSuccessResponse(response);
} else if (response != null && response.getStatusLine().getStatusCode() == 404) {
Expand All @@ -250,8 +247,8 @@ private String processSuccessResponse(HttpResponse response) throws IOException
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NO_CONTENT) {
return String.valueOf(HttpStatus.SC_NO_CONTENT);
} else if ((response.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED
|| response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) && response.getEntity() != null
&& response.getEntity().getContentLength() != 0) {
|| response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) && response.getEntity() != null
&& response.getEntity().getContentLength() != 0) {
try (InputStream is = response.getEntity().getContent()) {
return IOUtils.toString(is, StandardCharsets.UTF_8);
}
Expand Down Expand Up @@ -281,8 +278,8 @@ private HttpClient getHttpClient() {
if (client == null) {
HttpClientConnectionManager clientConnectionManager = getClientConnectionManager();
HttpClientBuilder httpClientBuilder = HttpClients.custom()
.setConnectionManager(clientConnectionManager)
.setConnectionReuseStrategy(new DefaultConnectionReuseStrategy());
.setConnectionManager(clientConnectionManager)
.setConnectionReuseStrategy(new DefaultConnectionReuseStrategy());
client = httpClientBuilder.build();
}
return client;
Expand Down Expand Up @@ -380,6 +377,33 @@ public List<RemoteDirectory> getProjectDirectories(long remoteProjectId,
}
}

public RemoteApproval getApproval(String accessToken, String projectId, String translationId) {
try {
URI uri = URI.create(CROWDIN_API_URL + PROJECTS + projectId + APPROVALS + translationId);

String response = processGet(uri, accessToken);
JSONObject jsonObject = new JSONObject(response).getJSONObject("data");

RemoteApproval remoteApproval = new RemoteApproval();

JSONObject userJsonObject = jsonObject.getJSONObject("user");

remoteApproval.setId(jsonObject.getInt("id"));

remoteApproval.setUserName(userJsonObject.getString("username"));
remoteApproval.setTranslationId(jsonObject.getString("translationId"));
remoteApproval.setLanguageId(jsonObject.getString("translationId"));
remoteApproval.setStringId(jsonObject.getString("stringId"));
remoteApproval.setLanguageId(jsonObject.getString("languageId"));

return remoteApproval;
} catch (CrowdinConnectionException e) {
LOG.warn("Unable to retrieve approval for crowdin translation with id {}.", translationId, e);
return null;
}
}


public void clearCache() {
// implemented in cached storage
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class Utils {
public static final String CONNECTOR_NAME = "crowdin";

public static final String[] CROWDIN_EVENTS = new String[] { "stringComment.created",
"stringComment.deleted", "suggestion.added", "suggestion.deleted", "suggestion.approved", "suggestion.disapproved" };
"stringComment.deleted", "suggestion.added", "suggestion.deleted", "suggestion.approved", "suggestion.disapproved" };

public static final String PROJECT_ID = "projectId";

Expand All @@ -34,6 +34,8 @@ public class Utils {

public static final String WEBHOOKS = "/webhooks/";

public static final String APPROVALS = "/approvals/";

public static final String AUTHORIZED_TO_ACCESS_CROWDIN_HOOKS = "The user is not authorized to access crowdin Hooks";

public static final String GAMIFICATION_GENERIC_EVENT = "exo.gamification.generic.action";
Expand All @@ -57,15 +59,15 @@ public class Utils {
public static final String LANGUAGE_ID = "languageId";

public static final String MUST_BE_HUMAN = "mustBeHuman";

public static final String TOTAL_TARGET_ITEM = "totalTargetItem";

public static final String STRING = "string";

public static final String TARGET_LANGUAGE = "targetLanguage";

public static final String PROJECT = "project";

public static final String TEXT = "text";

public static final String ID = "id";
Expand Down Expand Up @@ -180,7 +182,7 @@ public static String constructObjectIdAsJsonString(Map<String, Object> payload,
String sourceLanguageId = extractSubItem(payload, payloadObjectName, STRING, PROJECT, SOURCE_LANGUAGE_ID);
String targetLanguageId = extractSubItem(payload, payloadObjectName, TARGET_LANGUAGE, ID);
return "{\"id\":" + id + ",\"stringUrl\":\"https://crowdin.com/editor/" + projectSlug + "/" + fileId + "/" + sourceLanguageId
+ "-" + targetLanguageId + "?view=comfortable#" + stringId + "\"}";
+ "-" + targetLanguageId + "?view=comfortable#" + stringId + "\"}";
}

public static int countWords(String text) {
Expand Down
Loading