当前版本在后台发布文章时,增加了保存和恢复草稿的功能,后台还增加了友情链接功能。在install/create_table.zl脚本中,增加了和这些功能相关的表结构的创建语句

    页面导航:

项目下载地址:

    zenglBlog源代码的相关地址:https://github.com/zenglong/zenglBlog  最新版本对应的tag标签为:v0.8.0

zenglBlog v0.8.0:

    当前版本在后台发布文章时,增加了保存和恢复草稿的功能,后台还增加了友情链接功能。在install/create_table.zl脚本中,增加了和这些功能相关的表结构的创建语句:

// article表增加keywords字段
if(mysqlQuery(con, "ALTER TABLE article ADD COLUMN keywords varchar(255) NOT NULL DEFAULT '' COMMENT '关键词' AFTER description"))
	finish_with_error(con);
endif

// 将article表的title字段的最大字符数设置为255个字符
if(mysqlQuery(con, "ALTER TABLE article MODIFY COLUMN title varchar(255) NOT NULL DEFAULT '' COMMENT '文章标题'"))
	finish_with_error(con);
endif

// 将article表的description字段的最大字符数设置为1024个字符
if(mysqlQuery(con, "ALTER TABLE article MODIFY COLUMN description varchar(1024) NOT NULL DEFAULT '' COMMENT '文章描述'"))
	finish_with_error(con);
endif

