Compare commits

...

6 commits

Author SHA1 Message Date
Ell 6c781d560c fixed some typos 2023-08-17 18:20:11 +02:00
Ell 07995d2a9a allow automatically updating shares 2023-08-17 17:55:27 +02:00
Ell 0bb5ae69b0 handle when files are moved or deleted 2023-08-17 17:51:11 +02:00
Ell b22540c772 implemented opening note 2023-08-17 17:36:28 +02:00
Ell 7067957899 cleaned up view 2023-08-17 17:25:24 +02:00
Ell 0679f3add8 added the ability to include the note title in the note 2023-08-17 16:08:25 +02:00
7 changed files with 127 additions and 61 deletions

View file

@ -28,7 +28,7 @@ When sharing a note, its content as well as additional metadata created by the b
Due to the fact that note links are generated randomly, it is reasonably difficult for attackers to guess note links and access notes that they're not supposed to. That being said, all notes are still publicly available the same way that YouTube videos set to "Unlisted" are still publicly available: harder to access, but not impossible; not truly private.
Before being uploaded, notes go through minor preprocessing as part of the plugin, including
- stripping frontmatter (optionally, this can be changed in the plugin settinsg),
- stripping frontmatter (optionally, this can be changed in the plugin settings),
- converting all attachments to `base64` encoding and embedding them in the shared note as HTML.
Currently, links to other notes are included in your share, but don't actually lead anywhere, and sharing linked notes is not supported yet.
@ -68,7 +68,7 @@ Lastly, to get your server connected to the plugin, head to its settings and cha
## About This Site
The official Just Share Please site, [jsp.ellpeck.de](https://jsp.ellpeck.de), is run by Ellpeck, the creator of the plugin. It is hosted on a server in Germany, and connections to and from it are secured using SSL encryption, partly through LetsEncrypt and partly through Cloudflare SSL.
If you have lost access to your shares due to your settingds file being deleted or due to other reasons, and you want your shares to be deleted, please contact Ell at [me@ellpeck.de](mailto:me@ellpeck.de) or through his [Discord server](https://ellpeck.de/discord).
If you have lost access to your shares due to your settings file being deleted or due to other reasons, and you want your shares to be deleted, please contact Ell at [me@ellpeck.de](mailto:me@ellpeck.de) or through his [Discord server](https://ellpeck.de/discord).
Additionally, the site's source can be found [on GitHub](https://github.com/Ellpeck/ObsidianJustSharePlease/tree/main/server), and for information on privacy and ownership, see the [imprint](https://ellpeck.de/impressum/) and [privacy policy](https://ellpeck.de/privacy/). Ellpeck Games is not responsible for content published through Just Share Please.

View file

@ -25,7 +25,7 @@ export default class JustSharePleasePlugin extends Plugin {
this.registerEvent(this.app.workspace.on("file-menu", async (m, f) => {
if (f instanceof TFile && f.extension == "md") {
let shared = this.getSharedItem(f);
let shared = this.getSharedItem(f.path);
if (!shared) {
m.addItem(i => {
i.setTitle("Share to JSP");
@ -51,12 +51,40 @@ export default class JustSharePleasePlugin extends Plugin {
}
}
}));
this.registerEvent(this.app.vault.on("rename", (f, p) => {
if (f instanceof TFile) {
let shared = this.getSharedItem(p);
if (shared) {
shared.path = f.path;
this.refreshAllViews();
}
}
}));
this.registerEvent(this.app.vault.on("delete", f => {
if (f instanceof TFile) {
let shared = this.getSharedItem(f.path);
if (shared) {
if (this.settings.unshareDeletedFiles) {
this.deleteFile(shared, false);
} else {
this.refreshAllViews();
}
}
}
}));
this.registerEvent(this.app.vault.on("modify", f => {
if (this.settings.autoUpdateShares && f instanceof TFile) {
let shared = this.getSharedItem(f.path);
if (shared)
this.updateFile(shared, f, false);
}
}));
this.addCommand({
id: "share",
name: "Share current file to JSP",
editorCheckCallback: (checking, _, ctx) => {
if (!this.getSharedItem(ctx.file)) {
if (!this.getSharedItem(ctx.file.path)) {
if (!checking)
this.shareFile(ctx.file);
return true;
@ -68,7 +96,7 @@ export default class JustSharePleasePlugin extends Plugin {
id: "copy",
name: "Copy current file's JSP link",
editorCheckCallback: (checking, _, ctx) => {
let shared = this.getSharedItem(ctx.file);
let shared = this.getSharedItem(ctx.file.path);
if (shared) {
if (!checking)
this.copyShareLink(shared);
@ -81,7 +109,7 @@ export default class JustSharePleasePlugin extends Plugin {
id: "update",
name: "Update current file in JSP",
editorCheckCallback: (checking, _, ctx) => {
let shared = this.getSharedItem(ctx.file);
let shared = this.getSharedItem(ctx.file.path);
if (shared) {
if (!checking)
this.updateFile(shared, ctx.file);
@ -94,7 +122,7 @@ export default class JustSharePleasePlugin extends Plugin {
id: "delete",
name: "Delete current file from JSP",
editorCheckCallback: (checking, _, ctx) => {
let shared = this.getSharedItem(ctx.file);
let shared = this.getSharedItem(ctx.file.path);
if (shared) {
if (!checking)
this.deleteFile(shared);
@ -114,8 +142,8 @@ export default class JustSharePleasePlugin extends Plugin {
await this.saveData(this.settings);
}
getSharedItem(file: TFile): SharedItem {
return this.settings.shared.find(f => f.path == file.path);
getSharedItem(path: string): SharedItem {
return this.settings.shared.find(f => f.path == path);
}
async shareFile(file: TFile): Promise<SharedItem> {
@ -128,13 +156,12 @@ export default class JustSharePleasePlugin extends Plugin {
let shared = response.json as SharedItem;
shared.path = file.path;
await this.copyShareLink(shared, false);
new Notice(`Successfully shared ${file.basename} and copied link to clipboard`);
this.settings.shared.push(shared);
await this.saveSettings();
this.refreshAllViews();
await this.copyShareLink(shared, false);
new Notice(`Successfully shared ${file.basename} and copied link to clipboard`);
return shared;
} catch (e) {
new Notice(createFragment(f => {
@ -153,21 +180,20 @@ export default class JustSharePleasePlugin extends Plugin {
headers: {"Password": item.password},
body: JSON.stringify({content: await this.preProcessMarkdown(file)})
});
new Notice(`Successfully updated ${file.basename} on JSP`);
if (notice)
new Notice(`Successfully updated ${file.basename} on JSP`);
return true;
} catch (e) {
if (notice) {
new Notice(createFragment(f => {
f.createSpan({text: `There was an error updating ${file.basename}: `});
f.createEl("code", {text: e});
}), 10000);
}
new Notice(createFragment(f => {
f.createSpan({text: `There was an error updating ${file.basename}: `});
f.createEl("code", {text: e});
}), 10000);
console.log(e);
}
}
async deleteFile(item: SharedItem): Promise<boolean> {
async deleteFile(item: SharedItem, notice = true): Promise<boolean> {
let name = basename(item.path, extname(item.path));
try {
await requestUrl({
@ -175,12 +201,13 @@ export default class JustSharePleasePlugin extends Plugin {
method: "DELETE",
headers: {"Password": item.password}
});
new Notice(`Successfully deleted ${name} from JSP`);
this.settings.shared.remove(item);
await this.saveSettings();
this.refreshAllViews();
if (notice)
new Notice(`Successfully deleted ${name} from JSP`);
return true;
} catch (e) {
new Notice(createFragment(f => {
@ -204,6 +231,10 @@ export default class JustSharePleasePlugin extends Plugin {
if (this.settings.stripFrontmatter)
text = text.replace(/^---\s*\n.*?\n---\s*\n(.*)$/s, "$1");
// include note name
if (this.settings.includeNoteName)
text = `# ${file.basename}\n\n${text}`;
// embed attachments directly
let attachments = /!\[(.*)]\((.+)\)|!\[\[(.+)]]/g;
let match: RegExpExecArray;
@ -226,7 +257,6 @@ export default class JustSharePleasePlugin extends Plugin {
return text;
}
// TODO refresh when a file is moved or deleted in Obsidian
refreshAllViews(): void {
for (let leaf of this.app.workspace.getLeavesOfType(JSPView.type)) {
if (leaf.view instanceof JSPView)

View file

@ -14,7 +14,6 @@ export class JSPSettingsTab extends PluginSettingTab {
display(): void {
this.containerEl.empty();
this.containerEl.createEl("h2", {text: "Just Share Please Settings"});
new Setting(this.containerEl)
.setName("Just Share Please Server")
.setDesc(createFragment(f => {
@ -31,7 +30,6 @@ export class JSPSettingsTab extends PluginSettingTab {
await this.plugin.saveSettings();
});
});
new Setting(this.containerEl)
.setName("Strip Frontmatter")
.setDesc("Whether document frontmatter (also known as properties) should be removed from the uploaded share.")
@ -42,6 +40,36 @@ export class JSPSettingsTab extends PluginSettingTab {
await this.plugin.saveSettings();
});
});
new Setting(this.containerEl)
.setName("Include Note Name")
.setDesc("Whether the name of the shared note should be included in the share as a heading.")
.addToggle(t => {
t.setValue(this.plugin.settings.includeNoteName);
t.onChange(async v => {
this.plugin.settings.includeNoteName = v;
await this.plugin.saveSettings();
});
});
new Setting(this.containerEl)
.setName("Unshare Deleted Files")
.setDesc("Whether shares of files should be removed automatically when they are deleted. Only supported when deleting from within Obsidian.")
.addToggle(t => {
t.setValue(this.plugin.settings.unshareDeletedFiles);
t.onChange(async v => {
this.plugin.settings.unshareDeletedFiles = v;
await this.plugin.saveSettings();
});
});
new Setting(this.containerEl)
.setName("Automatically Update Shares")
.setDesc("Whether a file's share should automatically be updated when the file is changed from within Obsidian.")
.addToggle(t => {
t.setValue(this.plugin.settings.autoUpdateShares);
t.onChange(async v => {
this.plugin.settings.autoUpdateShares = v;
await this.plugin.saveSettings();
});
});
this.containerEl.createEl("hr");
this.containerEl.createEl("p", {text: "If you like this plugin and want to support its development, you can do so through my website by clicking this fancy image!"});

View file

@ -1,16 +1,20 @@
export const defaultSettings: JSPSettings = {
url: "https://jsp.ellpeck.de",
shared: [],
stripFrontmatter: true
stripFrontmatter: true,
includeNoteName: true,
unshareDeletedFiles: true,
autoUpdateShares: false
};
// TODO add a setting for auto-refreshing uploads when saving
// TODO add a setting for auto-removing JSP shares when the original file is deleted
export interface JSPSettings {
url: string;
shared: SharedItem[];
stripFrontmatter: boolean;
includeNoteName: boolean;
unshareDeletedFiles: boolean;
autoUpdateShares: boolean;
}
@ -18,7 +22,6 @@ export interface SharedItem {
id: string;
password: string;
// TODO auto-update path when file is moved
path: string;
}

View file

@ -16,34 +16,40 @@ export class JSPView extends ItemView {
public refresh(): void {
this.contentEl.empty();
let content = this.contentEl.createDiv({cls: "just-share-please-view"});
for (let shared of this.plugin.settings.shared) {
let file = this.plugin.app.vault.getAbstractFileByPath(shared.path) as TFile;
let div = content.createDiv({cls: "just-share-please-shared-item"});
div.createSpan({cls: "just-share-please-shared-name", text: basename(shared.path, extname(shared.path))});
new ButtonComponent(div)
.setClass("clickable-icon")
.setTooltip("Copy JSP link")
.setIcon("link")
.onClick(async () => this.plugin.copyShareLink(shared));
if (file) {
if (this.plugin.settings.shared.length > 0) {
for (let shared of this.plugin.settings.shared) {
let file = this.plugin.app.vault.getAbstractFileByPath(shared.path) as TFile;
let div = content.createDiv({cls: "just-share-please-shared-item"});
div.createSpan({cls: "just-share-please-shared-name", text: basename(shared.path, extname(shared.path))});
new ButtonComponent(div)
.setClass("clickable-icon")
.setTooltip("Open in Obsidian")
.setIcon("edit")
.onClick(async () => {
// TODO open in obsidian
});
.setTooltip("Copy JSP link")
.setIcon("link")
.onClick(async () => this.plugin.copyShareLink(shared));
if (file) {
new ButtonComponent(div)
.setClass("clickable-icon")
.setTooltip("Open in Obsidian")
.setIcon("edit")
.onClick(async e => {
let leaf = this.app.workspace.getLeaf(e.ctrlKey);
await leaf.openFile(file);
this.app.workspace.setActiveLeaf(leaf, {focus: true});
});
new ButtonComponent(div)
.setClass("clickable-icon")
.setTooltip("Update in JSP")
.setIcon("share")
.onClick(async () => this.plugin.updateFile(shared, file));
}
new ButtonComponent(div)
.setClass("clickable-icon")
.setTooltip("Update in JSP")
.setIcon("share")
.onClick(async () => this.plugin.updateFile(shared, file));
.setTooltip("Delete from JSP")
.setIcon("trash")
.onClick(async () => this.plugin.deleteFile(shared));
}
new ButtonComponent(div)
.setClass("clickable-icon")
.setTooltip("Delete from JSP")
.setIcon("trash")
.onClick(async () => this.plugin.deleteFile(shared));
} else {
content.createSpan({text: "You have not shared any items yet."});
}
}

View file

@ -1,16 +1,14 @@
{
"url": "https://jsp.ellpeck.de",
"url": "http://localhost:8080",
"shared": [
{
"id": "3c781ebc",
"password": "7eae9eea1f7503f6f236b242d45657e6",
"id": "234df7b7",
"password": "0ef1504bfe83cd9ca812434caa7aacf4",
"path": "Cool Test Note.md"
},
{
"id": "d47f7765",
"password": "513115b756d9ddebd3afd6efe4c248c6",
"path": "Even cooler note.md"
}
],
"stripFrontmatter": true
"stripFrontmatter": true,
"includeNoteName": true,
"unshareDeletedFiles": false,
"autoUpdateShares": false
}

View file

@ -1,7 +1,6 @@
---
test: yes
---
# Cool Test Note
This is a cool test note, my friends!
@ -25,6 +24,8 @@ $.ajax({
cool!!
blah blah i added and removed this
The following is $x^2 = 7$, but more complicated!
$$
x^2 + \sum_{i = 1}^{10000} x^2 \cdot 0 = 7