Fossil SCM
Per request in forumpost/9fd5135acc, added intra-document links to each response which link back to the being-replied-to post within the same page (using an #anchor-tag), without reloading from the server.
Commit
78721d4aa0fa51ee98107da408c89bfa9c5e140ef7086e309aa28e15b59950c0
Parent
611fe9b078d3774…
1 file changed
+52
-11
+52
-11
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -283,15 +283,55 @@ | ||
| 283 | 283 | @ Trust user "%h(pPost->zUser)" |
| 284 | 284 | @ so that future posts by "%h(pPost->zUser)" do not require moderation. |
| 285 | 285 | @ </label> |
| 286 | 286 | @ <input type="hidden" name="trustuser" value="%h(pPost->zUser)"> |
| 287 | 287 | } |
| 288 | + | |
| 289 | +/* | |
| 290 | +** If pPost->firt then this routine outputs an "in reply to" link to | |
| 291 | +** the post being replied to, else this is a no-op. | |
| 292 | +** | |
| 293 | +** The generated link is a document-local reference to #post-ID, where | |
| 294 | +** ID is the short-form UUID of the being-reponded-to post (the length | |
| 295 | +** being determined by the %!S cgi_printf() flag). | |
| 296 | +** | |
| 297 | +** zPostName must be the name of the current post being displayed, as | |
| 298 | +** it appears in the the URL. e.g. for /forumpost/abcddcba, zPostName | |
| 299 | +** must be "abcddcba". This is required for building an absolute link | |
| 300 | +** to work around the document having a BASE tag which breaks | |
| 301 | +** intra-document links (which HTML treats like relative links, thus | |
| 302 | +** applying the BASE HREF to them). | |
| 303 | +** | |
| 304 | +** zTMode must be the current thread display mode: "c"=chronological, | |
| 305 | +** "h"=hierarchical. (Without that parameter on the URL, the browser | |
| 306 | +** switches from chronological to hierarchical view when clicking an | |
| 307 | +** intra-document link in the former mode.) | |
| 308 | +*/ | |
| 309 | +static void forum_output_reply_link( const char *zPostName, | |
| 310 | + const char * zTMode, | |
| 311 | + const ForumEntry * pPost ){ | |
| 312 | + if( pPost->firt ){ | |
| 313 | + const ForumEntry *pIrt = pPost->pPrev; | |
| 314 | + while( pIrt && pIrt->fpid!=pPost->firt ) pIrt = pIrt->pPrev; | |
| 315 | + if( pIrt ){ | |
| 316 | + cgi_printf("in reply to [<a " | |
| 317 | + "href=\"%R/%s/%s?t=%s#post-%!S\">" | |
| 318 | + "%S</a>]\n", | |
| 319 | + g.zPath, zPostName, zTMode, pIrt->zUuid, | |
| 320 | + pIrt->zUuid); | |
| 321 | + } | |
| 322 | + } | |
| 323 | +} | |
| 288 | 324 | |
| 289 | 325 | /* |
| 290 | 326 | ** Display all posts in a forum thread in chronological order |
| 327 | +** | |
| 328 | +** See forum_output_reply_link() for details of the zPostName | |
| 329 | +** parameter. | |
| 291 | 330 | */ |
| 292 | -static void forum_display_chronological(int froot, int target){ | |
| 331 | +static void forum_display_chronological(int froot, int target, | |
| 332 | + const char *zPostName){ | |
| 293 | 333 | ForumThread *pThread = forumthread_create(froot, 0); |
| 294 | 334 | ForumEntry *p; |
| 295 | 335 | int notAnon = login_is_individual(); |
| 296 | 336 | for(p=pThread->pFirst; p; p=p->pNext){ |
| 297 | 337 | char *zDate; |
| @@ -306,26 +346,21 @@ | ||
| 306 | 346 | }else if( p->pLeaf!=0 ){ |
| 307 | 347 | @ <div id="forum%d(p->fpid)" class="forumTime forumObs"> |
| 308 | 348 | }else{ |
| 309 | 349 | @ <div id="forum%d(p->fpid)" class="forumTime"> |
| 310 | 350 | } |
| 351 | + @ <a name="post-%!S(p->zUuid)"></a> | |
| 311 | 352 | if( pPost->zThreadTitle ){ |
| 312 | 353 | @ <h1>%h(pPost->zThreadTitle)</h1> |
| 313 | 354 | } |
| 314 | 355 | zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); |
| 315 | 356 | @ <p>By %h(pPost->zUser) on %h(zDate) (%d(p->fpid)) |
| 316 | 357 | fossil_free(zDate); |
| 317 | 358 | if( p->pEdit ){ |
| 318 | 359 | @ edit of %z(href("%R/forumpost/%S?t=c",p->pEdit->zUuid))%d(p->fprev)</a> |
| 319 | 360 | } |
| 320 | - if( p->firt ){ | |
| 321 | - ForumEntry *pIrt = p->pPrev; | |
| 322 | - while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev; | |
| 323 | - if( pIrt ){ | |
| 324 | - @ reply to %z(href("%R/forumpost/%S?t=c",pIrt->zUuid))%d(p->firt)</a> | |
| 325 | - } | |
| 326 | - } | |
| 361 | + forum_output_reply_link( zPostName, "c", p ); | |
| 327 | 362 | if( p->pLeaf ){ |
| 328 | 363 | @ updated by %z(href("%R/forumpost/%S?t=c",p->pLeaf->zUuid))\ |
| 329 | 364 | @ %d(p->pLeaf->fpid)</a> |
| 330 | 365 | } |
| 331 | 366 | if( g.perm.Debug ){ |
| @@ -374,12 +409,16 @@ | ||
| 374 | 409 | forumthread_delete(pThread); |
| 375 | 410 | } |
| 376 | 411 | |
| 377 | 412 | /* |
| 378 | 413 | ** Display all messages in a forumthread with indentation. |
| 414 | +** | |
| 415 | +** See forum_output_reply_link() for details of the zPostName | |
| 416 | +** parameter. | |
| 379 | 417 | */ |
| 380 | -static int forum_display_hierarchical(int froot, int target){ | |
| 418 | +static int forum_display_hierarchical(int froot, int target, | |
| 419 | + const char * zPostName){ | |
| 381 | 420 | ForumThread *pThread; |
| 382 | 421 | ForumEntry *p; |
| 383 | 422 | Manifest *pPost, *pOPost; |
| 384 | 423 | int fpid; |
| 385 | 424 | const char *zUuid; |
| @@ -406,10 +445,11 @@ | ||
| 406 | 445 | }else{ |
| 407 | 446 | fpid = p->fpid; |
| 408 | 447 | zUuid = p->zUuid; |
| 409 | 448 | pPost = pOPost; |
| 410 | 449 | } |
| 450 | + @ <a name="post-%!S(zUuid)"></a> | |
| 411 | 451 | zSel = p->fpid==target ? " forumSel" : ""; |
| 412 | 452 | if( p->nIndent==1 ){ |
| 413 | 453 | @ <div id='forum%d(fpid)' class='forumHierRoot%s(zSel)'> |
| 414 | 454 | }else{ |
| 415 | 455 | @ <div id='forum%d(fpid)' class='forumHier%s(zSel)' \ |
| @@ -425,10 +465,11 @@ | ||
| 425 | 465 | fossil_free(zDate); |
| 426 | 466 | if( g.perm.Debug ){ |
| 427 | 467 | @ <span class="debug">\ |
| 428 | 468 | @ <a href="%R/artifact/%h(p->zUuid)">(%d(p->fpid))</a></span> |
| 429 | 469 | } |
| 470 | + forum_output_reply_link( zPostName, "h", p ); | |
| 430 | 471 | if( p->pLeaf ){ |
| 431 | 472 | zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); |
| 432 | 473 | if( fossil_strcmp(pOPost->zUser,pPost->zUser)==0 ){ |
| 433 | 474 | @ and edited on %h(zDate) |
| 434 | 475 | }else{ |
| @@ -543,14 +584,14 @@ | ||
| 543 | 584 | zMode = "h"; |
| 544 | 585 | } |
| 545 | 586 | } |
| 546 | 587 | if( zMode[0]=='c' ){ |
| 547 | 588 | style_submenu_element("Hierarchical", "%R/%s/%s?t=h", g.zPath, zName); |
| 548 | - forum_display_chronological(froot, fpid); | |
| 589 | + forum_display_chronological(froot, fpid, zName); | |
| 549 | 590 | }else{ |
| 550 | 591 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 551 | - forum_display_hierarchical(froot, fpid); | |
| 592 | + forum_display_hierarchical(froot, fpid, zName); | |
| 552 | 593 | } |
| 553 | 594 | style_load_js("forum.js"); |
| 554 | 595 | style_footer(); |
| 555 | 596 | } |
| 556 | 597 | |
| 557 | 598 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -283,15 +283,55 @@ | |
| 283 | @ Trust user "%h(pPost->zUser)" |
| 284 | @ so that future posts by "%h(pPost->zUser)" do not require moderation. |
| 285 | @ </label> |
| 286 | @ <input type="hidden" name="trustuser" value="%h(pPost->zUser)"> |
| 287 | } |
| 288 | |
| 289 | /* |
| 290 | ** Display all posts in a forum thread in chronological order |
| 291 | */ |
| 292 | static void forum_display_chronological(int froot, int target){ |
| 293 | ForumThread *pThread = forumthread_create(froot, 0); |
| 294 | ForumEntry *p; |
| 295 | int notAnon = login_is_individual(); |
| 296 | for(p=pThread->pFirst; p; p=p->pNext){ |
| 297 | char *zDate; |
| @@ -306,26 +346,21 @@ | |
| 306 | }else if( p->pLeaf!=0 ){ |
| 307 | @ <div id="forum%d(p->fpid)" class="forumTime forumObs"> |
| 308 | }else{ |
| 309 | @ <div id="forum%d(p->fpid)" class="forumTime"> |
| 310 | } |
| 311 | if( pPost->zThreadTitle ){ |
| 312 | @ <h1>%h(pPost->zThreadTitle)</h1> |
| 313 | } |
| 314 | zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); |
| 315 | @ <p>By %h(pPost->zUser) on %h(zDate) (%d(p->fpid)) |
| 316 | fossil_free(zDate); |
| 317 | if( p->pEdit ){ |
| 318 | @ edit of %z(href("%R/forumpost/%S?t=c",p->pEdit->zUuid))%d(p->fprev)</a> |
| 319 | } |
| 320 | if( p->firt ){ |
| 321 | ForumEntry *pIrt = p->pPrev; |
| 322 | while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev; |
| 323 | if( pIrt ){ |
| 324 | @ reply to %z(href("%R/forumpost/%S?t=c",pIrt->zUuid))%d(p->firt)</a> |
| 325 | } |
| 326 | } |
| 327 | if( p->pLeaf ){ |
| 328 | @ updated by %z(href("%R/forumpost/%S?t=c",p->pLeaf->zUuid))\ |
| 329 | @ %d(p->pLeaf->fpid)</a> |
| 330 | } |
| 331 | if( g.perm.Debug ){ |
| @@ -374,12 +409,16 @@ | |
| 374 | forumthread_delete(pThread); |
| 375 | } |
| 376 | |
| 377 | /* |
| 378 | ** Display all messages in a forumthread with indentation. |
| 379 | */ |
| 380 | static int forum_display_hierarchical(int froot, int target){ |
| 381 | ForumThread *pThread; |
| 382 | ForumEntry *p; |
| 383 | Manifest *pPost, *pOPost; |
| 384 | int fpid; |
| 385 | const char *zUuid; |
| @@ -406,10 +445,11 @@ | |
| 406 | }else{ |
| 407 | fpid = p->fpid; |
| 408 | zUuid = p->zUuid; |
| 409 | pPost = pOPost; |
| 410 | } |
| 411 | zSel = p->fpid==target ? " forumSel" : ""; |
| 412 | if( p->nIndent==1 ){ |
| 413 | @ <div id='forum%d(fpid)' class='forumHierRoot%s(zSel)'> |
| 414 | }else{ |
| 415 | @ <div id='forum%d(fpid)' class='forumHier%s(zSel)' \ |
| @@ -425,10 +465,11 @@ | |
| 425 | fossil_free(zDate); |
| 426 | if( g.perm.Debug ){ |
| 427 | @ <span class="debug">\ |
| 428 | @ <a href="%R/artifact/%h(p->zUuid)">(%d(p->fpid))</a></span> |
| 429 | } |
| 430 | if( p->pLeaf ){ |
| 431 | zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); |
| 432 | if( fossil_strcmp(pOPost->zUser,pPost->zUser)==0 ){ |
| 433 | @ and edited on %h(zDate) |
| 434 | }else{ |
| @@ -543,14 +584,14 @@ | |
| 543 | zMode = "h"; |
| 544 | } |
| 545 | } |
| 546 | if( zMode[0]=='c' ){ |
| 547 | style_submenu_element("Hierarchical", "%R/%s/%s?t=h", g.zPath, zName); |
| 548 | forum_display_chronological(froot, fpid); |
| 549 | }else{ |
| 550 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 551 | forum_display_hierarchical(froot, fpid); |
| 552 | } |
| 553 | style_load_js("forum.js"); |
| 554 | style_footer(); |
| 555 | } |
| 556 | |
| 557 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -283,15 +283,55 @@ | |
| 283 | @ Trust user "%h(pPost->zUser)" |
| 284 | @ so that future posts by "%h(pPost->zUser)" do not require moderation. |
| 285 | @ </label> |
| 286 | @ <input type="hidden" name="trustuser" value="%h(pPost->zUser)"> |
| 287 | } |
| 288 | |
| 289 | /* |
| 290 | ** If pPost->firt then this routine outputs an "in reply to" link to |
| 291 | ** the post being replied to, else this is a no-op. |
| 292 | ** |
| 293 | ** The generated link is a document-local reference to #post-ID, where |
| 294 | ** ID is the short-form UUID of the being-reponded-to post (the length |
| 295 | ** being determined by the %!S cgi_printf() flag). |
| 296 | ** |
| 297 | ** zPostName must be the name of the current post being displayed, as |
| 298 | ** it appears in the the URL. e.g. for /forumpost/abcddcba, zPostName |
| 299 | ** must be "abcddcba". This is required for building an absolute link |
| 300 | ** to work around the document having a BASE tag which breaks |
| 301 | ** intra-document links (which HTML treats like relative links, thus |
| 302 | ** applying the BASE HREF to them). |
| 303 | ** |
| 304 | ** zTMode must be the current thread display mode: "c"=chronological, |
| 305 | ** "h"=hierarchical. (Without that parameter on the URL, the browser |
| 306 | ** switches from chronological to hierarchical view when clicking an |
| 307 | ** intra-document link in the former mode.) |
| 308 | */ |
| 309 | static void forum_output_reply_link( const char *zPostName, |
| 310 | const char * zTMode, |
| 311 | const ForumEntry * pPost ){ |
| 312 | if( pPost->firt ){ |
| 313 | const ForumEntry *pIrt = pPost->pPrev; |
| 314 | while( pIrt && pIrt->fpid!=pPost->firt ) pIrt = pIrt->pPrev; |
| 315 | if( pIrt ){ |
| 316 | cgi_printf("in reply to [<a " |
| 317 | "href=\"%R/%s/%s?t=%s#post-%!S\">" |
| 318 | "%S</a>]\n", |
| 319 | g.zPath, zPostName, zTMode, pIrt->zUuid, |
| 320 | pIrt->zUuid); |
| 321 | } |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | /* |
| 326 | ** Display all posts in a forum thread in chronological order |
| 327 | ** |
| 328 | ** See forum_output_reply_link() for details of the zPostName |
| 329 | ** parameter. |
| 330 | */ |
| 331 | static void forum_display_chronological(int froot, int target, |
| 332 | const char *zPostName){ |
| 333 | ForumThread *pThread = forumthread_create(froot, 0); |
| 334 | ForumEntry *p; |
| 335 | int notAnon = login_is_individual(); |
| 336 | for(p=pThread->pFirst; p; p=p->pNext){ |
| 337 | char *zDate; |
| @@ -306,26 +346,21 @@ | |
| 346 | }else if( p->pLeaf!=0 ){ |
| 347 | @ <div id="forum%d(p->fpid)" class="forumTime forumObs"> |
| 348 | }else{ |
| 349 | @ <div id="forum%d(p->fpid)" class="forumTime"> |
| 350 | } |
| 351 | @ <a name="post-%!S(p->zUuid)"></a> |
| 352 | if( pPost->zThreadTitle ){ |
| 353 | @ <h1>%h(pPost->zThreadTitle)</h1> |
| 354 | } |
| 355 | zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); |
| 356 | @ <p>By %h(pPost->zUser) on %h(zDate) (%d(p->fpid)) |
| 357 | fossil_free(zDate); |
| 358 | if( p->pEdit ){ |
| 359 | @ edit of %z(href("%R/forumpost/%S?t=c",p->pEdit->zUuid))%d(p->fprev)</a> |
| 360 | } |
| 361 | forum_output_reply_link( zPostName, "c", p ); |
| 362 | if( p->pLeaf ){ |
| 363 | @ updated by %z(href("%R/forumpost/%S?t=c",p->pLeaf->zUuid))\ |
| 364 | @ %d(p->pLeaf->fpid)</a> |
| 365 | } |
| 366 | if( g.perm.Debug ){ |
| @@ -374,12 +409,16 @@ | |
| 409 | forumthread_delete(pThread); |
| 410 | } |
| 411 | |
| 412 | /* |
| 413 | ** Display all messages in a forumthread with indentation. |
| 414 | ** |
| 415 | ** See forum_output_reply_link() for details of the zPostName |
| 416 | ** parameter. |
| 417 | */ |
| 418 | static int forum_display_hierarchical(int froot, int target, |
| 419 | const char * zPostName){ |
| 420 | ForumThread *pThread; |
| 421 | ForumEntry *p; |
| 422 | Manifest *pPost, *pOPost; |
| 423 | int fpid; |
| 424 | const char *zUuid; |
| @@ -406,10 +445,11 @@ | |
| 445 | }else{ |
| 446 | fpid = p->fpid; |
| 447 | zUuid = p->zUuid; |
| 448 | pPost = pOPost; |
| 449 | } |
| 450 | @ <a name="post-%!S(zUuid)"></a> |
| 451 | zSel = p->fpid==target ? " forumSel" : ""; |
| 452 | if( p->nIndent==1 ){ |
| 453 | @ <div id='forum%d(fpid)' class='forumHierRoot%s(zSel)'> |
| 454 | }else{ |
| 455 | @ <div id='forum%d(fpid)' class='forumHier%s(zSel)' \ |
| @@ -425,10 +465,11 @@ | |
| 465 | fossil_free(zDate); |
| 466 | if( g.perm.Debug ){ |
| 467 | @ <span class="debug">\ |
| 468 | @ <a href="%R/artifact/%h(p->zUuid)">(%d(p->fpid))</a></span> |
| 469 | } |
| 470 | forum_output_reply_link( zPostName, "h", p ); |
| 471 | if( p->pLeaf ){ |
| 472 | zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); |
| 473 | if( fossil_strcmp(pOPost->zUser,pPost->zUser)==0 ){ |
| 474 | @ and edited on %h(zDate) |
| 475 | }else{ |
| @@ -543,14 +584,14 @@ | |
| 584 | zMode = "h"; |
| 585 | } |
| 586 | } |
| 587 | if( zMode[0]=='c' ){ |
| 588 | style_submenu_element("Hierarchical", "%R/%s/%s?t=h", g.zPath, zName); |
| 589 | forum_display_chronological(froot, fpid, zName); |
| 590 | }else{ |
| 591 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 592 | forum_display_hierarchical(froot, fpid, zName); |
| 593 | } |
| 594 | style_load_js("forum.js"); |
| 595 | style_footer(); |
| 596 | } |
| 597 | |
| 598 |