Skip to content

Commit 16b8db2

Browse files
authored
Merge branch 'main' into Auto-Generate-KB-Article-From-Resolved-Incident
2 parents 10aa401 + e3a3a32 commit 16b8db2

File tree

263 files changed

+9805
-493
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

263 files changed

+9805
-493
lines changed

.github/pull_request_template.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
# PR Description:
2-
2+
replace this with your description
33

44
# Pull Request Checklist
55

66
## Overview
7+
- [x] Put an x inside of the square brackets to check each item.
78
- [ ] I have read and understood the [CONTRIBUTING.md](CONTRIBUTING.md) guidelines
8-
- [ ] My pull request has a descriptive title that accurately reflects the changes
9+
- [ ] My pull request has a descriptive title that accurately reflects the changes and the description has been filled in above.
910
- [ ] I've included only files relevant to the changes described in the PR title and description
1011
- [ ] I've created a new branch in my forked repository for this contribution
1112

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Unassign stale PR assignees
2+
on:
3+
schedule:
4+
- cron: "*/15 * * * *"
5+
workflow_dispatch:
6+
inputs:
7+
enabled: { type: boolean, default: true }
8+
max_age_minutes: { type: number, default: 60 }
9+
dry_run: { type: boolean, default: false }
10+
11+
permissions:
12+
pull-requests: write
13+
issues: write
14+
contents: read
15+
16+
jobs:
17+
call:
18+
uses: ServiceNowDevProgram/Hacktoberfest/.github/workflows/unassign-stale.yml@main
19+
with:
20+
enabled: ${{ inputs.enabled }}
21+
max_age_minutes: ${{ inputs.max_age_minutes }}
22+
dry_run: ${{ inputs.dry_run }}
23+
secrets: inherit

