1 package org.itracker.web.servlets;
2
3 import com.sun.syndication.feed.module.DCModuleImpl;
4 import com.sun.syndication.feed.module.SyModuleImpl;
5 import com.sun.syndication.feed.rss.*;
6 import org.apache.commons.lang.StringUtils;
7 import org.apache.struts.config.ForwardConfig;
8 import org.apache.struts.config.ModuleConfig;
9 import org.apache.struts.taglib.TagUtils;
10 import org.apache.struts.util.ModuleUtils;
11 import org.itracker.core.resources.ITrackerResources;
12 import org.itracker.model.Issue;
13 import org.itracker.model.IssueHistory;
14 import org.itracker.model.PermissionType;
15 import org.itracker.model.Project;
16 import org.itracker.model.util.IssueUtilities;
17 import org.itracker.services.ConfigurationService;
18 import org.itracker.services.IssueService;
19 import org.itracker.services.ProjectService;
20 import org.itracker.web.util.Constants;
21 import org.itracker.web.util.HTMLUtilities;
22 import org.itracker.web.util.LoginUtilities;
23 import org.itracker.web.util.ServletContextUtils;
24 import org.springframework.http.HttpStatus;
25 import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
26 import org.springframework.http.server.ServletServerHttpResponse;
27
28 import javax.mail.internet.InternetAddress;
29 import javax.servlet.ServletConfig;
30 import javax.servlet.ServletContext;
31 import javax.servlet.ServletException;
32 import javax.servlet.http.HttpServletRequest;
33 import javax.servlet.http.HttpServletResponse;
34 import java.io.IOException;
35 import java.net.MalformedURLException;
36 import java.util.*;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
39
40
41 public class RssFeedController extends GenericController {
42
43 final private static Pattern URL_PATTERN = Pattern.compile("(?i).*/issues/p?([0-9]*)/?i?([0-9]*)");
44 @Override
45 public void init(ServletConfig config) throws ServletException {
46 super.init(config);
47
48 }
49
50 @Override
51 protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
52
53
54 RssChannelHttpMessageConverter conv = new RssChannelHttpMessageConverter();
55 try (ServletServerHttpResponse sr = new ServletServerHttpResponse(resp)) {
56 Matcher uriMatcher = URL_PATTERN.matcher(req.getRequestURI());
57
58 Integer projectId = null;
59 Integer issueId = null;
60 try {
61 if (uriMatcher.matches()) {
62 projectId = uriMatcher.group(1).equals("") ? null : Integer.valueOf(uriMatcher.group(1));
63 issueId = uriMatcher.group(2).equals("") ? null : Integer.valueOf(uriMatcher.group(2));
64
65 }
66
67 } catch (RuntimeException e) {
68 deny(sr);
69 return;
70 }
71
72 final IssueService is = ServletContextUtils.getItrackerServices().getIssueService();
73 final ProjectService ps = ServletContextUtils.getItrackerServices().getProjectService();
74 final ConfigurationService cs = ServletContextUtils.getItrackerServices().getConfigurationService();
75
76 final String baseURL = cs.getSystemBaseURL();
77 final String generator = cs.getProperty("notification_from_text",
78 "iTracker "
79 + cs.getProperty(Constants.VERSION_KEY, "3"));
80
81
82 Channel c = new Channel(conv.getSupportedMediaTypes().get(0).getType());
83
84
85 c.setGenerator(generator);
86
87
88 c.setModules(Arrays.asList(
89 new DCModuleImpl(),
90 new SyModuleImpl()
91 ));
92
93
94 c.setFeedType("rss_2.0");
95 c.setLink(cs.getSystemBaseURL() + req.getServletPath());
96
97 if (null != issueId) {
98 Issue i = is.getIssue(issueId);
99 if (!LoginUtilities.canViewIssue(i)) {
100 deny(sr);
101 return;
102 }
103
104 toChannel(c, i, baseURL);
105 } else if (null != projectId) {
106 Project p = ps.getProject(projectId);
107 if (null == p) {
108 deny(sr);
109 return;
110 }
111 if (!LoginUtilities.hasPermission(p, PermissionType.ISSUE_VIEW_USERS)) {
112 deny(sr);
113 return;
114 }
115
116 toChannel(c, p, is, req, baseURL);
117
118 } else {
119
120 toChannel(c, ps, req, baseURL);
121 }
122
123 conv.write(c, conv.getSupportedMediaTypes().get(0), sr);
124 }
125
126
127
128 }
129
130 private void toChannel(Channel c, ProjectService ps, HttpServletRequest req, String baseURL) {
131 List<Project> projects = ps.getAllAvailableProjects();
132 c.setTitle(ITrackerResources.getString("itracker.feed.projects.title"));
133 c.setDescription(ITrackerResources.getString("itracker.feed.projects.description", baseURL));
134 c.setLink(baseURL);
135 c.setUri(baseURL);
136 for (Project p : projects) {
137 if (LoginUtilities.hasAnyPermission(p, null)) {
138 Item pi = new Item();
139 pi.setLink(getProjectURL(p, req, getServletContext(), baseURL));
140
141 pi.setTitle(p.getName());
142 Guid guid = new Guid();
143 guid.setValue(pi.getLink());
144 guid.setPermaLink(true);
145
146 pi.setGuid(guid);
147
148 Description desc = new Description();
149 desc.setType(Content.TEXT);
150 desc.setValue(p.getDescription());
151 pi.setDescription(desc);
152
153 Content content = new Content();
154 content.setType(Content.HTML);
155
156 content.setValue(
157 p.getDescription()
158 );
159 pi.setPubDate(ps.getLatestIssueUpdatedDateByProjectId(p.getId()));
160
161 c.getItems().add(pi);
162 }
163 }
164 }
165
166 private void toChannel(Channel channel, Project project, IssueService is, HttpServletRequest req, String baseURL) throws MalformedURLException {
167 List<Issue> listIssues;
168 if (!LoginUtilities.hasPermission(project, PermissionType.ISSUE_VIEW_ALL)) {
169 listIssues = new ArrayList<>();
170 for (Issue issue : is.getIssuesByProjectId(project.getId())) {
171 if (LoginUtilities.canViewIssue(issue)) {
172 listIssues.add(issue);
173 }
174 }
175 } else {
176 listIssues = is.getIssuesByProjectId(project.getId());
177 }
178
179 channel.setDescription(ITrackerResources.getString("itracker.feed.project.description", project.getName()));
180 channel.setTitle(ITrackerResources.getString("itracker.feed.project.title ", project.getName(), project.getDescription()));
181 channel.setLink(getProjectURL(project, req, getServletContext(), baseURL));
182
183 is.getIssuesByProjectId(project.getId());
184 List<Item> sItems = projectIssueItems(req, baseURL, listIssues);
185
186 channel.setItems(sItems);
187 }
188
189 private void toChannel(Channel channel, Issue issue, String baseURL) throws MalformedURLException {
190 channel.setDescription( ITrackerResources.getString("itracker.feed.issue.description.description", String.valueOf(issue.getId())));
191 channel.setTitle(ITrackerResources.getString("itracker.feed.issue.title",
192 String.valueOf(issue.getId()), issue.getDescription()));
193 channel.setLink(getIssueURL(issue, baseURL));
194
195 channel.setTitle(issue.getId() + " - " + issue.getDescription());
196 List<Item> sItems = issueHistoryItems(baseURL, issue);
197
198 channel.setItems(sItems);
199 }
200
201
202 private void deny(ServletServerHttpResponse sr) {
203 sr.getHeaders().clear();
204 sr.setStatusCode(HttpStatus.FORBIDDEN);
205 }
206
207 private List<Item> projectIssueItems(HttpServletRequest req, String baseURL, List<Issue> listIssues) throws MalformedURLException {
208
209 List<Item> sItems = new ArrayList<>(listIssues.size());
210
211 final Iterator<Issue> issuesIt = listIssues.iterator();
212
213 Issue issue;
214 Item current;
215
216 while (issuesIt.hasNext()) {
217
218 issue = issuesIt.next();
219 current = new Item();
220 current.setUri(baseURL + req.getServletPath());
221 current.setLink(getIssueURL(issue, baseURL));
222
223 Guid guid = new Guid();
224 guid.setValue(current.getLink());
225 guid.setPermaLink(true);
226
227 current.setGuid(guid);
228
229 Description desc = new Description();
230 desc.setType(Content.TEXT);
231 desc.setValue("Issue " + issue.getId());
232 current.setDescription(desc);
233
234 current.setTitle(issue.getDescription());
235 current.setPubDate(issue.getLastModifiedDate());
236 Content c = new Content();
237 c.setType(Content.HTML);
238
239 c.setValue(
240 issue.getHistory().get(issue.getHistory().size() - 1).getDescription()
241 );
242 current.setContent(c);
243
244 sItems.add(current);
245 }
246 return sItems;
247 }
248
249 private List<Item> issueHistoryItems(String baseURL, Issue i) throws MalformedURLException {
250 List<Item> sItems = new ArrayList<>(i.getHistory().size());
251 Item current;
252
253 StringBuilder author = new StringBuilder();
254 List<IssueHistory> history = new LinkedList<>(i.getHistory());
255 Collections.reverse(history);
256 for (IssueHistory ih : history) {
257 int index = ( history.indexOf(ih)+1 );
258 if (index > 10) {
259
260 break;
261 }
262
263 current = new Item();
264 Content content = new Content();
265
266 current.setLink(getIssueURL(i, baseURL));
267
268 Guid guid = new Guid();
269 guid.setValue(current.getLink() + "#h" + index);
270 guid.setPermaLink(true);
271 current.setGuid(guid);
272
273 current.setTitle(ITrackerResources.getString("itracker.feed.history.title",
274 String.valueOf(history.size() + 1 - index)));
275
276 Description desc = new Description();
277 desc.setType(Content.TEXT);
278 desc.setValue(HTMLUtilities.removeMarkup( ih.getDescription() ));
279 current.setDescription(desc);
280
281 content.setType(Content.HTML);
282
283 content.setValue(
284 "<p>" + HTMLUtilities.removeMarkup( ih.getDescription() ) + "</p>"
285 );
286 current.setContent(content);
287 current.setPubDate(ih.getCreateDate());
288
289 author.setLength(0);
290 InternetAddress authorAddress = ih.getUser().getEmailAddress();
291 if (StringUtils.isNotBlank(authorAddress.getAddress())) {
292 author.append(authorAddress.getAddress());
293 if (StringUtils.isNotBlank(authorAddress.getPersonal())) {
294 author.append(' ')
295 .append('(')
296 .append(authorAddress.getPersonal())
297 .append(')');
298 }
299 current.setAuthor(String.valueOf(author));
300 }
301
302
303 sItems.add(current);
304 }
305 return sItems;
306 }
307
308
309 private String getIssueURL(Issue i, String baseUrl) throws MalformedURLException {
310 return String.valueOf(IssueUtilities.getIssueURL(i, baseUrl));
311
312 }
313
314 private String getProjectURL(Project p, HttpServletRequest req, ServletContext context, String baseUrl) {
315 ModuleConfig conf = ModuleUtils.getInstance().getModuleConfig(
316 "/module-projects",
317 req,
318 context);
319
320 ForwardConfig forwardConfig = conf.findForwardConfig("listissues");
321 return baseUrl + TagUtils.getInstance().pageURL(req, forwardConfig.getPath(), conf) + "?projectId=" + p.getId();
322 }
323 }