cht-conf 6.5.1 → 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,10 @@
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
+
1
8
  ## [6.5.1](https://github.com/medic/cht-conf/compare/v6.5.0...v6.5.1) (2026-07-15)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cht-conf",
3
- "version": "6.5.1",
3
+ "version": "6.5.2",
4
4
  "description": "Configure CHT deployments",
5
5
  "main": "./src/lib/main.js",
6
6
  "engines": {
@@ -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.transformer, options.enketo);
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,