This commit is contained in:
2026-09-19 21:35:08 +02:00
parent 15628739b1
commit 5601c282b7
13 changed files with 257 additions and 32 deletions
+14 -2
View File
@@ -4,7 +4,7 @@ Happy to finally bring you f0ckm! The long awaited imageboard solution that you
It features extensive tagging, searching, filtering and a variety of options. It features extensive tagging, searching, filtering and a variety of options.
The software is mostly generic, it can be modified easily via config.json to suit your communities needs. Most things can be enabled/disabled very easily and modified to needs. The software is mostly generic, it can be modified easily via `config.yaml` (or `config.json`) to suit your communities needs. Most things can be enabled/disabled very easily and modified to needs.
The software comes without any warranties or entitlements of any kind! The developer is not responsible for anything you do with the use of this software. The software comes without any warranties or entitlements of any kind! The developer is not responsible for anything you do with the use of this software.
@@ -20,7 +20,12 @@ first things
fill with for example: f0ckm (prefilled) fill with for example: f0ckm (prefilled)
`cp config_example.json config.json` Copy example configuration and customize:
`cp config_example.yaml config.yaml`
`npm run config:gen`
(Or `cp config_example.json config.json` if configuring directly in JSON)
Edit to needs, for sql you can do this: Edit to needs, for sql you can do this:
@@ -74,6 +79,13 @@ Create admin user in dev env
`DB_HOST=localhost DB_PORT=5454 node scripts/create-admin.mjs admin 'YOUR_PASSWORD_HERE'` `DB_HOST=localhost DB_PORT=5454 node scripts/create-admin.mjs admin 'YOUR_PASSWORD_HERE'`
now visit http://localhost:1337 in your browser, you can develop without needing to rebuild the docker image for every change now visit http://localhost:1337 in your browser, you can develop without needing to rebuild the docker image for every change
## Configuration Management
You can manage configuration in YAML (`config.yaml`) or JSON (`config.json`):
- `npm run config:gen`: Compiles `config.yaml` to `config.json`. Automatically runs before `npm run dev` and `npm start`.
- `npm run config:watch`: Watches `config.yaml` for edits and recompiles `config.json` automatically on save.
- `npm run config:to-yaml`: Converts existing `config.json` into `config.yaml`.
## NGINX ## NGINX
+6 -2
View File
@@ -164,7 +164,11 @@
"fileupload_comments_size": 104857600, "fileupload_comments_size": 104857600,
"fileupload_comments_max": 5, "fileupload_comments_max": 5,
"fileupload_comments_mode": "attachment", "fileupload_comments_mode": "attachment",
"fileupload_comments_mimes": ["image", "video", "audio"], "fileupload_comments_mimes": [
"image",
"video",
"audio"
],
"show_content_warning": true, "show_content_warning": true,
"default_comment_display_mode": 1, "default_comment_display_mode": 1,
"phrases": [ "phrases": [
@@ -302,4 +306,4 @@
"site_key": "YOUR_RECAPTCHA_V2_SITE_KEY", "site_key": "YOUR_RECAPTCHA_V2_SITE_KEY",
"secret_key": "YOUR_RECAPTCHA_V2_SECRET_KEY" "secret_key": "YOUR_RECAPTCHA_V2_SECRET_KEY"
} }
} }
+22 -1
View File
@@ -17,7 +17,8 @@
"jszip": "3.10.1", "jszip": "3.10.1",
"marked": "18.0.2", "marked": "18.0.2",
"matrix-js-sdk": "^40.3.0-rc.0", "matrix-js-sdk": "^40.3.0-rc.0",
"postgres": "^3.3.4" "postgres": "^3.3.4",
"yaml": "^2.9.1"
} }
}, },
"node_modules/@babel/runtime": { "node_modules/@babel/runtime": {
@@ -448,6 +449,21 @@
"bin": { "bin": {
"uuid": "dist-node/bin/uuid" "uuid": "dist-node/bin/uuid"
} }
},
"node_modules/yaml": {
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz",
"integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==",
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
},
"engines": {
"node": ">= 14.6"
},
"funding": {
"url": "https://github.com/sponsors/eemeli"
}
} }
}, },
"dependencies": { "dependencies": {
@@ -757,6 +773,11 @@
"version": "13.0.2", "version": "13.0.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.2.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.2.tgz",
"integrity": "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==" "integrity": "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw=="
},
"yaml": {
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz",
"integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw=="
} }
} }
} }
+4
View File
@@ -1786,6 +1786,10 @@ window.cancelAnimFrame = (function () {
// ── Onara Alternative Flavor: Fullscreen Modal Wrapper ───────────────────── // ── Onara Alternative Flavor: Fullscreen Modal Wrapper ─────────────────────
const isOnaraActive = () => { const isOnaraActive = () => {
// Global config onara overweighs always the user pref
if (window.f0ckSession && window.f0ckSession.onara_cfg !== undefined && window.f0ckSession.onara_cfg !== null) {
return !!window.f0ckSession.onara_cfg;
}
const stored = (() => { try { return localStorage.getItem('f0ck_onara_enabled'); } catch { return null; } })(); const stored = (() => { try { return localStorage.getItem('f0ck_onara_enabled'); } catch { return null; } })();
if (stored !== null) { if (stored !== null) {
const active = stored === 'true'; const active = stored === 'true';
+26 -17
View File
@@ -1586,29 +1586,38 @@
}; };
// Onara enable/disable toggle (localStorage only — no server round-trip needed) // Onara enable/disable toggle
const onaraEnabledToggle = document.getElementById('onara_enabled_toggle'); const onaraEnabledToggle = document.getElementById('onara_enabled_toggle');
if (onaraEnabledToggle) { if (onaraEnabledToggle) {
const LS_KEY = 'f0ck_onara_enabled'; const LS_KEY = 'f0ck_onara_enabled';
// Seed checkbox from localStorage; fall back to the server-set session value const hasGlobalConfig = window.f0ckSession && window.f0ckSession.onara_cfg !== undefined && window.f0ckSession.onara_cfg !== null;
const stored = localStorage.getItem(LS_KEY);
const sessionOnara = !!(window.f0ckSession?.onara);
onaraEnabledToggle.checked = stored !== null ? stored === 'true' : sessionOnara;
// Reflect current state into window.onara so isOnaraActive() sees it
window.onara = onaraEnabledToggle.checked;
try {
document.cookie = `f0ck_onara=${onaraEnabledToggle.checked ? '1' : '0'}; path=/; max-age=31536000; SameSite=Lax`;
} catch {}
onaraEnabledToggle.addEventListener('change', () => { if (hasGlobalConfig) {
const enabled = onaraEnabledToggle.checked; const globalVal = !!window.f0ckSession.onara_cfg;
localStorage.setItem(LS_KEY, String(enabled)); onaraEnabledToggle.checked = globalVal;
onaraEnabledToggle.disabled = true;
window.onara = globalVal;
} else {
// Seed checkbox from localStorage; fall back to the server-set session value
const stored = localStorage.getItem(LS_KEY);
const sessionOnara = !!(window.f0ckSession?.onara);
onaraEnabledToggle.checked = stored !== null ? stored === 'true' : sessionOnara;
// Reflect current state into window.onara so isOnaraActive() sees it
window.onara = onaraEnabledToggle.checked;
try { try {
document.cookie = `f0ck_onara=${enabled ? '1' : '0'}; path=/; max-age=31536000; SameSite=Lax`; document.cookie = `f0ck_onara=${onaraEnabledToggle.checked ? '1' : '0'}; path=/; max-age=31536000; SameSite=Lax`;
} catch {} } catch {}
window.onara = enabled;
showStatus('Onara viewer ' + (enabled ? 'enabled' : 'disabled') + '.', 'success'); onaraEnabledToggle.addEventListener('change', () => {
}); const enabled = onaraEnabledToggle.checked;
localStorage.setItem(LS_KEY, String(enabled));
try {
document.cookie = `f0ck_onara=${enabled ? '1' : '0'}; path=/; max-age=31536000; SameSite=Lax`;
} catch {}
window.onara = enabled;
showStatus('Onara viewer ' + (enabled ? 'enabled' : 'disabled') + '.', 'success');
});
}
} }
// Quote Emojis Toggle // Quote Emojis Toggle
+92
View File
@@ -0,0 +1,92 @@
#!/usr/bin/env node
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import YAML from 'yaml';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const rootDir = path.resolve(__dirname, '..');
const DEFAULT_CONFIG_YAML = path.join(rootDir, 'config.yaml');
const DEFAULT_CONFIG_JSON = path.join(rootDir, 'config.json');
const EXAMPLE_CONFIG_YAML = path.join(rootDir, 'config_example.yaml');
const EXAMPLE_CONFIG_JSON = path.join(rootDir, 'config_example.json');
function convertYamlToJson(yamlPath, jsonPath) {
if (!fs.existsSync(yamlPath)) {
console.error(`[CONFIG-GEN] Error: Source YAML file not found at ${yamlPath}`);
return false;
}
try {
const yamlContent = fs.readFileSync(yamlPath, 'utf8');
const parsed = YAML.parse(yamlContent);
if (parsed === null || typeof parsed !== 'object') {
throw new Error('Parsed YAML is not an object');
}
const jsonContent = JSON.stringify(parsed, null, 2) + '\n';
fs.writeFileSync(jsonPath, jsonContent, 'utf8');
console.log(`[CONFIG-GEN] Generated ${path.relative(rootDir, jsonPath)} from ${path.relative(rootDir, yamlPath)}`);
return true;
} catch (err) {
console.error(`[CONFIG-GEN] Failed to generate JSON from ${path.relative(rootDir, yamlPath)}: ${err.message}`);
return false;
}
}
function convertJsonToYaml(jsonPath, yamlPath) {
if (!fs.existsSync(jsonPath)) {
console.error(`[CONFIG-GEN] Error: Source JSON file not found at ${jsonPath}`);
return false;
}
try {
const jsonContent = fs.readFileSync(jsonPath, 'utf8');
const parsed = JSON.parse(jsonContent);
const doc = new YAML.Document(parsed);
const yamlContent = String(doc);
fs.writeFileSync(yamlPath, yamlContent, 'utf8');
console.log(`[CONFIG-GEN] Generated ${path.relative(rootDir, yamlPath)} from ${path.relative(rootDir, jsonPath)}`);
return true;
} catch (err) {
console.error(`[CONFIG-GEN] Failed to generate YAML from ${path.relative(rootDir, jsonPath)}: ${err.message}`);
return false;
}
}
const args = process.argv.slice(2);
const isWatch = args.includes('--watch') || args.includes('-w');
const isToYaml = args.includes('--to-yaml');
const includeAll = args.includes('--all') || args.includes('--example');
if (isToYaml) {
convertJsonToYaml(DEFAULT_CONFIG_JSON, DEFAULT_CONFIG_YAML);
if (includeAll || fs.existsSync(EXAMPLE_CONFIG_JSON)) {
convertJsonToYaml(EXAMPLE_CONFIG_JSON, EXAMPLE_CONFIG_YAML);
}
process.exit(0);
}
// Initial generation
let success = convertYamlToJson(DEFAULT_CONFIG_YAML, DEFAULT_CONFIG_JSON);
if (includeAll && fs.existsSync(EXAMPLE_CONFIG_YAML)) {
convertYamlToJson(EXAMPLE_CONFIG_YAML, EXAMPLE_CONFIG_JSON);
}
if (!success && !isWatch) {
process.exit(1);
}
if (isWatch) {
console.log(`[CONFIG-GEN] Watching ${path.relative(rootDir, DEFAULT_CONFIG_YAML)} for changes... (Press Ctrl+C to stop)`);
let debounceTimer = null;
fs.watch(DEFAULT_CONFIG_YAML, (eventType) => {
if (eventType === 'change' || eventType === 'rename') {
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
console.log(`[CONFIG-GEN] File change detected, re-generating config.json...`);
convertYamlToJson(DEFAULT_CONFIG_YAML, DEFAULT_CONFIG_JSON);
}, 100);
}
});
}
+29
View File
@@ -100,4 +100,33 @@ config.paths = {
import: resolveImportPath() import: resolveImportPath()
}; };
const configFilePath = path.resolve(base, "config.json");
let watchDebounceTimer = null;
try {
if (fs.existsSync(configFilePath)) {
const watcher = fs.watch(configFilePath, (eventType) => {
if (eventType === 'change' || eventType === 'rename') {
if (watchDebounceTimer) clearTimeout(watchDebounceTimer);
watchDebounceTimer = setTimeout(() => {
try {
const raw = fs.readFileSync(configFilePath, 'utf8');
const updated = JSON.parse(raw);
Object.assign(config, updated);
if (config.sql) {
config.sql.host = process.env.DB_HOST || process.env.POSTGRES_HOST || process.env.PGHOST || config.sql.host;
config.sql.port = parseInt(process.env.DB_PORT || process.env.POSTGRES_PORT || process.env.PGPORT || config.sql.port, 10);
config.sql.user = process.env.DB_USER || process.env.POSTGRES_USER || process.env.PGUSER || config.sql.user;
config.sql.password = process.env.DB_PASS || process.env.POSTGRES_PASSWORD || process.env.PGPASSWORD || config.sql.password;
config.sql.database = process.env.DB_NAME || process.env.POSTGRES_DB || process.env.PGDATABASE || config.sql.database;
}
console.log('[CONFIG] config.json reloaded dynamically');
} catch (_) {}
}, 100);
if (watchDebounceTimer.unref) watchDebounceTimer.unref();
}
});
if (watcher.unref) watcher.unref();
}
} catch (_) {}
export default config; export default config;
+10
View File
@@ -12,6 +12,7 @@ import cfg from "../config.mjs";
import security from "../security.mjs"; import security from "../security.mjs";
import crypto from "crypto"; import crypto from "crypto";
import path from "path"; import path from "path";
import YAML from "yaml";
import { getManualApproval, setManualApproval, getMinTags, setMinTags, getRegistrationOpen, setRegistrationOpen, getTrustedUploads, setTrustedUploads, getEnablePdf, setEnablePdf, getLogUserIps, setLogUserIps, getHashUserIps, setHashUserIps, getEnableCleanup, setEnableCleanup, getCleanupStartDate, setCleanupStartDate, getCleanupEndDate, setCleanupEndDate, getCleanupIncludeEngaged, setCleanupIncludeEngaged, getShitpostMode, ensureAllItemsHaveSlugs, getEnableItemSlugs, getBrandImageUrl } from "../settings.mjs"; import { getManualApproval, setManualApproval, getMinTags, setMinTags, getRegistrationOpen, setRegistrationOpen, getTrustedUploads, setTrustedUploads, getEnablePdf, setEnablePdf, getLogUserIps, setLogUserIps, getHashUserIps, setHashUserIps, getEnableCleanup, setEnableCleanup, getCleanupStartDate, setCleanupStartDate, getCleanupEndDate, setCleanupEndDate, getCleanupIncludeEngaged, setCleanupIncludeEngaged, getShitpostMode, ensureAllItemsHaveSlugs, getEnableItemSlugs, getBrandImageUrl } from "../settings.mjs";
export default (router, tpl) => { export default (router, tpl) => {
@@ -939,6 +940,15 @@ export default (router, tpl) => {
// Write formatted JSON to config.json on disk // Write formatted JSON to config.json on disk
await fs.writeFile(configPath, JSON.stringify(updatedConfig, null, 2) + "\n", "utf-8"); await fs.writeFile(configPath, JSON.stringify(updatedConfig, null, 2) + "\n", "utf-8");
// Also update config.yaml if it exists
const configYamlPath = path.resolve(process.cwd(), "config.yaml");
try {
const yamlDoc = new YAML.Document(updatedConfig);
await fs.writeFile(configYamlPath, String(yamlDoc), "utf-8");
} catch (yamlErr) {
console.warn('[ADMIN] Could not sync config.yaml:', yamlErr.message);
}
// Mutate in-memory cfg object so changes apply immediately // Mutate in-memory cfg object so changes apply immediately
Object.assign(cfg, updatedConfig); Object.assign(cfg, updatedConfig);
+16 -2
View File
@@ -221,7 +221,14 @@ export default (router, tpl) => {
console.log(`[AJAX-RANDOM] ${itemid} total=${tAjaxRender - tAjaxStart}ms | getRandom=${tRandom - tAjaxStart}ms | getf0ck=${tAjaxFetch - tRandom}ms | aux=${tAjaxAux - tAjaxFetch}ms | render=${tAjaxRender - tAjaxAux}ms`); console.log(`[AJAX-RANDOM] ${itemid} total=${tAjaxRender - tAjaxStart}ms | getRandom=${tRandom - tAjaxStart}ms | getf0ck=${tAjaxFetch - tRandom}ms | aux=${tAjaxAux - tAjaxFetch}ms | render=${tAjaxRender - tAjaxAux}ms`);
let itemPage = null; let itemPage = null;
if (query.onara === '1' || req.cookies?.onara === '1' || req.cookies?.f0ck_onara === '1' || cfg.onara) { const cfgOnara = cfg.onara !== undefined ? cfg.onara : cfg.websrv?.onara;
const cookieOnara = req.cookies?.f0ck_onara !== undefined
? (req.cookies.f0ck_onara === '1' || req.cookies.f0ck_onara === 'true')
: (req.cookies?.onara !== undefined ? (req.cookies.onara === '1' || req.cookies.onara === 'true') : null);
const effectiveOnara = (cfgOnara !== undefined && cfgOnara !== null)
? !!cfgOnara
: (query.onara === '1' || cookieOnara === true);
if (effectiveOnara) {
try { try {
itemPage = await f0cklib.getItemPage({ itemPage = await f0cklib.getItemPage({
targetItemId: data.item?.id || itemid, targetItemId: data.item?.id || itemid,
@@ -487,7 +494,14 @@ export default (router, tpl) => {
- Render: ${tAjaxRender - tAjaxAux}ms`); - Render: ${tAjaxRender - tAjaxAux}ms`);
let itemPage = null; let itemPage = null;
if (query.onara === '1' || req.cookies?.onara === '1' || req.cookies?.f0ck_onara === '1' || cfg.onara || query.get_page === '1') { const cfgOnara = cfg.onara !== undefined ? cfg.onara : cfg.websrv?.onara;
const cookieOnara = req.cookies?.f0ck_onara !== undefined
? (req.cookies.f0ck_onara === '1' || req.cookies.f0ck_onara === 'true')
: (req.cookies?.onara !== undefined ? (req.cookies.onara === '1' || req.cookies.onara === 'true') : null);
const effectiveOnara = (cfgOnara !== undefined && cfgOnara !== null)
? !!cfgOnara
: (query.onara === '1' || cookieOnara === true);
if (effectiveOnara || query.get_page === '1') {
try { try {
itemPage = await f0cklib.getItemPage({ itemPage = await f0cklib.getItemPage({
targetItemId: data.item?.id || itemid, targetItemId: data.item?.id || itemid,
+5 -3
View File
@@ -494,12 +494,14 @@ export default (router, tpl) => {
return res.reply({ body }); return res.reply({ body });
} }
const cfgOnara = cfg.onara !== undefined ? cfg.onara : cfg.websrv?.onara;
const cookieOnara = req.cookies?.f0ck_onara !== undefined const cookieOnara = req.cookies?.f0ck_onara !== undefined
? (req.cookies.f0ck_onara === '1' || req.cookies.f0ck_onara === 'true') ? (req.cookies.f0ck_onara === '1' || req.cookies.f0ck_onara === 'true')
: (req.cookies?.onara !== undefined ? (req.cookies.onara === '1' || req.cookies.onara === 'true') : null); : (req.cookies?.onara !== undefined ? (req.cookies.onara === '1' || req.cookies.onara === 'true') : null);
const isOnara = cookieOnara !== null // Global config onara overweighs always the user pref
? cookieOnara const isOnara = (cfgOnara !== undefined && cfgOnara !== null)
: !!(req.session?.onara || (cfg.onara !== undefined ? cfg.onara : cfg.websrv?.onara)); ? !!cfgOnara
: (cookieOnara !== null ? cookieOnara : !!req.session?.onara);
if (mode === 'item' && isOnara) { if (mode === 'item' && isOnara) {
const itemHtml = tpl.render('ajax-item', data, req); const itemHtml = tpl.render('ajax-item', data, req);
+22 -1
View File
@@ -1947,7 +1947,14 @@ process.on('uncaughtException', err => {
enable_comments: cfg.enable_comments !== false, enable_comments: cfg.enable_comments !== false,
public_nsfw: !!cfg.websrv.public_nsfw, public_nsfw: !!cfg.websrv.public_nsfw,
public_untagged: !!cfg.websrv.public_untagged, public_untagged: !!cfg.websrv.public_untagged,
onara: !!(cfg.onara !== undefined ? cfg.onara : cfg.websrv?.onara), get onara() {
const c = cfg.onara !== undefined ? cfg.onara : cfg.websrv?.onara;
return !!c;
},
get onara_cfg() {
const c = cfg.onara !== undefined ? cfg.onara : cfg.websrv?.onara;
return (c !== undefined && c !== null) ? !!c : null;
},
is_onara_item: false, is_onara_item: false,
enable_private_uploads: cfg.enable_private_uploads !== false, enable_private_uploads: cfg.enable_private_uploads !== false,
get enable_expiring_uploads() { return getEnableExpiringUploads(); }, get enable_expiring_uploads() { return getEnableExpiringUploads(); },
@@ -2147,12 +2154,26 @@ process.on('uncaughtException', err => {
data.max_file_size = lib.formatSize(cfg.main.maxfilesize * (activeReq.session?.admin ? cfg.main.adminmultiplier : 1)); data.max_file_size = lib.formatSize(cfg.main.maxfilesize * (activeReq.session?.admin ? cfg.main.adminmultiplier : 1));
data.max_file_size_bytes = Math.floor(cfg.main.maxfilesize * (activeReq.session?.admin ? cfg.main.adminmultiplier : 1)); data.max_file_size_bytes = Math.floor(cfg.main.maxfilesize * (activeReq.session?.admin ? cfg.main.adminmultiplier : 1));
data.web_url_upload = data.web_url_upload !== undefined ? data.web_url_upload : !!cfg.websrv.web_url_upload; data.web_url_upload = data.web_url_upload !== undefined ? data.web_url_upload : !!cfg.websrv.web_url_upload;
const cfgOnara = cfg.onara !== undefined ? cfg.onara : cfg.websrv?.onara;
const cookieOnara = activeReq.cookies?.f0ck_onara !== undefined
? (activeReq.cookies.f0ck_onara === '1' || activeReq.cookies.f0ck_onara === 'true')
: (activeReq.cookies?.onara !== undefined ? (activeReq.cookies.onara === '1' || activeReq.cookies.onara === 'true') : null);
const effectiveOnara = (cfgOnara !== undefined && cfgOnara !== null)
? !!cfgOnara
: (cookieOnara !== null ? cookieOnara : !!activeReq.session?.onara);
data.onara = effectiveOnara;
data.onara_cfg = (cfgOnara !== undefined && cfgOnara !== null) ? !!cfgOnara : null;
} else { } else {
data.recaptcha_enabled = perRequestRecaptcha; data.recaptcha_enabled = perRequestRecaptcha;
data.theme = data.theme || cfg.websrv.theme || 'f0ck'; data.theme = data.theme || cfg.websrv.theme || 'f0ck';
data.user_strict_bool = false; data.user_strict_bool = false;
data.user_logged_in_bool = false; data.user_logged_in_bool = false;
globals.csrf_token = data.csrf_token || ''; globals.csrf_token = data.csrf_token || '';
const cfgOnara = cfg.onara !== undefined ? cfg.onara : cfg.websrv?.onara;
data.onara = !!cfgOnara;
data.onara_cfg = (cfgOnara !== undefined && cfgOnara !== null) ? !!cfgOnara : null;
} }
return originalRender.call(tpl, view, data, (data.url && data.url !== req?.url) ? undefined : req); return originalRender.call(tpl, view, data, (data.url && data.url !== req?.url) ? undefined : req);
+9 -3
View File
@@ -253,11 +253,17 @@
</div> </div>
<div class="setting-item" style="margin-top: 15px;"> <div class="setting-item" style="margin-top: 15px;">
<label for="onara_enabled_toggle" style="cursor: pointer; display: flex; align-items: center; gap: 10px;"> <label for="onara_enabled_toggle" style="cursor: @if(onara_cfg !== undefined && onara_cfg !== null) not-allowed @else pointer @endif; display: flex; align-items: center; gap: 10px;">
<input type="checkbox" id="onara_enabled_toggle"> <input type="checkbox" id="onara_enabled_toggle" @if(onara_cfg !== undefined && onara_cfg !== null) disabled @if(onara_cfg) checked @endif @endif>
<span>Enable Onara viewer</span> <span>Enable Onara viewer</span>
</label> </label>
<small class="text-muted" style="margin-left: 25px;">Open items in the fullscreen Onara modal instead of navigating to the item page. Fine-tune appearance in the <strong>Tuner → Background</strong> tab.</small> <small class="text-muted" style="margin-left: 25px;">
@if(onara_cfg !== undefined && onara_cfg !== null)
Controlled by site configuration (@if(onara_cfg)enabled globally@else disabled globally@endif).
@else
Open items in the fullscreen Onara modal instead of navigating to the item page. Fine-tune appearance in the <strong>Tuner → Background</strong> tab.
@endif
</small>
</div> </div>
<div class="setting-item" style="margin-top: 15px;"> <div class="setting-item" style="margin-top: 15px;">
+2 -1
View File
@@ -231,7 +231,7 @@
@endif @endif
@if(private_society && !session) @if(private_society && !session)
<script> <script>
window.f0ckSession = { logged_in: false, is_anon: false, excluded_tags: [], onara: @if(onara) true @else false @endif, enable_item_slugs: @if(enable_item_slugs) true @else false @endif, enable_anonymous_access: @if(enable_anonymous_access) true @else false @endif, anon_permissions: {{ anon_permissions_json || '{}' }}, hide_comments_from_public: @if(hide_comments_from_public) true @else false @endif, guest_anonymize: @if(guest_anonymize) true @else false @endif, anon_anonymize: @if(anon_anonymize) true @else false @endif, is_anonymized: @if(is_anonymized) true @else false @endif, enable_xd_score: @if(enable_xd_score) true @else false @endif, default_theme: "{{ default_theme }}", show_content_warning: @if(show_content_warning) true @else false @endif, use_new_layout: @if(default_layout === 'legacy')false @else true @endif, comment_display_mode: {{ comment_display_mode }}, comment_max_length: {{ comment_max_length !== null && comment_max_length !== undefined ? comment_max_length : 'null' }}, development: @if(development) true @else false @endif, allow_comment_deletion: @if(allow_comment_deletion) true @else false @endif, hide_sidebar_default: @if(hide_sidebar_default) true @else false @endif, public_untagged: @if(public_untagged) true @else false @endif, public_nsfw: @if(public_nsfw) true @else false @endif }; window.f0ckSession = { logged_in: false, is_anon: false, excluded_tags: [], onara: @if(onara) true @else false @endif, onara_cfg: @if(onara_cfg !== undefined && onara_cfg !== null) @if(onara_cfg) true @else false @endif @else null @endif, enable_item_slugs: @if(enable_item_slugs) true @else false @endif, enable_anonymous_access: @if(enable_anonymous_access) true @else false @endif, anon_permissions: {{ anon_permissions_json || '{}' }}, hide_comments_from_public: @if(hide_comments_from_public) true @else false @endif, guest_anonymize: @if(guest_anonymize) true @else false @endif, anon_anonymize: @if(anon_anonymize) true @else false @endif, is_anonymized: @if(is_anonymized) true @else false @endif, enable_xd_score: @if(enable_xd_score) true @else false @endif, default_theme: "{{ default_theme }}", show_content_warning: @if(show_content_warning) true @else false @endif, use_new_layout: @if(default_layout === 'legacy')false @else true @endif, comment_display_mode: {{ comment_display_mode }}, comment_max_length: {{ comment_max_length !== null && comment_max_length !== undefined ? comment_max_length : 'null' }}, development: @if(development) true @else false @endif, allow_comment_deletion: @if(allow_comment_deletion) true @else false @endif, hide_sidebar_default: @if(hide_sidebar_default) true @else false @endif, public_untagged: @if(public_untagged) true @else false @endif, public_nsfw: @if(public_nsfw) true @else false @endif };
window.f0ckDebug = window.f0ckSession.development ? console.log.bind(console) : () => {}; window.f0ckDebug = window.f0ckSession.development ? console.log.bind(console) : () => {};
window.f0ckDefaultThumbSize = "{{ default_thumb_size }}"; window.f0ckDefaultThumbSize = "{{ default_thumb_size }}";
(() => { (() => {
@@ -471,6 +471,7 @@
<script> <script>
window.f0ckSession = { window.f0ckSession = {
onara: @if(onara) true @else false @endif, onara: @if(onara) true @else false @endif,
onara_cfg: @if(onara_cfg !== undefined && onara_cfg !== null) @if(onara_cfg) true @else false @endif @else null @endif,
public_untagged: @if(public_untagged) true @else false @endif, public_untagged: @if(public_untagged) true @else false @endif,
public_nsfw: @if(public_nsfw) true @else false @endif, public_nsfw: @if(public_nsfw) true @else false @endif,
enable_item_slugs: @if(enable_item_slugs) true @else false @endif, enable_item_slugs: @if(enable_item_slugs) true @else false @endif,