James Moger
2014-05-16 aa1361d04cfe09f90e7d8bece90c00dd6e4185bb
src/main/java/com/gitblit/tickets/ITicketService.java
@@ -49,6 +49,7 @@
import com.gitblit.models.TicketModel.Patchset;
import com.gitblit.models.TicketModel.Status;
import com.gitblit.tickets.TicketIndexer.Lucene;
import com.gitblit.utils.DeepCopier;
import com.gitblit.utils.DiffUtils;
import com.gitblit.utils.DiffUtils.DiffStat;
import com.gitblit.utils.StringUtils;
@@ -63,6 +64,8 @@
 *
 */
public abstract class ITicketService {
   public static final String SETTING_UPDATE_DIFFSTATS = "migration.updateDiffstats";
   private static final String LABEL = "label";
@@ -105,6 +108,8 @@
   private final Map<String, List<TicketLabel>> labelsCache;
   private final Map<String, List<TicketMilestone>> milestonesCache;
   private final boolean updateDiffstats;
   private static class TicketKey {
      final String repository;
@@ -163,6 +168,8 @@
      this.labelsCache = new ConcurrentHashMap<String, List<TicketLabel>>();
      this.milestonesCache = new ConcurrentHashMap<String, List<TicketMilestone>>();
      this.updateDiffstats = settings.getBoolean(SETTING_UPDATE_DIFFSTATS, true);
   }
   /**
@@ -244,6 +251,7 @@
    */
   public boolean isAcceptingTicketUpdates(RepositoryModel repository) {
      return isReady()
            && repository.hasCommits
            && repository.isBare
            && !repository.isFrozen
            && !repository.isMirror;
@@ -379,7 +387,9 @@
      } catch (IOException e) {
         log.error("failed to create label " + label + " in " + repository, e);
      } finally {
         db.close();
         if (db != null) {
            db.close();
         }
      }
      return lb;
   }
@@ -405,7 +415,9 @@
      } catch (IOException e) {
         log.error("failed to update label " + label + " in " + repository, e);
      } finally {
         db.close();
         if (db != null) {
            db.close();
         }
      }
      return false;
   }
@@ -444,7 +456,9 @@
      } catch (IOException e) {
         log.error("failed to rename label " + oldName + " in " + repository, e);
      } finally {
         db.close();
         if (db != null) {
            db.close();
         }
      }
      return false;
   }
@@ -473,7 +487,9 @@
      } catch (IOException e) {
         log.error("failed to delete label " + label + " in " + repository, e);
      } finally {
         db.close();
         if (db != null) {
            db.close();
         }
      }
      return false;
   }
@@ -548,9 +564,10 @@
   public TicketMilestone getMilestone(RepositoryModel repository, String milestone) {
      for (TicketMilestone ms : getMilestones(repository)) {
         if (ms.name.equalsIgnoreCase(milestone)) {
            TicketMilestone tm = DeepCopier.copy(ms);
            String q = QueryBuilder.q(Lucene.rid.matches(repository.getRID())).and(Lucene.milestone.matches(milestone)).build();
            ms.tickets = indexer.queryFor(q, 1, 0, Lucene.number.name(), true);
            return ms;
            tm.tickets = indexer.queryFor(q, 1, 0, Lucene.number.name(), true);
            return tm;
         }
      }
      return null;
@@ -579,7 +596,9 @@
      } catch (IOException e) {
         log.error("failed to create milestone " + milestone + " in " + repository, e);
      } finally {
         db.close();
         if (db != null) {
            db.close();
         }
      }
      return ms;
   }
@@ -611,7 +630,9 @@
      } catch (IOException e) {
         log.error("failed to update milestone " + milestone + " in " + repository, e);
      } finally {
         db.close();
         if (db != null) {
            db.close();
         }
      }
      return false;
   }
