diff --git a/docs/getting-started/config.md b/docs/getting-started/config.md index 8eea8be..9afa8bc 100644 --- a/docs/getting-started/config.md +++ b/docs/getting-started/config.md @@ -160,7 +160,6 @@ halo: cache: redis ``` - ### 压缩 启用压缩对于减少带宽和加快页面加载非常有用,在**未使用** `Nginx` 或 `Caddy` 等反向代理服务器时(反向代理服务器通常是默认开启 Gzip 的),可以考虑开启系统自带的 Gzip 功能。 diff --git a/docs/getting-started/install/third-party.md b/docs/getting-started/install/third-party.md index 54229cb..1a3f65c 100644 --- a/docs/getting-started/install/third-party.md +++ b/docs/getting-started/install/third-party.md @@ -12,4 +12,4 @@ title: 三方指南 - [如何在 Windows 下搭建 Halo](https://ykanade.cn/2021/08/11/HaloInWindows/) - [Halo 博客建站记录](https://catchersun.cn/archives/halo%E5%8D%9A%E5%AE%A2%E5%BB%BA%E7%AB%99%E8%AE%B0%E5%BD%95) - [Windows下搭建halo并配置nginx与SSL](https://blog.shikangsi.cn/archives/halo-windows) -- [Linux环境下如何分布式部署Halo](https://luoxx.top/archives/halo-distributed-deploy) \ No newline at end of file +- [Linux环境下如何分布式部署Halo](https://luoxx.top/archives/halo-distributed-deploy) diff --git a/i18n/zh-Hans/code.json b/i18n/zh-Hans/code.json index da01388..04b1916 100644 --- a/i18n/zh-Hans/code.json +++ b/i18n/zh-Hans/code.json @@ -19,9 +19,9 @@ "message": "请联系原始链接来源网站的所有者,并告知他们链接已损坏。", "description": "The 2nd paragraph of the 404 page" }, - "theme.AnnouncementBar.closeButtonAriaLabel": { - "message": "关闭", - "description": "The ARIA label for close button of announcement bar" + "theme.BackToTopButton.buttonAriaLabel": { + "message": "回到顶部", + "description": "The ARIA label for the back to top button" }, "theme.blog.archive.title": { "message": "历史博文", @@ -31,10 +31,6 @@ "message": "历史博文", "description": "The page & hero description of the blog archive page" }, - "theme.BackToTopButton.buttonAriaLabel": { - "message": "回到顶部", - "description": "The ARIA label for the back to top button" - }, "theme.blog.paginator.navAriaLabel": { "message": "博文列表分页导航", "description": "The ARIA label for the blog pagination" @@ -47,6 +43,10 @@ "message": "较旧的博文", "description": "The label used to navigate to the older blog posts page (next page)" }, + "theme.AnnouncementBar.closeButtonAriaLabel": { + "message": "关闭", + "description": "The ARIA label for close button of announcement bar" + }, "theme.blog.post.readingTime.plurals": { "message": "{readingTime} 分钟阅读", "description": "Pluralized label for \"{readingTime} min read\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" @@ -87,18 +87,6 @@ "message": "查看所有标签", "description": "The label of the link targeting the tag list page" }, - "theme.CodeBlock.copyButtonAriaLabel": { - "message": "复制代码到剪贴板", - "description": "The ARIA label for copy code blocks button" - }, - "theme.CodeBlock.copied": { - "message": "复制成功", - "description": "The copied button label on code blocks" - }, - "theme.CodeBlock.copy": { - "message": "复制", - "description": "The copy button label on code blocks" - }, "theme.colorToggle.ariaLabel": { "message": "切换浅色/暗黑模式(当前为{mode})", "description": "The ARIA label for the navbar color mode toggle" @@ -111,6 +99,18 @@ "message": "浅色模式", "description": "The name for the light color mode" }, + "theme.CodeBlock.copyButtonAriaLabel": { + "message": "复制代码到剪贴板", + "description": "The ARIA label for copy code blocks button" + }, + "theme.CodeBlock.copied": { + "message": "复制成功", + "description": "The copied button label on code blocks" + }, + "theme.CodeBlock.copy": { + "message": "复制", + "description": "The copy button label on code blocks" + }, "theme.docs.DocCard.categoryDescription": { "message": "{count} 个项目", "description": "The default description for a category card in the generated index about how many items this category includes" @@ -158,10 +158,6 @@ "theme.docs.versionBadge.label": { "message": "版本:{versionLabel}" }, - "theme.common.editThisPage": { - "message": "编辑此页", - "description": "The link label to edit the current page" - }, "theme.docs.versions.unreleasedVersionLabel": { "message": "此为 {siteTitle} {versionLabel} 版尚未发行的文档。", "description": "The label used to tell the user that he's browsing an unreleased doc version" @@ -178,6 +174,10 @@ "message": "最新版本", "description": "The label used for the latest version suggestion link label" }, + "theme.common.editThisPage": { + "message": "编辑此页", + "description": "The link label to edit the current page" + }, "theme.common.headingLinkTitle": { "message": "标题的直接链接", "description": "Title for link to heading" @@ -194,10 +194,6 @@ "message": "最后{byUser}{atDate}更新", "description": "The sentence used to display when a page has been last updated, and by who" }, - "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": { - "message": "← 回到主菜单", - "description": "The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)" - }, "theme.navbar.mobileVersionsDropdown.label": { "message": "选择版本", "description": "The label for the navbar versions dropdown on mobile view" @@ -210,6 +206,10 @@ "message": "本页总览", "description": "The label used by the button on the collapsible TOC component" }, + "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": { + "message": "← 回到主菜单", + "description": "The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)" + }, "theme.tags.tagsListLabel": { "message": "标签:", "description": "The label alongside a tag list" diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.5.0.json b/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.5.0.json new file mode 100644 index 0000000..152b123 --- /dev/null +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.5.0.json @@ -0,0 +1,46 @@ +{ + "version.label": { + "message": "1.5.0", + "description": "The label for version 1.5.0" + }, + "sidebar.tutorialSidebar.category.入门": { + "message": "入门", + "description": "The label for category 入门 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.安装指南": { + "message": "安装指南", + "description": "The label for category 安装指南 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.其他指南": { + "message": "其他指南", + "description": "The label for category 其他指南 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.用户指南": { + "message": "用户指南", + "description": "The label for category 用户指南 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.开发者指南": { + "message": "开发者指南", + "description": "The label for category 开发者指南 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.系统开发": { + "message": "系统开发", + "description": "The label for category 系统开发 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.主题开发": { + "message": "主题开发", + "description": "The label for category 主题开发 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.参与贡献": { + "message": "参与贡献", + "description": "The label for category 参与贡献 in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.link.腾讯云开发者实验室": { + "message": "腾讯云开发者实验室", + "description": "The label for link 腾讯云开发者实验室 in sidebar tutorialSidebar, linking to https://cloud.tencent.com/developer/labs/lab/10523" + }, + "sidebar.tutorialSidebar.link.REST API": { + "message": "REST API", + "description": "The label for link REST API in sidebar tutorialSidebar, linking to https://api.halo.run" + } +} diff --git a/package.json b/package.json index 727241b..33255c7 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "serve": "docusaurus serve", "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids", - "lint": "markdownlint-cli2 './docs/**/*.md' './versioned_docs/version-1.4.17/**/*.md'" + "lint": "markdownlint-cli2 './docs/**/*.md' './versioned_docs/version-1.5.0/**/*.md'" }, "dependencies": { "@docusaurus/core": "2.0.0-beta.17", diff --git a/versioned_docs/version-1.5.0/about.md b/versioned_docs/version-1.5.0/about.md new file mode 100644 index 0000000..d0eb731 --- /dev/null +++ b/versioned_docs/version-1.5.0/about.md @@ -0,0 +1,16 @@ +--- +title: 关于文档 +description: 关于本文档站点的一些说明 +--- + +:::note +此文档使用 [Docusaurus](https://docusaurus.io/) 搭建,感谢 [Docusaurus](https://github.com/facebook/docusaurus) 社区所做的贡献。 +::: + +## 参与贡献 + +:::tip +如果你发现文档中有不正确或者需要添加的内容,非常欢迎参与到文档编辑当中。 +::: + +当前文档的仓库地址为 [halo-dev/docs](https://github.com/halo-dev/docs) ,所以你可以 fork 此仓库,修改之后提交 `Pull request` 等待我们合并即可。 diff --git a/versioned_docs/version-1.5.0/contribution/issue.md b/versioned_docs/version-1.5.0/contribution/issue.md new file mode 100644 index 0000000..6ab45dd --- /dev/null +++ b/versioned_docs/version-1.5.0/contribution/issue.md @@ -0,0 +1,28 @@ +--- +title: 问题反馈 +description: 问题反馈渠道及指南 +--- + +:::info +如果您在使用过程中遇到了什么问题,您可以通过下面的方式反馈,但请尽量按照要求提出反馈。 +::: + +## GitHub Issues + +链接: + +如果你在使用过程中,遇到了一些 bug 或者需要添加某些新特性,请尽量在 GitHub Issues 进行反馈,这非常有助于我们跟踪解决此问题,您也可以很方便的接收到处理状态。 + +建议步骤: + +1. 在 [Issues 列表](https://github.com/halo-dev/halo/issues) 搜索相关问题,看看是否有其他人已经提到了此问题。 +2. 如果当前还没有人遇到您类似的问题,那么请点击右上角的 `New issue` 按钮创建新的 issue。 +3. 选择正确的反馈类型。 +4. 请尽可能详细的按照模板填写内容。 +5. 点击 `Submit new issue` 提交 issue。 + +## Halo 官方社区 + +链接: + +此平台主要目的用于与其他 Halo 用户进行交流。但如果您对 GitHub 不是很熟悉或者没有账号,您也可以在此平台进行反馈。 diff --git a/versioned_docs/version-1.5.0/contribution/pr.md b/versioned_docs/version-1.5.0/contribution/pr.md new file mode 100644 index 0000000..81293b2 --- /dev/null +++ b/versioned_docs/version-1.5.0/contribution/pr.md @@ -0,0 +1,58 @@ +--- +title: 代码贡献 +description: 代码贡献指南 +--- + +> 欢迎你参与 Halo 的开发,下面是参与代码贡献的指南,以供参考。 + +### 代码贡献步骤 + +#### 0. 提交 issue + +任何新功能或者功能改进建议都先提交 issue 讨论一下再进行开发,bug 修复可以直接提交 pull request。 + +#### 1. Fork 此仓库 + +点击右上角的 `fork` 按钮即可。 + +#### 2. Clone 仓库到本地 + +```bash +git clone https://github.com/{YOUR_USERNAME}/halo + +git submodule init + +git submodule update +``` + +#### 3. 创建新的开发分支 + +```bash +git checkout -b {BRANCH_NAME} +``` + +#### 4. 提交代码 + +```bash +git push origin {BRANCH_NAME} +``` + +#### 5. 提交 pull request + +回到自己的仓库页面,选择 `New pull request` 按钮,创建 `Pull request` 到原仓库的 `master` 分支。 + +然后等待我们 Review 即可,如有 `Change Request`,再本地修改之后再次提交即可。 + +#### 6. 更新主仓库代码到自己的仓库 + +```bash +git remote add upstream git@github.com:halo-dev/halo.git + +git pull upstream master + +git push +``` + +### 开发规范 + +请参考 [代码风格](/developer-guide/core/code-style),请确保所有代码格式化之后再提交。 diff --git a/versioned_docs/version-1.5.0/developer-guide/core/code-style.md b/versioned_docs/version-1.5.0/developer-guide/core/code-style.md new file mode 100644 index 0000000..c4298a1 --- /dev/null +++ b/versioned_docs/version-1.5.0/developer-guide/core/code-style.md @@ -0,0 +1,30 @@ +--- +title: 代码风格 +description: 代码风格的相关配置说明 +--- + +Halo 添加了 checkstyle 插件,来保证每位提交者代码的风格保持一致,减少无效代码的修改。本篇文章主要讲解如何在 IDEA 中添加 CheckStyle 插件,并引入项目所提供的 checkstyle.xml 配置。 + +## 安装 CheckStyle-IDEA + +- 进入 IDEA 插件市场。 +- 搜索 CheckStyle-IDEA,点击安装即可。 + +## 配置 CheckStyle + +- 进入 CheckStyle 配置(File | Settings | Tools | Checkstyle)。 +- 选择 Checkstyle 版本:8.39。 +- 在配置文件中点击添加按钮,配置描述可随便填写(推荐 Halo Checks),选择 ./config/checkstyle/checkstyle.xml,点击下一步和完成; +- 勾选刚刚创建的配置文件。 + +## 配置 Editor + +- 进入编辑器配置(File | Settings | Editor | Code Style) + +- 导入 checkstyle.xm 配置: + +![image.png](https://halo.run/upload/2020/2/image-0c7a018e73f74634a534fa3ba8806628.png) + +- 选择 `./config/checkstyle/checkstyle.xml` 配置文件,点击确定即可。 + +至此,有关代码风格检查工具和格式化配置已经完成。 diff --git a/versioned_docs/version-1.5.0/developer-guide/core/prepare.md b/versioned_docs/version-1.5.0/developer-guide/core/prepare.md new file mode 100644 index 0000000..7c07c48 --- /dev/null +++ b/versioned_docs/version-1.5.0/developer-guide/core/prepare.md @@ -0,0 +1,137 @@ +--- +title: 相关说明 +description: 开发环境的一些说明 +--- + +## 所需要的环境 + +1. IDE:[IntelliJ IDEA](https://www.jetbrains.com/idea/download/) 或者 [STS](https://spring.io/tools)(Spring Tools Suite) +2. 工具:[Gradle](https://gradle.org/),[Lombok](https://projectlombok.org/) 插件 +3. JDK:`11+` + +> 推荐 IntelliJ IDEA 社区版(开源免费) + +`Halo` 项目使用了 `Lombok`,运行 `Halo` 之前请检查 `IDE` 是否已经安装好了 `Lombok` 插件。 + +如果使用的 `IDE` 是 `IntelliJ IDEA`,请在设置中启用 `Build, Execution, Deployment/Annotation Processors` 的 `Enable annotation processing`。 + +如果仍然因为 `Lombok` 报错,请更新 `IntelliJ IDEA` 至最新版。 + +## 克隆项目(Optional) + +如果你已经 `fork` 了 [`Halo`](https://github.com/halo-dev/halo),请将以下命令中的 `halo-dev` 替换为你的 `Github 用户名`。 + +```bash +git clone --recursive https://github.com/halo-dev/halo + +// 如果你在 Github 上已经添加了你的 ssh key,请使用以下命令进行 clone: +git clone --recursive git@github.com:halo-dev/halo.git +``` + +这里推荐使用第二种方式进行克隆,这样每次提交代码的时候,就不会提示登录 `Github` 了。 + +## 导入项目 + +导入项目的时候请选择 `Gradle 项目`进行导入。 + +> `IntelliJ IDEA` 在导入项目的时候请勾选 `auto import`,推荐使用 `gradle wrapper`。 + +## 运行方式 + +`Halo` 运行方式总体来讲有以下两种: + +### 直接运行 `Application` 主类(配合 IDE 运行) + +如果需要指定配置,请在 `Run/Debug Configuration` 内进行设置 `VM options`(这里推荐采用上文的[自定义配置](#%E8%87%AA%E5%AE%9A%E4%B9%89%E9%85%8D%E7%BD%AE)),例如: + +```ini +-Dspring.profiles.active=dev +-Dhalo.auth-enabled=false +-Dhalo.production-env=false +``` + +### 采用 `gradle bootRun task` 运行(不需要 IDE) + +在项目根目录下运行以下命令: + +```bash +# 类 Unix 用户 +./gradlew bootRun + +# Windows 用户 +./gradlew.bat bootRun +``` + +如果需要手动指定配置,可添加 `--args` 参数(这里推荐采用上文的[自定义配置](#%E8%87%AA%E5%AE%9A%E4%B9%89%E9%85%8D%E7%BD%AE)),例如: + +```bash +# 类 Unix 用户 +./gradlew bootRun --args="--spring.profiles.active=dev --server.port=2333" + +# Windows 用户 +./gradlew.bat bootRun --args="--spring.profiles.active=dev --server.port=2333" +``` + +如果一切正常且没有额外指定配置,则可根据控制台的输出链接进行访问 `Halo`: + +```java +2019-06-06 16:20:52.285 INFO 1330 --- [ restartedMain] run.halo.app.listener.StartedListener : Halo started at http://127.0.0.1:8090 +2019-06-06 16:20:52.285 INFO 1330 --- [ restartedMain] run.halo.app.listener.StartedListener : Halo admin started at http://127.0.0.1:8090/admin +2019-06-06 16:20:52.285 DEBUG 1330 --- [ restartedMain] run.halo.app.listener.StartedListener : Halo doc was enable at http://127.0.0.1:8090/swagger-ui.html # 仅在开发环境才会输出 +``` + +首次运行的时候,会跳转到博客安装页面,请大家耐心填写完毕,最后正式进入`开发之旅`。 + +这里推荐一个配置: + +| key | value | +| :----- | :------------ | +| 用户名 | test | +| 昵称 | test | +| 邮箱 | test@test.com | +| 密码 | opentest | + +--- + +## 高级配置 + +### 调整启动参数(VMoptions) + +运行命令中添加 `VM options`,例如: + +```ini +-Dspring.profiles.active=dev +``` + +### 调整启动参数(args) + +运行命令中添加参数,例如: + +```ini +--spring.profiles.active=dev +``` + +`IDE` 一般都会提供一个 `Run/Debug 配置`,可以添加 `VM options` 和 `参数`。以上示例可以直接应用在以`命令运行`方式上。 + +### 启用自动构建(Build project automatically) + +在开发环境下,修改代码之后,每次都需要重启应用,是一个非常耗时的操作。 + +有一个专门解决这个问题的工具,那就是 [JRebel](https://jrebel.com/),但是它的费用实在是太昂贵,作为普通的开发者很难承受这笔费用(但**不推荐**使用破解版)。 + +于是 `Halo` 最后采用了 `Spring Boot` 官方推荐的 [Developer Tools](https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-devtools.html)。 + +这里以 `IntelliJ IDEA` 为例。 + +1. 进入 `Settings` (Preferences on macOS)。 +2. 打开 `Build, Execution, Deployment > Compiler`. 启用 `Build project automatically`。 +3. 点击 `应用`。 +4. 按 `Ctrl+Shift+A` (Cmd+Shift+A on macOS)快捷键,然后搜索 `Registry`。打开之后找到 `compiler.automake.allow.when.app.running`,并启用它 (IntelliJ IDEA 15 and newer)。 + +> 来源于 + +Developer Tools 原理 + +在保存代码的时候,`IDE` 会自动为我们编译代码,`Developer Tools` 检测到代码的 `class 文件`(只能检测 `classpath` 下的 `class 文件`)的变更,会自动重启项目。注意,这里的重启速度会有质的提升,具体原因是 `Spring Boot` 提供的 `restart 技术`提供了两个 `classloaders`:`base classloader` 和 `restart classloader`。当项目重启的时候 `restart classloader` 将会被抛弃,并重启创建一个,这比 `code starts`(冷启动)快很多。当然,肯定是比不上 `JRebel` 采用的 `Reload 技术`。 + +更多细节请查阅: [Automatic Restart](https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-devtools.html#using-boot-devtools-restart) diff --git a/versioned_docs/version-1.5.0/developer-guide/core/structure.md b/versioned_docs/version-1.5.0/developer-guide/core/structure.md new file mode 100644 index 0000000..a080fdc --- /dev/null +++ b/versioned_docs/version-1.5.0/developer-guide/core/structure.md @@ -0,0 +1,36 @@ +--- +title: 系统结构 +description: Halo 项目的构成 +--- + +[Halo](https://github.com/halo-dev/halo) 博客系统分为以下四个部分: + +| 项目名称 | 简介 | +| :------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------- | +| [halo](https://github.com/halo-dev/halo) | 提供整个系统的服务,采用 [Spring Boot](https://spring.io/) 开发 | +| [halo-admin](https://github.com/halo-dev/halo-admin) | 负责后台管理的渲染,采用 [Vue](https://vuejs.org/) 开发,已集成在 Halo 运行包内,无需独立部署。 | +| [halo-comment](https://github.com/halo-dev/halo-comment) | 评论插件,采用 [Vue](https://vuejs.org/) 开发,在主题中运行方式引入构建好的 `Javascript` 文件即可 | +| [halo-theme-\*](https://github.com/halo-dev) | 主题项目集,采用 [Freemarker](https://freemarker.apache.org/) 模板引擎编写,需要包含一些特殊的配置才能够被 halo 所使用 | + +## 自定义配置 + +> 为什么要提前讲自定义配置呢?是因为在这里让大家了解到 `Halo` 的`配置方式`,以及`配置优先级`,不至于未来运行项目的时候不知道如何优雅地修改配置。 + +`Halo` 配置目录优先级如下(从上到下优先级越来越小,上层的配置将会覆盖下层): + +- `Halo` 自定义配置 + - file:~/.halo/ + - file:~/.halo-dev/ +- `Spring Boot` 默认配置 + - file:./config/ + - file:./ + - classpath:/config/ + - classpath:/ + +> 参考: [Application Property Files](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-application-property-files) + +在开发的时候,希望大家能够在 `~/halo-dev/application.yml` 中进行添加自定义配置。当然后面也会讲到如何用`运行参数` 和 `VM options` 进行控制配置,届时可根据具体情况进行选择。 + +:::warning +开发的时候,我们不建议直接更改`项目源码`中的所包含的`配置文件`,包括 `application.yml`、`application-dev.yml`、`application-test.yml` 和 `application-user.yml`。 +::: diff --git a/versioned_docs/version-1.5.0/developer-guide/theme/config-files.md b/versioned_docs/version-1.5.0/developer-guide/theme/config-files.md new file mode 100644 index 0000000..18f8ee4 --- /dev/null +++ b/versioned_docs/version-1.5.0/developer-guide/theme/config-files.md @@ -0,0 +1,261 @@ +--- +title: 配置文件 +description: 主题配置文件的说明 +--- + +> Halo 的主题模块使用 yaml 来对主题进行配置,`theme.yaml` 里面主要描述主题的名称,开发者的信息,开源地址等等。`settings.yaml` 包含了主题所有的配置选项,需要注意的是,这些选项仅仅是用于构建配置表单,并不起到对主题的配置作用。 + +## theme.yaml + +```yaml +id: 主题id,唯一,不能与其他主题一样。我们建议设置为 `作者名_主题名称` +name: 主题名称 +author: + name: 作者名称 + website: 作者网址 +description: 主题描述 +logo: 主题 Logo 地址 +website: 主题地址,可填写为 git 仓库地址 +repo: 主题 git 仓库地址,如有填写,后台可在线更新 +version: 版本号 +require: 最低支持的 Halo 版本,如:1.3.0,那么如果用户的版本为 1.3.0 以下,则无法安装 + +postMetaField: 文章自定义 meta 变量 + - meta_key + +sheetMetaField: + - meta_key 页面自定义 meta 变量 +``` + +示例: + +```yaml +id: caicai_anatole +name: Anatole +author: + name: Caicai + website: https://www.caicai.me +description: A other Halo theme +logo: https://avatars1.githubusercontent.com/u/1811819?s=460&v=4 +website: https://github.com/halo-dev/halo-theme-anatole +repo: https://github.com/halo-dev/halo-theme-anatole +version: 1.0.0 +require: 1.3.0 +postMetaField: + - music_url # 假设在文章页面需要播放背景音乐,用户可以自己填写音乐地址。 + - download_url # 假设在文章页有一个下载按钮,那么用户也可以自己填写加载地址。 + +sheetMetaField: + - music_url + - download_url +``` + +### 自定义 meta + +这个为 1.2.0 引入的功能,用户可以在文章设置中设置自定义 meta,我们在 `theme.yaml` 中填写的 `postMetaField` 和 `sheetMetaField` 为预设项,当用户激活当前主题之后,在文章设置中即可看到预先设置好的项,然后填写对应的值即可。 + +关于这个 meta 变量如何调用的问题,会在后面的模板变量中阐述。 + +## settings.yaml + +```yaml +# Tab 节点 +group1: + label: 第一个 Tab 名称 + # 表单项 + items: + # 省略 +group2: + label: 第二个 Tab 名称 + # 表单项 + items: + # 省略 +``` + +## settings.yaml#items + +> settings.yaml 的 items 下即为所有表单元素,所支持的表单元素如下 + +```yaml +items: + + # 普通文本框 + item1: + name: item1 // 设置项的 name 值,在页面可通过 ${settings.item1!} 获取值。 + label: item1 // 表单项的 label + type: text // 表单项类型:普通文本框 + placeholder: '' // 表单项的 placeholder,一般给用户提示 + default: '' // 表单项的默认值 + description: '' // 描述,一般用于说明该设置的具体用途 + + # 颜色选择框 + item1: + name: item1 // 设置项的 name 值,在页面可通过 ${settings.item1!} 获取值。 + label: item1 // 表单项的 label + type: color // 表单项类型:颜色选择框 + placeholder: '' // 表单项的 placeholder,一般给用户提示 + default: '' // 表单项的默认值 + description: '' // 描述,一般用于说明该设置的具体用途 + + # 附件选择框 + item1: + name: item1 // 设置项的 name 值,在页面可通过 ${settings.item1!} 获取值。 + label: item1 // 表单项的 label + type: attachment // 表单项类型:颜色选择框 + placeholder: '' // 表单项的 placeholder,一般给用户提示 + default: '' // 表单项的默认值 + description: '' // 描述,一般用于说明该设置的具体用途 + + # 多行文本框 + item2: // 设置项的 name 值,在页面可通过 ${settings.item2!} 获取值。 + name: item2 + label: item2 // 同上 + type: textarea // 表单项类型:多行文本框 + placeholder: '' // 同上 + default: '' // 同上 + description: '' // 描述,一般用于说明该设置的具体用途 + + # 单选框 + item3: + name: item3 // 同上 + label: item3_label // 同上 + type: radio // 表单项类型:单选框 + data-type: bool // 数据类型:bool,string,long,double + default: value1 // 同上 + description: '' // 描述,一般用于说明该设置的具体用途 + options: // 选项 + - value: value1 // 值 + label: label1 // 说明 + - value: value2 + label: label2 + + # 下拉框 + item4: + name: item4 // 同上 + label: item4 // 同上 + type: select // 表单项类型:下拉框 + data-type: bool // 数据类型:bool,string,long,double + default: value1 // 同上 + description: '' // 描述,一般用于说明该设置的具体用途 + options: // 选项 + - value: value1 // 值 + label: label1 // 说明 + - value: value2 + label: label2 +``` + +### 一个例子 + +假设我们的配置文件如下: + +```yaml +general: + label: 基础设置 + items: + index_title: + name: index_title + label: 首页标题 + type: text + description: '注意:将覆盖博客标题' + background_cover: + name: background_cover + label: 首页背景图 + type: attachment + default: '/casper/assets/images/blog-cover.jpg' + description: '设置首页的背景图,你可以点击右边的选择按钮选择图片。' + background_color: + name: background_color + label: 首页背景颜色 + type: color + default: '#fff' + music_enabled: + name: music_enabled + label: 背景音乐 + type: radio + data-type: bool + default: false + description: '是否开启背景音乐,默认为 false' + options: + - value: true + label: 开启 + - value: false + label: 关闭 + code_pretty: + name: code_pretty + label: 文章代码高亮主题 + type: select + default: Default + options: + - value: Default + label: Default + - value: Coy + label: Coy + - value: Dark + label: Dark + - value: Okaidia + label: Okaidia + - value: Solarized Light + label: Solarized Light + - value: Tomorrow Night + label: Tomorrow Night + - value: Twilight + label: Twilight +``` + +页面取值: + +```html +// 获取首页标题 + +<#if settings.index_title?? && settings.index_title != ''> +

${settings.index_title!}

+ +``` + +```html +// 获取背景图片 + +<#if settings.background_cover?? && settings.background_cover != ''> + + +``` + +```html +// 获取背景颜色 + + + +或者 + + + +``` + +```html +// 判断是否开启背景音乐 + +<#if settings.music_enabled!false> + do something... + +``` + +```html +// 获取代码高亮主题 + + + +``` + +更多实例可参考:。 diff --git a/versioned_docs/version-1.5.0/developer-guide/theme/global-variable.md b/versioned_docs/version-1.5.0/developer-guide/theme/global-variable.md new file mode 100644 index 0000000..7ff661e --- /dev/null +++ b/versioned_docs/version-1.5.0/developer-guide/theme/global-variable.md @@ -0,0 +1,261 @@ +--- +title: 全局变量 +description: 系统提供的一些全局变量 +--- + +## 博客地址 + +```html +${blog_url!} +``` + +此变量与后台博客设置中的 `博客地址` 相对应。 + +## 网站根路径 + +```html +${context!} +``` + +需要注意的是,此变量和 `blog_url` 不同的是,这个变量有两种值,一种为相对路径形式,一种为绝对路径形式。 + +那么,当在后台博客设置中将 `全局绝对路径` 的选项打开时,`context` 变量值为 `${blog_url}/`,关闭时,`context` 的变量值为 `/`。 + +假设,我设置的 `博客地址` 为 `https://halo.run`,那么: + +- 全局绝对路径为开启的状态下: +- 全局绝对路径为关闭的状态下:/ + +## 主题资源根路径 + +```html +${theme_base!} +``` + +假设你的主题在 `~/halo-dev/templates/themes/anatole` 这个目录,那么 `theme_base` 为 `https://yourdomain/themes/anatole` + +举个例子,你当前开发的主题为 `anatole`,当你要获取主题下 `css/style.css` 这个文件的路径,那么: + +```html +${theme_base!}/css/style.css +``` + +## 主题信息 + +主题名称: + +```html +${theme.name!} +``` + +主题 git 仓库地址: + +```html +${theme.repo!} +``` + +主题版本号: + +```html +${theme.version!} +``` + +## 博客标题 + +```html +${blog_title!} +``` + +此变量与后台博客设置中的 `博客标题` 相对应。 + +## 博客 Logo + +```html +${blog_logo!} +``` + +此变量与后台博客设置中的 `Logo` 相对应。 + +## Halo 版本 + +```html +${version!} +``` + +当前 Halo 的版本,如:1.3.0 + +## 博主信息 + +昵称: + +```html +${user.nickname!} +``` + +邮箱地址: + +```html +${user.email!} +``` + +描述: + +```html +${user.description!} +``` + +头像地址: + +```html +${user.avatar!} +``` + +上次登录时间: + +```html +${user.expireTime!} +``` + +## SEO 关键词 + +```html +${meta_keywords!} +``` + +需要注意的是,虽然这个变量在任何页面都可以使用,但是其值可能在不同的页面是不一样的。会根据用户的设置,生成对应的值。 + +假设在文章页面: + +- 如果用户为文章设置了标签,而没有设置 `自定义关键词`,系统会自动将标签设置为页面关键词。 +- 如果用户设置了 `自定义关键词`,那么则会取用户设置的值。 + +## SEO 描述 + +```html +${meta_description!} +``` + +需要注意的是,虽然这个变量在任何页面都可以使用,但是其值可能在不同的页面是不一样的。会根据用户的设置,生成对应的值。 + +## RSS 2.0 订阅地址 + +```html +${rss_url!} +``` + +如:`https://yourdomain/rss.xml` + +## Atom 格式的订阅地址 + +```html +${atom_url!} +``` + +如:`https://yourdomain/atom.xml` + +## Xml 格式的网站地图地址 + +```html +${sitemap_xml_url!} +``` + +如:`https://yourdomain/sitemap.xml` + +## HTML 格式的网站地图地址 + +```html +${sitemap_html_url!} +``` + +如:`https://yourdomain/sitemap.html` + +## 友情链接页面地址 + +```html +${links_url!} +``` + +- **全局绝对路径为开启的状态下**:`https://yourdomain.com/{links_prefix}` +- **全局绝对路径为关闭的状态下**:`/{links_prefix}` + +`{links_prefix}` 是用户可设定的值,用户可以在后台修改 `友情链接` 的前缀,默认为 `links`。 + +## 图库页面地址 + +```html +${photos_url!} +``` + +- **全局绝对路径为开启的状态下**:`https://yourdomain.com/{photos_prefix}` +- **全局绝对路径为关闭的状态下**:`/{photos_prefix}` + +`{photos_prefix}` 是用户可设定的值,用户可以在后台修改 `图库页面` 的前缀,默认为 `photos`。 + +## 日志页面地址 + +```html +${journals_url!} +``` + +- **全局绝对路径为开启的状态下**:`https://yourdomain.com/{journals_prefix}` +- **全局绝对路径为关闭的状态下**:`/{journals_prefix}` + +`{journals_prefix}` 是用户可设定的值,用户可以在后台修改 `日志页面` 的前缀,默认为 `journals`。 + +## 文章归档页面地址 + +```html +${archives_url!} +``` + +- **全局绝对路径为开启的状态下**:`https://yourdomain.com/{archives_prefix}` +- **全局绝对路径为关闭的状态下**:`/{archives_prefix}` + +`{archives_prefix}` 是用户可设定的值,用户可以在后台修改 `归档` 的前缀,默认为 `archives`。 + +## 分类列表页面地址 + +```html +${categories_url!} +``` + +- **全局绝对路径为开启的状态下**:`https://yourdomain.com/{categories_prefix}` +- **全局绝对路径为关闭的状态下**:`/{categories_prefix}` + +`{categories_prefix}` 是用户可设定的值,用户可以在后台修改 `分类` 的前缀,默认为 `categories`。 + +## 标签列表页面地址 + +```html +${tags_url!} +``` + +- **全局绝对路径为开启的状态下**:`https://yourdomain.com/{tags_prefix}` +- **全局绝对路径为关闭的状态下**:`/{tags_prefix}` + +`{tags_prefix}` 是用户可设定的值,用户可以在后台修改 `标签` 的前缀,默认为 `tags`。 + +## 页面判断 + +判断当前页面是否是特定的页面。 + +- **is_index**:首页 +- **is_post**:文章页 +- **is_sheet**:自定义页面 +- **is_archives**:归档页面 +- **is_categories**:分类列表页面 +- **is_category**:单个分类页面 +- **is_tags**:标签列表页面 +- **is_tag**:单个标签页面 +- **is_search**:搜索结果页面 +- **is_journals**:日志页面 +- **is_photos**:图库页面 +- **is_links**:友情链接页面 + +用法: + +```html +<#if is_index??> + 当前页面是首页 + +``` diff --git a/versioned_docs/version-1.5.0/developer-guide/theme/page-variable.md b/versioned_docs/version-1.5.0/developer-guide/theme/page-variable.md new file mode 100644 index 0000000..555be22 --- /dev/null +++ b/versioned_docs/version-1.5.0/developer-guide/theme/page-variable.md @@ -0,0 +1,1354 @@ +--- +title: 页面变量 +description: 每个页面所返回的变量 +--- + +## 首页(index.ftl) + +访问路径:`http://yourdomain` + +### posts(List) + +#### 语法 + +```html +<#list posts.content as post> +// do something + +``` + +#### 参数 + +```json +[{ + "content": [{ + "categories": [{ + "createTime": "2020-10-13T13:23:39.143Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string" + }], + "commentCount": 0, + "createTime": "2020-10-13T13:23:39.143Z", + "disallowComment": true, + "editTime": "2020-10-13T13:23:39.143Z", + "editorType": "MARKDOWN", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaKeywords": "string", + "metas": {}, + "password": "string", + "slug": "string", + "status": "PUBLISHED", + "summary": "string", + "tags": [{ + "createTime": "2020-10-13T13:23:39.143Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + }], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2020-10-13T13:23:39.143Z", + "visits": 0, + "wordCount": 0 + }], + "empty": true, + "first": true, + "last": true, + "number": 0, + "numberOfElements": 0, + "pageable": { + "page": 0, + "size": 0, + "sort": [ + "string" + ] + }, + "size": 0, + "sort": { + "sort": [ + "string" + ] + }, + "totalElements": 0, + "totalPages": 0 +}] +``` + +#### 示例 + +遍历输出首页的文章: + +```html +<#list posts.content as post> + ${post.title!} + +``` + +输出: + +```html +title1 +title2 +title3 +``` + +## 文章页面(post.ftl) + +访问路径不固定,视固定链接配置而定,目前可以配置的有: + +- `http://yourdomain/archives/{slug}`(默认) +- `http://yourdomain/1970/01/{slug}` +- `http://yourdomain/1970/01/01/{slug}` +- `http://yourdomain/?p={id}` +- `http://yourdomain/archives/{id}` + +### post(Object) + +#### 语法 + +```html +${post.attribute} +``` + +注:attribute 代表具体属性。 + +#### 参数 + +```json +{ + "categories": [ + { + "createTime": "2021-02-25T13:15:58.589Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "password": "string", + "slug": "string", + "thumbnail": "string" + } + ], + "categoryIds": [ + 0 + ], + "commentCount": 0, + "createTime": "2021-02-25T13:15:58.589Z", + "disallowComment": true, + "editTime": "2021-02-25T13:15:58.589Z", + "editorType": "MARKDOWN", + "formatContent": "string", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaIds": [ + 0 + ], + "metaKeywords": "string", + "metas": [ + { + "createTime": "2021-02-25T13:15:58.589Z", + "id": 0, + "key": "string", + "postId": 0, + "value": "string" + } + ], + "originalContent": "string", + "password": "string", + "slug": "string", + "status": "DRAFT", + "summary": "string", + "tagIds": [ + 0 + ], + "tags": [ + { + "createTime": "2021-02-25T13:15:58.589Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + } + ], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2021-02-25T13:15:58.589Z", + "visits": 0, + "wordCount": 0 +} +``` + +#### 示例 + +获取文章标题: + +```html +${post.title!} +``` + +输出: + +```html +示例文章 +``` + +### prevPost(Object) + +#### 语法 + +```html +${prevPost.attribute} +``` + +注:attribute 代表具体属性。 + +#### 参数 + +```json +{ + "categories": [ + { + "createTime": "2021-02-25T13:15:58.589Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "password": "string", + "slug": "string", + "thumbnail": "string" + } + ], + "categoryIds": [ + 0 + ], + "commentCount": 0, + "createTime": "2021-02-25T13:15:58.589Z", + "disallowComment": true, + "editTime": "2021-02-25T13:15:58.589Z", + "editorType": "MARKDOWN", + "formatContent": "string", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaIds": [ + 0 + ], + "metaKeywords": "string", + "metas": [ + { + "createTime": "2021-02-25T13:15:58.589Z", + "id": 0, + "key": "string", + "postId": 0, + "value": "string" + } + ], + "originalContent": "string", + "password": "string", + "slug": "string", + "status": "DRAFT", + "summary": "string", + "tagIds": [ + 0 + ], + "tags": [ + { + "createTime": "2021-02-25T13:15:58.589Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + } + ], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2021-02-25T13:15:58.589Z", + "visits": 0, + "wordCount": 0 +} +``` + +#### 示例 + +获取上一篇文章的信息: + +```html +<#if prevPost??> + 上一篇:${prevPost.title!} + +``` + +输出: + +```html +上一篇:title1 +``` + +### nextPost(Object) + +#### 语法 + +```html +${nextPost.attribute} +``` + +注:attribute 代表具体属性。 + +#### 参数 + +```json +{ + "categories": [ + { + "createTime": "2021-02-25T13:15:58.589Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "password": "string", + "slug": "string", + "thumbnail": "string" + } + ], + "categoryIds": [ + 0 + ], + "commentCount": 0, + "createTime": "2021-02-25T13:15:58.589Z", + "disallowComment": true, + "editTime": "2021-02-25T13:15:58.589Z", + "editorType": "MARKDOWN", + "formatContent": "string", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaIds": [ + 0 + ], + "metaKeywords": "string", + "metas": [ + { + "createTime": "2021-02-25T13:15:58.589Z", + "id": 0, + "key": "string", + "postId": 0, + "value": "string" + } + ], + "originalContent": "string", + "password": "string", + "slug": "string", + "status": "DRAFT", + "summary": "string", + "tagIds": [ + 0 + ], + "tags": [ + { + "createTime": "2021-02-25T13:15:58.589Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + } + ], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2021-02-25T13:15:58.589Z", + "visits": 0, + "wordCount": 0 +} +``` + +#### 示例 + +获取下一篇文章的信息: + +```html +<#if nextPost??> + 下一篇:${nextPost.title!} + +``` + +输出: + +```html +下一篇:title3 +``` + +### categories(List) + +#### 语法 + +```html +<#list categories as category> +// do something + +``` + +#### 参数 + +```json +[{ + "createTime": "2021-02-25T13:32:11.189Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "password": "string", + "slug": "string", + "thumbnail": "string" +}] +``` + +#### 示例 + +获取文章的分类列表: + +```html +<#list categories as category> + ${category.name!} + +``` + +输出: + +```html +name1 +name2 +``` + +### tags(List) + +#### 语法 + +```html +<#list tags as tag> +// do something + +``` + +#### 参数 + +```json +[{ + "createTime": "2021-02-25T13:34:48.779Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" +}] +``` + +#### 示例 + +获取文章的标签列表: + +```html +<#list tags as tag> + ${tag.name!} + +``` + +输出: + +```html +name1 +name2 +``` + +### metas(Object) + +#### 语法 + +```html +${metas.key} +``` + +注:attribute 代表具体 key 值。 + +#### 参数 + +无 + +#### 示例 + +获取用户设置的音乐链接: + +```html + +``` + +输出: + +```html + +``` + +## 自定义页面(sheet.ftl) + +访问路径不固定,视固定链接配置而定,默认为:`http://yourdomain/s/{slug}` + +### sheet(Object) + +#### 语法 + +```html +${sheet.attribute} +``` + +注:attribute 代表具体属性。 + +#### 参数 + +```json +{ + "commentCount": 0, + "createTime": "2021-02-25T13:37:29.775Z", + "disallowComment": true, + "editTime": "2021-02-25T13:37:29.775Z", + "editorType": "MARKDOWN", + "formatContent": "string", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaIds": [ + 0 + ], + "metaKeywords": "string", + "metas": [ + { + "createTime": "2021-02-25T13:37:29.775Z", + "id": 0, + "key": "string", + "postId": 0, + "value": "string" + } + ], + "originalContent": "string", + "password": "string", + "slug": "string", + "status": "DRAFT", + "summary": "string", + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2021-02-25T13:37:29.775Z", + "visits": 0, + "wordCount": 0 +} +``` + +#### 示例 + +获取页面标题: + +```html +${sheet.title!} +``` + +输出: + +```html +示例页面 +``` + +### metas(Object) + +#### 语法 + +```html +${metas.key} +``` + +注:attribute 代表具体 key 值。 + +#### 参数 + +无 + +#### 示例 + +获取用户设置的音乐链接: + +```html + +``` + +输出: + +```html + +``` + +## 文章归档页面(archives.ftl) + +访问路径不固定,视固定链接配置而定,默认为:`http://yourdomain/archives` + +### posts(List) + +#### 语法 + +```html +<#list posts.content as post> +// do something + +``` + +#### 参数 + +```json +[{ + "content": [{ + "categories": [{ + "createTime": "2020-10-13T13:23:39.143Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string" + }], + "commentCount": 0, + "createTime": "2020-10-13T13:23:39.143Z", + "disallowComment": true, + "editTime": "2020-10-13T13:23:39.143Z", + "editorType": "MARKDOWN", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaKeywords": "string", + "metas": {}, + "password": "string", + "slug": "string", + "status": "PUBLISHED", + "summary": "string", + "tags": [{ + "createTime": "2020-10-13T13:23:39.143Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + }], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2020-10-13T13:23:39.143Z", + "visits": 0, + "wordCount": 0 + }], + "empty": true, + "first": true, + "last": true, + "number": 0, + "numberOfElements": 0, + "pageable": { + "page": 0, + "size": 0, + "sort": [ + "string" + ] + }, + "size": 0, + "sort": { + "sort": [ + "string" + ] + }, + "totalElements": 0, + "totalPages": 0 +}] +``` + +#### 示例 + +遍历输出归档页面的文章(无年份分组): + +```html +<#list posts.content as post> + ${post.title!} + +``` + +输出: + +```html +title1 +title2 +title3 +``` + +### archives(List) + +#### 语法 + +```html +<#list archives.content as archive> +// do something + +``` + +#### 参数 + +```json +{ + "content": [{ + "posts": [{ + "categories": [{ + "createTime": "2021-03-07T05:45:06.271Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "password": "string", + "slug": "string", + "thumbnail": "string" + }], + "commentCount": 0, + "createTime": "2021-03-07T05:45:06.271Z", + "disallowComment": true, + "editTime": "2021-03-07T05:45:06.271Z", + "editorType": "MARKDOWN", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaKeywords": "string", + "metas": {}, + "password": "string", + "slug": "string", + "status": "DRAFT", + "summary": "string", + "tags": [{ + "createTime": "2021-03-07T05:45:06.271Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + }], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2021-03-07T05:45:06.271Z", + "visits": 0, + "wordCount": 0 + }], + "year": 0 + }], + "hasContent": true, + "hasNext": true, + "hasPrevious": true, + "isEmpty": true, + "isFirst": true, + "page": 0, + "pages": 0, + "rpp": 0, + "total": 0 +} +``` + +#### 示例 + +遍历输出归档页面的文章(有年份分组): + +```html +<#list archives.content as archive> +

${archive.year?c}

+ <#list archive.posts as post> + ${post.title!} + + +``` + +输出: + +```html +

2021

+title1 +title2 +title3 +

2020

+title4 +title5 +title6 +``` + +## 分类目录页面(categories.ftl) + +访问路径不固定,视固定链接配置而定,默认为:`http://yourdomain/categories` + +此页面无页面变量,可以使用 [模板标签](/developer-guide/theme/template-tag) 获取分类列表。 + +## 单个分类所属文章页面(category.ftl) + +访问路径不固定,视固定链接配置而定,默认为:`http://yourdomain/categories/{slug}` + +### posts(List) + +#### 语法 + +```html +<#list posts.content as post> +// do something + +``` + +#### 参数 + +```json +[{ + "content": [{ + "categories": [{ + "createTime": "2020-10-13T13:23:39.143Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string" + }], + "commentCount": 0, + "createTime": "2020-10-13T13:23:39.143Z", + "disallowComment": true, + "editTime": "2020-10-13T13:23:39.143Z", + "editorType": "MARKDOWN", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaKeywords": "string", + "metas": {}, + "password": "string", + "slug": "string", + "status": "PUBLISHED", + "summary": "string", + "tags": [{ + "createTime": "2020-10-13T13:23:39.143Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + }], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2020-10-13T13:23:39.143Z", + "visits": 0, + "wordCount": 0 + }], + "empty": true, + "first": true, + "last": true, + "number": 0, + "numberOfElements": 0, + "pageable": { + "page": 0, + "size": 0, + "sort": [ + "string" + ] + }, + "size": 0, + "sort": { + "sort": [ + "string" + ] + }, + "totalElements": 0, + "totalPages": 0 +}] +``` + +#### 示例 + +遍历输出某个分类的文章: + +```html +<#list posts.content as post> + ${post.title!} + +``` + +输出: + +```html +title1 +title2 +title3 +``` + +### category(Object) + +#### 语法 + +```html +${category.attribute} +``` + +注:attribute 代表具体属性。 + +#### 参数 + +```json +{ + "createTime": "2020-10-11T05:59:40.622Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string", + "postCount": 0 +} +``` + +#### 示例 + +```html +分类:${category.name!} +``` + +输出: + +```html +分类:name1 +``` + +## 标签页面(tags.ftl) + +访问路径不固定,视固定链接配置而定,默认为:`http://yourdomain/tags` + +此页面无页面变量,可以使用 [模板标签](/developer-guide/theme/template-tag) 获取标签列表。 + +## 单个标签所属文章页面(tag.ftl) + +访问路径不固定,视固定链接配置而定,默认为:`http://yourdomain/tags/{slug}` + +### posts(List) + +#### 语法 + +```html +<#list posts.content as post> +// do something + +``` + +#### 参数 + +```json +[{ + "content": [{ + "categories": [{ + "createTime": "2020-10-13T13:23:39.143Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string" + }], + "commentCount": 0, + "createTime": "2020-10-13T13:23:39.143Z", + "disallowComment": true, + "editTime": "2020-10-13T13:23:39.143Z", + "editorType": "MARKDOWN", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaKeywords": "string", + "metas": {}, + "password": "string", + "slug": "string", + "status": "PUBLISHED", + "summary": "string", + "tags": [{ + "createTime": "2020-10-13T13:23:39.143Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + }], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2020-10-13T13:23:39.143Z", + "visits": 0, + "wordCount": 0 + }], + "empty": true, + "first": true, + "last": true, + "number": 0, + "numberOfElements": 0, + "pageable": { + "page": 0, + "size": 0, + "sort": [ + "string" + ] + }, + "size": 0, + "sort": { + "sort": [ + "string" + ] + }, + "totalElements": 0, + "totalPages": 0 +}] +``` + +#### 示例 + +遍历输出某个标签的文章: + +```html +<#list posts.content as post> + ${post.title!} + +``` + +输出: + +```html +title1 +title2 +title3 +``` + +### tag(Object) + +#### 语法 + +```html +${tag.attribute} +``` + +注:attribute 代表具体属性。 + +#### 参数 + +```json +{ + "createTime": "2020-10-11T06:14:30.595Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" +} +``` + +#### 示例 + +```html +标签:${tag.name!} +``` + +输出: + +```html +标签:name1 +``` + +## 文章搜索结果页面(search.ftl) + +访问路径:`http://yourdomain/search?keyword=keyword` + +### keyword(String) + +#### 语法 + +```html +${keyword!} +``` + +#### 参数 + +无 + +#### 示例 + +```html +搜索关键字为:${keyword!} +``` + +### posts(List) + +#### 语法 + +```html +<#list posts.content as post> +// do something + +``` + +#### 参数 + +```json +[{ + "content": [{ + "categories": [{ + "createTime": "2020-10-13T13:23:39.143Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string" + }], + "commentCount": 0, + "createTime": "2020-10-13T13:23:39.143Z", + "disallowComment": true, + "editTime": "2020-10-13T13:23:39.143Z", + "editorType": "MARKDOWN", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaKeywords": "string", + "metas": {}, + "password": "string", + "slug": "string", + "status": "PUBLISHED", + "summary": "string", + "tags": [{ + "createTime": "2020-10-13T13:23:39.143Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + }], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2020-10-13T13:23:39.143Z", + "visits": 0, + "wordCount": 0 + }], + "empty": true, + "first": true, + "last": true, + "number": 0, + "numberOfElements": 0, + "pageable": { + "page": 0, + "size": 0, + "sort": [ + "string" + ] + }, + "size": 0, + "sort": { + "sort": [ + "string" + ] + }, + "totalElements": 0, + "totalPages": 0 +}] +``` + +#### 示例 + +遍历输出某个搜索结果的文章: + +```html +<#list posts.content as post> + ${post.title!} + +``` + +输出: + +```html +title1 +title2 +title3 +``` + +## 友情链接页面(links.ftl) + +访问路径不固定,视固定链接配置而定,默认为:`http://yourdomain/links` + +此页面无页面变量,可以使用 [模板标签](/developer-guide/theme/template-tag) 获取友情链接列表。 + +## 图库页面(photos.ftl) + +访问路径不固定,视固定链接配置而定,默认为:`http://yourdomain/photos` + +### photos(List) + +#### 语法 + +```html +<#list photos.content as photo> +// do something + +``` + +#### 参数 + +```json +{ + "content": [{ + "description": "string", + "id": 0, + "location": "string", + "name": "string", + "takeTime": "2021-03-07T05:28:12.352Z", + "team": "string", + "thumbnail": "string", + "url": "string" + }], + "hasContent": true, + "hasNext": true, + "hasPrevious": true, + "isEmpty": true, + "isFirst": true, + "page": 0, + "pages": 0, + "rpp": 0, + "total": 0 +} +``` + +#### 示例 + +```html +<#list photos.content as photo> + ${photo.description!} + +``` + +输出: + +```html +山川 +河流 +绿树 +``` + +## 日志页面(journals.ftl) + +访问路径不固定,视固定链接配置而定,默认为:`http://yourdomain/journals` + +### journals(List) + +#### 语法 + +```html +<#list journals.content as journal> +// do something + +``` + +#### 参数 + +```json +{ + "content": [{ + "commentCount": 0, + "content": "string", + "createTime": "2021-03-07T05:32:06.365Z", + "id": 0, + "likes": 0, + "sourceContent": "string", + "type": "INTIMATE" + }], + "hasContent": true, + "hasNext": true, + "hasPrevious": true, + "isEmpty": true, + "isFirst": true, + "page": 0, + "pages": 0, + "rpp": 0, + "total": 0 +} +``` + +#### 示例 + +```html +
    + <#list journals.content as journal> +
  • + ${journal.createTime?string('yyyy年MM月dd日')}:${journal.content!} +
  • + +
+``` + +输出: + +```html +
    +
  • + 2021年3月7日:内容1 +
  • +
  • + 2021年3月7日:内容2 +
  • +
+``` diff --git a/versioned_docs/version-1.5.0/developer-guide/theme/prepare.md b/versioned_docs/version-1.5.0/developer-guide/theme/prepare.md new file mode 100644 index 0000000..a7e3b19 --- /dev/null +++ b/versioned_docs/version-1.5.0/developer-guide/theme/prepare.md @@ -0,0 +1,76 @@ +--- +title: 准备工作 +description: 主题开发的环境搭建 +--- + +:::info +Halo 的模板引擎为 Freemarker,建议在开发 Halo 的主题之前,先阅读一遍 Freemarker 的相关文档:。 +::: + +## 搭建开发环境 + +> 假设你已经在本地电脑配置好了 Java 开发环境。 + +Halo 的运行可参考上述 [系统开发](/developer-guide/core/prepare),或者直接下载打包好的程序启动,如下步骤: + +1. 从 [Github release](https://github.com/halo-dev/halo/releases) 或者 [https://dl.halo.run](https://dl.halo.run) 下载最新的运行包。 +2. 在终端中执行 `java -jar halo.jar --spring.profiles.active=dev` + +启动完成之后,在电脑的用户目录即可看到 `halo-dev` 文件夹。 + +## 新建主题 + +1. 在 `~/halo-dev/templates/themes` 下新建一个文件夹,该文件夹就是你所新建的主题目录。 +2. 使用你熟悉的编辑器打开你所新建的主题目录,这里我们推荐使用 [Visual Studio Code](https://code.visualstudio.com),并安装 [Freemarker](https://marketplace.visualstudio.com/items?itemName=dcortes92.FreeMarker) 扩展。 + +:::info +我们同样为 Halo 主题开发了一个 `Visual Studio Code` Snippets 扩展,以简化一些操作,但目前处于 beta 状态,有需要的可以试试 [Halo theme develop Snippets](https://marketplace.visualstudio.com/items?itemName=halo-dev.halo-theme-dev-snippets-for-vs-code)。 +::: + +## 开发约定 + +- 主题目录下必须存在 `theme.yaml(主题描述文件)`,`settings.yaml(主题配置文件)`,相关格式在后面会详细讲解。 +- 如果要开源到 Github 我们建议将仓库名设置为 `halo-theme-主题名`,并设置仓库的 `topic` 为 `halo` 和 `halo-theme`,这样可以方便使用者搜索。 +- 所有模板文件的后缀为 `.ftl`。 +- 主题目录需要以 `screenshot.png` 命名的预览图片,以供后台展示。 + +## 开发样板 + +> 为了让开发者更快速的上手主题的开发,我们提供了一个简单的开发样板以供参考。 + +仓库地址: + +## 目录结构 + +> 为了让开发更加规范,我们推荐使用以下的目录结构。 + +```txt +├── module // 公共模板目录 +│   ├── comment.ftl // 比如:评论模板 +│   ├── layout.ftl // 比如:布局模板 +├── source // 静态资源目录 +│   ├── css // 样式目录 +│   ├── images // 图片目录 +│   ├── js // JS 脚本目录 +│   └── plugins // 前端库目录 +├── index.ftl // 首页 +├── post.ftl // 文章页 +├── post_xxx.ftl // 自定义文章模板,如:post_diary.ftl。可在后台发布文章时选择。 +├── sheet.ftl // 自定义页面 +├── sheet_xxx.ftl // 自定义模板,如:sheet_search.ftl、sheet_author.ftl。可在后台发布页面时选择。 +├── archives.ftl // 归档页 +├── categories.ftl // 分类目录页 +├── category.ftl // 单个分类的所属文章页 +├── tags.ftl // 标签页面 +├── tag.ftl // 单个标签的所属文章页 +├── search.ftl // 搜索结果页 +├── links.ftl // 内置页面:友情链接 +├── photos.ftl // 内置页面:图库 +├── journals.ftl // 内置页面:日志 +├── 404.ftl // 404 页 +├── 500.ftl // 500 页 +├── README.md // README,一般用于主题介绍或说明 +├── screenshot.png // 主题预览图 +├── settings.yaml // 主题选项配置文件 +└── theme.yaml // 主题描述文件 +``` diff --git a/versioned_docs/version-1.5.0/developer-guide/theme/public-template-tag.md b/versioned_docs/version-1.5.0/developer-guide/theme/public-template-tag.md new file mode 100644 index 0000000..515a72a --- /dev/null +++ b/versioned_docs/version-1.5.0/developer-guide/theme/public-template-tag.md @@ -0,0 +1,91 @@ +--- +title: 公共宏模板 +description: 系统提供的一些宏模板 +--- + +> 为了减少重复代码,我们将某些常见的全局变量封装成了一个公共模板,我们只需要引入该模板,然后调用其中的宏模板即可。 + +## 公共 head 模板 + +> 需要注意的是,为了保证系统功能的完整性,我们强制要求在每个页面的 `` 标签下必须包含此模板。 + +```html +<@global.head /> +``` + +等同于: + +```html +<#if options.seo_spider_disabled!false> + + + +<@global.favicon /> +<@global.custom_head /> +<@global.custom_content_head /> +``` + +## 公共底部 + +> 需要注意的是,为了保证系统功能的完整性,我们强制要求在每个页面的尾部必须包含此模板。 + +```html +<@global.footer /> +``` + +等同于: + +```html +<@global.statistics /> +<@global.footer_info /> +```` + +## 相对时间 + +```html +<@global.timeline datetime="时间" /> + +// 输出 +x 年前/x 个月前/x 天前/昨天/x 小时前/x 分钟前/x 秒前/刚刚 +``` + +## 评论模块 + +```html +<@global.comment target= type="" /> +``` + +等同于: + +```html +<#if !post.disallowComment!false> + + + + +``` + +参数说明: + +- target:post / sheet / journal 对象 +- type:评论类型,可为:post / sheet / journal + +例子: + +在文章页面(post.ftl or post_xxx.ftl): + +```html +<@global.comment target=post type="post" /> +``` + +在自定义页面(sheet.ftl or post_sheet.ftl): + +```html +<@global.comment target=sheet type="sheet" /> +``` + +在日志页面(journals.ftl): + +```html +<@global.comment target=journal type="journal" /> +``` diff --git a/versioned_docs/version-1.5.0/developer-guide/theme/template-tag.md b/versioned_docs/version-1.5.0/developer-guide/theme/template-tag.md new file mode 100644 index 0000000..123e988 --- /dev/null +++ b/versioned_docs/version-1.5.0/developer-guide/theme/template-tag.md @@ -0,0 +1,2746 @@ +--- +title: 模板标签 +description: 用于获取数据的模板标签 +--- + +:::note +模板标签可以运用在页面的任何地方。 +::: + +## 文章(postTag) + +### 获取最新文章(latest) + +#### 语法 + +```html +<@postTag method="latest" top="获取条数"> +// do something + +``` + +参数: + +1. method:latest +2. top:所需要获取的条数 + +#### 返回参数 + +posts: + +```json +[{ + "categories": [{ + "createTime": "2020-10-11T05:22:08.264Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string" + }], + "commentCount": 0, + "createTime": "2020-10-11T05:22:08.264Z", + "disallowComment": true, + "editTime": "2020-10-11T05:22:08.264Z", + "editorType": "MARKDOWN", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaKeywords": "string", + "metas": {}, + "password": "string", + "slug": "string", + "status": "PUBLISHED", + "summary": "string", + "tags": [{ + "createTime": "2020-10-11T05:22:08.264Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + }], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2020-10-11T05:22:08.264Z", + "visits": 0, + "wordCount": 0 +}] +``` + +#### 示例 + +```html +<@postTag method="latest" top="3"> + <#list posts as post> + ${post.title!} + + +``` + +输出: + +```html +title1 +title2 +title3 +``` + +### 获取所有文章的数量(count) + +#### 语法 + +```html +<@postTag method="count"> +// do something + +``` + +参数: + +1. method:count + +#### 返回参数 + +```json +count: long +``` + +#### 示例 + +```html +<@postTag method="count"> +文章数量:${count!0} + +``` + +输出: + +```html +文章数量:20 +``` + +### 根据年份归档(archiveYear) + +#### 语法 + +```html +<@postTag method="archiveYear"> +// do something + +``` + +参数: + +1. method:archiveYear + +#### 返回参数 + +archives: + +```json +[{ + "posts": [{ + "categories": [{ + "createTime": "2020-10-11T05:30:45.245Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string" + }], + "commentCount": 0, + "createTime": "2020-10-11T05:30:45.245Z", + "disallowComment": true, + "editTime": "2020-10-11T05:30:45.245Z", + "editorType": "MARKDOWN", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaKeywords": "string", + "metas": {}, + "password": "string", + "slug": "string", + "status": "PUBLISHED", + "summary": "string", + "tags": [{ + "createTime": "2020-10-11T05:30:45.245Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + }], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2020-10-11T05:30:45.245Z", + "visits": 0, + "wordCount": 0 + }], + "year": 0 +}] +``` + +#### 示例 + +```html +<@postTag method="archiveYear"> + <#list archives as archive> +

年份: ${archive.year?c}

+
    + <#list archive.posts?sort_by("createTime")?reverse as post> +
  • + ${post.title!} +
  • + +
+ + +``` + +输出: + +```html +

2019

+ +

2018

+ +``` + +### 根据年月归档(archiveMonth) + +#### 语法 + +```html +<@postTag method="archiveMonth"> +// do something + +``` + +参数: + +1. method:archiveMonth + +#### 返回参数 + +archives: + +```json +[{ + "month": 0, + "posts": [{ + "categories": [{ + "createTime": "2020-10-11T05:35:01.835Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string" + }], + "commentCount": 0, + "createTime": "2020-10-11T05:35:01.835Z", + "disallowComment": true, + "editTime": "2020-10-11T05:35:01.835Z", + "editorType": "MARKDOWN", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaKeywords": "string", + "metas": {}, + "password": "string", + "slug": "string", + "status": "PUBLISHED", + "summary": "string", + "tags": [{ + "createTime": "2020-10-11T05:35:01.835Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + }], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2020-10-11T05:35:01.835Z", + "visits": 0, + "wordCount": 0 + }], + "year": 0 +}] +``` + +#### 示例 + +```html +<@postTag method="archiveMonth"> + <#list archives as archive> +

${archive.year?c}-${archive.month?c}

+
    + <#list archive.posts?sort_by("createTime")?reverse as post> +
  • + ${post.title!} +
  • + +
+ + +``` + +输出: + +```html +

2019-01

+ +

2018-12

+ +``` + +### 归档(archive) + +#### 语法 + +```html +<@postTag method="archive" type="year or month"> +// do something + +``` + +参数: + +1. method:archive +2. type: `year` 或者 `month` + +#### 返回参数 + +archives(year): + +```json +[{ + "posts": [{ + "categories": [{ + "createTime": "2020-10-11T05:30:45.245Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string" + }], + "commentCount": 0, + "createTime": "2020-10-11T05:30:45.245Z", + "disallowComment": true, + "editTime": "2020-10-11T05:30:45.245Z", + "editorType": "MARKDOWN", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaKeywords": "string", + "metas": {}, + "password": "string", + "slug": "string", + "status": "PUBLISHED", + "summary": "string", + "tags": [{ + "createTime": "2020-10-11T05:30:45.245Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + }], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2020-10-11T05:30:45.245Z", + "visits": 0, + "wordCount": 0 + }], + "year": 0 +}] +``` + +archives(month): + +```json +[{ + "month": 0, + "posts": [{ + "categories": [{ + "createTime": "2020-10-11T05:35:01.835Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string" + }], + "commentCount": 0, + "createTime": "2020-10-11T05:35:01.835Z", + "disallowComment": true, + "editTime": "2020-10-11T05:35:01.835Z", + "editorType": "MARKDOWN", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaKeywords": "string", + "metas": {}, + "password": "string", + "slug": "string", + "status": "PUBLISHED", + "summary": "string", + "tags": [{ + "createTime": "2020-10-11T05:35:01.835Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + }], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2020-10-11T05:35:01.835Z", + "visits": 0, + "wordCount": 0 + }], + "year": 0 +}] +``` + +#### 示例 + +```html +<@postTag method="archive" type="month"> + <#list archives as archive> +

