View Javadoc
1   /*
2   * This software was designed and created by Jason Carroll.
3   * Copyright (c) 2002, 2003, 2004 Jason Carroll.
4   * The author can be reached at jcarroll@cowsultants.com
5   * ITracker website: http://www.cowsultants.com
6   * ITracker forums: http://www.cowsultants.com/phpBB/index.php
7   *
8   * This program is free software; you can redistribute it and/or modify
9   * it only under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  */
18  
19  package org.itracker.services.implementations;
20  
21  import org.apache.log4j.Logger;
22  import org.itracker.IssueSearchException;
23  import org.itracker.ProjectException;
24  import org.itracker.core.resources.ITrackerResources;
25  import org.itracker.model.*;
26  import org.itracker.model.Notification.Role;
27  import org.itracker.model.Notification.Type;
28  import org.itracker.model.util.IssueUtilities;
29  import org.itracker.persistence.dao.*;
30  import org.itracker.services.ConfigurationService;
31  import org.itracker.services.IssueService;
32  import org.itracker.services.NotificationService;
33  
34  import java.util.*;
35  
36  /**
37   * Issue related service layer. A bit "fat" at this time, because of being a* direct EJB porting. Going go get thinner over time** @author ricardo
38   * */
39  
40  //TODO: Cleanup this file, go through all issues, todos, etc.
41  
42  public class IssueServiceImpl implements IssueService {
43  
44      private static final Logger logger = Logger
45              .getLogger(IssueServiceImpl.class);
46      private ConfigurationService configurationService;
47  
48      private CustomFieldDAO customFieldDAO;
49  
50      private ProjectDAO projectDAO;
51  
52      private IssueDAO issueDAO;
53  
54      private IssueHistoryDAO issueHistoryDAO;
55  
56      private IssueRelationDAO issueRelationDAO;
57  
58      private IssueAttachmentDAO issueAttachmentDAO;
59  
60      private ComponentDAO componentDAO;
61  
62      private IssueActivityDAO issueActivityDAO;
63  
64      private VersionDAO versionDAO;
65  
66      private NotificationService notificationService;
67      private UserDAO userDAO;
68  
69      public IssueServiceImpl() {
70  
71      }
72  
73      public Issue getIssue(Integer issueId) {
74          Issue issue = getIssueDAO().findByPrimaryKey(issueId);
75          return issue;
76      }
77  
78      /**
79       * @deprecated don't use to expensive memory use!
80       */
81      public List<Issue> getAllIssues() {
82          logger.warn("getAllIssues: use of deprecated API");
83          if (logger.isDebugEnabled()) {
84              logger
85                      .debug("getAllIssues: stacktrace was",
86                              new RuntimeException());
87          }
88          return getIssueDAO().findAll();
89      }
90  
91      /**
92       * Added implementation to make proper count of ALL issues, instead select
93       * them in a list and return its size
94       */
95      public Long getNumberIssues() {
96          return getIssueDAO().countAllIssues();
97      }
98  
99      public List<Issue> getIssuesCreatedByUser(Integer userId) {
100         return getIssuesCreatedByUser(userId, true);
101     }
102 
103     public List<Issue> getIssuesCreatedByUser(Integer userId,
104                                               boolean availableProjectsOnly) {
105         final List<Issue> issues;
106 
107         if (availableProjectsOnly) {
108             issues = getIssueDAO().findByCreatorInAvailableProjects(userId,
109                     IssueUtilities.STATUS_CLOSED);
110         } else {
111             issues = getIssueDAO().findByCreator(userId,
112                     IssueUtilities.STATUS_CLOSED);
113         }
114         return issues;
115     }
116 
117     public List<Issue> getIssuesOwnedByUser(Integer userId) {
118 
119         return getIssuesOwnedByUser(userId, true);
120 
121     }
122 
123     public List<Issue> getIssuesOwnedByUser(Integer userId,
124                                             boolean availableProjectsOnly) {
125         final List<Issue> issues;
126 
127         if (availableProjectsOnly) {
128             issues = getIssueDAO().findByOwnerInAvailableProjects(userId,
129                     IssueUtilities.STATUS_RESOLVED);
130         } else {
131             issues = getIssueDAO().findByOwner(userId,
132                     IssueUtilities.STATUS_RESOLVED);
133         }
134         return issues;
135     }
136 
137     public List<Issue> getIssuesWatchedByUser(Integer userId) {
138         return getIssuesWatchedByUser(userId, true);
139     }
140 
141     /**
142      * TODO move to {@link NotificationService}
143      */
144     public List<Issue> getIssuesWatchedByUser(Integer userId,
145                                               boolean availableProjectsOnly) {
146         final List<Issue> issues;
147 
148         if (availableProjectsOnly) {
149             issues = getIssueDAO().findByNotificationInAvailableProjects(
150                     userId, IssueUtilities.STATUS_CLOSED);
151         } else {
152             issues = getIssueDAO().findByNotification(userId,
153                     IssueUtilities.STATUS_CLOSED);
154         }
155         return issues;
156     }
157 
158     public List<Issue> getUnassignedIssues() {
159         return getUnassignedIssues(true);
160     }
161 
162     public List<Issue> getUnassignedIssues(boolean availableProjectsOnly) {
163         final List<Issue> issues;
164 
165         if (availableProjectsOnly) {
166             issues = getIssueDAO()
167                     .findByStatusLessThanEqualToInAvailableProjects(
168                             IssueUtilities.STATUS_UNASSIGNED);
169         } else {
170             issues = getIssueDAO().findByStatusLessThanEqualTo(
171                     IssueUtilities.STATUS_UNASSIGNED);
172         }
173         return issues;
174     }
175 
176     /**
177      * Returns all issues with a status equal to the given status number
178      *
179      * @param status the status to compare
180      * @return an array of IssueModels that match the criteria
181      */
182 
183     public List<Issue> getIssuesWithStatus(int status) {
184         List<Issue> issues = getIssueDAO().findByStatus(status);
185         return issues;
186     }
187 
188     /**
189      * Returns all issues with a status less than the given status number
190      *
191      * @param status the status to compare
192      * @return an array of IssueModels that match the criteria
193      */
194 
195     public List<Issue> getIssuesWithStatusLessThan(int status) {
196         List<Issue> issues = getIssueDAO().findByStatusLessThan(status);
197         return issues;
198     }
199 
200     /**
201      * Returns all issues with a severity equal to the given severity number
202      *
203      * @param severity the severity to compare
204      * @return an array of IssueModels that match the criteria
205      */
206 
207     public List<Issue> getIssuesWithSeverity(int severity) {
208         List<Issue> issues = getIssueDAO().findBySeverity(severity);
209         return issues;
210 
211     }
212 
213     public List<Issue> getIssuesByProjectId(Integer projectId) {
214         return getIssuesByProjectId(projectId, IssueUtilities.STATUS_END);
215     }
216 
217     public List<Issue> getIssuesByProjectId(Integer projectId, int status) {
218         List<Issue> issues = getIssueDAO().findByProjectAndLowerStatus(
219                 projectId, status);
220         return issues;
221     }
222 
223     public User getIssueCreator(Integer issueId) {
224         Issue issue = getIssueDAO().findByPrimaryKey(issueId);
225         User user = issue.getCreator();
226         return user;
227 
228     }
229 
230     public User getIssueOwner(Integer issueId) {
231         Issue issue = getIssueDAO().findByPrimaryKey(issueId);
232         User user = issue.getOwner();
233 
234         return user;
235 
236     }
237 
238     public List<Component> getIssueComponents(Integer issueId) {
239         Issue issue = getIssueDAO().findByPrimaryKey(issueId);
240         List<Component> components = issue.getComponents();
241 
242         return components;
243     }
244 
245     public List<Version> getIssueVersions(Integer issueId) {
246         Issue issue = getIssueDAO().findByPrimaryKey(issueId);
247 
248         List<Version> versions = issue.getVersions();
249         return versions;
250     }
251 
252     public List<IssueAttachment> getIssueAttachments(Integer issueId) {
253         Issue issue = getIssueDAO().findByPrimaryKey(issueId);
254 
255         List<IssueAttachment> attachments = issue.getAttachments();
256         return attachments;
257     }
258 
259     /**
260      * Old implementation is left here, commented, because it checked for
261      * history entry status. This feature was not finished, I think (RJST)
262      */
263     public List<IssueHistory> getIssueHistory(Integer issueId) {
264         return getIssueDAO().findByPrimaryKey(issueId).getHistory();
265     }
266 
267     public Issue createIssue(Issue issue, Integer projectId, Integer userId,
268                              Integer createdById) throws ProjectException {
269         Project project = getProjectDAO().findByPrimaryKey(projectId);
270         User creator = getUserDAO().findByPrimaryKey(userId);
271 
272         if (project.getStatus() != Status.ACTIVE) {
273             throw new ProjectException("Project is not active.");
274         }
275 
276         IssueActivity activity = new IssueActivity(issue, creator,
277                 IssueActivityType.ISSUE_CREATED);
278         activity.setDescription(ITrackerResources
279                 .getString("itracker.activity.system.createdfor")
280                 + " " + creator.getFirstName() + " " + creator.getLastName());
281 
282         activity.setIssue(issue);
283 
284         if (!(createdById == null || createdById.equals(userId))) {
285 
286             User createdBy = getUserDAO().findByPrimaryKey(createdById);
287             activity.setUser(createdBy);
288 
289             Notification watchModel = new Notification();
290 
291             watchModel.setUser(createdBy);
292 
293             watchModel.setIssue(issue);
294 
295             watchModel.setRole(Notification.Role.CONTRIBUTER);
296 
297             issue.getNotifications().add(watchModel);
298 
299         }
300 
301         List<IssueActivity> activities = new ArrayList<IssueActivity>();
302         activities.add(activity);
303         issue.setActivities(activities);
304 
305         issue.setProject(project);
306 
307         issue.setCreator(creator);
308 
309         // save
310         getIssueDAO().save(issue);
311 
312         return issue;
313     }
314 
315     /**
316      * Save a modified issue to the persistence layer
317      *
318      * @param issueDirty the changed, unsaved issue to update on persistency layer
319      * @param userId     the user-id of the changer
320      */
321     public Issue updateIssue(final Issue issueDirty, final Integer userId)
322             throws ProjectException {
323 
324         String existingTargetVersion = null;
325 
326         // detach the modified Issue form the Hibernate Session
327         getIssueDAO().detach(issueDirty);
328         // Retrieve the Issue from Hibernate Session and refresh it from
329         // Hibernate Session to previous state.
330         Issue persistedIssue = getIssueDAO().findByPrimaryKey(
331                 issueDirty.getId());
332 
333         getIssueDAO().refresh(persistedIssue);
334         if (logger.isDebugEnabled()) {
335             logger.debug("updateIssue: updating issue " + issueDirty
336                     + "\n(from " + persistedIssue + ")");
337         }
338 
339         User user = getUserDAO().findByPrimaryKey(userId);
340 
341         if (persistedIssue.getProject().getStatus() != Status.ACTIVE) {
342             throw new ProjectException("Project "
343                     + persistedIssue.getProject().getName() + " is not active.");
344         }
345 
346         if (!persistedIssue.getDescription().equalsIgnoreCase(
347                 issueDirty.getDescription())) {
348 
349             if (logger.isDebugEnabled()) {
350                 logger.debug("updateIssue: updating description from "
351                         + persistedIssue.getDescription());
352             }
353             IssueActivity activity = new IssueActivity();
354             activity.setActivityType(IssueActivityType.DESCRIPTION_CHANGE);
355             activity.setDescription(ITrackerResources
356                     .getString("itracker.web.generic.from")
357                     + ": " + persistedIssue.getDescription());
358             activity.setUser(user);
359             activity.setIssue(issueDirty);
360             issueDirty.getActivities().add(activity);
361 
362         }
363 
364         if (persistedIssue.getResolution() != null
365                 && !persistedIssue.getResolution().equalsIgnoreCase(
366                 issueDirty.getResolution())) {
367 
368             IssueActivity activity = new IssueActivity();
369             activity.setActivityType(IssueActivityType.RESOLUTION_CHANGE);
370             activity.setDescription(ITrackerResources
371                     .getString("itracker.web.generic.from")
372                     + ": " + persistedIssue.getResolution());
373             activity.setUser(user);
374             activity.setIssue(issueDirty);
375             issueDirty.getActivities().add(activity);
376         }
377 
378         if (null == persistedIssue.getStatus()
379                 || !persistedIssue.getStatus().equals(issueDirty.getStatus())) {
380             IssueActivity activity = new IssueActivity();
381             activity.setActivityType(IssueActivityType.STATUS_CHANGE);
382             activity.setDescription(IssueUtilities.getStatusName(persistedIssue
383                     .getStatus())
384                     + " "
385                     + ITrackerResources.getString("itracker.web.generic.to")
386                     + " "
387                     + IssueUtilities.getStatusName(issueDirty.getStatus()));
388             activity.setUser(user);
389             activity.setIssue(issueDirty);
390             issueDirty.getActivities().add(activity);
391         }
392 
393         if (issueDirty.getSeverity() != null
394                 && !issueDirty.getSeverity().equals(
395                 persistedIssue.getSeverity())
396                 && issueDirty.getSeverity() != -1) {
397 
398             IssueActivity activity = new IssueActivity();
399             activity.setActivityType(IssueActivityType.SEVERITY_CHANGE);
400             // FIXME why does it state Critical to Critical when it should Major to Critical!?
401             activity.setDescription(IssueUtilities
402                     .getSeverityName(persistedIssue.getSeverity())
403                     + " "
404                     + ITrackerResources.getString("itracker.web.generic.to")
405                     + " "
406                     + IssueUtilities.getSeverityName(issueDirty.getSeverity()));
407 
408             activity.setUser(user);
409             activity.setIssue(issueDirty);
410             issueDirty.getActivities().add(activity);
411         }
412 
413         if (persistedIssue.getTargetVersion() != null
414                 && issueDirty.getTargetVersion() != null
415                 && !persistedIssue.getTargetVersion().getId().equals(
416                 issueDirty.getTargetVersion().getId())) {
417             existingTargetVersion = persistedIssue.getTargetVersion()
418                     .getNumber();
419             Version version = this.getVersionDAO().findByPrimaryKey(
420                     issueDirty.getTargetVersion().getId());
421 
422             IssueActivity activity = new IssueActivity();
423             activity.setActivityType(IssueActivityType.TARGETVERSION_CHANGE);
424             String description = existingTargetVersion + " "
425                     + ITrackerResources.getString("itracker.web.generic.to")
426                     + " ";
427             description += version.getNumber();
428             activity.setDescription(description);
429             activity.setUser(user);
430             activity.setIssue(issueDirty);
431             issueDirty.getActivities().add(activity);
432         }
433 
434         // (re-)assign issue
435         User newOwner = issueDirty.getOwner();
436         issueDirty.setOwner(persistedIssue.getOwner());
437         if (logger.isDebugEnabled()) {
438             logger.debug("updateIssue: assigning from " + issueDirty.getOwner()
439                     + " to " + newOwner);
440         }
441         assignIssue(issueDirty, newOwner, user, false);
442         if (logger.isDebugEnabled()) {
443             logger.debug("updateIssue: updated assignment: " + issueDirty);
444         }
445 
446         if (logger.isDebugEnabled()) {
447             logger.debug("updateIssue: merging issue " + issueDirty + " to "
448                     + persistedIssue);
449         }
450 
451         persistedIssue = getIssueDAO().merge(issueDirty);
452 
453         if (logger.isDebugEnabled()) {
454             logger.debug("updateIssue: merged issue for saving: "
455                     + persistedIssue);
456         }
457         getIssueDAO().saveOrUpdate(persistedIssue);
458         if (logger.isDebugEnabled()) {
459             logger.debug("updateIssue: saved issue: " + persistedIssue);
460         }
461         return persistedIssue;
462     }
463 
464     /**
465      * Moves an issues from its current project to a new project.
466      *
467      * @param issue     an Issue of the issue to move
468      * @param projectId the id of the target project
469      * @param userId    the id of the user that is moving the issue
470      * @return an Issue of the issue after it has been moved
471      */
472 
473     public Issue moveIssue(Issue issue, Integer projectId, Integer userId) {
474 
475         if (logger.isDebugEnabled()) {
476             logger.debug("moveIssue: " + issue + " to project#" + projectId
477                     + ", user#" + userId);
478         }
479 
480         Project project = getProjectDAO().findByPrimaryKey(projectId);
481         User user = getUserDAO().findByPrimaryKey(userId);
482 
483         if (logger.isDebugEnabled()) {
484             logger.debug("moveIssue: " + issue + " to project: " + project
485                     + ", user: " + user);
486         }
487 
488         IssueActivity activity = new IssueActivity();
489         activity
490                 .setActivityType(org.itracker.model.IssueActivityType.ISSUE_MOVE);
491         activity.setDescription(issue.getProject().getName() + " "
492                 + ITrackerResources.getString("itracker.web.generic.to") + " "
493                 + project.getName());
494         activity.setUser(user);
495         activity.setIssue(issue);
496         issue.setProject(project);
497 
498         issue.getActivities().add(activity);
499 
500         if (logger.isDebugEnabled()) {
501             logger.debug("moveIssue: updated issue: " + issue);
502         }
503         try {
504             getIssueDAO().saveOrUpdate(issue);
505         } catch (Exception e) {
506             logger.error("moveIssue: failed to save issue: " + issue, e);
507             return null;
508         }
509         if (logger.isDebugEnabled()) {
510             logger.debug("moveIssue: saved move-issue to " + project);
511         }
512         return issue;
513 
514     }
515 
516     /**
517      * this should not exist. adding an history entry should be adding the
518      * history entry to the domain object and saving the object...
519      */
520     public boolean addIssueHistory(IssueHistory history) {
521         getIssueHistoryDAO().saveOrUpdate(history);
522         history.getIssue().getHistory().add(history);
523         getIssueDAO().saveOrUpdate(history.getIssue());
524         return true;
525     }
526 
527     /**
528      * TODO maybe it has no use at all. is it obsolete? when I'd set the
529      * issue-fields on an issue and then save/update issue, would it be good
530      * enough?
531      */
532     public boolean setIssueFields(Integer issueId, List<IssueField> fields) {
533         Issue issue = getIssueDAO().findByPrimaryKey(issueId);
534 
535         setIssueFields(issue, fields, true);
536 
537         return true;
538     }
539 
540     private boolean setIssueFields(Issue issue, List<IssueField> fields,
541                                    boolean save) {
542 
543         List<IssueField> issueFields = issue.getFields();
544 
545         if (fields.size() > 0) {
546             for (int i = 0; i < fields.size(); i++) {
547 
548                 IssueField field = fields.get(i);
549                 if (issueFields.contains(field)) {
550                     issueFields.remove(field);
551                 }
552 
553                 CustomField customField = getCustomFieldDAO().findByPrimaryKey(
554                         fields.get(i).getCustomField().getId());
555                 field.setCustomField(customField);
556                 field.setIssue(issue);
557 
558                 issueFields.add(field);
559             }
560         }
561         issue.setFields(issueFields);
562 
563         if (save) {
564             logger.debug("setIssueFields: save was true");
565             getIssueDAO().saveOrUpdate(issue);
566         }
567         return true;
568     }
569 
570     public boolean setIssueComponents(Integer issueId,
571                                       HashSet<Integer> componentIds, Integer userId) {
572 
573         Issue issue = getIssueDAO().findByPrimaryKey(issueId);
574         List<Component> components = new ArrayList<Component>(componentIds
575                 .size());
576         User user = userDAO.findByPrimaryKey(userId);
577         Iterator<Integer> idIt = componentIds.iterator();
578         while (idIt.hasNext()) {
579             Integer id = (Integer) idIt.next();
580             Component c = getComponentDAO().findById(id);
581             components.add(c);
582         }
583 
584         setIssueComponents(issue, components, user, true);
585         return true;
586     }
587 
588     private boolean setIssueComponents(Issue issue, List<Component> components,
589                                        User user, boolean save) {
590 
591         if (issue.getComponents() == null) {
592             if (logger.isInfoEnabled()) {
593                 logger.info("setIssueComponents: components was null");
594             }
595             issue.setComponents(new ArrayList<Component>(components.size()));
596         }
597         if (components.isEmpty() && !issue.getComponents().isEmpty()) {
598             addComponentsModifiedActivity(issue, user, new StringBuilder(
599                     ITrackerResources.getString("itracker.web.generic.all"))
600                     .append(" ").append(
601                             ITrackerResources
602                                     .getString("itracker.web.generic.removed"))
603                     .toString());
604             issue.getComponents().clear();
605         } else {
606             Collections.sort(issue.getComponents(), Component.NAME_COMPARATOR);
607 
608             for (Iterator<Component> iterator = issue.getComponents()
609                     .iterator(); iterator.hasNext(); ) {
610                 Component component = (Component) iterator.next();
611                 if (components.contains(component)) {
612                     components.remove(component);
613                 } else {
614                     addComponentsModifiedActivity(issue, user,
615                             new StringBuilder(ITrackerResources
616                                     .getString("itracker.web.generic.removed"))
617                                     .append(": ").append(component.getName())
618                                     .toString());
619                     iterator.remove();
620                 }
621             }
622             Collections.sort(components, Component.NAME_COMPARATOR);
623             for (Iterator<Component> iterator = components.iterator(); iterator
624                     .hasNext(); ) {
625 
626                 Component component = iterator.next();
627                 if (!issue.getComponents().contains(component)) {
628                     addComponentsModifiedActivity(issue, user,
629                             new StringBuilder(ITrackerResources
630                                     .getString("itracker.web.generic.added"))
631                                     .append(": ").append(component.getName())
632                                     .toString());
633                     issue.getComponents().add(component);
634                 }
635             }
636         }
637 
638         if (save) {
639             if (logger.isDebugEnabled()) {
640                 logger.debug("setIssueComponents: save was true");
641             }
642             getIssueDAO().saveOrUpdate(issue);
643         }
644         return true;
645 
646     }
647 
648     /**
649      * used by setIssueComponents for adding change activities
650      */
651     private void addComponentsModifiedActivity(Issue issue, User user,
652                                                String description) {
653         IssueActivity activity = new IssueActivity();
654         activity
655                 .setActivityType(org.itracker.model.IssueActivityType.COMPONENTS_MODIFIED);
656         activity.setDescription(description);
657         activity.setIssue(issue);
658         activity.setUser(user);
659         issue.getActivities().add(activity);
660     }
661 
662     private boolean setIssueVersions(Issue issue, List<Version> versions,
663                                      User user, boolean save) {
664 
665         if (issue.getVersions() == null) {
666             if (logger.isInfoEnabled()) {
667                 logger.info("setIssueVersions: versions were null!");
668             }
669             issue.setVersions(new ArrayList<Version>());
670         }
671 
672         if (versions.isEmpty() && !issue.getVersions().isEmpty()) {
673 
674             addVersionsModifiedActivity(issue, user, new StringBuilder(
675                     ITrackerResources.getString("itracker.web.generic.all"))
676                     .append(" ").append(
677                             ITrackerResources
678                                     .getString("itracker.web.generic.removed"))
679                     .toString());
680             issue.getVersions().clear();
681         } else {
682 
683             Collections.sort(issue.getVersions(), Version.VERSION_COMPARATOR);
684 
685             StringBuilder changesBuf = new StringBuilder();
686             for (Iterator<Version> iterator = issue.getVersions().iterator(); iterator
687                     .hasNext(); ) {
688 
689                 Version version = iterator.next();
690                 if (versions.contains(version)) {
691                     versions.remove(version);
692                 } else {
693                     if (changesBuf.length() > 0) {
694                         changesBuf.append(", ");
695                     }
696                     changesBuf.append(version.getNumber());
697                     iterator.remove();
698                 }
699             }
700 
701             if (changesBuf.length() > 0) {
702                 addVersionsModifiedActivity(issue, user, new StringBuilder(
703                         ITrackerResources
704                                 .getString("itracker.web.generic.removed"))
705                         .append(": ").append(changesBuf).toString());
706             }
707 
708             changesBuf = new StringBuilder();
709 
710             Collections.sort(versions, Version.VERSION_COMPARATOR);
711             for (Iterator<Version> iterator = versions.iterator(); iterator
712                     .hasNext(); ) {
713 
714                 Version version = iterator.next();
715                 if (changesBuf.length() > 0) {
716                     changesBuf.append(", ");
717                 }
718                 changesBuf.append(version.getNumber());
719                 issue.getVersions().add(version);
720             }
721             if (changesBuf.length() > 0) {
722                 addVersionsModifiedActivity(issue, user, new StringBuilder(
723                         ITrackerResources
724                                 .getString("itracker.web.generic.added"))
725                         .append(": ").append(changesBuf).toString());
726             }
727         }
728         if (save) {
729             if (logger.isDebugEnabled()) {
730                 logger.debug("setIssueVersions: updating issue: " + issue);
731             }
732             getIssueDAO().saveOrUpdate(issue);
733         }
734 
735         return true;
736     }
737 
738     /**
739      * used by setIssueComponents for adding change activities
740      */
741     private void addVersionsModifiedActivity(Issue issue, User user,
742                                              String description) {
743         IssueActivity activity = new IssueActivity();
744         activity
745                 .setActivityType(org.itracker.model.IssueActivityType.TARGETVERSION_CHANGE);
746         activity.setDescription(description);
747         activity.setIssue(issue);
748         activity.setUser(user);
749         issue.getActivities().add(activity);
750     }
751 
752     public boolean setIssueVersions(Integer issueId,
753                                     HashSet<Integer> versionIds, Integer userId) {
754 
755         Issue issue = getIssueDAO().findByPrimaryKey(issueId);
756         User user = userDAO.findByPrimaryKey(userId);
757         // load versions from ids
758         ArrayList<Version> versions = new ArrayList<Version>(versionIds.size());
759         Iterator<Integer> versionsIdIt = versionIds.iterator();
760         while (versionsIdIt.hasNext()) {
761             Integer id = versionsIdIt.next();
762             versions.add(getVersionDAO().findByPrimaryKey(id));
763         }
764 
765         return setIssueVersions(issue, versions, user, true);
766     }
767 
768     public IssueRelation getIssueRelation(Integer relationId) {
769 
770         IssueRelation issueRelation = getIssueRelationDAO().findByPrimaryKey(
771                 relationId);
772 
773         return issueRelation;
774 
775     }
776 
777     /**
778      * add a relation between two issues.
779      * <p/>
780      * TODO: There is no relation saved to database yet?
781      */
782     public boolean addIssueRelation(Integer issueId, Integer relatedIssueId,
783                                     IssueRelation.Type relationType, Integer userId) {
784 
785         User user = getUserDAO().findByPrimaryKey(userId);
786 
787         if (null == user) {
788             throw new IllegalArgumentException("Invalid user-id: " + userId);
789         }
790 
791         if (issueId != null && relatedIssueId != null) {
792 
793             IssueRelation.Type matchingRelationType = IssueUtilities
794                     .getMatchingRelationType(relationType);
795 
796             // if(matchingRelationType < 0) {
797 
798             // throw new CreateException("Unable to find matching relation type
799 
800             // for type: " + relationType);
801 
802             // }
803 
804             Issue issue = getIssueDAO().findByPrimaryKey(issueId);
805 
806             Issue relatedIssue = getIssueDAO().findByPrimaryKey(relatedIssueId);
807 
808             IssueRelation relationA = new IssueRelation();
809 
810             relationA.setRelationType(relationType);
811 
812             // relationA.setMatchingRelationId(relationBId);
813 
814             relationA.setIssue(issue);
815 
816             relationA.setRelatedIssue(relatedIssue);
817 
818             // set to 0 first, later reassign to relationB.id
819             relationA.setMatchingRelationId(0);
820 
821             relationA.setLastModifiedDate(new java.sql.Timestamp(new Date()
822                     .getTime()));
823 
824             getIssueRelationDAO().saveOrUpdate(relationA);
825 
826             IssueRelation relationB = new IssueRelation();
827 
828             relationB.setRelationType(matchingRelationType);
829 
830             // relationB.setMatchingRelationId(relationAId);
831 
832             relationB.setIssue(relatedIssue);
833 
834             relationB.setRelatedIssue(issue);
835 
836             relationB.setMatchingRelationId(relationA.getId());
837 
838             relationB.setLastModifiedDate(new java.sql.Timestamp(new Date()
839                     .getTime()));
840 
841             getIssueRelationDAO().saveOrUpdate(relationB);
842 
843             relationA.setMatchingRelationId(relationB.getId());
844             getIssueRelationDAO().saveOrUpdate(relationA);
845 
846             IssueActivity activity = new IssueActivity();
847             activity
848                     .setActivityType(org.itracker.model.IssueActivityType.RELATION_ADDED);
849             activity.setDescription(ITrackerResources.getString(
850                     "itracker.activity.relation.add", new Object[]{
851                     IssueUtilities.getRelationName(relationType),
852                     relatedIssueId}));
853 
854             activity.setIssue(issue);
855             issue.getActivities().add(activity);
856             // need to set user here
857             activity.setUser(user);
858             // need to save here
859             getIssueDAO().saveOrUpdate(issue);
860 
861             activity = new IssueActivity();
862             activity
863                     .setActivityType(org.itracker.model.IssueActivityType.RELATION_ADDED);
864             activity.setDescription(ITrackerResources.getString(
865                     "itracker.activity.relation.add", new Object[]{
866                     IssueUtilities
867                             .getRelationName(matchingRelationType),
868                     issueId}));
869             activity.setIssue(relatedIssue);
870             activity.setUser(user);
871             relatedIssue.getActivities().add(activity);
872             getIssueDAO().saveOrUpdate(relatedIssue);
873             return true;
874 
875         }
876 
877         return false;
878 
879     }
880 
881     public void removeIssueRelation(Integer relationId, Integer userId) {
882         IssueRelation issueRelation = getIssueRelationDAO().findByPrimaryKey(
883                 relationId);
884         Integer issueId = issueRelation.getIssue().getId();
885 
886         Integer relatedIssueId = issueRelation.getRelatedIssue().getId();
887 
888         Integer matchingRelationId = issueRelation.getMatchingRelationId();
889 
890         if (matchingRelationId != null) {
891             IssueActivity activity = new IssueActivity();
892             activity
893                     .setActivityType(org.itracker.model.IssueActivityType.RELATION_REMOVED);
894             activity.setDescription(ITrackerResources.getString(
895                     "itracker.activity.relation.removed", issueId.toString()));
896             // FIXME need to fix the commented code and save
897             // activity.setIssue(relatedIssueId);
898             // activity.setUser(userId);
899             // IssueRelationDAO.remove(matchingRelationId);
900         }
901 
902         IssueActivity activity = new IssueActivity();
903         activity
904                 .setActivityType(org.itracker.model.IssueActivityType.RELATION_REMOVED);
905         activity.setDescription(ITrackerResources
906                 .getString("itracker.activity.relation.removed", relatedIssueId
907                         .toString()));
908         // activity.setIssue(issueId);
909         // activity.setUser(userId);
910         // irHome.remove(relationId);
911         // need to save
912 
913         getIssueRelationDAO().delete(issueRelation);
914     }
915 
916     public boolean assignIssue(Integer issueId, Integer userId) {
917         return assignIssue(issueId, userId, userId);
918     }
919 
920     /**
921      * only use for updating issue from actions..
922      */
923     public boolean assignIssue(Integer issueId, Integer userId,
924                                Integer assignedByUserId) {
925 
926         return assignIssue(getIssueDAO().findByPrimaryKey(issueId),
927                 getUserDAO().findByPrimaryKey(userId), getUserDAO()
928                 .findByPrimaryKey(assignedByUserId), true);
929     }
930 
931     /**
932      * Only for use
933      *
934      * @param save save issue and send notification
935      */
936     private boolean assignIssue(Issue issue, User user, User assignedByUser,
937                                 final boolean save) {
938 
939         if (issue.getOwner() == user
940                 || (null != issue.getOwner() && issue.getOwner().equals(user))) {
941             // nothing to do.
942             if (logger.isDebugEnabled()) {
943                 logger.debug("assignIssue: attempted to reassign " + issue
944                         + " to current owner " + user);
945             }
946             return false;
947         }
948 
949         if (null == user) {
950             if (logger.isInfoEnabled()) {
951                 logger.info("assignIssue: call to unasign " + issue);
952             }
953 
954             return unassignIssue(issue, assignedByUser, save);
955         }
956 
957         if (logger.isInfoEnabled()) {
958             logger.info("assignIssue: assigning " + issue + " to " + user);
959         }
960 
961         User currOwner = issue.getOwner();
962 
963         if (!user.equals(currOwner)) {
964             if (currOwner != null
965                     && !notificationService.hasIssueNotification(issue,
966                     currOwner.getId(), Role.IP)) {
967                 // Notification notification = new Notification();
968                 Notification notification = new Notification(currOwner, issue,
969                         Role.IP);
970                 if (save) {
971                     notificationService.addIssueNotification(notification);
972                 } else {
973                     issue.getNotifications().add(notification);
974                 }
975             }
976 
977             IssueActivity activity = new IssueActivity();
978             activity
979                     .setActivityType(org.itracker.model.IssueActivityType.OWNER_CHANGE);
980             activity.setDescription((currOwner == null ? "["
981                     + ITrackerResources
982                     .getString("itracker.web.generic.unassigned") + "]"
983                     : currOwner.getLogin())
984                     + " "
985                     + ITrackerResources.getString("itracker.web.generic.to")
986                     + " " + user.getLogin());
987             activity.setUser(assignedByUser);
988             activity.setIssue(issue);
989             issue.getActivities().add(activity);
990 
991             issue.setOwner(user);
992 
993             if (logger.isDebugEnabled()) {
994                 logger.debug("assignIssue: current status: "
995                         + issue.getStatus());
996             }
997             if (issue.getStatus() < IssueUtilities.STATUS_ASSIGNED) {
998                 issue.setStatus(IssueUtilities.STATUS_ASSIGNED);
999                 if (logger.isDebugEnabled()) {
1000                     logger.debug("assignIssue: new status set to "
1001                             + issue.getStatus());
1002                 }
1003             }
1004 
1005             // send assignment notification
1006             if (save) {
1007                 if (logger.isDebugEnabled()) {
1008                     logger.debug("assignIssue: saving re-assigned issue");
1009                 }
1010                 getIssueDAO().saveOrUpdate(issue);
1011                 notificationService.sendNotification(issue, Type.ASSIGNED,
1012                         getConfigurationService().getSystemBaseURL());
1013 
1014             }
1015         }
1016         return true;
1017 
1018     }
1019 
1020     /**
1021      * @param save save issue and send notification
1022      */
1023     private boolean unassignIssue(Issue issue, User unassignedByUser,
1024                                   boolean save) {
1025         if (logger.isDebugEnabled()) {
1026             logger.debug("unassignIssue: " + issue);
1027         }
1028         if (issue.getOwner() != null) {
1029 
1030             if (logger.isDebugEnabled()) {
1031                 logger.debug("unassignIssue: unassigning from "
1032                         + issue.getOwner());
1033             }
1034             if (!notificationService.hasIssueNotification(issue, issue
1035                     .getOwner().getId(), Role.CONTRIBUTER)) {
1036                 // Notification notification = new Notification();
1037                 Notification notification = new Notification(issue.getOwner(),
1038                         issue, Role.CONTRIBUTER);
1039                 if (save) {
1040                     notificationService.addIssueNotification(notification);
1041                 } else {
1042                     issue.getNotifications().add(notification);
1043                 }
1044             }
1045             IssueActivity activity = new IssueActivity(issue, unassignedByUser,
1046                     IssueActivityType.OWNER_CHANGE);
1047             activity
1048                     .setDescription(issue.getOwner().getLogin()
1049                             + " "
1050                             + ITrackerResources
1051                             .getString("itracker.web.generic.to")
1052                             + " ["
1053                             + ITrackerResources
1054                             .getString("itracker.web.generic.unassigned")
1055                             + "]");
1056 
1057             issue.setOwner(null);
1058 
1059             if (issue.getStatus() >= IssueUtilities.STATUS_ASSIGNED) {
1060                 issue.setStatus(IssueUtilities.STATUS_UNASSIGNED);
1061             }
1062             if (save) {
1063                 if (logger.isDebugEnabled()) {
1064                     logger.debug("unassignIssue: saving unassigned issue..");
1065                 }
1066                 getIssueDAO().saveOrUpdate(issue);
1067                 notificationService.sendNotification(issue, Type.ASSIGNED,
1068                         getConfigurationService().getSystemBaseURL());
1069             }
1070         }
1071 
1072         return true;
1073     }
1074 
1075     /**
1076      * System-Update an issue, adds the action to the issue and updates the
1077      * issue
1078      */
1079     public Issue systemUpdateIssue(Issue updateissue, Integer userId)
1080             throws ProjectException {
1081 
1082         IssueActivity activity = new IssueActivity();
1083         activity.setActivityType(IssueActivityType.SYSTEM_UPDATE);
1084         activity.setDescription(ITrackerResources
1085                 .getString("itracker.activity.system.status"));
1086         ArrayList<IssueActivity> activities = new ArrayList<IssueActivity>();
1087 
1088         activity.setIssue(updateissue);
1089         activity.setUser(getUserDAO().findByPrimaryKey(userId));
1090         updateissue.getActivities().add(activity);
1091 
1092         Issue updated = updateIssue(updateissue, userId);
1093         updated.getActivities().addAll(activities);
1094         getIssueDAO().saveOrUpdate(updated);
1095 
1096         return updated;
1097     }
1098 
1099     /*
1100       * public boolean addIssueActivity(IssueActivityModel model) {
1101       *
1102       * Issue issue = ifHome.findByPrimaryKey(model.getIssueId());
1103       *
1104       * User user = ufHome.findByPrimaryKey(model.getUserId());
1105       *
1106       * //return addIssueActivity(model, issue, user); return
1107       * addIssueActivity(null, issue, user); }
1108       */
1109 
1110     /*
1111       * public boolean addIssueActivity(IssueActivityModel model, Issue issue) {
1112       *
1113       * User user = ufHome.findByPrimaryKey(model.getUserId());
1114       *
1115       * return true;//addIssueActivity(model, issue, user); }
1116       */
1117 
1118     /**
1119      * I think this entire method is useless - RJST TODO move to
1120      * {@link NotificationService}
1121      */
1122     /*
1123       * public boolean addIssueActivity(IssueActivityBean model, Issue issue,
1124       * User user) {
1125       *
1126       * IssueActivityBean activity = new IssueActivityBean();
1127       *
1128       * //activity.setModel(model);
1129       *
1130       * activity.setIssue(issue);
1131       *
1132       * activity.setUser(user);
1133       *
1134       * return true; }
1135       */
1136     public void updateIssueActivityNotification(Integer issueId,
1137                                                 boolean notificationSent) {
1138 
1139         if (issueId == null) {
1140 
1141             return;
1142 
1143         }
1144 
1145         Collection<IssueActivity> activity = getIssueActivityDAO()
1146                 .findByIssueId(issueId);
1147 
1148         for (Iterator<IssueActivity> iter = activity.iterator(); iter.hasNext(); ) {
1149 
1150             ((IssueActivity) iter.next()).setNotificationSent(notificationSent);
1151 
1152         }
1153 
1154     }
1155 
1156     /**
1157      * Adds an attachment to an issue
1158      *
1159      * @param attachment The attachment data
1160      * @param data       The byte data
1161      */
1162     public boolean addIssueAttachment(IssueAttachment attachment, byte[] data) {
1163         Issue issue = attachment.getIssue();
1164 
1165         attachment.setFileName("attachment_issue_" + issue.getId() + "_"
1166                 + attachment.getOriginalFileName());
1167         attachment.setFileData((data == null ? new byte[0] : data));
1168 
1169         // TODO: translate activity for adding attachments
1170         // IssueActivity activityAdd = new IssueActivity(issue,
1171         //         attachment.getUser(), IssueActivityType.ATTACHMENT_ADDED);
1172         // activityAdd.setDescription(attachment.getOriginalFileName());
1173         // issue.getActivities().add(activityAdd);
1174 
1175         if (logger.isDebugEnabled()) {
1176             logger.debug("addIssueAttachment: adding attachment " + attachment);
1177         }
1178         // add attachment to issue
1179         issue.getAttachments().add(attachment);
1180         if (logger.isDebugEnabled()) {
1181             logger.debug("addIssueAttachment: saving updated issue " + issue);
1182         }
1183         this.getIssueDAO().saveOrUpdate(issue);
1184         return true;
1185     }
1186 
1187     public boolean setIssueAttachmentData(Integer attachmentId, byte[] data) {
1188 
1189         if (attachmentId != null && data != null) {
1190 
1191             IssueAttachment attachment = getIssueAttachmentDAO()
1192                     .findByPrimaryKey(attachmentId);
1193 
1194             attachment.setFileData(data);
1195 
1196             return true;
1197 
1198         }
1199 
1200         return false;
1201 
1202     }
1203 
1204     public boolean setIssueAttachmentData(String fileName, byte[] data) {
1205 
1206         if (fileName != null && data != null) {
1207 
1208             IssueAttachment attachment = getIssueAttachmentDAO()
1209                     .findByFileName(fileName);
1210 
1211             attachment.setFileData(data);
1212 
1213             return true;
1214 
1215         }
1216 
1217         return false;
1218 
1219     }
1220 
1221     /**
1222      * Removes a attachement (deletes it)
1223      *
1224      * @param attachmentId the id of the <code>IssueAttachmentBean</code>
1225      */
1226     public boolean removeIssueAttachment(Integer attachmentId) {
1227 
1228         IssueAttachment attachementBean = this.getIssueAttachmentDAO()
1229                 .findByPrimaryKey(attachmentId);
1230 
1231         getIssueAttachmentDAO().delete(attachementBean);
1232 
1233         return true;
1234     }
1235 
1236     public Integer removeIssueHistoryEntry(Integer entryId, Integer userId) {
1237 
1238         IssueHistory history = getIssueHistoryDAO().findByPrimaryKey(entryId);
1239 
1240         if (history != null) {
1241 
1242             history.setStatus(IssueUtilities.HISTORY_STATUS_REMOVED);
1243 
1244             IssueActivity activity = new IssueActivity();
1245             activity
1246                     .setActivityType(org.itracker.model.IssueActivityType.REMOVE_HISTORY);
1247             activity.setDescription(ITrackerResources
1248                     .getString("itracker.web.generic.entry")
1249                     + " "
1250                     + entryId
1251                     + " "
1252                     + ITrackerResources
1253                     .getString("itracker.web.generic.removed") + ".");
1254 
1255             getIssueHistoryDAO().delete(history);
1256 
1257             return history.getIssue().getId();
1258 
1259         }
1260 
1261         return Integer.valueOf(-1);
1262 
1263     }
1264 
1265     public Project getIssueProject(Integer issueId) {
1266         Issue issue = getIssueDAO().findByPrimaryKey(issueId);
1267         Project project = issue.getProject();
1268 
1269         return project;
1270     }
1271 
1272     public HashSet<Integer> getIssueComponentIds(Integer issueId) {
1273 
1274         HashSet<Integer> componentIds = new HashSet<Integer>();
1275         Issue issue = getIssueDAO().findByPrimaryKey(issueId);
1276         Collection<Component> components = issue.getComponents();
1277 
1278         for (Iterator<Component> iterator = components.iterator(); iterator
1279                 .hasNext(); ) {
1280             componentIds.add(((Component) iterator.next()).getId());
1281         }
1282 
1283         return componentIds;
1284 
1285     }
1286 
1287     public HashSet<Integer> getIssueVersionIds(Integer issueId) {
1288 
1289         HashSet<Integer> versionIds = new HashSet<Integer>();
1290 
1291         Issue issue = getIssueDAO().findByPrimaryKey(issueId);
1292 
1293         Collection<Version> versions = issue.getVersions();
1294 
1295         for (Iterator<Version> iterator = versions.iterator(); iterator
1296                 .hasNext(); ) {
1297 
1298             versionIds.add(((Version) iterator.next()).getId());
1299 
1300         }
1301 
1302         return versionIds;
1303 
1304     }
1305 
1306     public List<IssueActivity> getIssueActivity(Integer issueId) {
1307 
1308         int i = 0;
1309 
1310         Collection<IssueActivity> activity = getIssueActivityDAO()
1311                 .findByIssueId(issueId);
1312 
1313         IssueActivity[] activityArray = new IssueActivity[activity.size()];
1314 
1315         for (Iterator<IssueActivity> iterator = activity.iterator(); iterator
1316                 .hasNext(); i++) {
1317 
1318             activityArray[i] = ((IssueActivity) iterator.next());
1319 
1320         }
1321 
1322         return Arrays.asList(activityArray);
1323 
1324     }
1325 
1326     /**
1327      * TODO move to {@link NotificationService} ?
1328      */
1329     public List<IssueActivity> getIssueActivity(Integer issueId,
1330                                                 boolean notificationSent) {
1331 
1332         int i = 0;
1333 
1334         Collection<IssueActivity> activity = getIssueActivityDAO()
1335                 .findByIssueIdAndNotification(issueId, notificationSent);
1336 
1337         IssueActivity[] activityArray = new IssueActivity[activity.size()];
1338 
1339         for (Iterator<IssueActivity> iterator = activity.iterator(); iterator
1340                 .hasNext(); i++) {
1341 
1342             activityArray[i] = ((IssueActivity) iterator.next());
1343 
1344         }
1345 
1346         return Arrays.asList(activityArray);
1347 
1348     }
1349 
1350     public Long getAllIssueAttachmentCount() {
1351         return getIssueAttachmentDAO().countAll().longValue();
1352     }
1353 
1354     public List<IssueAttachment> getAllIssueAttachments() {
1355         logger.warn("getAllIssueAttachments: use of deprecated API");
1356         if (logger.isDebugEnabled()) {
1357             logger.debug("getAllIssueAttachments: stacktrace was",
1358                     new RuntimeException());
1359         }
1360 
1361         List<IssueAttachment> attachments = getIssueAttachmentDAO().findAll();
1362 
1363         return attachments;
1364     }
1365 
1366     public IssueAttachment getIssueAttachment(Integer attachmentId) {
1367         IssueAttachment attachment = getIssueAttachmentDAO().findByPrimaryKey(
1368                 attachmentId);
1369 
1370         return attachment;
1371 
1372     }
1373 
1374     public byte[] getIssueAttachmentData(Integer attachmentId) {
1375 
1376         byte[] data;
1377 
1378         IssueAttachment attachment = getIssueAttachmentDAO().findByPrimaryKey(
1379                 attachmentId);
1380 
1381         data = attachment.getFileData();
1382 
1383         return data;
1384 
1385     }
1386 
1387     public int getIssueAttachmentCount(Integer issueId) {
1388 
1389         int i = 0;
1390 
1391         Issue issue = getIssueDAO().findByPrimaryKey(issueId);
1392 
1393         Collection<IssueAttachment> attachments = issue.getAttachments();
1394 
1395         i = attachments.size();
1396 
1397         return i;
1398 
1399     }
1400 
1401     /**
1402      * Returns the latest issue history entry for a particular issue.
1403      *
1404      * @param issueId the id of the issue to return the history entry for.
1405      * @return the latest IssueHistory, or null if no entries could be found
1406      */
1407     public IssueHistory getLastIssueHistory(Integer issueId) {
1408 
1409         List<IssueHistory> history = getIssueHistoryDAO()
1410                 .findByIssueId(issueId);
1411 
1412         if (null != history && history.size() > 0) {
1413             // sort ascending by id
1414             Collections.sort(history, AbstractEntity.ID_COMPARATOR);
1415             // return last entry in list
1416             return history.get(history.size() - 1);
1417         }
1418 
1419         return null;
1420 
1421     }
1422 
1423     public int getOpenIssueCountByProjectId(Integer projectId) {
1424 
1425         Collection<Issue> issues = getIssueDAO().findByProjectAndLowerStatus(
1426                 projectId, IssueUtilities.STATUS_RESOLVED);
1427 
1428         return issues.size();
1429 
1430     }
1431 
1432     public int getResolvedIssueCountByProjectId(Integer projectId) {
1433 
1434         Collection<Issue> issues = getIssueDAO().findByProjectAndHigherStatus(
1435                 projectId, IssueUtilities.STATUS_RESOLVED);
1436 
1437         return issues.size();
1438 
1439     }
1440 
1441     public int getTotalIssueCountByProjectId(Integer projectId) {
1442 
1443         Collection<Issue> issues = getIssueDAO().findByProject(projectId);
1444 
1445         return issues.size();
1446 
1447     }
1448 
1449     public Date getLatestIssueDateByProjectId(Integer projectId) {
1450 
1451         return getIssueDAO().latestModificationDate(projectId);
1452 
1453     }
1454 
1455     public List<Issue> getNextIssues(Integer issueId) {
1456         return getIssueDAO().findNextIssues(issueId);
1457     }
1458     public List<Issue> getPreviousIssues(Integer issueId) {
1459         return getIssueDAO().findPreviousIssues(issueId);
1460     }
1461 
1462     public boolean canViewIssue(Integer issueId, User user) {
1463 
1464         Issue issue = getIssue(issueId);
1465 
1466         Map<Integer, Set<PermissionType>> permissions = getUserDAO()
1467                 .getUsersMapOfProjectsAndPermissionTypes(user);
1468 
1469         return IssueUtilities.canViewIssue(issue, user.getId(), permissions);
1470 
1471     }
1472 
1473     public boolean canViewIssue(Issue issue, User user) {
1474 
1475         Map<Integer, Set<PermissionType>> permissions = getUserDAO()
1476                 .getUsersMapOfProjectsAndPermissionTypes(user);
1477 
1478         return IssueUtilities.canViewIssue(issue, user.getId(), permissions);
1479 
1480     }
1481 
1482     private UserDAO getUserDAO() {
1483         return userDAO;
1484     }
1485 
1486     private IssueDAO getIssueDAO() {
1487         return issueDAO;
1488     }
1489 
1490     private ProjectDAO getProjectDAO() {
1491         return projectDAO;
1492     }
1493 
1494     private IssueActivityDAO getIssueActivityDAO() {
1495         return issueActivityDAO;
1496     }
1497 
1498     private VersionDAO getVersionDAO() {
1499         return this.versionDAO;
1500     }
1501 
1502     private ComponentDAO getComponentDAO() {
1503         return this.componentDAO;
1504     }
1505 
1506     private CustomFieldDAO getCustomFieldDAO() {
1507         return customFieldDAO;
1508     }
1509 
1510     private IssueHistoryDAO getIssueHistoryDAO() {
1511         return issueHistoryDAO;
1512     }
1513 
1514     private IssueRelationDAO getIssueRelationDAO() {
1515         return issueRelationDAO;
1516     }
1517 
1518     private IssueAttachmentDAO getIssueAttachmentDAO() {
1519         return issueAttachmentDAO;
1520     }
1521 
1522     /**
1523      * get total size of all attachments in database
1524      */
1525     public Long getAllIssueAttachmentSize() {
1526 
1527         return getIssueAttachmentDAO().totalAttachmentsSize().longValue() / 1024;
1528 
1529     }
1530 
1531     public List<Issue> searchIssues(IssueSearchQuery queryModel, User user,
1532                                     Map<Integer, Set<PermissionType>> userPermissions)
1533             throws IssueSearchException {
1534         return getIssueDAO().query(queryModel, user, userPermissions);
1535     }
1536 
1537     public Long totalSystemIssuesAttachmentSize() {
1538         return getIssueAttachmentDAO().totalAttachmentsSize();
1539     }
1540 
1541     public ConfigurationService getConfigurationService() {
1542         return configurationService;
1543     }
1544 
1545     public void setUserDAO(UserDAO userDAO) {
1546         this.userDAO = userDAO;
1547     }
1548 
1549     public void setConfigurationService(ConfigurationService configurationService) {
1550         this.configurationService = configurationService;
1551     }
1552 
1553     public void setCustomFieldDAO(CustomFieldDAO customFieldDAO) {
1554         this.customFieldDAO = customFieldDAO;
1555     }
1556 
1557     public void setProjectDAO(ProjectDAO projectDAO) {
1558         this.projectDAO = projectDAO;
1559     }
1560 
1561     public void setIssueDAO(IssueDAO issueDAO) {
1562         this.issueDAO = issueDAO;
1563     }
1564 
1565     public void setIssueHistoryDAO(IssueHistoryDAO issueHistoryDAO) {
1566         this.issueHistoryDAO = issueHistoryDAO;
1567     }
1568 
1569     public void setIssueRelationDAO(IssueRelationDAO issueRelationDAO) {
1570         this.issueRelationDAO = issueRelationDAO;
1571     }
1572 
1573     public void setIssueAttachmentDAO(IssueAttachmentDAO issueAttachmentDAO) {
1574         this.issueAttachmentDAO = issueAttachmentDAO;
1575     }
1576 
1577     public void setComponentDAO(ComponentDAO componentDAO) {
1578         this.componentDAO = componentDAO;
1579     }
1580 
1581     public void setIssueActivityDAO(IssueActivityDAO issueActivityDAO) {
1582         this.issueActivityDAO = issueActivityDAO;
1583     }
1584 
1585     public void setVersionDAO(VersionDAO versionDAO) {
1586         this.versionDAO = versionDAO;
1587     }
1588 
1589     public void setNotificationService(NotificationService notificationService) {
1590         this.notificationService = notificationService;
1591     }
1592 }