CONTRIBUTING.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,25 @@ If you plan to submit another pull request while your original is still pending,
3131
- **Descriptive Pull Request Titles**: Your pull request must have explicit and descriptive titles that accurately represent the changes made.
3232
- **Scope Adherence**: Changes that fall outside the described scope will result in the entire pull request being rejected.
3333
- **Quality Over Quantity**: Low-effort or spam pull requests will be marked accordingly.
34-
- **Expanded Snippets**: Code snippets reused from the [ServiceNow Documentation](https://docs.servicenow.com/) or [API References](https://developer.servicenow.com/dev.do#!/reference/) are acceptable only if they are expanded in a meaningful way (e.g., with additional context, documentation, or variations). Remember: *QUANTITY IS FUN, QUALITY IS KEY.*
34+
- **Expanded Snippets**: Code snippets reused from the [ServiceNow Documentation](https://docs.servicenow.com/) or [API References](https://developer.servicenow.com/dev.do#!/reference/) are acceptable only if they are expanded in a meaningful way (e.g., with additional context, documentation, or variations). Remember: *"QUANTITY IS FUN, QUALITY IS KEY."*
3535
- **Relevance**: Code should be relevant to ServiceNow Developers.
3636
- **ES2021 Compatibility**: While ES2021 is allowed, we encourage you to disclose if your code is using ES2021 features, as not everyone may be working with ES2021-enabled applications.
3737

38+
## Core Documentation File Changes
39+
40+
**IMPORTANT**: For changes to core documentation files (README.md, CONTRIBUTING.md, LICENSE, etc.), contributors must:
41+
42+
1. **Submit an Issue First**: Before making any changes to core documentation files, create an issue describing:
43+
- What you intend to edit
44+
- Why the change is needed
45+
- Your proposed approach
46+
47+
2. **Get Assignment**: Wait to be assigned to the issue by a maintainer before submitting a PR.
48+
49+
3. **Reference the Issue**: Include the issue number in your PR title and description.
50+
51+
This process helps prevent merge conflicts when multiple contributors want to update the same documentation files and ensures all changes align with the project's direction.
52+
3853
## Repository Structure
3954

4055
**IMPORTANT**: The repository has been reorganized into major categories. All new contributions MUST follow this structure for PR approval.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
function onChange(control, oldValue, newValue, isLoading) {
2+
if (isLoading || newValue == '') {
3+
return;
4+
}
5+
6+
var faxRegex = /^\d{10}$/; //allow only 10 digit in fax number field
7+
if (!faxRegex.test(newValue)) {
8+
g_form.addErrorMessage('Please enter a valid 10-digit fax number');
9+
g_form.clearValue('fax_num');
10+
}
11+
12+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
This script ensures that the fax number entered consists of exactly 10 digits.
2+
Any input that doesn’t meet this requirement — such as numbers with fewer or more digits, letters, or special characters — will be automatically rejected to maintain proper validation and data consistency.
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Auto Save Draft Feature for Catalog Items
2+
3+
This snippet provides automatic draft saving functionality for ServiceNow Catalog Items, helping prevent data loss by automatically saving form data at regular intervals.
4+
5+
## Overview
6+
7+
The feature includes two implementations:
8+
1. Basic Implementation (`basic_implementation.js`)
9+
2. Advanced Implementation (`advanced_implementation.js`)
10+
11+
## Basic Implementation
12+
13+
### Features
14+
- Auto-saves form data every minute
15+
- Stores single draft in sessionStorage
16+
- Provides draft restoration on form load
17+
- Basic error handling and user feedback
18+
19+
### Usage
20+
```javascript
21+
// Apply in Catalog Client Script
22+
// Select "onLoad" for "Client script runs"
23+
// Copy content from basic_implementation.js
24+
```
25+
26+
## Advanced Implementation
27+
28+
### Enhanced Features
29+
- Multiple draft support (keeps last 3 drafts)
30+
- Advanced draft management
31+
- Draft selection dialog
32+
- Detailed metadata tracking
33+
- Improved error handling
34+
- User-friendly notifications
35+
36+
### Usage
37+
```javascript
38+
// Apply in Catalog Client Script
39+
// Select "onLoad" for "Client script runs"
40+
// Copy content from advanced_implementation.js
41+
```
42+
43+
## Technical Details
44+
45+
### Dependencies
46+
- ServiceNow Platform UI Framework
47+
- GlideForm API
48+
- GlideModal (advanced implementation only)
49+
50+
### Browser Support
51+
- Modern browsers with sessionStorage support
52+
- ES5+ compatible
53+
54+
### Security Considerations
55+
- Uses browser's sessionStorage (cleared on session end)
56+
- No sensitive data transmission
57+
- Instance-specific storage
58+
59+
## Implementation Guide
60+
61+
1. Create a new Catalog Client Script:
62+
- Table: Catalog Client Script [catalog_script_client]
63+
- Type: onLoad
64+
- Active: true
65+
66+
2. Choose implementation:
67+
- For basic needs: Copy `basic_implementation.js`
68+
- For advanced features: Copy `advanced_implementation.js`
69+
70+
3. Apply to desired Catalog Items:
71+
- Select applicable Catalog Items
72+
- Test in dev environment first
73+
74+
## Best Practices
75+
76+
1. Testing:
77+
- Test with various form states
78+
- Verify draft restoration
79+
- Check browser storage limits
80+
81+
2. Performance:
82+
- Default 60-second interval is recommended
83+
- Adjust based on form complexity
84+
- Monitor browser memory usage
85+
86+
3. User Experience:
87+
- Clear feedback messages
88+
- Confirmation dialogs
89+
- Error notifications
90+
91+
## Limitations
92+
93+
- Browser session dependent
94+
- Storage size limits
95+
- Form field compatibility varies
96+
97+
## Troubleshooting
98+
99+
Common issues and solutions:
100+
1. Draft not saving
101+
- Check browser console for errors
102+
- Verify sessionStorage availability
103+
- Check form modification detection
104+
105+
2. Restoration fails
106+
- Validate stored data format
107+
- Check browser storage permissions
108+
- Verify form field compatibility
109+
110+
## Version Information
111+
112+
- Compatible with ServiceNow: Rome and later
113+
- Browser Requirements: Modern browsers with ES5+ support
114+
- Last Updated: October 2025
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/**
2+
* Advanced Auto-save Draft Implementation with Enhanced Features
3+
* This version adds multi-draft support and advanced error handling
4+
*/
5+
6+
function onLoad() {
7+
var autosaveInterval = 60000; // 1 minute
8+
var maxDrafts = 3; // Maximum number of drafts to keep
9+
10+
// Initialize draft manager
11+
initializeDraftManager();
12+
13+
// Try to restore previous draft
14+
restoreLastDraft();
15+
16+
// Set up auto-save interval
17+
setInterval(function() {
18+
if (g_form.isModified()) {
19+
saveAdvancedDraft();
20+
}
21+
}, autosaveInterval);
22+
}
23+
24+
function initializeDraftManager() {
25+
window.draftManager = {
26+
maxDrafts: 3,
27+
draftPrefix: 'catalogDraft_' + g_form.getUniqueValue() + '_',
28+
29+
getAllDrafts: function() {
30+
var drafts = [];
31+
for (var i = 0; i < sessionStorage.length; i++) {
32+
var key = sessionStorage.key(i);
33+
if (key.startsWith(this.draftPrefix)) {
34+
drafts.push({
35+
key: key,
36+
data: JSON.parse(sessionStorage.getItem(key))
37+
});
38+
}
39+
}
40+
return drafts.sort((a, b) => b.data.timestamp - a.data.timestamp);
41+
},
42+
43+
cleanup: function() {
44+
var drafts = this.getAllDrafts();
45+
if (drafts.length > this.maxDrafts) {
46+
drafts.slice(this.maxDrafts).forEach(function(draft) {
47+
sessionStorage.removeItem(draft.key);
48+
});
49+
}
50+
}
51+
};
52+
}
53+
54+
function saveAdvancedDraft() {
55+
try {
56+
var draftData = {};
57+
g_form.serialize(draftData);
58+
59+
// Add metadata
60+
var draftKey = window.draftManager.draftPrefix + new Date().getTime();
61+
var draftInfo = {
62+
timestamp: new Date().getTime(),
63+
data: draftData,
64+
user: g_user.userName,
65+
catalog_item: g_form.getTableName(),
66+
fields_modified: g_form.getModifiedFields()
67+
};
68+
69+
sessionStorage.setItem(draftKey, JSON.stringify(draftInfo));
70+
window.draftManager.cleanup();
71+
72+
// Show success message with draft count
73+
var remainingDrafts = window.draftManager.getAllDrafts().length;
74+
g_form.addInfoMessage('Draft saved. You have ' + remainingDrafts + ' saved draft(s).');
75+
76+
} catch (e) {
77+
console.error('Error saving draft: ' + e);
78+
g_form.addErrorMessage('Failed to save draft: ' + e.message);
79+
}
80+
}
81+
82+
function restoreLastDraft() {
83+
try {
84+
var drafts = window.draftManager.getAllDrafts();
85+
86+
if (drafts.length > 0) {
87+
// If multiple drafts exist, show selection dialog
88+
if (drafts.length > 1) {
89+
showDraftSelectionDialog(drafts);
90+
} else {
91+
promptToRestoreDraft(drafts[0].data);
92+
}
93+
}
94+
} catch (e) {
95+
console.error('Error restoring draft: ' + e);
96+
g_form.addErrorMessage('Failed to restore draft: ' + e.message);
97+
}
98+
}
99+
100+
function showDraftSelectionDialog(drafts) {
101+
var dialog = new GlideModal('select_draft_dialog');
102+
dialog.setTitle('Available Drafts');
103+
104+
var html = '<div class="draft-list">';
105+
drafts.forEach(function(draft, index) {
106+
var date = new Date(draft.data.timestamp).toLocaleString();
107+
html += '<div class="draft-item" onclick="selectDraft(' + index + ')">';
108+
html += '<strong>Draft ' + (index + 1) + '</strong> - ' + date;
109+
html += '<br>Modified fields: ' + draft.data.fields_modified.join(', ');
110+
html += '</div>';
111+
});
112+
html += '</div>';
113+
114+
dialog.renderWithContent(html);
115+
}
116+
117+
function promptToRestoreDraft(draftInfo) {
118+
var timestamp = new Date(draftInfo.timestamp);
119+
if (confirm('A draft from ' + timestamp.toLocaleString() + ' was found. Would you like to restore it?')) {
120+
Object.keys(draftInfo.data).forEach(function(field) {
121+
g_form.setValue(field, draftInfo.data[field]);
122+
});
123+
g_form.addInfoMessage('Draft restored from ' + timestamp.toLocaleString());
124+
}
125+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* Basic Auto-save Draft Implementation
3+
* This version provides core functionality for auto-saving catalog item form data
4+
*/
5+
6+
function onLoad() {
7+
var autosaveInterval = 60000; // 1 minute
8+
9+
// Try to restore previous draft
10+
restoreLastDraft();
11+
12+
// Set up auto-save interval
13+
setInterval(function() {
14+
if (g_form.isModified()) {
15+
saveDraft();
16+
}
17+
}, autosaveInterval);
18+
}
19+
20+
function saveDraft() {
21+
try {
22+
var draftData = {};
23+
g_form.serialize(draftData);
24+
25+
var draftKey = 'catalogDraft_' + g_form.getUniqueValue();
26+
sessionStorage.setItem(draftKey, JSON.stringify({
27+
timestamp: new Date().getTime(),
28+
data: draftData
29+
}));
30+
31+
g_form.addInfoMessage('Draft saved automatically');
32+
} catch (e) {
33+
console.error('Error saving draft: ' + e);
34+
}
35+
}
36+
37+
function restoreLastDraft() {
38+
try {
39+
var draftKey = 'catalogDraft_' + g_form.getUniqueValue();
40+
var savedDraft = sessionStorage.getItem(draftKey);
41+
42+
if (savedDraft) {
43+
var draftData = JSON.parse(savedDraft);
44+
var timestamp = new Date(draftData.timestamp);
45+
46+
if (confirm('A draft from ' + timestamp.toLocaleString() + ' was found. Would you like to restore it?')) {
47+
Object.keys(draftData.data).forEach(function(field) {
48+
g_form.setValue(field, draftData.data[field]);
49+
});
50+
g_form.addInfoMessage('Draft restored from ' + timestamp.toLocaleString());
51+
} else {
52+
sessionStorage.removeItem(draftKey);
53+
}
54+
}
55+
} catch (e) {
56+
console.error('Error restoring draft: ' + e);
57+
}
58+
}

0 commit comments

Comments
 (0)