v0.3.0版本添加了文章管理功能,可以实现文章的添加、编辑、删除等。文章添加和编辑时,使用CKEditor在线编辑器,可以在编辑器中上传图片。还可以在添加及编辑文章时,单独上传一张图片,后台会自动生成该图片的缩略图...

    页面导航: 项目下载地址:

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

zenglBlog v0.3.0:

    v0.3.0版本添加了文章管理功能,可以实现文章的添加、编辑、删除等。文章添加和编辑时,使用CKEditor在线编辑器,可以在编辑器中上传图片。还可以在添加及编辑文章时,单独上传一张图片,后台会自动生成该图片的缩略图。

    要使用该版本新增的文章管理功能,首先需要新建和文章相关的数据库表结构,在install/create_table.zl中增加了文章表的创建语句:

..........................................................
// 创建文章表
if(mysqlQuery(con, "DROP TABLE IF EXISTS article"))
	finish_with_error(con);
endif
if(mysqlQuery(con, "CREATE TABLE article(
	id int NOT NULL AUTO_INCREMENT, 
	title varchar(100) NOT NULL DEFAULT '' COMMENT '文章标题', 
	description 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 '文章内容',
	`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
..........................................................


    在浏览器中直接运行install/create_table.zl就可以新建文章表,如果之前已经创建过users用户表,category分类表的,可以将users和category表的创建语句,以及插入users表的语句给注释掉,再执行该脚本。

    当前版本,在运行完install/create_table.zl安装脚本后,会生成install.lock锁文件,以防止误操作。在有锁文件的情况下,再次执行create_table.zl脚本,会提示lock file exists,并阻止脚本继续执行。

    创建好表结构后,进入后台,可以看到"文章列表"菜单。点击该菜单,一开始会看到空的文章列表:



图1:文章列表

    点击文章列表右上角的添加文章按钮,可以执行添加文章的操作:



图2:文章列表右上角的添加文章按钮

 
   

图3:添加文章界面

    在添加文章界面,可以设置文章的标题,描述,内容,所属上级分类,文章作者,以及上传一张图片作为缩略图(后台会自动对上传的图片进行缩放尺寸的操作):



图4:设置需要添加的文章内容及缩略图等

    在添加完文章后,就可以在列表中看到添加过的文章了:



图5:添加文章后的文章列表

    文章列表中可以根据文章标题和分类进行筛选,列表底部有分页,可以分页显示列表信息。在列表中还可以对文章进行编辑和删除操作。

    和文章相关的操作位于admin/article.zl脚本中:

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

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

class Article
	// 显示文章列表
	fun list()
		global menus, querys;
		data['title'] = '文章列表:';
		setCurMenu(menus, 'article.zl');
		data['csses', 0] = 'article_list.css';
		data['head_js', 0] = 'category.js';
		data['menus'] = menus;
		db = Article.initDB();
		Article.getCategory(db, data, querys);
		where = " where 1 ";
		if(bltCount(data['category']))
			where += " and cid=" + data['category', 'id'];
		endif
		stitle = bltStr(&querys['stitle']);
		if(stitle)
			where += ' and title like "%' + stitle + '%"';
			data['stitle'] = stitle;
		endif
		tmp = Mysql.fetchOne(db, "select count(1) as cnt from article " + where);
		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['articles'] = Mysql.fetchAll(db, "select a.id,title,thumbnail,author,created_at,updated_at,c.name as cname from article as a left join category as c on c.id=a.cid "+ where +
							" order by id desc limit " + page['offset'] + "," + page['limit']);
		print bltMustacheFileRender("tpl/article_list.tpl", data);
	endfun

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

	// 获取分类列表,或者某个分类的信息
	fun getCategory(db, data, posts = 0)
		if(posts && posts['cid'] > 0)
			data['category'] = Mysql.fetchOne(db, "select id,name from category where id='"+posts['cid']+"'");
		else
			data['categories'] = Mysql.fetchAll(db, "select id,name,childcnt from category where pid=0 order by id asc");
		endif
	endfun

	// 显示添加或编辑文章的页面
	fun showAdd(db, data, posts = 0, isadd = TRUE)
		global menus;
		setCurMenu(menus, 'article.zl');
		csses = bltArray('article_add.css', 'jquery.fileupload.css');
		js = bltArray('ckeditor/ckeditor.js', 'jquery.ui.widget.js', 'jquery.iframe-transport.js', 'jquery.fileupload.js', 'category.js');
		data['csses'] = csses;
		data['head_js'] = js;
		data['menus'] = menus;
		data['title'] = isadd ? '添加文章' : '编辑文章';
		data['act'] = isadd ? 'add' : 'edit';
		Article.getCategory(db, data, posts);
		print bltMustacheFileRender("tpl/article_add.tpl",data);
	endfun

	// 校验数据
	fun validate(db, posts, isadd = TRUE)
		bltUnset(&posts['thumbnail_up']);
		bltStr(&posts['title'], TRUE);
		bltStr(&posts['thumbnail'], TRUE);	
		bltStr(&posts['description'], TRUE);
		bltStr(&posts['content'], TRUE);
		bltInt(&posts['cid'], TRUE);
		bltStr(&posts['author'], TRUE);
		if(!posts['title'])
			data['err_msg'] = '标题不能为空';
		elif(!posts['content'])
			data['err_msg'] = '内容不能为空';
		elif(!posts['cid'])
			data['err_msg'] = '请选择有效的分类';
		elif(!posts['author'])
			data['err_msg'] = '作者不能为空';
		elif(!isadd && !posts['id'])
			data['err_msg'] = '无效的文章ID';
		endif
		if(data['err_msg'])
			data['posts'] = posts;
			if(posts['id'])
				data['id'] = posts['id'];
			endif
			Article.showAdd(db, data, posts, isadd);
			bltExit();
		endif
	endfun

	// 添加文章
	fun add()
		posts = rqtGetBodyAsArray();
		db = Article.initDB();
		if(posts['submit'])
			Article.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, 'article', posts);
			data['success_msg'] = '添加文章成功';
		endif
		Article.showAdd(db, data);
	endfun

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

	// 删除文章
	fun delete()
		global querys;
		db = Article.initDB();
		id = querys['id'] ? bltInt(querys['id']) : 0;
		if(id <= 0) 
			ajax_return('无效的文章id'); 
		endif
		Mysql.Exec(db, "DELETE FROM article WHERE id="+id);
		ajax_return();
	endfun
endclass


    和图片上传以及生成缩略图相关的代码位于admin/upload.zl脚本中:

inc 'common.zl';
use magick;

querys = rqtGetQuery();
action = querys['act'] ? querys['act'] : 'list';
if(action == 'ckImage')
	Upload.ckImage();
elif(action == 'thumbImg')
	Upload.thumbImg();
else
	print 'invalid act';
endif

class Upload
	// 检测上传图片的大小
	fun checkImageSize(exit = TRUE)
		rqtGetBody(&body_count);
		headers = rqtGetHeaders();
		if(headers['Content-Length'] > body_count)
			error = '上传的图片过大' + ' 上传大小:' + headers['Content-Length'] + ' 服务器截断大小:' + body_count;
			if(exit)
				rqtSetResponseHeader("Content-Type: application/json");
				data['error'] = error;
				print bltJsonEncode(data);
				bltExit();
			else
				return error;
			endif
		else
			return '';
		endif
	endfun

	// 通过CKEditor编辑器上传图片后,需要返回的js脚本。如果上传成功,urlpath就表示上传成功的图片的url地址,如果上传失败,则通过msg来返回出错信息
	fun returnCkMsg(msg, urlpath = '')
		global querys;
		funcNum = querys['CKEditorFuncNum'];
		print "<script type='text/javascript'>window.parent.CKEDITOR.tools.callFunction("+funcNum+", '"+
			urlpath+"', '" + msg + "');</script>";
	endfun

	// 下面的方法用于处理由CKEditor编辑器上传的图片
	fun ckImage()
		error = Upload.checkImageSize(FALSE);
		if(error != '')
			Upload.returnCkMsg(error);
			return;
		endif
		body_array = rqtGetBodyAsArray();
		if(bltCount(body_array['upload']) > 0)
			v = body_array['upload'];
			type = v['type']; // 通过图片类型确定需要设置的图片文件名后缀
			if(type == 'image/png')
				ext = '.png';
			elif(type == 'image/gif')
				ext = '.gif';
			else
				ext = '.jpg';
			endif
			dir = '/upload/image/' + bltDate('%Y%m%d') + '/'; // 根据当前日期,确定图片需要保存的目录
			bltMkdir('..' + dir);
			urlpath = dir + bltMd5(v['filename']) + ext; // 使用上传文件名的md5值来确定需要保存的图片的文件名
			bltWriteFile('..' + urlpath, v['content_ptr'], v['length']);
			wand = magickNewWand();
			if(!magickReadImage(wand, '..' + urlpath))
				msg = '上传失败,或者上传的不是有效的图片文件';
				bltUnlink('..' + urlpath);
				urlpath = '';
			else
				msg = '';
			endif
			cookies = rqtGetCookie();
			token = body_array['ckCsrfToken'];
			if(bltCount(token) == 0 || cookies['ckCsrfToken'] != token)
				print 'access denied'; bltExit();
			endif
			Upload.returnCkMsg(msg, urlpath);
		else
			print 'no upload';
		endif
	endfun

	// 下面方法用于处理缩略图的上传
	fun thumbImg()
		global querys;
		Upload.checkImageSize();
		body_array = rqtGetBodyAsArray(&body_count);
		if(bltCount(body_array['thumbnail_up']) > 0)
			v = body_array['thumbnail_up'];
			thumbdir = '/upload/thumb/' + bltDate('%Y%m%d') + '/'; // 根据当前日期,确定图片需要保存的目录
			bltMkdir('..' + thumbdir);
			tmp = '..' + thumbdir + 'tmp'; // TODO 多用户操作时可能会覆盖
			urlpath = thumbdir + bltMd5(v['filename']); // 使用上传文件名的md5值来确定需要保存的图片的文件名
			if(v['type'] == 'image/gif')
				framepath = urlpath + '-0.jpg'; // gif的第一帧对应的jpg
			else
				framepath = '';
			endif
			urlpath += '.jpg';
			bltWriteFile(tmp, v['content_ptr'], v['length']);
			rqtSetResponseHeader("Content-Type: application/json");
			wand = magickNewWand();
			if(!magickReadImage(wand, tmp))
				data['error'] = '上传失败,或者上传的不是有效的图片文件';
			elif(!magickResizeImage(wand, 200, 150, "LanczosFilter"))
				data['error'] = '生成缩略图失败,调整大小出错';
			elif(!magickWriteImage(wand, '..' + urlpath))
				data['error'] = '生成缩略图失败,写入失败';
			elif(bltFileExists('..' + urlpath))
				data['urlpath'] = urlpath;
			elif(framepath != '' && bltFileExists('..' + framepath))
				data['urlpath'] = framepath;
			else
				data['error'] = '生成缩略图失败,生成的缩略图不存在';
			endif
			bltUnlink(tmp);
			print bltJsonEncode(data);
		else
			print 'no thumbnail';
		endif
	endfun
endclass


    从上面的脚本中可以看到,图片上传和缩略图的生成用到了magick模块。

    和文章相关的模板文件为:admin/tpl/article_add.tpl以及admin/tpl/article_list.tpl。其中,article_add.tpl是用于显示添加和编辑文章界面的,article_list.tpl则是用于显示文章列表的模板。

    admin/tpl/article_add.tpl的内容如下:

{{> tpl/header.tpl}}
<h2 class="sub-header">{{title}}<a href="?act=list" class="btn btn-primary pull-right" role="button">返回列表</a></h2>
{{#err_msg}}<div class="alert alert-danger" role="alert">{{err_msg}}</div>{{/err_msg}}
{{#success_msg}}<div class="alert alert-success" role="alert">{{success_msg}}</div>{{/success_msg}}
<form action="?act={{act}}{{#id}}&id={{id}}{{/id}}" method="post" id="cate_form">
	{{#id}}<input type="hidden" name="id" value="{{id}}">{{/id}}
	<div class="form-group">
		<label for="title">标题:</label>
		<input type="text" class="form-control" name="title" {{#posts}}value="{{title}}"{{/posts}} id="title" placeholder="标题">
	</div>
	<div class="form-group">
		<label for="thumbnail-upload">缩略图:</label>
		<input id="thumbnail-upload" type="file" name="thumbnail_up" data-url="upload.zl?act=thumbImg">
		<input id="thumbnail-hidden" type="hidden" name="thumbnail" {{#posts}}value="{{thumbnail}}"{{/posts}}>
		<span id="thumbnail-span"></span>
		<img {{#posts}}src="{{thumbnail}}"{{/posts}} style="display:none" id="thumbnail-img" />
	</div>
	<div class="form-group">
		<label for="description">描述:</label>
		<textarea class="form-control" rows="5" name="description" id="description">{{#posts}}{{description}}{{/posts}}</textarea>
	</div>
	<div class="form-group">
		<label for="content">内容:</label>
		<textarea class="form-control" rows="20" cols="80" name="content" id="content">{{#posts}}{{content}}{{/posts}}</textarea>
	</div>
	{{#category}}
	<div class="form-group" id="p-cate">
		<label>上级分类:<span id="p-cate-name">{{name}}(cid={{id}})</span></label>
		<input type="hidden" name="cid" value="{{id}}" id="pid">
		<a href="javascript:void(0)" id="reset_category" class="btn btn-default" data-loading-text="加载分类列表...">重置上级分类</a>
	</div>
	{{/category}}
	{{^category}}
	<div class="form-group" id="p-cate">
		<label for="pid">上级分类:<span id="p-cate-name">(cid=0)</span></label>
		<input type="hidden" name="cid" value="0" id="pid">
		<select class="form-control sel_pid">
			<option value="0" data-childcnt="0">选择上级分类</option>
			{{#categories}}
			<option value="{{id}}" data-childcnt="{{childcnt}}">{{name}}</option>
			{{/categories}}
		</select>
	</div>
	{{/category}}
	<div class="form-group">
		<label for="author">作者:</label>
		<input type="text" class="form-control" name="author" {{#posts}}value="{{author}}"{{/posts}} id="author" placeholder="作者">
	</div>
	<button name="submit" value="Submit" type="submit" class="btn btn-primary">提交</button>
</form>
<script type="text/javascript">
	CKEDITOR.replace( 'content' ,{
		height: 300,
		filebrowserUploadUrl: 'upload.zl?act=ckImage',
		filebrowserUploadMethod: 'form'
	});
	$('#thumbnail-upload').fileupload({
		dataType: 'json',
		formData: {},
		add: function(e, data) {
			$('#thumbnail-span').text('上传中...');
			data.submit();
		},
		done: function (e, data) {
			if(typeof data.result.error === "undefined") {
				var $img = $('#thumbnail-img');
				$img.attr("src", data.result.urlpath);
				$img.show();
				$('#thumbnail-span').text('');
				$('#thumbnail-hidden').val(data.result.urlpath);
			} else {
				$('#thumbnail-img').hide();
				$('#thumbnail-span').text(data.result.error);
				$('#thumbnail-hidden').val("");
			}
        },
		fail: function() {
			$('#thumbnail-span').text('上传失败');
		}
	});

	$(document).ready(function() {
		{{#category}} {{! 如果设置了上级分类,则添加重置上级分类的脚本 }}	
		$('#reset_category').click(function(){
			reset_category_ajax();
		});
		{{/category}}
		var $img = $('#thumbnail-img');
		if($img.attr('src') != '') { // 如果所略图不为空,则在一开始就显示出来
			$img.show();
		}
	});
</script>
{{> tpl/footer.tpl}}


    admin/tpl/article_list.tpl的内容如下:

{{> tpl/header.tpl}}
<h2 class="sub-header">
	文章列表
	<a href="?act=add" class="btn btn-primary pull-right" role="button">添加文章</a>
</h2>
<div class="table-responsive">
	<form action="?" method="get" id="cate_form">
		<div class="form-group" id="p-title">
			<input type="text" class="form-control" name="stitle" {{#stitle}}value="{{stitle}}"{{/stitle}} placeholder="标题">
		</div>
		<div class="form-group" id="p-cate">
			{{#category}}
				<label id="p-cate-label">所属分类:<span id="p-cate-name">{{name}}(cid={{id}})</span></label>
				<input type="hidden" name="cid" value="{{id}}" id="pid">
				<a href="javascript:void(0)" id="reset_category" class="btn btn-default" data-loading-text="加载分类列表...">重置分类</a>
			{{/category}}
			{{^category}}
				<input type="hidden" name="cid" value="0" id="pid">
				<select class="form-control sel_pid">
					<option value="0" data-childcnt="0">选择上级分类</option>
					{{#categories}}
					<option value="{{id}}" data-childcnt="{{childcnt}}">{{name}}</option>
					{{/categories}}
				</select>
			{{/category}}
		</div>
		<div style="float:left"><button name="s" value="search" type="submit" class="btn btn-primary">搜索</button></div>
		<div style="clear:both"></div>
	</form>
	<table class="table table-hover">
		<thead>
		<tr>
			<th>id</th>
			<th>缩略图</th>
			<th>标题</th>
			<th>作者</th>
			<th>所属分类</th>
			<th>创建时间</th>
			<th>更新时间</th>
			<th>操作</th>
		</tr>
		</thead>
		<tbody>
			{{#articles}}
			<tr>
				<td>{{id}}</td>
				<td>{{#thumbnail}}<img src="{{thumbnail}}" width="100" height="80"/>{{/thumbnail}}</td>
				<td>{{title}}</td>
				<td>{{author}}</td>
				<td>{{cname}}</td>
				<td>{{created_at}}</td>
				<td>{{updated_at}}</td>
				<td>
					<a href="?act=edit&id={{id}}" title="编辑" class="glyphicon glyphicon-edit" aria-hidden="true"></a>  
					<a href="javascript:void(0)" data-id="{{id}}" title="删除" class="glyphicon glyphicon-trash del_article" aria-hidden="true"></a><span></span></td>
			</tr>
			{{/articles}}
			{{^articles}}
				<tr><td colspan=8>暂无文章</td></tr>
			{{/articles}}
		</tbody>
	</table>
	{{#page}}
	<nav aria-label="Page navigation">
	  <ul class="pagination">
		<li>
		  <a href="{{{link}}}page=1" aria-label="Previous">
		    <span aria-hidden="true">«</span>
		  </a>
		</li>
		<li>
		  <a href="{{{link}}}page={{prev}}" aria-label="Previous">
		    <span aria-hidden="true">‹</span>
		  </a>
		</li>
		{{#pages}}
			{{{.}}}
		{{/pages}}
		<li>
		  <a href="{{{link}}}page={{next}}" aria-label="Next">
		    <span aria-hidden="true">›</span>
		  </a>
		</li>
		<li>
		  <a href="{{{link}}}page={{totalpage}}" aria-label="Next">
		    <span aria-hidden="true">»</span>
		  </a>
		</li>
	  </ul>
	</nav>
	<span>总共{{total}}篇文章/共{{totalpage}}页</span>
	{{/page}}
</div>
<script type="text/javascript">
$( document ).ready(function() {
	$('.del_article').click(function(){
		var id = $(this).data('id');
		var next_span = $(this).next('span');
		var r = confirm("删除文章确认[id: "+id+"]");
		if(r == true) {
			var timestamp = new Date().getTime(); //IE浏览器ajax GET请求有缓存问题!
			$.ajax({
				type: 'GET',
				url: "article.zl?timestamp="+timestamp,
				dataType: "json",
				data: {
					"act": "delete",
					"id": id
				},
				beforeSend:function(){
					next_span.text(' 删除中...');
				},
				success: function(data){
					if(data.msg != 'success') {					
						alert('删除失败: ' + data.errmsg);
						next_span.text('');
						return;
					}
					alert('删除成功');
					window.location.reload();
				},
				//调用出错执行的函数
				error: function(err){
					alert('未知异常');
					next_span.text('');
				}
			});
		}
	});

	{{#category}} {{! 如果设置了上级分类,则添加重置上级分类的脚本 }}	
	$('#reset_category').click(function(){
		reset_category_ajax();
		$('#p-cate-label').hide();
	});
	{{/category}}
});
</script>
{{> tpl/footer.tpl}}


    此外,当前版本还新增了admin/helper.zl脚本,将一些通用的辅助函数写在该脚本中,例如:ajax_return用于返回成功或失败的函数,以及get_page用于获取分页相关的信息的函数:

// ajax返回成功或失败
fun ajax_return(errmsg = '')
	ret['msg'] = errmsg ? 'failed' : 'success';
	ret['errmsg'] = errmsg;
	rqtSetResponseHeader("Content-Type: application/json");
	print bltJsonEncode(ret);
	bltExit();
endfun

/**
 * 获取分页相关的信息,第一个参数per表示一页显示多少条记录,total表示总记录数
 * showpage表示需要显示的分页数,link表示需要访问的地址,以问号结尾
 */
fun get_page(per, total, showpage, link = '?')
	global querys;
	totalpage = bltInt(total/per); // 根据总记录数和每页需要显示的记录数计算总页数
	if(total % per > 0) // 如果有余数则将总页数加一
		totalpage++;
	endif
	half = bltInt(showpage / 2);
	curpage = bltInt(querys['page']); // 从查询字符串中获取page也就是当前分页
	if(curpage < 1)
		curpage = 1;
	elif(curpage > totalpage)
		curpage = totalpage;
	endif
	start = curpage - half; // 计算起始页号
	if(start < 1) 
		start = 1; 
	endif
	end = curpage + half; // 计算结束页号
	if(end > totalpage)
		end = totalpage;
	endif
	for(i=0;bltIterArray(querys,&i,&k,&v);) // 获取不包括page在内的查询字符串
		if(k != 'page')
			link += k + '=' + v + '&';
		endif
	endfor
	// 将分页信息存储到ret返回
	ret['start'] = start;
	ret['end'] = end;
	ret['offset'] = (curpage - 1) * per;
	ret['limit'] = per;
	ret['prev'] = (curpage <= 1) ? 1 : (curpage - 1); // 计算上一页
	ret['next'] = (curpage >= totalpage) ? totalpage : (curpage + 1); // 计算下一页
	ret['total'] = total;
	ret['totalpage'] = totalpage;
	ret['curpage'] = curpage;
	ret['link'] = link;
	return ret;
endfun


    此外,当前版本还去除了category.zl脚本中除了ajaxList方法外,其他方法中用到的bltHtmlEscape转义操作,因为zenglServer v0.11.0版本中的模板引擎在默认情况下会自动对字符串进行html转义。

    CKEditor编辑器上传的图片会存储在upload/image/%Y%m%d目录中,%Y%m%d表示当前上传的年月日。

    上传的缩略图会存储在upload/thumb/%Y%m%d目录中,%Y%m%d表示当前上传的年月日。

    以上就是当前版本的相关内容。OK,就到这里,休息,休息一下 o(∩_∩)o~~

结束语:

    如果你很想很想做一件事,把所有的心血都放在上面,就一定会成功

——  《隐形怪杰》
 
上下篇

下一篇: zenglBlog v0.4.0 前台首页,文章列表,文章内容页

上一篇: zenglBlog v0.2.0 分类管理

相关文章

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

zenglBlog v0.2.0 分类管理

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

zenglBlog v0.1.0 登录和后台界面

zenglBlog v0.8.0 增加保存草稿,友情链接,内容页上下篇,手机端返回顶部图片

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