mirror of https://github.com/TriliumNext/Notes
Merge remote-tracking branch 'origin/develop' into feature/task_list
commit
a3fbf15902
@ -1,30 +1,31 @@
|
||||
import OptionsWidget from "../options_widget.js";
|
||||
import { t } from "../../../../services/i18n.js";
|
||||
import type { OptionMap } from "../../../../../../services/options_interface.js";
|
||||
import TimeSelector from "../time_selector.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="options-section">
|
||||
<h4>${t("revisions_snapshot_interval.note_revisions_snapshot_interval_title")}</h4>
|
||||
|
||||
<p class="use-tn-links">${t("revisions_snapshot_interval.note_revisions_snapshot_description")}</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label>${t("revisions_snapshot_interval.snapshot_time_interval_label")}</label>
|
||||
<input class="revision-snapshot-time-interval-in-seconds form-control options-number-input" type="number" min="10">
|
||||
</div>
|
||||
<div id="time-selector-placeholder"></div>
|
||||
</div>`;
|
||||
|
||||
export default class RevisionsSnapshotIntervalOptions extends OptionsWidget {
|
||||
export default class RevisionsSnapshotIntervalOptions extends TimeSelector {
|
||||
|
||||
private $revisionsTimeInterval!: JQuery<HTMLElement>;
|
||||
constructor() {
|
||||
super({
|
||||
widgetId: "revision-snapshot-time-interval",
|
||||
widgetLabelId: "revisions_snapshot_interval.snapshot_time_interval_label",
|
||||
optionValueId: "revisionSnapshotTimeInterval",
|
||||
optionTimeScaleId: "revisionSnapshotTimeIntervalTimeScale",
|
||||
minimumSeconds: 10
|
||||
});
|
||||
super.doRender();
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $timeSelector = this.$widget;
|
||||
// inject TimeSelector widget template
|
||||
this.$widget = $(TPL);
|
||||
this.$revisionsTimeInterval = this.$widget.find(".revision-snapshot-time-interval-in-seconds");
|
||||
this.$revisionsTimeInterval.on("change", () => this.updateOption("revisionSnapshotTimeInterval", this.$revisionsTimeInterval.val()));
|
||||
}
|
||||
|
||||
async optionsLoaded(options: OptionMap) {
|
||||
this.$revisionsTimeInterval.val(options.revisionSnapshotTimeInterval);
|
||||
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector)
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,97 @@
|
||||
import OptionsWidget from "../options_widget.js";
|
||||
import options from "../../../../services/options.js";
|
||||
import { t } from "../../../../services/i18n.js";
|
||||
import type { OptionMap, OptionNames } from "../../../../../../services/options_interface.js";
|
||||
import searchService from "../../../../services/search.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="options-section">
|
||||
<h4>${t("share.title")}</h4>
|
||||
|
||||
<label class="tn-checkbox">
|
||||
<input class="form-check-input" type="checkbox" name="redirectBareDomain" value="true">
|
||||
${t("share.redirect_bare_domain")}
|
||||
</label>
|
||||
<p class="form-text">${t("share.redirect_bare_domain_description")}</p>
|
||||
|
||||
<label class="tn-checkbox">
|
||||
<input class="form-check-input" type="checkbox" name="showLoginInShareTheme" value="true">
|
||||
${t("share.show_login_link")}
|
||||
</label>
|
||||
<p class="form-text">${t("share.show_login_link_description")}</p>
|
||||
</div>`;
|
||||
|
||||
export default class ShareSettingsOptions extends OptionsWidget {
|
||||
private $shareRootCheck!: JQuery<HTMLElement>;
|
||||
private $shareRootStatus!: JQuery<HTMLElement>;
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
this.contentSized();
|
||||
|
||||
this.$shareRootCheck = this.$widget.find('.share-root-check');
|
||||
this.$shareRootStatus = this.$widget.find('.share-root-status');
|
||||
|
||||
// Add change handlers for both checkboxes
|
||||
this.$widget.find('input[type="checkbox"]').on("change", (e: JQuery.ChangeEvent) => {
|
||||
this.save();
|
||||
|
||||
// Show/hide share root status section based on redirectBareDomain checkbox
|
||||
const target = e.target as HTMLInputElement;
|
||||
if (target.name === 'redirectBareDomain') {
|
||||
this.$shareRootCheck.toggle(target.checked);
|
||||
if (target.checked) {
|
||||
this.checkShareRoot();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add click handler for check share root button
|
||||
this.$widget.find('.check-share-root').on("click", () => this.checkShareRoot());
|
||||
}
|
||||
|
||||
async optionsLoaded(options: OptionMap) {
|
||||
const redirectBareDomain = options.redirectBareDomain === "true";
|
||||
this.$widget.find('input[name="redirectBareDomain"]').prop("checked", redirectBareDomain);
|
||||
this.$shareRootCheck.toggle(redirectBareDomain);
|
||||
if (redirectBareDomain) {
|
||||
await this.checkShareRoot();
|
||||
}
|
||||
|
||||
this.$widget.find('input[name="showLoginInShareTheme"]').prop("checked", options.showLoginInShareTheme === "true");
|
||||
}
|
||||
|
||||
async checkShareRoot() {
|
||||
const $button = this.$widget.find('.check-share-root');
|
||||
$button.prop('disabled', true);
|
||||
|
||||
try {
|
||||
const shareRootNotes = await searchService.searchForNotes("#shareRoot");
|
||||
const sharedShareRootNote = shareRootNotes.find(note => note.isShared());
|
||||
|
||||
if (sharedShareRootNote) {
|
||||
this.$shareRootStatus
|
||||
.removeClass('text-danger')
|
||||
.addClass('text-success')
|
||||
.text(t("share.share_root_found", {noteTitle: sharedShareRootNote.title}));
|
||||
} else {
|
||||
this.$shareRootStatus
|
||||
.removeClass('text-success')
|
||||
.addClass('text-danger')
|
||||
.text(shareRootNotes.length > 0
|
||||
? t("share.share_root_not_shared", {noteTitle: shareRootNotes[0].title})
|
||||
: t("share.share_root_not_found"));
|
||||
}
|
||||
} finally {
|
||||
$button.prop('disabled', false);
|
||||
}
|
||||
}
|
||||
|
||||
async save() {
|
||||
const redirectBareDomain = this.$widget.find('input[name="redirectBareDomain"]').prop("checked");
|
||||
await this.updateOption<"redirectBareDomain">("redirectBareDomain", redirectBareDomain.toString());
|
||||
|
||||
const showLoginInShareTheme = this.$widget.find('input[name="showLoginInShareTheme"]').prop("checked");
|
||||
await this.updateOption<"showLoginInShareTheme">("showLoginInShareTheme", showLoginInShareTheme.toString());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
import { t } from "../../../../services/i18n.js";
|
||||
import TimeSelector from "../time_selector.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="options-section">
|
||||
<h4>${t("password.protected_session_timeout")}</h4>
|
||||
|
||||
<p>${t("password.protected_session_timeout_description")} <a class="tn-link" href="https://triliumnext.github.io/Docs/Wiki/protected-notes.html" class="external">${t("password.wiki")}</a> ${t("password.for_more_info")}</p>
|
||||
<div id="time-selector-placeholder"></div>
|
||||
</div>`;
|
||||
|
||||
export default class ProtectedSessionTimeoutOptions extends TimeSelector {
|
||||
constructor() {
|
||||
super({
|
||||
widgetId: "protected-session-timeout",
|
||||
widgetLabelId: "password.protected_session_timeout_label",
|
||||
optionValueId: "protectedSessionTimeout",
|
||||
optionTimeScaleId: "protectedSessionTimeoutTimeScale",
|
||||
minimumSeconds: 60
|
||||
});
|
||||
super.doRender();
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $timeSelector = this.$widget;
|
||||
// inject TimeSelector widget template
|
||||
this.$widget = $(TPL);
|
||||
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import html_sanitizer from "./html_sanitizer.js";
|
||||
import { trimIndentation } from "../../spec/support/utils.js";
|
||||
|
||||
describe("sanitize", () => {
|
||||
it("filters out position inline CSS", () => {
|
||||
const dirty = `<div style="z-index:999999999;margin:0px;left:250px;height:100px;display:table;background:none;position:fixed;top:250px;"></div>`;
|
||||
const clean = `<div></div>`;
|
||||
expect(html_sanitizer.sanitize(dirty)).toBe(clean);
|
||||
});
|
||||
|
||||
it("keeps inline styles defined in CKEDitor", () => {
|
||||
const dirty = trimIndentation`\
|
||||
<p>
|
||||
<span style="color:hsl(0, 0%, 90%);">
|
||||
Hi
|
||||
</span>
|
||||
|
||||
<span style="background-color:hsl(30, 75%, 60%);">
|
||||
there
|
||||
</span>
|
||||
</p>
|
||||
<figure class="table" style="float:left;height:800px;width:600px;">
|
||||
<table style="background-color:hsl(0, 0%, 90%);border-color:hsl(0, 0%, 0%);border-style:dotted;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border:2px groove hsl(60, 75%, 60%);"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>`;
|
||||
const clean = trimIndentation`\
|
||||
<p>
|
||||
<span style="color:hsl(0, 0%, 90%)">
|
||||
Hi
|
||||
</span>
|
||||
|
||||
<span style="background-color:hsl(30, 75%, 60%)">
|
||||
there
|
||||
</span>
|
||||
</p>
|
||||
<figure class="table" style="float:left;height:800px;width:600px">
|
||||
<table style="background-color:hsl(0, 0%, 90%);border-color:hsl(0, 0%, 0%);border-style:dotted">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border:2px groove hsl(60, 75%, 60%)"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>`;
|
||||
expect(html_sanitizer.sanitize(dirty)) .toBe(clean);
|
||||
});
|
||||
});
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue