Fossil SCM
Add the ability to display the history of edits to a single timeline post. Improvements to the CSS for timeline display in the default skin.
Commit
34d8d7e714a8e9f0ec785906a2cd9da01faac680d623612e6ab60a37f38b850a
Parent
bed0dd1a956c088…
2 files changed
+1
-1
+102
-1
+1
-1
| --- skins/default/css.txt | ||
| +++ skins/default/css.txt | ||
| @@ -145,11 +145,11 @@ | ||
| 145 | 145 | border-radius: 5px; |
| 146 | 146 | } |
| 147 | 147 | .content blockquote { |
| 148 | 148 | padding: 0 15px; |
| 149 | 149 | } |
| 150 | -div.forumHierRoot blockquote, div.forumHier blockquote, div.forumEdit blockquote { | |
| 150 | +div.forumHierRoot blockquote, div.forumHier blockquote, div.forumEdit blockquote, div.forumTime blockquote, div.forumTimeline blockquote { | |
| 151 | 151 | background-color: rgba(65, 131, 196, 0.1); |
| 152 | 152 | border-left: 3px solid #254769; |
| 153 | 153 | padding: .1em 1em; |
| 154 | 154 | } |
| 155 | 155 | |
| 156 | 156 |
| --- skins/default/css.txt | |
| +++ skins/default/css.txt | |
| @@ -145,11 +145,11 @@ | |
| 145 | border-radius: 5px; |
| 146 | } |
| 147 | .content blockquote { |
| 148 | padding: 0 15px; |
| 149 | } |
| 150 | div.forumHierRoot blockquote, div.forumHier blockquote, div.forumEdit blockquote { |
| 151 | background-color: rgba(65, 131, 196, 0.1); |
| 152 | border-left: 3px solid #254769; |
| 153 | padding: .1em 1em; |
| 154 | } |
| 155 | |
| 156 |
| --- skins/default/css.txt | |
| +++ skins/default/css.txt | |
| @@ -145,11 +145,11 @@ | |
| 145 | border-radius: 5px; |
| 146 | } |
| 147 | .content blockquote { |
| 148 | padding: 0 15px; |
| 149 | } |
| 150 | div.forumHierRoot blockquote, div.forumHier blockquote, div.forumEdit blockquote, div.forumTime blockquote, div.forumTimeline blockquote { |
| 151 | background-color: rgba(65, 131, 196, 0.1); |
| 152 | border-left: 3px solid #254769; |
| 153 | padding: .1em 1em; |
| 154 | } |
| 155 | |
| 156 |
+102
-1
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -501,10 +501,101 @@ | ||
| 501 | 501 | manifest_destroy(pPost); |
| 502 | 502 | @ </div> |
| 503 | 503 | } |
| 504 | 504 | forumthread_delete(pThread); |
| 505 | 505 | } |
| 506 | +/* | |
| 507 | +** Display all the edit history of post "target". | |
| 508 | +*/ | |
| 509 | +static void forum_display_history(int froot, int target, int bRawMode){ | |
| 510 | + ForumThread *pThread = forumthread_create(froot, 0); | |
| 511 | + ForumEntry *p; | |
| 512 | + int notAnon = login_is_individual(); | |
| 513 | + char cMode = bRawMode ? 'r' : 'c'; | |
| 514 | + ForumEntry *pLeaf = 0; | |
| 515 | + int cnt = 0; | |
| 516 | + for(p=pThread->pFirst; p; p=p->pNext){ | |
| 517 | + if( p->fpid==target ){ | |
| 518 | + pLeaf = p->pLeaf ? p->pLeaf : p; | |
| 519 | + break; | |
| 520 | + } | |
| 521 | + } | |
| 522 | + for(p=pThread->pFirst; p; p=p->pNext){ | |
| 523 | + char *zDate; | |
| 524 | + Manifest *pPost; | |
| 525 | + int isPrivate; /* True for posts awaiting moderation */ | |
| 526 | + int sameUser; /* True if author is also the reader */ | |
| 527 | + const char *zUuid; | |
| 528 | + char *zDisplayName; /* The display name */ | |
| 529 | + | |
| 530 | + if( p->fpid!=pLeaf->fpid && p->pLeaf!=pLeaf ) continue; | |
| 531 | + cnt++; | |
| 532 | + pPost = manifest_get(p->fpid, CFTYPE_FORUM, 0); | |
| 533 | + if( pPost==0 ) continue; | |
| 534 | + @ <div id="forum%d(p->fpid)" class="forumTime"> | |
| 535 | + zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); | |
| 536 | + zDisplayName = display_name_from_login(pPost->zUser); | |
| 537 | + @ <h3 class='forumPostHdr'>(%d(p->sid)) By %h(zDisplayName) on %h(zDate) | |
| 538 | + fossil_free(zDisplayName); | |
| 539 | + fossil_free(zDate); | |
| 540 | + if( g.perm.Debug ){ | |
| 541 | + @ <span class="debug">\ | |
| 542 | + @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span> | |
| 543 | + } | |
| 544 | + if( p->firt && cnt==1 ){ | |
| 545 | + ForumEntry *pIrt = p->pPrev; | |
| 546 | + while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev; | |
| 547 | + if( pIrt ){ | |
| 548 | + @ in reply to %z(href("%R/forumpost/%S?t=%c",pIrt->zUuid,cMode))\ | |
| 549 | + @ %d(pIrt->sid)</a> | |
| 550 | + } | |
| 551 | + } | |
| 552 | + zUuid = p->zUuid; | |
| 553 | + @ %z(href("%R/forumpost/%S?t=c",zUuid))[link]</a> | |
| 554 | + if( !bRawMode ){ | |
| 555 | + @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a> | |
| 556 | + } | |
| 557 | + isPrivate = content_is_private(p->fpid); | |
| 558 | + sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0; | |
| 559 | + @ </h3> | |
| 560 | + if( isPrivate && !g.perm.ModForum && !sameUser ){ | |
| 561 | + @ <p><span class="modpending">Awaiting Moderator Approval</span></p> | |
| 562 | + }else{ | |
| 563 | + forum_render(0, bRawMode?"text/plain":pPost->zMimetype, pPost->zWiki, | |
| 564 | + 0, 1); | |
| 565 | + } | |
| 566 | + if( g.perm.WrForum && p->pLeaf==0 ){ | |
| 567 | + int sameUser = login_is_individual() | |
| 568 | + && fossil_strcmp(pPost->zUser, g.zLogin)==0; | |
| 569 | + @ <p><form action="%R/forumedit" method="POST"> | |
| 570 | + @ <input type="hidden" name="fpid" value="%s(p->zUuid)"> | |
| 571 | + if( !isPrivate ){ | |
| 572 | + /* Reply and Edit are only available if the post has already | |
| 573 | + ** been approved */ | |
| 574 | + @ <input type="submit" name="reply" value="Reply"> | |
| 575 | + if( g.perm.Admin || sameUser ){ | |
| 576 | + @ <input type="submit" name="edit" value="Edit"> | |
| 577 | + @ <input type="submit" name="nullout" value="Delete"> | |
| 578 | + } | |
| 579 | + }else if( g.perm.ModForum ){ | |
| 580 | + /* Provide moderators with moderation buttons for posts that | |
| 581 | + ** are pending moderation */ | |
| 582 | + @ <input type="submit" name="approve" value="Approve"> | |
| 583 | + @ <input type="submit" name="reject" value="Reject"> | |
| 584 | + generateTrustControls(pPost); | |
| 585 | + }else if( sameUser ){ | |
| 586 | + /* A post that is pending moderation can be deleted by the | |
| 587 | + ** person who originally submitted the post */ | |
| 588 | + @ <input type="submit" name="reject" value="Delete"> | |
| 589 | + } | |
| 590 | + @ </form></p> | |
| 591 | + } | |
| 592 | + manifest_destroy(pPost); | |
| 593 | + @ </div> | |
| 594 | + } | |
| 595 | + forumthread_delete(pThread); | |
| 596 | +} | |
| 506 | 597 | |
| 507 | 598 | /* |
| 508 | 599 | ** Display all messages in a forumthread with indentation. |
| 509 | 600 | */ |
| 510 | 601 | static int forum_display_hierarchical(int froot, int target){ |
| @@ -576,10 +667,11 @@ | ||
| 576 | 667 | if( g.perm.Debug ){ |
| 577 | 668 | @ <span class="debug">\ |
| 578 | 669 | @ <a href="%R/artifact/%h(p->pLeaf->zUuid)">\ |
| 579 | 670 | @ (artifact-%d(p->pLeaf->fpid))</a></span> |
| 580 | 671 | } |
| 672 | + @ %z(href("%R/forumpost/%S?t=y",p->zUuid))[history]</a> | |
| 581 | 673 | manifest_destroy(pOPost); |
| 582 | 674 | } |
| 583 | 675 | if( fpid!=target ){ |
| 584 | 676 | @ %z(href("%R/forumpost/%S",zUuid))[link]</a> |
| 585 | 677 | } |
| @@ -645,10 +737,11 @@ | ||
| 645 | 737 | ** t=MODE Display mode. |
| 646 | 738 | ** 'c' for chronological |
| 647 | 739 | ** 'h' for hierarchical |
| 648 | 740 | ** 'a' for automatic |
| 649 | 741 | ** 'r' for raw |
| 742 | +** 'y' for history of post X only | |
| 650 | 743 | ** raw If present, show only the post specified and |
| 651 | 744 | ** show its original unformatted source text. |
| 652 | 745 | */ |
| 653 | 746 | void forumpost_page(void){ |
| 654 | 747 | forumthread_page(); |
| @@ -687,10 +780,12 @@ | ||
| 687 | 780 | ** t=MODE Display mode. MODE is... |
| 688 | 781 | ** 'c' for chronological, or |
| 689 | 782 | ** 'h' for hierarchical, or |
| 690 | 783 | ** 'a' for automatic, or |
| 691 | 784 | ** 'r' for raw. |
| 785 | +** raw Show only the post given by name= and show it unformatted | |
| 786 | +** hist Show only the edit history for the name= post | |
| 692 | 787 | */ |
| 693 | 788 | void forumthread_page(void){ |
| 694 | 789 | int fpid; |
| 695 | 790 | int froot; |
| 696 | 791 | const char *zName = P("name"); |
| @@ -718,11 +813,13 @@ | ||
| 718 | 813 | zMode = "c"; /* Default to chronological on mobile */ |
| 719 | 814 | }else{ |
| 720 | 815 | zMode = "h"; |
| 721 | 816 | } |
| 722 | 817 | } |
| 723 | - forumthread_page_header(froot, fpid); | |
| 818 | + if( zMode[0]!='y' ){ | |
| 819 | + forumthread_page_header(froot, fpid); | |
| 820 | + } | |
| 724 | 821 | if( bRaw && fpid ){ |
| 725 | 822 | Manifest *pPost; |
| 726 | 823 | pPost = manifest_get(fpid, CFTYPE_FORUM, 0); |
| 727 | 824 | if( pPost==0 ){ |
| 728 | 825 | @ <p>No such forum post: %h(zName) |
| @@ -743,10 +840,14 @@ | ||
| 743 | 840 | forum_display_chronological(froot, fpid, 0); |
| 744 | 841 | }else if( zMode[0]=='r' ){ |
| 745 | 842 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 746 | 843 | style_submenu_element("Hierarchical", "%R/%s/%s?t=h", g.zPath, zName); |
| 747 | 844 | forum_display_chronological(froot, fpid, 1); |
| 845 | + }else if( zMode[0]=='y' ){ | |
| 846 | + style_header("Edit History Of A Forum Post"); | |
| 847 | + style_submenu_element("Complete Thread", "%R/%s/%s?t=a", g.zPath, zName); | |
| 848 | + forum_display_history(froot, fpid, 0); | |
| 748 | 849 | }else{ |
| 749 | 850 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 750 | 851 | style_submenu_element("Unformatted", "%R/%s/%s?t=r", g.zPath, zName); |
| 751 | 852 | forum_display_hierarchical(froot, fpid); |
| 752 | 853 | } |
| 753 | 854 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -501,10 +501,101 @@ | |
| 501 | manifest_destroy(pPost); |
| 502 | @ </div> |
| 503 | } |
| 504 | forumthread_delete(pThread); |
| 505 | } |
| 506 | |
| 507 | /* |
| 508 | ** Display all messages in a forumthread with indentation. |
| 509 | */ |
| 510 | static int forum_display_hierarchical(int froot, int target){ |
| @@ -576,10 +667,11 @@ | |
| 576 | if( g.perm.Debug ){ |
| 577 | @ <span class="debug">\ |
| 578 | @ <a href="%R/artifact/%h(p->pLeaf->zUuid)">\ |
| 579 | @ (artifact-%d(p->pLeaf->fpid))</a></span> |
| 580 | } |
| 581 | manifest_destroy(pOPost); |
| 582 | } |
| 583 | if( fpid!=target ){ |
| 584 | @ %z(href("%R/forumpost/%S",zUuid))[link]</a> |
| 585 | } |
| @@ -645,10 +737,11 @@ | |
| 645 | ** t=MODE Display mode. |
| 646 | ** 'c' for chronological |
| 647 | ** 'h' for hierarchical |
| 648 | ** 'a' for automatic |
| 649 | ** 'r' for raw |
| 650 | ** raw If present, show only the post specified and |
| 651 | ** show its original unformatted source text. |
| 652 | */ |
| 653 | void forumpost_page(void){ |
| 654 | forumthread_page(); |
| @@ -687,10 +780,12 @@ | |
| 687 | ** t=MODE Display mode. MODE is... |
| 688 | ** 'c' for chronological, or |
| 689 | ** 'h' for hierarchical, or |
| 690 | ** 'a' for automatic, or |
| 691 | ** 'r' for raw. |
| 692 | */ |
| 693 | void forumthread_page(void){ |
| 694 | int fpid; |
| 695 | int froot; |
| 696 | const char *zName = P("name"); |
| @@ -718,11 +813,13 @@ | |
| 718 | zMode = "c"; /* Default to chronological on mobile */ |
| 719 | }else{ |
| 720 | zMode = "h"; |
| 721 | } |
| 722 | } |
| 723 | forumthread_page_header(froot, fpid); |
| 724 | if( bRaw && fpid ){ |
| 725 | Manifest *pPost; |
| 726 | pPost = manifest_get(fpid, CFTYPE_FORUM, 0); |
| 727 | if( pPost==0 ){ |
| 728 | @ <p>No such forum post: %h(zName) |
| @@ -743,10 +840,14 @@ | |
| 743 | forum_display_chronological(froot, fpid, 0); |
| 744 | }else if( zMode[0]=='r' ){ |
| 745 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 746 | style_submenu_element("Hierarchical", "%R/%s/%s?t=h", g.zPath, zName); |
| 747 | forum_display_chronological(froot, fpid, 1); |
| 748 | }else{ |
| 749 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 750 | style_submenu_element("Unformatted", "%R/%s/%s?t=r", g.zPath, zName); |
| 751 | forum_display_hierarchical(froot, fpid); |
| 752 | } |
| 753 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -501,10 +501,101 @@ | |
| 501 | manifest_destroy(pPost); |
| 502 | @ </div> |
| 503 | } |
| 504 | forumthread_delete(pThread); |
| 505 | } |
| 506 | /* |
| 507 | ** Display all the edit history of post "target". |
| 508 | */ |
| 509 | static void forum_display_history(int froot, int target, int bRawMode){ |
| 510 | ForumThread *pThread = forumthread_create(froot, 0); |
| 511 | ForumEntry *p; |
| 512 | int notAnon = login_is_individual(); |
| 513 | char cMode = bRawMode ? 'r' : 'c'; |
| 514 | ForumEntry *pLeaf = 0; |
| 515 | int cnt = 0; |
| 516 | for(p=pThread->pFirst; p; p=p->pNext){ |
| 517 | if( p->fpid==target ){ |
| 518 | pLeaf = p->pLeaf ? p->pLeaf : p; |
| 519 | break; |
| 520 | } |
| 521 | } |
| 522 | for(p=pThread->pFirst; p; p=p->pNext){ |
| 523 | char *zDate; |
| 524 | Manifest *pPost; |
| 525 | int isPrivate; /* True for posts awaiting moderation */ |
| 526 | int sameUser; /* True if author is also the reader */ |
| 527 | const char *zUuid; |
| 528 | char *zDisplayName; /* The display name */ |
| 529 | |
| 530 | if( p->fpid!=pLeaf->fpid && p->pLeaf!=pLeaf ) continue; |
| 531 | cnt++; |
| 532 | pPost = manifest_get(p->fpid, CFTYPE_FORUM, 0); |
| 533 | if( pPost==0 ) continue; |
| 534 | @ <div id="forum%d(p->fpid)" class="forumTime"> |
| 535 | zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); |
| 536 | zDisplayName = display_name_from_login(pPost->zUser); |
| 537 | @ <h3 class='forumPostHdr'>(%d(p->sid)) By %h(zDisplayName) on %h(zDate) |
| 538 | fossil_free(zDisplayName); |
| 539 | fossil_free(zDate); |
| 540 | if( g.perm.Debug ){ |
| 541 | @ <span class="debug">\ |
| 542 | @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span> |
| 543 | } |
| 544 | if( p->firt && cnt==1 ){ |
| 545 | ForumEntry *pIrt = p->pPrev; |
| 546 | while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev; |
| 547 | if( pIrt ){ |
| 548 | @ in reply to %z(href("%R/forumpost/%S?t=%c",pIrt->zUuid,cMode))\ |
| 549 | @ %d(pIrt->sid)</a> |
| 550 | } |
| 551 | } |
| 552 | zUuid = p->zUuid; |
| 553 | @ %z(href("%R/forumpost/%S?t=c",zUuid))[link]</a> |
| 554 | if( !bRawMode ){ |
| 555 | @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a> |
| 556 | } |
| 557 | isPrivate = content_is_private(p->fpid); |
| 558 | sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0; |
| 559 | @ </h3> |
| 560 | if( isPrivate && !g.perm.ModForum && !sameUser ){ |
| 561 | @ <p><span class="modpending">Awaiting Moderator Approval</span></p> |
| 562 | }else{ |
| 563 | forum_render(0, bRawMode?"text/plain":pPost->zMimetype, pPost->zWiki, |
| 564 | 0, 1); |
| 565 | } |
| 566 | if( g.perm.WrForum && p->pLeaf==0 ){ |
| 567 | int sameUser = login_is_individual() |
| 568 | && fossil_strcmp(pPost->zUser, g.zLogin)==0; |
| 569 | @ <p><form action="%R/forumedit" method="POST"> |
| 570 | @ <input type="hidden" name="fpid" value="%s(p->zUuid)"> |
| 571 | if( !isPrivate ){ |
| 572 | /* Reply and Edit are only available if the post has already |
| 573 | ** been approved */ |
| 574 | @ <input type="submit" name="reply" value="Reply"> |
| 575 | if( g.perm.Admin || sameUser ){ |
| 576 | @ <input type="submit" name="edit" value="Edit"> |
| 577 | @ <input type="submit" name="nullout" value="Delete"> |
| 578 | } |
| 579 | }else if( g.perm.ModForum ){ |
| 580 | /* Provide moderators with moderation buttons for posts that |
| 581 | ** are pending moderation */ |
| 582 | @ <input type="submit" name="approve" value="Approve"> |
| 583 | @ <input type="submit" name="reject" value="Reject"> |
| 584 | generateTrustControls(pPost); |
| 585 | }else if( sameUser ){ |
| 586 | /* A post that is pending moderation can be deleted by the |
| 587 | ** person who originally submitted the post */ |
| 588 | @ <input type="submit" name="reject" value="Delete"> |
| 589 | } |
| 590 | @ </form></p> |
| 591 | } |
| 592 | manifest_destroy(pPost); |
| 593 | @ </div> |
| 594 | } |
| 595 | forumthread_delete(pThread); |
| 596 | } |
| 597 | |
| 598 | /* |
| 599 | ** Display all messages in a forumthread with indentation. |
| 600 | */ |
| 601 | static int forum_display_hierarchical(int froot, int target){ |
| @@ -576,10 +667,11 @@ | |
| 667 | if( g.perm.Debug ){ |
| 668 | @ <span class="debug">\ |
| 669 | @ <a href="%R/artifact/%h(p->pLeaf->zUuid)">\ |
| 670 | @ (artifact-%d(p->pLeaf->fpid))</a></span> |
| 671 | } |
| 672 | @ %z(href("%R/forumpost/%S?t=y",p->zUuid))[history]</a> |
| 673 | manifest_destroy(pOPost); |
| 674 | } |
| 675 | if( fpid!=target ){ |
| 676 | @ %z(href("%R/forumpost/%S",zUuid))[link]</a> |
| 677 | } |
| @@ -645,10 +737,11 @@ | |
| 737 | ** t=MODE Display mode. |
| 738 | ** 'c' for chronological |
| 739 | ** 'h' for hierarchical |
| 740 | ** 'a' for automatic |
| 741 | ** 'r' for raw |
| 742 | ** 'y' for history of post X only |
| 743 | ** raw If present, show only the post specified and |
| 744 | ** show its original unformatted source text. |
| 745 | */ |
| 746 | void forumpost_page(void){ |
| 747 | forumthread_page(); |
| @@ -687,10 +780,12 @@ | |
| 780 | ** t=MODE Display mode. MODE is... |
| 781 | ** 'c' for chronological, or |
| 782 | ** 'h' for hierarchical, or |
| 783 | ** 'a' for automatic, or |
| 784 | ** 'r' for raw. |
| 785 | ** raw Show only the post given by name= and show it unformatted |
| 786 | ** hist Show only the edit history for the name= post |
| 787 | */ |
| 788 | void forumthread_page(void){ |
| 789 | int fpid; |
| 790 | int froot; |
| 791 | const char *zName = P("name"); |
| @@ -718,11 +813,13 @@ | |
| 813 | zMode = "c"; /* Default to chronological on mobile */ |
| 814 | }else{ |
| 815 | zMode = "h"; |
| 816 | } |
| 817 | } |
| 818 | if( zMode[0]!='y' ){ |
| 819 | forumthread_page_header(froot, fpid); |
| 820 | } |
| 821 | if( bRaw && fpid ){ |
| 822 | Manifest *pPost; |
| 823 | pPost = manifest_get(fpid, CFTYPE_FORUM, 0); |
| 824 | if( pPost==0 ){ |
| 825 | @ <p>No such forum post: %h(zName) |
| @@ -743,10 +840,14 @@ | |
| 840 | forum_display_chronological(froot, fpid, 0); |
| 841 | }else if( zMode[0]=='r' ){ |
| 842 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 843 | style_submenu_element("Hierarchical", "%R/%s/%s?t=h", g.zPath, zName); |
| 844 | forum_display_chronological(froot, fpid, 1); |
| 845 | }else if( zMode[0]=='y' ){ |
| 846 | style_header("Edit History Of A Forum Post"); |
| 847 | style_submenu_element("Complete Thread", "%R/%s/%s?t=a", g.zPath, zName); |
| 848 | forum_display_history(froot, fpid, 0); |
| 849 | }else{ |
| 850 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 851 | style_submenu_element("Unformatted", "%R/%s/%s?t=r", g.zPath, zName); |
| 852 | forum_display_hierarchical(froot, fpid); |
| 853 | } |
| 854 |