如何从零开发一个jpress的友情链接插件(附插件下载地址)
在制作本站的主题模板时,需要开发一款jpress的友情链接插件,通过查找相关的文章以及自己的摸索最终终于完成了插件的制作,写出教程供有需求的朋友借鉴和交流。
插件是jpress的一种扩展功能实现,比如友情链接插件,我们可以在代码中编写自己的控制器(Controller)、model、拦截器(Interceptor)、Handler...等等。听起来很复杂,其实操作起来很简单。
目录文件结构及说明
我们先来看一下开发完成后整体的目录及文件的结构:
japress-addon
├─ pom.xml
│
└─src
└─main
├─java
│ └─com
│ └─miigua
│ └─japress
│ │ Codegen.java
│ │ JaPressAddon.java
│ │
│ ├─controller
│ │ _FriendsLinksController.java
│ │
│ ├─directive
│ │ FriendsLinksDirective.java
│ │
│ ├─model
│ │ │ JapressFriendsLink.java
│ │ │
│ │ └─base
│ │ BaseJapressFriendsLink.java
│ │
│ └─service
│ │ JapressFriendsLinkService.java
│ │
│ └─provider
│ JapressFriendsLinkServiceProvider.java
│
├─resources
│ │ addon.txt
│ │ readme.txt
│ │
│ └─sql
│ install.sql
│ uninstall.sql
│
└─webapp
└─views
japress_friendslinks_edit.html
japress_friendslinks_list.html
接下来对关键目录及文件进行说明解释。
addon.txt
此插件的描述信息,相关信息会显示在后台插件列表处。主要有插件id,名字(title),版本(version、versionCode),描述(description)、作者(author)、网址(authorWebsite)等信息,注意放到resources路径下,内容一看就懂,不需要过多解释,直接上内容:
id=com.miigua.japress
title=JaPress模板配套插件
description=含友情链接功能;
author=miigua.com
authorWebsite=http://www.miigua.com
version=1.0.0
versionCode=1
readme.txt
在后台插件列表处点击插件的详情会显示此文件的内容,通常写插件的调用方法,详细使用说明等信息。支持部分Markdown语法。
install.sql、uninstall.sql
建表和删表的sql语句,用来在插件安装和卸载的时候创建和删除表。放到resources路径或者resources子目录下,内容如下:
install.sql
DROP TABLE IF EXISTS `japress_friends_link`;
CREATE TABLE `japress_friends_link` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`title` varchar(1000) CHARACTER SET utf8mb4 DEFAULT '' COMMENT '标题',
`url` varchar(500) CHARACTER SET utf8mb4 DEFAULT '' COMMENT '链接',
`target` varchar(30) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '打开方式',
`type` varchar(10) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '类型',
`icon` varchar(500) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '图标',
`created` datetime DEFAULT NULL COMMENT '创建日期',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='友情链接';
uninstall.sql
DROP TABLE IF EXISTS `japress_friends_link`
JaPressAddon.java
作用是用于监听本插件在安装、卸载、启用和停止的动作,然后做对于的改变。 代码如下:
public class JaPressAddon extends AddonBase {
@Override
public void onInstall(AddonInfo addonInfo) {
System.out.println("JaPressAddon onInstall");
try {
AddonUtil.execSqlFile(addonInfo, "sql/install.sql");
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void onUninstall(AddonInfo addonInfo) {
System.out.println("JaPressAddon onUninstall");
try {
AddonUtil.execSqlFile(addonInfo, "sql/uninstall.sql");
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void onStart(AddonInfo addonInfo) {
System.out.println("JaPressAddon onStart");
MenuGroup orderMenuGroup = new MenuGroup();
orderMenuGroup.setId("japressFriendsLinks");
orderMenuGroup.setText("JaPress友情链接");
orderMenuGroup.setIcon("<i class=\"fas fa-link\"></i>");
MenuManager.me().getModuleMenus().add(orderMenuGroup);
}
@Override
public void onStop(AddonInfo addonInfo) {
System.out.println("JaPressAddon onStop");
MenuManager.me().deleteMenuGroup("japressFriendsLinks");
}
}
onInstall() : 用于在此插件被安装的时候执行,在这个插件的生命周期中只会执行一次,就是被安装的时候,在这个方法中,我使用 sql/install.sql 的sql语句创建了相关数据表。
onUninstall():用于在此插件被卸载的时候执行,这这个操作的往往是和 onInstall() 相反的,当 onInstall() 创建表或其他资源,我们应该在 onUninstall() 删除表或删除 onInstall() 创建的资源,在这里我执行相关的sql文件删除表。
onStart():此方法是网站管理员在后台进行启动的时候触发的,当次插件被启动之后,以后只要重启容器比如tomcat、undertow等都会执行此方法。在此方法中,我创建了一个后台菜单,注意这个groupid要跟 _FriendsLinksController 中生成的菜单的groupid一样,菜单会出现在后台左侧。
onStop():和 onStart() 相反,我在此方法中移除了菜单。
pom.xml
这个文件不需要过多解释,文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jpress-addons</artifactId>
<groupId>io.jpress</groupId>
<version>4.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.miigua.japress</groupId>
<artifactId>japress-addon</artifactId>
<dependencies>
<dependency>
<groupId>io.jpress</groupId>
<artifactId>jpress-core</artifactId>
</dependency>
<dependency>
<groupId>io.jpress</groupId>
<artifactId>codegen</artifactId>
<version>4.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/webapp</directory>
<includes>
<include>**/*.*</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
需要注意如下问题:
- 标明打包时将resources及webapp文件夹打包进来,否则在安装时就会出问题。
- 依赖jpress-core,如果你需要其他模块,则在dependencies中继续引入。
Codegen.java
此文件主要的功能是生成操作数据库相关的model和service等,内容如下:
package com.miigua.japress;
import io.jpress.codegen.AddonGenerator;
public class Codegen {
private static String dbUrl = "jdbc:mysql://ip/数据库名";
private static String dbUser = "root";
private static String dbPassword = "数据库密码";
private static String addonName = "friendsLinks";
private static String dbTables = "japress_friends_link";
private static String modelPackage = "com.miigua.japress.model";
private static String servicePackage = "com.miigua.japress.service";
public static void main(String[] args) {
AddonGenerator moduleGenerator = new AddonGenerator(addonName, dbUrl, dbUser, dbPassword, dbTables, modelPackage, servicePackage);
// moduleGenerator.setGenUI(true);
moduleGenerator.gen();
}
}
dbTables为要生成的数据表名,注意不要写错。运行这个java文件就会得到相应的model、service,复制到我们的项目中使用即可。
_FriendsLinksController.java
后台相关操作Controller,这里主要处理友情链接列表展示、添加、删除等操作。
@RequestMapping(value = "/admin/japress/friendslinks", viewPath = "/views")
public class _FriendsLinksController extends AdminControllerBase {
@Inject
private JapressFriendsLinkService service;
@AdminMenu(text = "友情链接列表", groupId = "japressFriendsLinks")
public void list() {
Page<JapressFriendsLink> entries=service.paginate(getPagePara(), 10);
setAttr("page", entries);
render("japress_friendslinks_list.html");
}
public void edit() {
int entryId = getParaToInt(0, 0);
JapressFriendsLink entry = entryId > 0 ? service.findById(entryId) : null;
setAttr("japressFriendsLink", entry);
render("japress_friendslinks_edit.html");
}
public void doSave() {
JapressFriendsLink entry = getModel(JapressFriendsLink.class,"japressFriendsLink");
service.saveOrUpdate(entry);
renderJson(Ret.ok().set("id", entry.getId()));
}
public void doDel() {
Long id = getIdPara();
render(service.deleteById(id) ? Ret.ok() : Ret.fail());
}
@EmptyValidate(@Form(name = "ids"))
public void doDelByIds() {
Set<String> idsSet = getParaSet("ids");
if (service.batchDeleteByIds(idsSet.toArray())){
for (String id : idsSet){
service.deleteById(Long.valueOf(id));
}
}
renderOkJson();
}
}
这个controller中我们要操作数据库所以注入了JapressFriendsLinkService 来操作相关的表。
list() 对应到后台菜单,点击后台菜单时会执行这里,我们分页展示了友情链接列表,然后渲染html页面。注意在jpress中,方法名即为url,也就是访问list这个方法的url就是 /admin/japress/friendslinks/list
FriendsLinksDirective.java
此文件定义前台html页面调用的标签即渲染的内容,文件内容如下:
@JFinalDirective("japressFriendsLinks")
public class FriendsLinksDirective extends JbootDirectiveBase {
@Inject
private JapressFriendsLinkService friendsLinkService;
@Override
public void onRender(Env env, Scope scope, Writer writer) {
String orderBy = getPara("orderBy", scope, "id desc");
String type = getPara("type", scope, "");
int count = getParaToInt("count", scope, Integer.MAX_VALUE);
Columns columns = new Columns();
if(StringUtil.isNotEmpty(type)) {
columns.eq("type", type);
}
List<JapressFriendsLink> friendsLinks = friendsLinkService.findListByColumns(columns, orderBy, count);
scope.setLocal("japressFriendsLinks", friendsLinks);
renderBody(env, scope, writer);
}
@Override
public boolean hasEnd() {
return true;
}
}
需要注意的是:
- @JFinalDirective注解中的内容即为前台html调用的标签(不是后台管理端的html),例如我上面的写法调用时应为:#japressFriendsLinks()
- hasEnd() 方法用来标识此标签是否需要 #end 标签,注意默认 hasEnd 返回的是false。
- scope.setLocal("japressFriendsLinks", friendsLinks) 中 japressFriendsLinks 为前台html变量名,我们可以使用 Enjoy 模板引擎的相关标签对它进行读取。
标签调用方法如下:
#japressFriendsLinks()
#for(f : japressFriendsLinks)
<li>
<a href="#(f.url)" target="#(f.target)">
<img class="frinds-links-icon" src="#(f.icon ?? '/templates/japress/img/chrome.png')">
#(f.title)
</a>
</li>
#end
#end
model和service
此类文件为Codegen运行后生成的文件,主要用来操作数据库,不再过多赘述。
japress_friendslinks_list.html
插件后台列表页面,不再赘述,直接贴代码:
#@layout()
#define script()
<script>
function doDel(id) {
ajaxGet("/admin/japress/friendslinks/doDel/" + id);
}
</script>
#end
#define content()
<section class="content-header">
<div class="container-fluid">
<div class="row">
<div class="col-sm-6 mb-2">
<div class="row mb-2">
<div class="col-sm-12">
<h1>
JaPress友情链接
<small data-toggle="tooltip" title="" data-placement="right"
data-trigger="hover"><i class="nav-icon far fa-question-circle"></i></small>
<small> 首页 / JaPress友情链接 / 列表</small>
</h1>
</div>
</div>
</div>
<div class="col-sm-6">
<div class=" float-sm-right">
<a href="./edit" class="btn btn-primary ">
<i class="fas fa-plus"></i> 新建
</a>
</div>
</div>
</div>
</div><!-- /.container-fluid -->
</section>
<section class="content">
<div class="container-fluid">
<div class="card card-outline card-primary">
<!-- /.card-header -->
<div class="card-body p-0">
<table class="table table-striped">
<tbody>
<tr>
<th style="width: 10px">
<input class="tableCheckAll" id="checkall" type="checkbox"/>
</th>
<th>标题</th>
<th>链接</th>
<th>打开方式</th>
<th>类型</th>
<th>图标</th>
<th>创建时间</th>
</tr>
#for(entry : page.list)
<tr>
<td><input name="tableItem" type="checkbox" value="#(entry.id)" /></td>
<td width="200px">
#(entry.title ??)
<div class="jp-action-card">
<div class="jp-action-body">
<a href="/admin/japress/friendslinks/edit/#(entry.id)">编辑</a> |
<a href="javascript:;" class="red-action"
onclick="doDel('#(entry.id)')">删除</a>
</div>
</div>
</td>
<td>#(entry.url ??)</td>
<td>#(entry.target ??)</td>
<td>#(entry.type ??)</td>
<td>#(entry.icon ??)</td>
<td>#(entry.created ??)</td>
</tr>
#else
<tr class="nodata"><td colspan="999">暂无数据</td> </tr>
#end
</tbody>
</table>
</div>
<!-- /.card-body -->
<div class="card-footer">
<div class="row">
<div class="col-sm-6">
<div class="row">
<div class="form-group">
<div class="form-check">
<input class="form-check-input tableCheckAll" id="checkall" type="checkbox" >
<label class="form-check-label" for="checkall">全选</label>
</div>
</div>
<div class="form-group col-lg-3">
<select class="form-control" name="action">
<option value="">请选择...</option>
<option value="doDelByIds" open-type="del-confirm">
批量删除
</option>
</select>
</div>
<div class="form-group col-lg-3">
<button type="button" class="btn btn-outline-primary batchExec">批量操作</button>
</div>
</div>
</div>
<div class="col-sm-6">
#@_paginate()
</div>
</div>
</div>
</div>
<!-- /.card -->
</div>
</section>
#end
japress_friendslinks_edit.html
插件后台编辑页面,不再赘述,直接贴代码:
#@layout()
#define script()
<script>
function doSubmit() {
ajaxSubmit("#form", function (data) {
$("#entryId").attr("value", data.id);
toastr.success('内容保存成功。');
})
}
$("#submit").on("click", function () {
doSubmit();
})
</script>
#end
#define content()
<section class="content-header">
<div class="container-fluid">
<div class="row">
<div class="col-sm-6">
<div class="row mb-2">
<div class="col-sm-12">
<h1>
#(japressFriendsLink ? '编辑' : '新增')
<small data-toggle="tooltip" title="" data-placement="right"
data-trigger="hover"><i class="nav-icon far fa-question-circle"></i></small>
<small> 首页 / JaPress友情链接 / #(japressFriendsLink ? '编辑' : '新增')</small>
</h1>
</div>
</div>
</div>
</div>
</div><!-- /.container-fluid -->
</section>
<section class="content">
<div class="container-fluid">
<div class="card card-outline card-primary">
<form class=" form-setting" autocomplete="off"
action="/admin/japress/friendslinks/doSave"
method="POST" id="form">
<div class="card-body">
<input type="hidden" id="entryId" name="japressFriendsLink.id" value="#(japressFriendsLink.id ??)">
<div class="form-group row">
<label class="col-sm-2 col-form-label">标题</label>
<div class="col-sm-6">
<input type="input" name="japressFriendsLink.title" class="form-control" value="#(japressFriendsLink.title ??)"
placeholder="">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">链接</label>
<div class="col-sm-6">
<input type="input" name="japressFriendsLink.url" class="form-control" value="#(japressFriendsLink.url ??)"
placeholder="">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">打开方式</label>
<div class="col-sm-6">
<input type="input" name="japressFriendsLink.target" class="form-control" value="#(japressFriendsLink.target ?? '_blank')"
placeholder="">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">类型</label>
<div class="col-sm-6">
<input type="input" name="japressFriendsLink.type" class="form-control" value="#(japressFriendsLink.type ??)"
placeholder="">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">图标</label>
<div class="col-sm-6">
<input type="input" name="japressFriendsLink.icon" class="form-control" value="#(japressFriendsLink.icon ??)"
placeholder="">
</div>
</div>
</div>
<!-- /.card-body -->
<div class="card-footer">
<div class="offset-sm-2 col-sm-10 submit-block">
<div class="card-submit">
<button id="submit" type="button" class="btn btn-primary">提交</button>
</div>
</div>
</div>
<!-- /.card-footer -->
</form>
</div>
</div>
</section>
#end
插件安装及使用
打包安装
使用mvn clean package对插件进行打包,打包完毕后会在插件文件夹 target目录下生成jar包,将jar包拖入到jpress后台插件安装页面即可完成安装。
如何使用
在前台模板文件中使用如方法来调用:
#japressFriendsLinks(type='1',count=30,orderBy='id desc')
#for(f : japressFriendsLinks)
<li>
<a href="#(f.url)" target="#(f.target)">
<img class="frinds-links-icon" src="#(f.icon ?? '/templates/japress/img/chrome.png')">
#(f.title)
</a>
</li>
#end
#end
参数说明:
type:类型,在后台添加的时候可以设置自定义类型,针对不同页面显示不同的类型
count:数量,默认为Integer.MAX_VALUE
orderBy:排序,默认为id desc
插件下载地址
请在我的github japress-theme 处下载该友情链接插件,另外该链接也提供本站主题 japress的下载,JaPress 是一款高颜值、自适应而且 SEO 友好的免费主题(theme)模板,她具有如下特色:颜值高 支持友情链接 性能好 完美自适应多终端浏览器 功能丰富且支持后台自定义配置(自定义 logo、favicon 图标、banner、首页推荐图、默认缩略图、打赏图片、字体、主题颜色、代码块颜色、鼠标光标样式、导航图标等等)。
以上便是如何在jpress从零开发一个插件的全部内容,有疑问或者建议的小伙伴欢迎评论区留言交流,需要源代码的请留下邮箱,看到后发给你。
版权声明:
作者:Miigua
链接:https://www.miigua.com/article/10.html
来源:米瓜的博客
文章版权归作者所有,未经允许请勿转载。
全部评论