Improved the Social Post: removed the <br> char.
This commit is contained in:
parent
12c1a42138
commit
8d9e63b560
|
@ -673,7 +673,7 @@ public class ManageProductWidget extends Composite{
|
||||||
for(SimilarGRSFRecord sR: bean.getSimilarGrsfRecords()){
|
for(SimilarGRSFRecord sR: bean.getSimilarGrsfRecords()){
|
||||||
if(sR.isSuggestedMerge()){
|
if(sR.isSuggestedMerge()){
|
||||||
bean.setMergesInvolved(true);
|
bean.setMergesInvolved(true);
|
||||||
report += "\n\t - merge the current record with record '" + sR.getTitle() + " ;";
|
report += "\n\t - merge the current record with record '" + sR.getTitle() + "' ;";
|
||||||
report += "\n\t \t- GRSF Name '" + sR.getTitle() + "' ;";
|
report += "\n\t \t- GRSF Name '" + sR.getTitle() + "' ;";
|
||||||
report += "\n\t \t- Short Name '" + sR.getShortName() + "' ;";
|
report += "\n\t \t- Short Name '" + sR.getShortName() + "' ;";
|
||||||
report += "\n\t \t- URL '" + sR.getUrl() + "' ;";
|
report += "\n\t \t- URL '" + sR.getUrl() + "' ;";
|
||||||
|
|
|
@ -41,9 +41,10 @@ import org.json.simple.JSONObject;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For managing the different interactions with social channels (posts and mails)
|
* For managing the different interactions with social channels (posts and
|
||||||
|
* mails)
|
||||||
|
*
|
||||||
* @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
|
* @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
|
||||||
*/
|
*/
|
||||||
public class SocialCommunications {
|
public class SocialCommunications {
|
||||||
|
@ -66,7 +67,8 @@ public class SocialCommunications {
|
||||||
// for writing a post in the GRSF admin context
|
// for writing a post in the GRSF admin context
|
||||||
private static final String APPLICATION_ID_CATALOGUE_MANAGER = "org.gcube.datacatalogue.GRSFNotifier";
|
private static final String APPLICATION_ID_CATALOGUE_MANAGER = "org.gcube.datacatalogue.GRSFNotifier";
|
||||||
|
|
||||||
// emails to be sent to editors and reviewers and post to be written into the grsf admin vre
|
// emails to be sent to editors and reviewers and post to be written into the
|
||||||
|
// grsf admin vre
|
||||||
private static final String POST_MESSAGE = "Dear members,"
|
private static final String POST_MESSAGE = "Dear members,"
|
||||||
+ "\nThe record 'PRODUCT_TITLE' has been just updated by USER_FULLNAME."
|
+ "\nThe record 'PRODUCT_TITLE' has been just updated by USER_FULLNAME."
|
||||||
+ "\nYou can inspect it here: LINK_RECORD.";
|
+ "\nYou can inspect it here: LINK_RECORD.";
|
||||||
|
@ -89,7 +91,7 @@ public class SocialCommunications {
|
||||||
+ "<br>a revert operation (undo merge) has been requested on record RECORD_URL, by ADMIN_WHO_CHANGED.";
|
+ "<br>a revert operation (undo merge) has been requested on record RECORD_URL, by ADMIN_WHO_CHANGED.";
|
||||||
|
|
||||||
private static final String EMAIL_EDITOR_REVERT = "Dear ORIGINAL_USER,"
|
private static final String EMAIL_EDITOR_REVERT = "Dear ORIGINAL_USER,"
|
||||||
+"<br>a revert operation (undo merge) has been requested on this RECORD_URL you managed by ADMIN_WHO_CHANGED.";
|
+ "<br>a revert operation (undo merge) has been requested on this RECORD_URL you managed by ADMIN_WHO_CHANGED.";
|
||||||
|
|
||||||
// post on revert
|
// post on revert
|
||||||
private static final String POST_ON_REVERT = "Dear members,"
|
private static final String POST_ON_REVERT = "Dear members,"
|
||||||
|
@ -103,35 +105,38 @@ public class SocialCommunications {
|
||||||
* @param context
|
* @param context
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static String getBaseUrlSocialService(HttpServletRequest httpServletRequest){
|
public static String getBaseUrlSocialService(HttpServletRequest httpServletRequest) {
|
||||||
|
|
||||||
String context = ScopeProvider.instance.get();
|
String context = ScopeProvider.instance.get();
|
||||||
|
|
||||||
String keyPerContext = SOCIAL_NETWORKING_BASE_URL_SESSION_KEY + context;
|
String keyPerContext = SOCIAL_NETWORKING_BASE_URL_SESSION_KEY + context;
|
||||||
String basePath = (String) httpServletRequest.getSession().getAttribute(keyPerContext);
|
String basePath = (String) httpServletRequest.getSession().getAttribute(keyPerContext);
|
||||||
|
|
||||||
if(basePath == null){
|
if (basePath == null) {
|
||||||
try{
|
try {
|
||||||
|
|
||||||
SimpleQuery query = queryFor(GCoreEndpoint.class);
|
SimpleQuery query = queryFor(GCoreEndpoint.class);
|
||||||
query.addCondition(String.format("$resource/Profile/ServiceClass/text() eq '%s'",serviceClass));
|
query.addCondition(String.format("$resource/Profile/ServiceClass/text() eq '%s'", serviceClass));
|
||||||
query.addCondition("$resource/Profile/DeploymentData/Status/text() eq 'ready'");
|
query.addCondition("$resource/Profile/DeploymentData/Status/text() eq 'ready'");
|
||||||
query.addCondition(String.format("$resource/Profile/ServiceName/text() eq '%s'",serviceName));
|
query.addCondition(String.format("$resource/Profile/ServiceName/text() eq '%s'", serviceName));
|
||||||
query.setResult("$resource/Profile/AccessPoint/RunningInstanceInterfaces//Endpoint[@EntryName/string() eq \""+resource+"\"]/text()");
|
query.setResult(
|
||||||
|
"$resource/Profile/AccessPoint/RunningInstanceInterfaces//Endpoint[@EntryName/string() eq \""
|
||||||
|
+ resource + "\"]/text()");
|
||||||
|
|
||||||
DiscoveryClient<String> client = client();
|
DiscoveryClient<String> client = client();
|
||||||
List<String> endpoints = client.submit(query);
|
List<String> endpoints = client.submit(query);
|
||||||
if (endpoints == null || endpoints.isEmpty())
|
if (endpoints == null || endpoints.isEmpty())
|
||||||
throw new Exception("Cannot retrieve the GCoreEndpoint serviceName: "+serviceName +", serviceClass: " +serviceClass +", in scope: "+context);
|
throw new Exception("Cannot retrieve the GCoreEndpoint serviceName: " + serviceName
|
||||||
|
+ ", serviceClass: " + serviceClass + ", in scope: " + context);
|
||||||
|
|
||||||
basePath = endpoints.get(0);
|
basePath = endpoints.get(0);
|
||||||
if(basePath==null)
|
if (basePath == null)
|
||||||
throw new Exception("Endpoint:"+resource+", is null for serviceName: "+serviceName +", serviceClass: " +serviceClass +", in scope: "+context);
|
throw new Exception("Endpoint:" + resource + ", is null for serviceName: " + serviceName
|
||||||
|
+ ", serviceClass: " + serviceClass + ", in scope: " + context);
|
||||||
|
|
||||||
httpServletRequest.getSession().setAttribute(keyPerContext, basePath);
|
httpServletRequest.getSession().setAttribute(keyPerContext, basePath);
|
||||||
|
|
||||||
}catch(Exception e){
|
} catch (Exception e) {
|
||||||
logger.error("Unable to retrieve such service endpoint information!", e);
|
logger.error("Unable to retrieve such service endpoint information!", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,31 +146,35 @@ public class SocialCommunications {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Require a proper application token for writing a post and send messages.
|
* Require a proper application token for writing a post and send messages.
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static String requireApplicationToken(String serviceUrl) throws Exception{
|
private static String requireApplicationToken(String serviceUrl) throws Exception {
|
||||||
|
|
||||||
String currentScope = ScopeProvider.instance.get();
|
String currentScope = ScopeProvider.instance.get();
|
||||||
String tokenUser = SecurityTokenProvider.instance.get();
|
String tokenUser = SecurityTokenProvider.instance.get();
|
||||||
|
|
||||||
logger.info("Current scope for requireApplicationToken is " + currentScope + " and token is " + tokenUser.substring(0, 10) + "***************");
|
logger.info("Current scope for requireApplicationToken is " + currentScope + " and token is "
|
||||||
|
+ tokenUser.substring(0, 10) + "***************");
|
||||||
String basePath = serviceUrl;
|
String basePath = serviceUrl;
|
||||||
|
|
||||||
if(basePath == null){
|
if (basePath == null) {
|
||||||
|
|
||||||
logger.error("Unable to write a post because there is no social networking service available");
|
logger.error("Unable to write a post because there is no social networking service available");
|
||||||
throw new Exception("Unable to discover the social networking service");
|
throw new Exception("Unable to discover the social networking service");
|
||||||
|
|
||||||
}else{
|
} else {
|
||||||
|
|
||||||
basePath = basePath.endsWith("/") ? basePath : basePath + "/";
|
basePath = basePath.endsWith("/") ? basePath : basePath + "/";
|
||||||
|
|
||||||
try(CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build();){
|
try (CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy())
|
||||||
|
.build();) {
|
||||||
|
|
||||||
// ask token application
|
// ask token application
|
||||||
HttpPost postRequest = new HttpPost(basePath + SOCIAL_SERVICE_APPLICATION_TOKEN + "?gcube-token=" + tokenUser);
|
HttpPost postRequest = new HttpPost(
|
||||||
|
basePath + SOCIAL_SERVICE_APPLICATION_TOKEN + "?gcube-token=" + tokenUser);
|
||||||
JSONObject requestToken = new JSONObject();
|
JSONObject requestToken = new JSONObject();
|
||||||
requestToken.put("app_id", APPLICATION_ID_CATALOGUE_MANAGER);
|
requestToken.put("app_id", APPLICATION_ID_CATALOGUE_MANAGER);
|
||||||
StringEntity input = new StringEntity(requestToken.toJSONString());
|
StringEntity input = new StringEntity(requestToken.toJSONString());
|
||||||
|
@ -178,21 +187,21 @@ public class SocialCommunications {
|
||||||
if (response.getStatusLine().getStatusCode() != 201) {
|
if (response.getStatusLine().getStatusCode() != 201) {
|
||||||
throw new RuntimeException("Failed to retrieve application token : HTTP error code : "
|
throw new RuntimeException("Failed to retrieve application token : HTTP error code : "
|
||||||
+ response.getStatusLine().getStatusCode());
|
+ response.getStatusLine().getStatusCode());
|
||||||
}else{
|
} else {
|
||||||
|
|
||||||
Map<String, Object> mapResponseGeneratedToken = getResponseEntityAsJSON(response);
|
Map<String, Object> mapResponseGeneratedToken = getResponseEntityAsJSON(response);
|
||||||
boolean successGeneratedToken = (boolean)mapResponseGeneratedToken.get("success");
|
boolean successGeneratedToken = (boolean) mapResponseGeneratedToken.get("success");
|
||||||
if(!successGeneratedToken){
|
if (!successGeneratedToken) {
|
||||||
|
|
||||||
throw new RuntimeException("Failed to generate the token for the application!"
|
throw new RuntimeException("Failed to generate the token for the application!"
|
||||||
+ " Error message is " + mapResponseGeneratedToken.get("message"));
|
+ " Error message is " + mapResponseGeneratedToken.get("message"));
|
||||||
|
|
||||||
}else{
|
} else {
|
||||||
return (String)mapResponseGeneratedToken.get("result");
|
return (String) mapResponseGeneratedToken.get("result");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}catch(Exception e){
|
} catch (Exception e) {
|
||||||
logger.error("Failed to create a post", e);
|
logger.error("Failed to create a post", e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
@ -201,6 +210,7 @@ public class SocialCommunications {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify the users about the required changes.
|
* Notify the users about the required changes.
|
||||||
|
*
|
||||||
* @param bean
|
* @param bean
|
||||||
* @param url
|
* @param url
|
||||||
* @param username
|
* @param username
|
||||||
|
@ -210,54 +220,66 @@ public class SocialCommunications {
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static void writePostOnRevert(String serviceUrl, RevertableOperationInfo rInfo, boolean enablePostNotification, String userCurrentUrl) throws Exception{
|
public static void writePostOnRevert(String serviceUrl, RevertableOperationInfo rInfo,
|
||||||
|
boolean enablePostNotification, String userCurrentUrl) throws Exception {
|
||||||
|
|
||||||
// discover service endpoint for the social networking library
|
// discover service endpoint for the social networking library
|
||||||
String currentScope = ScopeProvider.instance.get();
|
String currentScope = ScopeProvider.instance.get();
|
||||||
String tokenUser = SecurityTokenProvider.instance.get();
|
String tokenUser = SecurityTokenProvider.instance.get();
|
||||||
|
|
||||||
logger.info("Current scope for writePostOnRevert is " + currentScope + " and token is " + tokenUser.substring(0, 10) + "***************");
|
logger.info("Current scope for writePostOnRevert is " + currentScope + " and token is "
|
||||||
|
+ tokenUser.substring(0, 10) + "***************");
|
||||||
String basePath = serviceUrl;
|
String basePath = serviceUrl;
|
||||||
|
|
||||||
if(basePath == null){
|
if (basePath == null) {
|
||||||
|
|
||||||
logger.error("Unable to write a post because there is no social networking service available");
|
logger.error("Unable to write a post because there is no social networking service available");
|
||||||
throw new Exception("Unable to discover the social networking service");
|
throw new Exception("Unable to discover the social networking service");
|
||||||
|
|
||||||
}else{
|
} else {
|
||||||
|
|
||||||
basePath = basePath.endsWith("/") ? basePath : basePath + "/";
|
basePath = basePath.endsWith("/") ? basePath : basePath + "/";
|
||||||
|
|
||||||
try(CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build();){
|
try (CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy())
|
||||||
|
.build();) {
|
||||||
|
|
||||||
// require url
|
// require url
|
||||||
String applicationToken = requireApplicationToken(serviceUrl);
|
String applicationToken = requireApplicationToken(serviceUrl);
|
||||||
|
|
||||||
//see Feature #17576 updated by Francesco
|
// see Feature #17576 updated by Francesco
|
||||||
/*final String profilePageURL = GCubePortalConstants.PREFIX_GROUP_URL + extractOrgFriendlyURL(userCurrentUrl) + GCubePortalConstants.USER_PROFILE_FRIENDLY_URL;
|
/*
|
||||||
String userFullNameHighlightedCurrent = "<a class=\"link\" href=\"" + profilePageURL + "?"+
|
* final String profilePageURL = GCubePortalConstants.PREFIX_GROUP_URL +
|
||||||
Base64.getEncoder().encodeToString(USER_PROFILE_OID.getBytes())+"="+
|
* extractOrgFriendlyURL(userCurrentUrl) +
|
||||||
Base64.getEncoder().encodeToString(rInfo.getUserNameCurrentAdmin().getBytes())+"\">"+rInfo.getFullNameCurrentAdmin()+
|
* GCubePortalConstants.USER_PROFILE_FRIENDLY_URL; String
|
||||||
"</a> ";
|
* userFullNameHighlightedCurrent = "<a class=\"link\" href=\"" + profilePageURL
|
||||||
*/
|
* + "?"+ Base64.getEncoder().encodeToString(USER_PROFILE_OID.getBytes())+"="+
|
||||||
|
* Base64.getEncoder().encodeToString(rInfo.getUserNameCurrentAdmin().getBytes()
|
||||||
|
* )+"\">"+rInfo.getFullNameCurrentAdmin()+ "</a> ";
|
||||||
|
*/
|
||||||
|
|
||||||
String userFullNameHighlightedCurrent = "@"+rInfo.getUserNameCurrentAdmin();
|
String userFullNameHighlightedCurrent = "@" + rInfo.getUserNameCurrentAdmin();
|
||||||
|
|
||||||
/*String userFullNameHighlightedOriginal = "<a class=\"link\" href=\"" + profilePageURL + "?"+
|
/*
|
||||||
Base64.getEncoder().encodeToString(USER_PROFILE_OID.getBytes())+"="+
|
* String userFullNameHighlightedOriginal = "<a class=\"link\" href=\"" +
|
||||||
Base64.getEncoder().encodeToString(rInfo.getUserNameOriginalAdmin().getBytes())+"\">"+rInfo.getFullNameOriginalAdmin()+
|
* profilePageURL + "?"+
|
||||||
"</a> ";*/
|
* Base64.getEncoder().encodeToString(USER_PROFILE_OID.getBytes())+"="+
|
||||||
|
* Base64.getEncoder().encodeToString(rInfo.getUserNameOriginalAdmin().getBytes(
|
||||||
|
* ))+"\">"+rInfo.getFullNameOriginalAdmin()+ "</a> ";
|
||||||
|
*/
|
||||||
|
|
||||||
String userFullNameHighlightedOriginal = "@"+rInfo.getUserNameOriginalAdmin();
|
String userFullNameHighlightedOriginal = "@" + rInfo.getUserNameOriginalAdmin();
|
||||||
// replace
|
// replace
|
||||||
String message = POST_ON_REVERT.replace("RECORD_URL", rInfo.getRecordUrl()).replace("ADMIN_WHO_CHANGED", userFullNameHighlightedCurrent).replace("ORIGINAL_USER",userFullNameHighlightedOriginal);
|
String message = POST_ON_REVERT.replace("RECORD_URL", rInfo.getRecordUrl())
|
||||||
|
.replace("ADMIN_WHO_CHANGED", userFullNameHighlightedCurrent)
|
||||||
|
.replace("ORIGINAL_USER", userFullNameHighlightedOriginal);
|
||||||
|
|
||||||
// add hashtag
|
// add hashtag
|
||||||
message +="\n\n";
|
message += "\n\n";
|
||||||
message += " #" + HashTagsOnUpdate.REVERTED_MERGE.getString();
|
message += " #" + HashTagsOnUpdate.REVERTED_MERGE.getString();
|
||||||
|
|
||||||
logger.info("The post that is going to be written is -> " + message);
|
logger.info("The post that is going to be written is -> " + message);
|
||||||
HttpPost postRequest = new HttpPost(basePath + SOCIAL_SERVICE_WRITE_APPLICATION_POST + "?gcube-token=" + applicationToken);
|
HttpPost postRequest = new HttpPost(
|
||||||
|
basePath + SOCIAL_SERVICE_WRITE_APPLICATION_POST + "?gcube-token=" + applicationToken);
|
||||||
JSONObject object = new JSONObject();
|
JSONObject object = new JSONObject();
|
||||||
object.put("text", message);
|
object.put("text", message);
|
||||||
object.put("enable_notification", enablePostNotification);
|
object.put("enable_notification", enablePostNotification);
|
||||||
|
@ -272,7 +294,7 @@ public class SocialCommunications {
|
||||||
throw new RuntimeException("Failed to write application post : HTTP error code : "
|
throw new RuntimeException("Failed to write application post : HTTP error code : "
|
||||||
+ response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
|
+ response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
|
||||||
|
|
||||||
}catch(Exception e){
|
} catch (Exception e) {
|
||||||
logger.error("Failed to create a post", e);
|
logger.error("Failed to create a post", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -280,6 +302,7 @@ public class SocialCommunications {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify the users about the required changes.
|
* Notify the users about the required changes.
|
||||||
|
*
|
||||||
* @param bean
|
* @param bean
|
||||||
* @param url
|
* @param url
|
||||||
* @param username
|
* @param username
|
||||||
|
@ -289,60 +312,67 @@ public class SocialCommunications {
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static void writeProductPost(String serviceUrl, ManageProductBean bean, String username, String fullName, boolean enablePostNotification, String userCurrentUrl) throws Exception{
|
public static void writeProductPost(String serviceUrl, ManageProductBean bean, String username, String fullName,
|
||||||
|
boolean enablePostNotification, String userCurrentUrl) throws Exception {
|
||||||
|
|
||||||
// discover service endpoint for the social networking library
|
// discover service endpoint for the social networking library
|
||||||
String currentScope = ScopeProvider.instance.get();
|
String currentScope = ScopeProvider.instance.get();
|
||||||
String tokenUser = SecurityTokenProvider.instance.get();
|
String tokenUser = SecurityTokenProvider.instance.get();
|
||||||
|
|
||||||
logger.info("Current scope for writeProductPost is " + currentScope + " and token is " + tokenUser.substring(0, 10) + "***************");
|
logger.info("Current scope for writeProductPost is " + currentScope + " and token is "
|
||||||
|
+ tokenUser.substring(0, 10) + "***************");
|
||||||
String basePath = serviceUrl;
|
String basePath = serviceUrl;
|
||||||
|
|
||||||
if(basePath == null){
|
if (basePath == null) {
|
||||||
|
|
||||||
logger.error("Unable to write a post because there is no social networking service available");
|
logger.error("Unable to write a post because there is no social networking service available");
|
||||||
throw new Exception("Unable to discover the social networking service");
|
throw new Exception("Unable to discover the social networking service");
|
||||||
|
|
||||||
}else{
|
} else {
|
||||||
|
|
||||||
basePath = basePath.endsWith("/") ? basePath : basePath + "/";
|
basePath = basePath.endsWith("/") ? basePath : basePath + "/";
|
||||||
|
|
||||||
try(CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build();){
|
try (CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy())
|
||||||
|
.build();) {
|
||||||
|
|
||||||
// require url
|
// require url
|
||||||
String applicationToken = requireApplicationToken(serviceUrl);
|
String applicationToken = requireApplicationToken(serviceUrl);
|
||||||
|
|
||||||
//see Feature #17576 Updated by Francesco
|
// see Feature #17576 Updated by Francesco
|
||||||
/*final String profilePageURL = GCubePortalConstants.PREFIX_GROUP_URL + extractOrgFriendlyURL(userCurrentUrl) + GCubePortalConstants.USER_PROFILE_FRIENDLY_URL;
|
/*
|
||||||
String userFullNameHighlighted = "<a class=\"link\" href=\"" + profilePageURL + "?"+
|
* final String profilePageURL = GCubePortalConstants.PREFIX_GROUP_URL +
|
||||||
Base64.getEncoder().encodeToString(USER_PROFILE_OID.getBytes())+"="+
|
* extractOrgFriendlyURL(userCurrentUrl) +
|
||||||
Base64.getEncoder().encodeToString(username.getBytes())+"\">"+fullName+
|
* GCubePortalConstants.USER_PROFILE_FRIENDLY_URL; String
|
||||||
"</a> ";
|
* userFullNameHighlighted = "<a class=\"link\" href=\"" + profilePageURL + "?"+
|
||||||
*/
|
* Base64.getEncoder().encodeToString(USER_PROFILE_OID.getBytes())+"="+
|
||||||
|
* Base64.getEncoder().encodeToString(username.getBytes())+"\">"+fullName+
|
||||||
|
* "</a> ";
|
||||||
|
*/
|
||||||
|
|
||||||
String userFullNameHighlighted = "@"+username;
|
String userFullNameHighlighted = "@" + username;
|
||||||
|
|
||||||
// replace
|
// replace
|
||||||
String message = POST_MESSAGE.replace("PRODUCT_TITLE", bean.getTitle()).replace("LINK_RECORD", bean.getUrl()).
|
String message = POST_MESSAGE.replace("PRODUCT_TITLE", bean.getTitle())
|
||||||
replace("USER_FULLNAME", userFullNameHighlighted);
|
.replace("LINK_RECORD", bean.getUrl()).replace("USER_FULLNAME", userFullNameHighlighted);
|
||||||
|
|
||||||
if(bean.getReport() != null && !bean.getReport().isEmpty())
|
if (bean.getReport() != null && !bean.getReport().isEmpty())
|
||||||
message += ADD_REPORT.replace("REPORT_UPDATE", bean.getReport());
|
message += ADD_REPORT.replace("<br>", "").replace("REPORT_UPDATE", bean.getReport());
|
||||||
|
|
||||||
Set<String> hashtags = bean.getHashtags();
|
Set<String> hashtags = bean.getHashtags();
|
||||||
logger.debug("Hashtags are " + hashtags);
|
logger.debug("Hashtags are " + hashtags);
|
||||||
if(hashtags != null && !hashtags.isEmpty()){
|
if (hashtags != null && !hashtags.isEmpty()) {
|
||||||
message +="\n\n";
|
message += "\n\n";
|
||||||
for (String hashtag : hashtags) {
|
for (String hashtag : hashtags) {
|
||||||
message += " #" + hashtag;
|
message += " #" + hashtag;
|
||||||
}
|
}
|
||||||
//Added by Francesco Mangiacrapa see at Feature #16312
|
// Added by Francesco Mangiacrapa see at Feature #16312
|
||||||
String normalizedFullName = fullName.trim().replace(" ", "_");
|
String normalizedFullName = fullName.trim().replace(" ", "_");
|
||||||
message += " #" + normalizedFullName;
|
message += " #" + normalizedFullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("The post that is going to be written is -> " + message);
|
logger.info("The post that is going to be written is -> " + message);
|
||||||
HttpPost postRequest = new HttpPost(basePath + SOCIAL_SERVICE_WRITE_APPLICATION_POST + "?gcube-token=" + applicationToken);
|
HttpPost postRequest = new HttpPost(
|
||||||
|
basePath + SOCIAL_SERVICE_WRITE_APPLICATION_POST + "?gcube-token=" + applicationToken);
|
||||||
JSONObject object = new JSONObject();
|
JSONObject object = new JSONObject();
|
||||||
object.put("text", message);
|
object.put("text", message);
|
||||||
object.put("enable_notification", enablePostNotification);
|
object.put("enable_notification", enablePostNotification);
|
||||||
|
@ -356,7 +386,7 @@ public class SocialCommunications {
|
||||||
if (response.getStatusLine().getStatusCode() != 201)
|
if (response.getStatusLine().getStatusCode() != 201)
|
||||||
throw new RuntimeException("Failed to write application post : HTTP error code : "
|
throw new RuntimeException("Failed to write application post : HTTP error code : "
|
||||||
+ response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
|
+ response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
|
||||||
}catch(Exception e){
|
} catch (Exception e) {
|
||||||
logger.error("Failed to create a post", e);
|
logger.error("Failed to create a post", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -372,13 +402,14 @@ public class SocialCommunications {
|
||||||
} else {
|
} else {
|
||||||
friendlyURL = friendlyURL.split("\\?")[0].split("\\#")[0];
|
friendlyURL = friendlyURL.split("\\?")[0].split("\\#")[0];
|
||||||
}
|
}
|
||||||
return "/"+friendlyURL;
|
return "/" + friendlyURL;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send an email to the administrator as well as the
|
* Send an email to the administrator as well as the
|
||||||
|
*
|
||||||
* @param bean
|
* @param bean
|
||||||
* @param catalogue
|
* @param catalogue
|
||||||
* @param username
|
* @param username
|
||||||
|
@ -388,15 +419,9 @@ public class SocialCommunications {
|
||||||
* @throws Exceptio
|
* @throws Exceptio
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static void sendEmailAdministrators(
|
public static void sendEmailAdministrators(String serviceUrl, ManageProductBean bean, DataCatalogue catalogue,
|
||||||
String serviceUrl,
|
String username, String fullName, long groupId, String clientCurrenturl, boolean isMergeInvolved)
|
||||||
ManageProductBean bean,
|
throws Exception {
|
||||||
DataCatalogue catalogue,
|
|
||||||
String username,
|
|
||||||
String fullName,
|
|
||||||
long groupId,
|
|
||||||
String clientCurrenturl,
|
|
||||||
boolean isMergeInvolved) throws Exception {
|
|
||||||
|
|
||||||
// get the list of GRSF Reviewers to alert them as well
|
// get the list of GRSF Reviewers to alert them as well
|
||||||
RoleManager roleManager = new LiferayRoleManager();
|
RoleManager roleManager = new LiferayRoleManager();
|
||||||
|
@ -406,9 +431,9 @@ public class SocialCommunications {
|
||||||
|
|
||||||
List<String> reviewers = new ArrayList<String>(reviewersGcube.size());
|
List<String> reviewers = new ArrayList<String>(reviewersGcube.size());
|
||||||
|
|
||||||
for(GCubeUser gU: reviewersGcube){
|
for (GCubeUser gU : reviewersGcube) {
|
||||||
// if the user is a reviewer, then send the email just once
|
// if the user is a reviewer, then send the email just once
|
||||||
if(!gU.getUsername().equals(username))
|
if (!gU.getUsername().equals(username))
|
||||||
reviewers.add(gU.getUsername());
|
reviewers.add(gU.getUsername());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,39 +446,41 @@ public class SocialCommunications {
|
||||||
String currentScope = ScopeProvider.instance.get();
|
String currentScope = ScopeProvider.instance.get();
|
||||||
String tokenUser = SecurityTokenProvider.instance.get();
|
String tokenUser = SecurityTokenProvider.instance.get();
|
||||||
|
|
||||||
logger.info("Current scope for writeProductPost is " + currentScope + " and token is " + tokenUser.substring(0, 10) + "***************");
|
logger.info("Current scope for writeProductPost is " + currentScope + " and token is "
|
||||||
|
+ tokenUser.substring(0, 10) + "***************");
|
||||||
String basePath = serviceUrl;
|
String basePath = serviceUrl;
|
||||||
|
|
||||||
if(basePath == null){
|
if (basePath == null) {
|
||||||
|
|
||||||
logger.error("Unable to write a post because there is no social networking service available");
|
logger.error("Unable to write a post because there is no social networking service available");
|
||||||
throw new Exception("Unable to discover the social networking service");
|
throw new Exception("Unable to discover the social networking service");
|
||||||
|
|
||||||
}else{
|
} else {
|
||||||
|
|
||||||
basePath = basePath.endsWith("/") ? basePath : basePath + "/";
|
basePath = basePath.endsWith("/") ? basePath : basePath + "/";
|
||||||
|
|
||||||
try(CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build();){
|
try (CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy())
|
||||||
|
.build();) {
|
||||||
|
|
||||||
/// require url
|
/// require url
|
||||||
String applicationToken = requireApplicationToken(serviceUrl);
|
String applicationToken = requireApplicationToken(serviceUrl);
|
||||||
|
|
||||||
String revertUrl = "";
|
String revertUrl = "";
|
||||||
if(isMergeInvolved)
|
if (isMergeInvolved)
|
||||||
revertUrl = getEncodedUrlManage(operation, username, System.currentTimeMillis(), bean.getKnowledgeBaseId(), clientCurrenturl);
|
revertUrl = getEncodedUrlManage(operation, username, System.currentTimeMillis(),
|
||||||
|
bean.getKnowledgeBaseId(), clientCurrenturl);
|
||||||
String messageToEditor = (EMAIL_MESSAGE_EDITOR +
|
|
||||||
(isMergeInvolved? REVERT_LINK_PIECE : "")).replace("USER_FULLNAME", fullName).replace("PRODUCT_TITLE", bean.getTitle()).
|
|
||||||
replace("LINK_RECORD", bean.getUrl()).replace("LINK", revertUrl);
|
|
||||||
String messageToReviewer = (EMAIL_MESSAGE_REVIEWER+
|
|
||||||
(isMergeInvolved? REVERT_LINK_PIECE : "")).replace("USER_FULLNAME", fullName).replace("PRODUCT_TITLE", bean.getTitle()).
|
|
||||||
replace("LINK_RECORD", bean.getUrl()).replace("LINK", revertUrl);
|
|
||||||
|
|
||||||
|
String messageToEditor = (EMAIL_MESSAGE_EDITOR + (isMergeInvolved ? REVERT_LINK_PIECE : ""))
|
||||||
|
.replace("USER_FULLNAME", fullName).replace("PRODUCT_TITLE", bean.getTitle())
|
||||||
|
.replace("LINK_RECORD", bean.getUrl()).replace("LINK", revertUrl);
|
||||||
|
String messageToReviewer = (EMAIL_MESSAGE_REVIEWER + (isMergeInvolved ? REVERT_LINK_PIECE : ""))
|
||||||
|
.replace("USER_FULLNAME", fullName).replace("PRODUCT_TITLE", bean.getTitle())
|
||||||
|
.replace("LINK_RECORD", bean.getUrl()).replace("LINK", revertUrl);
|
||||||
|
|
||||||
String subject = "Update request on GRSF Record";
|
String subject = "Update request on GRSF Record";
|
||||||
|
|
||||||
// append report
|
// append report
|
||||||
if(bean.getReport() != null){
|
if (bean.getReport() != null) {
|
||||||
messageToEditor += ADD_REPORT.replace("REPORT_UPDATE", bean.getReport());
|
messageToEditor += ADD_REPORT.replace("REPORT_UPDATE", bean.getReport());
|
||||||
messageToReviewer += ADD_REPORT.replace("REPORT_UPDATE", bean.getReport());
|
messageToReviewer += ADD_REPORT.replace("REPORT_UPDATE", bean.getReport());
|
||||||
}
|
}
|
||||||
|
@ -482,7 +509,7 @@ public class SocialCommunications {
|
||||||
|
|
||||||
Map<String, Object> mapResponseWritePost = getResponseEntityAsJSON(response);
|
Map<String, Object> mapResponseWritePost = getResponseEntityAsJSON(response);
|
||||||
|
|
||||||
if (response.getStatusLine().getStatusCode() != 201){
|
if (response.getStatusLine().getStatusCode() != 201) {
|
||||||
logger.error("Failed to send message to editor : HTTP error code : "
|
logger.error("Failed to send message to editor : HTTP error code : "
|
||||||
+ response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
|
+ response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
|
||||||
}
|
}
|
||||||
|
@ -494,7 +521,7 @@ public class SocialCommunications {
|
||||||
reqMessage.put("subject", subject);
|
reqMessage.put("subject", subject);
|
||||||
reqMessage.put("body", messageToReviewer);
|
reqMessage.put("body", messageToReviewer);
|
||||||
recipients = new JSONArray();
|
recipients = new JSONArray();
|
||||||
for(String reviewer: reviewers){
|
for (String reviewer : reviewers) {
|
||||||
JSONObject recip = new JSONObject();
|
JSONObject recip = new JSONObject();
|
||||||
recip.put("id", reviewer);
|
recip.put("id", reviewer);
|
||||||
recipients.add(recip);
|
recipients.add(recip);
|
||||||
|
@ -509,12 +536,12 @@ public class SocialCommunications {
|
||||||
response = client.execute(postRequest);
|
response = client.execute(postRequest);
|
||||||
mapResponseWritePost = getResponseEntityAsJSON(response);
|
mapResponseWritePost = getResponseEntityAsJSON(response);
|
||||||
|
|
||||||
if (response.getStatusLine().getStatusCode() != 201){
|
if (response.getStatusLine().getStatusCode() != 201) {
|
||||||
logger.error("Failed to send message to reviewers : HTTP error code : "
|
logger.error("Failed to send message to reviewers : HTTP error code : "
|
||||||
+ response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
|
+ response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}catch(Exception e){
|
} catch (Exception e) {
|
||||||
logger.error("Failed to send messages", e);
|
logger.error("Failed to send messages", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -522,11 +549,8 @@ public class SocialCommunications {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static void sendEmailAdministratorsOnOperationReverted(
|
public static void sendEmailAdministratorsOnOperationReverted(String serviceUrl, RevertableOperationInfo rInfo,
|
||||||
String serviceUrl,
|
long groupId) throws Exception {
|
||||||
RevertableOperationInfo rInfo,
|
|
||||||
long groupId
|
|
||||||
) throws Exception {
|
|
||||||
|
|
||||||
// get the list of GRSF Reviewers to alert them as well
|
// get the list of GRSF Reviewers to alert them as well
|
||||||
RoleManager roleManager = new LiferayRoleManager();
|
RoleManager roleManager = new LiferayRoleManager();
|
||||||
|
@ -536,9 +560,9 @@ public class SocialCommunications {
|
||||||
|
|
||||||
List<String> reviewers = new ArrayList<String>(reviewersGcube.size());
|
List<String> reviewers = new ArrayList<String>(reviewersGcube.size());
|
||||||
|
|
||||||
for(GCubeUser gU: reviewersGcube){
|
for (GCubeUser gU : reviewersGcube) {
|
||||||
// if the user is a reviewer, then send the email just once
|
// if the user is a reviewer, then send the email just once
|
||||||
if(!gU.getUsername().equals(rInfo.getUserNameOriginalAdmin()))
|
if (!gU.getUsername().equals(rInfo.getUserNameOriginalAdmin()))
|
||||||
reviewers.add(gU.getUsername());
|
reviewers.add(gU.getUsername());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,26 +572,31 @@ public class SocialCommunications {
|
||||||
String currentScope = ScopeProvider.instance.get();
|
String currentScope = ScopeProvider.instance.get();
|
||||||
String tokenUser = SecurityTokenProvider.instance.get();
|
String tokenUser = SecurityTokenProvider.instance.get();
|
||||||
|
|
||||||
logger.info("Current scope for writeProductPost is " + currentScope + " and token is " + tokenUser.substring(0, 10) + "***************");
|
logger.info("Current scope for writeProductPost is " + currentScope + " and token is "
|
||||||
|
+ tokenUser.substring(0, 10) + "***************");
|
||||||
String basePath = serviceUrl;
|
String basePath = serviceUrl;
|
||||||
|
|
||||||
if(basePath == null){
|
if (basePath == null) {
|
||||||
|
|
||||||
logger.error("Unable to write a post because there is no social networking service available");
|
logger.error("Unable to write a post because there is no social networking service available");
|
||||||
throw new Exception("Unable to discover the social networking service");
|
throw new Exception("Unable to discover the social networking service");
|
||||||
|
|
||||||
}else{
|
} else {
|
||||||
|
|
||||||
basePath = basePath.endsWith("/") ? basePath : basePath + "/";
|
basePath = basePath.endsWith("/") ? basePath : basePath + "/";
|
||||||
|
|
||||||
try(CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build();){
|
try (CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy())
|
||||||
|
.build();) {
|
||||||
|
|
||||||
String applicationToken = requireApplicationToken(serviceUrl);
|
String applicationToken = requireApplicationToken(serviceUrl);
|
||||||
|
|
||||||
String messageToEditor = EMAIL_EDITOR_REVERT.replace("RECORD_URL", rInfo.getRecordUrl()).replace("ORIGINAL_USER", rInfo.getFullNameOriginalAdmin()).
|
String messageToEditor = EMAIL_EDITOR_REVERT.replace("RECORD_URL", rInfo.getRecordUrl())
|
||||||
replace("ADMIN_WHO_CHANGED", rInfo.getFullNameCurrentAdmin());
|
.replace("ORIGINAL_USER", rInfo.getFullNameOriginalAdmin())
|
||||||
String messageToReviewer = EMAIL_REVIEWER_REVERT.replace("ADMIN_WHO_CHANGED", rInfo.getFullNameCurrentAdmin()).replace("RECORD_URL", rInfo.getRecordUrl()).
|
.replace("ADMIN_WHO_CHANGED", rInfo.getFullNameCurrentAdmin());
|
||||||
replace("ORIGINAL_USER", rInfo.getFullNameOriginalAdmin());
|
String messageToReviewer = EMAIL_REVIEWER_REVERT
|
||||||
|
.replace("ADMIN_WHO_CHANGED", rInfo.getFullNameCurrentAdmin())
|
||||||
|
.replace("RECORD_URL", rInfo.getRecordUrl())
|
||||||
|
.replace("ORIGINAL_USER", rInfo.getFullNameOriginalAdmin());
|
||||||
String subject = "Revert merge request on GRSF Record";
|
String subject = "Revert merge request on GRSF Record";
|
||||||
|
|
||||||
messageToEditor = messageToEditor.replace("<br>", "\n");
|
messageToEditor = messageToEditor.replace("<br>", "\n");
|
||||||
|
@ -591,7 +620,7 @@ public class SocialCommunications {
|
||||||
|
|
||||||
Map<String, Object> mapResponseWritePost = getResponseEntityAsJSON(response);
|
Map<String, Object> mapResponseWritePost = getResponseEntityAsJSON(response);
|
||||||
|
|
||||||
if (response.getStatusLine().getStatusCode() != 201){
|
if (response.getStatusLine().getStatusCode() != 201) {
|
||||||
logger.error("Failed to send message to editor : HTTP error code : "
|
logger.error("Failed to send message to editor : HTTP error code : "
|
||||||
+ response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
|
+ response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
|
||||||
}
|
}
|
||||||
|
@ -603,7 +632,7 @@ public class SocialCommunications {
|
||||||
reqMessage.put("subject", subject);
|
reqMessage.put("subject", subject);
|
||||||
reqMessage.put("body", messageToReviewer);
|
reqMessage.put("body", messageToReviewer);
|
||||||
recipients = new JSONArray();
|
recipients = new JSONArray();
|
||||||
for(String reviewer: reviewers){
|
for (String reviewer : reviewers) {
|
||||||
JSONObject recip = new JSONObject();
|
JSONObject recip = new JSONObject();
|
||||||
recip.put("id", reviewer);
|
recip.put("id", reviewer);
|
||||||
recipients.add(recip);
|
recipients.add(recip);
|
||||||
|
@ -615,12 +644,12 @@ public class SocialCommunications {
|
||||||
response = client.execute(postRequest);
|
response = client.execute(postRequest);
|
||||||
mapResponseWritePost = getResponseEntityAsJSON(response);
|
mapResponseWritePost = getResponseEntityAsJSON(response);
|
||||||
|
|
||||||
if (response.getStatusLine().getStatusCode() != 201){
|
if (response.getStatusLine().getStatusCode() != 201) {
|
||||||
logger.error("Failed to send message to editor : HTTP error code : "
|
logger.error("Failed to send message to editor : HTTP error code : "
|
||||||
+ response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
|
+ response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}catch(Exception e){
|
} catch (Exception e) {
|
||||||
logger.error("Failed to send messages", e);
|
logger.error("Failed to send messages", e);
|
||||||
throw new Exception(e);
|
throw new Exception(e);
|
||||||
}
|
}
|
||||||
|
@ -630,24 +659,28 @@ public class SocialCommunications {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the url to be send for reverting the operation
|
* Create the url to be send for reverting the operation
|
||||||
|
*
|
||||||
* @param httpSession
|
* @param httpSession
|
||||||
* @return
|
* @return
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public static String getEncodedUrlManage(RevertableOperations operation, String administrator, long timestamp, String uuid, String clientCurrenturl) throws Exception{
|
public static String getEncodedUrlManage(RevertableOperations operation, String administrator, long timestamp,
|
||||||
|
String uuid, String clientCurrenturl) throws Exception {
|
||||||
logger.info("Request for revert link. Client current url is " + clientCurrenturl);
|
logger.info("Request for revert link. Client current url is " + clientCurrenturl);
|
||||||
RevertOperationUrl operationUrl = new RevertOperationUrl(clientCurrenturl, administrator, timestamp, uuid, operation);
|
RevertOperationUrl operationUrl = new RevertOperationUrl(clientCurrenturl, administrator, timestamp, uuid,
|
||||||
|
operation);
|
||||||
String shortUrl = operationUrl.getShortUrl();
|
String shortUrl = operationUrl.getShortUrl();
|
||||||
return shortUrl;
|
return shortUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the json response to a map
|
* Convert the json response to a map
|
||||||
|
*
|
||||||
* @param response
|
* @param response
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static Map<String, Object> getResponseEntityAsJSON(HttpResponse response){
|
public static Map<String, Object> getResponseEntityAsJSON(HttpResponse response) {
|
||||||
|
|
||||||
Map<String, Object> toReturn = null;
|
Map<String, Object> toReturn = null;
|
||||||
HttpEntity entity = response.getEntity();
|
HttpEntity entity = response.getEntity();
|
||||||
|
@ -660,7 +693,7 @@ public class SocialCommunications {
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
toReturn = objectMapper.readValue(jsonString, HashMap.class);
|
toReturn = objectMapper.readValue(jsonString, HashMap.class);
|
||||||
logger.debug("Map is " + toReturn);
|
logger.debug("Map is " + toReturn);
|
||||||
}catch(Exception e){
|
} catch (Exception e) {
|
||||||
logger.error("Failed to read json object", e);
|
logger.error("Failed to read json object", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue