Skip to content

Commit

Permalink
Navigate events using links in popups.
Browse files Browse the repository at this point in the history
  • Loading branch information
svenschoenung committed Jul 24, 2018
1 parent 058d313 commit 15d5461
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 89 deletions.
78 changes: 43 additions & 35 deletions src/main/java/io/jenkins/plugins/view/calendar/CalendarEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,55 +30,66 @@
import java.util.*;

public class CalendarEvent {
private final String id;
private final TopLevelItem item;
private final Run build;
private final Calendar start;
private final Calendar end;
private final Calendar nextRun;
private final CalendarEventType type;
private final String title;
private final String url;
private final long duration;
private final boolean future;

private List<CalendarEvent> lastEvents;
private CalendarEvent previousEvent;
private CalendarEvent nextEvent;
private CalendarEvent nextScheduledEvent;

@SuppressWarnings("PMD.NullAssignment")
public CalendarEvent(final TopLevelItem item, final Calendar start, final long durationInMillis) {
this.id = initId(item.getUrl());
this.item = item;
this.build = null;
this.future = true;
this.type = CalendarEventType.FUTURE;
this.title = item.getFullDisplayName();
this.url = item.getUrl();
this.duration = durationInMillis;
this.start = start;
this.end = initEnd();
this.nextRun = null;
this.end = initEnd(start, durationInMillis);
}

@SuppressWarnings("PMD.NullAssignment")
public CalendarEvent(final TopLevelItem item, final Run build) {
this.id = initId(build.getUrl());
this.item = item;
this.build = build;
this.future = false;
this.type = CalendarEventType.fromResult(build.getResult());
this.title = build.getFullDisplayName();
this.url = build.getUrl();
this.duration = build.getDuration();
this.start = Calendar.getInstance();
this.start.setTimeInMillis(build.getStartTimeInMillis());
this.end = initEnd();
this.nextRun = new CronJobService().getNextRun(item);
this.end = initEnd(this.start, build.getDuration());
}

private static String initId(String url) {
return url.replace("/", "-").toLowerCase().replaceAll("-$", "");
}

private Calendar initEnd() {
private static Calendar initEnd(final Calendar start, final long duration) {
// duration needs to be at least 1sec otherwise
// fullcalendar will not properly display the event
final long dur = (this.duration < 1000) ? 1000 : this.duration;
final long dur = (duration < 1000) ? 1000 : duration;
final Calendar end = Calendar.getInstance();
end.setTime(this.start.getTime());
end.setTime(start.getTime());
end.add(Calendar.SECOND, (int) (dur / 1000));
return end;
}

public String getId() {
return this.id;
}

public TopLevelItem getItem() {
return this.item;
}
Expand Down Expand Up @@ -120,7 +131,7 @@ public long getDuration() {
}

public boolean isFuture() {
return this.future;
return build == null;
}

public String getTimestampString() {
Expand Down Expand Up @@ -149,39 +160,36 @@ public String getIconClassName() {
}
}

public List<Build> getLastBuilds() {
if (item instanceof Job) {
return ((Job)item).getLastBuildsOverThreshold(5, Result.ABORTED);
}
return new ArrayList<>();
public Run getBuild() {
return build;
}

public Run getPreviousBuild() {
if (build != null) {
return build.getPreviousBuild();
public List<CalendarEvent> getLastEvents() {
if (this.lastEvents == null) {
this.lastEvents = new CalendarEventService().getLastEvents(this, 5);
}
return null;
return this.lastEvents;
}

public Run getNextBuild() {
if (build != null) {
return build.getNextBuild();
}
return null;
}

public Run getBuild() {
return build;
public CalendarEvent getPreviousEvent() {
if (previousEvent == null && build != null) {
previousEvent = new CalendarEventService().getPreviousEvent(this);
}
return previousEvent;
}

public Job getJob() {
if (build != null) {
return build.getParent();
public CalendarEvent getNextEvent() {
if (nextEvent == null && build != null) {
nextEvent = new CalendarEventService().getNextEvent(this);
}
return null;
return nextEvent;
}

public Calendar getNextRun() {
return nextRun;
public CalendarEvent getNextScheduledEvent() {
if (nextScheduledEvent == null && build != null) {
nextScheduledEvent = new CalendarEventService().getNextScheduledEvent(this);
}
return nextScheduledEvent;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@
*/
package io.jenkins.plugins.view.calendar;

import hudson.model.AbstractProject;
import hudson.model.Job;
import hudson.model.Run;
import hudson.model.TopLevelItem;
import hudson.model.*;
import hudson.scheduler.CronTab;
import hudson.triggers.Trigger;
import hudson.util.RunList;
Expand Down Expand Up @@ -72,7 +69,7 @@ public List<CalendarEvent> getFutureEvents(final List<TopLevelItem> items, final
if (!(item instanceof AbstractProject)) {
continue;
}
final long durationInMillis = ((AbstractProject)item).getEstimatedDuration();
final long estimatedDuration = ((AbstractProject)item).getEstimatedDuration();
final List<Trigger> triggers = cronJobService.getCronTriggers(item);
for (final Trigger trigger: triggers) {
final List<CronTab> cronTabs = cronJobService.getCronTabs(trigger);
Expand All @@ -81,7 +78,7 @@ public List<CalendarEvent> getFutureEvents(final List<TopLevelItem> items, final
Calendar next = cronTab.ceil(timeInMillis);
while (next != null && next.compareTo(start) >= 0 && next.compareTo(end) < 0) {
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
final CalendarEvent event = new CalendarEvent(item, next, durationInMillis);
final CalendarEvent event = new CalendarEvent(item, next, estimatedDuration);
events.add(event);
timeInMillis = next.getTimeInMillis() + 1000 * 60;
next = cronTab.ceil(timeInMillis);
Expand Down Expand Up @@ -110,4 +107,48 @@ public List<CalendarEvent> getPastEvents(final List<TopLevelItem> items, final C
return events;
}

public List<CalendarEvent> getLastEvents(final CalendarEvent event, final int numberOfEvents) {
final List<CalendarEvent> lastEvents = new ArrayList<>();
final TopLevelItem item = event.getItem();
if (item instanceof Job) {
final List<Build> lastBuilds = ((Job) item).getLastBuildsOverThreshold(numberOfEvents, Result.ABORTED);
for (final Build lastBuild: lastBuilds) {
lastEvents.add(new CalendarEvent(item, lastBuild));
}
}
return lastEvents;
}

public CalendarEvent getPreviousEvent(final CalendarEvent event) {
if (event.getBuild() != null) {
final Run previousBuild = event.getBuild().getPreviousBuild();
if (previousBuild != null) {
return new CalendarEvent(event.getItem(), previousBuild);
}
}
return null;
}

public CalendarEvent getNextEvent(final CalendarEvent event) {
if (event.getBuild() != null) {
final Run nextBuild = event.getBuild().getNextBuild();
if (nextBuild != null) {
return new CalendarEvent(event.getItem(), nextBuild);
}
}
return null;
}

public CalendarEvent getNextScheduledEvent(final CalendarEvent event) {
final TopLevelItem item = event.getItem();
if (!(item instanceof AbstractProject)) {
return null;
}
final Calendar nextStart = cronJobService.getNextStart(item);
if (nextStart != null) {
final long estimatedDuration = ((AbstractProject)item).getEstimatedDuration();
return new CalendarEvent(item, nextStart, estimatedDuration);
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public List<Trigger> getCronTriggers(final TopLevelItem item) {
return triggers;
}

public Calendar getNextRun(final TopLevelItem item) {
public Calendar getNextStart(final TopLevelItem item) {
final Calendar now = GregorianCalendar.getInstance();
Calendar next = null;
final List<Trigger> triggers = getCronTriggers(item);
Expand Down
10 changes: 8 additions & 2 deletions src/main/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,15 @@ $(function() {
viewRender: function(view, element) {
window.location = hashParams.serialize({date: view.calendar.currentDate.format('YYYY-MM-DD'), view: view.type});
},
eventAfterAllRender: function(view) {
if (CalendarViewOptions.gotoEventId) {
popup.openForEventId(CalendarViewOptions.gotoEventId, view);
CalendarViewOptions.gotoEventId = null;
}
},
eventMouseover: function(event, jsEvent, view) {
var target = jsEvent.target || jsEvent.srcElement;
popup.show(event, view, $(target).closest('.fc-event')[0]);
var target = $(jsEvent.target || jsEvent.srcElement).closest('.fc-event')[0];
popup.openForEvent(event, view, target, false);
}
});
});
40 changes: 31 additions & 9 deletions src/main/js/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ function rightForPastBuild(event, view) {
.append($('<div></div>')
.append(event.job.icon)
.append(' ')
.append($('<a></a>').attr('href', event.job.url).text(event.job.name)))
.append($('<a></a>').attr('href', event.job.url).text(event.job.title)))
.append($('<div class="nextBuild"></div>')
.append(event.job.nextRun ? [CalendarViewOptions.popupText.nextBuild, dateLink(event.job.nextRun, view)] : ''));
.append(event.nextScheduledBuild ? [CalendarViewOptions.popupText.nextBuild, dateLink(event.nextScheduledBuild, view)] : ''));
}

function rightForFutureBuild(event, view) {
Expand Down Expand Up @@ -105,23 +105,29 @@ function bottom(event, view) {
}

function build(build, view) {
var $link = $('<a></a>').attr('href', build.url).text(build.name);
var $dateLink = dateLink(build.start, view);
var $link = $('<a></a>').attr('href', build.url).text(build.title);
var $dateLink = dateLink(build, view);
return [build.icon, ' ', $link, ' ', $dateLink];
}

function dateLink(date, view) {
var mom = moment(date);
function dateLink(build, view) {
var date = moment(build.start);
var dateFormat = CalendarViewOptions.formats[view.type].popupBuildTimeFormat;
var $dateLink = $('<a class="time" href="#"></a>');
$dateLink.click(function() {
view.calendar.gotoDate(mom);
closeAll();
if (view.intervalStart.isSameOrBefore(date) && view.intervalEnd.isAfter(date)) {
openForEventId(build.id, view);
} else {
CalendarViewOptions.gotoEventId = build.id;
view.calendar.gotoDate(date);
}
return false;
});
return $dateLink.append($('<time></time>').text(mom.format(dateFormat)));
return $dateLink.append($('<time></time>').text(date.format(dateFormat)));
}

export function show(event, view, target) {
export function openForEvent(event, view, target, manual) {
var $popup = popup(event, view, {
close: function() { popupInstance.hide(); }
});
Expand All @@ -132,9 +138,25 @@ export function show(event, view, target) {
animation: 'fade',
interactive: true,
theme: 'jenkins',
trigger: manual ? 'manual' : 'mouseenter focus',
size: 'large',
onHidden: function() {
popupInstance.destroy();
}
});
if (manual) {
popupInstance.show();
}
}

export function openForEventId(eventId, view) {
var event = view.calendar.clientEvents(function(e) { return e.id === eventId; })[0];
var target = $('.event-id-' + eventId)[0];
openForEvent(event, view, target, true);
}

export function closeAll() {
$('*[data-tippy]').each(function(i, el) {
el._tippy.destroy();
});
}
Loading

0 comments on commit 15d5461

Please sign in to comment.