@@ -627,6 +648,22 @@
    * @since 1.4.0
    */
   public synchronized boolean renameMilestone(RepositoryModel repository, String oldName, String newName, String createdBy) {
      return renameMilestone(repository, oldName, newName, createdBy, true);
   }
   /**
    * Renames a milestone.
    *
    * @param repository
    * @param oldName
    * @param newName
    * @param createdBy
    * @param notifyOpenTickets
    * @return true if successful
    * @since 1.6.0
    */
   public synchronized boolean renameMilestone(RepositoryModel repository, String oldName,
         String newName, String createdBy, boolean notifyOpenTickets) {
      if (StringUtils.isEmpty(newName)) {
         throw new IllegalArgumentException("new milestone can not be empty!");
      }
@@ -639,7 +676,7 @@
         config.setString(MILESTONE, newName, STATUS, milestone.status.name());
         config.setString(MILESTONE, newName, COLOR, milestone.color);
         if (milestone.due != null) {
            config.setString(MILESTONE, milestone.name, DUE,
            config.setString(MILESTONE, newName, DUE,
                  new SimpleDateFormat(DUE_DATE_PATTERN).format(milestone.due));
         }
         config.save();
@@ -651,15 +688,21 @@
            Change change = new Change(createdBy);
            change.setField(Field.milestone, newName);
            TicketModel ticket = updateTicket(repository, qr.number, change);
            notifier.queueMailing(ticket);
            if (notifyOpenTickets && ticket.isOpen()) {
               notifier.queueMailing(ticket);
            }
         }
         notifier.sendAll();
         if (notifyOpenTickets) {
            notifier.sendAll();
         }
         return true;
      } catch (IOException e) {
         log.error("failed to rename milestone " + oldName + " in " + repository, e);
      } finally {
         db.close();
         if (db != null) {
            db.close();
         }
      }
      return false;
   }
@@ -674,11 +717,27 @@
    * @since 1.4.0
    */
   public synchronized boolean deleteMilestone(RepositoryModel repository, String milestone, String createdBy) {
      return deleteMilestone(repository, milestone, createdBy, true);
   }
   /**
    * Deletes a milestone.
    *
    * @param repository
    * @param milestone
    * @param createdBy
    * @param notifyOpenTickets
    * @return true if successful
    * @since 1.6.0
    */
   public synchronized boolean deleteMilestone(RepositoryModel repository, String milestone,
         String createdBy, boolean notifyOpenTickets) {
      if (StringUtils.isEmpty(milestone)) {
         throw new IllegalArgumentException("milestone can not be empty!");
      }
      Repository db = null;
      try {
         TicketMilestone tm = getMilestone(repository, milestone);
         db = repositoryManager.getRepository(repository.name);
         StoredConfig config = db.getConfig();
         config.unsetSection(MILESTONE, milestone);
@@ -686,14 +745,37 @@
         milestonesCache.remove(repository.name);
         TicketNotifier notifier = createNotifier();
         for (QueryResult qr : tm.tickets) {
            Change change = new Change(createdBy);
            change.setField(Field.milestone, "");
            TicketModel ticket = updateTicket(repository, qr.number, change);
            if (notifyOpenTickets && ticket.isOpen()) {
               notifier.queueMailing(ticket);
            }
         }
         if (notifyOpenTickets) {
            notifier.sendAll();
         }
         return true;
      } catch (IOException e) {
         log.error("failed to delete milestone " + milestone + " in " + repository, e);
      } finally {
         db.close();
         if (db != null) {
            db.close();
         }
      }
      return false;
   }
   /**
    * Returns the set of assigned ticket ids in the repository.
    *
    * @param repository
    * @return a set of assigned ticket ids in the repository
    * @since 1.6.0
    */
   public abstract Set<Long> getIds(RepositoryModel repository);
   /**
    * Assigns a new ticket id.
@@ -757,7 +839,7 @@
         ticket = getTicketImpl(repository, ticketId);
         // if ticket exists
         if (ticket != null) {
            if (ticket.hasPatchsets()) {
            if (ticket.hasPatchsets() && updateDiffstats) {
               Repository r = repositoryManager.getRepository(repository.name);
               try {
                  Patchset patchset = ticket.getCurrentPatchset();
@@ -790,6 +872,33 @@
    */
   protected abstract TicketModel getTicketImpl(RepositoryModel repository, long ticketId);
   /**
    * Returns the journal used to build a ticket.
    *
    * @param repository
    * @param ticketId
    * @return the journal for the ticket, if it exists, otherwise null
    * @since 1.6.0
    */
   public final List<Change> getJournal(RepositoryModel repository, long ticketId) {
      if (hasTicket(repository, ticketId)) {
         List<Change> journal = getJournalImpl(repository, ticketId);
         return journal;
      }
      return null;
   }
   /**
    * Retrieves the ticket journal.
    *
    * @param repository
    * @param ticketId
    * @return a ticket, if it exists, otherwise null
    * @since 1.6.0
    */
   protected abstract List<Change> getJournalImpl(RepositoryModel repository, long ticketId);
   /**
    * Get the ticket url
    *