Skip to content

Commit 27e2049

Browse files
committed
fixes
1 parent 99c58d2 commit 27e2049

File tree

3 files changed

+60
-39
lines changed

3 files changed

+60
-39
lines changed

specifyweb/frontend/js_src/lib/components/AppResources/Editor.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,23 @@ import { AppResourcesTab, useEditorTabs } from './Tabs';
4646
import { getScope } from './tree';
4747
import type { ScopedAppResourceDir } from './types';
4848
import { appResourceSubTypes } from './types';
49+
import { replaceViewsetNameInXml } from './xmlUtils';
4950

5051
export const AppResourceContext = React.createContext<
5152
SpecifyResource<SpAppResource>
5253
>(undefined!);
5354

55+
const syncViewsetNameInXml = (
56+
data: string | null | undefined,
57+
appResource: SpecifyResource<SpAppResource | SpViewSetObject>
58+
): string => {
59+
const viewSet = toTable(appResource, 'SpViewSetObj');
60+
const name = viewSet?.get('name');
61+
if (typeof data !== 'string') return data ?? '';
62+
if (typeof name !== 'string' || name.length === 0) return data;
63+
return replaceViewsetNameInXml(data, name);
64+
};
65+
5466
export function AppResourceEditor({
5567
resource,
5668
directory,
@@ -291,9 +303,13 @@ export function AppResourceEditor({
291303
typeof lastDataRef.current === 'function'
292304
? lastDataRef.current()
293305
: lastDataRef.current;
306+
const syncedData = syncViewsetNameInXml(
307+
data === undefined ? resourceData.data : data,
308+
appResource
309+
);
294310
const appResourceData = deserializeResource({
295311
...resourceData,
296-
data: data === undefined ? resourceData.data : data,
312+
data: syncedData,
297313
spAppResource:
298314
toTable(appResource, 'SpAppResource')?.get(
299315
'resource_uri'

specifyweb/frontend/js_src/lib/components/AppResources/EditorWrapper.tsx

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import type { AppResourcesOutlet } from './index';
3232
import { globalResourceKey } from './tree';
3333
import type { ScopedAppResourceDir } from './types';
3434
import { appResourceSubTypes } from './types';
35+
import { replaceViewsetNameInXml } from './xmlUtils';
3536

3637
export function AppResourceView(): JSX.Element {
3738
return <Wrapper mode="appResources" />;
@@ -197,50 +198,33 @@ function useAppResource(
197198
);
198199
}
199200

200-
/*
201-
* REFACTOR:
202-
* Split this function up.
203-
* Currently, the resource is not needed until subtype needs to be determined.
204-
* All the functionality that does not depend on resource should be part of a different
205-
* function.
206-
*/
207201
function useInitialData(
208202
resource: SerializedResource<SpAppResource | SpViewSetObj>,
209203
initialDataFrom: number | undefined,
210204
templateFile: string | undefined
211205
): string | false | undefined {
212206
return useAsyncState(
213207
React.useCallback(async () => {
214-
const escapeXml = (s: string): string =>
215-
s
216-
.replace(/&/g, '&amp;')
217-
.replace(/</g, '&lt;')
218-
.replace(/>/g, '&gt;')
219-
.replace(/"/g, '&quot;');
220-
221208
const replaceViewsetName = (data: string | null | undefined): string => {
222-
const xml = data ?? '';
223209
const resourceName = (resource as any)?.name ?? '';
224-
if (typeof resourceName !== 'string' || resourceName.length === 0)
225-
return xml;
226-
return xml.replace(
227-
/(<viewset\b[^>]*\bname=)(["])(.*?)\2/,
228-
(_match, p1, p2) => `${p1}${p2}${escapeXml(resourceName)}${p2}`
229-
);
210+
return replaceViewsetNameInXml(data, resourceName);
230211
};
231212

232-
if (typeof initialDataFrom === 'number') {
233-
const { data } = await fetchResource('SpAppResourceData', initialDataFrom);
234-
return replaceViewsetName(data);
235-
}
236-
if (typeof templateFile === 'string') {
237-
try {
238-
const { data } = await ajax(`/static/config/${templateFile}`, { headers: {} });
239-
return replaceViewsetName(data);
240-
} catch {
241-
return '';
213+
if (typeof initialDataFrom === 'number')
214+
return fetchResource('SpAppResourceData', initialDataFrom).then(
215+
({ data }) => replaceViewsetName(data ?? ''));
216+
else if (typeof templateFile === 'string') {
217+
if (templateFile.includes('..'))
218+
console.error(
219+
'Relative paths not allowed. Path is always relative to /static/config/'
220+
);
221+
else
222+
return ajax(`/static/config/${templateFile}`, {
223+
headers: {},
224+
})
225+
.then(({ data }) => replaceViewsetName(data))
226+
.catch(() => '');
242227
}
243-
}
244228
const subType = f.maybe(
245229
toResource(resource, 'SpAppResource'),
246230
getAppResourceType
@@ -250,15 +234,13 @@ function useInitialData(
250234
const useTemplate =
251235
typeof type.name === 'string' &&
252236
(!('useTemplate' in type) || type.useTemplate);
253-
if (useTemplate) {
254-
const { data } = await ajax(getAppResourceUrl(type.name, 'quiet'), {
237+
if (useTemplate)
238+
return ajax(getAppResourceUrl(type.name, 'quiet'), {
255239
headers: {},
256-
});
257-
return replaceViewsetName(data);
258-
}
240+
}).then(({ data }) => replaceViewsetName(data));
259241
}
260242
return false;
261-
}, [initialDataFrom, templateFile, resource]),
243+
}, [initialDataFrom, templateFile]),
262244
false
263245
)[0];
264246
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// shared utilities for XML manipulation in AppResources
2+
export const escapeXml = (value: string): string =>
3+
value
4+
.replace(/&/g, '&amp;')
5+
.replace(/</g, '&lt;')
6+
.replace(/>/g, '&gt;')
7+
.replace(/"/g, '&quot;');
8+
9+
// replace the viewset name attribute in XML content
10+
export const replaceViewsetNameInXml = (
11+
data: string | null | undefined,
12+
resourceName: string
13+
): string => {
14+
const xml = data ?? '';
15+
if (typeof resourceName !== 'string' || resourceName.length === 0)
16+
return xml;
17+
return xml.replace(
18+
/(<viewset\b[^>]*\bname=)([\"])(.*?)\2/,
19+
(_match, prefix, quote) => `${prefix}${quote}${escapeXml(resourceName)}${quote}`
20+
);
21+
};
22+
23+

0 commit comments

Comments
 (0)