profoundjs 6.1.0 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/htdocs/profoundui/proddata/css/logic.css +21 -2
- package/htdocs/profoundui/proddata/css/profoundui.css +16 -0
- package/htdocs/profoundui/proddata/js/designer.js +3893 -3813
- package/htdocs/profoundui/proddata/js/key_management.js +15 -21
- package/htdocs/profoundui/proddata/js/rich-display-react-component.js +42 -42
- package/htdocs/profoundui/proddata/js/rich-display-vue-component.js +27 -27
- package/htdocs/profoundui/proddata/js/runtime.js +1493 -1489
- package/htdocs/profoundui/proddata/js/soapclient.js +388 -395
- package/index.js +4 -4
- package/package.json +9 -1
- package/profound.jse +1 -1
- package/setup/call.js +1 -1
- package/setup/completeInstall.js +26 -56
- package/setup/convertStartJS.js +40 -30
- package/setup/encrypt_client_file.js +4 -0
- package/setup/install.js +1 -1
- package/setup/install_utils.js +15 -30
- package/setup/lic_client.js +9 -0
- package/setup/modules/papisamples/.noderun/settings.json +5 -0
- package/setup/modules/papisamples/APISample.api.json +313 -0
- package/setup/modules/papisamples/APISample.js +55 -0
- package/setup/modules/papisamples/APISample.json +751 -0
- package/setup/modules/papisamples/README.md +17 -0
- package/setup/modules/papisamples/hobbyshop.csv +6 -0
- package/setup/modules/papisamples/readCSVFile.js +136 -0
- package/setup/modules/puiscreens.json +6 -5
- package/setup/pjsdist.savf +0 -0
- package/setup/removeExtraComponents.js +4 -6
- package/setup/setup.js +87 -59
- package/views/cloud.css +19 -6
- package/views/key.ejs +32 -2
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
# Profound API Samples
|
|
3
|
+
|
|
4
|
+
A sample Profound API program. Server-side API logic is defined in low-code and JavaScript. A client-side program exists to demonstrate calling the API end point.
|
|
5
|
+
|
|
6
|
+
## Sample GET, POST, PUT, DELETE, and Grid Request
|
|
7
|
+
|
|
8
|
+
Run the sample user interface by clicking either
|
|
9
|
+
* Launch > Launch App in Browser Tab
|
|
10
|
+
* Launch > Launch App in IDE
|
|
11
|
+
|
|
12
|
+
## Files
|
|
13
|
+
* APISample.api.json - the server-side logic for the API endpoints, as Low Code steps
|
|
14
|
+
* APISample.js - program logic for the sample user interface to interact with the APIs
|
|
15
|
+
* APISample.json - sample user interface to interact with the APIs
|
|
16
|
+
* hobbyshop.csv - sample data file
|
|
17
|
+
* readCSVFile.js - sample module to handle reading and writing to the data file. Used in Low Code steps.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* A utility function to read a CSV file and output its contents in a pre-determined format.
|
|
4
|
+
*/
|
|
5
|
+
const fs = require("fs"); // filestream module
|
|
6
|
+
const readline = require("readline"); // line-by-line reading module
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Read a file line by line and output an object with its contents, with each field as properties of the object.
|
|
10
|
+
* @param {String} filename
|
|
11
|
+
* @param {Number} rowID
|
|
12
|
+
* @returns {Object}
|
|
13
|
+
*/
|
|
14
|
+
async function fileRead(filename, rowID = 0) {
|
|
15
|
+
let fLength = 0;
|
|
16
|
+
const lData = [];
|
|
17
|
+
let lTemp = null;
|
|
18
|
+
// Start a file stream to read the file line-by-line
|
|
19
|
+
const fstream = fs.createReadStream(filename);
|
|
20
|
+
const linereader = readline.createInterface({
|
|
21
|
+
input: fstream,
|
|
22
|
+
crlfDelay: Infinity // used to identify all \r\n entries in the file as single line breaks
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Start reading the lines of the file
|
|
26
|
+
for await (const line of linereader) {
|
|
27
|
+
fLength++;
|
|
28
|
+
if (rowID == 0) { // read the whole file, parse each line into arrays
|
|
29
|
+
lTemp = line.split(",");
|
|
30
|
+
lData.push({ ID: lTemp[0], Name: lTemp[1], Price: lTemp[2], Quantity: lTemp[3] });
|
|
31
|
+
}
|
|
32
|
+
else if (fLength == rowID) {
|
|
33
|
+
lTemp = line.split(",");
|
|
34
|
+
lData.push({ ID: lTemp[0], Name: lTemp[1], Price: lTemp[2], Quantity: lTemp[3] });
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return { length: fLength, data: lData };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Read a file line by line, changing the specified line, and then write the data back to the file.
|
|
43
|
+
* Return a message indicating which row was updated.
|
|
44
|
+
* The original file is changed.
|
|
45
|
+
* @param {String} filename
|
|
46
|
+
* @param {Number} rowID
|
|
47
|
+
* @returns {String}
|
|
48
|
+
*/
|
|
49
|
+
async function fileUpdate(filename, rowID, data) {
|
|
50
|
+
let fLength = 0;
|
|
51
|
+
// Start a file stream to read the file line-by-line
|
|
52
|
+
const fstream = fs.createReadStream(filename);
|
|
53
|
+
const linereader = readline.createInterface({
|
|
54
|
+
input: fstream,
|
|
55
|
+
crlfDelay: Infinity // used to identify all \r\n entries in the file as single line breaks
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
let strUpdated = "";
|
|
59
|
+
|
|
60
|
+
// Start reading the lines of the file
|
|
61
|
+
for await (const line of linereader) {
|
|
62
|
+
fLength++;
|
|
63
|
+
if (fLength == rowID) {
|
|
64
|
+
strUpdated += fLength + "," + data + "\n"; // update the specific line if found
|
|
65
|
+
}
|
|
66
|
+
else strUpdated += line + "\n";
|
|
67
|
+
};
|
|
68
|
+
linereader.close();
|
|
69
|
+
fstream.close();
|
|
70
|
+
|
|
71
|
+
// remove the last entry of \n
|
|
72
|
+
strUpdated = strUpdated.substring(0, strUpdated.length - 1);
|
|
73
|
+
|
|
74
|
+
const message = await new Promise((resolve, reject) => {
|
|
75
|
+
// rewrite the file with the updated data
|
|
76
|
+
fs.writeFile(filename, strUpdated, (err) => {
|
|
77
|
+
if (err) reject(err);
|
|
78
|
+
else resolve("Row ID: " + iID + "\nData Updated: " + data);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return message;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Read a file line by line and build a string with the file contents, with the line specified by rowID removed.
|
|
87
|
+
* Write the new string to the file, overwriting the original file.
|
|
88
|
+
* Return a message indicating which row was deleted.
|
|
89
|
+
* @param {String} filename
|
|
90
|
+
* @param {Number} rowID
|
|
91
|
+
* @returns {String}
|
|
92
|
+
*/
|
|
93
|
+
async function lineDelete(filename, rowID) {
|
|
94
|
+
let iCol = 0;
|
|
95
|
+
let fLength = 0;
|
|
96
|
+
let strUpdated = "";
|
|
97
|
+
let strTemp = "";
|
|
98
|
+
// Start a file stream to read the file line-by-line
|
|
99
|
+
const fstream = fs.createReadStream(filename);
|
|
100
|
+
const linereader = readline.createInterface({
|
|
101
|
+
input: fstream,
|
|
102
|
+
crlfDelay: Infinity // used to identify all \r\n entries in the file as single line breaks
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Start reading the lines of the file
|
|
106
|
+
for await (const line of linereader) {
|
|
107
|
+
fLength++;
|
|
108
|
+
if (fLength < rowID) { // Copy the line as is if it's not the row ID requested
|
|
109
|
+
strUpdated += line + "\n";
|
|
110
|
+
}
|
|
111
|
+
else if (fLength > rowID) { // Update the first column to reflect the correct line
|
|
112
|
+
iCol = line.indexOf(","); // find first occurence of , as the first column
|
|
113
|
+
strTemp = line.substring(iCol, line.length); // move the , and the data to temp string
|
|
114
|
+
strUpdated += (fLength - 1) + strTemp + "\n"; // add the line and its updated column number
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
linereader.close();
|
|
118
|
+
fstream.close();
|
|
119
|
+
|
|
120
|
+
// remove the last entry of \n
|
|
121
|
+
strUpdated = strUpdated.substring(0, strUpdated.length - 1);
|
|
122
|
+
|
|
123
|
+
// Write the strUpdated string to the file at filename:
|
|
124
|
+
const message = new Promise((resolve, reject) => {
|
|
125
|
+
fs.writeFile(filename, strUpdated, (err) => {
|
|
126
|
+
if (err) reject(err);
|
|
127
|
+
else resolve("Row " + rowID + " deleted.");
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
return message;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
exports.fileRead = fileRead;
|
|
135
|
+
exports.fileUpdate = fileUpdate;
|
|
136
|
+
exports.lineDelete = lineDelete;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
{
|
|
5
5
|
"screen": {
|
|
6
6
|
"record format name": "signonscrn",
|
|
7
|
-
"onload": "if(window.puiMobileClient == null){\n if(window.device != null){\n if(window.device.platform != null){\n if(window.device.platform == \"iOS\"){\n applyProperty(\"btnBack\", \"visibility\", \"hidden\");\n applyProperty(\"btnBack\", \"field type\", \"
|
|
7
|
+
"onload": "if(window.puiMobileClient == null){\n if(window.device != null){\n if(window.device.platform != null){\n if(window.device.platform == \"iOS\"){\n applyProperty(\"btnBack\", \"visibility\", \"hidden\");\n applyProperty(\"btnBack\", \"field type\", \"graphic button\");\n applyProperty(\"SignOnButton\", \"width\", \"340px\");\n applyProperty(\"SignOnButton\", \"field type\", \"graphic button\");\n }\n } \n }\n}\npui.confirmOnClose = false;"
|
|
8
8
|
},
|
|
9
9
|
"items": [
|
|
10
10
|
{
|
|
@@ -166,7 +166,7 @@
|
|
|
166
166
|
"screen": {
|
|
167
167
|
"record format name": "errscrn",
|
|
168
168
|
"disable enter key": "true",
|
|
169
|
-
"onload": "\nif (pui[\"errorScreen\"] && pui[\"errorScreen\"][\"onload\"]) {\n pui[\"errorScreen\"][\"onload\"]();\n}\nelse {\n // Backwards compatibility code for when pui[\"errorScreen\"][\"onload\"]() is not available\n if ((window[\"puiMobileClient\"] == null && window[\"device\"] != null &&\n window[\"device\"][\"platform\"] == \"iOS\") ||\n pui.genie != null) {\n applyProperty(\"btnBack\", \"visibility\", \"hidden\");\n applyProperty(\"btnBack\", \"field type\", \"
|
|
169
|
+
"onload": "\nif (pui[\"errorScreen\"] && pui[\"errorScreen\"][\"onload\"]) {\n pui[\"errorScreen\"][\"onload\"]();\n}\nelse {\n // Backwards compatibility code for when pui[\"errorScreen\"][\"onload\"]() is not available\n if ((window[\"puiMobileClient\"] == null && window[\"device\"] != null &&\n window[\"device\"][\"platform\"] == \"iOS\") ||\n pui.genie != null) {\n applyProperty(\"btnBack\", \"visibility\", \"hidden\");\n applyProperty(\"btnBack\", \"field type\", \"graphic button\");\n }\n if (pui.genie != null) {\n applyProperty(\"NewSessionButton\", \"value\", pui.getLanguageText(\"runtimeText\", \"ok\"));\n applyProperty(\"NewSessionButton\", \"onclick\", \"pui.click();\");\n }\n pui.formatErrorText();\n pui.confirmOnClose = false;\n pui.shutdownOnClose = false;\n}\n"
|
|
170
170
|
},
|
|
171
171
|
"items": [
|
|
172
172
|
{
|
|
@@ -704,7 +704,7 @@
|
|
|
704
704
|
{
|
|
705
705
|
"screen": {
|
|
706
706
|
"record format name": "eojscrn",
|
|
707
|
-
"onload": "if(window.device != null){\n if(window.device.platform != null){\n if(window.device.platform == \"iOS\"){\n applyProperty(\"btnBack\", \"visibility\", \"hidden\");\n applyProperty(\"btnBack\", \"field type\", \"
|
|
707
|
+
"onload": "if(window.device != null){\n if(window.device.platform != null){\n if(window.device.platform == \"iOS\"){\n applyProperty(\"btnBack\", \"visibility\", \"hidden\");\n applyProperty(\"btnBack\", \"field type\", \"graphic button\");\n applyProperty(\"NewSessionButton2\", \"width\", \"335px\");\n applyProperty(\"NewSessionButton2\", \"field type\", \"graphic button\");\n }\n }\n}\n\npui.endOfSession();"
|
|
708
708
|
},
|
|
709
709
|
"items": [
|
|
710
710
|
{
|
|
@@ -774,7 +774,7 @@
|
|
|
774
774
|
{
|
|
775
775
|
"screen": {
|
|
776
776
|
"record format name": "timoutscrn",
|
|
777
|
-
"onload": "if(window.device != null){\n if(window.device.platform != null){\n if(window.device.platform == \"iOS\"){\n applyProperty(\"btnBack\", \"visibility\", \"hidden\");\n applyProperty(\"btnBack\", \"field type\", \"
|
|
777
|
+
"onload": "if(window.device != null){\n if(window.device.platform != null){\n if(window.device.platform == \"iOS\"){\n applyProperty(\"btnBack\", \"visibility\", \"hidden\");\n applyProperty(\"btnBack\", \"field type\", \"graphic button\");\n applyProperty(\"NewSessionButton3\", \"width\", \"335px\");\n applyProperty(\"NewSessionButton3\", \"field type\", \"graphic button\");\n }\n }\n}\n\npui.endOfSession(\"Your session has timed out.\");"
|
|
778
778
|
},
|
|
779
779
|
"items": [
|
|
780
780
|
{
|
|
@@ -988,7 +988,8 @@
|
|
|
988
988
|
"rjZeroFill": "false",
|
|
989
989
|
"dataType": "varchar",
|
|
990
990
|
"formatting": "Text",
|
|
991
|
-
"textTransform": "none"
|
|
991
|
+
"textTransform": "none",
|
|
992
|
+
"designValue": "@media screen { #_id_ > .puiresp { display:grid; } }"
|
|
992
993
|
},
|
|
993
994
|
"use viewport": "true",
|
|
994
995
|
"bottom": "0px",
|
package/setup/pjsdist.savf
CHANGED
|
Binary file
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
5
|
Discovers and removes additional components that exist outside of the package installation directory.
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
It's used by Profound Installer
|
|
8
8
|
|
|
9
9
|
Any changes to the behavior may require changes to the "install_info.json" file or Profound Installer.
|
|
@@ -15,9 +15,8 @@ const readline = require("readline");
|
|
|
15
15
|
|
|
16
16
|
const IBMi = iutils.isIBMi();
|
|
17
17
|
|
|
18
|
-
(async
|
|
18
|
+
(async() => {
|
|
19
19
|
try {
|
|
20
|
-
|
|
21
20
|
// Parse arguments.
|
|
22
21
|
const argDefs = {
|
|
23
22
|
alias: {
|
|
@@ -39,7 +38,7 @@ const IBMi = iutils.isIBMi();
|
|
|
39
38
|
|
|
40
39
|
// Show help and quit, if requested.
|
|
41
40
|
if (args["help"] === true) {
|
|
42
|
-
const HELP = `Usage: node removeExtraComponents.js [OPTION]
|
|
41
|
+
const HELP = `Usage: node removeExtraComponents.js [OPTION]
|
|
43
42
|
-h, --help Print this help and exit.
|
|
44
43
|
-d, --dry-run Report what items would be removed, without removing them.
|
|
45
44
|
-s, --silent Bypass confirmation prompt.
|
|
@@ -170,7 +169,6 @@ It is intended to be used before uninstalling the package via NPM.
|
|
|
170
169
|
}
|
|
171
170
|
console.log("Removal of extra components complete.");
|
|
172
171
|
process.stdin.destroy();
|
|
173
|
-
|
|
174
172
|
}
|
|
175
173
|
catch (error) {
|
|
176
174
|
process.stdin.destroy();
|
|
@@ -194,7 +192,7 @@ function exec(command, quiet = false) {
|
|
|
194
192
|
const opts = {
|
|
195
193
|
env: {
|
|
196
194
|
QIBM_USE_DESCRIPTOR_STDIO: "Y",
|
|
197
|
-
QIBM_MULTI_THREADED: "N"
|
|
195
|
+
QIBM_MULTI_THREADED: "N"
|
|
198
196
|
},
|
|
199
197
|
stdio: "pipe",
|
|
200
198
|
windowsHide: true
|
package/setup/setup.js
CHANGED
|
@@ -28,8 +28,8 @@ const uuidv4 = require("uuid").v4;
|
|
|
28
28
|
|
|
29
29
|
const IBMi = iutils.isIBMi();
|
|
30
30
|
|
|
31
|
-
let RED
|
|
32
|
-
let CYAN
|
|
31
|
+
let RED = "\x1b[31m";
|
|
32
|
+
let CYAN = "\x1b[36m";
|
|
33
33
|
let RESET = "\x1b[0m";
|
|
34
34
|
|
|
35
35
|
const logDir = path.join(os.tmpdir(), "profoundjs-" + uuidv4());
|
|
@@ -38,9 +38,8 @@ let logFile;
|
|
|
38
38
|
|
|
39
39
|
let SILENT_MODE = false;
|
|
40
40
|
|
|
41
|
-
(async
|
|
41
|
+
(async() => {
|
|
42
42
|
try {
|
|
43
|
-
|
|
44
43
|
// Parse arguments.
|
|
45
44
|
const args = minimist(
|
|
46
45
|
process.argv.slice(2),
|
|
@@ -73,7 +72,7 @@ let SILENT_MODE = false;
|
|
|
73
72
|
|
|
74
73
|
// Show help and quit, if requested.
|
|
75
74
|
if (args["help"]) {
|
|
76
|
-
|
|
75
|
+
const HELP = `Usage: npm run setup -- [OPTION]
|
|
77
76
|
-c, --config-file=<file> Alternate config file.
|
|
78
77
|
-h, --help Print this help and exit.
|
|
79
78
|
--nodegit Install nodegit.
|
|
@@ -289,6 +288,13 @@ let SILENT_MODE = false;
|
|
|
289
288
|
log("Copying pjssamples.");
|
|
290
289
|
copyDir(path.join(__dirname, "modules", "pjssamples"), path.join(deployDir, "modules"));
|
|
291
290
|
|
|
291
|
+
// Create PAPI samples directory
|
|
292
|
+
if (directoryExists(path.join(deployDir, "modules", "papisamples"))) {
|
|
293
|
+
fs.removeSync(path.join(deployDir, "modules", "papisamples"));
|
|
294
|
+
}
|
|
295
|
+
log("Copying papisamples.");
|
|
296
|
+
copyDir(path.join(__dirname, "modules", "papisamples"), path.join(deployDir, "modules"));
|
|
297
|
+
|
|
292
298
|
// Create store_credentials.js.
|
|
293
299
|
if (fileExists(path.join(deployDir, "store_credentials.js"))) {
|
|
294
300
|
log("store_credentials.js file exists.");
|
|
@@ -307,6 +313,17 @@ let SILENT_MODE = false;
|
|
|
307
313
|
log("store_options.js created.");
|
|
308
314
|
}
|
|
309
315
|
|
|
316
|
+
// Begin OAuth2 requirements.
|
|
317
|
+
// Create encrypt_client_file.js.
|
|
318
|
+
if (fileExists(path.join(deployDir, "encrypt_client_file.js"))) {
|
|
319
|
+
log("encrypt_client_file.js file exists.");
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
copyFile(path.join(__dirname, "encrypt_client_file.js"), deployDir, "utf8");
|
|
323
|
+
log("encrypt_client_file.js created.");
|
|
324
|
+
}
|
|
325
|
+
// End OAuth2 requirements.
|
|
326
|
+
|
|
310
327
|
// Create call.js.
|
|
311
328
|
if (fileExists(path.join(deployDir, "call.js"))) {
|
|
312
329
|
log("call.js file exists.");
|
|
@@ -316,6 +333,10 @@ let SILENT_MODE = false;
|
|
|
316
333
|
log("call.js created.");
|
|
317
334
|
}
|
|
318
335
|
|
|
336
|
+
// Create lic_client.js
|
|
337
|
+
copyFile(path.join(__dirname, "lic_client.js"), deployDir, "utf8");
|
|
338
|
+
log("lic_client.js created.");
|
|
339
|
+
|
|
319
340
|
// Convert start.js to Promises, if necessary.
|
|
320
341
|
const convertStartJS = require("./convertStartJS.js");
|
|
321
342
|
if (convertStartJS(path.join(deployDir, "start.js"))) {
|
|
@@ -326,7 +347,7 @@ let SILENT_MODE = false;
|
|
|
326
347
|
if (IBMi) {
|
|
327
348
|
log("");
|
|
328
349
|
if (args["ibmi-connector-library"] || args["ibmi-instance"] !== undefined) {
|
|
329
|
-
|
|
350
|
+
const portNumber = config.port || 8081;
|
|
330
351
|
let connectorLibrary = "*NONE";
|
|
331
352
|
let connectorIASP;
|
|
332
353
|
if (args["ibmi-connector-library"]) {
|
|
@@ -350,7 +371,7 @@ let SILENT_MODE = false;
|
|
|
350
371
|
}
|
|
351
372
|
}
|
|
352
373
|
// First, try copying the save file
|
|
353
|
-
|
|
374
|
+
let success = runCommand("CPYFRMSTMF FROMSTMF('" + __dirname + "/pjsdist.savf') TOMBR('/QSYS.LIB/QGPL.LIB/PJSDIST.FILE') MBROPT(*REPLACE)");
|
|
354
375
|
if (!success) {
|
|
355
376
|
logError("Unable to create installer save file.");
|
|
356
377
|
die();
|
|
@@ -366,13 +387,15 @@ let SILENT_MODE = false;
|
|
|
366
387
|
die();
|
|
367
388
|
}
|
|
368
389
|
// Now, try running PJSINSTALL
|
|
369
|
-
|
|
370
|
-
if (connectorLibrary != "*NONE")
|
|
390
|
+
let command = "QGPL/PJSINSTALL CONNLIB(" + connectorLibrary + ")";
|
|
391
|
+
if (connectorLibrary != "*NONE") {
|
|
371
392
|
command += " CONNIASP(" + connectorIASP + ") CONNHOST('localhost') CONNPORT(" + portNumber + ")";
|
|
393
|
+
}
|
|
372
394
|
command += " SVRNAME(" + svrname + ")";
|
|
373
|
-
if (svrname != "*NONE")
|
|
395
|
+
if (svrname != "*NONE") {
|
|
374
396
|
command += " SVRDIR('" + deployDir + "') " +
|
|
375
397
|
"SVRAUTO(" + ((autostart) ? "*YES" : "*NO") + ") CCSID(" + ccsid + ") NODEPATH('" + nodePath + "')";
|
|
398
|
+
}
|
|
376
399
|
success = runCommand(command, "-Ke");
|
|
377
400
|
// Clean up program and save file
|
|
378
401
|
log("");
|
|
@@ -402,7 +425,6 @@ let SILENT_MODE = false;
|
|
|
402
425
|
}
|
|
403
426
|
}
|
|
404
427
|
log(`\n${CYAN}Profound.js installation complete.${RESET}\n`);
|
|
405
|
-
|
|
406
428
|
}
|
|
407
429
|
catch (error) {
|
|
408
430
|
logError(error);
|
|
@@ -424,7 +446,7 @@ function logToFile(...args) {
|
|
|
424
446
|
if (SILENT_MODE) {
|
|
425
447
|
return;
|
|
426
448
|
}
|
|
427
|
-
let output = `${util.format(...args)}\n`;
|
|
449
|
+
let output = `${util.format(...args)}\n`; // Mimic console.log().
|
|
428
450
|
output = output.replace(/\x1b\[[0-9]+m/g, ""); // Strip terminal escape codes for colors.
|
|
429
451
|
fs.writeFileSync(logFile, output, { flag: "a" });
|
|
430
452
|
}
|
|
@@ -442,7 +464,7 @@ function copyDir(dir, destinationDir) {
|
|
|
442
464
|
fs.mkdirSync(path.join(destinationDir, dirName));
|
|
443
465
|
const files = fs.readdirSync(dir);
|
|
444
466
|
files.forEach(function(file, index) {
|
|
445
|
-
if(fs.lstatSync(path.join(dir, file)).isDirectory()) { // recurse
|
|
467
|
+
if (fs.lstatSync(path.join(dir, file)).isDirectory()) { // recurse
|
|
446
468
|
copyDir(path.join(dir, file), path.join(destinationDir, dirName));
|
|
447
469
|
}
|
|
448
470
|
else {
|
|
@@ -453,18 +475,22 @@ function copyDir(dir, destinationDir) {
|
|
|
453
475
|
|
|
454
476
|
function copyFile(file, destination, type) {
|
|
455
477
|
if (type == null) type = "binary";
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
478
|
+
let toFile;
|
|
479
|
+
if (directoryExists(destination)) {
|
|
480
|
+
toFile = path.join(destination, path.basename(file));
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
toFile = destination;
|
|
484
|
+
}
|
|
485
|
+
const content = fs.readFileSync(file, type);
|
|
461
486
|
fs.writeFileSync(toFile, content, type);
|
|
462
487
|
}
|
|
463
488
|
|
|
464
489
|
function createPuiscreens(deployDir) {
|
|
465
|
-
|
|
490
|
+
const targetPuiScreensFile = path.join(deployDir, "modules", "puiscreens.json");
|
|
491
|
+
function copyFileOldVersionIs(version, compareStr) {
|
|
466
492
|
compareStr = compareStr || "=";
|
|
467
|
-
log("Found Standard puiscreens.json file version "+compareStr+" "+version+", replacing with current version...");
|
|
493
|
+
log("Found Standard puiscreens.json file version " + compareStr + " " + version + ", replacing with current version...");
|
|
468
494
|
copyFile(path.join(__dirname, "modules", "puiscreens.json"), targetPuiScreensFile, "utf8");
|
|
469
495
|
log("puiscreens.json file was replaced.");
|
|
470
496
|
}
|
|
@@ -477,11 +503,15 @@ function createPuiscreens(deployDir) {
|
|
|
477
503
|
// 3. If it's not an official version, check if we have lower case User & Password. If not, then backup existing version to puiscreens_bak.json and install this official version.
|
|
478
504
|
// 4. Otherwise it's not an official version but should work OK. So leave existing puiscreens.json, and check if we already have a puiscreens_orig.json, and
|
|
479
505
|
// create it if not.
|
|
480
|
-
var targetPuiScreensFile = path.join(deployDir, "modules", "puiscreens.json");
|
|
481
506
|
if (fileExists(targetPuiScreensFile)) {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
507
|
+
const data = fs.readFileSync(targetPuiScreensFile);
|
|
508
|
+
const checksum = crypto.createHash("sha512").update(data).digest("hex"); // sha512 best for 64-bit
|
|
509
|
+
let format = null;
|
|
510
|
+
let puiScreens;
|
|
511
|
+
let goodUser = false;
|
|
512
|
+
let goodPassword = false;
|
|
513
|
+
let goodSubmit = false;
|
|
514
|
+
let goodError = false;
|
|
485
515
|
|
|
486
516
|
switch (checksum) {
|
|
487
517
|
case "4fb38daa851aacd21cf3aeaa92304df4e0353cae2b6dab3a5fd53c0cf4d42875b5efadbb4bb29642cbff07bfdd8e1c7a9c107f582905f7fc798c3841e888ee61":
|
|
@@ -531,39 +561,38 @@ function createPuiscreens(deployDir) {
|
|
|
531
561
|
|
|
532
562
|
case "3aaab88d4cf922f01a09bab3424ecf81460c7679a6d51ecb0f187fdce97e627499128e9b8c46d83075098e7e8f7150b73936056b1accedff55a79c69d65cb20b":
|
|
533
563
|
// Note: Betas were considered pre-release versions. 6.0.0 is the release version; so 6.0.0 is newer than 6.0.0-beta.X
|
|
534
|
-
|
|
564
|
+
copyFileOldVersionIs("6.0.0");
|
|
565
|
+
break;
|
|
566
|
+
|
|
567
|
+
case "41a350ae45c7616fe1824fb236b48ab64ca6d0de1023946e16ef9de4eea804649ff13123fa4ce630bb93308c8312f75e12448bbe58e91802dc8b12cc27bfd051":
|
|
568
|
+
log("Found Standard puiscreens.json file version > 6.0.0, no need for update.");
|
|
535
569
|
break;
|
|
536
570
|
|
|
537
571
|
default:
|
|
538
|
-
|
|
572
|
+
puiScreens = JSON.parse(data);
|
|
539
573
|
log("Found Customized puiscreens.json file, analyzing contents...");
|
|
540
574
|
|
|
541
575
|
// We need to check the signon screen and make sure we have lower case bound fields
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
if (itemValue && typeof itemValue === "object") {
|
|
552
|
-
if (itemValue.fieldName === "ssuser") goodUser = true;
|
|
553
|
-
else if (itemValue.fieldName === "sspassword") goodPassword = true;
|
|
554
|
-
}
|
|
555
|
-
var itemResponse = format.items[j].response;
|
|
556
|
-
if (itemResponse && typeof itemResponse === "object") {
|
|
557
|
-
if (itemResponse.fieldName === "sssubmit") goodSubmit = true;
|
|
558
|
-
}
|
|
559
|
-
var itemHtml = format.items[j].html;
|
|
560
|
-
if (itemHtml && typeof itemHtml === "object") {
|
|
561
|
-
if (itemHtml.fieldName === "sserror") goodError = true;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
if (goodUser && goodPassword && goodSubmit && goodError) break formats;
|
|
576
|
+
if (Array.isArray(puiScreens.formats)) {
|
|
577
|
+
format = puiScreens.formats.find(format => typeof format.screen["record format name"] === "string" && format.screen["record format name"].toLowerCase() === "signonscrn");
|
|
578
|
+
}
|
|
579
|
+
if (format) {
|
|
580
|
+
for (const j in format.items) {
|
|
581
|
+
const itemValue = format.items[j].value;
|
|
582
|
+
if (itemValue && typeof itemValue === "object") {
|
|
583
|
+
if (itemValue.fieldName === "ssuser") goodUser = true;
|
|
584
|
+
else if (itemValue.fieldName === "sspassword") goodPassword = true;
|
|
565
585
|
}
|
|
566
|
-
|
|
586
|
+
const itemResponse = format.items[j].response;
|
|
587
|
+
if (itemResponse && typeof itemResponse === "object") {
|
|
588
|
+
if (itemResponse.fieldName === "sssubmit") goodSubmit = true;
|
|
589
|
+
}
|
|
590
|
+
const itemHtml = format.items[j].html;
|
|
591
|
+
if (itemHtml && typeof itemHtml === "object") {
|
|
592
|
+
if (itemHtml.fieldName === "sserror") goodError = true;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (goodUser && goodPassword && goodSubmit && goodError) break;
|
|
567
596
|
}
|
|
568
597
|
}
|
|
569
598
|
|
|
@@ -588,9 +617,9 @@ function createPuiscreens(deployDir) {
|
|
|
588
617
|
}
|
|
589
618
|
|
|
590
619
|
function directoryExists(dir) {
|
|
591
|
-
|
|
620
|
+
let exists = false;
|
|
592
621
|
try {
|
|
593
|
-
|
|
622
|
+
const stat = fs.statSync(dir);
|
|
594
623
|
if (stat && stat.isDirectory()) exists = true;
|
|
595
624
|
}
|
|
596
625
|
catch (err) {
|
|
@@ -600,9 +629,9 @@ function directoryExists(dir) {
|
|
|
600
629
|
}
|
|
601
630
|
|
|
602
631
|
function fileExists(file) {
|
|
603
|
-
|
|
632
|
+
let exists = false;
|
|
604
633
|
try {
|
|
605
|
-
|
|
634
|
+
const stat = fs.statSync(file);
|
|
606
635
|
if (stat && stat.isFile()) exists = true;
|
|
607
636
|
}
|
|
608
637
|
catch (err) {
|
|
@@ -612,9 +641,8 @@ function fileExists(file) {
|
|
|
612
641
|
}
|
|
613
642
|
|
|
614
643
|
function runCommand(command, switches) {
|
|
615
|
-
|
|
616
644
|
log("Executing IBM i command: " + command);
|
|
617
|
-
|
|
645
|
+
const args = [command];
|
|
618
646
|
if (typeof switches == "string") {
|
|
619
647
|
args.unshift(switches);
|
|
620
648
|
}
|
|
@@ -632,7 +660,8 @@ function runCommand(command, switches) {
|
|
|
632
660
|
env: {
|
|
633
661
|
QIBM_USE_DESCRIPTOR_STDIO: "Y",
|
|
634
662
|
QIBM_MULTI_THREADED: "N" // Many IBM commands cannot run in multi-threaded mode...
|
|
635
|
-
|
|
663
|
+
}
|
|
664
|
+
};
|
|
636
665
|
|
|
637
666
|
const results = child_process.spawnSync("system", args, options);
|
|
638
667
|
|
|
@@ -648,8 +677,7 @@ function runCommand(command, switches) {
|
|
|
648
677
|
}
|
|
649
678
|
else {
|
|
650
679
|
logError("Process ended due to signal %s", signal);
|
|
651
|
-
logError(results);
|
|
680
|
+
logError(results); // show full results including detailed error
|
|
652
681
|
}
|
|
653
682
|
return false;
|
|
654
|
-
|
|
655
683
|
}
|
package/views/cloud.css
CHANGED
|
@@ -258,10 +258,23 @@
|
|
|
258
258
|
letter-spacing: .25px;
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
-
#_cloud_signin_button {
|
|
261
|
+
#_cloud_signin_button, #_cloud_signin_button_right {
|
|
262
262
|
margin-top: 15px;
|
|
263
|
-
|
|
264
|
-
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
@media (max-width: 575px) {
|
|
266
|
+
#_cloud_signin_button, #_cloud_signin_button_right {
|
|
267
|
+
width: 100%;
|
|
268
|
+
display:block;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
@media (min-width: 576px) {
|
|
272
|
+
#_cloud_signin_button, #_cloud_signin_button_right {
|
|
273
|
+
width: 47%;
|
|
274
|
+
}
|
|
275
|
+
#_cloud_signin_button_right {
|
|
276
|
+
float: right;
|
|
277
|
+
}
|
|
265
278
|
}
|
|
266
279
|
|
|
267
280
|
/*--- select template ---*/
|
|
@@ -602,9 +615,9 @@ span.pui-fa-brands-icons.fa-google {
|
|
|
602
615
|
}
|
|
603
616
|
|
|
604
617
|
._cloud_signin_col {
|
|
605
|
-
float: left;
|
|
606
|
-
width: 50%;
|
|
607
|
-
margin: auto;
|
|
618
|
+
/* float: left; */
|
|
619
|
+
/* width: 50%; */
|
|
620
|
+
/* margin: auto; */
|
|
608
621
|
padding: 0 40px;
|
|
609
622
|
margin-top: 6px;
|
|
610
623
|
box-sizing: border-box;
|
package/views/key.ejs
CHANGED
|
@@ -107,6 +107,21 @@
|
|
|
107
107
|
color: white;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
.pjs-key-page .floating-lic-info {
|
|
111
|
+
color: white;
|
|
112
|
+
font-size: 15px;
|
|
113
|
+
width: 100%;
|
|
114
|
+
padding: 10px 30px 10px 60px;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.pjs-key-page .floating-lic-info div.pl-uuid span {
|
|
118
|
+
letter-spacing: 1.0px;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.pjs-key-page .floating-lic-info div.fl-lic-verif span.fl-lic-verif-dta.warning {
|
|
122
|
+
color: #FF3F00;
|
|
123
|
+
}
|
|
124
|
+
|
|
110
125
|
.pjs-key-page .system-info {
|
|
111
126
|
padding-left: 30px;
|
|
112
127
|
padding-right: 30px;
|
|
@@ -353,6 +368,21 @@
|
|
|
353
368
|
</div>
|
|
354
369
|
<h1>License Key Management</h1>
|
|
355
370
|
</div>
|
|
371
|
+
<% if (typeof floating !== "undefined"){ %>
|
|
372
|
+
<div class="floating-lic-info">
|
|
373
|
+
<div class="pl-uuid"><span>Floating License UUID: </span><%= floating.uuid %></div>
|
|
374
|
+
<div class="fl-lic-verif"><span class="fl-lic-verif-lbl">Verified: </span>
|
|
375
|
+
<% if (floating.verified) { %>
|
|
376
|
+
<span class="fl-lic-verif-dta">yes</span>
|
|
377
|
+
<% } else { %>
|
|
378
|
+
<span class="fl-lic-verif-dta warning">no - <%= floating.verified_msg %></span>
|
|
379
|
+
<% } %>
|
|
380
|
+
</div>
|
|
381
|
+
<div class="pulse-ts-exp">
|
|
382
|
+
<span>Machine heartbeat pulse required by (UTC): </span><%= floating.pulse_exp %>
|
|
383
|
+
</div>
|
|
384
|
+
</div>
|
|
385
|
+
<% } %>
|
|
356
386
|
<div class="system-info">
|
|
357
387
|
<div>
|
|
358
388
|
<div class="machine-id"><span class="machine-id-label">Machine ID: </span>
|
|
@@ -408,7 +438,7 @@
|
|
|
408
438
|
<form method="post" action="/key">
|
|
409
439
|
<input type="hidden" name="apply" value="1" />
|
|
410
440
|
<div class="paste-key">
|
|
411
|
-
<textarea placeholder="Paste License Key Here" id="keyData" name="keyData" required></textarea>
|
|
441
|
+
<textarea placeholder="Paste License Key or Floating License Authorization Code Here" id="keyData" name="keyData" required></textarea>
|
|
412
442
|
</div>
|
|
413
443
|
<div class="apply-key">
|
|
414
444
|
<input type="submit" value="Apply Key" />
|
|
@@ -422,7 +452,7 @@
|
|
|
422
452
|
<label for="keyFile" />Or <span class="key-file-search">
|
|
423
453
|
<div class="key-file-search-div">Browse</div>
|
|
424
454
|
<input type="file" id="keyFile" name="keyFile" required onchange="displayKeyFileName(this);" />
|
|
425
|
-
</span> to select a <a href="https://docs.profoundlogic.com/x/_wLrAw">Key File</a></label>
|
|
455
|
+
</span> to select a <a href="https://docs.profoundlogic.com/x/_wLrAw">Key or Floating License Authorization Code File</a></label>
|
|
426
456
|
</div>
|
|
427
457
|
<div class="key-file">
|
|
428
458
|
<input class="key-file-to-upload" id="keyFileName" placeholder="Nothing to upload yet..." disabled="disabled" />
|