如何从零开发一个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>

 

需要注意如下问题:

  1. 标明打包时将resources及webapp文件夹打包进来,否则在安装时就会出问题。
  2. 依赖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;
        }
    }

 

需要注意的是:

  1. @JFinalDirective注解中的内容即为前台html调用的标签(不是后台管理端的html),例如我上面的写法调用时应为:#japressFriendsLinks()
  2. hasEnd() 方法用来标识此标签是否需要 #end 标签,注意默认 hasEnd 返回的是false。
  3. 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
来源:米瓜的博客
文章版权归作者所有,未经允许请勿转载。

THE END
二维码
打赏
请在后台主题设置处设置打赏图片
< <上一篇
下一篇>>