├── .gitignore ├── README.md ├── src └── main │ ├── resources │ └── ru │ │ └── mail │ │ └── jira │ │ └── plugins │ │ └── commons │ │ ├── email-text.vm │ │ └── email-html.vm │ └── java │ └── ru │ └── mail │ └── jira │ └── plugins │ └── commons │ ├── RestFieldException.java │ ├── StreamRestResult.java │ ├── HttpSenderException.java │ ├── SentryClient.java │ ├── ContractUtils.java │ ├── RestExecutor.java │ ├── HttpSender.java │ ├── LocalUtils.java │ └── CommonUtils.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea 3 | target -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | commons 2 | ======= 3 | 4 | ru.mail.jira.plugins.commons 5 | -------------------------------------------------------------------------------- /src/main/resources/ru/mail/jira/plugins/commons/email-text.vm: -------------------------------------------------------------------------------- 1 | #disable_html_escaping() 2 | $subject 3 | #dashes($subject) 4 | 5 | 6 | $message 7 | #parse("templates/email/text/includes/footer.vm") -------------------------------------------------------------------------------- /src/main/java/ru/mail/jira/plugins/commons/RestFieldException.java: -------------------------------------------------------------------------------- 1 | package ru.mail.jira.plugins.commons; 2 | 3 | public class RestFieldException extends IllegalArgumentException { 4 | private final String field; 5 | 6 | public RestFieldException(String message, String field) { 7 | super(message); 8 | this.field = field; 9 | } 10 | 11 | public String getField() { 12 | return field; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/resources/ru/mail/jira/plugins/commons/email-html.vm: -------------------------------------------------------------------------------- 1 | #disable_html_escaping() 2 | #parse( "templates/email/html/includes/emailconstants.vm" ) 3 | #parse( "templates/email/html/includes/header.vm" ) 4 | 5 | #set( $pageTitle = $subject ) 6 | #rowWrapperNormal( "#parse( 'templates/email/html/includes/patterns/page-title.vm' )" ) 7 | 8 | #rowWrapperNormalBegin( "", "wrapper-special-margin" ) 9 | #set( $n = " 10 | " ) 11 |

$message.replaceAll( "$n", "
" )

12 | #rowWrapperNormalEnd() 13 | 14 | #parse( "templates/email/html/includes/footer.vm" ) 15 | -------------------------------------------------------------------------------- /src/main/java/ru/mail/jira/plugins/commons/StreamRestResult.java: -------------------------------------------------------------------------------- 1 | package ru.mail.jira.plugins.commons; 2 | 3 | import java.io.InputStream; 4 | 5 | public class StreamRestResult { 6 | private final InputStream inputStream; 7 | private final String contentType; 8 | 9 | public StreamRestResult(InputStream inputStream, String contentType) { 10 | this.inputStream = inputStream; 11 | this.contentType = contentType; 12 | } 13 | 14 | public InputStream getInputStream() { 15 | return inputStream; 16 | } 17 | 18 | public String getContentType() { 19 | return contentType; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/ru/mail/jira/plugins/commons/HttpSenderException.java: -------------------------------------------------------------------------------- 1 | package ru.mail.jira.plugins.commons; 2 | 3 | import java.io.IOException; 4 | 5 | @SuppressWarnings("UnusedDeclaration") 6 | public class HttpSenderException extends IOException { 7 | private int responseCode; 8 | private String requestMessage; 9 | private String responseMessage; 10 | 11 | public HttpSenderException(int responseCode, String requestMessage, String responseMessage) { 12 | super(String.format("Invalid server response, code: %d, message: %s", responseCode, responseMessage)); 13 | this.responseCode = responseCode; 14 | this.requestMessage = requestMessage; 15 | this.responseMessage = responseMessage; 16 | } 17 | 18 | public int getResponseCode() { 19 | return responseCode; 20 | } 21 | 22 | public String getRequestMessage() { 23 | return requestMessage; 24 | } 25 | 26 | public String getResponseMessage() { 27 | return responseMessage; 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return String.format("SenderException[responseCode=%d, requestMessage=%s, responseMessage=%s]", responseCode, requestMessage, responseMessage); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/ru/mail/jira/plugins/commons/SentryClient.java: -------------------------------------------------------------------------------- 1 | package ru.mail.jira.plugins.commons; 2 | 3 | 4 | import com.atlassian.jira.component.ComponentAccessor; 5 | import com.atlassian.jira.security.JiraAuthenticationContext; 6 | import com.atlassian.jira.user.ApplicationUser; 7 | import io.sentry.Sentry; 8 | import io.sentry.event.UserBuilder; 9 | 10 | public class SentryClient { 11 | private static JiraAuthenticationContext jiraAuthenticationContext; 12 | 13 | public static void init(String dsn) { 14 | jiraAuthenticationContext = ComponentAccessor.getJiraAuthenticationContext(); 15 | Sentry.init(dsn); 16 | } 17 | 18 | public static void capture(Throwable throwable) { 19 | try { 20 | setContextUser(); 21 | Sentry.capture(throwable); 22 | } catch (Throwable e) { 23 | } finally { 24 | Sentry.clearContext(); 25 | } 26 | } 27 | 28 | public static void capture(String message) { 29 | try { 30 | setContextUser(); 31 | Sentry.capture(message); 32 | } catch (Throwable e) { 33 | } finally { 34 | Sentry.clearContext(); 35 | } 36 | } 37 | 38 | public static void close() { 39 | Sentry.close(); 40 | } 41 | 42 | private static void setContextUser() { 43 | if (jiraAuthenticationContext != null && jiraAuthenticationContext.isLoggedInUser()) { 44 | ApplicationUser user = jiraAuthenticationContext.getLoggedInUser(); 45 | Sentry.setUser(new UserBuilder() 46 | .setId(user.getKey()) 47 | .setEmail(user.getEmailAddress()) 48 | .setUsername(user.getDisplayName()) 49 | .build()); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | ru.mail.jira.plugins 8 | commons 9 | 1.0 10 | 11 | 12 | 13 | com.atlassian.jira 14 | jira-api 15 | ${jira.version} 16 | provided 17 | 18 | 19 | com.atlassian.jira 20 | jira-core 21 | ${jira.version} 22 | provided 23 | 24 | 25 | com.atlassian.plugins.rest 26 | atlassian-rest-common 27 | 1.0.2 28 | provided 29 | 30 | 31 | 32 | io.sentry 33 | sentry 34 | 1.1.0 35 | 36 | 37 | slf4j-api 38 | org.slf4j 39 | 40 | 41 | 42 | 43 | 44 | 45 | 7.3.3 46 | 47 | 48 | 49 | 50 | atlassian 51 | https://maven.atlassian.com/repository/public 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/ru/mail/jira/plugins/commons/ContractUtils.java: -------------------------------------------------------------------------------- 1 | package ru.mail.jira.plugins.commons; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | @SuppressWarnings("UnusedDeclaration") 7 | public class ContractUtils { 8 | private final static Pattern INN_PATTERN = Pattern.compile("\\d{10}|\\d{12}"); 9 | private final static Pattern SNILS_WITH_CHECKSUM_PATTERN = Pattern.compile("(\\d{3}-\\d{3}-\\d{3}) (\\d{2})"); 10 | private final static Pattern SNILS_WITHOUT_CHECKSUM_PATTERN = Pattern.compile("\\d{3}-\\d{3}-\\d{3}"); 11 | private final static Pattern EXTRA_SNILS_VALIDATE_PATTERN = Pattern.compile("0{3}|1{3}|2{3}|3{3}|4{3}|5{3}|6{3}|7{3}|8{3}|9{3}"); 12 | 13 | public static boolean isValidSnils(String snils) { 14 | Matcher snilsWithCsMatcher = SNILS_WITH_CHECKSUM_PATTERN.matcher(snils); 15 | if (snilsWithCsMatcher.matches()) { 16 | String joinedPartBeforeSpace = snilsWithCsMatcher.group(1).replaceAll("-", ""); 17 | int checksum = Integer.valueOf(snilsWithCsMatcher.group(2)); 18 | 19 | if (EXTRA_SNILS_VALIDATE_PATTERN.matcher(joinedPartBeforeSpace).find()) 20 | return false; 21 | 22 | int sum = 0; 23 | for (int i = 0, j = 9; i < 9; i++, j--) 24 | sum += Character.getNumericValue(joinedPartBeforeSpace.charAt(i)) * j; 25 | 26 | if (sum < 100) 27 | return checksum == sum; 28 | else if (sum == 100 || sum == 101) 29 | return checksum == 0; 30 | else 31 | return (checksum == sum % 101) || (checksum == 0 && sum % 101 == 100); 32 | } 33 | 34 | if (SNILS_WITHOUT_CHECKSUM_PATTERN.matcher(snils).matches()) { 35 | String joinedPartBeforeSpace = snils.replaceAll("-", ""); 36 | return !EXTRA_SNILS_VALIDATE_PATTERN.matcher(joinedPartBeforeSpace).find() && Integer.valueOf(joinedPartBeforeSpace) <= 1001998; 37 | } 38 | 39 | return false; 40 | } 41 | 42 | public static boolean isValidInn(String inn) { 43 | return INN_PATTERN.matcher(inn).matches(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/ru/mail/jira/plugins/commons/RestExecutor.java: -------------------------------------------------------------------------------- 1 | package ru.mail.jira.plugins.commons; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import javax.ws.rs.core.Response; 7 | 8 | @SuppressWarnings("UnusedDeclaration") 9 | public abstract class RestExecutor { 10 | private static final String FIELD_HEADER = "X-Atlassian-Rest-Exception-Field"; 11 | 12 | private static final Logger log = LoggerFactory.getLogger(RestExecutor.class); 13 | 14 | protected abstract T doAction() throws Exception; 15 | 16 | public Response getResponse() { 17 | return getResponse(Response.Status.OK); 18 | } 19 | 20 | public Response getResponse(Response.Status successStatus) { 21 | try { 22 | T actionResult = doAction(); 23 | Response.ResponseBuilder responseBuilder = Response.status(successStatus).entity(actionResult); 24 | 25 | if (actionResult instanceof byte[]) 26 | responseBuilder = responseBuilder.type("application/force-download") 27 | .header("Content-Transfer-Encoding", "binary") 28 | .header("charset", "UTF-8"); 29 | else if(actionResult instanceof StreamRestResult) 30 | responseBuilder = responseBuilder.entity(((StreamRestResult) actionResult).getInputStream()) 31 | .type(((StreamRestResult) actionResult).getContentType()); 32 | return responseBuilder.build(); 33 | } catch (SecurityException e) { 34 | return Response.status(Response.Status.UNAUTHORIZED).entity(e.getMessage()).build(); 35 | } catch (IllegalArgumentException e) { 36 | Response.ResponseBuilder responseBuilder = Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()); 37 | if (e instanceof RestFieldException) 38 | responseBuilder = responseBuilder.header(FIELD_HEADER, ((RestFieldException) e).getField()); 39 | return responseBuilder.build(); 40 | } catch (Exception e) { 41 | SentryClient.capture(e); 42 | log.error("REST Exception", e); 43 | return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/ru/mail/jira/plugins/commons/HttpSender.java: -------------------------------------------------------------------------------- 1 | package ru.mail.jira.plugins.commons; 2 | 3 | import org.apache.commons.io.IOUtils; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.apache.log4j.Logger; 6 | 7 | import javax.xml.bind.DatatypeConverter; 8 | import java.io.IOException; 9 | import java.net.HttpURLConnection; 10 | import java.net.URL; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | @SuppressWarnings("UnusedDeclaration") 15 | public class HttpSender { 16 | private static final Logger log = Logger.getLogger(HttpSender.class); 17 | 18 | private final String url; 19 | private String user; 20 | private String password; 21 | private final Map headers = new HashMap(); 22 | 23 | public HttpSender(String url, Object... params) { 24 | this.url = CommonUtils.formatUrl(url, params); 25 | } 26 | 27 | public HttpSender setAuthenticationInfo(String user, String password) { 28 | this.user = user; 29 | this.password = password; 30 | return this; 31 | } 32 | 33 | public HttpSender setHeader(String header, String value) { 34 | headers.put(header, value); 35 | return this; 36 | } 37 | 38 | public HttpSender setContentTypeJson() { 39 | setHeader("Content-Type", "application/json; charset=utf-8"); 40 | return this; 41 | } 42 | 43 | private String getAuthRealm() { 44 | return DatatypeConverter.printBase64Binary(user.concat(":").concat(password).getBytes()); 45 | } 46 | 47 | private String send(String method, String body) throws IOException { 48 | log.info(String.format("Sending HTTP request: %s", url)); 49 | 50 | HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); 51 | try { 52 | connection.setDoInput(true); 53 | connection.setDoOutput(StringUtils.isNotEmpty(body)); 54 | connection.setAllowUserInteraction(true); 55 | connection.setRequestMethod(method); 56 | if (StringUtils.isNotEmpty(user) && StringUtils.isNotEmpty(password)) 57 | connection.setRequestProperty("Authorization", "Basic " + getAuthRealm()); 58 | for (Map.Entry entry : headers.entrySet()) 59 | connection.setRequestProperty(entry.getKey(), entry.getValue()); 60 | if (StringUtils.isNotEmpty(body)) 61 | IOUtils.write(body, connection.getOutputStream()); 62 | 63 | int rc = connection.getResponseCode(); 64 | if (rc == HttpURLConnection.HTTP_OK) { 65 | String response = IOUtils.toString(connection.getInputStream(), "UTF-8"); 66 | log.debug(String.format("HTTP response body:\n %s", response)); 67 | return response; 68 | } else { 69 | String response = connection.getErrorStream() != null ? IOUtils.toString(connection.getErrorStream(), "UTF-8") : ""; 70 | log.debug(String.format("HTTP error stream:\n %s", response)); 71 | throw new HttpSenderException(rc, body, response); 72 | } 73 | } finally { 74 | connection.disconnect(); 75 | } 76 | } 77 | 78 | public String sendGet() throws IOException { 79 | return send("GET", null); 80 | } 81 | 82 | public String sendGet(String body) throws IOException { 83 | return send("GET", body); 84 | } 85 | 86 | public String sendPost(String body) throws IOException { 87 | return send("POST", body); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/ru/mail/jira/plugins/commons/LocalUtils.java: -------------------------------------------------------------------------------- 1 | package ru.mail.jira.plugins.commons; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | import java.text.DateFormatSymbols; 6 | import java.text.SimpleDateFormat; 7 | import java.util.ArrayList; 8 | import java.util.Calendar; 9 | import java.util.List; 10 | 11 | @SuppressWarnings("UnusedDeclaration") 12 | public class LocalUtils { 13 | private static final String ZERO_CAPTION = "ноль"; 14 | private static final String MINUS_CAPTION = "минус"; 15 | 16 | private static final String[][] MAGNITUDE_CAPTIONS = new String[][] { 17 | { null, null, null, null }, 18 | { "FEMALE", "тысяча", "тысячи", "тысяч" }, 19 | { null, "миллион", "миллиона", "миллионов" }, 20 | { null, "миллиард", "миллиарда", "миллиардов" } 21 | }; 22 | private static final int SEX_MAGNITUDE = 0; 23 | private static final int ONE_MAGNITUDE = 1; 24 | private static final int FOUR_MAGNITUDE = 2; 25 | private static final int MANY_MAGNITUDE = 3; 26 | 27 | private static final String[][] DIGIT_CAPTIONS = new String[][] { 28 | { null, null, "десять", null, null }, 29 | { "один", "одна", "одиннадцать", "десять", "сто" }, 30 | { "два", "две", "двенадцать", "двадцать", "двести" }, 31 | { "три", "три", "тринадцать", "тридцать", "триста" }, 32 | { "четыре", "четыре", "четырнадцать", "сорок", "четыреста" }, 33 | { "пять", "пять", "пятнадцать", "пятьдесят", "пятьсот" }, 34 | { "шесть", "шесть", "шестнадцать", "шестьдесят", "шестьсот" }, 35 | { "семь", "семь", "семнадцать", "семьдесят", "семьсот" }, 36 | { "восемь", "восемь", "восемнадцать", "восемьдесят", "восемьсот" }, 37 | { "девять", "девять", "девятнадцать", "девяносто", "девятьсот" } 38 | }; 39 | private static final int MALE_DIGIT = 0; 40 | private static final int FEMALE_DIGIT = 1; 41 | private static final int TEEN_DIGIT = 2; 42 | private static final int DECADE_DIGIT = 3; 43 | private static final int HUNDRED_DIGIT = 4; 44 | 45 | public static String numberToCaption(int number) { 46 | if (number == 0) 47 | return ZERO_CAPTION; 48 | 49 | List result = new ArrayList(); 50 | 51 | if (number < 0) { 52 | result.add(MINUS_CAPTION); 53 | number = -number; 54 | } 55 | 56 | for (int magnitude = MAGNITUDE_CAPTIONS.length - 1; magnitude >= 0; magnitude--) { 57 | int magnitudeMultiplier = (int) Math.pow(1000, magnitude); 58 | int part = number / magnitudeMultiplier; 59 | number %= magnitudeMultiplier; 60 | 61 | if (part > 0) { 62 | if (part >= 100) { 63 | result.add(DIGIT_CAPTIONS[part / 100][HUNDRED_DIGIT]); 64 | part %= 100; 65 | } 66 | if (part >= 10 && part < 20) { 67 | result.add(DIGIT_CAPTIONS[part - 10][TEEN_DIGIT]); 68 | part = 0; 69 | } 70 | if (part >= 20) { 71 | result.add(DIGIT_CAPTIONS[part / 10][DECADE_DIGIT]); 72 | part %= 10; 73 | } 74 | if (part > 0) { 75 | boolean female = MAGNITUDE_CAPTIONS[magnitude][SEX_MAGNITUDE] != null; 76 | result.add(DIGIT_CAPTIONS[part][female ? FEMALE_DIGIT : MALE_DIGIT]); 77 | } 78 | 79 | if (magnitude > 0) 80 | switch (part) { 81 | case 1: 82 | result.add(MAGNITUDE_CAPTIONS[magnitude][ONE_MAGNITUDE]); 83 | break; 84 | case 2: case 3: case 4: 85 | result.add(MAGNITUDE_CAPTIONS[magnitude][FOUR_MAGNITUDE]); 86 | break; 87 | default: 88 | result.add(MAGNITUDE_CAPTIONS[magnitude][MANY_MAGNITUDE]); 89 | break; 90 | } 91 | } 92 | } 93 | 94 | return StringUtils.join(result, " "); 95 | } 96 | 97 | public static final String[] MONTH_NAMES_NOMINATIVE = new String[] { "Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь" }; 98 | public static final String[] MONTH_NAMES_GENITIVE = new String[] { "января", "февраля", "марта", "апреля", "мая", "июня", "июля", "августа", "сентября", "октября", "ноября", "декабря" }; 99 | 100 | public static SimpleDateFormat updateMonthNames(SimpleDateFormat simpleDateFormat, String[] monthNames) { 101 | DateFormatSymbols dateFormatSymbols = simpleDateFormat.getDateFormatSymbols(); 102 | dateFormatSymbols.setMonths(monthNames); 103 | simpleDateFormat.setDateFormatSymbols(dateFormatSymbols); 104 | return simpleDateFormat; 105 | } 106 | 107 | public static boolean isWeekend(Calendar calendar) { 108 | return calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY; 109 | } 110 | 111 | public static boolean isHoliday(Calendar calendar) { 112 | int month = calendar.get(Calendar.MONTH); 113 | int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); 114 | if (month == Calendar.JANUARY && dayOfMonth < 9) 115 | return true; 116 | if (month == Calendar.FEBRUARY && dayOfMonth == 23) 117 | return true; 118 | if (month == Calendar.MARCH && dayOfMonth == 8) 119 | return true; 120 | if (month == Calendar.MAY && (dayOfMonth == 1 || dayOfMonth == 9)) 121 | return true; 122 | if (month == Calendar.JUNE && dayOfMonth == 12) 123 | return true; 124 | if (month == Calendar.NOVEMBER && dayOfMonth == 4) 125 | return true; 126 | return false; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/ru/mail/jira/plugins/commons/CommonUtils.java: -------------------------------------------------------------------------------- 1 | package ru.mail.jira.plugins.commons; 2 | 3 | import com.atlassian.jira.component.ComponentAccessor; 4 | import com.atlassian.jira.issue.Issue; 5 | import com.atlassian.jira.issue.fields.CustomField; 6 | import com.atlassian.jira.mail.Email; 7 | import com.atlassian.jira.mail.builder.EmailBuilder; 8 | import com.atlassian.jira.notification.NotificationRecipient; 9 | import com.atlassian.jira.security.groups.GroupManager; 10 | import com.atlassian.jira.user.ApplicationUser; 11 | import com.atlassian.jira.user.util.UserManager; 12 | import com.atlassian.jira.util.ErrorCollection; 13 | import com.atlassian.mail.queue.MailQueueItem; 14 | import org.apache.commons.lang3.StringUtils; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | import java.io.PrintWriter; 19 | import java.io.StringWriter; 20 | import java.io.UnsupportedEncodingException; 21 | import java.io.Writer; 22 | import java.net.URLEncoder; 23 | import java.util.*; 24 | 25 | @SuppressWarnings("UnusedDeclaration") 26 | public class CommonUtils { 27 | private final static Logger log = LoggerFactory.getLogger(CommonUtils.class); 28 | 29 | public static String getXmlTagContent(String xml, String tagName) { 30 | String startTag = String.format("<%s>", tagName); 31 | String endTag = String.format("", tagName); 32 | int startPos = xml.indexOf(startTag); 33 | int endPos = xml.indexOf(endTag); 34 | return (startPos >= 0 && endPos >= 0) ? xml.substring(startPos + startTag.length(), endPos) : ""; 35 | } 36 | 37 | public static String join(List stringList) { 38 | if (stringList == null) 39 | return ""; 40 | return StringUtils.join(stringList, ", "); 41 | } 42 | 43 | public static List split(String joinedString) { 44 | List stringList = new LinkedList(); 45 | if (StringUtils.isNotBlank(joinedString)) 46 | for (String s : joinedString.trim().split("\\s*,\\s*")) 47 | if (StringUtils.isNotEmpty(s)) 48 | stringList.add(s); 49 | return stringList; 50 | } 51 | 52 | public static String convertUserKeysToJoinedString(List userKeys) { 53 | List userNames = new LinkedList(); 54 | if (userKeys != null) 55 | for (String userKey : userKeys) { 56 | ApplicationUser user = ComponentAccessor.getUserManager().getUserByKey(userKey); 57 | if (user != null) 58 | userNames.add(user.getName()); 59 | } 60 | return join(userNames); 61 | } 62 | 63 | public static List convertJoinedStringToUserKeys(String joinedString) { 64 | List userKeys = new LinkedList(); 65 | for (String userName : split(joinedString)) { 66 | ApplicationUser user = ComponentAccessor.getUserManager().getUserByName(userName); 67 | if (user == null) 68 | throw new IllegalArgumentException(ComponentAccessor.getJiraAuthenticationContext().getI18nHelper().getText("user.picker.errors.usernotfound", userName)); 69 | userKeys.add(user.getKey()); 70 | } 71 | return userKeys; 72 | } 73 | 74 | public static String formatErrorCollection(ErrorCollection errorCollection) { 75 | Collection lines = new ArrayList(); 76 | if (errorCollection.getErrorMessages() != null) 77 | lines.addAll(errorCollection.getErrorMessages()); 78 | if (errorCollection.getErrors() != null) 79 | for (Map.Entry e : errorCollection.getErrors().entrySet()) 80 | lines.add(String.format("%s: %s", e.getKey(), e.getValue())); 81 | return StringUtils.join(lines, "\n"); 82 | } 83 | 84 | public static CustomField getCustomField(Long id) { 85 | CustomField customField = ComponentAccessor.getCustomFieldManager().getCustomFieldObject(id); 86 | if (customField == null) 87 | throw new IllegalStateException(String.format("Custom field (%d) is not found.", id)); 88 | return customField; 89 | } 90 | 91 | public static CustomField getCustomField(String id) { 92 | CustomField customField = ComponentAccessor.getCustomFieldManager().getCustomFieldObject(id); 93 | if (customField == null) 94 | throw new IllegalStateException(String.format("Custom field (%s) is not found.", id)); 95 | return customField; 96 | } 97 | 98 | public static String getCustomFieldStringValue(Issue issue, long customFieldId) { 99 | CustomField customField = getCustomField(customFieldId); 100 | Object customFieldValue = issue.getCustomFieldValue(customField); 101 | if (customFieldValue != null) 102 | return customFieldValue.toString().trim(); 103 | else 104 | return ""; 105 | } 106 | 107 | public static boolean isUserInGroups(ApplicationUser user, Collection groupNames) { 108 | if (user == null) 109 | return false; 110 | 111 | GroupManager groupManager = ComponentAccessor.getGroupManager(); 112 | for (String groupName : groupNames) 113 | if (groupManager.isUserInGroup(user.getName(), groupName)) 114 | return true; 115 | return false; 116 | } 117 | 118 | public static void sendEmail(List recipientKeys, String subject, String message) { 119 | UserManager userManager = ComponentAccessor.getUserManager(); 120 | for (String key : recipientKeys) { 121 | ApplicationUser user = userManager.getUserByKey(key); 122 | if (user != null) 123 | sendEmail(user, subject, message); 124 | else 125 | throw new IllegalArgumentException("Bad user key => " + key); 126 | } 127 | } 128 | 129 | public static void sendEmail(ApplicationUser recipient, String subject, String message) { 130 | Map params = new HashMap(); 131 | params.put("subject", subject); 132 | params.put("message", message); 133 | 134 | Email email = new Email(recipient.getEmailAddress()); 135 | 136 | NotificationRecipient notificationRecipient = new NotificationRecipient(recipient); 137 | boolean isHtmlFormat = !NotificationRecipient.MIMETYPE_TEXT.equals(notificationRecipient.getFormat()); 138 | 139 | MailQueueItem item = new EmailBuilder(email, notificationRecipient) 140 | .withSubject(subject) 141 | .withBodyFromFile(isHtmlFormat ? "ru/mail/jira/plugins/commons/email-html.vm" : "ru/mail/jira/plugins/commons/email-text.vm") 142 | .addParameters(params) 143 | .renderLater(); 144 | ComponentAccessor.getMailQueue().addItem(item); 145 | } 146 | 147 | public static String getStackTrace(Throwable e) { 148 | Writer stackTrace = new StringWriter(); 149 | e.printStackTrace(new PrintWriter(stackTrace)); 150 | return stackTrace.toString(); 151 | } 152 | 153 | public static String formatUrl(String url, Object... params) { 154 | if (params == null || params.length == 0) 155 | return url; 156 | 157 | try { 158 | for (int i = 0; i < params.length; i++) 159 | params[i] = URLEncoder.encode(params[i].toString(), "UTF-8"); 160 | } catch (UnsupportedEncodingException ignored) { 161 | } 162 | return String.format(url, params); 163 | } 164 | } 165 | --------------------------------------------------------------------------------