Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Initial bits for "locking" forum (sub)threads using a "closed" tag. This currently affects the display but does not hinder edits made via malicious misuse because the pieces needed for such validation do not yet have access to the relevant ForumPost objects. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | forumpost-locking |
| Files: | files | file ages | folders |
| SHA3-256: |
4d664bfe55c756acf9eed6140b7c3802 |
| User & Date: | stephan 2023-02-21 00:52:44.574 |
Context
|
2023-02-21
| ||
| 01:58 | Add capability to determine whether a given sub-thread inherits a lock from a parent. Re-label "locked" to "closed" per /chat feedback. ... (check-in: 464f4d175f user: stephan tags: forumpost-locking) | |
| 00:52 | Initial bits for "locking" forum (sub)threads using a "closed" tag. This currently affects the display but does not hinder edits made via malicious misuse because the pieces needed for such validation do not yet have access to the relevant ForumPost objects. ... (check-in: 4d664bfe55 user: stephan tags: forumpost-locking) | |
|
2023-02-20
| ||
| 14:58 | Change the typedef of u32 in search.c so that it works in older PPC macs. ... (check-in: f82caec579 user: drh tags: trunk) | |
Changes
Changes to src/default.css.
| ︙ | ︙ | |||
896 897 898 899 900 901 902 903 904 905 906 907 908 909 |
div.forumTime {
border: 1px solid black;
padding-left: 1ex;
padding-right: 1ex;
margin-top: 1ex;
display: flex;
flex-direction: column;
}
.forum div > form {
margin: 0.5em 0;
}
.forum-post-collapser {
/* Common style for the bottom-of-post and right-of-post
expand/collapse widgets. */
| > > > > > > > > > | 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 |
div.forumTime {
border: 1px solid black;
padding-left: 1ex;
padding-right: 1ex;
margin-top: 1ex;
display: flex;
flex-direction: column;
}
div.forumClosed {
border-style: dotted;
opacity: 0.7;
}
div.forumClosed > *:first-child::before {
content: "[LOCKED] ";
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. */
|
| ︙ | ︙ |
Changes to src/forum.c.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
ForumPost *pEditNext; /* This post is edited by pEditNext */
ForumPost *pEditPrev; /* This post is an edit of pEditPrev */
ForumPost *pNext; /* Next in chronological order */
ForumPost *pPrev; /* Previous in chronological order */
ForumPost *pDisplay; /* Next in display order */
int nEdit; /* Number of edits to this post */
int nIndent; /* Number of levels of indentation for this post */
};
/*
** A single instance of the following tracks all entries for a thread.
*/
struct ForumThread {
ForumPost *pFirst; /* First post in chronological order */
| > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
ForumPost *pEditNext; /* This post is edited by pEditNext */
ForumPost *pEditPrev; /* This post is an edit of pEditPrev */
ForumPost *pNext; /* Next in chronological order */
ForumPost *pPrev; /* Previous in chronological order */
ForumPost *pDisplay; /* Next in display order */
int nEdit; /* Number of edits to this post */
int nIndent; /* Number of levels of indentation for this post */
int fClosed; /* tagxref.tagtype if this (sub)thread has a closed tag. */
};
/*
** A single instance of the following tracks all entries for a thread.
*/
struct ForumThread {
ForumPost *pFirst; /* First post in chronological order */
|
| ︙ | ︙ | |||
75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
" WHERE A.fpid=$rid AND B.froot=A.froot AND B.fprev=$rid"
);
db_bind_int(&q, "$rid", rid);
res = db_step(&q)==SQLITE_ROW;
db_reset(&q);
return res;
}
/*
** Delete a complete ForumThread and all its entries.
*/
static void forumthread_delete(ForumThread *pThread){
ForumPost *pPost, *pNext;
for(pPost=pThread->pFirst; pPost; pPost = pNext){
| > > > > > > > > > > > > > > > > > | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
" WHERE A.fpid=$rid AND B.froot=A.froot AND B.fprev=$rid"
);
db_bind_int(&q, "$rid", rid);
res = db_step(&q)==SQLITE_ROW;
db_reset(&q);
return res;
}
/*
** Returns true if p, or any parent of p, has an active "closed" tag.
** Returns 0 if !p. For an edited chain of post, the tag is checked on
** the final edit in the chain, as that permits that a post can be
** locked and later unlocked.
*/
int forum_post_is_closed(ForumPost *p){
if( !p ) return 0;
if( p->pEditTail ) p = p->pEditTail;
if( p->fClosed ) return p->fClosed;
else if( p->pIrt ){
return forum_post_is_closed(p->pIrt->pEditTail
? p->pIrt->pEditTail : p->pIrt);
}
return 0;
}
/*
** Delete a complete ForumThread and all its entries.
*/
static void forumthread_delete(ForumThread *pThread){
ForumPost *pPost, *pNext;
for(pPost=pThread->pFirst; pPost; pPost = pNext){
|
| ︙ | ︙ | |||
213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
pPost->pEditPrev = p;
pPost->pEditHead = p->pEditHead ? p->pEditHead : p;
for(; p; p=p->pEditPrev ){
p->nEdit = pPost->nEdit;
p->pEditTail = pPost;
}
}
}
db_finalize(&q);
if( computeHierarchy ){
/* Compute the hierarchical display order */
pPost = pThread->pFirst;
pPost->nIndent = 1;
| > | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
pPost->pEditPrev = p;
pPost->pEditHead = p->pEditHead ? p->pEditHead : p;
for(; p; p=p->pEditPrev ){
p->nEdit = pPost->nEdit;
p->pEditTail = pPost;
}
}
pPost->fClosed = rid_has_active_tag_name(pPost->fpid, "closed");
}
db_finalize(&q);
if( computeHierarchy ){
/* Compute the hierarchical display order */
pPost = pThread->pFirst;
pPost->nIndent = 1;
|
| ︙ | ︙ | |||
298 299 300 301 302 303 304 |
fossil_print("fpid = %d\n", fpid);
fossil_print("froot = %d\n", froot);
pThread = forumthread_create(froot, 1);
fossil_print("Chronological:\n");
fossil_print(
/* 0 1 2 3 4 5 6 7 */
/* 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123 */
| | | > > | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
fossil_print("fpid = %d\n", fpid);
fossil_print("froot = %d\n", froot);
pThread = forumthread_create(froot, 1);
fossil_print("Chronological:\n");
fossil_print(
/* 0 1 2 3 4 5 6 7 */
/* 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123 */
" sid rev closed fpid pIrt pEditPrev pEditTail hash\n");
for(p=pThread->pFirst; p; p=p->pNext){
fossil_print("%4d %4d %7d %9d %9d %9d %9d %8.8s\n",
p->sid, p->rev,
p->fClosed,
p->fpid, p->pIrt ? p->pIrt->fpid : 0,
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, "");
|
| ︙ | ︙ | |||
456 457 458 459 460 461 462 463 464 465 466 467 | char *zEditorName; /* Name of user who provided the current edit */ char *zDate; /* The time/date string */ char *zHist; /* History query string */ Manifest *pManifest; /* Manifest comprising the current post */ int bPrivate; /* True for posts awaiting moderation */ int bSameUser; /* True if author is also the reader */ int iIndent; /* Indent level */ const char *zMimetype;/* Formatting MIME type */ /* Get the manifest for the post. Abort if not found (e.g. shunned). */ pManifest = manifest_get(p->fpid, CFTYPE_FORUM, 0); if( !pManifest ) return; | > | > | | | | 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 |
char *zEditorName; /* Name of user who provided the current edit */
char *zDate; /* The time/date string */
char *zHist; /* History query string */
Manifest *pManifest; /* Manifest comprising the current post */
int bPrivate; /* True for posts awaiting moderation */
int bSameUser; /* True if author is also the reader */
int iIndent; /* Indent level */
int fClosed; /* True if (sub)thread is closed */
const char *zMimetype;/* Formatting MIME type */
/* Get the manifest for the post. Abort if not found (e.g. shunned). */
pManifest = manifest_get(p->fpid, CFTYPE_FORUM, 0);
if( !pManifest ) return;
fClosed = forum_post_is_closed(p);
/* When not in raw mode, create the border around the post. */
if( !bRaw ){
/* Open the <div> enclosing the post. Set the class string to mark the post
** as selected and/or obsolete. */
iIndent = (p->pEditHead ? p->pEditHead->nIndent : p->nIndent)-1;
@ <div id='forum%d(p->fpid)' class='forumTime\
@ %s(bSelect ? " forumSel" : "")\
@ %s(fClosed ? " forumClosed" : "")\
@ %s(p->pEditTail ? " forumObs" : "")' \
if( iIndent && iIndentScale ){
@ style='margin-left:%d(iIndent*iIndentScale)ex;'>
}else{
@ >
}
/* If this is the first post (or an edit thereof), emit the thread title. */
if( pManifest->zThreadTitle ){
@ <h1>%h(pManifest->zThreadTitle)</h1>
}
/* Begin emitting the header line. The forum of the title
** varies depending on whether:
** * The post is unedited
** * The post was last edited by the original author
** * The post was last edited by a different person
*/
if( p->pEditHead ){
zDate = db_text(0, "SELECT datetime(%.17g,toLocal())",
p->pEditHead->rDate);
}else{
zPosterName = forum_post_display_name(p, pManifest);
zEditorName = zPosterName;
}
zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", p->rDate);
if( p->pEditPrev ){
zPosterName = forum_post_display_name(p->pEditHead, 0);
zEditorName = forum_post_display_name(p, pManifest);
zHist = bHist ? "" : zQuery[0]==0 ? "?hist" : "&hist";
@ <h3 class='forumPostHdr'>(%d(p->sid)\
@ .%0*d(fossil_num_digits(p->nEdit))(p->rev))
if( fossil_strcmp(zPosterName, zEditorName)==0 ){
@ By %s(zPosterName) on %h(zDate) edited from \
@ %z(href("%R/forumpost/%S%s%s",p->pEditPrev->zUuid,zQuery,zHist))\
@ %d(p->sid).%0*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a>
}else{
@ Originally by %s(zPosterName) \
@ with edits by %s(zEditorName) on %h(zDate) from \
@ %z(href("%R/forumpost/%S%s%s",p->pEditPrev->zUuid,zQuery,zHist))\
@ %d(p->sid).%0*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a>
}
}else{
zPosterName = forum_post_display_name(p, pManifest);
@ <h3 class='forumPostHdr'>(%d(p->sid))
@ By %s(zPosterName) on %h(zDate)
}
fossil_free(zDate);
/* If debugging is enabled, link to the artifact page. */
if( g.perm.Debug ){
|
| ︙ | ︙ | |||
579 580 581 582 583 584 585 |
if( !bRaw ){
/* If the user is able to write to the forum and if this post has not been
** edited, create a form with various interaction buttons. */
if( g.perm.WrForum && !p->pEditTail ){
@ <div><form action="%R/forumedit" method="POST">
@ <input type="hidden" name="fpid" value="%s(p->zUuid)">
if( !bPrivate ){
| | > > > > | | | > > | > | 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 |
if( !bRaw ){
/* If the user is able to write to the forum and if this post has not been
** edited, create a form with various interaction buttons. */
if( g.perm.WrForum && !p->pEditTail ){
@ <div><form action="%R/forumedit" method="POST">
@ <input type="hidden" name="fpid" value="%s(p->zUuid)">
if( !bPrivate ){
/* Reply and Edit are only available if the post has been
** approved. Closed threads can only be edited or replied to
** by an admin but a user may delete their own posts even if
** they are closed. */
if( g.perm.Admin || !fClosed ){
@ <input type="submit" name="reply" value="Reply">
if( g.perm.Admin || (bSameUser && !fClosed) ){
@ <input type="submit" name="edit" value="Edit">
}
if( g.perm.Admin || bSameUser ){
@ <input type="submit" name="nullout" value="Delete">
}
}
}else if( g.perm.ModForum ){
/* Allow moderators to approve or reject pending posts. Also allow
** forum supervisors to mark non-special users as trusted and therefore
** able to post unmoderated. */
@ <input type="submit" name="approve" value="Approve">
@ <input type="submit" name="reject" value="Reject">
|
| ︙ | ︙ |
Changes to src/tag.c.
| ︙ | ︙ | |||
901 902 903 904 905 906 907 |
"SELECT tag.tagid FROM tagxref, tag"
" WHERE tagxref.rid=%d AND tagtype>0 "
" AND tag.tagid=%d"
" AND tagxref.tagid=tag.tagid",
rid, tagId
);
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > | 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 |
"SELECT tag.tagid FROM tagxref, tag"
" WHERE tagxref.rid=%d AND tagtype>0 "
" AND tag.tagid=%d"
" AND tagxref.tagid=tag.tagid",
rid, tagId
);
}
/*
** Returns tagxref.tagtype if the given blob.rid has a tagxref.rid
** entry an active 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 = 0;
assert( 0 != zTagName );
db_static_prepare(&q,
"SELECT tagxref.tagtype FROM tagxref, tag"
" WHERE tagxref.rid=:rid AND tagtype>0 "
" AND tag.tagname=:tagname"
" AND tagxref.tagid=tag.tagid"
);
db_bind_int(&q, ":rid", rid);
db_bind_text(&q, ":tagname", zTagName);
if( SQLITE_ROW==db_step(&q) ){
rc = db_column_int(&q, 0);
}
db_reset(&q);
return rc;
}
|