cht-conf 6.5.0 → 6.5.2
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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [6.5.2](https://github.com/medic/cht-conf/compare/v6.5.1...v6.5.2) (2026-08-10)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **#832:** fix contact form xml parsing when init has sub-group ([#835](https://github.com/medic/cht-conf/issues/835)) ([e4a11ce](https://github.com/medic/cht-conf/commit/e4a11ced5a48019718299600a122fcd70067b38a))
|
|
7
|
+
|
|
8
|
+
## [6.5.1](https://github.com/medic/cht-conf/compare/v6.5.0...v6.5.1) (2026-07-15)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **#830:** backup-form should not crash if form has no attachments ([#831](https://github.com/medic/cht-conf/issues/831)) ([48ada02](https://github.com/medic/cht-conf/commit/48ada020f4c1df2e896da7e64a55b71779845bbb)), closes [#830](https://github.com/medic/cht-conf/issues/830)
|
|
14
|
+
|
|
1
15
|
# [6.5.0](https://github.com/medic/cht-conf/compare/v6.4.1...v6.5.0) (2026-06-23)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
|
@@ -20,7 +20,7 @@ module.exports = {
|
|
|
20
20
|
return db.get(form.id, { attachments: true, binary: true })
|
|
21
21
|
.then(form => {
|
|
22
22
|
fs.writeJson(`${backupDir}/context.json`, form.context);
|
|
23
|
-
Object.keys(form._attachments).forEach(name => {
|
|
23
|
+
Object.keys(form._attachments || {}).forEach(name => {
|
|
24
24
|
const att = form._attachments[name];
|
|
25
25
|
const destination = fs.path.join(backupDir, name);
|
|
26
26
|
if (fs.path.dirname(destination) !== backupDir) {
|
|
@@ -2,6 +2,23 @@ const convertForms = require('../lib/convert-forms').execute;
|
|
|
2
2
|
const environment = require('../lib/environment');
|
|
3
3
|
const fs = require('../lib/sync-fs');
|
|
4
4
|
const { CONTACT_FORMS_PATH } = require('../lib/project-paths');
|
|
5
|
+
const { getNode, XPATH_BODY } = require('../lib/forms-utils');
|
|
6
|
+
|
|
7
|
+
const getBodyGroup = (xmlDoc, ref) => getNode(xmlDoc, `${XPATH_BODY}//group[@ref="${ref}"]`);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Renders the contact fields on the init page instead of on a page of their own, by making the
|
|
11
|
+
* <contact> body group the last child of the <init> group. Only the body group is moved: the
|
|
12
|
+
* contact data must stay at /data/contact for the webapp to save the doc correctly.
|
|
13
|
+
*/
|
|
14
|
+
const moveContactGroupIntoInit = (xmlDoc) => {
|
|
15
|
+
const initGroup = getBodyGroup(xmlDoc, '/data/init');
|
|
16
|
+
const contactGroup = getBodyGroup(xmlDoc, '/data/contact');
|
|
17
|
+
|
|
18
|
+
if (initGroup && contactGroup) {
|
|
19
|
+
initGroup.appendChild(contactGroup);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
5
22
|
|
|
6
23
|
const convertContactForm = (forms) => {
|
|
7
24
|
const dir = `${environment.pathToProject}/${CONTACT_FORMS_PATH}`;
|
|
@@ -20,6 +37,7 @@ const convertContactForm = (forms) => {
|
|
|
20
37
|
return convertForms(environment.pathToProject, 'contact', {
|
|
21
38
|
enketo: true,
|
|
22
39
|
forms: forms,
|
|
40
|
+
domTransformer: moveContactGroupIntoInit,
|
|
23
41
|
transformer: (xml, path) => {
|
|
24
42
|
const type = path.replace(/.*\/(.*?)(-(create|edit))?\.xml.swp$/, '$1');
|
|
25
43
|
|
|
@@ -60,27 +78,6 @@ const convertContactForm = (forms) => {
|
|
|
60
78
|
}
|
|
61
79
|
}
|
|
62
80
|
|
|
63
|
-
if (xml.includes('ref="/data/contact"')) {
|
|
64
|
-
const groupRegex = name => new RegExp(`(\\s*)<group(\\s.*)?\\sref="${name}"(\\s.*)?>[^]*?</group>`);
|
|
65
|
-
let matchedBlock;
|
|
66
|
-
|
|
67
|
-
if (xml.match(groupRegex('/data/init'))) {
|
|
68
|
-
xml = xml.replace(groupRegex('/data/contact'), match => {
|
|
69
|
-
matchedBlock = match;
|
|
70
|
-
return '';
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
if (matchedBlock) {
|
|
74
|
-
const stripTrailingGroup = s => s.replace(/[\r\n\s]*<\/group>$/, '');
|
|
75
|
-
xml = xml.replace(groupRegex('/data/init'), match => {
|
|
76
|
-
return stripTrailingGroup(match) +
|
|
77
|
-
stripTrailingGroup(matchedBlock).replace(/\n/g, '\n ') +
|
|
78
|
-
'\n </group>\n </group>';
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
81
|
return xml;
|
|
85
82
|
},
|
|
86
83
|
});
|
|
@@ -53,7 +53,7 @@ const execute = async (projectDir, subDirectory, options = {}) => {
|
|
|
53
53
|
try {
|
|
54
54
|
await xls2xform(escapeWhitespacesInPath(sourcePath), escapeWhitespacesInPath(xmlSwpPath), xls);
|
|
55
55
|
const hiddenFields = await getHiddenFields(`${fs.withoutExtension(sourcePath)}.properties.json`);
|
|
56
|
-
fixXml(xmlSwpPath, hiddenFields, options
|
|
56
|
+
fixXml(xmlSwpPath, hiddenFields, options);
|
|
57
57
|
} catch (e) {
|
|
58
58
|
nodeFs.rmSync(xmlSwpPath, { force: true });
|
|
59
59
|
throw e;
|
|
@@ -123,7 +123,7 @@ const xls2xform = async (sourcePath, targetPath, xlsxFileName) => {
|
|
|
123
123
|
|
|
124
124
|
// here we fix the form content in arcane ways. Seeing as we have out own fork
|
|
125
125
|
// of pyxform, we should probably be doing this fixing there.
|
|
126
|
-
const fixXml = (path, hiddenFields, transformer, enketo) => {
|
|
126
|
+
const fixXml = (path, hiddenFields, { transformer, domTransformer, enketo } = {}) => {
|
|
127
127
|
// This is not how you should modify XML, but we have reasonable control over
|
|
128
128
|
// the input and so far this works OK. Keep an eye on the tests, and any
|
|
129
129
|
// future changes to the output of xls2xform.
|
|
@@ -159,6 +159,10 @@ const fixXml = (path, hiddenFields, transformer, enketo) => {
|
|
|
159
159
|
addRepeatCount(xmlDoc);
|
|
160
160
|
handleDbDocRefs(xmlDoc);
|
|
161
161
|
|
|
162
|
+
if (domTransformer) {
|
|
163
|
+
domTransformer(xmlDoc, path);
|
|
164
|
+
}
|
|
165
|
+
|
|
162
166
|
const xmlString = serializer.serializeToString(xmlDoc);
|
|
163
167
|
xml = xmlFormat(xmlString, {
|
|
164
168
|
collapseContent: true,
|