Skip to content

Commit ffa9e63

Browse files
authored
Merge branch 'ServiceNowDevProgram:main' into main
2 parents 303e53f + 45fd932 commit ffa9e63

File tree

6 files changed

+179
-0
lines changed

6 files changed

+179
-0
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
**UI Action**:
2+
Publish a Retired Knowledge Article again after it is already retired.
3+
4+
**How it works:**
5+
1. The code finds existing articles in the Knowledge base that share the same Article ID but are not the current article and are 'retired'.
6+
2. It updates the workflow state of these found articles to 'outdated'.
7+
3. For the current article, it sets the workflow_state to 'pubblished', records the current date and updates the 'published' field with the date.
8+
4. It also removes the pre-existing 'retired' date from the 'retired' field.
9+
5. It redirects the user to the updated current record.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
var kbArticle = new GlideRecord('kb_knowledge');
2+
kbArticle.setWorkflow(false);
3+
kbArticle.addQuery('article_id', current.article_id);
4+
kbArticle.addQuery('sys_id', "!=", current.sys_id); //articles that are not the current one
5+
kbArticle.addQuery('workflow_state', 'retired');
6+
kbArticle.query();
7+
while (kbArticle.next()) {
8+
kbArticle.workflow_state = 'outdated'; //setting the articles as outdated
9+
kbArticle.update();
10+
}
11+
current.workflow_state = 'published'; //publishing retired kb article again
12+
current.published = new GlideDate();
13+
current.retired = ""; //clearing retired field value
14+
current.update();
15+
action.setRedirectURL(current);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Dynamic Record Archiving Logic
2+
3+
## Overview
4+
This snippet provides a reusable logic to archive or flag records in ServiceNow that are older than a specified threshold. It helps maintain data hygiene, improve performance, and support compliance with data retention policies.
5+
6+
You can choose between two modes:
7+
- **Flag Mode**: Marks records as archived using a custom field (`u_archived`).
8+
- **Move Mode**: Moves records to a corresponding archive table (e.g., `incident_archive`) and deletes the original.
9+
10+
---
11+
12+
## Use Case
13+
- Archive incidents older than 1 year.
14+
- Clean up old tasks, requests, or custom records.
15+
- Automate data lifecycle management via Scheduled Jobs.
16+
17+
---
18+
19+
## Parameters
20+
| Parameter | Description |
21+
|---------------------|-----------------------------------------------------------------------------|
22+
| `tableName` | Name of the table to archive (e.g., `incident`) |
23+
| `dateField` | Date field to filter by (e.g., `opened_at`, `created_on`) |
24+
| `archiveThresholdDays` | Number of days before today to consider for archiving (e.g., `365`) |
25+
| `archiveMode` | `'flag'` to mark records, `'move'` to transfer to archive table |
26+
27+
---
28+
29+
## Example Usage
30+
```javascript
31+
var archiver = new RecordArchiver('incident', 'opened_at', 365, 'flag');
32+
archiver.archiveRecords();
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
var RecordArchiver = Class.create();
2+
RecordArchiver.prototype = {
3+
initialize: function(tableName, dateField, archiveThresholdDays, archiveMode) {
4+
this.tableName = tableName;
5+
this.dateField = dateField;
6+
this.archiveThresholdDays = archiveThresholdDays;
7+
this.archiveMode = archiveMode || 'flag'; // 'flag' or 'move'
8+
},
9+
10+
archiveRecords: function() {
11+
var thresholdDate = new GlideDateTime();
12+
thresholdDate.addDaysUTC(-this.archiveThresholdDays);
13+
14+
var gr = new GlideRecord(this.tableName);
15+
gr.addQuery(this.dateField, '<=', thresholdDate);
16+
gr.query();
17+
18+
var count = 0;
19+
while (gr.next()) {
20+
if (this.archiveMode === 'flag') {
21+
gr.setValue('u_archived', true); // Assumes a custom field 'u_archived'
22+
gr.update();
23+
} else if (this.archiveMode === 'move') {
24+
this._moveToArchiveTable(gr);
25+
}
26+
count++;
27+
}
28+
29+
gs.info('Archived ' + count + ' records from ' + this.tableName);
30+
},
31+
32+
_moveToArchiveTable: function(record) {
33+
var archiveGr = new GlideRecord(this.tableName + '_archive');
34+
archiveGr.initialize();
35+
36+
var fields = record.getFields();
37+
for (var i = 0; i < fields.size(); i++) {
38+
var fieldName = fields.get(i).getName();
39+
if (archiveGr.isValidField(fieldName)) {
40+
archiveGr.setValue(fieldName, record.getValue(fieldName));
41+
}
42+
}
43+
44+
archiveGr.insert();
45+
record.deleteRecord();
46+
},
47+
48+
type: 'RecordArchiver'
49+
};
50+
51+
// Example usage:
52+
//var archiver = new RecordArchiver('incident', 'opened_at', 365, 'flag'); // Archive incidents older than 1 year
53+
//archiver.archiveRecords();
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Recursive GlideRecord Fetcher
2+
## Overview
3+
This snippet provides a reusable logic to recursively fetch child records from a parent record in ServiceNow. It is useful for traversing hierarchical relationships such as tasks, categories, CMDB CI relationships, or any table with a parent-child structure.
4+
5+
The logic prevents infinite loops by tracking visited records and supports nesting of children for structured output.
6+
7+
---
8+
9+
## Use Case
10+
- Fetch all subtasks under a parent task.
11+
- Traverse CMDB CI relationships recursively.
12+
- Build hierarchical views of organizational units or categories.
13+
14+
---
15+
16+
## Parameters
17+
| Parameter | Description |
18+
|------------------|-----------------------------------------------------------------------------|
19+
| `tableName` | Name of the table to query (e.g., `task`, `cmdb_ci`, `custom_table`) |
20+
| `parentField` | Field that links to the parent record (e.g., `parent`, `parent_id`) |
21+
| `parentSysId` | Sys ID of the root parent record to start traversal |
22+
23+
---
24+
25+
## Example Usage
26+
```javascript
27+
var fetcher = new RecursiveFetcher('task', 'parent');
28+
var hierarchy = fetcher.fetchChildren('abc123sysid'); // Replace with actual parent sys_id
29+
gs.info(JSON.stringify(hierarchy));
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
var RecursiveFetcher = Class.create();
2+
RecursiveFetcher.prototype = {
3+
initialize: function(tableName, parentField) {
4+
this.tableName = tableName;
5+
this.parentField = parentField;
6+
this.visited = [];
7+
},
8+
9+
fetchChildren: function(parentSysId) {
10+
if (this.visited.indexOf(parentSysId) !== -1) {
11+
// Avoid infinite loops
12+
return [];
13+
}
14+
15+
this.visited.push(parentSysId);
16+
var children = [];
17+
18+
var gr = new GlideRecord(this.tableName);
19+
gr.addQuery(this.parentField, parentSysId);
20+
gr.query();
21+
22+
while (gr.next()) {
23+
var child = {
24+
sys_id: gr.getValue('sys_id'),
25+
name: gr.getDisplayValue('name') || gr.getDisplayValue('short_description'),
26+
children: this.fetchChildren(gr.getValue('sys_id')) // Recursive call
27+
};
28+
children.push(child);
29+
}
30+
31+
return children;
32+
},
33+
34+
type: 'RecursiveFetcher'
35+
};
36+
37+
// Example usage:
38+
//var fetcher = new RecursiveFetcher('task', 'parent');
39+
//var hierarchy = fetcher.fetchChildren('abc123sysid'); // Replace with actual parent sys_id
40+
//gs.info(JSON.stringify(hierarchy));
41+
``

0 commit comments

Comments
 (0)