[js 片段] 发布时只显示单篇文档_release_v1.0
功能介绍
-
点击分享按钮, 复制 url 到剪切板, 黑色是分享单篇的按钮, 蓝色的是分享单篇及所有子文档的按钮
-
可以直接通过 url 访问单篇文档 或 单篇及所有子文档
- 单篇 url 示例:
http://127.0.0.1:6808/stage/build/desktop/?id=20250125151959-7as5mvq
- 单篇及所有子文档 示例:
http://127.0.0.1:6808/stage/build/desktop/?id=20250125151959-7as5mvq&sub_file=true
- 单篇 url 示例:
重要
重要! 重要! 重要!
要手动修改 cfg 下的 host_ip
host_ip 获取方法: 思源设置-> 发布-> 服务访问地址(随便挑一个)
免责说明
-
脚本核心思路是将各种元素隐藏, 来达到 好像被限制了的效果, 实际上并不能完全限制
比如: 可以通过全局搜索, 搜之后打开文档; 通过手动修改页面 css 访问 等如果介意, 请不要食用
-
测得不多, 有 bug 欢迎反馈
/* ### [js片段] 发布时只显示单篇文档_release_v1.0 #### 功能介绍 1. 点击分享按钮, 复制url到剪切板, 黑色是分享单篇的按钮, 蓝色的是分享单篇及所有子文档的按钮 2. 可以直接通过url访问单篇文档 或 单篇及所有子文档 * 单篇url 示例: `http://127.0.0.1:6808/stage/build/desktop/?id=20250125151959-7as5mvq` * 单篇及所有子文档 示例: `http://127.0.0.1:6808/stage/build/desktop/?id=20250125151959-7as5mvq&sub_file=true` #### 重要 **重要! 重要! 重要!** 要手动修改 cfg下的host_ip host_ip获取方法: 思源设置->发布->服务访问地址(随便挑一个) #### 免责说明 1. 脚本核心思路是将各种元素隐藏, 来达到 好像被限制了的效果, 实际上并不能完全限制 比如: 可以通过全局搜索, 搜之后打开文档; 通过手动修改页面css访问 等 如果介意, 请不要食用 2. 测得不多, 有bug欢迎反馈 */ (() => { /***************************自主配置begin**************************************/ // 修改配置后, 需要刷新页面(设置->快捷键->刷新), 否则会有问题 const cfg = { forbidden_root_file_access: true, // 禁止访问根目录 forbidden_other_file_access: true, // 禁止访问其他文件 host_ip: "127.0.0.1:6808", // }; /***************************自主配置end****************************************/ // 生成唯一ID用于日志标识 const SESSION_ID = '单篇发布js_' + Date.now(); function mlog(...args) { const now = new Date(); const hours = String(now.getHours()).padStart(2, '0'); // 获取小时并补零 const minutes = String(now.getMinutes()).padStart(2, '0'); // 获取分钟并补零 const seconds = String(now.getSeconds()).padStart(2, '0'); // 获取秒数并补零 const milliseconds = String(now.getMilliseconds()).padStart(3, '0'); // 获取毫秒并补零 const timeString = `${hours}:${minutes}:${seconds}.${milliseconds}`; // 形成 hh:mm:ss.SSS 格式 console.log(`[${SESSION_ID}] [${timeString}]:`, ...args); } function while_exist(flag_func, func_cb, max_cnt = 0) { let cnt = 0; let initInterval = setInterval(() => { cnt++; mlog("cnt: ", cnt) const flag = flag_func() if (flag) { clearInterval(initInterval); func_cb(flag); } if (cnt >= max_cnt && max_cnt != 0) { // 超次数退出 clearInterval(initInterval); } }, 200); } async function sendPushMessageUrl(url, msg) { mlog(msg) return fetch(url, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ msg: msg, timeout: 2000 // 如果不传 timeout,默认值为 7000 }), }) .then((response) => { if (response.ok) { mlog('响应数据:' + response.text()); } else { throw new Error("Failed to get file content"); } }) .catch((error) => { mlog('请求失败:', error); }); }; async function sendPushMessage(msg) { sendPushMessageUrl("/api/notification/pushMsg", msg); } async function sendErrorPushMessage(msg) { sendPushMessageUrl("/api/notification/pushErrMsg", msg); } function hide_ele(selector_str) { const style = document.createElement('style'); style.textContent = `${selector_str} { display: none; }`; // 将 <style> 添加到 <head> 中 document.head.appendChild(style); } function set_ele_display(selector_str, val) { const ele = document.querySelector(selector_str) if (ele) { ele.style.display = val; } } function handle_forbidden_other_file() { // 禁止访问其他文件, 就是把书签和标签隐藏 if (!cfg.forbidden_other_file_access) return; mlog("隐藏标签等按钮, 防止通过这些按钮访问其他文件") const basic_selector_list = [ '[data-type="bookmark"]' , // 书签 'span[data-type="tag"]' , // 标签 '[data-type="graph"]' , // 关系图 '[data-type="globalGraph"]', // 全局关系图 '[data-type="backlink"]' , // 反链 '#barCommand' , // 命令面板 '#barSearch' , // 全局搜索 '[data-type="doc"]' , // 文档菜单 '.layout__center [data-type="wnd"]>.fn__flex', // 页签 ] basic_selector_list.forEach(selector_str => { hide_ele(selector_str) }) } // 禁止访问根目录, 就是把文档树隐藏 function handle_forbidden_root_file() { if (cfg.forbidden_root_file_access) { mlog("隐藏文档树, 防止访问") hide_ele('#layouts') } } // 处理发布单篇文件 function handle_share_web_page_one_file() { mlog('发布单篇文件, 隐藏文档树') // 隐藏文档树按钮和文档树 hide_ele('[data-type="file"]') set_ele_display('.layout__dockl', 'none') // 监听大纲按钮 const outline_ele = document.querySelector('[data-type="outline"]') if (!outline_ele) return; const outline_click_cb = (event) => { // 隐藏文档树的时候, 将这个元素隐藏了, 回导致大纲也无法显示 // 监听到大纲按钮时, 将这个元素恢复一下, 然后取消监听 set_ele_display('.layout__dockl', 'flex'); outline_ele.removeEventListener("click", outline_click_cb, true); }; outline_ele.addEventListener("click", outline_click_cb, true); } // 找到当前打开的文档的li元素 function find_file_ele_li_open_now() { document.querySelector('[data-type="focus"]')?.click(); return document.querySelector('.sy__file li.b3-list-item--focus'); } // 处理发布单篇及子文档 function handle_share_web_page_sub_file(li_ele) { if (!li_ele) return; document.querySelectorAll('.sy__file>.fn__flex-1>ul li').forEach(li_node => { // ul.remove(); if (li_node != li_ele) { li_node.style.display = 'none'; } }) // 隐藏定位按钮, 折叠按钮 hide_ele('[data-type="focus"]') hide_ele('[data-type="collapse"]') // 设置折叠按钮为关 li_ele.querySelector('span.b3-list-item__toggle--hl>svg.b3-list-item__arrow--open')?.parentElement.click() } // 处理单篇发布功能 function handle_share_web_page(url) { // 禁止访问其他文件 handle_forbidden_other_file(); if (!url.includes('?id=') && !url.includes('&id=')) { // 禁止访问根目录 handle_forbidden_root_file(); return; } if (url.includes('&sub_file=true')) { mlog('发布单篇文件和子文档, 隐藏父级文档') while_exist(find_file_ele_li_open_now, li_ele => { mlog(li_ele) // 发布单篇&子文档 handle_share_web_page_sub_file(li_ele); // 监听文档树按钮, 再次执行隐藏函数 document.querySelector('[data-type="file"]')?.addEventListener("click", event => handle_share_web_page_sub_file(li_ele), true); }) } else { // 发布单篇 handle_share_web_page_one_file(); } } // 分享按钮被点击 function share_btn_click_event(btn_type) { // 获取当前文件id const file_id = find_file_ele_li_open_now()?.getAttribute('data-node-id'); if (file_id == null || file_id.length == 0) { sendErrorPushMessage('没有打开的文档, 请检查') return; } // 拼接分享url let url = `http://${cfg.host_ip}/stage/build/desktop/?id=${file_id}` if (btn_type == "allsub") { url = url + '&sub_file=true'; } // 复制到剪切板 navigator.clipboard.writeText(url).then(() => { sendPushMessage('url已经复制到剪切板'); }) } // 增加分享按钮 function insert_and_listener_share_btn() { while_exist(() => { return document.querySelector('#drag') }, () => { // 定义要插入的 HTML 结构 const htmlString = ` <div id="share_web_page_one" class="toolbar__item ariaLabel" data-position="right" aria-label="分享当前文档, 点击复制url"> <svg><use xlink:href="#iconOpenWindow"></use></svg> </div> <div id="share_web_page_allsub" class="toolbar__item ariaLabel" data-position="right" aria-label="分享当前文档和所有子文档, 点击复制url"> <svg><use xlink:href="#iconOpenWindow" style="color: cornflowerblue;"></use></svg> </div> `; // 插件按钮 const plugin_btn = document.querySelector('#drag'); // 插入 HTML if (plugin_btn) { plugin_btn.insertAdjacentHTML('afterend', htmlString); // 选择刚插入的元素 const share_one = plugin_btn.nextElementSibling; const share_allsub = share_one.nextElementSibling; // 监听点击事件 share_one.addEventListener('click', (event) => share_btn_click_event('one')); share_allsub.addEventListener('click', (event) => share_btn_click_event('allsub')); } }) } function listener_hotkey_event() { } // 初始化并监听点击事件 while_exist(() => { return document.querySelector('.sy__file>.fn__flex-1'); }, treeContainer => { mlog('开始', location.pathname, siyuan.config.readonly) if (location.pathname == '/stage/build/desktop/' || siyuan.config.readonly) { // 网页 && 只读 handle_share_web_page(location.href); } else { // 增加分享按钮 insert_and_listener_share_btn(); // 监听分享快捷键 listener_hotkey_event(); } }) })()
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于