// 创建文章草稿表
if(mysqlQuery(con, "CREATE TABLE article_draft(
	id int NOT NULL AUTO_INCREMENT, 
	title varchar(255) NOT NULL DEFAULT '' COMMENT '文章标题', 
	description varchar(1024) NOT NULL DEFAULT '' COMMENT '文章描述',
	keywords varchar(255) NOT NULL DEFAULT '' COMMENT '关键词',
	thumbnail varchar(255) NOT NULL DEFAULT '' COMMENT '文章缩略图', 
	author varchar(100) NOT NULL DEFAULT '' COMMENT '文章作者',
	cid int NOT NULL DEFAULT '0' COMMENT '分类ID', 
	content mediumtext NOT NULL COMMENT '文章内容',
	`time` timestamp NULL DEFAULT NULL COMMENT '保存草稿时间',
	PRIMARY KEY (id)
	) ENGINE=MyISAM DEFAULT CHARSET utf8 COLLATE utf8_general_ci COMMENT='文章草稿表'"))
	finish_with_error(con);
endif

// 创建友情链接表
if(mysqlQuery(con, "CREATE TABLE friendly_link(
	id int NOT NULL AUTO_INCREMENT, 
	name varchar(255) NOT NULL DEFAULT '' COMMENT '网站名称',
	url varchar(1024) NOT NULL DEFAULT '' COMMENT '网站url',
	description varchar(1024) NOT NULL DEFAULT '' COMMENT '网站描述',
	logo varchar(1024) NOT NULL DEFAULT '' COMMENT '网站LOGO',
	`created_at` timestamp NULL DEFAULT NULL COMMENT '创建时间',
	`updated_at` timestamp NULL DEFAULT NULL COMMENT '更新时间',
	PRIMARY KEY (id)
	) ENGINE=MyISAM DEFAULT CHARSET utf8 COLLATE utf8_general_ci COMMENT='友情链接表'"))
	finish_with_error(con);
endif

    上面代码中,除了创建文章草稿表和友情链接表外,还在article文章表中新增了keywords关键词字段,并将title字段的长度调整为255,description字段长度调整为1024,从而让标题和描述能容纳更多字符。当保存草稿的时候,会将当前发布文章的所有字段都保存到上面新增的article_draft表中,该表目前只会保存一条记录,用于记录最近保存的文章的所有信息。

    如果之前执行过上面这个脚本的话,需要先将install/install.lock安装锁文件删除掉,然后再将其他的表结构创建语句,以及插入管理员用户信息的语句给注释掉,接着就可以正常执行该脚本来创建和调整相关的表结构了。

    在创建好表结构后,就可以使用草稿功能了。后台发布文章的界面,新增了三个和草稿相关的按钮:

保存、查看及恢复草稿按钮

    点击保存草稿按钮,会将当前编辑的文章的所有信息(包括标题、关键词、内容等)都保存到草稿中。点击查看草稿按钮,可以查看当前保存在草稿中的具体数据:

查看草稿

    点击从草稿恢复数据按钮,会提示是否从草稿中恢复数据,如果点击确定就会执行恢复操作,恢复操作会将标题,关键词,描述,内容,分类等所有字段都进行恢复。

    和草稿相关的代码位于admin/article.zl脚本中:

inc 'common.zl';
inc 'helper.zl';

querys = rqtGetQuery();
action = querys['act'] ? querys['act'] : 'list';
if(action == 'list')
.......................................................
elif(action == 'save_draft')
	Article.saveDraft();
elif(action == 'show_draft')
	Article.showDraft();
elif(action == 'get_draft')
	Article.getDraft();
else
	print 'invalid act';
endif

class Article
	.......................................................

	// 保存草稿
	fun saveDraft()
		posts = rqtGetBodyAsArray();
		if(posts['submit'])
			bltUnset(&posts['submit']);
			posts['time'] = bltDate('%Y-%m-%d %H:%M:%S');
			db = Article.initDB();
			draft = Mysql.fetchOne(db, "select * from article_draft order by id desc limit 0,1");
			if(!bltCount(draft))
				Mysql.Insert(db, 'article_draft', posts);
			else
				Mysql.Update(db, 'article_draft', posts, 'id=' + draft['id']);
			endif
		else
			ajax_return('invalid posts');
		endif
		ajax_return();
	endfun

	// 查看草稿
	fun showDraft()
		db = Article.initDB();
		data = Mysql.fetchOne(db, "select * from article_draft order by id desc limit 0,1");
		if(!bltCount(data))
			data['message'] = '暂无草稿';
		endif
		print bltMustacheFileRender("tpl/show_draft.tpl",data);
	endfun

	// 获取草稿,可用于恢复操作
	fun getDraft()
		db = Article.initDB();
		data = Mysql.fetchOne(db, "select * from article_draft order by id desc limit 0,1");
		data['cdata'] = Mysql.fetchOne(db, "select id,name from category where id=" + data['cid']);
		rqtSetResponseHeader("Content-Type: application/json");
		print bltJsonEncode(data);
	endfun
endclass

    上面代码在保存草稿时,如果article_draft表中没有数据,就插入一条草稿数据,如果已存在了草稿数据,就直接更新已存在的草稿数据。因此,目前草稿只能保存最近的一份数据。

    在后台还可以看到友情链接菜单,使用该菜单可以管理友情链接:

友情链接后台菜单

    在友情链接列表的右上角点击"添加友情链接"按钮可以执行添加友情链接的操作:

后台添加友情链接按钮

添加友情链接界面

    点击添加按钮,就可以进入上图所示的界面来添加友情链接,网站名称和网站url是必填项,还可以上传网站的LOGO,和添加一些网站描述。

    添加了友情链接后,在首页底部就可以看到这些友情链接了:

首页底部的友情链接

    如果是通过动态脚本index.zl访问首页,应该可以马上看到新增加的友情链接,如果访问index.html静态首页,则需要先在后台生成静态页面,才能看到新增的友情链接。

    和友情链接相关的代码位于admin/friendly.zl脚本中:

inc 'common.zl';
inc 'helper.zl';

querys = rqtGetQuery();
action = querys['act'] ? querys['act'] : 'list';
if(action == 'list')
	Friendly.list();
elif(action == 'add')
	Friendly.add();
elif(action == 'edit')
	Friendly.edit();
elif(action == 'delete')
	Friendly.delete();
else
	print 'invalid act';
endif

class Friendly
	// 显示友情链接列表
	fun list()
		global menus, querys;
		data['title'] = '友情链接:';
		setCurMenu(menus, 'friendly.zl');
		data['menus'] = menus;
		db = Friendly.initDB();
		tmp = Mysql.fetchOne(db, "select count(1) as cnt from friendly_link ");
		total = bltInt(tmp['cnt']);
		page = get_page(10, total, 10);
		for(i=page['start']; i <= page['end'];i++)
			cur = (page['curpage'] == i) ? ' class="active"' : '';
			pages[] = '<li'+cur+'><a href="'+page['link']+'page='+i+'">'+i+'</a></li>';
		endfor
		data['page'] = page;
		data['pages'] = pages;
		data['items'] = Mysql.fetchAll(db, "select * from friendly_link order by id desc limit " + page['offset'] + "," + page['limit']);
		print bltMustacheFileRender("tpl/friendly_list.tpl", data);
	endfun

	// 初始化数据库连接
	fun initDB()
		global config;
		db = bltArray();
		Mysql.init(db, config, "tpl/error.tpl");
		return db;
	endfun

	// 显示添加或编辑友情链接的页面
	fun showAdd(db, data, isadd = TRUE)
		global menus;
		setCurMenu(menus, 'friendly.zl');
		csses = bltArray('jquery.fileupload.css');
		js = bltArray('jquery.ui.widget.js', 'jquery.iframe-transport.js', 'jquery.fileupload.js');
		data['csses'] = csses;
		data['head_js'] = js;
		data['menus'] = menus;
		data['title'] = isadd ? '添加友情链接' : '编辑友情链接';
		data['act'] = isadd ? 'add' : 'edit';
		print bltMustacheFileRender("tpl/friendly_add.tpl", data);
	endfun

	// 校验数据
	fun validate(db, posts, isadd = TRUE)
		bltUnset(&posts['thumbnail_up']);
		bltStr(&posts['name'], TRUE);
		bltStr(&posts['url'], TRUE);
		bltStr(&posts['logo'], TRUE);
		bltStr(&posts['description'], TRUE);
		if(!posts['name'])
			data['err_msg'] = '网站名称不能为空';
		elif(!posts['url'])
			data['err_msg'] = '网站url不能为空';
		elif(!isadd && !posts['id'])
			data['err_msg'] = '无效的ID';
		endif
		if(data['err_msg'])
			data['posts'] = posts;
			if(posts['id'])
				data['id'] = posts['id'];
			endif
			Friendly.showAdd(db, data, isadd);
			bltExit();
		endif
	endfun

	// 添加友情链接
	fun add()
		posts = rqtGetBodyAsArray();
		db = Friendly.initDB();
		if(posts['submit'])
			Friendly.validate(db, posts);
			bltUnset(&posts['submit']);
			posts['created_at'] = bltDate('%Y-%m-%d %H:%M:%S');
			posts['updated_at'] = posts['created_at'];
			Mysql.Insert(db, 'friendly_link', posts);
			data['success_msg'] = '添加友情链接成功';
		endif
		Friendly.showAdd(db, data);
	endfun

	// 编辑友情链接
	fun edit()
		global querys;
		posts = rqtGetBodyAsArray();
		db = Friendly.initDB();
		if(posts['submit'])
			id = bltInt(posts['id']);
			Friendly.validate(db, posts);
			bltUnset(&posts['submit']);
			posts['updated_at'] = bltDate('%Y-%m-%d %H:%M:%S');
			Mysql.Update(db, 'friendly_link', posts, 'id=' + id);
			data['success_msg'] = '编辑友情链接成功';
		else
			id = bltInt(querys['id']);
		endif
		data['posts'] = Mysql.fetchOne(db, "select * from friendly_link where id='"+id+"'");
		if(!bltCount(data['posts']))
			data['err_msg'] = '无效的ID';
			id = 0;
		endif
		data['id'] = id;
		Friendly.showAdd(db, data, FALSE);
	endfun

	// 删除友情链接
	fun delete()
		global querys;
		db = Friendly.initDB();
		id = querys['id'] ? bltInt(querys['id']) : 0;
		if(id <= 0) 
			ajax_return('无效的id');
		endif
		Mysql.Exec(db, "DELETE FROM friendly_link WHERE id="+id);
		ajax_return();
	endfun
endclass

    从代码中可以看到,友情链接的增删改操作都是针对friendly_link表进行的。

    当前版本的前台内容页,还增加了上下篇和相关文章,方便查看同一分类下的其他文章:

上下篇和相关文章

    上下篇相关的代码位于article.class.zl脚本中:

class Article
	// 显示文章内容
	fun show(static = FALSE)
		....................................................................
		common_sql = "'" + id + "' and cid='"+ data['cid'] +"' order by ";
		prev_article = Mysql.fetchOne(db, "select id,title,DATE_FORMAT(created_at, '%Y%m') as format_created from article where id < " + common_sql + " id desc limit 0,1");
		if(bltCount(prev_article) > 0)
			data['prev_article'] = prev_article;
		endif
		next_article = Mysql.fetchOne(db, "select id,title,DATE_FORMAT(created_at, '%Y%m') as format_created from article where id > " + common_sql + " id asc limit 0,1");
		if(bltCount(next_article) > 0)
			data['next_article'] = next_article;
		endif
		rand_article = Mysql.fetchAll(db, "select id,title,DATE_FORMAT(created_at, '%Y%m') as format_created from article where id != " + common_sql + " RAND() limit 6");
		if(bltCount(rand_article) > 0)
			data['rand_article'] = rand_article;
		endif
		....................................................................
		content = bltMustacheFileRender("/tpl/article_show.tpl", data);
		if(!static)
			print content;
		else
			return content;
		endif
	endfun

	....................................................................
endclass

    上面的prev_article变量存储了上一篇文章的数据,next_article变量存储了下一篇文章的数据,rand_article变量存储了从当前分类中获取的随机的几篇文章的数据。

    最后,手机端访问前台首页,列表页和内容页时,会使用较小尺寸的返回顶部图片,PC端还是使用大的返回顶部图片,这样手机端的返回顶部图片就不会遮挡太多的内容了:

手机端使用小尺寸的返回顶部图片

    在页面上用js做了手机端的检测:

$(document).ready(function () { 
	if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) { // 检测到手机端则使用小的返回顶部图片
		$.fn.yestop({yes_image: '/assets/image/yestop_sm.png', yes_bottom:"15px", yes_right: "15px", yes_width: "36px", yes_height: "36px"});
	}
	else { // 否则使用大的返回顶部图片
		$.fn.yestop({yes_image: '/assets/image/yestop.png'});
	}
	$(".row .content img").removeAttr("style").addClass("img-responsive center-block");
});

    以上就是当前版本的相关内容,休息,休息一下。

结束语:

    鬼谷子:你去山谷采上一只山花,为师为你占上一卦

    孙膑:是 ... 先生就用这只花为弟子占一卦吧

    鬼谷子:好,就用这只菊花吧。此花虽经吹残,但它本性耐寒,经霜不败,因而预示着你今后虽被残害,但还不至于大难大凶,此花被人供于案上瓶内,这预示着你将受到世人的敬重,此花被你两番放入,预示着你不会马上功成名就。此花最后又被你重放瓶中,或许你将来会在你的故乡齐国建功立业。供养此花的花瓶乃金铜铸成,它与钟鼎同类,看来你将来要威震天下,名字刻于钟鼎之上。

—— 《孙子兵法与三十六计》

上下篇

下一篇: 暂无

上一篇: zenglBlog v0.7.0 增加公告功能,内容页图片自适应,首页及列表页样式调整

相关文章

zenglBlog v0.1.0 登录和后台界面

zenglBlog v0.2.0 分类管理

zenglBlog v0.3.0 文章管理,图片上传,生成缩略图

zenglBlog v0.6.0-v0.6.2 生成静态页面

zenglBlog v0.5.0 发表评论,图形验证码

zenglBlog v0.4.0 前台首页,文章列表,文章内容页