Skip to content

Commit 9036020

Browse files
authored
Merge pull request #3 from sonnenkern/develop
New option --resolve-references
2 parents 175333f + 5fabd53 commit 9036020

9 files changed

+295
-42
lines changed

.eslintrc.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"extends": ["eslint:recommended"],
33
"parserOptions": {
4-
"ecmaVersion": 2017
4+
"ecmaVersion": 9
55
},
66
"rules": {
77
"require-atomic-updates": "warn"

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ npm install quip-export
7070
-z, --zip Zip export files
7171
--embedded-styles Embedded in each document stylesheet
7272
--embedded-images Embedded images
73+
--resolve-references Resolves references to other Quip documents and folders to a proper relative path
7374
--debug Extended logging
7475
```
7576

__tests__/app.test.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ function initApp() {
5050
destination: 'c:/temp',
5151
token: 'TOKEN',
5252
['embedded-styles']: true,
53-
['embedded-images']: true
53+
['embedded-images']: true,
54+
['resolve-references']: true
5455
});
5556
QuipService.mockImplementation(() => {
5657
return {
@@ -163,7 +164,8 @@ describe('main() tests', () => {
163164
{
164165
documentTemplate,
165166
documentCSS: documentCSS,
166-
embeddedImages: true
167+
embeddedImages: true,
168+
resolveReferences: true
167169
}
168170
);
169171
expect(app.quipProcessor.setLogger).toHaveBeenCalledWith(app.Logger);

app.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,8 @@ class App {
160160
{
161161
documentTemplate,
162162
documentCSS: this.cliArguments['embedded-styles']? documentCSS : '',
163-
embeddedImages: this.cliArguments['embedded-images']
163+
embeddedImages: this.cliArguments['embedded-images'],
164+
resolveReferences: this.cliArguments['resolve-references']
164165
});
165166
this.quipProcessor.setLogger(this.Logger);
166167

lib/QuipProcessor.js

+96-25
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class QuipProcessor {
2222
this.threadsTotal = 0;
2323
this.foldersTotal = 0;
2424

25+
this.referencesMap = new Map();
26+
2527
this.phase = 'STOP'; //START, STOP, ANALYSIS, EXPORT
2628

2729
this.quipService = new QuipService(quipToken, options.quipApiURL);
@@ -45,21 +47,21 @@ class QuipProcessor {
4547
this.start = true;
4648
this.threadsProcessed = 0;
4749

48-
const user = await this.quipService.getUser();
49-
if(!user) {
50+
this.quipUser = await this.quipService.getUser();
51+
if(!this.quipUser) {
5052
this.logger.error("Can't load the User");
5153
this.stopExport();
5254
return;
5355
}
5456

5557
let folderIdsToExport = [
56-
//user.desktop_folder_id,
57-
//user.archive_folder_id,
58-
//user.starred_folder_id,
59-
user.private_folder_id,
60-
//user.trash_folder_id
61-
...user.shared_folder_ids,
62-
...user.group_folder_ids
58+
//this.quipUser.desktop_folder_id,
59+
//this.quipUser.archive_folder_id,
60+
//this.quipUser.starred_folder_id,
61+
this.quipUser.private_folder_id,
62+
//this.quipUser.trash_folder_id
63+
...this.quipUser.shared_folder_ids,
64+
...this.quipUser.group_folder_ids
6365
];
6466

6567
if(folderIds && folderIds.length > 0) {
@@ -81,34 +83,84 @@ class QuipProcessor {
8183
this.phase = phase;
8284
}
8385

84-
_getMatches(text, regexp/*, threadId*/) {
86+
_getMatches(text, regexp) {
8587
const matches = [];
8688

8789
let regexpResult = regexp.exec(text);
8890

8991
while (regexpResult != null) {
90-
//if(regexpResult[2] === threadId) {
91-
matches.push({
92-
replacement: regexpResult[1],
93-
threadId: regexpResult[2],
94-
blobId: regexpResult[3],
95-
fileName: regexpResult[4]
96-
});
97-
//}
92+
matches.push({
93+
replacement: regexpResult[1],
94+
threadId: regexpResult[2],
95+
blobId: regexpResult[3],
96+
fileName: regexpResult[4]
97+
});
9898
regexpResult = regexp.exec(text);
9999
}
100100

101101
return matches;
102102
}
103103

104+
async _resolveReferences(html, pathDeepness) {
105+
//look up for document or folder references
106+
const regexp = new RegExp(`href=\\"(${this.quipUser.url}/([\\w-]+))\\"`, 'gim');
107+
const matchesReference = this._getMatches(html, regexp);
108+
109+
//replace references to documents
110+
if(this.options.resolveReferences) {
111+
for(const reference of matchesReference) {
112+
html = await this._processReference(html, reference, pathDeepness);
113+
}
114+
}
115+
116+
return html;
117+
}
118+
119+
async _processReference(html, reference, pathDeepness) {
120+
let referencedObject = this.referencesMap.get(reference.threadId);
121+
122+
let path, folder, title;
123+
if(referencedObject) {
124+
path = referencedObject.path;
125+
folder = referencedObject.folder;
126+
title = referencedObject.title;
127+
}
128+
129+
if(!path) {
130+
//correct path for threads
131+
const referencedThread = await this.quipService.getThread(reference.threadId);
132+
if(!referencedThread) {
133+
return html;
134+
}
135+
referencedObject = this.referencesMap.get(referencedThread.thread.id);
136+
if(referencedObject) {
137+
path = referencedObject.path;
138+
folder = false;
139+
title = referencedThread.thread.title;
140+
}
141+
}
142+
143+
if(!path || !title) {
144+
return html;
145+
}
146+
147+
if(folder) {
148+
path = '../'.repeat(pathDeepness) + path + title;
149+
} else {
150+
path = '../'.repeat(pathDeepness) + path + sanitizeFilename(title.trim()) + '.html';
151+
}
152+
153+
return html.replace(reference.replacement, path);
154+
}
155+
104156
async _processDocumentThread(quipThread, path) {
105157
//look up for images in html
106158
let regexp = new RegExp("src='(/blob/([\\w-]+)/([\\w-]+))'", 'gim');
107-
const matchesImg = this._getMatches(quipThread.html, regexp, quipThread.thread.id);
159+
const matchesImg = this._getMatches(quipThread.html, regexp);
108160

109161
//look up for links in html
110162
regexp = new RegExp('href=\\"(.*/blob/(.+)/(.+)\\?name=(.+))\\"', 'gim');
111-
const matchesLink = this._getMatches(quipThread.html, regexp, quipThread.thread.id);
163+
const matchesLink = this._getMatches(quipThread.html, regexp);
112164

113165
const pathDeepness = path.split("/").length-1;
114166
let wrappedHtml = quipThread.html;
@@ -126,7 +178,7 @@ class QuipProcessor {
126178

127179
if(this.documentTemplate) {
128180
//wrap html code
129-
wrappedHtml = ejs.render(this.documentTemplate,documentRenderOptions);
181+
wrappedHtml = ejs.render(this.documentTemplate, documentRenderOptions);
130182
}
131183

132184
//replace blob references for links
@@ -139,6 +191,11 @@ class QuipProcessor {
139191
wrappedHtml = await this._processFile(wrappedHtml, image, path, this.options.embeddedImages);
140192
}
141193

194+
//replace references to documents
195+
if(this.options.resolveReferences) {
196+
wrappedHtml = await this._resolveReferences(wrappedHtml, pathDeepness);
197+
}
198+
142199
this.saveCallback(wrappedHtml, sanitizeFilename(`${quipThread.thread.title.trim()}.html`), 'THREAD', path);
143200
}
144201

@@ -273,19 +330,33 @@ class QuipProcessor {
273330
});
274331
}
275332

276-
async _countThreadsAndFolders(quipFolder) {
333+
async _countThreadsAndFolders(quipFolder, path) {
277334
const threadIds = [];
278335
const folderIds = [];
279336

337+
if(this.options.resolveReferences) {
338+
this.referencesMap.set(quipFolder.folder.id, {
339+
path,
340+
folder: true,
341+
title: quipFolder.folder.title
342+
});
343+
}
344+
280345
if(!quipFolder.children || quipFolder.children.length === 0) {
281346
return;
282347
}
283348

349+
const pathForChildren = `${path}${quipFolder.folder.title}/`;
350+
284351
for(const index in quipFolder.children) {
285352
const quipChild = quipFolder.children[index];
286-
287353
if(quipChild.thread_id) { //thread
288354
threadIds.push(quipChild.thread_id);
355+
if(this.options.resolveReferences) {
356+
this.referencesMap.set(quipChild.thread_id, {
357+
path: pathForChildren
358+
});
359+
}
289360
} else if(quipChild.folder_id) { //folder
290361
folderIds.push(quipChild.folder_id);
291362
}
@@ -308,7 +379,7 @@ class QuipProcessor {
308379
}
309380

310381
for(const index in childFolders) {
311-
await this._countThreadsAndFolders(childFolders[index]);
382+
await this._countThreadsAndFolders(childFolders[index], pathForChildren);
312383
}
313384
}
314385

@@ -326,7 +397,7 @@ class QuipProcessor {
326397
}
327398

328399
for(const index in quipFolders) {
329-
await this._countThreadsAndFolders(quipFolders[index]);
400+
await this._countThreadsAndFolders(quipFolders[index], "");
330401
}
331402

332403
this._changePhase('EXPORT');

0 commit comments

Comments
 (0)