Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Initial (untested) code for creating the control artifact for closing and re-opening forum threads. Extend test-forumthread's tree view to show thread closure. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | forumpost-locking |
| Files: | files | file ages | folders |
| SHA3-256: |
32fc62e68160783ab85ed1ceeb0ab3d2 |
| User & Date: | stephan 2023-02-21 10:30:22.137 |
Context
|
2023-02-22
| ||
| 04:46 | Rework forumpost closure to always apply to the first artifact in an edit chain to enable consistent behavior across the whole chain and responses to arbitrary versions within that chain. Add rudimentary UI elements for closing/re-opening posts, but their layout needs to be revisited (noting that they need to be in a separate form from the main editor so that closing/re-opening introduces only a smalll control artifact instead of a whole forumpost artifact). ... (check-in: cc6ca4e110 user: stephan tags: forumpost-locking) | |
|
2023-02-21
| ||
| 10:30 | Initial (untested) code for creating the control artifact for closing and re-opening forum threads. Extend test-forumthread's tree view to show thread closure. ... (check-in: 32fc62e681 user: stephan tags: forumpost-locking) | |
| 09:52 | Add db_add_unsent() and replace numerous "INSERT OR IGNORE INTO unset" statements with that. ... (check-in: 98d4ee73d7 user: stephan tags: forumpost-locking) | |
Changes
Changes to src/default.css.
| ︙ | ︙ | |||
905 906 907 908 909 910 911 912 913 914 915 916 917 918 |
opacity: 0.7;
}
div.forumClosed > *:first-child::before {
content: "[CLOSED] ";
color: red;
opacity: 0.7;
}
.forum div > form {
margin: 0.5em 0;
}
.forum-post-collapser {
/* Common style for the bottom-of-post and right-of-post
expand/collapse widgets. */
font-size: 0.8em;
| > > > | 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 |
opacity: 0.7;
}
div.forumClosed > *:first-child::before {
content: "[CLOSED] ";
color: red;
opacity: 0.7;
}
/*div.forumClosed > div.forumPostBody {
filter: blur(5px);
}*/
.forum div > form {
margin: 0.5em 0;
}
.forum-post-collapser {
/* Common style for the bottom-of-post and right-of-post
expand/collapse widgets. */
font-size: 0.8em;
|
| ︙ | ︙ |
Changes to src/forum.c.
| ︙ | ︙ | |||
88 89 90 91 92 93 94 | ** locked and later unlocked. The return value is the tagxref.rowid ** value of the tagxref entry which applies the "closed" tag, or 0 if ** no active tag is found. ** ** If bCheckParents is true then p's thread parents are checked ** (recursively) for closure, else only p is checked. */ | | | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
** locked and later unlocked. The return value is the tagxref.rowid
** value of the tagxref entry which applies the "closed" tag, or 0 if
** no active tag is found.
**
** If bCheckParents is true then p's thread parents are checked
** (recursively) for closure, else only p is checked.
*/
static int forum_post_is_closed(ForumPost *p, int bCheckParents){
if( !p ) return 0;
if( p->pEditTail ) p = p->pEditTail;
if( p->iClosed || !bCheckParents ) return p->iClosed;
else if( p->pIrt ){
return forum_post_is_closed(p->pIrt->pEditTail
? p->pIrt->pEditTail : p->pIrt,
bCheckParents);
|
| ︙ | ︙ | |||
140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
rid = SQLITE_ROW==db_step(&qIrt) ? db_column_int(&qIrt, 0) : 0;
db_reset(&qIrt);
if( rid ){
rc = forum_rid_is_closed(rid, 1);
}
return rc>0 ? -rc : rc;
}
/*
** If iClosed is true and the current user has admin privileges, this
** renders either a checkbox to unlock forum post fpid (if iClosed>0)
** or a SPAN.warning element that the given post inherits the CLOSED
** status from a parent post (if iClosed<0). If neither of the initial
** conditions is true, this is a no-op.
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
rid = SQLITE_ROW==db_step(&qIrt) ? db_column_int(&qIrt, 0) : 0;
db_reset(&qIrt);
if( rid ){
rc = forum_rid_is_closed(rid, 1);
}
return rc>0 ? -rc : rc;
}
/*
** UNTESTED!
**
** Closes or re-opens the given forum RID via addition of a new
** control artifact into the repository.
**
** If doClose is true then a propagating "closed" tag is added, except
** as noted below, with the given optional zReason string as the tag's
** value. If doClose is false then any active "closed" tag on frid is
** cancelled, except as noted below. zReason is ignored if doClose is
** false or if zReason is NULL or starts with a NUL byte.
**
** This function only adds a "closed" tag to frid if
** forum_rid_is_closed() indicates that frid is not closed. If a
** parent post is already closed, no tag is added. Similarly, it will
** only remove a "closed" tag from a post which has its own "closed"
** tag, and will not remove an inherited one from a parent post.
**
** If doClose is true and frid is closed (directly or inherited), this
** is a no-op. Likewise, if doClose is false and frid itself is not
** closed (not accounting for an inherited closed tag), this is a
** no-op.
**
** Returns true if it actually creates a new tag, else false. Fails
** fatally on error. If it returns true then any ForumPost::iClosed
** values from previously loaded posts are invalidated if they refer
** to the amended post or a response to it.
**
** Sidebars:
**
** - Unless the caller has a transaction open, via
** db_begin_transaction(), there is a very tiny race condition
** window during which the caller's idea of whether or not the forum
** post is closed may differ from the current repository state.
**
** - This routine assumes that frid really does refer to a forum post.
**
** - This routine assumes that frid is not private or pending
** moderation.
**
** - Closure of a forum post requires a propagating "closed" tag to
** account for how edits of posts are handled. This differs from
** closure of a branch, where a non-propagating tag is used.
*/
/*static*/ int forumpost_close(int frid, int doClose, const char *zReason){
Blob artifact = BLOB_INITIALIZER; /* Output artifact */
Blob cksum = BLOB_INITIALIZER; /* Z-card */
int iClosed; /* true if frid is closed */
int trid; /* RID of new control artifact */
db_begin_transaction();
iClosed = forum_rid_is_closed(frid, 1);
if( (iClosed && doClose
/* Already closed, noting that in the case of (iClosed<0), it's
** actually a parent which is closed. */)
|| (iClosed<=0 && !doClose
/* This entry is not closed, but a parent post may be. */) ){
db_end_transaction(0);
return 0;
}
if( doClose==0 || (zReason && !zReason[0]) ){
zReason = 0;
}
blob_appendf(&artifact, "D %z\n", date_in_standard_format( "now" ));
blob_appendf(&artifact,
"T %cclosed %z%s%F\n",
doClose ? '*' : '-', rid_to_uuid(frid),
zReason ? " " : "", zReason ? zReason : "");
blob_appendf(&artifact, "U %F\n", login_name());
md5sum_blob(&artifact, &cksum);
blob_appendf(&artifact, "Z %b\n", &cksum);
blob_reset(&cksum);
trid = content_put_ex(&artifact, 0, 0, 0, 0);
if( trid==0 ){
fossil_fatal("Error saving tag artifact: %s", g.zErrMsg);
}
if( manifest_crosslink(trid, &artifact,
MC_NONE /*MC_PERMIT_HOOKS?*/)==0 ){
fossil_fatal("%s", g.zErrMsg);
}
assert( blob_is_reset(&artifact) );
db_add_unsent(trid);
/* Potential TODO: if (iClosed>0) then we could find the initial tag
** artifact and content_deltify(thatRid,&trid,1,0). Given the tiny
** size of these artifacts, however, that would save little space,
** if any. */
db_end_transaction(0);
return 1;
}
/*
** If iClosed is true and the current user has admin privileges, this
** renders either a checkbox to unlock forum post fpid (if iClosed>0)
** or a SPAN.warning element that the given post inherits the CLOSED
** status from a parent post (if iClosed<0). If neither of the initial
** conditions is true, this is a no-op.
|
| ︙ | ︙ | |||
405 406 407 408 409 410 411 |
p->pEditPrev ? p->pEditPrev->fpid : 0,
p->pEditTail ? p->pEditTail->fpid : 0, p->zUuid);
}
fossil_print("\nDisplay\n");
for(p=pThread->pDisplay; p; p=p->pDisplay){
fossil_print("%*s", (p->nIndent-1)*3, "");
if( p->pEditTail ){
| | | > > > > | 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 |
p->pEditPrev ? p->pEditPrev->fpid : 0,
p->pEditTail ? p->pEditTail->fpid : 0, p->zUuid);
}
fossil_print("\nDisplay\n");
for(p=pThread->pDisplay; p; p=p->pDisplay){
fossil_print("%*s", (p->nIndent-1)*3, "");
if( p->pEditTail ){
fossil_print("%d->%d", p->fpid, p->pEditTail->fpid);
}else{
fossil_print("%d", p->fpid);
}
if( p->iClosed ){
fossil_print(" [closed%s]", p->iClosed<0 ? " via parent" : "");
}
fossil_print("\n");
}
forumthread_delete(pThread);
}
/*
** Render a forum post for display
*/
|
| ︙ | ︙ |
Changes to src/tag.c.
| ︙ | ︙ | |||
908 909 910 911 912 913 914 915 916 917 918 919 920 921 |
/*
** Returns tagxref.rowid if the given blob.rid has a tagxref.rid entry
** of an active (non-cancelled) tag matching the given rid and tag
** name string, else returns 0. Note that this function does not
** distinguish between a non-existent tag and a cancelled tag.
*/
int rid_has_active_tag_name(int rid, const char *zTagName){
static Stmt q = empty_Stmt_m;
int rc;
assert( 0 != zTagName );
if( !q.pStmt ){
| > > > > | 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 |
/*
** Returns tagxref.rowid if the given blob.rid has a tagxref.rid entry
** of an active (non-cancelled) tag matching the given rid and tag
** name string, else returns 0. Note that this function does not
** distinguish between a non-existent tag and a cancelled tag.
**
** Design note: the return value is the tagxref.rowid because that
** gives us an easy way to fetch the value of the tag later on, if
** needed.
*/
int rid_has_active_tag_name(int rid, const char *zTagName){
static Stmt q = empty_Stmt_m;
int rc;
assert( 0 != zTagName );
if( !q.pStmt ){
|
| ︙ | ︙ |