${archive.year?c}-${archive.month?c}

+
    + <#list archive.posts?sort_by("createTime")?reverse as post> +
  • + ${post.title!} +
  • + +
+ + +``` + +输出: + +```html +

2019-01

+ +

2018-12

+ +``` + +### 根据分类 id 获取文章(listByCategoryId) + +#### 语法 + +```html +<@postTag method="listByCategoryId" categoryId="分类 id"> +// do something + +``` + +参数: + +1. method:listByCategoryId +2. categoryId:分类 id + +#### 返回参数 + +posts: + +```json +[{ + "categories": [{ + "createTime": "2020-10-11T05:35:01.811Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string" + }], + "commentCount": 0, + "createTime": "2020-10-11T05:35:01.811Z", + "disallowComment": true, + "editTime": "2020-10-11T05:35:01.811Z", + "editorType": "MARKDOWN", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaKeywords": "string", + "metas": {}, + "password": "string", + "slug": "string", + "status": "PUBLISHED", + "summary": "string", + "tags": [{ + "createTime": "2020-10-11T05:35:01.811Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + }], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2020-10-11T05:35:01.811Z", + "visits": 0, + "wordCount": 0 +}] +``` + +#### 示例 + +```html +<@postTag method="listByCategoryId" top="${category.id?c}"> + 分类 ${category.name!} 下的文章: + <#list posts as post> + ${post.title!} + + +``` + +输出: + +```html +title1 +title2 +title3 +``` + +### 根据分类 slug 获取文章(listByCategorySlug) + +#### 语法 + +```html +<@postTag method="listByCategorySlug" categorySlug="分类 slug"> +// do something + +``` + +参数: + +1. method:listByCategorySlug +2. categorySlug:分类 slug + +#### 返回参数 + +posts: + +```json +[{ + "categories": [{ + "createTime": "2020-10-11T05:35:01.811Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string" + }], + "commentCount": 0, + "createTime": "2020-10-11T05:35:01.811Z", + "disallowComment": true, + "editTime": "2020-10-11T05:35:01.811Z", + "editorType": "MARKDOWN", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaKeywords": "string", + "metas": {}, + "password": "string", + "slug": "string", + "status": "PUBLISHED", + "summary": "string", + "tags": [{ + "createTime": "2020-10-11T05:35:01.811Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + }], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2020-10-11T05:35:01.811Z", + "visits": 0, + "wordCount": 0 +}] +``` + +#### 示例 + +```html +<@postTag method="listByCategorySlug" categorySlug="${category.slug!}"> + 分类 ${category.name!} 下的文章: + <#list posts as post> + ${post.title!} + + +``` + +输出: + +```html +title1 +title2 +title3 +``` + +### 根据标签 id 获取文章(listByTagId) + +#### 语法 + +```html +<@postTag method="listByTagId" tagId="标签 id"> +// do something + +``` + +参数: + +1. method:listByTagId +2. tagId:标签 id + +#### 返回参数 + +posts: + +```json +[{ + "categories": [{ + "createTime": "2020-10-11T05:35:01.811Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string" + }], + "commentCount": 0, + "createTime": "2020-10-11T05:35:01.811Z", + "disallowComment": true, + "editTime": "2020-10-11T05:35:01.811Z", + "editorType": "MARKDOWN", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaKeywords": "string", + "metas": {}, + "password": "string", + "slug": "string", + "status": "PUBLISHED", + "summary": "string", + "tags": [{ + "createTime": "2020-10-11T05:35:01.811Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + }], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2020-10-11T05:35:01.811Z", + "visits": 0, + "wordCount": 0 +}] +``` + +#### 示例 + +```html +<@postTag method="listByTagId" tagId="${tag.id?c}"> + 标签 ${tag.name!} 下的文章: + <#list posts as post> + ${post.title!} + + +``` + +输出: + +```html +title1 +title2 +title3 +``` + +### 根据标签 slug 获取文章(listByTagSlug) + +#### 语法 + +```html +<@postTag method="listByTagSlug" tagSlug="标签 slug"> +// do something + +``` + +参数: + +1. method:listByTagSlug +2. tagSlug:标签 slug + +#### 返回参数 + +posts: + +```json +[{ + "categories": [{ + "createTime": "2020-10-11T05:35:01.811Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string" + }], + "commentCount": 0, + "createTime": "2020-10-11T05:35:01.811Z", + "disallowComment": true, + "editTime": "2020-10-11T05:35:01.811Z", + "editorType": "MARKDOWN", + "fullPath": "string", + "id": 0, + "likes": 0, + "metaDescription": "string", + "metaKeywords": "string", + "metas": {}, + "password": "string", + "slug": "string", + "status": "PUBLISHED", + "summary": "string", + "tags": [{ + "createTime": "2020-10-11T05:35:01.811Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" + }], + "template": "string", + "thumbnail": "string", + "title": "string", + "topPriority": 0, + "topped": true, + "updateTime": "2020-10-11T05:35:01.811Z", + "visits": 0, + "wordCount": 0 +}] +``` + +#### 示例 + +```html +<@postTag method="listByTagSlug" tagSlug="${tag.slug!}"> + 标签 ${tag.name!} 下的文章: + <#list posts as post> + ${post.title!} + + +``` + +输出: + +```html +title1 +title2 +title3 +``` + +## 评论(commentTag) + +### 获取最新评论(latest) + +#### 语法 + +```html +<@commentTag method="latest" top="获取条数"> +// do something + +``` + +参数: + +1. method:latest +2. top:所需要获取的条数 + +#### 返回参数 + +comments: + +```json +[{ + "allowNotification": true, + "author": "string", + "authorUrl": "string", + "content": "string", + "createTime": "2020-10-13T12:35:54.974Z", + "email": "string", + "gravatarMd5": "string", + "id": 0, + "ipAddress": "string", + "isAdmin": true, + "parentId": 0, + "post": { + "createTime": "2020-10-13T12:35:54.974Z", + "editTime": "2020-10-13T12:35:54.974Z", + "editorType": "MARKDOWN", + "fullPath": "string", + "id": 0, + "metaDescription": "string", + "metaKeywords": "string", + "slug": "string", + "status": "PUBLISHED", + "title": "string", + "updateTime": "2020-10-13T12:35:54.974Z" + }, + "status": "PUBLISHED", + "userAgent": "string" +}] +``` + +#### 示例 + +```html +<@commentTag method="latest" top="获取条数"> +
    + <#list comments.content as comment> +
  • ${comment.author!}:${comment.content!}
  • + +
+ +``` + +输出: + +```html +
    +
  • author1:content1
  • +
  • author2:content2
  • +
+``` + +### 获取所有评论的数量(count) + +#### 语法 + +```html +<@commentTag method="count"> +// do something + +``` + +参数: + +1. method:count + +#### 返回参数 + +```json +count: long +``` + +#### 示例 + +```html +<@commentTag method="count"> +评论数量:${count!0} + +``` + +输出: + +```html +文章数量:20 +``` + +## 分类目录(categoryTag) + +### 获取所有分类目录(list) + +#### 语法 + +```html +<@categoryTag method="list"> +// do something + +``` + +参数: + +1. method:list + +#### 返回参数 + +categories: + +```json +[{ + "createTime": "2020-10-11T05:59:40.622Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string", + "postCount": 0 +}] +``` + +#### 示例 + +```html +<@categoryTag method="list"> + <#list categories as category> + ${category.name!}(${category.postCount!}) + + +``` + +输出: + +```html +name1(2) +name2(12) +``` + +### 获取分类目录树结构(tree) + +#### 语法 + +```html +<@categoryTag method="tree"> +// do something + +``` + +参数: + +1. method:tree + +#### 返回参数 + +categories: + +```json +[ + { + "children": [ + { + "children": [], + "createTime": "2022-02-12T14:11:06.376Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "password": "string", + "slug": "string", + "thumbnail": "string" + } + ], + "createTime": "2022-02-12T14:11:06.376Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "password": "string", + "slug": "string", + "thumbnail": "string" + } +] +``` + +#### 示例 + +```html +<@categoryTag method="tree"> +
    + <#list categories as category> +
  • + + ${category.name!} + +
  • + + <#if category.children?? && category.children?size gt 0> + <@renderCategories category.children> + + +
+ + +<#macro renderCategories categories> +
    + <#list categories as category> +
  • + + ${(category.name)!} + + <#if category.children?? && category.children?size gt 0> + <@renderCategories category.children> + +
  • + +
+ +``` + +输出: + +```html + +``` + +### 获取文章的所有分类(listByPostId) + +#### 语法 + +```html +<@categoryTag method="listByPostId" postId="文章 id"> +// do something + +``` + +参数: + +1. method:listByPostId +2. postId:文章 id + +#### 返回参数 + +categories: + +```json +[{ + "createTime": "2020-10-11T05:59:40.622Z", + "description": "string", + "fullPath": "string", + "id": 0, + "name": "string", + "parentId": 0, + "slug": "string", + "thumbnail": "string" +}] +``` + +#### 示例 + +```html +<@categoryTag method="listByPostId" postId="${post.id?c}"> + <#list categories as category> + ${category.name} + + +``` + +输出: + +```html +name1 +name2 +``` + +### 获取所有分类的数量(count) + +#### 语法 + +```html +<@categoryTag method="count"> +// do something + +``` + +参数: + +1. method:count + +#### 返回参数 + +```json +count: long +``` + +#### 示例 + +```html +<@categoryTag method="count"> +分类数量:${count!0} + +``` + +输出: + +```html +分类数量:20 +``` + +## 标签(tagTag) + +### 获取所有标签(list) + +#### 语法 + +```html +<@tagTag method="list"> +// 返回参数:tags + +``` + +参数: + +1. method:list + +#### 返回参数 + +tags: + +```json +[{ + "createTime": "2020-10-11T06:14:30.595Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string", + "postCount": 0 +}] +``` + +#### 示例 + +```html +<@tagTag method="list"> + <#list tags as tag> + ${tag.name!}(${tag.postCount!}) + + +``` + +输出: + +```html +name1(1) +name2(20) +``` + +### 获取文章的所有标签(listByPostId) + +#### 语法 + +```html +<@tagTag method="listByPostId" postId="文章 id"> +// do something + +``` + +参数: + +1. method:listByPostId +2. postId:文章 id + +#### 返回参数 + +tags: + +```json +[{ + "createTime": "2020-10-11T06:14:30.595Z", + "fullPath": "string", + "id": 0, + "name": "string", + "slug": "string", + "thumbnail": "string" +}] +``` + +#### 示例 + +```html +<@tagTag method="listByPostId" postId="${post.id?c}"> + <#list tags as tag> + ${tag.name} + + +``` + +输出: + +```html +name1 +name2 +``` + +### 获取所有标签的数量(count) + +#### 语法 + +```html +<@tagTag method="count"> +// do something + +``` + +参数: + +1. method:count + +#### 返回参数 + +```json +count: long +``` + +#### 示例 + +```html +<@tagTag method="count"> +标签数量:${count!0} + +``` + +输出: + +```html +标签数量:20 +``` + +## 菜单(menuTag) + +### 获取所有菜单(list) + +#### 语法 + +```html +<@menuTag method="list"> +// do something + +``` + +参数: + +1. method:list + +#### 返回参数 + +menus: + +```json +[{ + "icon": "string", + "id": 0, + "name": "string", + "parentId": 0, + "priority": 0, + "target": "string", + "team": "string", + "url": "string" +}] +``` + +#### 示例 + +```html +<@menuTag method="list"> + + +``` + +输出: + +```html + +``` + +### 获取多级菜单(tree) + +#### 语法 + +```html +<@menuTag method="tree"> +// do something + +``` + +参数: + +1. method:tree + +#### 返回参数 + +menus: + +```json +[{ + "children": [{ + "children": [{}], + "icon": "string", + "id": 0, + "name": "string", + "parentId": 0, + "priority": 0, + "target": "string", + "team": "string", + "url": "string" + }], + "icon": "string", + "id": 0, + "name": "string", + "parentId": 0, + "priority": 0, + "target": "string", + "team": "string", + "url": "string" +}] +``` + +#### 示例 + +```html +<@menuTag method="tree"> +
    + <#list menus as menu> +
  • + ${menu.name!} + <#if menu.children?? && menu.children?size gt 0> + + +
  • + +
+ +``` + +输出: + +```html + +``` + +### 根据分组获取菜单(listByTeam) + +#### 语法 + +```html +<@menuTag method="listByTeam" team="team 名称"> +// do something + +``` + +参数: + +1. method:listByTeam +2. team:team 名称 + +#### 返回参数 + +menus: + +```json +[{ + "icon": "string", + "id": 0, + "name": "string", + "parentId": 0, + "priority": 0, + "target": "string", + "team": "string", + "url": "string" +}] +``` + +#### 示例 + +```html +<@menuTag method="listByTeam" team="main"> + + +``` + +输出: + +```html + +``` + +### 根据分组获取多级菜单(treeByTeam) + +#### 语法 + +```html +<@menuTag method="treeByTeam" team="team 名称"> +// do something + +``` + +参数: + +1. method:treeByTeam +2. team:team 名称 + +#### 返回参数 + +menus: + +```json +[{ + "children": [{ + "children": [{}], + "icon": "string", + "id": 0, + "name": "string", + "parentId": 0, + "priority": 0, + "target": "string", + "team": "string", + "url": "string" + }], + "icon": "string", + "id": 0, + "name": "string", + "parentId": 0, + "priority": 0, + "target": "string", + "team": "string", + "url": "string" +}] +``` + +#### 示例 + +```html +<@menuTag method="treeByTeam" team="main"> +
    + <#list menus as menu> +
  • + ${menu.name!} + <#if menu.children?? && menu.children?size gt 0> + + +
  • + +
+ +``` + +输出: + +```html + +``` + +## 友情链接(linkTag) + +### 获取所有友情链接(list) + +#### 语法 + +```html +<@linkTag method="list"> +// do something + +``` + +参数: + +1. method:list + +#### 返回参数 + +links: + +```json +[{ + "id": 0, + "name": "string", + "url": "string", + "logo": "string", + "description": "string", + "team": "string", + "priority": 0, + "createTime": "2021-01-10 20:48:00", + "updateTime": "2021-01-10 20:48:00" +}] +``` + +#### 示例 + +```html + +``` + +输出: + +```html + +``` + +### 乱序获取所有友情链接(listByRandom) + +#### 语法 + +```html +<@linkTag method="listByRandom"> +// do something + +``` + +参数: + +1. method:listByRandom + +#### 返回参数 + +```json +[{ + "id": 0, + "name": "string", + "url": "string", + "logo": "string", + "description": "string", + "team": "string", + "priority": 0, + "createTime": "2021-01-10 20:48:00", + "updateTime": "2021-01-10 20:48:00" +}] +``` + +#### 示例 + +```html + +``` + +输出: + +```html + +``` + +### 获取分组友情链接(listTeams) + +#### 语法 + +```html +<@linkTag method="listTeams"> +// do something + +``` + +参数: + +1. method:listTeams + +#### 返回参数 + +teams: + +```json +[{ + "team": "string", + "links": [{ + "id": 0, + "name": "string", + "url": "string", + "logo": "string", + "description": "string", + "team": "string", + "priority": 0 + }] +}] +``` + +#### 示例 + +```html +<@linkTag method="listTeams"> + <#list teams as team> +

${team.team}

+ + + +``` + +输出: + +```html +

Halo 相关

+ +

网友们

+ +``` + +### 乱序获取分组友情链接(listTeamsByRandom) + +#### 语法 + +```html +<@linkTag method="listTeamsByRandom"> +// do something + +``` + +参数: + +1. method:listTeamsByRandom + +#### 返回参数 + +teams: + +```json +[{ + "team": "string", + "links": [{ + "id": 0, + "name": "string", + "url": "string", + "logo": "string", + "description": "string", + "team": "string", + "priority": 0 + }] +}] +``` + +#### 示例 + +```html +<@linkTag method="listTeamsByRandom"> + <#list teams as team> +

${team.team}

+ + + +``` + +输出: + +```html +

Halo 相关

+ +

网友们

+ +``` + +### 获取所有友情链接的数量(count) + +#### 语法 + +```html +<@linkTag method="count"> +// do something + +``` + +参数: + +1. method:count + +#### 返回参数 + +```json +count: long +``` + +#### 示例 + +```html +<@linkTag method="count"> +友情链接数量:${count!0} + +``` + +输出: + +```html +友情链接数量:20 +``` + +## 图库(photoTag) + +### 获取所有图片(list) + +#### 语法 + +```html +<@photoTag method="list"> +// do something + +``` + +参数: + +1. method:list + +#### 返回参数 + +photos: + +```json +[{ + "id": 0, + "name": "string", + "description": "string", + "takeTime": "2021-01-10 20:48:00", + "location": "string", + "thumbnail": "string", + "url": "string", + "team": "string", + "createTime": "2021-01-10 20:48:00", + "updateTime": "2021-01-10 20:48:00" +}] +``` + +#### 示例 + +```html +<@photoTag method="list"> + <#list photos as photo> + ${photo.description} + + +``` + +输出: + +```html +山川 +河流 +绿树 +``` + +### 获取所有分组图片(listTeams) + +#### 语法 + +```html +<@photoTag method="listTeams"> +// do something + +``` + +参数: + +1. method:listTeams + +#### 返回参数 + +teams: + +```json +[{ + "team": "string", + "photos": [{ + "id": 0, + "name": "string", + "thumbnail": "string", + "takeTime": "2021-01-10 20:48:00", + "url": "string", + "team": "string", + "location": "string", + "description": "string" + }] +}] +``` + +#### 示例 + +```html +<@photoTag method="listTeams"> + <#list teams as team> +

${team.team}

+ <#list team.photos as photo> + ${photo.description} + + + +``` + +输出: + +```html +

风景

+山川 +河流 +绿树 +

旅行

+四川 +重庆 +深圳 +``` + +### 根据分组获取图片(listByTeam) + +#### 语法 + +```html +<@photoTag method="listByTeam" team="team 名称"> +// do something + +``` + +参数: + +1. method:listByTeam +2. team:team 名称 + +#### 返回参数 + +photos: + +```json +[{ + "id": 0, + "name": "string", + "description": "string", + "takeTime": "2021-01-10 20:48:00", + "location": "string", + "thumbnail": "string", + "url": "string", + "team": "string", + "createTime": "2021-01-10 20:48:00", + "updateTime": "2021-01-10 20:48:00" +}] +``` + +#### 示例 + +```html +<@photoTag method="listTeams" team="风景"> + <#list team.photos as photo> + ${photo.description} + + +``` + +输出: + +```html +山川 +河流 +绿树 +``` + +### 获取所有图片的数量(count) + +#### 语法 + +```html +<@photoTag method="count"> +// do something + +``` + +参数: + +1. method:count + +#### 返回参数 + +```json +count: long +``` + +#### 示例 + +```html +<@linkTag method="count"> +图片数量:${count!0} + +``` + +输出: + +```html +图片数量:20 +``` + +## 分页(paginationTag) + +### 获取首页文章列表的分页数据(index) + +#### 语法 + +```html +<@paginationTag method="index" page="${posts.number}" total="${posts.totalPages}" display="3"> +// do something + +``` + +参数: + +1. method:index +2. page:当前页,通过 `${posts.number}` 得到 +3. total:总页数,通过 `${posts.totalPages}` 得到 +4. display:页码展示数量 + +#### 返回参数 + +pagination: + +```json +{ + "nextPageFullPath": "string", + "prevPageFullPath": "string", + "hasPrev": true, + "hasNext": true, + "rainbowPages": [{ + "page": 0, + "fullPath": "string", + "isCurrent": true + }] +} +``` + +#### 示例 + +```html +
    + <@paginationTag method="index" page="${posts.number}" total="${posts.totalPages}" display="3"> + <#if pagination.hasPrev> +
  • + 上一页 +
  • + + <#list pagination.rainbowPages as number> + <#if number.isCurrent> +
  • + ${number.page!} +
  • + <#else> +
  • + ${number.page!} +
  • + + + <#if pagination.hasNext> +
  • + 下一页 +
  • + + +
+``` + +输出: + +```html + +``` + +### 获取文章归档列表的分页数据(archives) + +#### 语法 + +```html +<@paginationTag method="archives" page="${posts.number}" total="${posts.totalPages}" display="3"> +// do something + +``` + +参数: + +1. method:archives +2. page:当前页,通过 `${posts.number}` 得到 +3. total:总页数,通过 `${posts.totalPages}` 得到 +4. display:页码展示数量 + +#### 返回参数 + +pagination: + +```json +{ + "nextPageFullPath": "string", + "prevPageFullPath": "string", + "hasPrev": true, + "hasNext": true, + "rainbowPages": [{ + "page": 0, + "fullPath": "string", + "isCurrent": true + }] +} +``` + +#### 示例 + +```html +
    + <@paginationTag method="archives" page="${posts.number}" total="${posts.totalPages}" display="3"> + <#if pagination.hasPrev> +
  • + 上一页 +
  • + + <#list pagination.rainbowPages as number> + <#if number.isCurrent> +
  • + ${number.page!} +
  • + <#else> +
  • + ${number.page!} +
  • + + + <#if pagination.hasNext> +
  • + 下一页 +
  • + + +
+``` + +输出: + +```html + +``` + +### 获取搜索结果文章列表的分页数据(search) + +#### 语法 + +```html +<@paginationTag method="search" page="${posts.number}" total="${posts.totalPages}" keyword="${keyword}" display="3"> +// do something + +``` + +参数: + +1. method:search +2. page:当前页,通过 `${posts.number}` 得到 +3. total:总页数,通过 `${posts.totalPages}` 得到 +4. keyword: 关键词 +5. display:页码展示数量 + +#### 返回参数 + +pagination: + +```json +{ + "nextPageFullPath": "string", + "prevPageFullPath": "string", + "hasPrev": true, + "hasNext": true, + "rainbowPages": [{ + "page": 0, + "fullPath": "string", + "isCurrent": true + }] +} +``` + +#### 示例 + +```html +
    + <@paginationTag method="search" page="${posts.number}" total="${posts.totalPages}" keyword="${keyword}" display="3"> + <#if pagination.hasPrev> +
  • + 上一页 +
  • + + <#list pagination.rainbowPages as number> + <#if number.isCurrent> +
  • + ${number.page!} +
  • + <#else> +
  • + ${number.page!} +
  • + + + <#if pagination.hasNext> +
  • + 下一页 +
  • + + +
+``` + +输出: + +```html + +``` + +### 获取标签下文章列表的分页数据(tagPosts) + +#### 语法 + +```html +<@paginationTag method="tagPosts" slug="${tag.slug!}" page="${posts.number}" total="${posts.totalPages}" display="3"> +// do something + +``` + +参数: + +1. method:tagPosts +2. page:当前页,通过 `${posts.number}` 得到 +3. total:总页数,通过 `${posts.totalPages}` 得到 +4. display:页码展示数量 +5. slug:标签 slug + +#### 返回参数 + +pagination: + +```json +{ + "nextPageFullPath": "string", + "prevPageFullPath": "string", + "hasPrev": true, + "hasNext": true, + "rainbowPages": [{ + "page": 0, + "fullPath": "string", + "isCurrent": true + }] +} +``` + +#### 示例 + +```html +
    + <@paginationTag method="tagPosts" slug="${tag.slug!}" page="${posts.number}" total="${posts.totalPages}" display="3"> + <#if pagination.hasPrev> +
  • + 上一页 +
  • + + <#list pagination.rainbowPages as number> + <#if number.isCurrent> +
  • + ${number.page!} +
  • + <#else> +
  • + ${number.page!} +
  • + + + <#if pagination.hasNext> +
  • + 下一页 +
  • + + +
+``` + +输出: + +```html + +``` + +### 获取分类下文章列表的分页数据(categoryPosts) + +#### 语法 + +```html +<@paginationTag method="categoryPosts" slug="${category.slug!}" page="${posts.number}" total="${posts.totalPages}" display="3"> +// do something + +``` + +参数: + +1. method:categoryPosts +2. page:当前页,通过 `${posts.number}` 得到 +3. total:总页数,通过 `${posts.totalPages}` 得到 +4. display:页码展示数量 +5. slug:标签 slug + +#### 返回参数 + +pagination: + +```json +{ + "nextPageFullPath": "string", + "prevPageFullPath": "string", + "hasPrev": true, + "hasNext": true, + "rainbowPages": [{ + "page": 0, + "fullPath": "string", + "isCurrent": true + }] +} +``` + +#### 示例 + +```html +
    + <@paginationTag method="categoryPosts" slug="${category.slug!}" page="${posts.number}" total="${posts.totalPages}" display="3"> + <#if pagination.hasPrev> +
  • + 上一页 +
  • + + <#list pagination.rainbowPages as number> + <#if number.isCurrent> +
  • + ${number.page!} +
  • + <#else> +
  • + ${number.page!} +
  • + + + <#if pagination.hasNext> +
  • + 下一页 +
  • + + +
+``` + +输出: + +```html + +``` + +### 获取图库页面图片列表的分页数据(photos) + +#### 语法 + +```html +<@paginationTag method="photos" page="${photos.number}" total="${photos.totalPages}" display="3"> +// do something + +``` + +参数: + +1. method:photos +2. page:当前页,通过 `${photos.number}` 得到 +3. total:总页数,通过 `${photos.totalPages}` 得到 +4. display:页码展示数量 + +#### 返回参数 + +pagination: + +```json +{ + "nextPageFullPath": "string", + "prevPageFullPath": "string", + "hasPrev": true, + "hasNext": true, + "rainbowPages": [{ + "page": 0, + "fullPath": "string", + "isCurrent": true + }] +} +``` + +#### 示例 + +```html +
    + <@paginationTag method="photos" page="${photos.number}" total="${photos.totalPages}" display="3"> + <#if pagination.hasPrev> +
  • + 上一页 +
  • + + <#list pagination.rainbowPages as number> + <#if number.isCurrent> +
  • + ${number.page!} +
  • + <#else> +
  • + ${number.page!} +
  • + + + <#if pagination.hasNext> +
  • + 下一页 +
  • + + +
+``` + +输出: + +```html + +``` + +### 获取日志页面日志列表的分页数据(journals) + +#### 语法 + +```html +<@paginationTag method="journals" page="${journals.number}" total="${journals.totalPages}" display="3"> +// do something + +``` + +参数: + +1. method:journals +2. page:当前页,通过 `${journals.number}` 得到 +3. total:总页数,通过 `${journals.totalPages}` 得到 +4. display:页码展示数量 + +#### 返回参数 + +pagination: + +```json +{ + "nextPageFullPath": "string", + "prevPageFullPath": "string", + "hasPrev": true, + "hasNext": true, + "rainbowPages": [{ + "page": 0, + "fullPath": "string", + "isCurrent": true + }] +} +``` + +#### 示例 + +```html +
    + <@paginationTag method="journals" page="${journals.number}" total="${journals.totalPages}" display="3"> + <#if pagination.hasPrev> +
  • + 上一页 +
  • + + <#list pagination.rainbowPages as number> + <#if number.isCurrent> +
  • + ${number.page!} +
  • + <#else> +
  • + ${number.page!} +
  • + + + <#if pagination.hasNext> +
  • + 下一页 +
  • + + +
+``` + +输出: + +```html + +``` diff --git a/versioned_docs/version-1.5.0/getting-started/config.md b/versioned_docs/version-1.5.0/getting-started/config.md new file mode 100644 index 0000000..9afa8bc --- /dev/null +++ b/versioned_docs/version-1.5.0/getting-started/config.md @@ -0,0 +1,238 @@ +--- +title: 配置参考 +description: Halo 配置文件的详细介绍及参考 +--- + +Halo 的配置文件名为 `application.yaml`,其必须位于[工作目录](/getting-started/prepare#工作目录) `~/.halo` 下。 Halo 会读取该目录下的配置文件进行加载。 + +:::info +如果您是单独下载的官方配置文件,则必须将文件名 application-template.yaml 重命名为 application.yaml +::: + +如下将详细列出配置文件 `application.yaml` 中所有的配置项。 + +## 基础配置 + +基础配置中的配置设置一般来说是**必要的**,且必须在 application.yaml 里进行定义。 + +### 端口 + +用于指定 HTTP 服务器监听的端口,Halo 默认设置为 `8090`。 + +```yaml +server: + port: 8090 +``` + +请注意,如果您选择设置端口为 `80`,则需要确保您的 80 端口未被占用,通常**不建议**直接设置为 80 端口。 + +### 数据库 + +Halo 目前支持 `H2` 及 `MySQL` 数据库。 + +:::tip +得益于我们使用的 ORM 框架,Halo 在首次启动的时候会自动根据实体类创建表结构,无需通过 SQL 脚本自行创建,也不会提供所谓的 SQL 脚本。所以,此步骤仅需配置好数据库连接地址和用户名密码即可。注意,H2 无需手动创建数据库,MySQL 需要。 +::: + +#### H2 + +:::info +推荐使用 **H2**,较其他数据库来说更为方便。 +::: + +```yaml +spring: + datasource: + driver-class-name: org.h2.Driver + url: jdbc:h2:file:~/.halo/db/halo + username: admin + password: 123456 + h2: + console: + settings: + web-allow-others: false + path: /h2-console + enabled: false +``` + +**注意事项**: + +- `url` 为默认的数据本地存储地址,请勿修改。 +- 默认的数据库账户和密码为 `admin` 和 `123456`,建议将其修改,并妥善保存(此用户名和密码在 Halo 第一次启动的时候将自动创建。并且不支持首次启动后,通过修改配置文件中的账户或者密码,如果修改,再次启动将提示用户名或者密码错误。)。 +- 线上环境中,`h2` 的配置使用默认即可。如果需要手动修改一些数据,可将 `web-allow-others` 和 `enabled` 设为 `true` 来开启 h2 控制台,访问路径为 `ip:端口/h2-console`。`JDBC URL`,`username`,`password` 使用配置文件中的即可。 + +:::warning +特别注意:在开启 `h2-console` 并完成所需操作之后,一定要再次关闭 `h2-console` 并重启,不要长时间将 `h2-console` 处于开启状态,这可能会有隐性的安全风险。 +::: + +#### MySQL + +```yaml +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/halodb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: root + password: 123456 +``` + +**要求**: + +1. 版本:5.7 + +2. 字符集(Character Set):`utf8mb4` +3. 排序规则(Collate):`utf8mb4_bin` +4. 存储引擎:`InnoDB` + +综上,建议创建数据库采用下面的命令: + +```bash +create database halodb character set utf8mb4 collate utf8mb4_bin; +``` + +**注意事项**: + +- `username` 及 `password` 需要修改为您的 MySQL 数据库账号和密码。 +- 默认时区为 `Asia/Shanghai`,如果与您所在时区不一致,则可以修改为您所在的时区。 + +## 高级配置 + +高级配置中的配置设置是可选的,如果不需要,可以略过。 + +### 后台路径 + +Halo 支持自定义后台管理的**根路径**。 + +```yaml +halo: + # Your admin client path is https://your-domain/{admin-path} + admin-path: admin +``` + +注意:仅为改动后台管理的根路径,因此前后不带 `/`。 + +### 缓存 + +某些情况下,需要用户根据需求来设置缓存数据的保存方式,例如将缓存数据持久化保存在本地。 + +```yaml +halo: + # memory or level or redis + cache: memory +``` + +目前支持三种策略: + +- `memory` 将数据缓存至内存,重启服务缓存将清空。 +- `level` 将数据缓存至本地,重启服务不会清空缓存。 +- `redis` 将数据缓存至 Redis,重启服务不会清空缓存,如需分布式部署 Halo,请选用此种缓存方式。 + +**注意事项**: + +- 如果选用 Redis 缓存方式,请在配置文件加入 Redis 相关配置,完整的配置示例如下: + +```yaml +server: + port: 8090 + + # Response data gzip. + compression: + enabled: true + +spring: + datasource: + # MySQL database configuration. + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/halodb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: root + password: 123456 + redis: + # Redis cache configuration. + port: 6379 + database: 0 + host: 127.0.0.1 + password: 123456 + +halo: + # Your admin client path is https://your-domain/{admin-path} + admin-path: admin + + # memory or level or redis + cache: redis +``` + +### 压缩 + +启用压缩对于减少带宽和加快页面加载非常有用,在**未使用** `Nginx` 或 `Caddy` 等反向代理服务器时(反向代理服务器通常是默认开启 Gzip 的),可以考虑开启系统自带的 Gzip 功能。 + +```yaml +server: + # Response data gzip. + compression: + enabled: true +``` + +## 示例配置文件 + +:::info +建议根据使用的数据库类型查看。 +::: + +### H2 Database + +```yaml +server: + port: 8090 + + # Response data gzip. + compression: + enabled: true +spring: + datasource: + # H2 database configuration. + driver-class-name: org.h2.Driver + url: jdbc:h2:file:~/.halo/db/halo + username: admin + password: 123456 + + # H2 database console configuration. + h2: + console: + settings: + web-allow-others: false + path: /h2-console + enabled: false + +halo: + # Your admin client path is https://your-domain/{admin-path} + admin-path: admin + + # memory or level or redis + cache: memory +``` + +### MySQL + +```yaml +server: + port: 8090 + + # Response data gzip. + compression: + enabled: true +spring: + datasource: + # MySQL database configuration. + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/halodb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: root + password: 123456 + +halo: + # Your admin client path is https://your-domain/{admin-path} + admin-path: admin + + # memory or level or redis + cache: memory +``` + +官方的完整示例配置文件可以在 [https://dl.halo.run/config/](https://dl.halo.run/config/) 下找到。 diff --git a/versioned_docs/version-1.5.0/getting-started/downloads.md b/versioned_docs/version-1.5.0/getting-started/downloads.md new file mode 100644 index 0000000..591ab97 --- /dev/null +++ b/versioned_docs/version-1.5.0/getting-started/downloads.md @@ -0,0 +1,25 @@ +--- +title: 资源下载 +description: 目前所有与 Halo 相关的下载地址 +--- + +## Github + +:::note +如果您的服务器在海外,推荐从 GitHub 下载。 +::: + +- [运行包](https://github.com/halo-dev/halo/releases) +- [配置文件](https://github.com/halo-dev/halo-common) + +## 官方镜像源 + +- [https://dl.halo.run](https://dl.halo.run) + +此镜像源由 [Nova Kwok](https://nova.moe/) 提供并维护。 + +## 三方镜像源 + +- [https://halo.cary.tech](https://halo.cary.tech) + +此镜像源由 [新逸Cary](https://blog.xinac.cn) 提供并维护。 diff --git a/versioned_docs/version-1.5.0/getting-started/install/docker.md b/versioned_docs/version-1.5.0/getting-started/install/docker.md new file mode 100644 index 0000000..cbcad15 --- /dev/null +++ b/versioned_docs/version-1.5.0/getting-started/install/docker.md @@ -0,0 +1,112 @@ +--- +title: 使用 Docker 部署 Halo +description: 使用 Docker 部署 +--- + +:::info +在继续操作之前,我们推荐您先阅读[《写在前面》](../prepare),这可以快速帮助你了解 Halo。 +::: + +## 使用 Docker 镜像 + +Halo 在 Docker Hub 上发布的镜像为 [halohub/halo](https://hub.docker.com/r/halohub/halo) + +1. 创建 [工作目录](../prepare#工作目录) + +```bash +mkdir ~/.halo && cd ~/.halo +``` + +2. 下载示例配置文件到 [工作目录](../prepare#工作目录) + +```bash +wget https://dl.halo.run/config/application-template.yaml -O ./application.yaml +``` + +3. 编辑配置文件,配置数据库或者端口等,如需配置请参考 [配置参考](../config) + +```bash +vim application.yaml +``` + +4. 拉取最新的 Halo 镜像 + +```bash +docker pull halohub/halo:1.5.0 +``` + +:::info +查看最新版本镜像: ,我们推荐使用具体版本号的镜像,但也提供了 `latest` 标签的镜像,它始终是最新的。 +::: + +5. 创建容器 + +```bash +docker run -it -d --name halo -p 8090:8090 -v ~/.halo:/root/.halo --restart=unless-stopped halohub/halo:1.5.0 +``` + +:::info +注意:此命令默认使用自带的 H2 Database 数据库。如需使用 MySQL,请参考:[使用 Docker 部署 Halo 和 MySQL](./other/docker-mysql) +::: + +- **-it:** 开启输入功能并连接伪终端 +- **-d:** 后台运行容器 +- **--name:** 为容器指定一个名称 +- **-p:** 端口映射,格式为 `主机(宿主)端口:容器端口` ,可在 `application.yaml` 配置。 +- **-v:** 工作目录映射。形式为:-v 宿主机路径:/root/.halo,后者不能修改。 +- **--restart:** 建议设置为 `unless-stopped`,在 Docker 启动的时候自动启动 Halo 容器。 + +6. 打开 `http://ip:端口号` 即可看到安装引导界面。 + +:::tip +如果需要配置域名访问,建议先配置好反向代理以及域名解析再进行初始化。如果通过 `http://ip:端口号` 的形式无法访问,请到服务器厂商后台将运行的端口号添加到安全组,如果服务器使用了 Linux 面板,请检查此 Linux 面板是否有还有安全组配置,需要同样将端口号添加到安全组。 +::: + +## 反向代理 + +你可以在下面的反向代理软件中任选一项,我们假设你已经安装好了其中一项,并对其的基本操作有一定了解。如果你对 Nginx 不熟悉,我们推荐使用 [OneinStack](./other/oneinstack) 来管理 Nginx。 + +### Nginx + +```nginx +upstream halo { + server 127.0.0.1:8090; +} +server { + listen 80; + listen [::]:80; + server_name www.yourdomain.com; + client_max_body_size 1024m; + location / { + proxy_pass http://halo; + proxy_set_header HOST $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} +``` + +### Caddy 1.x + +```txt +https://www.yourdomain.com { + gzip + tls your@email.com + proxy / localhost:8090 { + transparent + } +} +``` + +### Caddy 2.x + +```txt +www.yourdomain.com + +encode gzip + +reverse_proxy 127.0.0.1:8090 +``` + +以上配置都可以在 找到。 diff --git a/versioned_docs/version-1.5.0/getting-started/install/linux.md b/versioned_docs/version-1.5.0/getting-started/install/linux.md new file mode 100644 index 0000000..62ba59c --- /dev/null +++ b/versioned_docs/version-1.5.0/getting-started/install/linux.md @@ -0,0 +1,271 @@ +--- +title: 在 Linux 环境部署 +description: 在 Linux 上快速安装 Halo +--- + +:::info +在继续操作之前,我们推荐您先阅读[《写在前面》](../prepare),这可以快速帮助你了解 Halo。 +::: + +## 依赖检查 + +目前运行 Halo 的最低依赖要求为 JRE 11,请务必确保在进行下面操作之前已经正确安装了 JRE。 + +目前介绍两种 Linux 发行版的安装方式,均为 OpenJRE,不推荐 Oracle 版本。 + +### CentOS + +```bash +sudo yum install java-11-openjdk -y +``` + +检查版本: + +```bash +java -version +``` + +如果输出以下类似内容即代表成功 + +```bash +openjdk version "11.0.10" 2021-01-19 LTS +OpenJDK Runtime Environment 18.9 (build 11.0.10+9-LTS) +OpenJDK 64-Bit Server VM 18.9 (build 11.0.10+9-LTS, mixed mode, sharing) +``` + +### Ubuntu + +```bash +sudo apt-get install openjdk-11-jre -y +``` + +检查版本: + +```bash +java -version +``` + +如果输出以下类似内容即代表成功 + +```bash +openjdk version "11.0.10" 2021-01-19 +OpenJDK Runtime Environment (build 11.0.10+9-Ubuntu-0ubuntu1.20.04) +OpenJDK 64-Bit Server VM (build 11.0.10+9-Ubuntu-0ubuntu1.20.04, mixed mode) +``` + +## 安装 + +1. 创建新的系统用户 + +:::info +我们不推荐直接使用系统 root 用户来运行 Halo。如果您需要直接使用 root 用户,请跳过这一步。 +::: + +创建一个名为 halo 的用户(名字可以随意) + +```bash +useradd -m halo +``` + +给予 sudo 权限 + +```bash +usermod -aG wheel halo +``` + +为 halo 用户创建密码 + +```bash +passwd halo +``` + +登录到 halo 账户 + +```bash +su - halo +``` + +2. 创建存放 [运行包](../prepare#运行包) 的目录,这里以 `~/app` 为例 + +```bash +mkdir ~/app && cd ~/app +``` + +3. 下载运行包 + +```bash +wget https://dl.halo.run/release/halo-1.5.0.jar -O halo.jar +``` + +:::info +如果下载速度不理想,可以 [在这里](../downloads) 选择其他下载地址。 +::: + +4. 创建 [工作目录](../prepare#工作目录) + +```bash +mkdir ~/.halo && cd ~/.halo +``` + +5. 下载示例配置文件到 [工作目录](../prepare#工作目录) + +```bash +wget https://dl.halo.run/config/application-template.yaml -O ./application.yaml +``` + +6. 编辑配置文件,配置数据库或者端口等,如需配置请参考 [配置参考](../config) + +```bash +vim application.yaml +``` + +7. 测试运行 Halo + +```bash +cd ~/app && java -jar halo.jar +``` + +8. 如看到类似以下日志输出,则代表启动成功。 + +```bash +run.halo.app.listener.StartedListener : Halo started at http://127.0.0.1:8090 +run.halo.app.listener.StartedListener : Halo admin started at http://127.0.0.1:8090/admin +run.halo.app.listener.StartedListener : Halo has started successfully! +``` + +打开 `http://ip:端口号` 即可看到安装引导界面。 + +:::info +如测试启动正常,请继续看`作为服务运行`部分,第 8 步仅仅作为测试。当你关闭 ssh 连接之后,服务会停止。你可使用 CTRL+C 停止运行测试进程。 +::: + +:::tip +如果需要配置域名访问,建议先配置好反向代理以及域名解析再进行初始化。如果通过 `http://ip: 端口号` 的形式无法访问,请到服务器厂商后台将运行的端口号添加到安全组,如果服务器使用了 Linux 面板,请检查此 Linux 面板是否有还有安全组配置,需要同样将端口号添加到安全组。 +::: + +## 作为服务运行 + +1. 退出 halo 账户,登录到 root 账户 + +> 如果当前就是 root 账户,请略过此步骤。 + +```bash +exit +``` + +2. 下载 Halo 官方的 halo.service 模板 + +```bash +wget https://dl.halo.run/config/halo.service -O /etc/systemd/system/halo.service +``` + +3. 修改 halo.service + +```bash +vim /etc/systemd/system/halo.service +``` + +4. 修改配置 + +- **YOUR_JAR_PATH**:Halo 运行包的绝对路径,例如 `/home/halo/app/halo.jar`,注意:此路径不支持 `~` 符号。 +- **USER**:运行 Halo 的系统用户,如果有按照上方教程创建新的用户来运行 Halo,修改为你创建的用户名称即可。反之请删除 `User=USER`。 + +```ini +[Unit] +Description=Halo Service +Documentation=https://halo.run +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=USER +ExecStart=/usr/bin/java -server -Xms256m -Xmx256m -jar YOUR_JAR_PATH +ExecStop=/bin/kill -s QUIT $MAINPID +Restart=always +StandOutput=syslog + +StandError=inherit + +[Install] +WantedBy=multi-user.target +``` + +:::tip +请确保 `/usr/bin/java` 是正确无误的。建议将 `ExecStart` 中的命令复制出来运行一下,保证命令有效。 +::: + +5. 重新加载 systemd + +```bash +systemctl daemon-reload +``` + +6. 运行服务 + +```bash +systemctl start halo +``` + +7. 在系统启动时启动服务 + +```bash +systemctl enable halo +``` + +您可以查看服务日志检查启动状态 + +```bash +journalctl -n 20 -u halo +``` + +## 反向代理 + +你可以在下面的反向代理软件中任选一项,我们假设你已经安装好了其中一项,并对其基本操作有一定了解。如果你对 Nginx 不熟悉,我们推荐使用 [OneinStack](./other/oneinstack) 来管理 Nginx。 + +### Nginx + +```nginx +upstream halo { + server 127.0.0.1:8090; +} +server { + listen 80; + listen [::]:80; + server_name www.yourdomain.com; + client_max_body_size 1024m; + location / { + proxy_pass http://halo; + proxy_set_header HOST $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} +``` + +注意:Nginx 默认的 `client_max_body_size` 配置大小为 1m,可能会导致你在 Halo 后台上传文件被 Nginx 限制,所以此示例配置文件加上了 `client_max_body_size 1024m;` 这行配置。当然,1024m 可根据你的需要自行修改。 + +### Caddy 1.x + +```txt +https://www.yourdomain.com { + gzip + tls your@email.com + proxy / localhost:8090 { + transparent + } +} +``` + +### Caddy 2.x + +```txt +www.yourdomain.com + +encode gzip + +reverse_proxy 127.0.0.1:8090 +``` + +以上配置都可以在 找到。 diff --git a/versioned_docs/version-1.5.0/getting-started/install/other/bt-panel.md b/versioned_docs/version-1.5.0/getting-started/install/other/bt-panel.md new file mode 100644 index 0000000..a403fda --- /dev/null +++ b/versioned_docs/version-1.5.0/getting-started/install/other/bt-panel.md @@ -0,0 +1,149 @@ +--- +title: 与宝塔面板配合使用 +description: 与宝塔面板配合使用的指南 +--- + +## 声明 + +1. 本组织与宝塔面板官方无任何合作和利益关系。 +2. 您在使用期间如果有宝塔面板所带来的问题,均与我们无关。 +3. 开始之前,我们默认认为您已经安装好了宝塔面板,以及熟悉宝塔面板的使用。 +4. 建议使用宝塔面板最新版本,截止到撰写指南的时刻,宝塔面板的版本为 `7.4.7`。 + +## Halo 部署 + +参见 [在 Linux 环境部署](/getting-started/install/linux) + +:::info +`「作为服务运行」` 部分可以不进行操作,只需测试启动无误即可,后面将使用宝塔面板软件包中的 `Supervisor` 托管 Halo 进程。 +::: + +## 宝塔面板所需软件包下载 + +需要在宝塔面板的软件商店安装的软件包有: + +1. Nginx +2. Supervisor + +## 使用 Supervisor 托管 Halo 进程 + +打开 Supervisor 管理器的设置,点击 `添加守护进程` 按钮。 + +需要填写的表单信息如下: + +- **名称**:随意 +- **启动用户**:如果您按照 [在 Linux 环境部署](/getting-started/install/linux) 创建了用于运行 Halo 的用户,则选择您创建的用户即可。否则选择默认的 root。 +- **运行目录**:运行包的存放目录,按照实际情况填写,需要保证你所选的目录包含运行包。 +- **启动命令**:`java -server -Xms256m -Xmx256m -jar halo.jar` + +填写完成之后点击 `确定` 按钮即可。 + +## 添加站点并配置 Nginx + +1. 点击左侧的 `网站` 菜单项,点击 `添加站点` 按钮。 + +需要填写的表单信息如下: + +- **域名**:填写您已经解析到当前服务器公网 IP 的域名。 +- **PHP版本**:纯静态 + +填写完成之后点击 `提交` 按钮即可。 + +2. 设置 SSL + +:::info +在配置反向代理之前,我们推荐先设置好 SSL 证书。 +::: + +- 可选择 `宝塔 SSL` 或者 `Let's Encrypt` 进行证书申请。 +- 需要开启右上角的 `强制 HTTPS`。 + +3. 修改配置文件 + +在根节点添加: + +```nginx +upstream halo { + server 127.0.0.1:8090; +} +``` + +> 其中的 8090 为 Halo 的运行端口,请按需修改。 + +在 server 节点添加: + +```nginx +location / { + proxy_pass http://halo; + proxy_set_header HOST $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +} +``` + +修改 server 节点中的 `location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$` 节点: + +```nginx +location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ +{ + proxy_pass http://halo; + expires 30d; + error_log /dev/null; + access_log off; +} +``` + +修改 server 节点中的 `location ~ .*\.(js|css)?$` 节点: + +```nginx +location ~ .*\.(js|css)?$ +{ + proxy_pass http://halo; + expires 12h; + error_log /dev/null; + access_log off; +} +``` + +完整配置文件示例(仅包含关键部分): + +```nginx +upstream halo { + server 127.0.0.1:8090; +} +server +{ + ... + + location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ + { + proxy_pass http://halo; + expires 30d; + error_log /dev/null; + access_log off; + } + + location ~ .*\.(js|css)?$ + { + proxy_pass http://halo; + expires 12h; + error_log /dev/null; + access_log off; + } + + location / { + proxy_pass http://halo; + proxy_set_header HOST $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + ... +} +``` + +随后点击保存即可。如果配置不生效,请重载 Nginx 或者 重启 Nginx。 + +最后,访问域名即可进行 Halo 的初始化。 diff --git a/versioned_docs/version-1.5.0/getting-started/install/other/docker-mysql.md b/versioned_docs/version-1.5.0/getting-started/install/other/docker-mysql.md new file mode 100644 index 0000000..6b10e13 --- /dev/null +++ b/versioned_docs/version-1.5.0/getting-started/install/other/docker-mysql.md @@ -0,0 +1,202 @@ +--- +title: 使用 Docker 部署 Halo 和 MySQL +description: Halo 与 MySQL 在 Docker 中的部署方案 +--- + +### 简介 + +该章节我们将分三种情况为您说明该如何同时使用 Docker + MySQL 来部署 Halo + +前提条件: 我们默认您的机器上已经安装好 `Docker` + +- 如果你想完全通过 `Docker` 运行 `MySQL` 和 `Halo` 请参考小节《统一使用 Docker 安装》 +- 如果你已经有 `Docker`部署的 `MySQL`,想安装 `Halo` 请参考小节《MySQL 部署在 Docker 如何使用 Docker 安装 Halo》 +- 如果你已有 `MySQL` 但部署在宿主机,想通过 `Docker` 安装 `Halo` 请参考小节《MySQL 在宿主机如何通过 Docker 安装 Halo》 + +### 统一使用 Docker 安装 + +如果你的机器上没有现成的 `MySQL` 可供使用,那么您可以选择使用 `Docker` 来运行 `MySQL` 和 `Halo` + +1. 创建 Docker 自定义桥接网络 + +```shell +docker network create halo-net +``` + +:::tip +如果你之前有 Docker 使用经验,你可能已经习惯了使用 `--link` 参数来使容器互联。 + +但随着 Docker 网络的完善,强烈建议大家将容器加入自定义的 Docker 网络来连接多个容器,而不是使用 --link 参数。 +Docker 官方文档中称:该--link 标志是 Docker 的遗留功能。它可能最终会被删除。除非您确定需要继续使用它,否则我们建议您使用用户定义的网络来促进两个容器之间的通信,而不是使用 --link。 +::: + +2. 拉取 `MySQL` 镜像 + +```shell +docker pull mysql:8.0.27 +``` + +3. 创建 `MySQL` 数据目录 + +```shell +mkdir -p ~/.halo/mysql +``` + +3. 启动 `MySQL` 实例 + +```shell +docker run --name some-mysql -v ~/.halo/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw --net halo-net --restart=unless-stopped -d mysql:8.0.27 +``` + +注意: 请将 `my-secret-pw` 修改为自己需要的密码后再执行,密码尽量包含小写字母、大写字母、数字和特殊字符且长度超过 8 位。 + +:::tip 释意 + +`-e MYSQL_ROOT_PASSWORD=my-secret-pw`: 指定`MySQL`的登录密码为 `my-secret-pw` + +`-v ~/.halo/mysql:/var/lib/mysql` 命令: 将宿主机的目录 `~/.halo/mysql` 挂载到容器内部的目录 `/var/lib/mysql`,默认情况下 MySQL 将向 `~/.halo/mysql` 写入其数据文件。 + +`--net halo-net`: 将该容器加入到 `halo-net` 网络,连接到 `halo-net` 网络的任何其他容器都可以访问 `some-mysql`容器上的所有端口。 + +::: + +4. 进入 MySQL 容器中登录 MySQL 并创建 Halo 需要的数据库 + +- (1) some-mysql 为 MySQL 实例的容器名称 + + ```shell + docker exec -it some-mysql /bin/bash + ``` + +- (2) 登录 MySQL + + ```shell + mysql -u root -p + ``` + +- (3) 输入 MySQL 数据库密码 + +- (4) 创建数据库 + + ```shell + create database halodb character set utf8mb4 collate utf8mb4_bin; + ``` + +- (5) 使用 `exit`退出`MySQL` 并退出容器 + +5. 创建 `Halo` 工作目录 + +```bash +mkdir ~/.halo && cd ~/.halo +``` + +6. 下载示例配置文件到[工作目录](https://docs.halo.run/getting-started/prepare#工作目录) + +```shell +wget https://dl.halo.run/config/application-template.yaml -O ./application.yaml +``` + +7. 编辑配置文件,配置数据库,其他配置请参考[参考配置](https://docs.halo.run/getting-started/config) + +```shell +vim application.yaml +``` + +你需要做如下几个步骤: + +- 注释 H2 database configuration.部分 +- 启用 MySQL database configuration.部分 +- 修改 datasource 下的 url 中的 ip 地址部分为容器名称并修改密码为您设定的 `MySQL` 密码 + +修改后的内容如下: + +```yaml +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://some-mysql:3306/halodb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: root + password: my-secret-pw +``` + +8. 创建 Halo 容器实例 + +```shell +docker run -it -d --name halo -p 8090:8090 -v ~/.halo:/root/.halo --net halo-net --restart=unless-stopped halohub/halo:1.5.0 +``` + +9. 打开 `http://ip:端口号` 即可看到安装引导界面。 + +### MySQL 部署在 Docker 如何使用 Docker 安装 Halo + +如果您已有 `Docker` 部署的 `MySQL` 实例,那么为了保证 `Halo` 和 `MySQL` 两个容器的网络可以互通,和上文同样的思路可以创建一个网络让 `MySQL` 和 `Halo` 都加入进来。 + +1. 使用 `docker ps` 来查看的你 `MySQL` 容器实例的名称或 `container id`, 例如 `some-mysql` +2. 创建一个桥接网络,让 `MySQL` 加入,首先使用 `docker network ls` 来查看一下都有哪些网络名称,起一个不会冲突的网络名称,例如 `halo-net`,其次让已经存在的 MySQL 容器实例加入到该网络中 + +```shell +docker network connect halo-net some-mysql +``` + +3. 同之前一样创建 `Halo` 工作目录 + +```bash +mkdir ~/.halo && cd ~/.halo +``` + +4. 下载示例配置文件到[工作目录](https://docs.halo.run/getting-started/prepare#工作目录) + +```shell +wget https://dl.halo.run/config/application-template.yaml -O ./application.yaml +``` + +5. 编辑配置文件,修改 `MySQL` 的数据库连接和密码 + +```shell +vim application.yaml +``` + +你需要做如下几个步骤: + +- 注释 H2 database configuration.部分 +- 启用 MySQL database configuration.部分 +- 修改 datasource 下的 url 中的 ip 地址部分为容器名称并修改密码为您设定的 `MySQL` 密码 + +修改后的内容如下: + +```yaml +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://some-mysql:3306/halodb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: root + password: my-secret-pw +``` + +6. 创建 `Halo` 容器实例,并使用 `--net` 指定网络为刚才创建的`halo-net` + +```shell +docker run -it -d --name halo -p 8090:8090 -v ~/.halo:/root/.halo --net halo-net --restart=unless-stopped halohub/halo:1.5.0 +``` + +### MySQL 在宿主机如何通过 Docker 安装 Halo + +如果你已有 `MySQL` 但安装在宿主机,你想使用 `Docker` 安装 `Halo` 那么此时为了保证 `MySQL` 和 `Halo` 能网络互通,可以使用 `host` 网络模式即 `--net host`。 + +1. 创建 `Halo` 的工作目录 + +```shell +mkdir ~/.halo && cd ~/.halo +``` + +2. 拉取配置 + +```shell +wget https://dl.halo.run/config/application-template.yaml -O ./application.yaml +``` + +3. 使用 `Docker` 启动 `Halo` 实例并指定网络模式为 `host` + +```shell +docker run -it -d --name halo -p 8090:8090 -v ~/.halo:/root/.halo --net host --restart=unless-stopped halohub/halo:1.5.0 +``` diff --git a/versioned_docs/version-1.5.0/getting-started/install/other/oneinstack.md b/versioned_docs/version-1.5.0/getting-started/install/other/oneinstack.md new file mode 100644 index 0000000..353adfe --- /dev/null +++ b/versioned_docs/version-1.5.0/getting-started/install/other/oneinstack.md @@ -0,0 +1,274 @@ +--- +title: 与 OneinStack 配合使用 +description: 使用 OneinStack 的 vhost 脚本创建 Halo 站点的 Nginx 配置文件 +--- + +## Halo 部署 + +参见 [在 Linux 环境部署](/getting-started/install/linux) + +:::info +`「反向代理」` 部分不进行操作,保证 Halo 服务运行无误即可。 +::: + +## 通过 OneinStack 安装 Nginx + +点击下方链接进入 OneinStack 官网,仅选择 `安装 Nginx`,其他的都可以取消选择。 + + + +最后点击 `复制安装命令` 到服务器执行即可。如果你仅安装 Nginx,你的链接应该是这样: + +```bash +wget -c http://mirrors.linuxeye.com/oneinstack-full.tar.gz && tar xzf oneinstack-full.tar.gz && ./oneinstack/install.sh --nginx_option 1 +``` + +:::info +这一步会经过编译安装,可能会导致安装时间很漫长,这主要取决于你服务器的性能。 +::: + +出现下面的信息即代表安装成功: + +```bash +Nginx installed successfully! +Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service. +Redirecting to /bin/systemctl start nginx.service +####################Congratulations######################## +Total OneinStack Install Time: 5 minutes + +Nginx install dir: /usr/local/nginx +``` + +## 创建 vhost + +> 即创建一个站点,你可以通过这样的方式在你的服务器创建无限个站点。接下来的目的就是创建一个站点,并反向代理到 Halo。这一步在此教程使用 `demo.halo.run` 这个域名做演示,实际情况请修改此域名。 + +1. 进入到 oneinstack 目录,执行 vhost 创建命令 + +```bash +cd oneinstack +``` + +```bash +sh vhost.sh +``` + +2. 按照提示选择或输入相关信息 + +```bash +What Are You Doing? + 1. Use HTTP Only + 2. Use your own SSL Certificate and Key + 3. Use Let's Encrypt to Create SSL Certificate and Key + q. Exit +Please input the correct option: +``` + +这一步是选择证书配置方式,如果你有自己的证书,输入 2 即可。如果需要使用 `Let's Encrypt` 申请证书,选择 3 即可。 + +```bash +Please input domain(example: www.example.com): +``` + +输入自己的域名即可,前提是已经提前解析好了域名。 + +```bash +Please input the directory for the domain:demo.halo.run : +(Default directory: /data/wwwroot/demo.halo.run): +``` + +提示输入站点根目录,因为我们是使用 Nginx 的反向代理,所以这个目录是没有必要配置的,我们直接使用默认的即可(直接回车)。 + +```bash +Do you want to add more domain name? [y/n]: +``` + +是否需要添加其他域名,按照需要选择即可,如果不需要,输入 n 并回车确认。 + +```bash +Do you want to add hotlink protection? [y/n]: +``` + +是否需要做防盗链处理,按照需要选择即可。 + +```bash +Allow Rewrite rule? [y/n]: +``` + +路径重写配置,我们不需要,选择 n 回车确定即可。 + +```bash +Allow Nginx/Tengine/OpenResty access_log? [y/n]: +``` + +Nginx 的请求日志,建议选择 y。 + +这样就完成了 vhost 站点的创建,最终会输出站点的相关信息: + +```bash +Your domain: demo.halo.run +Virtualhost conf: /usr/local/nginx/conf/vhost/demo.halo.run.conf +Directory of: /data/wwwroot/demo.halo.run +``` + +Nginx 的配置文件即 `/usr/local/nginx/conf/vhost/demo.halo.run.conf`。 + +## 修改 Nginx 配置文件 + +上方创建 vhost 的过程并没有创建反向代理的配置,所以需要我们自己修改一下配置文件。 + +1. 使用你熟悉的工具打开配置文件,此教程使用 vim。 + +```bash +vim /usr/local/nginx/conf/vhost/demo.halo.run.conf +``` + +2. 删除一些不必要的配置 + +```nginx +location ~ [^/]\.php(/|$) { + #fastcgi_pass remote_php_ip:9000; + fastcgi_pass unix:/dev/shm/php-cgi.sock; + fastcgi_index index.php; + include fastcgi.conf; +} +``` + +此段配置是针对 php 应用的,所以可以删掉。 + +3. 添加 `upstream` 配置 + +在 `server` 的同级节点添加如下配置: + +```nginx +upstream halo { + server 127.0.0.1:8090; +} +``` + +4. 在 `server` 节点添加如下配置 + +```nginx +location / { + proxy_set_header HOST $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://halo; +} +``` + +5. 修改 `location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$` 节点 + +```nginx +location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ { + proxy_pass http://halo; + expires 30d; + access_log off; +} +``` + +6. 修改 `location ~ .*\.(js|css)?$` 节点 + +```nginx +location ~ .*\.(js|css)?$ { + proxy_pass http://halo; + expires 7d; + access_log off; +} +``` + +如果不按照第 5,6 步操作,请求一些图片或者样式文件不会经过 Halo,所以请不要忽略此配置。 + +7. 添加 acme.sh 续签验证路由 + +OneinStack 使用的 acme.sh 管理证书,如果你在创建 vhost 的时候选择了使用 `Let's Encrypt` 申请证书,那么 OneinStack 会在系统内添加一个定时任务去自动续签证书,acme.sh 默认验证站点所有权的方式为在站点根目录生成一个文件(.well-known)来做验证,由于配置了反向代理,所以在验证的时候是无法直接访问到站点目录下的 .well-known 文件夹下的验证文件的。需要添加如下配置: + +```nginx +location ^~ /.well-known/acme-challenge/ { + default_type "text/plain"; + allow all; + root /data/wwwroot/demo.halo.run/; +} +``` + +至此,配置修改完毕,保存即可。最终你的配置文件可能如下面配置一样: + +```nginx +upstream halo { + server 127.0.0.1:8090; +} +server { + listen 80; + listen [::]:80; + listen 443 ssl http2; + listen [::]:443 ssl http2; + ssl_certificate /usr/local/nginx/conf/ssl/demo.halo.run.crt; + ssl_certificate_key /usr/local/nginx/conf/ssl/demo.halo.run.key; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; + ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; + ssl_prefer_server_ciphers on; + ssl_session_timeout 10m; + ssl_session_cache builtin:1000 shared:SSL:10m; + ssl_buffer_size 1400; + add_header Strict-Transport-Security max-age=15768000; + ssl_stapling on; + ssl_stapling_verify on; + server_name demo.halo.run; + access_log /data/wwwlogs/demo.halo.run_nginx.log combined; + index index.html index.htm index.php; + root /data/wwwroot/demo.halo.run; + if ($ssl_protocol = "") { return 301 https://$host$request_uri; } + include /usr/local/nginx/conf/rewrite/none.conf; + #error_page 404 /404.html; + #error_page 502 /502.html; + location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ { + proxy_pass http://halo; + expires 30d; + access_log off; + } + location ~ .*\.(js|css)?$ { + proxy_pass http://halo; + expires 7d; + access_log off; + } + location ~ /(\.user\.ini|\.ht|\.git|\.svn|\.project|LICENSE|README\.md) { + deny all; + } + location / { + proxy_set_header HOST $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://halo; + } + location ^~ /.well-known/acme-challenge/ { + default_type "text/plain"; + allow all; + root /data/wwwroot/demo.halo.run/; + } +} +``` + +## 重载 Nginx 使配置生效 + +验证 nginx 配置 + +```bash +nginx -t +``` + +如果输出如下提示则代表配置有效: + +```bash +nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok +nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful +``` + +重载 Nginx 配置: + +```bash +nginx -s reload +``` + +至此,整个教程完毕,现在你可以访问域名检查是否已经配置成功。 diff --git a/versioned_docs/version-1.5.0/getting-started/install/other/tencent-cloudbase.md b/versioned_docs/version-1.5.0/getting-started/install/other/tencent-cloudbase.md new file mode 100644 index 0000000..2353e0e --- /dev/null +++ b/versioned_docs/version-1.5.0/getting-started/install/other/tencent-cloudbase.md @@ -0,0 +1,79 @@ +--- +title: 腾讯云 CloudBase +description: 使用腾讯云 CloudBase 一键部署 +--- + +## 声明 + +1. 本组织与腾讯云官方无任何合作和利益关系。 +2. 您在使用期间如果有腾讯云所带来的问题,均与我们无关。 +3. 开始之前,我们默认认为您已经了解过 [腾讯云云开发](https://cloud.tencent.com/product/tcb)。 + +## 注意事项 + +1. 系统使用内置的 H2 Database,暂不支持使用 MySQL。 +1. 工作目录保存在腾讯云提供的 CFS 上,在使用此方式创建应用的时候会要求创建 CFS。 +1. 目前使用该方式部署,不支持修改 [配置文件](/getting-started/config)。 +1. 费用相关请参考 + +## 进入云开发页面 + +点击下方按钮即可进入到腾讯云云开发 CloudBase 创建应用界面,此按钮可在云开发页面自动选择 Halo 的配置模板。 + +[![CloudBase](https://main.qcloudimg.com/raw/67f5a389f1ac6f3b4d04c7256438e44f.svg)](https://console.cloud.tencent.com/tcb/env/index?action=CreateAndDeployCloudBaseProject&appUrl=https%3A%2F%2Fgithub.com%2Fhalo-dev%2Ftencent-cloudbase-halo&branch=master) + +## 配置并部署 + +**填写环境名称**,这里建议把 `开启免费额度` 打开,然后点击下一步。 + +![tencent-cloudbase-1.png](/img/tencent-cloudbase/tencent-cloudbase-1.png) + +**应用配置**,需要注意:这里需要开通 CFS,用于存储 Halo 的工作目录。 + +![tencent-cloudbase-2.png](/img/tencent-cloudbase/tencent-cloudbase-2.png) + +**提交授权**,点击 `授权并开通`。 + +![tencent-cloudbase-3.png](/img/tencent-cloudbase/tencent-cloudbase-3.png) + +**创建环境中**,完成之后,点击 `环境` 下面的卡片,即可进入详细配置页面。 + +![tencent-cloudbase-4.png](/img/tencent-cloudbase/tencent-cloudbase-4.png) + +**构建应用中**,这里可能会等待较长时间。 + +![tencent-cloudbase-5.png](/img/tencent-cloudbase/tencent-cloudbase-5.png) + +**构建应用完成**,需要注意的是,构建完成之后可能还需要等待一小段时间才能正常访问,这个期间是在等待 Halo 应用启动完毕。点击 `访问` 按钮即可进入 Halo 初始化页面。 + +![tencent-cloudbase-6.png](/img/tencent-cloudbase/tencent-cloudbase-6.png) + +**Halo 初始化页面**: + +![tencent-cloudbase-7.png](/img/tencent-cloudbase/tencent-cloudbase-7.png) + +**CFS 管理页面**:[https://console.cloud.tencent.com/cfs/fs](https://console.cloud.tencent.com/cfs/fs) + +![tencent-cloudbase-8.png](/img/tencent-cloudbase/tencent-cloudbase-8.png) + +## 更新 Halo + +> 当 Halo 有新版本更新的时候,你可以采用下面的方式进行版本升级。 + +### 删除旧版本 + +前往 [云托管](https://console.cloud.tencent.com/tcb/service) 页面,点击服务名称进入版本列表,然后删除当前使用的版本。 + +![tencent-cloudbase-10.png](/img/tencent-cloudbase/tencent-cloudbase-10.png) + +### 重新部署 + +回到 [我的应用](https://console.cloud.tencent.com/tcb/apps/index) 页面,点击 `部署` 按钮并确定。 + +![tencent-cloudbase-11.png](/img/tencent-cloudbase/tencent-cloudbase-11.png) + +## 相关链接 + +- [Halo 配置文件仓库](https://github.com/halo-dev/tencent-cloudbase-halo) +- [腾讯云 CloudBase](https://console.cloud.tencent.com/tcb/env/index) +- [云开发使用指南](https://cloud.tencent.com/document/product/876) diff --git a/versioned_docs/version-1.5.0/getting-started/install/third-party.md b/versioned_docs/version-1.5.0/getting-started/install/third-party.md new file mode 100644 index 0000000..1a3f65c --- /dev/null +++ b/versioned_docs/version-1.5.0/getting-started/install/third-party.md @@ -0,0 +1,15 @@ +--- +title: 三方指南 +--- + +:::tip +此类指南或教程均来自第三方或者网友提供,我们不能一直保证其具有有效性,请参考的时候仔细甄别。另外,如果您有写类似的教程,也可以[联系我们](mailto:hi@halo.run)放在下方。 +::: + +- [Halo 博客安装教程,一款优秀的 Java 开源博客系统](https://www.cnblogs.com/bronya0/p/14198512.html) +- [如何用服务器优雅地搭建一个博客—Hello,Halo!](https://www.bilibili.com/video/BV1JN411Q7Na/) +- [Halo+云服务器+Nginx反向代理实现个人博客搭建](https://yusart.xyz/archives/halo%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA) +- [如何在 Windows 下搭建 Halo](https://ykanade.cn/2021/08/11/HaloInWindows/) +- [Halo 博客建站记录](https://catchersun.cn/archives/halo%E5%8D%9A%E5%AE%A2%E5%BB%BA%E7%AB%99%E8%AE%B0%E5%BD%95) +- [Windows下搭建halo并配置nginx与SSL](https://blog.shikangsi.cn/archives/halo-windows) +- [Linux环境下如何分布式部署Halo](https://luoxx.top/archives/halo-distributed-deploy) diff --git a/versioned_docs/version-1.5.0/getting-started/install/windows.md b/versioned_docs/version-1.5.0/getting-started/install/windows.md new file mode 100644 index 0000000..72cab80 --- /dev/null +++ b/versioned_docs/version-1.5.0/getting-started/install/windows.md @@ -0,0 +1,79 @@ +--- +title: 在 Windows 服务器上部署 +--- + +:::info +在继续操作之前,我们推荐您先阅读[《写在前面》](/getting-started/prepare),这可以快速帮助你了解 Halo。 +::: + +## 系统要求 + +目前运行 Halo 的最低依赖要求为 JRE 11,而 Java9 之后将不再提供 32 位系统的环境,因此请确保您的服务器属于 64 位 CPU。 + +## 依赖检查 + +如下将介绍在 Windows 下安装 OpenJRE 11 的方式。如果您的服务器已经安装过 OpenJRE 11,则可以直接跳过本节。 + +1. 使用浏览器前往 下载 OpenJRE 11 的可执行程序。 + +2. 下载时会提示登录“红帽”,任意注册账号登录即可。登录完成之后会自动下载 JRE。 + +3. 双击 MSI 安装程序,安装 JRE 至服务器,注意到安装程序第三步时,勾选 `JAVA_HOME Variable`,其余直接 `next` 即可 + +![img2.png](/img/img2.png) + +4. 安装完成之后, 使用 Win+R 打开运行窗口并输入 `CMD` 后,回车打开 CMD 窗口。之后键入 `java -version`。显示如下所示内容即代表安装成功。 + +```bash +openjdk version "11.0.10" 2021-01-19 LTS +OpenJDK Runtime Environment 18.9 (build 11.0.10+9-LTS) +OpenJDK 64-Bit Server VM 18.9 (build 11.0.10+9-LTS, mixed mode) +``` + +![1615618595.jpg](/img/1615618595.jpg) + +## 安装 Halo + +1. 下载运行包 + +使用浏览器前往 下载最新版本 Halo 运行包,**并保存至桌面**。修改 Jar 包名称为 `halo.jar` + +:::info +如果下载速度不理想,可以[在这里](/getting-started/downloads)选择其他下载地址。 +::: + +2. 下载示例配置文件 + +使用浏览器前往 下载示例配置文件,**并保存至桌面**。修改示例配置文件名字为 `application.yaml`。 + +:::tip +IE 浏览器下,配置文件可能会以文本的形式直接打开,因此推荐使用其他浏览器或下载器下载。 +::: + +3. 使用记事本编辑配置文件,配置数据库或者端口等,如需配置请参考[参考配置](/getting-started/config) + +4. 测试运行 Halo + +使用 Win+R 打开运行窗口并输入 `CMD` 后,回车打开 CMD 窗口,并键入如下命令。 + +```bash +cd Desktop && java -jar halo.jar +``` + +5. 如看到类似以下日志输出,则代表启动成功。 + +```bash +run.halo.app.listener.StartedListener : Halo started at http://127.0.0.1:8090 +run.halo.app.listener.StartedListener : Halo admin started at http://127.0.0.1:8090/admin +run.halo.app.listener.StartedListener : Halo has started successfully! +``` + +浏览器打开 `http://ip:端口号` 即可看到安装引导界面。 + +:::info +如测试启动正常,请继续看`作为服务运行`部分,第 4 ~ 5 步仅仅作为测试。当你关闭 CMD 窗口之后,服务会停止。你可使用 CTRL+C 停止运行测试进程。 +::: + +:::tip +如果需要配置域名访问,建议先配置好反向代理以及域名解析再进行初始化。如果通过 `http://ip:端口号` 的形式无法访问,请到服务器厂商后台将运行的端口号添加到安全组,如果服务器使用了 Linux 面板,请检查此 Linux 面板是否有还有安全组配置,需要同样将端口号添加到安全组。 +::: diff --git a/versioned_docs/version-1.5.0/getting-started/prepare.md b/versioned_docs/version-1.5.0/getting-started/prepare.md new file mode 100644 index 0000000..96a3354 --- /dev/null +++ b/versioned_docs/version-1.5.0/getting-started/prepare.md @@ -0,0 +1,99 @@ +--- +title: 写在前面 +description: 在开始前,您需要了解的事项 +--- + +## 环境要求 + +这里将讲述运行 Halo 所要求的一些软硬件的配置,我们建议您在运行或者部署之前先浏览一遍此页面。 + +### 硬件配置 + +:::tip +如果您要使用服务器进行部署 Halo,您需要注意的是,Halo 目前不支持市面上的云虚拟主机,请使用云服务器或者 VPS。 +::: + +#### CPU + +无特别要求。目前我们的 [Docker 镜像](https://hub.docker.com/r/halohub/halo) 也已经支持多平台。 + +#### 内存 + +为了获得更好的体验,我们建议至少配置 1G 的 RAM。 + +#### 磁盘 + +无特别要求,理论上如果不大量在服务器上传附件,Halo 对磁盘的容量要求并不是很高。但我们推荐最好使用 SSD 硬盘的服务器,能更快的运行 Halo。 + +#### 网络 + +Halo 目前必须在外网畅通的情况下使用,否则会导致页面异常。 + +### 软件环境 + +Halo 理论上可以运行在任何支持 Java 的平台。 + +#### JRE(Java Runtime Environment) + +必须在运行环境安装好 JRE,这是运行 Halo 所要求的的最低软件环境要求。如果您使用 Docker 运行 Halo,可忽略此要求。 + +:::info +从 1.4.3 起,版本要求为 11 以上的版本。1.4.3 以下需要 1.8 以上的版本。 +::: + +#### MySQL(可选) + +这并不是 Halo 必须依赖的,Halo 默认使用自带的 `H2 Database`,无需单独安装。如果 `H2 Database` 不能满足你的要求,您需要在系统内安装并运行好 MySQL。 + +具体要求: + +1. 版本:5.7 + +2. 字符集(Character Set):`utf8mb4` +3. 排序规则(Collate):`utf8mb4_bin` +4. 存储引擎:`InnoDB` + +综上,建议创建数据库采用下面的命令: + +```bash +create database halodb character set utf8mb4 collate utf8mb4_bin; +``` + +#### Web 服务器(可选) + +如果您部署在生产环境,那么你很可能需要进行域名绑定,这时候我们推荐使用诸如 [Nginx](http://nginx.org/)、[Caddy](https://caddyserver.com/) 之类的 Web 服务器进行反向代理。 + +#### Wget(可选) + +后续的文档中,我们会使用 wget 为例,用于下载所需要的文件,所以请确保服务器已经安装好了这个软件包。当然,下载文件不限制工具,如果你对其他工具熟悉,可以忽略。 + +#### Vim(可选) + +后续的文档中,我们会使用 vim 为例,用于修改一些必要的配置文件,所以同样请确保服务器已经安装了这个软件包。当前,修改文档也不限制工具,如果你对其他编辑软件熟悉,也可以忽略。 + +## 浏览器支持 + +1. 用户前台:视主题所支持的情况而定,由于目前的评论模块使用了 [Vuejs](https://cn.vuejs.org/v2/guide/installation.html#%E5%85%BC%E5%AE%B9%E6%80%A7) 开发,所以在 [Vuejs](https://cn.vuejs.org/v2/guide/installation.html#%E5%85%BC%E5%AE%B9%E6%80%A7) 不支持的某些浏览器中无法正常显示评论区域。 +2. 管理后台:支持目前常见的现代浏览器,具体视 [Vuejs](https://cn.vuejs.org/v2/guide/installation.html#%E5%85%BC%E5%AE%B9%E6%80%A7) 框架的支持情况而定。 + +## 名词解释 + +这里将列出后续文档中一些和 Halo 相关的名词含义。 + +### ~(符号) + +代表当前系统下的 [用户目录](https://zh.wikipedia.org/wiki/%E5%AE%B6%E7%9B%AE%E5%BD%95)。 + +### 运行包 + +指 Halo 构建所产生的 Jar 包,后缀为 `.jar`。可能与其他网站应用有所区别的是,Halo 仅仅只有这一个文件。而且所有数据统一保存在下面所说的 `工作目录`。 + +### 工作目录 + +指 Halo 所依赖的工作目录,在 Halo 运行的时候会在系统当前用户目录下产生一个 `.halo` 的文件夹,绝对路径为 `~/.halo`。由于这个工作目录是固定的,所以上面所说的 `运行包`不限制所存放的位置,里面通常包含下列目录或文件: + +1. `db`:存放 H2 Database 的物理文件,如果您使用 MySQL 数据库,那么不会存在这个目录。 +2. `templates/themes`:里面包含用户所下载的主题。 +3. `static`:相当于网站的根目录。 +4. `logs`:运行日志目录。 +5. `upload`:附件目录。 +6. `application.yaml`:配置文件。 diff --git a/versioned_docs/version-1.5.0/getting-started/upgrade.md b/versioned_docs/version-1.5.0/getting-started/upgrade.md new file mode 100644 index 0000000..3c7bbde --- /dev/null +++ b/versioned_docs/version-1.5.0/getting-started/upgrade.md @@ -0,0 +1,107 @@ +--- +title: 版本升级 +description: 版本升级指南 +--- + +当前最新版本为:1.5.0。在更新之前建议先查阅变更日志: + +## Linux + +:::info +我们假设你存放运行包的路径为 `~/app`,运行包的文件名为 `halo.jar`,且使用了 systemd 进行进程管理,如有不同,下列命令请按需修改。 +::: + +1. 停止正在运行的服务 + +```bash +service halo stop +``` + +2. 备份数据以及旧的运行包(**重要**) + +```bash +cp -r ~/.halo ~/.halo.archive +``` + +```bash +mv ~/app/halo.jar ~/app/halo.jar.archive +``` + +> 需要注意的是,`.halo.archive` 和 `halo.jar.archive` 文件名不一定要根据此文档命名,这里仅仅是个示例。 + +3. 下载最新版本的运行包 + +```bash +cd ~/app && wget https://dl.halo.run/release/halo-1.5.0.jar -O halo.jar +``` + +:::info +如果下载速度不理想,可以[在这里](/getting-started/downloads)选择其他下载地址。 +::: + +4. 启动测试 + +```bash +java -jar halo.jar +``` + +:::info +如测试启动正常,请继续下一步。使用 CTRL+C 停止运行测试进程。 +::: + +5. 重启服务 + +```bash +service halo start +``` + +## Docker + +:::info +我们假设您的容器是按照 [使用 Docker 部署 Halo](/getting-started/install/docker) 中的命令构建的。如有不同,请根据实际情况修改。 +::: + +1. 停止并删除当前运行中的容器 + +```bash +docker stop halo +``` + +```bash +docker rm -f halo +``` + +:::info +你的容器名称不一定为 `halo`,在执行前可以先执行 `docker ps -a` 查看一下。 +::: + +2. 备份数据(重要) + +```bash +cp -r ~/.halo ~/.halo.archive +``` + +> 需要注意的是,`.halo.archive` 文件名不一定要根据此文档命名,这里仅仅是个示例。 + +1. 拉取最新的 Halo 镜像 + +```bash +docker pull halohub/halo:1.5.0 +``` + +:::info +查看最新版本镜像: ,我们推荐使用具体版本号的镜像,但也提供了 `latest` 标签的镜像,它始终是最新的。 +::: + +4. 创建容器 + +```bash +docker run -it -d --name halo -p 8090:8090 -v ~/.halo:/root/.halo --restart=unless-stopped halohub/halo:1.5.0 +``` + +- **-it:** 开启输入功能并连接伪终端 +- **-d:** 后台运行容器 +- **--name:** 为容器指定一个名称 +- **-p:** 端口映射,格式为 `主机(宿主)端口:容器端口` ,可在 `application.yaml` 配置。 +- **-v:** 工作目录映射。形式为:-v 宿主机路径:/root/.halo,后者不能修改。 +- **--restart:** 建议设置为 `unless-stopped`,在 Docker 启动的时候自动启动 Halo 容器。 diff --git a/versioned_docs/version-1.5.0/intro.md b/versioned_docs/version-1.5.0/intro.md new file mode 100644 index 0000000..fa586f6 --- /dev/null +++ b/versioned_docs/version-1.5.0/intro.md @@ -0,0 +1,92 @@ +--- +id: intro +sidebar_label: 简介 +title: '' +sidebar_position: 1 +hide_title: true +slug: / +--- + +

+ + Halo logo + +

+ +

Halo [ˈheɪloʊ],一款现代化的开源博客/CMS系统,值得一试。

+ +

+GitHub release +GitHub All Releases +Docker pulls +GitHub last commit +GitHub Workflow Status +
+官网 +文档 +社区 +Gitee +Telegram 频道 +

+ +------------------------------ + +## 快速开始 + +### Fat Jar + +下载最新的 Halo 运行包: + +```bash +curl -L https://github.com/halo-dev/halo/releases/download/v1.4.17/halo-1.4.17.jar --output halo.jar +``` + +其他地址: + +```bash +java -jar halo.jar +``` + +### Docker + +```bash +docker run -it -d --name halo -p 8090:8090 -v ~/.halo:/root/.halo --restart=always halohub/halo +``` + +详细部署文档请查阅: + +## 在线体验 + +- 环境地址: +- 后台地址: +- 用户名:demo +- 密码:P@ssw0rd123.. +- 使用前请阅读: + +## 生态 + +| 项目 | 状态 | 描述 | +| ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | +| [halo-admin](https://github.com/halo-dev/halo-admin) | npm release | Web 管理端 UI,已内置在主应用 | +| [js-sdk](https://github.com/halo-dev/js-sdk) | npm release | JavaScript SDK | +| [halo-comment](https://github.com/halo-dev/halo-comment) | npm release | 独立评论组件,可以非常方便的集成到主题中 | +| [halo-comment-normal](https://github.com/halo-dev/halo-comment-normal) | npm release | 另外一款评论组件 | +| [halo-mobile-app](https://github.com/halo-dev/halo-mobile-app) | 已停止维护 | 移动端管理 APP | +| [tencent-cloudbase-halo](https://github.com/halo-dev/tencent-cloudbase-halo) | 无 | 腾讯云 CloudBase 一键部署配置 | +| [halo-theme-\*](https://github.com/topics/halo-theme) | 无 | GitHub 上开源的 Halo 主题集合 | + +## 许可证 + +[![license](https://img.shields.io/github/license/halo-dev/halo.svg?style=flat-square)](https://github.com/halo-dev/halo/blob/master/LICENSE) + +Halo 使用 GPL-v3.0 协议开源,请遵守开源协议。 + +## 贡献 + +参考 [CONTRIBUTING](https://github.com/halo-dev/halo/blob/master/CONTRIBUTING.md)。 + + + +## 状态 + +![Repobeats analytics](https://repobeats.axiom.co/api/embed/ad008b2151c22e7cf734d2688befaa795d593b95.svg "Repobeats analytics image") diff --git a/versioned_docs/version-1.5.0/user-guide/backup-migration.md b/versioned_docs/version-1.5.0/user-guide/backup-migration.md new file mode 100644 index 0000000..8106c48 --- /dev/null +++ b/versioned_docs/version-1.5.0/user-guide/backup-migration.md @@ -0,0 +1,38 @@ +--- +title: 备份迁移 +description: 关于备份和迁移的最佳实践 +--- + +:::info +在开始之前,我们推荐你先阅读 [《写在前面》](/getting-started/prepare) 的名词解释部分。 +::: + +## 备份 + +### 数据备份 + +目前 Halo 在后台的小工具中提供了数据导出的功能,此功能的作用为导出数据库的所有数据,格式为 `JSON`。通常可以作为切换数据库类型的时候使用。需要注意的是,此备份仅仅为备份数据,不包含其他诸如主题、附件等资料。如下图: + +![halo-data-export.png](/img/halo-data-export.png) + +点击右下角的备份按钮即可导出所有数据,之后点击备份文件的标题即可下载。 + +### 整站备份 + +通过 [《写在前面》](/getting-started/prepare) 的名词解释部分我们可以知道,Halo 的所有数据都是存放在当前用户目录的工作目录(.halo)下的(使用 MySQL 数据库除外,你还需要导出 MySQL 数据)。**所以我们备份整站的数据仅需备份这个目录即可**,不管你使用何种方式。不过,为了操作方便,我们也在后台的小工具中提供了备份整站数据的功能,和上面所说的数据备份一致,点击备份按钮即可打包工作目录文件夹。如下图: + +![halo-workspace-export.png](/img/halo-workspace-export.png) + +## 迁移 + +### 导入数据 + +此功能为导入上面所说的数据备份产生的数据文件(JSON 格式),并非整站备份的工作目录文件。需要注意的是,此功能仅在站点初始化的时候支持。如下图: + +![halo-data-import.png](/img/halo-data-import.png) + +上传文件之后,点击导入即可。 + +### 整站迁移 + +此操作通常用于迁移服务器,基于上面 **整站备份** 所说,Halo 的所有数据都是存放于当前用户目录的工作目录(.halo)下的。当然,这仅限于使用 **H2 Database** 的情况下,如果你使用的 MySQL,那么还需要手动导出 MySQL 数据。所以,我们迁移服务器仅仅需要需要将工作目录的备份文件上传到新服务器的用户目录下解压,然后按照 [《安装指南》](/getting-started/install/linux) 重新安装即可。MySQL 用户还需要做的就是手动导出 MySQL 数据,并在新服务器上导入。 diff --git a/versioned_docs/version-1.5.0/user-guide/config.md b/versioned_docs/version-1.5.0/user-guide/config.md new file mode 100644 index 0000000..f348c04 --- /dev/null +++ b/versioned_docs/version-1.5.0/user-guide/config.md @@ -0,0 +1,172 @@ +--- +title: 功能配置 +description: 系统功能的相关配置说明 +--- + +## 修改博客地址 + +很多人在部署完成之后都会惊奇的发现,博客前台居然没有样式?究其原因就是无法获取到静态资源,那么为什么获取不到呢?那就是你的博客地址没有设置正确。 + +第一步: + +登录到后台之后,进入 博客设置 -> 常规设置。 + +第二步: + +修改你的博客地址: + +- 如果你没有进行域名解析或者没有配置反向代理,那么博客地址一般为 `http://ip:端口`。 + +- 如果你进行了域名解析且配置了反向代理,但是没有配置 SSL 证书,那么博客地址一般为 `http://域名`。 + +- 如果你进行了域名解析、配置了反向代理、也配置了 SSL 证书,那么博客地址一般为 `https://域名`。 + +说了这么多,就是希望你别把 `http://` 或者 `https://` 搞混了。 + +另外,需要注意的是,地址尾部不需要 `/`。 + +## 注册 Gravatar + +很多人反映评论中自己的头像不显示,其实评论部分调用的头像并不是在个人资料中设置的那个,而是 `Gravatar`。所以这一点怪我,从 Halo 发布至今我都没有说明这个事情。 + +至于什么是 `Gravatar`,引用 `Gravatar` 官方的说明:`全球公认的头像`。这可不是在吹牛,因为只要接入了 Gravatar 的网站,你在网站上使用在 Gravatar 注册的邮箱之后,都会显示你设置的头像,而不需要额外设置。比较出名的网站有 `Github`,`Gitlab`,`V2ex` 等。所以点击 注册一个吧。 + +## 设置用户头像/Logo/Favicon + +### 头像 + +第一步: + +登录到后台之后,进入 用户 -> 个人资料。 + +第二步: + +点击左侧头像即可选择本地附件中的图片。另外,也支持设置为 `Gravatar` 头像。点击 `使用 Gravatar` 即可。当然,前提是你注册了 `Gravatar` 才行。 + +### Logo/Favicon + +第一步: + +登录到后台之后,进入 系统 -> 博客设置。 + +第二步: + +在常规设置中找到 Logo/Favicon 输入框,点击输入框后面的按钮即可调用附件库选择图片。 + +## SEO 设置 + +先说说程序本身对 SEO 做的优化: + +- 支持 sitemap 站点地图:可访问 /sitemap.xml 或 /sitemap.html +- 全站绝对路径 +- 页面静态化 +- 支持伪静态(1.3.0+) + +需要自己设置的有: + +第一步: + +登录到后台之后,进入 系统 -> 博客设置 -> SEO 设置。 + +第二步: + +填写你需要的一些关键词和博客描述,这部分可能需要你了解一定的 SEO 优化技巧。 + +> 另外,文章和自定义页面都可以自行设置关键词和描述(文章设置的高级设置中)。当然,你也可以不设置,系统为自动取你设置的文章标签为关键词,描述会自动取文章的部分内容。 + +Q&A + +Q:我刚发布的文章,为啥百度搜索不到呢? + +A:这是一件非常正常的事情,建议注册百度的站长工具,手动或者自动提交链接。 + +## SMTP 服务 + +SMTP 服务,简称发信服务,顾名思义就是用来发邮件的。那么,为什么要设置这个呢?有两个用途: + +1. 评论通知,收到评论之后发信通知博主,评论者被回复之后发信通知被评论者。 + +2. 找回密码。 + +第一步: + +登录到后台之后,进入 系统 -> 博客设置 -> SMTP 设置。 + +第二步: + +勾选 `是否启用`,并配置相关信息: + +1. SMTP 地址,到邮箱服务商查询。举两个例子:QQ 邮箱(smtp.qq.com),163 邮箱(smtp.163.com) + +2. 发送协议:一般不修改,具体可到邮箱服务商查询。 + +3. SSL 端口:一般不修改,具体可到邮箱服务商查询。 + +4. 邮箱账号:你的账号。 + +5. 邮箱密码:你的密码。 + +6. 发件人:随意。 + +第三步: + +点击发送测试,填上你可以接收到的邮箱,并填写测试内容,最后点击发送。不出意外就可以发送成功,失败了就检查下配置吧。 + +Q&A: + +Q:发送失败 + +A:先检查配置是否有误,如不知道哪里错误,请查看日志。点击左上角 `Halo Dashboard`即可进入开发者选项,点击 `实时日志` 即可看到最近的日志。 + +Q:日志说我授权出错,但是我密码明明是对的啊! + +A:部分邮箱所谓的密码并不是你的登陆密码,需要去邮箱服务商生成 `授权码`。这个才是你需要填写的。 + +## 设置统计代码 + +第一步: + +登录到后台之后,进入 系统 -> 博客设置 -> 其他设置。 + +第二步: + +将 `网站统计平台` 提供给你的一串代码填写到 `统计代码` 即可。 + +Q&A: + +Q:什么是网站统计平台? + +A:诸如百度统计,CNZZ,Google Analytics。可以非常详细的统计你网站的访问情况。 + +## 找一个合适自己的主题 + +当你搭建好博客之后,如果你不喜欢默认的主题,那么你可以去 [https://halo.run/themes/](https://halo.run/themes/) 选择你喜欢的主题。在这里主要讲一些注意事项。 + +### 如何安装? + +登录到后台之后,进入 外观 -> 主题,在后下角有一个按钮,点击之后选择 `安装主题`。系统提供的两种安装方式。 + +1. 第一种,远程拉取。你只需要把主题的 Github 地址复制进去点击下载即可。 +2. 第二种,本地上传,你需要将主题安装包(releases)下载到本地,然后选择 `本地上传` 点击选择你下载好的主题即可。 + +> 一般情况下,推荐第二种,第一种一般安装的为开发版本,建议谨慎使用。 + +### 设置主题 + +> 可能有一部分人不太清楚,一般情况下,每个主题都是有单独的设置选项的。 + +点击主题下方的设置按钮即可进入主题设置。另外主题设置还有一种预览模式,可以实时查看主题设置变更后的效果。当你设置当前激活主题的时候,在页面右下角有一个 `预览模式` 的按钮,点击即可进入预览模式。 + +## 设置备案信息 + +由于后台博客设置并没有提供设置备案信息的选项,所以有很多人不知道该怎么添加备案信息,这里简单说明一下。 + +第一步: + +登录到后台之后,进入 系统 -> 博客设置 -> 常规设置。 + +这时候你应该可以看到一个`页脚信息`的选项,而且有提示 `支持 HTML 格式的文本`。所以我们把备案信息放在这里即可。 + +第二步: + +将整理好的备案信息填写到页脚信息即可,如果没记错的话,公安联网备案是有提供`网站公安机关备案号`和`备案编号HTML代码`的。 diff --git a/versioned_docs/version-1.5.0/user-guide/faq.md b/versioned_docs/version-1.5.0/user-guide/faq.md new file mode 100644 index 0000000..ecc2fc3 --- /dev/null +++ b/versioned_docs/version-1.5.0/user-guide/faq.md @@ -0,0 +1,113 @@ +--- +title: 常见问题 +description: 使用上的常见问题 +--- + +### Halo 是什么? + +**Halo** [ˈheɪloʊ],一款现代化的开源博客/CMS系统,值得一试。 + +### 没有提供 SQL 脚本,是否需要手动建表? + +得益于我们使用的 ORM 框架,Halo 在首次启动的时候会自动根据实体类创建表结构,无需通过 SQL 脚本自行创建,也不会提供所谓的 SQL 脚本。所以仅需配置好数据库连接地址和用户名密码即可。注意,H2 无需手动创建数据库,MySQL 需要。 + +详情可见:[配置参考](/getting-started/config#数据库) + +### 为什么百度无法搜索到我的站点? + +这是一个暂时无法解答的问题。所涉及到的问题过多,受影响因素可能有域名、服务器 IP 位置、建站时间、网站结构、内容等等。建议了解一下 SEO 相关知识对网站进行优化,目前我们在程序方面做的优化有: + +- 支持 sitemap 站点地图:可访问 `/sitemap.xml` 或 `/sitemap.html` +- 全站绝对路径 +- 页面静态化 +- 支持自定义文章关键字和描述 +- 支持自定义站点关键字以及站点描述 + +### 忘记了管理员密码,如何重置? + +目前在登录页面含有隐藏的 `找回密码` 链接,点击即可进入找回密码页面,具体可参考以下步骤: + +1. 在登录页面按下键盘快捷键(Windows / Linux:`Shift + Alt + H`,macOS:`Shift + Command + H`)即可显示 `找回密码` 链接。 +2. 按照表单提示输入用户名和邮箱,点击 `获取` 按钮即可发送带有验证码的邮件。 +3. 按照表单填写验证码和新密码,点击重置密码即可。 + +> 需要注意的是,第 2 步中的获取验证码需要事先配置了 SMTP 发信设置,否则无法发送验证码。但你可以登录服务器查看 Halo 运行日志,搜索 `Got reset password code` 关键字即可获取到验证码。 + +### 附件上传提示 `413 Request Entity Too Large` 如何解决? + +这可能是由于 Nginx 的上传大小限制所导致的。可以在 Nginx 的配置文件下的 server 节点加入 `client_max_body_size 1024m;` 即可解决,如果 1024m 还不够,请自行断定,详细配置参考如下: + +```nginx {4} +server { + listen 80; + server_name localhost; + client_max_body_size 1024m; +} +``` + +### 开启了两步验证但丢失了验证设备或 APP,如何取消两步验证? + +可以参考 [忘记了管理员密码,如何重置?](./user-guide/faq.md#忘记了管理员密码如何重置) 重置密码,完成重置密码之后即可清除两步验证。 + +### 网站加载速度慢,是什么问题导致的? + +导致网站加载速度慢的原因有很多,建议打开浏览器的 Developer Tools 查看具体是哪个请求时间过长,然后进行针对性的优化。这里提供一些可能的原因: + +1. 服务器带宽过小,很多厂商提供的最低带宽一般是 1M。 +2. 服务器地区过远,这个需要自行取舍。 +3. 网站上的图片过多或者体积过大,可以尝试压缩图片,或者参考 [优雅的让 Halo 支持 webp 图片输出](https://halo.run/archives/halo-and-webp.html) 的教程配置一个 Webp 图片的服务。 +4. 部分主题的静态资源可能是由公共 CDN 提供的,当这个 CDN 不稳定的时候可能会导致加载变慢。 + +一些提升网站加载速度的建议: + +1. 尽量不要选择 1M 带宽的服务器,可以根据自己的预算适当提升带宽。一般 3M 以上即可。 +2. 尽量购买网络质量较好的服务器,或者较近区域的服务器。 +3. 如果一定需要放大量的图片,建议先无损压缩一下再使用。 +4. 如上所说,可以自行搭建一个 Webp 图片转换的服务,参考 [优雅的让 Halo 支持 webp 图片输出](https://halo.run/archives/halo-and-webp.html)。 +5. 如果网站的静态资源加载慢是由三方 CDN 导致的,可以自行修改主题。 +6. 可以使用全站 CDN 加速的方案。 + +### 如何在一台服务器上部署多个站点? + +参考 [写在前面/工作目录](../getting-started/prepare.md#工作目录) 我们可以知道,工作目录对于 Halo 主程序来说是固定的。如果我们需要部署多个站点,我们提供以下两种方式以供参考: + +1. 创建多个 Linux 账户,并在每个账户上运行一个独立的 Halo。因为工作目录是基于账户的,所以每个账户都有自己的工作目录。但是有一点需要注意,就是需要修改每一个 Halo 的运行端口,参考:[配置参考/端口](../getting-started/config#%E7%AB%AF%E5%8F%A3) +2. 使用 Docker 创建多个容器,因为使用 Docker 可以将内部的工作目录映射到宿主机的任何目录,可以参考以下创建容器的方式: + + ```bash + # 第一个 Halo 容器 + docker run -it -d --name halo1 -p 8090:8090 -v ~/.halo.1:/root/.halo --restart=unless-stopped halohub/halo:latest + + # 第二个 Halo 容器 + docker run -it -d --name halo2 -p 8091:8090 -v ~/.halo.2:/root/.halo --restart=unless-stopped halohub/halo:latest + ``` + +更多 Docker 相关的教程请参考:[使用 Docker 部署 Halo](../getting-started/install/docker.md) + +### 如何查看运行日志? + +1. 登录到服务器,查看工作目录下的 `logs/spring.log`。 +2. 在 Halo 后台进入开发者选项(点击左上角 `Halo Dashboard` 10 次),选择 `实时日志` 界面。 + +### SMTP 发信设置配置正确,但是发信失败,如何解决? + +可能是部分厂商不允许使用密码作为客户端登录的凭证,一般会提供类似 `授权码` 的设置,将 `授权码` 当做密码在 Halo 后台设置即可。如还有其他类型的原因,欢迎向我们提交 issue:[https://github.com/halo-dev/halo/issues/new/choose](https://github.com/halo-dev/halo/issues/new/choose) + +### 网站配置了全站 CDN 导致后台部分功能异常,如何解决? + +可能是 CDN 厂商默认关闭了 `参数跟随` 选项,导致部分接口参数没有正确添加到回源请求上。你可以在 CDN 控制台查找此选项并打开。或者设置路径过滤,过滤掉 `/api/admin`,让接口请求始终访问回源地址。 + +### 前台样式丢失,如何解决? + +前台样式不正常或者丢失有很多种问题的可能,最快捷定位问题的方式就是打开浏览器控制台查看具体请求的错误,以下列出了部分导致出现该问题的常见原因: + +1. 后台设置的 `博客地址` 与实际访问地址不一致。也可能是开启了 https 之后,无法正常加载 http 资源,将 `博客地址` 改为 https 协议即可。 +2. Nginx 配置了静态资源缓存,但没有设置 `proxy_pass`,参考如下: + + ```nginx + location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ { + proxy_pass http://halo; + expires 30d; + access_log off; + } + ``` diff --git a/versioned_docs/version-1.5.0/user-guide/markdown.md b/versioned_docs/version-1.5.0/user-guide/markdown.md new file mode 100644 index 0000000..ac9ff8f --- /dev/null +++ b/versioned_docs/version-1.5.0/user-guide/markdown.md @@ -0,0 +1,211 @@ +--- +title: Markdown 语法 +description: Halo 编辑器中所支持的 Markdown 语法说明 +--- + +## 基础语法 + +Halo 使用的 `Markdown` 解析器为 [flexmark-java](https://github.com/vsch/flexmark-java),基于 [CommonMark (spec 0.28)](https://spec.commonmark.org/0.28/) 标准开发,语法参考:[https://spec.commonmark.org/0.28/](https://spec.commonmark.org/0.28/)。 + +## 代码块 + +````markdown +```language +代码块 +``` +```` + +其中,language 为必填,如果不填写,很可能主题的代码高亮插件无法识别代码的语言,导致样式异常。举几个例子: + +````markdown +```java +public static void main(String[] args){ +    System.out.println("Hello World!"); +} +``` +```` + +````markdown +```javascript +console.log("Hello World!") +``` +```` + +## TOC + +在文章的最前面加上 `[TOC]` 即可。 + +## 自动链接 + +支持自动将一个链接解析为可点击的格式,如下: + +```markdown +https://halo.run +``` + +将被解析为: + +```html +https://halo.run +``` + +## Emoji + +支持将 Emoji 的文字形式转化为图片形式,如下: + +```markdown +:100: +``` + +将被解析为: + +```html +💯 +``` + +更多 Emoji 表情可访问:[https://emoji.svend.cc](https://emoji.svend.cc) + +## 数学公式 + +行内公式: + +```markdown +$a \ne 0$ +``` + +段落公式: + +```markdown +$$ +x = {-b \pm \sqrt{b^2-4ac} \over 2a}. +$$ +``` + +> 需要注意的是,并不是每一款主题都支持显示数学公式,你可以先到你使用的主题设置中查看是否支持,如不支持,请看下面的解决方案。 + +首先,登陆到后台,进入 `系统 -> 博客设置 -> 其他设置`。将下面的代码复制到 `自定义内容页面 head`。 + +```html + + +``` + +## 图表 + +饼图: + +````markdown +```mermaid +pie title NETFLIX + "Time spent looking for movie" : 90 + "Time spent watching it" : 10 +``` +```` + +更多用法查看:[https://mermaidjs.github.io/#/](https://mermaidjs.github.io/#/) + +> 需要注意的是,并不是每一款主题都支持显示图表,你可以先到你使用的主题设置中查看是否支持,如不支持,请看下面的解决方案。 + +首先,登陆到后台,进入 `系统 -> 博客设置 -> 其他设置`。将下面的代码复制到 `自定义内容页面 head`。 + +```html + +``` + +## 短连接 + +:::tip +这属于实验性特性,也许我们将在未来的版本移除这个特性。 +::: + +Halo 内置一些短连接以更好地支持一些 HTML 语法,但是编辑器目前并不能解析,只能发布之后才可以看到效果,如下: + +### 网易云音乐 + +#### 语法 + +```markdown +[music:id] +``` + +#### 示例 + +```markdown +[music:32507038] +``` + +#### 解析结果 + +```html + +``` + +### 哔哩哔哩动画 + +#### 语法 + +```markdown +[bilibili:aid,width,height] +``` + +#### 示例 + +```markdown +[bilibili:65898131,256,256] +``` + +#### 解析结果 + +```html + +``` + +## 脚注 + +语法: + +```markdown +[^脚注名] +[^脚注名]: 脚注内容 +``` + +示例: + +```markdown +驿外[^1]断桥边,寂寞开无主。已是黄昏独自愁,更着风和雨 +[^1]: 驿(yì)外:指荒僻、冷清之地。驿,驿站。 +``` + +解析结果: + +```html +

