obsidian-confluence 5.6.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.
- package/CHANGELOG.md +1002 -0
- package/README.md +72 -0
- package/docs/screenshots/commands.png +0 -0
- package/docs/screenshots/ribbon.png +0 -0
- package/docs/screenshots/settings.png +0 -0
- package/esbuild.config.mjs +55 -0
- package/package.json +33 -0
- package/src/CompletedModal.tsx +160 -0
- package/src/ConfluencePerPageForm.tsx +525 -0
- package/src/ConfluenceSettingTab.ts +124 -0
- package/src/MyBaseClient.ts +220 -0
- package/src/adaptors/obsidian.ts +146 -0
- package/src/custom.d.ts +19 -0
- package/src/main.ts +501 -0
- package/tsconfig.json +13 -0
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Obsidian Confluence Integration Plugin
|
|
2
|
+
|
|
3
|
+
[](https://api.securityscorecards.dev/projects/github.com/markdown-confluence/markdown-confluence)
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2022 Atlassian Pty Ltd
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2022 Atlassian US, Inc.
|
|
8
|
+
|
|
9
|
+
`Obsidian Confluence Integration Plugin` is an open-source plugin for [Obsidian.md](https://obsidian.md/) that allows you to publish markdown content from Obsidian to [Atlassian Confluence](https://www.atlassian.com/software/confluence). It supports [Obsidian markdown extensions](https://help.obsidian.md/How+to/Format+your+notes) for richer content and includes a CLI for pushing markdown files from the command line. Currently, the plugin only supports Atlassian Cloud instances.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Publish Obsidian notes to Atlassian Confluence
|
|
14
|
+
- Support for Obsidian markdown extensions
|
|
15
|
+
- CLI for pushing markdown files from disk
|
|
16
|
+
- Commands and ribbon icon for easy access
|
|
17
|
+
|
|
18
|
+
## Issues
|
|
19
|
+
Please log issues to https://github.com/markdown-confluence/markdown-confluence/issues as this is where the code is being developed.
|
|
20
|
+
|
|
21
|
+
## Getting Started
|
|
22
|
+
|
|
23
|
+
1. Install the `confluence-integration` plugin from Obsidian's community plugins browser.
|
|
24
|
+
2. Open the plugin settings and configure the following fields:
|
|
25
|
+
|
|
26
|
+
- `Confluence Base URL`: The base URL of your Atlassian Confluence instance (e.g., `https://your-domain.atlassian.net`)
|
|
27
|
+
- `Confluence Parent Id`: The Confluence page ID where your notes will be published as child pages
|
|
28
|
+
- `Atlassian User Name`: Your Atlassian account's email address
|
|
29
|
+
- `Atlassian API Token`: Your Atlassian API token. You can generate one from your [Atlassian Account Settings](https://id.atlassian.com/manage-profile/security/api-tokens).
|
|
30
|
+
- `Folder To Publish`: The name of the folder in Obsidian containing the notes you want to publish (default: "Confluence Pages")
|
|
31
|
+
|
|
32
|
+

|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### Ribbon Icon
|
|
37
|
+
|
|
38
|
+
Click the cloud icon in the ribbon to publish the notes from the configured folder to Confluence.
|
|
39
|
+
|
|
40
|
+

|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
### Commands
|
|
44
|
+
|
|
45
|
+
Use the command palette (`Ctrl/Cmd + P`) to execute the "Publish All to Confluence" command, which publishes all the notes from the configured folder to Confluence.
|
|
46
|
+
|
|
47
|
+

|
|
48
|
+
|
|
49
|
+
### connie-publish Frontmatter
|
|
50
|
+
|
|
51
|
+
To publish pages outside the `folderToPublish`, add the `connie-publish` YAML frontmatter to your notes:
|
|
52
|
+
|
|
53
|
+
```yaml
|
|
54
|
+
---
|
|
55
|
+
connie-publish: true
|
|
56
|
+
---
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Example Workflow
|
|
60
|
+
1. Install and configure the `confluence-integration` plugin.
|
|
61
|
+
2. Create a folder in your Obsidian vault named "Confluence Pages" (or the folder name you specified in the settings).
|
|
62
|
+
3. Add notes to this folder or add the connie-publish frontmatter to other notes.
|
|
63
|
+
4. Click the cloud icon in the ribbon or use the "Publish All to Confluence" command to publish your notes to Confluence.
|
|
64
|
+
|
|
65
|
+
### Contributing
|
|
66
|
+
Contributions are welcome! If you have a feature request, bug report, or want to improve the plugin, please open an issue or submit a pull request on the GitHub repository.
|
|
67
|
+
|
|
68
|
+
### License
|
|
69
|
+
This project is licensed under the [Apache 2.0](https://github.com/markdown-confluence/markdown-confluence/blob/main/LICENSE) License.
|
|
70
|
+
|
|
71
|
+
## Disclaimer:
|
|
72
|
+
The Apache license is only applicable to the Obsidian Confluence Integration (“Integration“), not to any third parties' services, websites, content or platforms that this Integration may enable you to connect with. In another word, there is no license granted to you by the above identified licensor(s) to access any third-party services, websites, content, or platforms. You are solely responsible for obtaining licenses from such third parties to use and access their services and to comply with their license terms. Please do not disclose any passwords, credentials, or tokens to any third-party service in your contribution to this Obsidian Confluence Integration project.”
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import esbuild from "esbuild";
|
|
2
|
+
import process from "process";
|
|
3
|
+
import builtins from 'builtin-modules'
|
|
4
|
+
import { writeFileSync } from 'fs';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const banner =
|
|
8
|
+
`/*
|
|
9
|
+
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
|
10
|
+
if you want to view the source, please visit the github repository of this plugin
|
|
11
|
+
*/
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
const prod = (process.argv[2] === 'production');
|
|
15
|
+
|
|
16
|
+
const buildConfig = {
|
|
17
|
+
banner: {
|
|
18
|
+
js: banner,
|
|
19
|
+
},
|
|
20
|
+
entryPoints: ['src/main.ts'],
|
|
21
|
+
bundle: true,
|
|
22
|
+
external: [
|
|
23
|
+
'obsidian',
|
|
24
|
+
'electron',
|
|
25
|
+
'@codemirror/autocomplete',
|
|
26
|
+
'@codemirror/collab',
|
|
27
|
+
'@codemirror/commands',
|
|
28
|
+
'@codemirror/language',
|
|
29
|
+
'@codemirror/lint',
|
|
30
|
+
'@codemirror/search',
|
|
31
|
+
'@codemirror/state',
|
|
32
|
+
'@codemirror/view',
|
|
33
|
+
'@lezer/common',
|
|
34
|
+
'@lezer/highlight',
|
|
35
|
+
'@lezer/lr',
|
|
36
|
+
...builtins
|
|
37
|
+
],
|
|
38
|
+
format: 'cjs',
|
|
39
|
+
target: 'chrome106',
|
|
40
|
+
logLevel: "info",
|
|
41
|
+
sourcemap: prod ? false : 'inline',
|
|
42
|
+
treeShaking: true,
|
|
43
|
+
outdir: prod ? 'dist' : '../../dev-vault/.obsidian/plugins/obsidian-confluence',
|
|
44
|
+
mainFields: ['module', 'main'],
|
|
45
|
+
minify: true,
|
|
46
|
+
metafile: true,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
if (prod) {
|
|
50
|
+
const buildResult = await esbuild.build(buildConfig);
|
|
51
|
+
writeFileSync("./dist/meta.json", JSON.stringify(buildResult.metafile));
|
|
52
|
+
} else {
|
|
53
|
+
const context = await esbuild.context(buildConfig);
|
|
54
|
+
await context.watch();
|
|
55
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "obsidian-confluence",
|
|
3
|
+
"version": "5.6.0",
|
|
4
|
+
"description": "This library allows you to publish your notes to Confluence",
|
|
5
|
+
"main": "main.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "node esbuild.config.mjs",
|
|
9
|
+
"build": "tsc && node esbuild.config.mjs production",
|
|
10
|
+
"fmt": "npx prettier --write src/",
|
|
11
|
+
"lint": "eslint --ignore-path ../../.eslintignore --ext .jsx,tsx,.js,.ts src/",
|
|
12
|
+
"prettier-check": "npx prettier --check src/"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [],
|
|
15
|
+
"author": "andymac4182",
|
|
16
|
+
"license": "Apache 2.0",
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/mime-types": "^2.1.1",
|
|
19
|
+
"@types/react-dom": "^18.0.11",
|
|
20
|
+
"obsidian": "1.1.1"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"confluence.js": "^1.6.3",
|
|
24
|
+
"mime-types": "^2.1.35",
|
|
25
|
+
"react": "^16.14.0",
|
|
26
|
+
"react-dom": "^16.14.0",
|
|
27
|
+
"md-confluence-lib": "5.6.0",
|
|
28
|
+
"md-confluence-mermaid-electron-renderer": "5.6.0"
|
|
29
|
+
},
|
|
30
|
+
"resolutions": {
|
|
31
|
+
"prosemirror-model": "1.14.3"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Modal, App } from "obsidian";
|
|
2
|
+
import ReactDOM from "react-dom";
|
|
3
|
+
import React, { useState } from "react";
|
|
4
|
+
import { UploadAdfFileResult } from "md-confluence-lib";
|
|
5
|
+
|
|
6
|
+
export interface FailedFile {
|
|
7
|
+
fileName: string;
|
|
8
|
+
reason: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface UploadResults {
|
|
12
|
+
errorMessage: string | null;
|
|
13
|
+
failedFiles: FailedFile[];
|
|
14
|
+
filesUploadResult: UploadAdfFileResult[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface UploadResultsProps {
|
|
18
|
+
uploadResults: UploadResults;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const CompletedView: React.FC<UploadResultsProps> = ({ uploadResults }) => {
|
|
22
|
+
const { errorMessage, failedFiles, filesUploadResult } = uploadResults;
|
|
23
|
+
const [expanded, setExpanded] = useState(false);
|
|
24
|
+
|
|
25
|
+
const countResults = {
|
|
26
|
+
content: { same: 0, updated: 0 },
|
|
27
|
+
images: { same: 0, updated: 0 },
|
|
28
|
+
labels: { same: 0, updated: 0 },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
filesUploadResult.forEach((result) => {
|
|
32
|
+
countResults.content[result.contentResult]++;
|
|
33
|
+
countResults.images[result.imageResult]++;
|
|
34
|
+
countResults.labels[result.labelResult]++;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const renderUpdatedFiles = (type: "content" | "image" | "label") => {
|
|
38
|
+
return filesUploadResult
|
|
39
|
+
.filter((result) => result[`${type}Result`] === "updated")
|
|
40
|
+
.map((result, index) => (
|
|
41
|
+
<li key={index}>
|
|
42
|
+
<a href={result.adfFile.pageUrl}>
|
|
43
|
+
{result.adfFile.absoluteFilePath}
|
|
44
|
+
</a>
|
|
45
|
+
</li>
|
|
46
|
+
));
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div className="upload-results">
|
|
51
|
+
<div>
|
|
52
|
+
<h1>Confluence Publish</h1>
|
|
53
|
+
</div>
|
|
54
|
+
{errorMessage ? (
|
|
55
|
+
<div className="error-message">
|
|
56
|
+
<h3>Error</h3>
|
|
57
|
+
<p>{errorMessage}</p>
|
|
58
|
+
</div>
|
|
59
|
+
) : (
|
|
60
|
+
<>
|
|
61
|
+
<div className="successful-uploads">
|
|
62
|
+
<h3>Successful Uploads</h3>
|
|
63
|
+
<p>
|
|
64
|
+
{filesUploadResult.length} file(s) uploaded
|
|
65
|
+
successfully.
|
|
66
|
+
</p>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
{failedFiles.length > 0 && (
|
|
70
|
+
<div className="failed-uploads">
|
|
71
|
+
<h3>Failed Uploads</h3>
|
|
72
|
+
<p>
|
|
73
|
+
{failedFiles.length} file(s) failed to upload.
|
|
74
|
+
</p>
|
|
75
|
+
<ul>
|
|
76
|
+
{failedFiles.map((file, index) => (
|
|
77
|
+
<li key={index}>
|
|
78
|
+
<strong>{file.fileName}</strong>:{" "}
|
|
79
|
+
{file.reason}
|
|
80
|
+
</li>
|
|
81
|
+
))}
|
|
82
|
+
</ul>
|
|
83
|
+
</div>
|
|
84
|
+
)}
|
|
85
|
+
|
|
86
|
+
<table className="result-table">
|
|
87
|
+
<thead>
|
|
88
|
+
<tr>
|
|
89
|
+
<th>Type</th>
|
|
90
|
+
<th>Same</th>
|
|
91
|
+
<th>Updated</th>
|
|
92
|
+
</tr>
|
|
93
|
+
</thead>
|
|
94
|
+
<tbody>
|
|
95
|
+
<tr>
|
|
96
|
+
<td>Content</td>
|
|
97
|
+
<td>{countResults.content.same}</td>
|
|
98
|
+
<td>{countResults.content.updated}</td>
|
|
99
|
+
</tr>
|
|
100
|
+
<tr>
|
|
101
|
+
<td>Images</td>
|
|
102
|
+
<td>{countResults.images.same}</td>
|
|
103
|
+
<td>{countResults.images.updated}</td>
|
|
104
|
+
</tr>
|
|
105
|
+
<tr>
|
|
106
|
+
<td>Labels</td>
|
|
107
|
+
<td>{countResults.labels.same}</td>
|
|
108
|
+
<td>{countResults.labels.updated}</td>
|
|
109
|
+
</tr>
|
|
110
|
+
</tbody>
|
|
111
|
+
</table>
|
|
112
|
+
<div className="expandable-section">
|
|
113
|
+
<button onClick={() => setExpanded(!expanded)}>
|
|
114
|
+
{expanded ? "Collapse" : "Expand"} Updated Files
|
|
115
|
+
</button>
|
|
116
|
+
{expanded && (
|
|
117
|
+
<div className="updated-files">
|
|
118
|
+
<div className="updated-content">
|
|
119
|
+
<h4>Updated Content</h4>
|
|
120
|
+
<ul>{renderUpdatedFiles("content")}</ul>
|
|
121
|
+
</div>
|
|
122
|
+
<div className="updated-images">
|
|
123
|
+
<h4>Updated Images</h4>
|
|
124
|
+
<ul>{renderUpdatedFiles("image")}</ul>
|
|
125
|
+
</div>
|
|
126
|
+
<div className="updated-labels">
|
|
127
|
+
<h4>Updated Labels</h4>
|
|
128
|
+
<ul>{renderUpdatedFiles("label")}</ul>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
)}
|
|
132
|
+
</div>
|
|
133
|
+
</>
|
|
134
|
+
)}
|
|
135
|
+
</div>
|
|
136
|
+
);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export class CompletedModal extends Modal {
|
|
140
|
+
uploadResults: UploadResultsProps;
|
|
141
|
+
|
|
142
|
+
constructor(app: App, uploadResults: UploadResultsProps) {
|
|
143
|
+
super(app);
|
|
144
|
+
this.uploadResults = uploadResults;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
override onOpen() {
|
|
148
|
+
const { contentEl } = this;
|
|
149
|
+
ReactDOM.render(
|
|
150
|
+
React.createElement(CompletedView, this.uploadResults),
|
|
151
|
+
contentEl,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
override onClose() {
|
|
156
|
+
const { contentEl } = this;
|
|
157
|
+
ReactDOM.unmountComponentAtNode(contentEl);
|
|
158
|
+
contentEl.empty();
|
|
159
|
+
}
|
|
160
|
+
}
|