imdone-core 1.54.10 → 2.0.0

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.
Files changed (71) hide show
  1. package/README.md +2 -2
  2. package/lib/cli.js +114 -0
  3. package/lib/index.js +23 -0
  4. package/package.json +48 -66
  5. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -37
  6. package/.github/workflows/ci.yml +0 -24
  7. package/.imdoneignore +0 -12
  8. package/.mocharc.json +0 -10
  9. package/.prettierrc +0 -6
  10. package/CHANGELOG.md +0 -160
  11. package/_navbar.md +0 -1
  12. package/cli.js +0 -74
  13. package/index-markdown.js +0 -55
  14. package/index.html +0 -184
  15. package/index.js +0 -1
  16. package/lib/ProjectContext.js +0 -42
  17. package/lib/adapters/date-time.js +0 -20
  18. package/lib/adapters/file-gateway.js +0 -127
  19. package/lib/adapters/git-download.js +0 -35
  20. package/lib/adapters/markdown.js +0 -168
  21. package/lib/adapters/parsers/DateLanguageParser.js +0 -10
  22. package/lib/adapters/parsers/content-transformer.js +0 -111
  23. package/lib/adapters/parsers/file/LocalFileParserFactory.js +0 -37
  24. package/lib/adapters/parsers/file/ReadStreamCodeParser.js +0 -126
  25. package/lib/adapters/parsers/file/ReadStreamMarkdownParser.js +0 -17
  26. package/lib/adapters/parsers/file/ReadStreamTaskParser.js +0 -104
  27. package/lib/adapters/parsers/function-parser.js +0 -29
  28. package/lib/adapters/parsers/task/CardContentParser.js +0 -376
  29. package/lib/adapters/parsers/task/CheckStyleTaskParser.js +0 -28
  30. package/lib/adapters/parsers/task/CodeStyleTaskParser.js +0 -24
  31. package/lib/adapters/parsers/task/HashStyleTaskParser.js +0 -27
  32. package/lib/adapters/parsers/task/MarkdownStyleTaskParser.js +0 -24
  33. package/lib/adapters/parsers/task/TaskParser.js +0 -38
  34. package/lib/adapters/readers/FileSystemProjectReader.js +0 -45
  35. package/lib/adapters/storage/config.js +0 -49
  36. package/lib/adapters/storage/tags.js +0 -30
  37. package/lib/adapters/yaml.js +0 -6
  38. package/lib/card.js +0 -300
  39. package/lib/config.js +0 -214
  40. package/lib/constants.js +0 -97
  41. package/lib/context/ApplicationContext.js +0 -32
  42. package/lib/context/StreamingApplicationContext.js +0 -31
  43. package/lib/controlers/SaveViewController.js +0 -10
  44. package/lib/default-settings.js +0 -37
  45. package/lib/domain/entities/FileProjectContext.js +0 -10
  46. package/lib/domain/entities/View.js +0 -30
  47. package/lib/domain/usecases/GetViewsUsecase.js +0 -50
  48. package/lib/domain/usecases/OpenProjectUsecase.js +0 -13
  49. package/lib/domain/usecases/SaveViewUsecase.js +0 -57
  50. package/lib/file.js +0 -1395
  51. package/lib/languages.js +0 -114
  52. package/lib/list.js +0 -105
  53. package/lib/migrate-config.js +0 -165
  54. package/lib/mixins/repo-fs-store.js +0 -424
  55. package/lib/mixins/repo-watched-fs-store.js +0 -195
  56. package/lib/plugins/archive-plugin.js +0 -70
  57. package/lib/plugins/default-board-actions-plugin.js +0 -56
  58. package/lib/plugins/default-board-properties-plugin.js +0 -101
  59. package/lib/plugins/epic-plugin.js +0 -176
  60. package/lib/plugins/extension-plugin.js +0 -87
  61. package/lib/plugins/persist-tags-plugin.js +0 -13
  62. package/lib/plugins/plugin-manager.js +0 -421
  63. package/lib/plugins/plugin-registry.js +0 -61
  64. package/lib/project-factory.js +0 -45
  65. package/lib/project.js +0 -911
  66. package/lib/repository.js +0 -1746
  67. package/lib/task.js +0 -798
  68. package/lib/tools.js +0 -250
  69. package/lib/usecases/get-imdone-url.js +0 -6
  70. package/lib/usecases/get-project-tags.js +0 -16
  71. package/lib/usecases/get-tasks-in-file.js +0 -29
