Skip to content

Commit

Permalink
Merge pull request #776 from terwer/feature/hotfix
Browse files Browse the repository at this point in the history
feat: #774 #751 知乎平台支持公式和表格
  • Loading branch information
terwer committed Oct 10, 2023
2 parents 10816dc + 7af4590 commit 1f4c905
Show file tree
Hide file tree
Showing 16 changed files with 237 additions and 26 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ Publish articles from siyuan-note to platforms such as Yuque, Notion, Cnblogs, W

Support features such as fast publishing, image bed management, platform expansion, smart labels, etc.

> Latest New Features: 🎉 Release of Publish Tool New Version `1.15.0`
- `1.15.1` addresses minor bug fixes for the Juejin platform, resolving issues related to duplicate tags during article editing.
- This update introduces the Halo platform. After upgrading to `1.15.0+`, you can publish to the Halo2 site.
- It also fixes issues related to YAML parsing in certain scenarios, and errors when tags are empty on some platforms.
- Oh, in version `1.14.0` and beyond, the author quietly developed an import feature. You can use the `Settings` -> `Publish Settings` -> `Import Predefined Platforms` feature to quickly import builtin platforms 😄
🎉 Exciting News: New tool version `1.16.0` is out!

- `1.16.0` focuses on enhancing support for **formulas** and **tables** on Zhihu. Additionally, GitHub now allows custom author homepage configuration.
- `1.15.1` addresses minor issues like fixing publishing errors on Juejin, handling anomalies during article deletion, and resolving tag duplication during article editing.
- Introducing support for the Halo platform! After upgrading to `1.15.0+`, you can seamlessly publish content to Halo2 sites.
- In `1.15.0`, fixes were made for misinterpretation of YAML in certain scenarios and publishing errors on platforms when tags were empty.
- Oh, by the way, starting from version `1.14.0`, the author secretly developed an import feature. You can swiftly import built-in platforms using the `Settings` -> `Publish Settings` -> `Import Predefined Platforms` feature 😄.

> Platform Limitations:
- **WeChat Official Accounts**: Publishing Tool now supports posting articles to the WeChat Official Accounts draft box in version `1.13.0` and beyond. However, due to limitations on the WeChat platform in Electron, normal login operations are not possible. Therefore, you will need to visit `https://mp.weixin.qq.com/` yourself, complete the login, copy the cookie, and then paste it into the configuration options.
Expand Down
7 changes: 4 additions & 3 deletions README_zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@

将思源笔记的文章发布到语雀、Notion、Cnblogs、WordPress、Typecho、Hexo、知乎 等平台,支持极速发布、图床管理、平台扩展、智能标签等特色功能。

> 最近的新特性版本:🎉 发布工具新版本 `1.15.0` 发布
> 最近的新特性版本:🎉 发布工具新版本 `1.16.0` 发布
- `1.15.1` 小版本修复掘金平台发布错误的问题,删除一次文艺以及文章编辑时候标签重复问题。
- `1.16.0` 主要对知乎平台进行**公式****表格**支持。另外,对于GitHub,也支持配置自定义作者主页了。
- `1.15.1` 小版本修复掘金平台发布错误的问题,部分场景下文章删除异常问题,以及文章编辑时候标签重复问题。
- 新增 Halo 平台,升级到 `1.15.0+` 之后可发布到 Halo2 站点。
- 同时修复了 yaml 部分场景五解析、标签为空的时候部分平台发布错误的问题。
- `1.15.0` 修复了 yaml 部分场景误解析、标签为空的时候部分平台发布错误的问题。
- 咳咳,另外,在 `1.14.0` 版本之后,作者还悄悄开发了导入功能。您可以使用 `设置` -> `发布设置` -> `导入预定义平台` 功能快速导入内置平台😄

> 平台限制说明
Expand Down
4 changes: 2 additions & 2 deletions esbuild.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ const isWindows = os.platform() === "win32"

