(十一)日志创建页
2018-05-23 本文已影响13人
顽强的猫尾草
首先安装markdown2库来支持markdown语言:
$ sudo pip install markdown2
$ sudo pip install pygments
注意要把安装后的markdown2.py和pygments文件夹放到www文件夹下,因为我们想支持代码块以及表格等额外的markdown语言,而默认这些特性是关闭的,因此需要稍微修改一下markdown2.py:
...
def markdown_path(path, encoding="utf-8", html4tags=False, tab_width=DEFAULT_TAB_WIDTH,
safe_mode=None, extras=["fenced-code-blocks", "tables"],
link_patterns=None, use_file_vars=False):
...
需要什么就在这里找到然后填在extras后面。
还需要下载至少一种pygments-css来支持代码颜色:
$ git clone https://github.com/richleland/pygments-css.git
$ cd pygments-css
把makefile文件里的.highlight
替换成.codehilite
,接着:
$ make cssfiles
当然还要在base.html里面添加这个css文件,不然也是显示不出来的:
<link rel="stylesheet" href="/static/css/pygments-css/murphy.css">
随便选了一个,好像不太好看,有空再换一换。另外需要在三个反引号后指出用的是什么语言,不然还是不能显示出颜色...(这是后话):
然后修改handlers.py,添加如下内容:
- 将输入文本转化成html文本
- 检查用户是否为管理员
- 获取页码
- 呈现博客内容的网页处理函数
- 根据博客id获取博客内容的网页处理函数
- 创建/编辑博客的网页处理函数
- 发布博客的网页处理函数
import markdown2
def text2html(text):
# 先将text去除空行,再写成html格式
lines = map(lambda s: '<p>%s</p>' % s.replace('&', '&').replace('<', '<').replace('>', '>'), filter(lambda s: s.strip() != '', text.split('\n')))
return ''.join(lines)
def check_admin(request):
if request.__user__ is None or not request.__user__.admin:
raise APIPermissionError()
def get_page_index(page_str):
p = 1
try:
p = int(page_str)
except ValueError as e:
pass
if p < 1:
p = 1
return p
@get('/blog/{id}')
async def get_blog(id):
blog = await Blog.find(id) # 根据id查询到blog对象
comments = await Comment.findAll('blog_id=?', [id], orderBy='created_at desc')
for c in comments:
c.html_content = text2html(c.content)
blog.html_content = markdown2.markdown(blog.content)
return {
'__template__': 'blog.html',
'blog': blog,
'comments': comments
}
@get('/manage/blogs/create')
def manage_create_blogs():
return {
'__template__': 'manage_blog_edit.html',
'id': '',
'action': '/api/blogs'
}
@get('/api/blogs/{id}')
async def api_get_blog(*, id):
blog = await Blog.find(id)
# 返回到manage_blog_edit.html中, 当需要编辑旧博客时
return blog
@post('/api/blogs')
async def api_create_blog(request, *, name, summary, content):
check_admin(request) # 只有管理员才可以发布博客
if not name or not name.strip():
raise APIValueError('name', 'name cannot be empty.')
if not summary or not summary.strip():
raise APIValueError('summary', 'summary cannot be empty.')
if not content or not content.strip():
raise APIValueError('content', 'content cannot be empty.')
blog = Blog(
user_id=request.__user__.id, # app.py中把cookie2user获取到的用户赋给了request.__user__
user_name=request.__user__.name,
user_image=request.__user__.image,
name=name.strip(),
summary=summary.strip(),
content=content.strip()
)
await blog.save()
return blog
然后使用MVVM模式写一个创建博客的页面templates/manage_blog_edit.html,这种模式的好处是前后端分离,逻辑清晰:
{% extends '__base__.html' %}
{% block title %}编辑日志{% endblock %}
{% block beforehead %}
<script>
var
ID = '{{ id }}', // 写博客时id还未被创建
action = '{{ action }}';
function initVM(blog) {
var vm = new Vue({
el: '#vm', // 绑定的View, 这里是#vm, 即id为vm的DOM, 对应的是一个<div>标签
data: blog, // JS对象表示的Model, 在下面初始化为{name:'',summary:'',content:''}
methods: { // View可以触发的JS函数, submit就是提交表单时触发的函数
submit: function (event) {
event.preventDefault();
var $form = $('#vm').find('form');
$form.postJSON(action, this.$data, function (err, r) {
if (err) {
$form.showFormError(err);
}
else {
// 提交后跳转到的地址
return location.assign('/blog/' + r.id); # 跳转到日志内容页
}
});
}
}
});
$('#vm').show();
}
$(function () {
if (ID) { //旧博客, 调出来继续编辑
getJSON('/api/blogs/' + ID, function (err, blog) {
if (err) {
return fatal(err); // fatal函数?
}
$('#loading').hide();
initVM(blog);
});
}
else { // 新博客
$('#loading').hide();
initVM({
name: '',
summary: '',
content: ''
});
}
});
</script>
{% endblock %}
{% block content %}
<div class="uk-width-1-1 uk-margin-bottom">
<div class="uk-panel uk-panel-box">
<ul class="uk-breadcrumb">
<li><a href="/manage/comments">评论</a></li>
<li><a href="/manage/blogs">日志</a></li>
<li><a href="/manage/users">用户</a></li>
</ul>
</div>
</div>
<div id="error" class="uk-width-1-1"></div>
<div id="loading" class="uk-width-1-1 uk-text-center">
<span><i class="uk-icon-spinner uk-icon-medium uk-icon-spin"></i> 正在加载...</span>
</div>
<div id="vm" class="uk-width-2-3">
<!-- 把提交表单的事件关联到submit方法 -->
<form v-on="submit: submit" class="uk-form uk-form-stacked">
<div class="uk-alert uk-alert-danger uk-hidden"></div>
<div class="uk-form-row">
<label class="uk-form-label">标题:</label>
<div class="uk-form-controls">
<input v-model="name" name="name" type="text" placeholder="标题" class="uk-width-1-1">
</div>
</div>
<div class="uk-form-row">
<label class="uk-form-label">摘要:</label>
<div class="uk-form-controls">
<textarea v-model="summary" rows="4" name="summary" type="text" placeholder="摘要" class="uk-width-1-1" style="resize:none;"></textarea>
</div>
</div>
<div class="uk-form-row">
<label class="uk-form-label">内容:</label>
<div class="uk-form-controls">
<textarea v-model="content" rows="16" name="content" type="text" placeholder="内容" class="uk-width-1-1" style="resize:none;"></textarea>
</div>
</div>
<div class="uk-form-row">
<button type="submit" class="uk-button uk-button-primary"><i class="uk-icon-save"></i> 保存</button>
<a href="/manage/blogs" class="uk-button"><i class="uk-icon-times"></i> 取消</a>
</div>
</form>
</div>
{% endblock %}
注意我们默认注册的时候都不是admin账户,需要在数据库中修改权限:
之所以admin两边要加反引号是因为admin是数据库的关键字,要避免冲突,不然会报“Error 1064”错误。
然后登陆这个账户才是管理员权限,可以写博客。