FeatherWiki/extensions/data-import-export_hu.js

236 lines
12 KiB
JavaScript

/**
* This file is part of Feather Wiki.
*
* Feather Wiki is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* Feather Wiki is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with Feather Wiki. If not, see https://www.gnu.org/licenses/.
*/
// Ez a kiegészítő egy „Adatkezelés” szekciót ad a Wiki beállítások oldalához, amely lehetővé teszi, hogy több HTML, Markdown és egyszerű szöveges fájlt importáljon új oldalaként a wikibe és
// exportálja a jelenlegi wiki összes oldalát HTML vagy Markdown fájlként. Exportáláskor egy CDN-ből tölti be a JSZip modult, hogy az összes oldalt egyetlen .zip fájlba csomagolja.
// A wiki teljes nyers JSON-adatait is exportálhatja tömörítetlen formában, de ez kevésbé hasznos lehet számára, ha nem tudja, hogyan lehet a JSON-adatokat manipulálni.
FW.ready(() => {
const { state, emitter } = FW;
console.log('a data-import-export.js futtatása');
window.FW.data = {
import: () => {
const input = html`<input type="file" multiple accept="text/html,.md,.markdown,text/plain" onchange=${e => {
const { files } = e.target;
if (files.length < 1) return;
function readFile(file, callback) {
const reader = new FileReader();
reader.onload = e => callback(e.target.result);
reader.onerror = e => callback(null);
reader.readAsText(file);
}
let processed = 0;
for (let i = 0; i < files.length; i++) {
const f = files[i];
readFile(f, (content => {
if (content) {
const pg = window.FW.data['parse' + (f.type === 'text/html' ? 'Html' : 'Text')](f, content);
pg.content = window.FW.img.fix(pg.content, true);
state.p.pages.push(pg);
emitter.emit(state.events.CHECK_CHANGED);
}
processed++;
}));
}
const checkDone = setInterval(() => {
if (processed >= files.length) {
clearInterval(checkDone);
emitter.emit(state.events.NOTIFY, 'Az importálás befejeződött. Ne felejtse el menteni a wikijét!');
}
}, 300);
}} />`;
document.body.appendChild(input);
input.click();
document.body.removeChild(input);
},
parseHtml: (file, content) => {
let name;
if (content.includes('</head>')) {
const headContent = content.replace(/.*?<head[^>]*?>(.*)?<\/head>.*/ims, '$1');
const head = html`<head>${html.raw(headContent)}</head>`;
if (head.querySelector('title')) {
name = head.querySelector('title').innerText;
}
}
const body = content.includes('</body>') ? content.replace(/.*?<body[^>]*?>(.*)?<\/body>.*/ims, '$1') : content;
const doc = html`<body>${html.raw(body)}</body>`;
if (!name && doc.querySelector('h1')) {
name = doc.querySelector('h1').innerText;
}
const pg = window.FW.data.generatePage(name ?? file.name);
if (pg.cd > (file.lastModified ?? (Date.now() + 1))) {
pg.cd = file.lastModified;
}
pg.content = doc.innerHtml;
return pg;
},
parseText: (file, content) => {
let name, tags, headContent;
if (content.match(/^---/) !== null) {
headContent = content.replace(/^---\r?\n(.*)?\r?\n---.*/ms, '$1');
if (headContent.match(/^(name|title): */im) !== null) {
name = headContent.replace(/.*\n(?:name|title): *(.+?)\r?\n.*/ims, '$1');
}
if (headContent.match(/^tags: */im) !== null) {
if (headContent.match(/^tags:\s*^- /ims) !== null) {
tags = headContent.replace(/.*\ntags:\s*(- .+)+.*/ims, '$1').replace(/- /g, '').split('\n');
} else {
tagsRaw = headContent.replace(/.*\ntags: *(.+).*/ims, '$1');
try {
if (tagsRaw.includes('[')) {
tags = JSON.parse(tagsRaw);
} else {
tags = tagsRaw.split(',');
}
} catch (e) {}
}
}
}
const body = headContent ? content.replace(new RegExp('---\\r?\\n' + headContent + '\\r?\\n---\\r?\\n'), '') : content;
const doc = html`<body>${md(body)}</body>`;
if (!name && doc.querySelector('h1')) {
name = doc.querySelector('h1').innerText;
}
const pg = window.FW.data.generatePage(name ?? file.name);
if (pg.cd > (file.lastModified ?? (Date.now() + 1))) {
pg.cd = file.lastModified;
}
pg.content = body;
pg.editor = 'md';
if (tags) pg.tags = tags.map(t => t.trim()).filter(t => t.length).join(',');
return pg;
},
generatePage: name => {
const genId = () => {
const s = [];
for (let i = 0; i < 9; i++) {
const code = Math.round(Math.random() * (126 - 32)) + 32;
s.push(String.fromCharCode(code));
}
return s.join('');
}
let id;
do {
id = genId();
}
while (state.p.pages.findIndex(p => p.id === id) >= 0);
// Ensure unique slug
let d = 0,
s = FW.slug(name),
slug;
do {
slug = s + (d > 0 ? '_' + d : '');
d++;
}
while (state.p.pages.some(pp => pp.slug === slug))
return { id, name, slug, cd: Date.now() };
},
export: () => {
// If JSZip hasn't been loaded yet, load it before running the export
if (typeof JSZip === 'undefined') {
const jszipScript = document.createElement('script');
document.body.appendChild(jszipScript);
jszipScript.onload = () => window.FW.data.doExport();
jszipScript.src = 'https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js';
} else {
window.FW.data.doExport();
}
},
dl: (mime, b64, suffix) => {
const download = html`<a href="data:${mime};base64,${b64}" download="${window.FW.slug(state.p.name)}${suffix}">download</a>`;
document.body.appendChild(download);
download.click();
document.body.removeChild(download);
},
doExport: () => {
var zip = new JSZip();
state.p.pages.forEach(pg => {
let filename = pg.slug;
let content = window.FW.inject.img(window.FW.img.fix(pg.content ?? ''));
// replace internal links
(content?.match(/\[\[.+?(?=\]\])/g) ?? []).forEach(l => {
const match = l.replace('[[', '').split('|');
const slug = match[1] ? match[1].trim() : window.FW.slug(match[0]);
const exists = state.p.pages.find(pg => pg.slug === slug);
if (exists) {
content = content.replace(
`${l}]]`,
`<a href="${slug}.${exists.editor === 'md' ? 'md' : 'html'}">${match[0]}</a>`
);
}
});
if (pg.editor === 'md') {
filename += '.md';
if (pg.tags) {
content = 'Tagged: ' + pg.tags + '\n\n' + content;
}
content = '# ' + pg.name + '\n\n' + content;
} else {
filename += '.html';
if (pg.tags) {
content = '<p>Tagged: ' + pg.tags + '</p>\n' + content;
}
content = '<h1>' + pg.name + '</h1>\n' + content;
}
zip.file(filename, content);
});
zip.generateAsync({type:"base64"}).then(function (base64) {
window.FW.data.dl('application/zip', base64, '_pages.zip');
});
},
exportJson: () => {
window.FW.data.dl('application/json', btoa(unescape(encodeURIComponent(JSON.stringify(state.p)))), '.json');
},
exportStaticHtml: () => {
const css = state.c + ' main>section>header{display:none;} @media print{main>.sb{display:none;}main>section>article{page-break-after:always;}main>section>header{display:unset;}}';
const st = { ...state, c: css, p: { ...state.p, static: true, published: true } };
const static = FW.gen(st)
.replace(/<script.+?<\/script>/gsm, '')
.replace(/<\/main> <footer>.+?<\/footer>/g, '</main>')
// Insert print-only header
.replace('</ul></nav></div> <section>', `</ul></nav></div> <section><header><span class=db><a href=? class=t>${st.p.name}</a></span>${ st.p.desc ? `<p class=pb>${st.p.desc}</p>` : ''}<hr></header>`);
window.FW.data.dl('text/html', btoa(unescape(encodeURIComponent(static))), '.html');
},
};
['DOMContentLoaded', 'render'].forEach(ev => {
emitter.on(ev, () => {
setTimeout(() => { // Adds a very small delay so it injects after render when elements exist in DOM
renderImportExportSection();
}, 50);
});
});
emitter.emit('DOMContentLoaded');
function renderImportExportSection () {
// Don't run if not on settings page or if already rendered
if (state.query.page !== 's' || !!document.querySelector('#dataManagementSection')) return;
const section = html`<section id="dataManagementSection" style="margin-top:20px;">
<h1>Adatkezelés</h1>
<div class="r">
<div class="c w12">
<button onclick=${() => window.FW.data.import()} style="margin-right:10px;">Fájlok Importálása Oldalakként</button>
<p class="h">Válasszon ki 1 vagy több HTML-, Markdown- és egyszerű szöveges fájlt, amelyeket oldalakként szeretne importálni.</p>
<p class="h">Ha a HTML fájlok rendelkeznek egy <code>title</code> elemmel a <code>head</code> elemen belül, vagy egy <code>H1</code> elemet tartalmaznak, szövegük az oldal címe lesz. Egyéb esetben az oldal címe a fájl neve, kiterjesztés nélkül.</p>
<p class="h">Ha a Markdown fájlok rendelkeznek YAML frontmatterrel, a <code>name</code> vagy a <code>title</code> mező lesz az oldal címe és a <code>tags</code> mező a címkéket határozza meg. Egyéb esetben az első <code>#</code> címsorszint (ha légezik), vagy a fájl neve kiterjesztés nélkül lesz használva.</p>
<p class="h">Az importáló mindent megtesz a tartalom és a fájltípus elemzése érdekében, de különböző felhasználási esetek kimaradhatnak. Minél egyszerűbbek a fájlok, annál jobbak lesznek az eredmények.</p>
</div>
<div class="c w12">
<button onclick=${() => window.FW.data.export()}>Oldalak Exportálása</button>
<p class="h">Összegyűjt minden wiki oldalt egy <code>.zip</code> fájlba, az oldalakat slugjuknak megfelelően elnevezve. Az oldalak neve egy <code>H1</code> elemben lesz megadva a fájlok elején, minden létező címke ezek alatt lesz megtalálható.</p>
<p class="h">A szülő/gyerek kapcsolatok <em>nem</em> lesznek megőrizve a kiterjesztéssel való exportáláskor, valamint minden kép az oldal tartalmába lesz beszúrva. Belső hivatkozások HTML hivatkozásokká alakulnak át, megfelelően hivatkozva a céloldal HTML fájljára.</p>
<p class="h">Megjegyzés: az exportban a wiki beállítások oldalán megadott tartalom nem lesz elérhető.</p>
<button onclick=${() => window.FW.data.exportJson()}>Nyers JSON Adat exportálása</button>
<button onclick=${() => window.FW.data.exportStaticHtml()}>Statikus HTML Exportálása</button>
</div>
</div>
</section>`;
const page = document.querySelector('article.mw');
page.appendChild(section);
}
});