|
1
|
/* |
|
2
|
** Copyright (c) 2012 D. Richard Hipp |
|
3
|
** |
|
4
|
** This program is free software; you can redistribute it and/or |
|
5
|
** modify it under the terms of the Simplified BSD License (also |
|
6
|
** known as the "2-Clause License" or "FreeBSD License".) |
|
7
|
** |
|
8
|
** This program is distributed in the hope that it will be useful, |
|
9
|
** but without any warranty; without even the implied warranty of |
|
10
|
** merchantability or fitness for a particular purpose. |
|
11
|
** |
|
12
|
** Author contact information: |
|
13
|
** [email protected] |
|
14
|
** http://www.hwaci.com/drh/ |
|
15
|
** |
|
16
|
******************************************************************************* |
|
17
|
** |
|
18
|
** This file contains code used to deal with moderator actions for |
|
19
|
** Wiki and Tickets. |
|
20
|
*/ |
|
21
|
#include "config.h" |
|
22
|
#include "moderate.h" |
|
23
|
#include <assert.h> |
|
24
|
|
|
25
|
/* |
|
26
|
** Create a table to represent pending moderation requests, if the |
|
27
|
** table does not already exist. |
|
28
|
*/ |
|
29
|
void moderation_table_create(void){ |
|
30
|
db_multi_exec( |
|
31
|
"CREATE TABLE IF NOT EXISTS repository.modreq(\n" |
|
32
|
" objid INTEGER PRIMARY KEY,\n" /* Record pending approval */ |
|
33
|
" attachRid INT,\n" /* Object attached */ |
|
34
|
" tktid TEXT\n" /* Associated ticket id */ |
|
35
|
");\n" |
|
36
|
); |
|
37
|
} |
|
38
|
|
|
39
|
/* |
|
40
|
** Return TRUE if the modreq table exists |
|
41
|
*/ |
|
42
|
int moderation_table_exists(void){ |
|
43
|
return db_table_exists("repository", "modreq"); |
|
44
|
} |
|
45
|
|
|
46
|
/* |
|
47
|
** Return TRUE if the object specified is being held for moderation. |
|
48
|
*/ |
|
49
|
int moderation_pending(int rid){ |
|
50
|
static Stmt q; |
|
51
|
int rc; |
|
52
|
if( rid==0 || !moderation_table_exists() ) return 0; |
|
53
|
db_static_prepare(&q, "SELECT 1 FROM modreq WHERE objid=:objid"); |
|
54
|
db_bind_int(&q, ":objid", rid); |
|
55
|
rc = db_step(&q)==SQLITE_ROW; |
|
56
|
db_reset(&q); |
|
57
|
return rc; |
|
58
|
} |
|
59
|
|
|
60
|
/* |
|
61
|
** If the rid object is being held for moderation, write out |
|
62
|
** an "awaiting moderation" message and return true. |
|
63
|
** |
|
64
|
** If the object is not being held for moderation, simply return |
|
65
|
** false without generating any output. |
|
66
|
*/ |
|
67
|
int moderation_pending_www(int rid){ |
|
68
|
int pending = moderation_pending(rid); |
|
69
|
if( pending ){ |
|
70
|
@ <span class="modpending">(Awaiting Moderator Approval)</span> |
|
71
|
} |
|
72
|
return pending; |
|
73
|
} |
|
74
|
|
|
75
|
|
|
76
|
/* |
|
77
|
** Return TRUE if there any pending moderation requests. |
|
78
|
*/ |
|
79
|
int moderation_needed(void){ |
|
80
|
if( !moderation_table_exists() ) return 0; |
|
81
|
return db_exists("SELECT 1 FROM modreq"); |
|
82
|
} |
|
83
|
|
|
84
|
/* |
|
85
|
** Check to see if the object identified by RID is used for anything. |
|
86
|
*/ |
|
87
|
static int object_used(int rid){ |
|
88
|
static const char *const aTabField[] = { |
|
89
|
"modreq", "attachRid", |
|
90
|
"mlink", "mid", |
|
91
|
"mlink", "fid", |
|
92
|
"tagxref", "srcid", |
|
93
|
"tagxref", "rid", |
|
94
|
}; |
|
95
|
int i; |
|
96
|
for(i=0; i<count(aTabField); i+=2){ |
|
97
|
if( db_exists("SELECT 1 FROM \"%w\" WHERE \"%w\"=%d", |
|
98
|
aTabField[i], aTabField[i+1], rid) ) return 1; |
|
99
|
} |
|
100
|
return 0; |
|
101
|
} |
|
102
|
|
|
103
|
/* |
|
104
|
** Delete a moderation item given by objid |
|
105
|
*/ |
|
106
|
void moderation_disapprove(int objid){ |
|
107
|
Stmt q; |
|
108
|
char *zTktid; |
|
109
|
int attachRid = 0; |
|
110
|
int rid; |
|
111
|
if( !moderation_pending(objid) ) return; |
|
112
|
db_begin_transaction(); |
|
113
|
rid = objid; |
|
114
|
while( rid && content_is_private(rid) ){ |
|
115
|
db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid); |
|
116
|
while( db_step(&q)==SQLITE_ROW ){ |
|
117
|
int ridUser = db_column_int(&q, 0); |
|
118
|
content_undelta(ridUser); |
|
119
|
} |
|
120
|
db_finalize(&q); |
|
121
|
db_multi_exec( |
|
122
|
"DELETE FROM blob WHERE rid=%d;" |
|
123
|
"DELETE FROM delta WHERE rid=%d;" |
|
124
|
"DELETE FROM event WHERE objid=%d;" |
|
125
|
"DELETE FROM tagxref WHERE rid=%d;" |
|
126
|
"DELETE FROM private WHERE rid=%d;" |
|
127
|
"DELETE FROM attachment WHERE attachid=%d;", |
|
128
|
rid, rid, rid, rid, rid, rid |
|
129
|
); |
|
130
|
if( db_table_exists("repository","forumpost") ){ |
|
131
|
db_multi_exec("DELETE FROM forumpost WHERE fpid=%d", rid); |
|
132
|
} |
|
133
|
zTktid = db_text(0, "SELECT tktid FROM modreq WHERE objid=%d", rid); |
|
134
|
if( zTktid && zTktid[0] ){ |
|
135
|
ticket_rebuild_entry(zTktid); |
|
136
|
fossil_free(zTktid); |
|
137
|
} |
|
138
|
attachRid = db_int(0, "SELECT attachRid FROM modreq WHERE objid=%d", rid); |
|
139
|
if( rid==objid ){ |
|
140
|
db_multi_exec("DELETE FROM modreq WHERE objid=%d", rid); |
|
141
|
} |
|
142
|
if( attachRid && object_used(attachRid) ) attachRid = 0; |
|
143
|
admin_log("Disapproved moderation of rid %d.", rid); |
|
144
|
rid = attachRid; |
|
145
|
} |
|
146
|
db_end_transaction(0); |
|
147
|
} |
|
148
|
|
|
149
|
/* |
|
150
|
** Approve an object held for moderation. |
|
151
|
*/ |
|
152
|
void moderation_approve(char class, int rid){ |
|
153
|
if( !moderation_pending(rid) ) return; |
|
154
|
db_begin_transaction(); |
|
155
|
db_multi_exec( |
|
156
|
"DELETE FROM private WHERE rid=%d;" |
|
157
|
"INSERT OR IGNORE INTO unclustered VALUES(%d);" |
|
158
|
"INSERT OR IGNORE INTO unsent VALUES(%d);", |
|
159
|
rid, rid, rid |
|
160
|
); |
|
161
|
db_multi_exec("DELETE FROM modreq WHERE objid=%d", rid); |
|
162
|
admin_log("Approved moderation of rid %c-%d.", class, rid); |
|
163
|
if( class!='a' ) search_doc_touch(class, rid, 0); |
|
164
|
setup_incr_cfgcnt(); |
|
165
|
db_end_transaction(0); |
|
166
|
} |
|
167
|
|
|
168
|
/* |
|
169
|
** WEBPAGE: modreq |
|
170
|
** |
|
171
|
** Show all pending moderation request |
|
172
|
*/ |
|
173
|
void modreq_page(void){ |
|
174
|
Blob sql; |
|
175
|
Stmt q; |
|
176
|
|
|
177
|
login_check_credentials(); |
|
178
|
if( !g.perm.ModWiki && !g.perm.ModTkt && !g.perm.ModForum ){ |
|
179
|
login_needed(g.anon.ModWiki && g.anon.ModTkt && g.anon.ModForum); |
|
180
|
return; |
|
181
|
} |
|
182
|
style_header("Pending Moderation Requests"); |
|
183
|
@ <h2>All Pending Moderation Requests</h2> |
|
184
|
if( moderation_table_exists() ){ |
|
185
|
blob_init(&sql, timeline_query_for_www(), -1); |
|
186
|
blob_append_sql(&sql, |
|
187
|
" AND event.objid IN (SELECT objid FROM modreq)" |
|
188
|
" ORDER BY event.mtime DESC" |
|
189
|
); |
|
190
|
db_prepare(&q, "%s", blob_sql_text(&sql)); |
|
191
|
www_print_timeline(&q, 0, 0, 0, 0, 0, 0, 0); |
|
192
|
db_finalize(&q); |
|
193
|
} |
|
194
|
style_finish_page(); |
|
195
|
} |
|
196
|
|
|
197
|
/* |
|
198
|
** Disapproves any entries in the modreq table which belong to any |
|
199
|
** user whose name is no longer found in the user table. This is only |
|
200
|
** intended to be called after user deletion via /setup_uedit. |
|
201
|
** |
|
202
|
** To figure out whether a name exists it cross-references |
|
203
|
** coalesce(event.euser, event.user) with user.login, limiting the |
|
204
|
** selection to event entries where objid matches an entry in the |
|
205
|
** modreq table. |
|
206
|
** |
|
207
|
** This is a no-op if called without g.perm.Admin permissions or if |
|
208
|
** moderation_table_exists() returns false. |
|
209
|
*/ |
|
210
|
void moderation_disapprove_for_missing_users(){ |
|
211
|
Stmt q; |
|
212
|
if( !g.perm.Admin || !moderation_table_exists() ){ |
|
213
|
return; |
|
214
|
} |
|
215
|
db_begin_transaction(); |
|
216
|
db_prepare(&q, |
|
217
|
"SELECT objid FROM event WHERE objid IN " |
|
218
|
"(SELECT objid FROM modreq) " |
|
219
|
"AND coalesce(euser,user) NOT IN " |
|
220
|
"(SELECT login FROM user)" |
|
221
|
); |
|
222
|
while( db_step(&q)==SQLITE_ROW ){ |
|
223
|
int const objid = db_column_int(&q, 0); |
|
224
|
moderation_disapprove(objid); |
|
225
|
} |
|
226
|
db_finalize(&q); |
|
227
|
setup_incr_cfgcnt(); |
|
228
|
db_end_transaction(0); |
|
229
|
} |
|
230
|
|