// ==UserScript==
// @name Duoshuo Admin
// @name:zh-CN 多说评论管理
// @namespace http://gerald.top
// @homepage https://gerald.top/code/DuoshuoAdmin
// @author Gerald <i@gerald.top>
// @icon http://cn.gravatar.com/avatar/a0ad718d86d21262ccd6ff271ece08a3?s=80
// @description 更加强大的多说评论管理脚本,可以自动分析所有有评论的页面,方便地修改thread_key,避免评论丢失。
// @version 0.3
// @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:3px;box-shadow:0 0 5px rgba(0,0,0,.1);}
.dsa-dialog{background:white;position:absolute;top:50px;right:600px;width:400px;z-index:10;box-shadow:0 0 5px rgba(0,0,0,.1);border:1px solid gray;padding:10px;border-radius:3px;}
.dsa-dialog label{display:block;}
.dsa-dialog input[type=text],.dsa-dialog input[type=url]{display:block;width:100%;}
.dsa-panel{background:white;position:fixed;top:50px;right:0;bottom:0;width:600px;box-shadow:-2px 0 5px gray;}
.dsa-panel-body{position:absolute;top:30px;left:10px;right:10px;bottom:10px;overflow:auto;}
.dsa-panel table{width:100%;border-spacing:5px;border-collapse:separate;table-layout:fixed;}
.dsa-panel td>*{padding:5px;}
.dsa-panel .key{cursor:pointer;}
.dsa-panel .key:hover{background:olive;color:white;}
`)
.appendTo(document.head);
const menu = $('<ul class="dropdown-menu">');
$('<li class="dropdown">')
.prependTo('.navbar-nav')
.html('<a href class="dropdown-toggle" data-toggle="dropdown">评论管理<b class="caret"></b>')
.append(menu);
$('<li><a href="https://gerald.top/code/DuoshuoAdmin" target=_blank>使用须知</a></li>')
.appendTo(menu);
function EditPanel(thread) {
this.thread = thread;
[
'onSubmit',
'onClose',
'onVisitURL',
'onSolveConflict',
].forEach(key => {
this[key] = this[key].bind(this);
});
this.dialog = $('<form class="dsa-dialog">').appendTo(document.body)
.html(`
<h3>修改Thread</h3>
<label>
thread_key:
<input type=text data-id=thread_key>
</label>
<label>
title:
<input type=text data-id=title>
</label>
<label>
url: <a href="#" data-id=visit_url>[Go]</a>
<input type=url data-id=url>
</label>
<label>
<input type=checkbox data-id=post_enable>
post_enable
</label>
<button class="btn btn-primary" type="submit">保存</button>
<button class="btn btn-default" type="button" data-id=cancel>关闭</button>
<button class="btn btn-default" data-id=conflict style="display:none">修改冲突</button>
<div data-id=msg></div>
`)
.submit(this.onSubmit)
.on('click', '[data-id=visit_url]', this.onVisitURL)
.on('click', '[data-id=cancel]', this.onClose)
.on('click', '[data-id=conflict]', this.onSolveConflict)
;
this.find('thread_key').val(thread.thread_key);
this.find('title').val(thread.title);
this.find('url').val(thread.url);
this.find('post_enable').prop('checked', thread.post_enable);
}
EditPanel.panels = {};
EditPanel.open = function (thread) {
var panel = EditPanel.panels[thread.thread_id];
if (!panel) {
panel = EditPanel.panels[thread.thread_id] = new EditPanel(thread);
}
panel.dialog.appendTo(document.body);
return panel;
};
EditPanel.prototype.find = function (key) {
return this.dialog.find(`[data-id=${JSON.stringify(key)}]`);
};
EditPanel.prototype.onSubmit = function (e) {
e.preventDefault();
$.post('/api/threads/update.json', {
thread_id: this.thread.thread_id,
thread_key: this.find('thread_key').val(),
title: this.find('title').val(),
url: this.find('url').val(),
post_enable: this.find('post_enable').prop('checked') ? 1 : 0,
}, r => {
const thread = r.response;
if (thread) {
this.thread = thread;
const i = threads.findIndex(_thread => _thread.thread_id === thread.thread_id);
if (~i) threads[i] = thread;
save();
this.find('msg').html('修改成功!');
} else {
this.find('msg').html('<span style="color:red">修改失败!</span>');
const conflict = this.find('conflict').show();
this.find('thread_key').one('change', () => conflict.hide());
}
});
};
EditPanel.prototype.onVisitURL = function (e) {
window.open(this.find('url').val());
};
EditPanel.prototype.onSolveConflict = function (e) {
$.get('/api/threads/listPosts.json', {
thread_key: this.find('thread_key').val(),
limit: 1,
}, r => {
EditPanel.open(r.thread);
});
};
EditPanel.prototype.onClose = function (e) {
this.dialog.remove();
};
function getThreadByKey(key, cb) {
}
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="dsa-panel-body">').appendTo(panel);
$('<a href>显示文章列表</a>')
.appendTo($('<li>').appendTo(menu))
.click(function(e){
e.preventDefault();
panel.area.html('');
var table=$('<table>').appendTo(panel.area);
$('<colgroup><col width=100><col width=200><col></colgroup>').appendTo(table);
$('<tr><th>thread_key</th><th>title</th><th>url</th></tr>').appendTo(table);
threads.forEach(thread => {
const row = $('<tr>').appendTo(table);
$('<span class="key">')
.text(thread.thread_key)
.click(() => EditPanel.open(thread))
.wrap('<td>').parent().appendTo(row);
$('<td>').text(thread.title).appendTo(row);
$('<a target=_blank>')
.attr('href', thread.url)
.text(thread.url)
.wrap('<td>').parent().appendTo(row);
});
panel.show();
});
$('<a href>自动分析评论</a>')
.appendTo($('<li>').appendTo(menu))
.click(function(e){
e.preventDefault();
var dthreads={};
var limit=100,n=~~(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();
}
});
});