Name
sn_sow_chg.SOWChgConflictCalendarSNC
Description
No description available
Script
var SOWChgConflictCalendarSNC = Class.create();
SOWChgConflictCalendarSNC.LOG_PROP = "sn_sow_chg.change_conflict_calendar.log";
SOWChgConflictCalendarSNC.CALENDAR_RELATED_CHANGE_LIMIT = "sn_sow_chg.change_conflict_calendar.related.limit";
SOWChgConflictCalendarSNC.I18N = {
"APPLIES_TO": gs.getMessage("Applies to"),
"BLACKOUT_WINDOWS": gs.getMessage("Blackout windows"),
"END_DATE": gs.getMessage("End date"),
"MAINTENANCE_WINDOWS": gs.getMessage("Maintenance windows"),
"START_DATE": gs.getMessage("Start date"),
"TIME_ZONE": gs.getMessage("Time zone"),
"DAY": gs.getMessage("Day"),
"WEEK": gs.getMessage("Week"),
"FOUR_WEEKS": gs.getMessage("{0} Weeks", [4]),
},
SOWChgConflictCalendarSNC.prototype = {
PLUGIN_CONFLICT_DETECTION: 'com.snc.change.collision',
initialize: function(tableName, sysId) {
this.tableName = tableName || "change_request";
this.sysId = sysId;
this.changeGR = new GlideRecordSecure(this.tableName);
this.validTable = this.changeGR.isValid();
this.validChange = this.validTable ? this.changeGR.get(this.sysId) : false;
this.validChangePlannedDates = !this.changeGR.start_date.nil() && !this.changeGR.end_date.nil();
this._ciDataCache = {};
this._log = new global.GSLog(SOWChgConflictCalendarSNC.LOG_PROP, this.type).setLog4J();
},
buildChangeConflictCalendar: function(dateRange) {
var changeConflictCalendar = {};
if (!this.validChange) {
changeConflictCalendar.error = {
message: gs.getMessage("Record not found, cannot display schedule")
};
return changeConflictCalendar;
}
changeConflictCalendar.timeProperties = {
firstDayWeek: new GlideDateTime(gs.beginningOfThisWeek()).getDayOfWeekLocalTime(),
locale: gs.getSession().getLanguage(),
timezone: gs.getSession().getTimeZoneName()
};
changeConflictCalendar.timelineViewSettings = this._defineTimelineViewProperties();
this.setRangeDates(dateRange);
changeConflictCalendar.change = this._changeToEvent(this.changeGR, "change", "rgb(var(--now-color_grouped--green-2, 147, 170, 125))");
changeConflictCalendar.changeState = this._defineConflictStatus();
changeConflictCalendar.flat = [];
changeConflictCalendar.flat.push(changeConflictCalendar.change);
changeConflictCalendar.timelineSections = this._defineTimelineSections(changeConflictCalendar);
// No need to process
if (!this.validChangePlannedDates || !this._hasAffectedCIs())
return changeConflictCalendar;
var changeConflictData = this._calculateConflictData();
changeConflictCalendar.related_change = this.processRelatedInfo(changeConflictData);
changeConflictCalendar.flat = changeConflictCalendar.flat.concat(changeConflictCalendar.related_change);
changeConflictCalendar.timelineSections = this._defineTimelineSections(changeConflictCalendar);
changeConflictCalendar.schedule = this.processScheduleInfo(changeConflictData);
changeConflictCalendar.schedule.maintenance.forEach(function(maintenanceSchedule) {
changeConflictCalendar.flat = changeConflictCalendar.flat.concat(maintenanceSchedule.windows);
}, this);
changeConflictCalendar.schedule.blackout.forEach(function(blackoutSchedule) {
changeConflictCalendar.flat = changeConflictCalendar.flat.concat(blackoutSchedule.windows);
}, this);
if (this._log.atLevel(global.GSLog.DEBUG))
this._log.debug("[getEvents] changeConflictCalendar.flat: " + JSON.stringify(changeConflictCalendar, 3,3,3));
return changeConflictCalendar;
},
_defineConflictStatus: function() {
var changeConflictState = "not_run";
switch (this.changeGR.getValue("conflict_status")) {
case "Conflict":
changeConflictState = "conflict";
break;
case "No Conflict":
changeConflictState = "no_conflict";
break;
case "Not Run":
changeConflictState = "not_run";
break;
default:
changeConflictState = "not_run";
break;
}
return changeConflictState;
},
_calculateConflictData: function() {
var changeConflictConfig = {
date_range: [this.rangeStart, this.rangeEnd],
dry_run: true,
collect_window_data: true,
allow_partially_overlapping_windows: true,
show_timing_info: false,
include_current_ci: this.relatedCi,
include_assigned_to: this.relatedAssigned
};
if (!this.maintenance)
changeConflictConfig.maintenance = ["maintenance"];
if (!this.blackout)
changeConflictConfig.blackout = ["blackout"];
if (!GlidePluginManager.isActive(this.PLUGIN_CONFLICT_DETECTION))
return {
maintenance: [],
blackout: [],
is_invalid_conflict_checker_si: true
};
var conflictChecker = new global.ChangeCheckConflicts(this.changeGR, changeConflictConfig);
if (!conflictChecker.getWindowData) {
this._log.error("ChangeCheckConflicts does not have \"getWindowData\" defined. Please ensure the latest version of ChangeCheckConflicts provided by ServiceNow is in use.");
return {
maintenance: [],
blackout: [],
is_invalid_conflict_checker_si: true
};
}
conflictChecker.check();
return conflictChecker.getWindowData();
},
_defineTimelineViewProperties: function() {
return {
"TIMELINE_DAY": {
"eventHeight": 40,
"xStep": 60,
"xSize": 24,
"xStart": 0,
"xUnitName": "minutes",
"titleWidth": 240,
"viewName": "TIMELINE_DAY",
"groupBy": "group",
"scrollDebounceTime": 100,
"noScrolling": false,
"eventMinWidthMS": 1000,
"animation": false,
"enableColumnNavigation": false,
"hideAgendaView": true,
"hideSidebarOnLoad": false,
"snapGranularity": 15,
"splitRow": 2,
"timeRowSpan": 60,
"numberOfDays": 1,
"viewLabel": SOWChgConflictCalendarSNC.I18N.DAY,
"isReadonly": true,
"rowHeightBottomPaddingInLines": 1,
"sectionHeaderHeight": 40,
"templates": {
"rowTitle": {
"type": "macroponent",
"value": "be243d9c0bd21110c85e8a8db777b28a"
},
"mainGrid": {
"type": "",
"value": ""
},
"timeScaleGrid": {
"type": "",
"value": ""
},
"sectionHeadTitle": {
"type": "",
"value": ""
},
"sectionHeadBody": {
"type": "",
"value": ""
},
"rowBody": {
"type": "",
"value": ""
},
"eventBody": {
"type": "",
"value": ""
},
"timestampBody": {
"type": "component",
"value": "sn-now-calendar-timestamp-view"
}
},
"trackConfig": {
"normal": {
"style": {},
"paddingTop": 1,
"paddingBottom": 1,
"eventHeight": 40
},
"priority": {
"style": {},
"paddingTop": 1,
"paddingBottom": 1,
"eventHeight": 40
}
}
},
"TIMELINE_WEEK": {
"eventHeight": 40,
"xStep": 1,
"xSize": 7,
"xStart": 0,
"xUnitName": "days",
"titleWidth": 240,
"viewName": "TIMELINE_WEEK",
"groupBy": "group",
"scrollDebounceTime": 100,
"noScrolling": false,
"eventMinWidthMS": 1000,
"animation": false,
"enableColumnNavigation": false,
"hideAgendaView": true,
"hideSidebarOnLoad": false,
"snapGranularity": 480,
"splitRow": 4,
"numberOfDays": 7,
"isReadonly": true,
"rowHeightBottomPaddingInLines": 1,
"sectionHeaderHeight": 40,
"viewLabel": SOWChgConflictCalendarSNC.I18N.WEEK,
"templates": {
"rowTitle": {
"type": "macroponent",
"value": "be243d9c0bd21110c85e8a8db777b28a"
},
"mainGrid": {
"type": "",
"value": ""
},
"timeScaleGrid": {
"type": "",
"value": ""
},
"sectionHeadTitle": {
"type": "",
"value": ""
},
"sectionHeadBody": {
"type": "",
"value": ""
},
"rowBody": {
"type": "",
"value": ""
},
"eventBody": {
"type": "",
"value": ""
},
"timestampBody": {
"type": "component",
"value": "sn-now-calendar-timestamp-view"
}
},
"trackConfig": {
"normal": {
"style": {},
"paddingTop": 1,
"paddingBottom": 1,
"eventHeight": 40
},
"priority": {
"style": {},
"paddingTop": 1,
"paddingBottom": 1,
"eventHeight": 40
}
},
}
};
},
_defineTimelineSections: function(changeConflictCalendar) {
var timelineSections = [
{
"id": "change",
"title": changeConflictCalendar.change._title,
"isCollapsed": false,
"showHeader": false,
"textColor": ""
},
{
"id": "blackout",
"title": SOWChgConflictCalendarSNC.I18N.BLACKOUT_WINDOWS,
"isCollapsed": false,
"showHeader": false,
"textColor": ""
},
{
"id": "maintenance",
"title": SOWChgConflictCalendarSNC.I18N.MAINTENANCE_WINDOWS,
"isCollapsed": false,
"showHeader": false,
"textColor": ""
},
{
"id": "conflict",
"title": "",
"isCollapsed": false,
"showHeader": true,
"textColor": "",
"children": [
{
"id": "config_item_section",
"title": "",
"isCollapsed": false,
"showHeader": true,
"textColor": "",
"children": [
]
},
{
"id": "resource_section",
"title": "",
"isCollapsed": false,
"showHeader": true,
"textColor": "",
"children": [
]
}
]
}
];
var resourceCount = 0;
var configCount = 0;
if (changeConflictCalendar.related_change)
changeConflictCalendar.related_change.forEach(function(changeObj) {
if (changeObj._group === "resource") {
resourceCount++;
timelineSections[3].children[1].children.push({
"id": changeObj.id,
"title": changeObj._title,
});
} else {
configCount++;
timelineSections[3].children[0].children.push({
"id": changeObj.id,
"title": changeObj._title,
});
}
});
timelineSections[3].children[0].title = gs.getMessage("Configuration item ({0})", "" + configCount);
if (!configCount)
timelineSections[3].children[0].isCollapsed = true;
timelineSections[3].children[1].title = gs.getMessage("Assigned to ({0})", "" + resourceCount);
if (!resourceCount)
timelineSections[3].children[1].isCollapsed = true;
timelineSections[3].title = gs.getMessage("Related changes ({0})", (resourceCount + configCount) + "");
if (!(resourceCount + configCount))
timelineSections[3].isCollapsed = true;
return timelineSections;
},
processRelatedInfo: function(windowData) {
var relatedChangeRequests = [];
var foundRelatedChgRequestsObj = {};
// Derived from Conflict Detection
var changeIdArr = Object.keys(windowData.scheduled);
if (this._log.atLevel(global.GSLog.DEBUG))
this._log.debug("[processRelatedInfo] changeIdArr: " + changeIdArr);
if (changeIdArr && changeIdArr.length) {
var changesGR = new GlideRecordSecure(this.tableName);
changesGR.addActiveQuery();
changesGR.addQuery("sys_id", changeIdArr);
this._addDateRangeQuery(changesGR, this.rangeStart, this.rangeEnd);
var relatedChangeLimit = parseInt(gs.getProperty(SOWChgConflictCalendarSNC.CALENDAR_RELATED_CHANGE_LIMIT, 200));
changesGR.setLimit(isNaN(relatedChangeLimit) ? 200 : relatedChangeLimit);
relatedChangeRequests = relatedChangeRequests.concat(this.getChangesInformation(changesGR, foundRelatedChgRequestsObj));
}
return relatedChangeRequests;
},
processScheduleInfo: function(windowData) {
return {
maintenance: this.getScheduleInformation(windowData.maintenance, "maintenance", "rgb(var(--now-color_grouped--blue-2, 120, 176, 204))"),
blackout: this.getScheduleInformation(windowData.blackout, "blackout", "rgb(var(--now-color_grouped--gray-5, 77, 80, 83))"),
warn_invalid_conflict_checker: windowData.is_invalid_conflict_checker_si
};
},
getChangesInformation: function(changesGR, foundRelatedChgRequestsObj) {
var changes = [];
if (changesGR) {
changesGR.query();
while (changesGR.next()) {
if (foundRelatedChgRequestsObj[changesGR.getUniqueValue()])
continue;
var grouping = "related";
if (!changesGR.assigned_to.nil() && changesGR.getValue("assigned_to") === this.changeGR.getValue("assigned_to"))
grouping = "resource";
else
grouping = "config_item";
var changeObj = this._changeToEvent(changesGR, grouping, "rgb(var(--now-color_grouped--green-yellow-2, 154, 173, 94))");
changes.push(changeObj);
foundRelatedChgRequestsObj[changesGR.getUniqueValue()] = true;
}
}
return changes;
},
getScheduleInformation: function(windowArr, group, bgColor) {
var schedules = {};
for (var configItemId in windowArr) {
var windowsByCI = windowArr[configItemId];
for (var i = 0; i < windowsByCI.length; i++) {
var window = windowsByCI[i];
var scheduleObj = schedules[window.scheduleId];
if (scheduleObj) {
this._addCiToRelated(scheduleObj.related_cis, configItemId, window);
continue;
}
scheduleObj = {
sys_id: window.scheduleId,
windows: [],
related_cis: []
};
this._addCiToRelated(scheduleObj.related_cis, configItemId, window);
var schedule = new GlideSchedule(window.scheduleId);
scheduleObj.title = schedule.getName();
scheduleObj.time_zone = schedule.getTimeZone();
var scheduleTimeMap = schedule.getTimeMap(this.rangeStart, this.rangeEnd, scheduleObj.time_zone);
while (scheduleTimeMap.hasNext()) {
var scheduleDateTimeSpan = scheduleTimeMap.next();
var startSDT = scheduleDateTimeSpan.getStart();
var endSDT = scheduleDateTimeSpan.getEnd();
if (!startSDT || !endSDT)
continue;
var startGDT = startSDT.getGlideDateTime();
var endGDT = endSDT.getGlideDateTime();
var scheduleName = schedule.getName();
scheduleName = global.JSUtil.nil(scheduleName.trim()) ? gs.getMessage("(empty)") : scheduleName;
var ariaStart = startGDT ? startGDT.getDisplayValue() : "";
var ariaEnd = endGDT ? endGDT.getDisplayValue() : "";
var scheduleWindowObj = {
id: window.scheduleId,
start: startGDT.getNumericValue(),
start_value: startGDT.getValue(),
end: endGDT.getNumericValue(),
end_value: endGDT.getValue(),
title: "",
_title: scheduleName,
description: "",
group: group,
bgColor: bgColor,
textColor: "",
borderColor: bgColor,
gradientColor1: "",
gradientColor2: "",
readOnly: true,
allowMove: "",
allowResize: "",
childEvents: "",
ariaLabel: this._getAriaLabel(group, ariaStart, ariaEnd),
order: "",
contextMenuOptions: "",
_table: group === "blackout" ? "cmn_schedule_blackout" : "cmn_schedule_maintenance",
_sys_id: window.scheduleId
};
scheduleWindowObj.popoverDetails = [
{
"label": SOWChgConflictCalendarSNC.I18N.TIME_ZONE,
"value": scheduleObj.time_zone
},
{
"label": SOWChgConflictCalendarSNC.I18N.APPLIES_TO,
"value": {
"type": "text-link",
"label": this._ciDataCache[configItemId].name,
"href": "javascript:void(0)",
"underlined": true,
"variant": "primary",
"opensWindow": false,
"_table": this._ciDataCache[configItemId].class_name.value,
"_sys_id": configItemId
}
},
{
"label": SOWChgConflictCalendarSNC.I18N.START_DATE,
"value": startGDT.getDisplayValue()
},
{
"label": SOWChgConflictCalendarSNC.I18N.END_DATE,
"value": endGDT.getDisplayValue()
}
];
scheduleObj.windows.push(scheduleWindowObj);
}
schedules[window.scheduleId] = scheduleObj;
}
}
var result = [];
for (var scheduleId in schedules)
result.push(schedules[scheduleId]);
if (this._log.atLevel(global.GSLog.DEBUG))
this._log.debug("[getScheduleInformation] result: " + JSON.stringify(result, 3,3,3));
return result;
},
_getAriaLabel: function (eventType, startDisplayValue, endDisplayValue) {
if (!eventType || !startDisplayValue || !endDisplayValue)
return "";
if (eventType === "blackout")
return gs.getMessage("Blackout scheduled from {0} to {1}", [startDisplayValue, endDisplayValue]);
if (eventType === "maintenance")
return gs.getMessage("Maintenance scheduled from {0} to {1}", [startDisplayValue, endDisplayValue]);
if (eventType === "change")
return gs.getMessage("Change scheduled from {0} to {1}", [startDisplayValue, endDisplayValue]);
if (eventType === "related")
return gs.getMessage("Related Change scheduled from {0} to {1}", [startDisplayValue, endDisplayValue]);
return "";
},
_addCiToRelated: function(arr, configItemId, window) {
// For maintenance/blackout schedules that apply to a Change Request the configItem value will be null.
if (!configItemId || "null" === configItemId)
return;
if (window && window.relatedCiId)
this._addCiToRelated(arr, window.relatedCiId);
else if (arr.every(function(ciData) {
return ciData.sys_id !== configItemId;
})) {
var ciData = this._ciDataCache[configItemId];
if (!ciData) {
ciData = {};
var configItemGR = new GlideRecord("cmdb_ci");
if (configItemGR.get(configItemId)) {
ciData.name = configItemGR.getDisplayValue("name");
ciData.class_name = {
"display": configItemGR.getDisplayValue("sys_class_name"),
"value": configItemGR.getRecordClassName()
};
}
this._ciDataCache[configItemId] = ciData;
}
arr.push({
sys_id: configItemId,
name: ciData.name,
ci_class: ciData.ci_class
});
}
},
getChangesWithSameAssignmentGroup: function(startDate, endDate, foundRelatedChgRequestsObj) {
if (!this.changeGR.getValue("assignment_group"))
return null;
var changesGR = new GlideRecordSecure(this.tableName);
changesGR.addActiveQuery();
var ignoreChangeSysIds = Object.keys(foundRelatedChgRequestsObj);
ignoreChangeSysIds.push(this.changeGR.getUniqueValue());
changesGR.addQuery("sys_id", "!=", ignoreChangeSysIds);
changesGR.addQuery("assignment_group", this.changeGR.getValue("assignment_group"));
this._addDateRangeQuery(changesGR, startDate, endDate);
return changesGR;
},
includeMaintenance: function(include) {
this.maintenance = this._getBooleanValue(include);
},
includeBlackout: function(include) {
this.blackout = this._getBooleanValue(include);
},
includeRelated: function(include) {
this.related = this._getBooleanValue(include);
},
includeRelatedByCI: function(include) {
this.relatedCi = this._getBooleanValue(include);
},
includeRelatedByAssignedTo: function(include) {
this.relatedAssigned = this._getBooleanValue(include);
},
includeRelatedByAssignmentGroup: function(include) {
this.relatedGroup = this._getBooleanValue(include);
},
setRangeDates: function(dateRange) {
dateRange = dateRange.split("_");
var startGDT = new GlideDateTime();
if (dateRange[0] && dateRange[0] !== "null") {
// Because setNumericValue is not exposed in Scoped apps
startGDT.subtract(startGDT.getNumericValue());
startGDT.add(dateRange[0]);
}
this.rangeStart = this._isDateTimeValid(startGDT) ? startGDT : new GlideDateTime();
var endGDT = null;
if (dateRange[1] && dateRange[1] !== "null") {
endGDT = new GlideDateTime();
endGDT.subtract(endGDT.getNumericValue());
endGDT.add(dateRange[1]);
}
if (this._isDateTimeValid(endGDT))
this.rangeEnd = endGDT;
else {
endGDT = new GlideDateTime(this.rangeStart);
endGDT.addDaysUTC(1);
this.rangeEnd = endGDT;
}
if (this._log.atLevel(global.GSLog.DEBUG))
this._log.debug("[setRangeDates] Date range set from " + this.rangeStart + " to " + this.rangeEnd);
},
_changeToEvent: function(changeGR, group, bgColor) {
var popoverDetails = [
this._getFieldLabelValue(changeGR, "assigned_to"),
this._getFieldLabelValue(changeGR, "assignment_group"),
this._getFieldLabelValue(changeGR, "short_description"),
this._getFieldLabelValue(changeGR, "start_date"),
this._getFieldLabelValue(changeGR, "end_date")
];
var startGDT;
var endGDT;
if (this.validChangePlannedDates) {
startGDT = new GlideDateTime(changeGR.getValue("start_date"));
endGDT = new GlideDateTime(changeGR.getValue("end_date"));
}
var ariaStart = startGDT ? startGDT.getDisplayValue() : "";
var ariaEnd = endGDT ? endGDT.getDisplayValue() : "";
return {
id: changeGR.getUniqueValue(),
start: startGDT ? startGDT.getNumericValue() : 0,
start_value: startGDT ? changeGR.getValue("start_date") : "",
end: endGDT ? endGDT.getNumericValue() : 0,
end_value: endGDT ? changeGR.getValue("end_date") : "",
title: "",
description: changeGR.getDisplayValue("short_description"),
group: group === "change" ? group : changeGR.getUniqueValue(),
_group: group,
bgColor: bgColor,
textColor: "",
borderColor: bgColor,
gradientColor1: "",
gradientColor2: "",
readOnly: true,
allowMove: "",
allowResize: "",
childEvents: "",
ariaLabel: this._getAriaLabel(group, ariaStart, ariaEnd),
order: "",
contextMenuOptions: "",
popoverDetails: popoverDetails,
_assigned_to: changeGR.getDisplayValue("assigned_to"),
_cmdb_ci: changeGR.getDisplayValue("cmdb_ci"),
_title: changeGR.getDisplayValue(),
_table: changeGR.getRecordClassName(),
_sys_id: changeGR.getUniqueValue()
};
},
_getFieldLabelValue: function(gr, field) {
return {
"label": gr[field].getLabel(),
"value": gr.getDisplayValue(field)
};
},
_toJSON: function(gr, fields) {
var values = {};
for (var i = 0; i < fields.length; i++) {
values[fields[i]] = {
value: gr.getValue(fields[i]),
display_value: gr.getDisplayValue(fields[i])
};
if ((fields[i] === "start_date" || fields[i] === "end_date") && values[fields[i]].value)
values[fields[i]].millis = new GlideDateTime(values[fields[i]].value).getNumericValue();
}
return values;
},
_isDateTimeValid: function(glideDateTime) {
return glideDateTime && typeof glideDateTime.isValid === "function" && glideDateTime.isValid();
},
_addDateRangeQuery: function(changesGR, startDate, endDate) {
if (startDate)
changesGR.addQuery("end_date", ">", startDate);
if (endDate)
changesGR.addQuery("start_date", "<", endDate);
},
_getBooleanValue: function(value) {
if (typeof value === "boolean")
return value;
else
return value === "true";
},
_hasAffectedCIs: function() {
var gr = new GlideRecord("task_ci");
gr.addQuery("task", this.changeGR.getUniqueValue());
gr.addNotNullQuery("ci_item");
gr.setLimit(1);
gr.query();
return gr.hasNext();
},
type: "SOWChgConflictCalendarSNC"
};
Sys ID
5c32233a0b011110c85e8a8db777b2eb