let baseDir
if (isWatch || isServe) {
baseDir = "/Users/terwer/Documents/mydocs/SiYuanWorkspace/test/data/plugins/siyuan-plugin-publisher"
// baseDir = "/Users/zhangyue/Documents/terwer/SiyuanWorkspace/test/data/plugins/siyuan-plugin-publisher"
// baseDir = "/Users/terwer/Documents/mydocs/SiYuanWorkspace/test/data/plugins/siyuan-plugin-publisher"
baseDir = "/Users/zhangyue/Documents/terwer/SiyuanWorkspace/test/data/plugins/siyuan-plugin-publisher"
// baseDir = "/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/data/plugins/siyuan-plugin-publisher"
if (isWindows) {
baseDir = "C:\\Users\\terwer\\Documents\\mydocs\\SiyuanWorkspace\\test\\data\\plugins\\siyuan-plugin-publisher"
Expand Down
7 changes: 7 additions & 0 deletions src/adaptors/api/base/github/commonGithubConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import { PasswordType } from "zhi-blog-api"
import { CommonBlogConfig } from "~/src/adaptors/api/base/commonBlogConfig.ts"
import { StrUtil } from "zhi-common"

/**
* CommonGithubConfig 类用于存储 GitHub 相关配置信息
Expand Down Expand Up @@ -60,6 +61,11 @@ class CommonGithubConfig extends CommonBlogConfig {
*/
public email: string

/**
* 作者主页
*/
public site: string

/**
* Markdown文件名规则(占位符:[yyyy] [MM] [dd] [slug] [filename] )
*/
Expand Down Expand Up @@ -111,6 +117,7 @@ class CommonGithubConfig extends CommonBlogConfig {
this.defaultMsg = "auto published by siyuan-plugin-publisher"
this.author = "terwer"
this.email = "youweics@163.com"
this.site = StrUtil.pathJoin(this.home, "/" + this.username)
this.mdFilenameRule = "[filename].md"
this.useMdFilename = false
this.usePathCategory = false
Expand Down
4 changes: 2 additions & 2 deletions src/adaptors/api/vuepress/vuepressYamlConverterAdaptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ class VuepressYamlConverterAdaptor extends YamlConvertAdaptor {
}

// author
let githubUrl = "https://github.com/terwer"
const githubCfg = cfg as CommonGithubConfig
if (githubCfg.home) {
let githubUrl = githubCfg.site
if (StrUtil.isEmptyString(githubCfg.site)) {
githubUrl = StrUtil.pathJoin(githubCfg.home, "/" + githubCfg.username)
}
yamlFormatObj.yamlObj.author = {
Expand Down
4 changes: 2 additions & 2 deletions src/adaptors/web/wechat/wechatWebAdaptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -516,9 +516,9 @@ class WechatWebAdaptor extends BaseWebApi {

// 发布格式
if (cfg?.pageType == PageTypeEnum.Markdown) {
post.description = post.markdown
updatedPost.description = updatedPost.markdown
} else {
post.description = post.html
updatedPost.description = updatedPost.html
}

return updatedPost
Expand Down
98 changes: 98 additions & 0 deletions src/adaptors/web/zhihu/zhihuUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright (c) 2023, Terwer . All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Terwer designates this
* particular file as subject to the "Classpath" exception as provided
* by Terwer in the LICENSE file that accompanied this code.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Terwer, Shenzhen, Guangdong, China, youweics@163.com
* or visit www.terwer.space if you need additional information or have any
* questions.
*/

import * as cheerio from "cheerio"

/**
* 知乎工具类
*
* @author terwer
* @since 1.6.0
*/
class ZhihuUtils {
/**
* 处理HTML中的表格,将表格头部移动到表格体部分
*
* @param html - 包含表格的HTML字符串
* @returns 处理后的HTML字符串
*/
public static processZHTable(html: string): string {
// 使用Cheerio加载HTML
const $ = cheerio.load(html)

// 获取thead内容
const theadContent = $("table thead").html()
// 移除thead
$("table thead").remove()
// 将thead内容添加到tbody的第一个位置
$("table tbody tr:first-child").before(theadContent)

// 选择表格元素并修改属性
const table = $("table")
table
.attr("data-draft-node", "block")
.attr("data-draft-type", "table")
.attr("data-size", "normal")
.attr("data-row-style", "normal")

return $.html()
}

public static processZHMath(html: string): string {
// 使用Cheerio加载HTML
const $ = cheerio.load(html)

// 选择所有带有类名"language-math"的<span>元素
$("span.language-math").each((index, element) => {
// 获取元素的文本内容
const mathContent = $(element).text()

// 创建替代的<img>标签
const imgTag = `<img eeimg="1" src="//www.zhihu.com/equation?tex=${encodeURIComponent(mathContent)}"
alt="${mathContent}" />`

// 用新的<img>标签替换原始元素
$(element).replaceWith(imgTag)
})

// 选择所有带有类名"language-math"的<div>元素
$("div.language-math").each((index, element) => {
// 获取元素的文本内容
const mathContent = $(element).text()

// 创建替代的<img>标签
const imgTag = `<p><img eeimg="1" src="//www.zhihu.com/equation?tex=${encodeURIComponent(mathContent)}"
alt="${mathContent}"/></p>`

// 用新的<img>标签替换原始元素
$(element).replaceWith(imgTag)
})

// 输出修改后的HTML
return $.html()
}
}

export default ZhihuUtils
36 changes: 35 additions & 1 deletion src/adaptors/web/zhihu/zhihuWebAdaptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
*/

import { BaseWebApi } from "~/src/adaptors/web/base/baseWebApi.ts"
import { CategoryInfo, Post, UserBlog } from "zhi-blog-api"
import { BlogConfig, CategoryInfo, PageTypeEnum, Post, UserBlog } from "zhi-blog-api"
import * as cheerio from "cheerio"
import { JsonUtil, StrUtil } from "zhi-common"
import CryptoJS from "crypto-js"
import { arrayToBuffer } from "~/src/utils/polyfillUtils.ts"
import { getAliOssClient } from "~/src/vendors/alioss/s3oss.ts"
import _ from "lodash"
import ZhihuUtils from "~/src/adaptors/web/zhihu/zhihuUtils.ts"

/**
* 知乎网页授权适配器
Expand Down Expand Up @@ -98,6 +100,38 @@ class ZhihuWebAdaptor extends BaseWebApi {
return result
}

public override async preEditPost(post: Post, id?: string, publishCfg?: any): Promise<Post> {
// 公共的属性预处理
const doc = await super.preEditPost(post, id, publishCfg)

// 知乎自定义的处理
const cfg: BlogConfig = publishCfg?.cfg
const updatedPost = _.cloneDeep(doc) as Post
const html = updatedPost.html
this.logger.info("准备处理知乎正文")
this.logger.debug("html =>", { html: html })
let updatedHtml = html

// 处理表格
updatedHtml = ZhihuUtils.processZHTable(updatedHtml)
// 处理数学公式
updatedHtml = ZhihuUtils.processZHMath(updatedHtml)

// 处理完毕
updatedPost.html = updatedHtml
this.logger.info("知乎正文处理完毕")
this.logger.debug("updatedHtml =>", { updatedHtml: updatedHtml })

// 发布格式
if (cfg?.pageType == PageTypeEnum.Markdown) {
updatedPost.description = updatedPost.markdown
} else {
updatedPost.description = updatedPost.html
}

return updatedPost
}

public async addPost(post: Post) {
const params = JSON.stringify({
title: post.title,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,17 @@ const props = defineProps({
})
// emits
const emit = defineEmits(["onHomeChange"])
const emit = defineEmits(["onHomeChange", "onApiUrlChange", "onUsernameChange"])
const handleHomeChange = (value: string | number): void => {
if (emit) {
emit("onHomeChange", value, formData.cfg)
}
}
const handleUsernameChange = (value: string | number): void => {
if (emit) {
emit("onUsernameChange", value, formData.cfg)
}
}
// datas
const isLoading = ref(false)
Expand Down Expand Up @@ -293,7 +298,11 @@ onMounted(async () => {
</el-form-item>
<!-- 登录名 -->
<el-form-item :label="t('setting.common.username')" v-if="props.cfg.usernameEnabled">
<el-input v-model="formData.cfg.username" :placeholder="props.cfg?.placeholder.usernamePlaceholder" />
<el-input
v-model="formData.cfg.username"
:placeholder="props.cfg?.placeholder.usernamePlaceholder"
@input="handleUsernameChange"
/>
</el-form-item>
<!-- 密码 -->
<el-form-item
Expand Down Expand Up @@ -424,4 +433,4 @@ onMounted(async () => {
.inline-tip
margin 0
padding-left 0
</style>
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,6 @@ const syncDefaultPath = (cfg: any) => {
<el-form-item :label="t('setting.blog.yamlLinkEnabled')">
<el-switch v-model="(main.cfg as any).yamlLinkEnabled" />
</el-form-item>
<el-form-item>
<a href="javascript:;" @click="toggleAdvance">{{ formData.advanceBtnText }}</a>
</el-form-item>
<!-- Github分支名 -->
<el-form-item :label="t('setting.blog.type.github.default.branch')">
<el-input
Expand All @@ -98,6 +95,9 @@ const syncDefaultPath = (cfg: any) => {
:placeholder="t('setting.blog.type.github.default.path.tip')"
/>
</el-form-item>
<el-form-item>
<a href="javascript:;" @click="toggleAdvance">{{ formData.advanceBtnText }}</a>
</el-form-item>
<div v-if="formData.showAdvancedConfig">
<!-- 提交信息 -->
<el-form-item :label="t('setting.blog.type.github.msg')">
Expand All @@ -111,6 +111,10 @@ const syncDefaultPath = (cfg: any) => {
<el-form-item :label="t('setting.blog.type.github.email')">
<el-input v-model="(main.cfg as any).email" :placeholder="t('setting.blog.type.github.email.tip')" />
</el-form-item>
<!-- 作者主页 -->
<el-form-item :label="t('setting.blog.type.github.site')">
<el-input v-model="(main.cfg as any).site" :placeholder="t('setting.blog.type.github.site.tip')" />
</el-form-item>
<!-- 文件规则 -->
<el-form-item :label="t('setting.blog.mdFilenameRule')">
<el-input
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { useVueI18n } from "~/src/composables/useVueI18n.ts"
import { useVuepressApi } from "~/src/adaptors/api/vuepress/useVuepressApi.ts"
import { VuepressConfig } from "~/src/adaptors/api/vuepress/vuepressConfig.ts"
import { VuepressPlaceholder } from "~/src/adaptors/api/vuepress/vuepressPlaceholder.ts"
import { StrUtil } from "zhi-common"
const props = defineProps({
apiType: {
Expand All @@ -46,10 +47,32 @@ vuepressPlaceholder.passwordPlaceholder = t("setting.blog.type.github.token.tip"
vuepressPlaceholder.apiUrlPlaceholder = t("setting.blog.github.apiurl.tip")
vuepressPlaceholder.previewUrlPlaceholder = t("setting.blog.previewUrl.tip")
vuepressCfg.placeholder = vuepressPlaceholder
// 处理事件的方法
const onHomeChange = (value: string, cfg: VuepressConfig) => {
if (StrUtil.isEmptyString(cfg.home) || StrUtil.isEmptyString(cfg.username)) {
cfg.site = ""
} else {
cfg.site = StrUtil.pathJoin(cfg.home, "/" + cfg.username)
}
}
const onUsernameChange = (value: string, cfg: VuepressConfig) => {
// sync site
if (StrUtil.isEmptyString(cfg.home) || StrUtil.isEmptyString(cfg.username)) {
cfg.site = ""
} else {
cfg.site = StrUtil.pathJoin(cfg.home, "/" + cfg.username)
}
}
</script>

<template>
<common-github-setting :api-type="props.apiType" :cfg="vuepressCfg">
<common-github-setting
:api-type="props.apiType"
:cfg="vuepressCfg"
@onHomeChange="onHomeChange"
@onUsernameChange="onUsernameChange"
>
<template #header="header"> </template>
<template #main="main"> </template>
<template #footer="footer"> </template>
Expand Down
Loading

0 comments on commit 1f4c905

Please sign in to comment.