驿外[1]断桥边,寂寞开无主。已是黄昏独自愁,更着风和雨

+
+
+
    +
  1. 驿(yì)外:指荒僻、冷清之地。驿,驿站。 ↩︎

    +
  2. +
+
+``` diff --git a/versioned_sidebars/version-1.5.0-sidebars.json b/versioned_sidebars/version-1.5.0-sidebars.json new file mode 100644 index 0000000..ee0bca7 --- /dev/null +++ b/versioned_sidebars/version-1.5.0-sidebars.json @@ -0,0 +1,90 @@ +{ + "tutorialSidebar": [ + "intro", + { + "type": "category", + "label": "入门", + "collapsed": false, + "items": [ + "getting-started/prepare", + { + "type": "category", + "label": "安装指南", + "items": [ + "getting-started/install/linux", + "getting-started/install/docker", + { + "type": "category", + "label": "其他指南", + "items": [ + "getting-started/install/other/bt-panel", + "getting-started/install/other/oneinstack", + "getting-started/install/other/tencent-cloudbase", + "getting-started/install/other/docker-mysql", + { + "type": "link", + "label": "腾讯云开发者实验室", + "href": "https://cloud.tencent.com/developer/labs/lab/10523" + } + ] + }, + "getting-started/install/third-party" + ] + }, + "getting-started/config", + "getting-started/upgrade", + "getting-started/downloads" + ] + }, + { + "type": "category", + "label": "用户指南", + "items": [ + "user-guide/backup-migration", + "user-guide/markdown", + "user-guide/faq" + ] + }, + { + "type": "category", + "label": "开发者指南", + "items": [ + { + "type": "category", + "label": "系统开发", + "items": [ + "developer-guide/core/structure", + "developer-guide/core/prepare", + "developer-guide/core/code-style" + ] + }, + { + "type": "category", + "label": "主题开发", + "items": [ + "developer-guide/theme/prepare", + "developer-guide/theme/config-files", + "developer-guide/theme/global-variable", + "developer-guide/theme/public-template-tag", + "developer-guide/theme/page-variable", + "developer-guide/theme/template-tag" + ] + }, + { + "type": "link", + "label": "REST API", + "href": "https://api.halo.run" + } + ] + }, + { + "type": "category", + "label": "参与贡献", + "items": [ + "contribution/issue", + "contribution/pr" + ] + }, + "about" + ] +} diff --git a/versions.json b/versions.json index d845fa7..c73715f 100644 --- a/versions.json +++ b/versions.json @@ -1,4 +1,5 @@ [ + "1.5.0", "1.4.17", "1.4.16", "1.4.15"