Skip to content

Commit da2ad6c

Browse files
authored
Merge branch 'main' into detect-oldvalue-newvalue-in-cs
2 parents 66f92b7 + d4b8154 commit da2ad6c

File tree

86 files changed

+1999
-11
lines changed

Some content is hidden

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

86 files changed

+1999
-11
lines changed

.github/pull_request_template.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# PR Description:
2+
3+
14
# Pull Request Checklist
25

36
## Overview

.github/scripts/validate-structure.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,34 @@ function validateFilePath(filePath) {
4949
const normalized = filePath.replace(/\\/g, '/');
5050
const segments = normalized.split('/');
5151

52+
// Check for invalid characters that break local file systems
53+
for (let i = 0; i < segments.length; i++) {
54+
const segment = segments[i];
55+
56+
// Check for trailing periods (invalid on Windows)
57+
if (segment.endsWith('.')) {
58+
return `Invalid folder/file name '${segment}' in path '${normalized}': Names cannot end with a period (.) as this breaks local file system sync on Windows.`;
59+
}
60+
61+
// Check for trailing spaces (invalid on Windows)
62+
if (segment.endsWith(' ')) {
63+
return `Invalid folder/file name '${segment}' in path '${normalized}': Names cannot end with a space as this breaks local file system sync on Windows.`;
64+
}
65+
66+
// Check for reserved Windows names
67+
const reservedNames = ['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9'];
68+
const nameWithoutExt = segment.split('.')[0].toUpperCase();
69+
if (reservedNames.includes(nameWithoutExt)) {
70+
return `Invalid folder/file name '${segment}' in path '${normalized}': '${nameWithoutExt}' is a reserved name on Windows and will break local file system sync.`;
71+
}
72+
73+
// Check for invalid characters (Windows and general file system restrictions)
74+
const invalidChars = /[<>:"|?*\x00-\x1F]/;
75+
if (invalidChars.test(segment)) {
76+
return `Invalid folder/file name '${segment}' in path '${normalized}': Contains characters that are invalid on Windows file systems (< > : " | ? * or control characters).`;
77+
}
78+
}
79+
5280
if (!allowedCategories.has(segments[0])) {
5381
return null;
5482
}

.github/workflows/validate-structure.yml

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ jobs:
6565
cat "$tmp_error" >&2
6666
6767
if grep -q 'Folder structure violations found' "$tmp_output" "$tmp_error"; then
68+
# Save validation output for use in PR comment
69+
cat "$tmp_output" "$tmp_error" > "$RUNNER_TEMP/validation_output.txt"
6870
echo "status=failed" >> "$GITHUB_OUTPUT"
6971
exit 0
7072
fi
@@ -87,11 +89,37 @@ jobs:
8789
const owner = context.repo.owner;
8890
const repo = context.repo.repo;
8991
92+
const fs = require('fs');
93+
const output = fs.readFileSync(process.env.RUNNER_TEMP + '/validation_output.txt', 'utf8');
94+
95+
let commentBody = `Thank you for your contribution. However, it doesn't comply with our contributing guidelines.\n\n`;
96+
97+
// Check if the error is about invalid file/folder names
98+
if (output.includes('Names cannot end with a period') ||
99+
output.includes('Names cannot end with a space') ||
100+
output.includes('is a reserved name on Windows') ||
101+
output.includes('Contains characters that are invalid')) {
102+
commentBody += `**❌ Invalid File/Folder Names Detected**\n\n`;
103+
commentBody += `Your contribution contains file or folder names that will break when syncing to local file systems (especially Windows):\n\n`;
104+
commentBody += `\`\`\`\n${output}\n\`\`\`\n\n`;
105+
commentBody += `**Common issues:**\n`;
106+
commentBody += `- Folder/file names ending with a period (.) - not allowed on Windows\n`;
107+
commentBody += `- Folder/file names ending with spaces - not allowed on Windows\n`;
108+
commentBody += `- Reserved names like CON, PRN, AUX, NUL, COM1-9, LPT1-9 - not allowed on Windows\n`;
109+
commentBody += `- Invalid characters: < > : " | ? * or control characters\n\n`;
110+
commentBody += `Please rename these files/folders to be compatible with all operating systems.\n\n`;
111+
} else {
112+
commentBody += `As a reminder, the general requirements (as outlined in the [CONTRIBUTING.md file](https://github.com/ServiceNowDevProgram/code-snippets/blob/main/CONTRIBUTING.md)) are the following: follow the folder+subfolder guidelines and include a README.md file explaining what the code snippet does.\n\n`;
113+
commentBody += `**Validation errors:**\n\`\`\`\n${output}\n\`\`\`\n\n`;
114+
}
115+
116+
commentBody += `Review your contribution against the guidelines and make the necessary adjustments. Closing this for now. Once you make additional changes, feel free to re-open this Pull Request or create a new one.`;
117+
90118
await github.rest.issues.createComment({
91119
owner,
92120
repo,
93121
issue_number: pullNumber,
94-
body: `Thank you for your contribution. However, it doesn't comply with our contributing guidelines. As a reminder, the general requirements (as outlined in the [CONTRIBUTING.md file](https://github.com/ServiceNowDevProgram/code-snippets/blob/main/CONTRIBUTING.md)) are the following: follow the folder+subfolder guidelines and include a README.md file explaining what the code snippet does. Review your contribution against the guidelines and make the necessary adjustments. Closing this for now. Once you make additional changes, feel free to re-open this Pull Request or create a new one.`.trim()
122+
body: commentBody.trim()
95123
});
96124
97125
await github.rest.pulls.update({
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
In ServiceNow, Open catalog client Scripts [catalog_script_client] and paste the code snippet of [spModalSweetAlerts.js] file.
2+
3+
Setup:
4+
1. A catalog item having variable name Rewards[rewards] of type 'Select Box'(include none as true) and 2 choices(Yes and No)
5+
2. A Single line type field named 'Reward Selected' [reward_selected] which will hold the value selected by user from the spModal popup.
6+
3. The onLoad catalog client script setup as below:
7+
4. Type: onChange
8+
5. Variable: rewards (as per step 1)
9+
6. Script: [[spModalSweetAlerts.js]]
10+
11+
12+
13+
Screenshots:
14+
15+
16+
<img width="1338" height="268" alt="image" src="https://github.com/user-attachments/assets/f7f22b83-7e0e-47bb-bbed-2a8f38783a4d" />
17+
18+
19+
Rewards selected as 'Yes'
20+
21+
<img width="1353" height="327" alt="image" src="https://github.com/user-attachments/assets/1bb55339-36b4-4a9c-8b65-2b254b87cf5b" />
22+
23+
From the spModal popup select anyone of the reward, it should be populated in the Reward Selected field.
24+
Along with that a message shows the same of the selection.
25+
26+
<img width="1350" height="319" alt="image" src="https://github.com/user-attachments/assets/1b23c766-51f8-4b01-9073-f836f390deb2" />
27+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
function onChange(control, oldValue, newValue) {
2+
if (newValue == 'Yes') {
3+
spModal.open({
4+
title: "Reward Type",
5+
message: "Please select the category of Reward",
6+
buttons: [{
7+
label: "Star Performer",
8+
value: "Star Performer"
9+
},
10+
{
11+
label: "Emerging Player",
12+
value: "Emerging Player"
13+
},
14+
{
15+
label: "High Five Award",
16+
value: "High Five Award"
17+
},
18+
{
19+
label: "Rising Star",
20+
value: "Rising Star"
21+
}
22+
]
23+
}).then(function(choice) {
24+
if (choice && choice.value) {
25+
g_form.addInfoMessage('Selected Reward: '+ choice.label);
26+
g_form.setValue('reward_selected', choice.value);
27+
}
28+
});
29+
} else {
30+
g_form.clearValue('reward_selected');
31+
}
32+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
## Dynamically Switch Form View Based on Field Value
2+
3+
This client script demonstrates how to **automatically switch form views** based on the value of a field.
4+
5+
**Use case:**
6+
For example, if the **Category** field is set to *Hardware*, the form view switches to **ess**.
7+
You can extend this by updating the mapping object to support additional fields and values (e.g., *Software → itil*, *Network → support*).
8+
9+
**Benefit:**
10+
Improves user experience by guiding users to the **most relevant form view**, ensuring the right fields are shown for the right scenario.
11+
12+
**Test:**
13+
- Change the **Category** field to *Hardware* → Form view should switch to **ess**.
14+
- Update mapping to add new conditions (e.g., *Software → itil*) and verify the view switches accordingly.
15+
16+
**How to Use:**
17+
1. **Modify the table name** in the `switchView` function to match your target table:
18+
```javascript
19+
switchView("section", "<your_table_name>", targetView);
20+
2. **Modify the view mapping**
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* dynamic-form-view-onchange.js
3+
*
4+
* Dynamically switches the form view automatically depending on the value of a specific field.
5+
* Example: If Category = Hardware → switch to ess view.
6+
* Extendable by modifying the mapping object for different fields/values.
7+
*
8+
*/
9+
10+
function onChange(control, oldValue, newValue, isLoading) {
11+
if (isLoading || !newValue) {
12+
return;
13+
}
14+
15+
// Field value → view name mapping
16+
var viewMapping = {
17+
"hardware": "ess",
18+
"software": "itil",
19+
"network": "support"
20+
};
21+
22+
var fieldValue = newValue.toLowerCase();
23+
var targetView = viewMapping[fieldValue];
24+
25+
if (targetView) {
26+
try {
27+
// Here for example the table name is incident
28+
switchView("section", "incident", targetView);
29+
} catch (e) {
30+
console.error("View switch failed: ", e);
31+
}
32+
}
33+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Field Completion Counter
2+
3+
## Use Case / Requirement
4+
Display a simple message showing how many fields are completed vs total fields on a form. This helps users track their progress while filling out forms.
5+
6+
## Solution
7+
A simple onLoad client script that:
8+
- Counts filled vs empty fields
9+
- Shows completion status in an info message
10+
- Updates when fields are modified
11+
12+
## Implementation
13+
Add this as an **onLoad** client script on any form.
14+
15+
## Notes
16+
- Excludes system fields and read-only fields
17+
- Updates in real-time as users fill fields
18+
- Simple and lightweight solution
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
function onLoad() {
2+
// Display field completion counter
3+
showFieldProgress();
4+
5+
// Set up listener for field changes
6+
setupProgressUpdater();
7+
8+
function showFieldProgress() {
9+
var allFields = g_form.getFieldNames();
10+
var visibleFields = [];
11+
var filledFields = 0;
12+
13+
// Count visible, editable fields
14+
for (var i = 0; i < allFields.length; i++) {
15+
var fieldName = allFields[i];
16+
17+
// Skip system fields and hidden/readonly fields
18+
if (fieldName.indexOf('sys_') === 0 ||
19+
!g_form.isVisible(fieldName) ||
20+
g_form.isReadOnly(fieldName)) {
21+
continue;
22+
}
23+
visibleFields.push(fieldName);
24+
25+
// Check if field has value
26+
if (g_form.getValue(fieldName)) {
27+
filledFields++;
28+
}
29+
}
30+
var totalFields = visibleFields.length;
31+
var percentage = totalFields > 0 ? Math.round((filledFields / totalFields) * 100) : 0;
32+
33+
g_form.addInfoMessage('Form Progress: ' + filledFields + '/' + totalFields + ' fields completed (' + percentage + '%)');
34+
}
35+
36+
function setupProgressUpdater() {
37+
// Simple debounced update
38+
var updateTimer;
39+
40+
function updateProgress() {
41+
clearTimeout(updateTimer);
42+
updateTimer = setTimeout(function() {
43+
g_form.clearMessages();
44+
showFieldProgress();
45+
}, 500);
46+
}
47+
48+
// Listen for any field change
49+
var allFields = g_form.getFieldNames();
50+
for (var i = 0; i < allFields.length; i++) {
51+
g_form.addElementChangeListener(allFields[i], updateProgress);
52+
}
53+
}
54+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# The Glide User (g_user) is a global object available within the client side. It provides information about the logged-in user.
2+
3+
Property Description
4+
5+
g_user.userID Sys ID of the currently logged-in user
6+
g_user.name User's Full name
7+
g_user.firstName User's First name
8+
g_user.lastName User's Last name
9+
10+
# It also has some methods available within the client side.
11+
12+
Method Description
13+
14+
g_user.hasRole() Determine whether the logged-in user has a specific role
15+
g_user.hasRoleExactly() Do not consider the admin role while evaluating the method
16+
g_user.hasRoles() You can pass two or more roles in a single method

0 commit comments

Comments
 (0)