// ==UserScript==
// @name Duoshuo Admin
// @name:zh-CN 多说评论管理
// @namespace http://gerald.top
// @author Gerald <[email protected]>
// @description 更加强大的多说评论管理脚本,可以自动分析所有有评论的页面,方便地修改thread_key,避免评论丢失。
// @version 0.1
// @match http://*.duoshuo.com/admin/*
// @grant none
// ==/UserScript==
var threads;
try {
threads=JSON.parse(localStorage.threadData);
} catch(e) {
threads=[];
}
function save(){
localStorage.threadData=JSON.stringify(threads);
}
$('<style>')
.html('\
.dsa-popup{background:white;position:fixed;top:70px;right:70px;padding:10px;border-radius:5px;box-shadow:1px 2px 5px gray;}\
.dsa-dialog{background:white;position:absolute;top:50px;right:500px;width:400px;z-index:10;box-shadow:4px 4px 5px gray;border:1px solid gray;padding:10px;border-radius:10px;}\
.dsa-dialog h4{color:purple;}\
.dsa-panel{background:white;position:fixed;top:50px;right:0;bottom:0;width:500px;box-shadow:-2px 0 5px gray;}\
.dsa-panel .area{position:absolute;top:30px;left:10px;right:10px;bottom:10px;overflow:auto;}\
.dsa-panel table{width:100%;border-spacing:5px;border-collapse:separate;}\
.dsa-panel td>*{padding:5px;}\
.dsa-panel .key{cursor:pointer;}\
.dsa-panel .key:hover{background:purple;color:white;}\
').appendTo(document.head);
var menu=$('<ul class="dropdown-menu">')
.appendTo(
$('<li class="dropdown">')
.prependTo('.navbar-nav')
.html('<a href class="dropdown-toggle" data-toggle="dropdown">评论管理<b class="caret"></b>')
);
var notice=$('<div class="dsa-dialog">').appendTo(document.body).hide()
.css({
right:300,
width:500,
}).html('\
<h3>多说评论管理脚本使用须知</h3>\
<h4>为什么要有这个脚本?</h4>\
<p>因为多说自己的管理界面很多bug而且官方似乎不打算改进了的样子。使用这个脚本可以将所有评论对应的页面提取出来,避免修改过程中有遗漏导致评论丢失的情况。</p>\
<h4>什么是自动分析评论?</h4>\
<p>脚本将自动分析该站点下所有的评论,将所有有评论的页面提取并保存下来,然后可以通过显示文章列表看到,并方便地对页面的thread_key进行管理和修改。</p>\
<h4>如何操作比较好?</h4>\
<p>先使用此脚本修改thread_key,然后修改网站页面的thread_key。(这样可以避免网站页面修改后、多说旧thread_key修改前,多说自动生成为新thread_key生成对应的新thread的问题。这么拗口不知道能不能看明白。。)</p>\
<h4>为什么有时候修改thread_key会失败?</h4>\
<p>首先要知道,多说的文章管理中,对文章的删除其实并不是真正的删除,<b>只是隐藏而已</b>,所以如果一篇被“删除”的文章占用了你想修改的thread_key,那么修改就会失败,这时需要先把旧的被“删除”的文章改一个其他的(没用的)thread_key,然后再改现在这个才能成功。</p>\
<p>如果在使用脚本修改thread_key时失败,脚本会自动弹出旧的thread让你修改。将旧的thread改成一个没用的thread_key再来改当前thread就没有问题了。</p>\
');
$('<a href>关闭</a>').prependTo(notice).css('float','right').click(function(e){
e.preventDefault();
notice.hide();
});
$('<a href>使用须知</a>')
.appendTo($('<li>').appendTo(menu))
.click(function(e){
e.preventDefault();
notice.show();
});
function getThreadByKey(key,cb){
$.get('/api/threads/listPosts.json',{
thread_key:key,
limit:1
},function(r){
cb(r.thread);
});
}
function updateKey(id,key,cb){
$.post('/api/threads/update.json',{
thread_id:id,
thread_key:key,
},function(r){
cb(r.response);
});
}
function editKey(e){
var cur=$(e.target);
var i=cur.data('num');
var thread=threads[i];
if(!thread) return;
current=cur;
editPanel.current.threadKey.val(thread.thread_key);
editPanel.current.find('.title').html($('<a target=_blank>').attr('href',thread.url).text(thread.title));
editPanel.other.hide();
editPanel.show();
}
function saveKey(e){
var i=current.data('num');
var thread=threads[i];
if(!thread) return;
updateKey(thread.thread_id,editPanel.current.threadKey.val(),function(r){
if(r){
threads[i]=r;
current.text(r.thread_key);
closeEdit();
} else {
getThreadByKey(editPanel.current.threadKey.val(),function(r){
var other=editPanel.other;
other.data('id',r.thread_id);
other.threadKey.val(r.thread_key);
other.find('.title').html($('<a target=_blank>').attr('href',r.url).text(r.title));
editPanel.current.find('.msg').html('您要使用的thread_key被以下thread占用,请先修改之。');
other.show();
});
}
});
}
function saveOtherKey(e){
var other=editPanel.other;
updateKey(other.data('id'),other.threadKey.val(),function(r){
if(r){
editPanel.current.find('.msg').html('占用thread已修改,可以继续修改当前thread。');
other.hide();
} else {
other.find('.msg').html('thread_key修改失败,请换一个thread_key试试。');
}
});
}
function closeEdit(e){
if(e) e.preventDefault();
current=null;
editPanel.fadeOut();
}
var current=null;
var editPanel=$('<div class="dsa-dialog">').appendTo(document.body).hide()
.html('\
<h3>修改Thread</h3>\
<div class=current>\
<h4>thread_key</h4>\
<div><input type=text class=thread-key style="width:100%"></div>\
<h4>title</h4>\
<p class=title></p>\
<button class="btn btn-primary save">修改</button>\
<span class=msg></span>\
</div><div class=other><hr>\
<h3>占用此thread_key的Thread</h3>\
<h4>thread_key</h4>\
<div><input type=text class=thread-key style="width:100%"></div>\
<h4>title</h4>\
<p class=title></p>\
<button class="btn btn-primary save">修改</button>\
<span class="msg"></span>\
</div>\
');
editPanel.current=editPanel.find('.current');
editPanel.current.threadKey=editPanel.current.find('.thread-key');
editPanel.current.find('.save').click(saveKey);
editPanel.other=editPanel.find('.other');
editPanel.other.threadKey=editPanel.other.find('.thread-key');
editPanel.other.find('.save').click(saveOtherKey);
$('<a href>关闭</a>').prependTo(editPanel).css('float','right').click(closeEdit);
var panel=$('<div class="dsa-panel">').appendTo(document.body).hide();
$('<a href>关闭</a>').appendTo(panel).css({
position:'absolute',
top:10,
right:20,
}).click(function(e){
e.preventDefault();
panel.hide();
});
panel.area=$('<div class="area">').appendTo(panel);
$('<a href>显示文章列表</a>')
.appendTo($('<li>').appendTo(menu))
.click(function(e){
e.preventDefault();
panel.area.html('');
var table=$('<table>').appendTo(panel.area);
$('<tr><th>thread_key</th><th>title</th></tr>').appendTo(table);
$.each(threads,function(i,o){
var row=$('<tr>').appendTo(table);
$('<span class="key">').data('num',i).text(o.thread_key).appendTo($('<td>').appendTo(row)).click(editKey);
$('<a target=_blank>').attr('href',o.url).text(o.title).appendTo($('<td>').appendTo(row));
});
panel.show();
});
$('<a href>自动分析评论</a>')
.appendTo($('<li>').appendTo(menu))
.click(function(e){
e.preventDefault();
var dthreads={};
var limit=100,n=Math.ceil(DUOSHUO.site.comments/limit),i;
var finished=0;
var popup=$('<div class="dsa-popup">').html('正在分析评论:<span></span>/'+n).appendTo(document.body);
var prog=popup.find('span').html(finished);
for(i=0;i<n;i++)
$.get('/api/posts/list.json',{
order:'desc',
source:'duoshuo,repost',
max_depth:1,
limit:limit,
'related[]':'thread',
nonce:DUOSHUO.nonce,
status:'all',
page:i+1,
},function(r){
$.each(r.response,function(i,p){
var t=p.thread;
dthreads[t.thread_id]=t;
});
prog.html(++finished);
if(finished==n) {
popup.html('评论分析完成!');
popup.fadeOut(5000,function(){
popup.remove();
});
threads=[];
for(var i in dthreads) threads.push(dthreads[i]);
threads.sort(function(a,b){return a.thread_id>b.thread_id;});
save();
}
});
});