Skip to content

Commit 1f94268

Browse files
ZougoudaPiiXiieeS
andauthored
fix: WEB-207 SVG images now have the proper src, width and alignment (#431)
* fix: WEB-207 SVG images now have the proper src, width and alignment * fix: WEB-207 now with associated Unit Test * build: add prettier conf in eslint - added redundant prettier controls in eslintrc to ensure alignment between workstations and developers - .prettierrc is kept for backwards compatibility Co-authored-by: Jose Gascon <[email protected]> Co-authored-by: PiiXiieeS <[email protected]>
1 parent 0df5956 commit 1f94268

File tree

6 files changed

+109
-1
lines changed

6 files changed

+109
-1
lines changed

.eslintrc.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,13 @@ module.exports = {
2222
'@typescript-eslint/explicit-function-return-type': 'off',
2323
'@typescript-eslint/explicit-module-boundary-types': 'off',
2424
'@typescript-eslint/no-explicit-any': 'off',
25+
'prettier/prettier': [
26+
'error',
27+
{
28+
'singleQuote': true,
29+
'semi': true,
30+
'trailingComma': 'all'
31+
},
32+
],
2533
},
2634
};

.prettierrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"singleQuote": true,
3+
"semi": true,
34
"trailingComma": "all"
45
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"start:dev": "run-p start:dev:nest start:dev:scss",
1919
"start:debug": "nest start --debug --watch",
2020
"start:prod": "node dist/src/main",
21-
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix --max-warnings 3",
21+
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --max-warnings 3",
22+
"lint:fix": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix --max-warnings 3",
2223
"test": "npm run test:unit:cov && npm run test:e2e",
2324
"test:unit:watch": "jest --config jest-unit.config.js --watch",
2425
"test:unit:cov": "jest --config jest-unit.config.js --coverage",

src/proxy-page/proxy-page.service.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import addSlidesCSS from './steps/addSlidesCSS';
3838
import addSlidesJS from './steps/addSlidesJS';
3939
import addUnsupportedMacroIndicator from './steps/addUnsupportedMacroIndicator';
4040
import getFirstExcerpt from 'src/proxy-api/steps/getFirstExcerpt';
41+
import fixSVG from './steps/fixSVG';
4142
import fixTableBackground from './steps/fixTableBackground';
4243

4344
@Injectable()
@@ -108,6 +109,7 @@ export class ProxyPageService {
108109
} else if (type !== 'notitle') {
109110
addHeaderTitle()(this.context);
110111
}
112+
fixSVG(this.config)(this.context);
111113
fixTableBackground()(this.context);
112114
delUnnecessaryCode()(this.context);
113115
addCustomCss(this.config, style)(this.context);
@@ -165,6 +167,7 @@ export class ProxyPageService {
165167
fixRoadmap(this.config)(this.context);
166168
fixImageSize()(this.context);
167169
fixFrameAllowFullscreen()(this.context);
170+
fixSVG(this.config)(this.context);
168171
fixTableBackground()(this.context);
169172
delUnnecessaryCode()(this.context);
170173
await addJiraPromise;

src/proxy-page/steps/fixSVG.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { ContextService } from '../../context/context.service';
2+
import { Step } from '../proxy-page.step';
3+
import * as cheerio from 'cheerio';
4+
import { ConfigService } from '@nestjs/config';
5+
import { parseString } from 'xml2js';
6+
7+
/**
8+
* ### Proxy page step to fix svg
9+
*
10+
* This module gets Cheerio to search all images ('img')
11+
* and set the source according to attribute 'data-encoded-xml'
12+
*
13+
* @param {ConfigService} config
14+
* @returns void
15+
*/
16+
export default (config: ConfigService): Step => {
17+
return (context: ContextService): void => {
18+
context.setPerfMark('fixSVG');
19+
const $ = context.getCheerioBody();
20+
const webBasePath = config.get('web.absoluteBasePath');
21+
22+
$('img').each((_index: number, elementImg: cheerio.Element) => {
23+
const imgDataEncodedXml = $(elementImg).attr('data-encoded-xml');
24+
if (imgDataEncodedXml) {
25+
const decodedXML = (
26+
decodeURIComponent(imgDataEncodedXml) ?? ''
27+
).replace(/\+/g, ' ');
28+
parseString(decodedXML, (err, xmlData) => {
29+
if (err) return;
30+
31+
/* get the actual SVG attributes */
32+
const { 'ac:width': width, 'ac:align': align } =
33+
xmlData['ac:image']['$'];
34+
const { 'ri:filename': filename } =
35+
xmlData['ac:image']['ri:attachment'].shift()['$'];
36+
37+
/* set the new imageSrc and width attributes */
38+
$(elementImg).attr(
39+
'src',
40+
`${webBasePath}/wiki/download/attachments/${context.getPageId()}/${filename}`,
41+
);
42+
$(elementImg).attr('width', width);
43+
const alignmentClass = {
44+
center: 'image-center',
45+
left: 'image-left',
46+
right: 'image-right',
47+
};
48+
if (align) {
49+
$(elementImg).addClass(alignmentClass[align]);
50+
}
51+
52+
/* clean off attributes we don't need anymore */
53+
$(elementImg).removeAttr('data-encoded-xml');
54+
$(elementImg).removeClass('transform-error');
55+
});
56+
}
57+
});
58+
59+
context.getPerfMeasure('fixSVG');
60+
};
61+
};

tests/unit/steps/fixSVG.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { ConfigService } from '@nestjs/config';
2+
import { ContextService } from '../../../src/context/context.service';
3+
import fixSVG from '../../../src/proxy-page/steps/fixSVG';
4+
import { createModuleRefForStep } from './utils';
5+
6+
describe('ConfluenceProxy / fixLinks', () => {
7+
let context: ContextService;
8+
let config: ConfigService;
9+
let webBasePath;
10+
11+
beforeEach(async () => {
12+
const moduleRef = await createModuleRefForStep();
13+
context = moduleRef.get<ContextService>(ContextService);
14+
config = moduleRef.get<ConfigService>(ConfigService);
15+
webBasePath = config.get('web.absoluteBasePath');
16+
17+
context.initPageContext('XXX', '123456', 'dark');
18+
});
19+
20+
it('should replace the img src, width and class', () => {
21+
const step = fixSVG(config);
22+
const example =
23+
'<html><head></head><body>' +
24+
'<img data-encoded-xml="%3Cac%3Aimage+ac%3Aalign%3D%22center%22+ac%3Alayout%3D%22center%22+ac%3Aoriginal-height%3D%22512%22+ac%3Aoriginal-width%3D%22512%22+ac%3Awidth%3D%22340%22%3E%3Cri%3Aattachment+ri%3Afilename%3D%22cloud-outline.svg%22+ri%3Aversion-at-save%3D%221%22+%2F%3E%3C%2Fac%3Aimage%3E">' +
25+
'</body></html>';
26+
context.setHtmlBody(example);
27+
step(context);
28+
const expected =
29+
'<html><head></head><body><div id="Content">' +
30+
`<img src="${webBasePath}/wiki/download/attachments/123456/cloud-outline.svg" width="340" class="image-center">` +
31+
'</div></body></html>';
32+
expect(context.getHtmlBody()).toEqual(expected);
33+
})
34+
});

0 commit comments

Comments
 (0)