快点击屏幕顶部或者右下角的 体验一下新的外观设置面板吧~面板底部靠左的按钮即可用于前台登陆。
现在许多主题都使用 PJAX 或者其他技术让整个站点变成了一个单页应用,主要目的是让浏览体验更加连贯。但是 Typecho 毕竟是比较传统的程序,某些 API 是缺乏的(Wordpress 有 REST API),许多东西还是要靠登陆后台完成,例如回复读者评论,总免不了要去登陆页面溜达一圈,有点烦人;为了安全性,也不建议将后台路径暴露出来。
我去年写了一篇 Typecho 前台登录、Gist 页面上线,在页面上仿照 Typecho 的 admin.php 添加一个 form 来实现前台登陆,效果不错。但是后来发现 PJAX 切换页面后就不行了,需要刷新一下才能登陆…… 不过也没管了。
在 VOID 进入第三版之际我想把这个功能加回来,研究后发现其实很简单……Typecho 给每个页面分配的 loginAction 都不同,因此如果希望在某页面登陆后跳转回当前页面,loginAction 需要与页面链接、referer 相匹配。
如果 PJAX 的容器不包含 form 元素,那么切换页面后 loginAction 与 referer 都还是老的,因此处理时只需要检测页面是否经过了 PJAX 跳转,如果跳转了,那么这两个参数就要更新一下。更新也很简单,发起一个 AJAX 后台请求一下当前页面,从返回的 HTML 里把新的 loginAction 挑出来,并且把 referer 设置成当前页面。
以下是简单的代码说明。form 元素依旧与之前相同,并加上一个 id 方便区分:
<form action="<?php $this->options->loginAction(); ?>" id="login-form" method="post" name="login" role="form">
<input type="text" name="name" autocomplete="username" placeholder="请输入用户名" required/>
<input type="password" name="password" autocomplete="current-password" placeholder="请输入密码" required/>
<input type="hidden" name="referer" value="<?php
if($this->is('index')) $this->options->siteUrl();
else $this->permalink();
?>">
<button class="btn btn-normal" type="submit">登录</button>
</form>
前台在 PJAX 完成时添加一个标志记录一下:
$(document).on('pjax:complete', function () {
$('form#login-form').addClass('need-refresh');
});
然后在合适的时候(比如唤起登陆框时)执行以下更新代码:
if ($('#login-form').hasClass('need-refresh')) {
$.get({
url: location.href,
success: function (data) {
$('form#login-form').attr('action', $(data).find('form#login-form').attr('action'));
$('form#login-form').removeClass('need-refresh');
},
error: function () {
alert('请求登陆参数错误。请在刷新后尝试登陆。');
setTimeout(function () {
location.reload();
}, 1000);
}
});
}
当然你可以写一些 CSS 来提示用户当前在请求新的登陆参数,需要稍等一下。比如:
form#login-form.need-refresh {
opacity: 0.5;
}
上面的方法每次请求都要重载整个页面,给服务端带来了额外负担。经评论区提示,可改用 POST 请求当前页面地址,并在后端针对响应,免去执行其他无用代码。例如:
$.ajax({
type: 'POST',
url: window.location.href,
data: {action: 'getLoginAction'},
success: function (data) {
$('form#loggin-form').attr('action', data);
$('form#loggin-form').removeClass('need-refresh');
},
error: function () {
alert('请求登陆参数错误。请在刷新后尝试登陆。');
setTimeout(function () {
location.reload();
}, 1000);
}
});
注意这里不要使用 GET 请求,因为参数不同生成的 loginAction 也不同,GET 请求不方便随意带参数。在后端主题代码的入口处(例如 header.php 或者 head.php 顶部)添加:
if (isset($_POST['action'])) {
if ($_POST['action'] == 'getLoginAction') {
echo $this->options->loginAction;
exit;
}
}
这也勉强算是 VOID 开发笔记的一篇吧…… 就酱。