Typecho自带了文章加密(给文章设置访问密码),但是存在奇怪的bug,以及不兼容PJAX。
403
状态码,所以无法PJAX跳转页面。一如既往地,网上没有找到相关教程QAQ,只能自己瞎搞了。
我目前找到唯一能够解决PJAX跳转问题的只有泽泽社长的插件,看了下实现方式感觉不能直接移植进主题中。不过我发现该插件半路阻止了Typecho原先的加密文章策略,这可能就是解决了403
的原因(在Typecho返回403
状态码之前结束了该策略),根据这个猜测我们就可以采取类似的方式来解决了!
是的,它非常的暴力,只需要在functions.php
中的themeInit
函数最前面加上:
function themeInit($archive){
if ($archive->hidden) header('HTTP/1.1 200 OK');
//$archive->hidden判断是否是加密文章
//强行返回200状态码
}
这样就可以顺利的用PJAX跳转进加密文章了。
首先我们参考泽泽社长的教程,自定义密码验证表单(下面是MDUI2333中的模板):
<?php if ($this->hidden){ ?>
<form action="<?php echo Typecho_Widget::widget('Widget_Security')->getTokenUrl($this->permalink); ?>" method="post" class="mdui-center" id="password-form">
<div class="mdui-valign mdui-center" style="width:100%;max-width:500px;">
<div class="mdui-textfield" style="display:inline-block;width:100%;margin-right:8px;">
<input class="mdui-textfield-input" type="text" name="protectPassword" placeholder="请输入密码访问" />
<?php if ($this->fields->passwordhint){ ?><div class="mdui-textfield-helper">提示:<?php echo $this->fields->passwordhint; ?></div><?php } ?>
</div>
<input type="hidden" name="protectCID" value="<?php $this->cid(); ?>" />
<button class="mdui-btn mdui-btn-icon mdui-color-theme-accent" type="submit" mdui-tooltip="{content:'提交密码',position:'top'}"><i class="mdui-icon material-icons"></i></button>
</div>
</form>
<?php } else $this->content(); ?>
然后很可怜的是,PJAX又双叒叕跪了,因为PJAX跳转时密码验证的链接(getTokenUrl
)无法正确的获取,这时候我们参考熊猫小A的方法,采用AJAX获取正确的链接,然后用AJAX提交表单。
先在后端写上对应的接口,在functions.php
的themeInit
函数随便什么位置加上下面的代码:
function themeInit($archive){
if ($archive->is('post') && $_SERVER['REQUEST_METHOD']=='POST' && $posts['type']=='getTokenUrl')
die(Typecho_Widget::widget('Widget_Security')->getTokenUrl($archive->permalink));
}
前端对应的js
代码(信息提示部分如mdui.alert
需要自定义,下同):
$('#password-form').submit(function(){
var passworddata=$(this).serializeArray();
$.ajax({
type:'POST',url:window.location.href,data:{'type':'getTokenUrl'},
success:function(tokenurl){ //tokenurl即获取的链接
//这里是第二层AJAX
},
error:function() {mdui.alert('发生了未知错误,请刷新页面');}
});
return false;
});
成功获取链接之后,采用AJAX提交密码:
$('#password-form').submit(function(){
var passworddata=$(this).serializeArray();
$.ajax({
type:'POST',url:window.location.href,data:{'type':'getTokenUrl'},
success:function(tokenurl){
$.ajax({
type:'POST',url:tokenurl,data:passworddata,
success:function(data){
//这里是第三层AJAX
},
error:function() {mdui.alert('发生了未知错误,请刷新页面');}
});
},
error:function() {mdui.alert('发生了未知错误,请刷新页面');}
});
return false;
});
不过,AJAX提交显然还是避免不了密码错乱的问题,所以需要第三层AJAX。
密码错乱只是影响了返回的结果(输入了正确的密码,并且Cookie已经记录了,却还是返回密码输入错误),因此我们不根据返回的结果判断密码是否正确,而是自己写一个接口。
在functions.php
的themeInit
函数随便什么位置加上下面的代码:
//printjson和printarray函数在下面会用到
function printjson($json) {header('Content-type:application/json;charset=utf-8');die($json);}
function printarray($data) {printjson(json_encode($data));}
function themeInit($archive){
if ($archive->is('post') && $_SERVER['REQUEST_METHOD']=='POST' && $posts['type']=='ishidden')
printarray(array('hidden'=>$archive->hidden));
}
然后根据返回的hidden
值(true/false
)就可以得知密码是否正确:
$('#password-form').submit(function(){
var passworddata=$(this).serializeArray();
$.ajax({
type:'POST',url:window.location.href,data:{'type':'getTokenUrl'},
success:function(tokenurl){
$.ajax({
type:'POST',url:tokenurl,data:passworddata,
success:function(data){
$.ajax({
type:'POST',url:window.location.href,data:{'type':'ishidden'},
success:function(data){
if (data.hidden) mdui.alert('对不起,您输入的密码错误'); //文章仍为加密状态,说明密码错误
else $.pjax({url:window.location.href,container:'#pjax-container',fragment:'#pjax-container',timeout:8000});
//文章不是加密状态,PJAX刷新页面
},
error:function() {mdui.alert('发生了未知错误,请刷新页面');}
});
},
error:function() {mdui.alert('发生了未知错误,请刷新页面');}
});
},
error:function() {mdui.alert('发生了未知错误,请刷新页面');}
});
return false;
});
将上面的代码整合,得到完整的代码如下(部分内容请根据主题自行修改):
后端(functions.php
):
function printjson($json) {header('Content-type:application/json;charset=utf-8');die($json);}
function printarray($data) {printjson(json_encode($data));}
function themeInit($archive){
if ($archive->hidden) header('HTTP/1.1 200 OK');
//其他内容
if ($archive->is('post') && $_SERVER['REQUEST_METHOD']=='POST' && $posts['type']=='getTokenUrl')
die(Typecho_Widget::widget('Widget_Security')->getTokenUrl($archive->permalink));
if ($archive->is('post') && $_SERVER['REQUEST_METHOD']=='POST' && $posts['type']=='ishidden')
printarray(array('hidden'=>$archive->hidden));
}
前端:
<?php if ($this->hidden){ ?>
<form class="mdui-center" id="password-form">
<div class="mdui-valign mdui-center" style="width:100%;max-width:500px;">
<div class="mdui-textfield" style="display:inline-block;width:100%;margin-right:8px;">
<input class="mdui-textfield-input" type="text" name="protectPassword" placeholder="请输入密码访问" />
<?php if ($this->fields->passwordhint){ ?><div class="mdui-textfield-helper">提示:<?php echo $this->fields->passwordhint; ?></div><?php } ?>
</div>
<input type="hidden" name="protectCID" value="<?php $this->cid(); ?>" />
<button class="mdui-btn mdui-btn-icon mdui-color-theme-accent" type="submit" mdui-tooltip="{content:'提交密码',position:'top'}"><i class="mdui-icon material-icons"></i></button>
</div>
</form>
<script>
$('#password-form').submit(function(){
var passworddata=$(this).serializeArray();
$.ajax({
type:'POST',url:window.location.href,data:{'type':'getTokenUrl'},
success:function(tokenurl){
$.ajax({
type:'POST',url:tokenurl,data:passworddata,
success:function(data){
$.ajax({
type:'POST',url:window.location.href,data:{'type':'ishidden'},
success:function(data){
if (data.hidden) mdui.alert('对不起,您输入的密码错误');
else $.pjax({url:window.location.href,container:'#pjax-container',fragment:'#pjax-container',timeout:8000});
},
error:function() {mdui.alert('发生了未知错误,请刷新页面');}
});
},
error:function() {mdui.alert('发生了未知错误,请刷新页面');}
});
},
error:function() {mdui.alert('发生了未知错误,请刷新页面');}
})
return false;
});
</script>
<?php } else $this->content(); ?>
至此就解决了!可以跳转到这篇文章进行测试,如果PJAX跳转、AJAX密码验证都成功了就说明这篇教程可行啦2333。
发现进你那个测试文章页面,页面直接刷新了,没有pjax效果了,可以尝试这样的方式强制修改其状态码 😄
@泽泽
震惊,可能是Typecho升级之后烂掉了,有空试试这样改
第一层ajax就报错了 😭 ,两年了 博主有新方案吗 我用的MoOx
/
pjax,不是jquery那个
@走火入魔
其他的ajax/pjax我没用过,可能不太懂
而且我也已经很久没有写前后端的东西了,现在也不太能提供帮助
文章加密码?有问题啊小老弟
@初夏阳光
我不是,我没有,别乱说
欸,好像有点复杂了..
其实可以直接判断请求是否由 PJAX 发起的,如果是则设置状态码为 200 (如果强行设置为 200 的话可能会被爬虫抓取?)
密码验证我是直接获取密码提交表单的链接,直接提交,然后取返回结果的
@ohmyga
emm 没看见是多篇文章会错乱(
@ohmyga
我自己也觉得这套娃好复杂啊 😭
其实我试了下几个主题,只有您的Castle不会错乱,不知道什么原因,我有空再看看吧