package/lib/project.js DELETED
@@ -1,911 +0,0 @@
1
- const { Project } = require('imdone-api/lib/project')
2
- const PluginManager = require('./plugins/plugin-manager')
3
- const Repository = require('./repository')
4
- const _cloneDeep = require('lodash.clonedeep')
5
- const newCard = require('./card')
6
- const { JOURNAL_TYPE, CONFIG_DIR } = require('./constants')
7
- const _path = require('path')
8
- const moment = require('moment')
9
- const { getIsoDateWithOffset } = require('./adapters/date-time')
10
- const fileGateway = require('./adapters/file-gateway')
11
- const matter = require('gray-matter')
12
- const _isObject = require('lodash.isobject')
13
- const _isString = require('lodash.isstring')
14
- const _isFunction = require('lodash.isfunction')
15
- const exec = require('child_process').exec
16
- const fastSort = require('fast-sort/dist/sort.js')
17
- const Task = require('./task')
18
- const eol = require('eol')
19
- const { renderMarkdown, extractWikilinkTopics } = require('./adapters/markdown')
20
- const { getFunctionSignature } = require('./adapters/parsers/function-parser')
21
- const { format } = require('./adapters/parsers/content-transformer')
22
-
23
- function calculateTotals(lists = []) {
24
- const totals = {}
25
- lists.forEach((list) => {
26
- try {
27
- totals[list.name] = list.tasks.length
28
- } catch (e) {
29
- console.error('Error calculating list totals:', e)
30
- }
31
- })
32
- return totals
33
- }
34
-
35
- function onChange(project, event, data) {
36
- project.emit(event, data)
37
- }
38
-
39
- const EVENTS = [
40
- 'file.processed',
41
- 'files.found',
42
- 'file.update',
43
- 'file.saved',
44
- 'list.modified',
45
- 'config.update',
46
- ]
47
-
48
- module.exports = class WorkerProject extends Project {
49
- constructor(repo) {
50
- super()
51
- this.repo = repo
52
- this.innerFilter = ''
53
- this.fileGateway = fileGateway
54
- this._updatedAt = undefined
55
- this.data = {}
56
- this.dataKeys = []
57
- this.repo.project = this
58
- this.pluginManager = new PluginManager(this)
59
- }
60
-
61
- get configDir() {
62
- return _path.join(this.path, CONFIG_DIR)
63
- }
64
-
65
- get allTopics() {
66
- return [...this.repo.allTopics]
67
- }
68
-
69
- get allTags() {
70
- return [...this.repo.allTags]
71
- }
72
-
73
- get allContexts() {
74
- return [...this.repo.allContexts]
75
- }
76
-
77
- get allMeta() {
78
- return this.repo.allMeta
79
- }
80
-
81
- get lists() {
82
- return this.getLists({
83
- tasks: this.filteredCards,
84
- })
85
- }
86
-
87
- get allLists() {
88
- return this.getLists()
89
- }
90
-
91
- get filteredCards() {
92
- return this.getCards(this.filter)
93
- }
94
-
95
- get isoDateWithOffset() {
96
- return getIsoDateWithOffset()
97
- }
98
-
99
- get updatedAt() {
100
- return this._updatedAt
101
- }
102
-
103
- get files() {
104
- return this.repo.getFilePaths()
105
- }
106
-
107
- get config() {
108
- return this.repo.config
109
- }
110
-
111
- get defaultFilter() {
112
- return this.config && this.config.defaultFilter
113
- }
114
-
115
- set defaultFilter(filter) {
116
- this.config.defaultFilter = filter
117
- }
118
-
119
- get totals() {
120
- return calculateTotals(this.lists)
121
- }
122
-
123
- get path() {
124
- return this.repo.path
125
- }
126
-
127
- get name() {
128
- return this.repo.getDisplayName()
129
- }
130
-
131
- get doneList() {
132
- return this.config.getDoneList()
133
- }
134
-
135
- get boardActions() {
136
- return [
137
- ...this.pluginManager.getBoardActions(),
138
- ]
139
- }
140
-
141
- get filter() {
142
- return this.innerFilter
143
- }
144
-
145
- set filter(filter) {
146
- this.innerFilter = filter
147
- }
148
-
149
- async init(cb) {
150
- const promise = new Promise(async (resolve, reject) => {
151
- this.pluginManager.on('plugin-installed', () => this.emitUpdate())
152
- this.pluginManager.on('plugin-uninstalled', () => this.emitUpdate())
153
- this.pluginManager.on('plugins-reloaded', () => this.emitUpdate())
154
- await this.pluginManager.loadPlugins()
155
- this.data = await this.pluginManager.getBoardProperties()
156
- this.dataKeys = this.getDataKeys(this.data)
157
-
158
- console.log('data', this.data)
159
- console.log('dataKeys', this.dataKeys)
160
- EVENTS.forEach((event) => {
161
- this.repo.on(event, (data) => onChange(this, event, data))
162
- })
163
-
164
- this.repo.on('task.found', (task) => this.pluginManager.onTaskFound(task))
165
-
166
- this.repo.init(async (err, files) => {
167
- if (err) {
168
- if (cb) cb(err)
169
- else reject(err)
170
- return
171
- }
172
- this.pluginManager
173
- .startDevMode()
174
- .then(() => {
175
- if (cb) cb(null, files)
176
- else resolve(files)
177
- })
178
- .catch(err => {
179
- console.log('Error on starting dev mode', err)
180
- if (cb) cb(err)
181
- else reject(err)
182
- })
183
- })
184
- })
185
-
186
- if (!cb) return promise
187
- }
188
-
189
- initIndexes(allLists) {
190
- allLists.forEach((list) => {
191
- list.tasks.filter(list => !list.filter).forEach((task, index) => {
192
- task.index = index
193
- })
194
- })
195
- }
196
-
197
- // HACK To handle circular dependency issue with file
198
- // <!--
199
- // order:0
200
- // -->
201
- toJSON() {
202
- return { path: this.path }
203
- }
204
-
205
- getListsForImdoneJSON() {
206
- this.initIndexes(this.repo.getTasksByList())
207
- let allLists = this.getLists({
208
- populateFiltered: true,
209
- })
210
- const totals = calculateTotals(allLists)
211
- allLists = allLists.map((list) => {
212
- list = { ...list }
213
- list.tasks = list.tasks.map((card, index) => {
214
- card.interpretedContent = ''
215
- card.totals = totals
216
- return card
217
- })
218
- return list
219
- })
220
-
221
- return { allLists, totals }
222
- }
223
-
224
- getInitializedCards(allLists, totals) {
225
- return allLists
226
- .map((list) => list.tasks)
227
- .flat()
228
- .map(
229
- (card) => card.init(totals)
230
- )
231
- }
232
-
233
- async toImdoneJSON() {
234
- this.pluginManager.initDevMode()
235
- console.time('toJSON time')
236
- this._updatedAt = new Date()
237
- const { allLists , totals } = this.getListsForImdoneJSON()
238
-
239
- console.time('plugin onBeforeBoardUpdate time')
240
- await this.pluginManager.onBeforeBoardUpdate()
241
- console.timeEnd('plugin onBeforeBoardUpdate time')
242
-
243
- console.time('getBoardProperties time')
244
- const data = this.data = await this.pluginManager.getBoardProperties()
245
- console.timeEnd('getBoardProperties time')
246
-
247
- console.time('getDataKeys time')
248
- const dataKeys = this.dataKays = this.getDataKeys(data)
249
- console.timeEnd('getDataKeys time')
250
-
251
- console.time('card init time')
252
- const cards = this.getInitializedCards(allLists, totals)
253
- console.timeEnd('card init time')
254
-
255
- console.time('plugin onBoardUpdate time')
256
- await this.pluginManager.onBoardUpdate(allLists)
257
- console.timeEnd('plugin onBoardUpdate time')
258
-
259
- console.time('getTags time')
260
- const tags = this.getTags(cards)
261
- console.timeEnd('getTags time')
262
-
263
- console.time('getLists time')
264
- const lists = this.filter
265
- ? this.getRequestedLists(cards)
266
- : allLists
267
- console.timeEnd('getLists time')
268
-
269
- console.timeEnd('toJSON time')
270
- return {
271
- path: this.path,
272
- config: this.config,
273
- lists: lists,
274
- files: this.files,
275
- totals,
276
- totalCards: this.repo.getTasks().length,
277
- tags,
278
- allMeta: this.allMeta,
279
- allContexts: this.allContexts,
280
- allTags: this.allTags,
281
- filter: this.filter,
282
- defaultFilter: this.defaultFilter,
283
- actions: this.boardActions,
284
- plugins: this.pluginManager.getPlugins(),
285
- data,
286
- dataKeys,
287
- queryProps: this.getQueryProps(cards),
288
- }
289
- }
290
-
291
- getRequestedLists(cards) {
292
- return this.getLists({
293
- tasks: this.getCards(this.filter, cards),
294
- populateFiltered: true,
295
- }).map(list => {
296
- const configList = this.config.lists.find(l => l.name === list.name)
297
- list.hidden = configList && configList.hidden
298
- if (this.hideLists.includes(list.name)) {
299
- list.hidden = true
300
- }
301
- return list
302
- })
303
- }
304
-
305
- emit() {} // This is implemented in imdone UI worker
306
-
307
- emitUpdate() {
308
- this.emit('file.update')
309
- }
310
-
311
- destroy() {
312
- this.repo.destroy()
313
- if (this.pluginManager) this.pluginManager.destroyPlugins()
314
- }
315
-
316
- getDataKeys(data) {
317
- const keys = Object.keys(data).map((key) => {
318
- const value = data[key]
319
- if (_isFunction(value)) {
320
- key = getFunctionSignature(value)
321
- }
322
- return key
323
- })
324
- keys.forEach((key) => {
325
- const value = data[key]
326
- if (_isObject(value) && !Array.isArray(value)) {
327
- keys.push(...Object.keys(value).map((k) => `${key}.${k}`))
328
- }
329
- })
330
- return keys
331
- }
332
-
333
- removeList(list) {
334
- this.repo.removeList(list)
335
- }
336
-
337
- getLists(opts) {
338
- const {tasks = this.getDefaultFilteredCards(), populateFiltered = false} = opts || {}
339
- return Repository.getTasksByList(this.repo, tasks, true, populateFiltered)
340
- }
341
-
342
- getTaskQueryProps(props) {
343
- const queryProps = []
344
- if (props) {
345
- // add all properties of the task to the queryProps array
346
- Object.keys(props).forEach((key) => {
347
- if (key === 'data') return
348
- if (queryProps.includes(key)) return
349
-
350
- queryProps.push(key)
351
-
352
- if (_isObject(props[key])) {
353
- Object.keys(props[key]).forEach((subKey) => {
354
- if (!queryProps.includes(`${key}.${subKey}`))
355
- queryProps.push(`${key}.${subKey}`)
356
- })
357
- }
358
- })
359
- }
360
- return queryProps
361
- }
362
-
363
- getQueryProps(cards) {
364
- if (!cards) return []
365
- const queryProps = this.getTaskQueryProps(cards[0])
366
- return [
367
- ...new Set(queryProps),
368
- ...[...this.repo.metaKeys].map(key => `allMeta.${key}`),
369
- ...[...this.repo.metaKeys].map(key => `meta.${key}`),
370
- ...this.allTopics,
371
- ...this.allTags,
372
- ...this.allContexts,
373
- ]
374
- }
375
-
376
-
377
- getTags(cards = this.getDefaultFilteredCards()) {
378
- const tags = []
379
- cards.forEach((card) => {
380
- card.allTags.forEach((tag) => {
381
- let foundTag = tags.find(({name}) => tag === name)
382
- if (!foundTag) {
383
- foundTag = {name: tag, count: 1}
384
- tags.push(foundTag)
385
- }
386
- foundTag.count++
387
- })
388
- })
389
-
390
- return fastSort(tags).by([
391
- {desc: (tag) => tag.count},
392
- {asc: (tag) => tag.name},
393
- ])
394
- }
395
-
396
- getDefaultFilteredCards() {
397
- return this.filterCards(this.repo.getTasks(), this.defaultFilter)
398
- }
399
-
400
- getAllCards(filter) {
401
- const allTasks = this.repo.getTasks()
402
- return this.getCards(filter, allTasks)
403
- }
404
-
405
- getCards(filter, cards = this.getDefaultFilteredCards()) {
406
- cards = filter ? this.filterCards(cards, filter) : cards
407
- return cards
408
- }
409
-
410
- filterCards(cards, filter) {
411
- this.hideLists = []
412
- const {
413
- result,
414
- hideLists
415
- } = Repository.filterCards(cards, filter)
416
- this.hideLists = hideLists
417
- return result
418
- }
419
-
420
- async addMetadata(task, key, value) {
421
- if (task.hasMetaData(key, value)) return
422
- if (!task.allMeta[key]) task.allMeta[key] = []
423
- task.allMeta[key].push(value)
424
- if (!/^['"]/.test(value) && /\s/.test(value)) value = `"${value}"`
425
- const metaData = `${key}${this.config.getMetaSep()}${value}`
426
- const content = task.addToLastCommentInContent(
427
- task.content,
428
- metaData,
429
- this.config.isMetaNewLine()
430
- )
431
- return await this.updateCardContent(task, content)
432
- }
433
-
434
- async removeMetadata(task, key, value) {
435
- if (!task.meta[key]) return
436
- const file = this.getFileForTask(task)
437
- return this.updateCardContent(
438
- task,
439
- file.removeMetaData(task.content, key, value)
440
- )
441
- }
442
-
443
- async addTag(task, tag) {
444
- if (task.tags.includes(tag)) return
445
- task.allTags.push(tag)
446
- const tagContent = `${this.config.getTagPrefix()}${tag}`
447
- const content = task.addToLastCommentInContent(
448
- task.content,
449
- tagContent,
450
- this.config.isMetaNewLine()
451
- )
452
- return this.updateCardContent(task, content)
453
- }
454
-
455
- async removeTag(task, tag) {
456
- if (!task.tags.includes(tag)) return
457
- const tagContent = new RegExp(
458
- `\\${this.config.getTagPrefix()}${tag}\\s`,
459
- 'g'
460
- )
461
- console.log('removeTag regex:', tagContent)
462
- const content = task.content.replace(tagContent, '')
463
- return this.updateCardContent(task, content)
464
- }
465
-
466
- async moveTask(task, newList, newPos) {
467
- return new Promise((resolve, reject) => {
468
- this.repo.moveTask({task, newList, newPos}, (err) => {
469
- if (err) return reject(err)
470
- resolve()
471
- })
472
- })
473
- }
474
-
475
- getFile(filePath) {
476
- return this.repo.getFile(filePath)
477
- }
478
-
479
- getFileForTask(task) {
480
- return this.repo.getFileForTask(task)
481
- }
482
-
483
- rollBackFileForTask(task) {
484
- return this.getFileForTask(task).rollback().extractTasks(this.config)
485
- }
486
-
487
- async updateCardContent(task, content) {
488
- return new Promise((resolve, reject) => {
489
- this.repo.modifyTaskFromContent(task, content, (err, file) => {
490
- if (err) {
491
- console.error(err)
492
- reject(err)
493
- }
494
- this.emitUpdate()
495
- resolve(file)
496
- })
497
- })
498
- }
499
-
500
- async snackBar({ message, type, duration }) {
501
- this.emit('project.snackBar', { message, type, duration })
502
- }
503
-
504
- async toast({ message, type, duration }) {
505
- this.emit('project.toast', { message, type, duration })
506
- }
507
-
508
- filterLists(filter, lists = this.lists) {
509
- return lists.map((list) => {
510
- let newList = { ...list, tasks: [] }
511
- newList = _cloneDeep(newList)
512
- newList.tasks = Repository.query(list.tasks, filter)
513
- return newList
514
- })
515
- }
516
-
517
- async copyToClipboard(text, message) {this.files
518
- this.emit('project.copyToClipboard', { text, message })
519
- }
520
-
521
- async openUrl(url) {
522
- this.emit('project.openUrl', url)
523
- }
524
-
525
- // TODO For methods that emit, they should still do the thing without imdone UI
526
- // <!--
527
- // #urgent
528
- // #important
529
- // order:-195
530
- // -->
531
- async openPath(path) {
532
- this.emit('project.openPath', path)
533
- }
534
-
535
- saveFile(content, file) {
536
- const filePath = this.getFullPath(file)
537
- this.emit('project.saveFile', { file: filePath, content })
538
- }
539
-
540
- /*
541
- * @param {Object} opts - The options object
542
- * @param {String} opts.list - The list name to add the card to
543
- * @param {String} opts.path - The path to the file to add the card to
544
- * @param {String} opts.template - The template to use for the new card
545
- * @param {String} opts.title - The title of the new card
546
- * @param {String} opts.comments - The comments to add to the new card
547
- * @param {Boolean} opts.emit - Whether to emit the new card event
548
- */
549
- async newCard({ list, path, template, title, comments, emit = true }) {
550
- if (!path || !_path.parse(path).ext) path = this.getNewCardsFile({title})
551
- path = this.getFullPath(path)
552
-
553
- const { isFile, isDirectory } = fileGateway.preparePathForWriting(path)
554
-
555
- if (!template) template = this.getNewCardTemplate(path, isFile)
556
-
557
- const boardData = await this.pluginManager.getBoardProperties();
558
- template = format(template, boardData)
559
-
560
- if (comments) template = Task.addToLastCommentInContent(template, comments, this.config.isMetaNewLine())
561
-
562
- let relativePath = _path.relative(this.path, path)
563
- if (isDirectory) relativePath += _path.sep
564
-
565
- const data = {
566
- list,
567
- path,
568
- relativePath,
569
- template,
570
- isDirectory,
571
- }
572
- if (emit) this.emit('project.newCard', data)
573
- return data
574
- }
575
-
576
- async addCardToFile(opts) {
577
- return this.addTaskToFile(opts)
578
- }
579
-
580
- async addTaskToFile({path, list = this.config.getDefaultList(), content, tags = [], contexts = [], meta = [], useCardTemplate = false}) {
581
- const pluginMods = await this.pluginManager.onBeforeAddTask({path, list, meta, tags, contexts, content, useCardTemplate})
582
- path = pluginMods.path
583
- content = pluginMods.content
584
- meta = pluginMods.meta
585
- tags = pluginMods.tags
586
- contexts = pluginMods.contexts
587
- const cardData = await this.newCard({list, path, title: eol.split(content)[0], emit: false})
588
- const filePath = cardData.path
589
- if (useCardTemplate) content += cardData.template
590
-
591
- const boardData = await this.pluginManager.getBoardProperties();
592
- const data = { ...boardData, ...cardData}
593
- content = format(content, data)
594
-
595
- content = this.addTagsToContent(tags, content)
596
- content = this.addContextsToContent(contexts, content)
597
- content = this.addMetaToContent(meta, content)
598
-
599
- return new Promise((resolve, reject) => {
600
- this.repo.addTaskToFile(filePath, list, content, (err, file, task) => {
601
- if (err) return reject(err)
602
- resolve({ file, task })
603
- })
604
- })
605
- }
606
-
607
- addMetaToContent(meta, content) {
608
- if (meta && meta.length > 0) {
609
- let metaContent = ''
610
- meta.forEach(({ key, value }) => {
611
- if (!/^['"]/.test(value) && /\s/.test(value)) value = `"${value}"`
612
- const metaData = `${key}${this.repo.config.getMetaSep()}${value}`
613
- if (content.includes(metaData)) return
614
- const existingMetadata = Task.parseMetaData(this.config, content)[key]
615
- if (existingMetadata) {
616
- content = Task.removeMetaData({
617
- config: this.config,
618
- content,
619
- key,
620
- value: existingMetadata[0],
621
- })
622
- }
623
- const spaceOrNewLine = this.config.isMetaNewLine() ? '\n' : ' '
624
- metaContent = `${metaContent}${spaceOrNewLine}${metaData}`
625
- })
626
- content = Task.addToLastCommentInContent(content, metaContent.trim(), this.config.isMetaNewLine())
627
- }
628
- return content
629
- }
630
-
631
- addContextsToContent(contexts, content) {
632
- if (contexts && contexts.length > 0) {
633
- let contextContent = ''
634
- contexts.forEach(context => {
635
- const contextWithPrefix = `@${context}`
636
- if (content.includes(contextWithPrefix)) return
637
- contextContent = `${contextContent} @${context}`
638
- })
639
- content = Task.addToLastCommentInContent(content, contextContent.trim(), this.config.isMetaNewLine())
640
- }
641
- return content
642
- }
643
-
644
- addTagsToContent(tags, content) {
645
- if (tags && tags.length > 0) {
646
- let tagContent = ''
647
- tags.forEach(tag => {
648
- const tagWithPrefix = `${this.config.getTagPrefix()}${tag}`
649
- if (content.includes(tagWithPrefix)) return
650
- tagContent = `${tagContent} ${tagWithPrefix}`
651
- })
652
- content = Task.addToLastCommentInContent(content, tagContent.trim(), this.config.isMetaNewLine())
653
- }
654
- return content
655
- }
656
-
657
- async deleteTask(task) {
658
- return new Promise((resolve, reject) => {
659
- this.repo.deleteTask(task, async (err) => {
660
- if (err) return reject(err)
661
- await this.pluginManager.onAfterDeleteTask(task)
662
- resolve()
663
- })
664
- })
665
- }
666
-
667
- async deleteTasks(tasks) {
668
- return new Promise((resolve, reject) => {
669
- this.repo.deleteTasks(tasks, async (err) => {
670
- if (err) return reject(err)
671
- resolve()
672
- })
673
- })
674
-
675
- }
676
-
677
- setFilter(filter) {
678
- this.emit('project.filter', { filter })
679
- }
680
-
681
- getNewCardTemplate(file, isFile) {
682
- const frontMatter = this.getNewCardFileFrontMatter(file, isFile)
683
- const card = newCard(
684
- {
685
- frontMatter,
686
- repoId: this.path,
687
- text: '',
688
- source: { path: this.getNewCardsFile() },
689
- },
690
- this,
691
- true
692
- )
693
- card.init(this.totals)
694
- return card.formatContent(frontMatter.template).content
695
- }
696
-
697
- getNewCardFileFrontMatter(file, isFile) {
698
- let fileContent = ''
699
- if (file && isFile) {
700
- fileContent = fileGateway.readFileSync(file, 'utf8')
701
- } else if (JOURNAL_TYPE.NEW_FILE === this.config.journlType) {
702
- fileContent = this.config.journalTemplate
703
- }
704
- let { props, computed, template } = this.config.settings.cards
705
- const frontMatter = matter(fileContent).data || {}
706
- if (!_isObject(props)) props = {}
707
- if (!_isObject(computed)) computed = {}
708
- if (!_isString(template)) template = ''
709
- if (!_isObject(frontMatter.props)) frontMatter.props = {}
710
- if (!_isObject(frontMatter.computed)) frontMatter.computed = {}
711
- if (!_isString(frontMatter.template)) frontMatter.template = template
712
- props = {
713
- ...props,
714
- ...frontMatter.props,
715
- now: new Date().toDateString(),
716
- totals: this.totals,
717
- }
718
- computed = { ...computed, ...frontMatter.computed }
719
- return {
720
- ...frontMatter,
721
- props,
722
- computed,
723
- template: frontMatter.template,
724
- }
725
- }
726
-
727
- getNewCardsFile(opts = { relPath: false }) {
728
- const { relPath, title } = opts
729
- if (!this.config) return ''
730
- const filePath = this.appendNewCardsTo(title)
731
- if (!filePath) return ''
732
- return filePath
733
- }
734
-
735
- appendNewCardsTo(title) {
736
- const journalType = this.config.journalType
737
- if (journalType === JOURNAL_TYPE.SINGLE_FILE)
738
- return this.getFullPath(this.config.appendNewCardsTo)
739
- if (journalType === JOURNAL_TYPE.FOLDER)
740
- return this.getJournalFile().fullFilePath
741
- if (journalType === JOURNAL_TYPE.NEW_FILE) {
742
- if (!title) return this.getFullPath(this.config.journalPath)
743
- const fileName = `${this.sanitizeFileName(title)}.md`
744
- const fileFolder = this.getFullPath(this.config.journalPath)
745
- const filePath = _path.join(fileFolder, fileName)
746
-
747
- return filePath
748
- }
749
- }
750
-
751
- sanitizeFileName(fileName) {
752
- return fileGateway.sanitizeFileName(fileName, this.config.replaceSpacesWith)
753
- }
754
-
755
- getJournalFile() {
756
- const month = moment().format('YYYY-MM')
757
- const today = moment().format('YYYY-MM-DD')
758
- const journalPath = this.config.journalPath
759
- const folderPath = _path.join(journalPath, month)
760
- const journalFilePrefix = this.config.journalFilePrefix
761
- const journalFileSuffix = this.config.journalFileSuffix
762
- const filePath = _path.join(
763
- folderPath,
764
- `${journalFilePrefix}${today}${journalFileSuffix}.md`
765
- )
766
-
767
- const fullFilePath = this.getFullPath(filePath)
768
- return { filePath, fullFilePath }
769
- }
770
-
771
- getFullPath(...path) {
772
- if (_path.join(...path).startsWith(this.path)) {
773
- return _path.join(...path)
774
- }
775
- return _path.join.apply({}, [this.path, ...path])
776
- }
777
-
778
- performCardAction(action, task) {
779
- task = this.repo.getTask(task.id)
780
- try {
781
- action = JSON.parse(action)
782
- } catch (e) {
783
- //
784
- }
785
- if (action.plugin) return this.pluginManager.performCardAction(action, task)
786
-
787
- const actionFunction = task.getCardActions()[action.index].action
788
- // TODO These card actions should be removed
789
- // #imdone-1.54.0
790
- // <!--
791
- // order:-175
792
- // -->
793
- const actions = {
794
- filter: (filter) => {
795
- this.setFilter(filter)
796
- },
797
- newCard: async (list, path) => {
798
- if (task.source.lang !== 'text')
799
- return this.alert('Unable to append cards in code files.')
800
- if (path) path = this.getFullPath(path)
801
- await this.newCard({ list, path })
802
- },
803
- alert: (msg) => {
804
- this.toast({ message: msg })
805
- },
806
- openUrl: (url) => {
807
- this.openUrl(url)
808
- },
809
- execCommand: (cmd) => {
810
- return this.exec(cmd)
811
- },
812
- copy: (content, msg) => {
813
- this.copyToClipboard(content, msg)
814
- },
815
- }
816
- const actionThis = {
817
- ...task.data,
818
- ...task.desc,
819
- actions,
820
- }
821
-
822
- try {
823
- console.log('actionFunction:', actionFunction)
824
- const func = new Function(`return ${actionFunction}`)()
825
- func.apply(actionThis)
826
- } catch (e) {
827
- console.error(e)
828
- console.log('action:', actionFunction)
829
- console.log('this:', actionThis)
830
- }
831
- }
832
- performBoardAction(action, task) {
833
- if (task) task = this.repo.getTask(task.id)
834
- if (action && action.plugin)
835
- return this.pluginManager.performBoardAction(action, task)
836
- const actions = {
837
- filter: (filter) => {
838
- this.setFilter(filter)
839
- },
840
- alert: (msg) => {
841
- this.toast({ message: msg })
842
- },
843
- saveFile: ({ file, content }) => {
844
- this.saveFile(content, file)
845
- },
846
- mailto: ({ subject, body, to, cc, bcc }) => {
847
- const params = []
848
- if (subject) params.push(`subject=${encodeURIComponent(subject)}`)
849
- if (body) params.push(`body=${encodeURIComponent(body)}`)
850
- if (cc) params.push(`cc=${encodeURIComponent(cc)}`)
851
- if (bcc) params.push(`bcc=${encodeURIComponent(bcc)}`)
852
- const url = `mailto:${to}?${params.join('&')}`
853
- console.log('opening email with:', url)
854
- this.openUrl(url)
855
- },
856
- copy: (content, message) => {
857
- this.copyToClipboard(content, message || 'Your content has been copied')
858
- },
859
- updateCard: (task, content) => {
860
- this.updateCardContent(task, content)
861
- },
862
- }
863
- const actionFunction = this.boardActions[action.index].action
864
- const actionThis = { cards: this.lists, ...actions }
865
- try {
866
- actionFunction.apply(actionThis)
867
- } catch (err) {
868
- console.error(err)
869
- console.log('action:', actionFunction)
870
- console.log('this:', actionThis)
871
- }
872
- }
873
-
874
- exec(cmd) {
875
- return new Promise((resolve, reject) => {
876
- exec(cmd, (error, stdout, stderr) => {
877
- if (stderr) console.warn('cmd stderr:', stderr)
878
- if (error) return reject(error)
879
- resolve(stdout)
880
- })
881
- })
882
- }
883
-
884
- installPlugin({ name, version }) {
885
- return this.pluginManager.installPlugin({ name, version })
886
- }
887
-
888
- uninstallPlugin(name) {
889
- return this.pluginManager.uninstallPlugin(name)
890
- }
891
-
892
- refresh() {
893
- return new Promise((resolve, reject) => {
894
- this.repo.refresh((err) => {
895
- if (err) return reject(err)
896
- ;(async () => {
897
- await this.pluginManager.reloadPlugins()
898
- resolve()
899
- })()
900
- })
901
- })
902
- }
903
-
904
- renderMarkdown(content, filePath) {
905
- return renderMarkdown(content, filePath || this.path)
906
- }
907
-
908
- extractWikilinkTopics(markdown) {
909
- return extractWikilinkTopics(markdown)
910
- }
911
- }