Fossil SCM

The /search page now covers wiki and check-in comments. And the formatting of snippets is improved. The search is still done by full-scan but the infrastructure is coming into place to handle the search using an index.

drh 2015-02-01 00:15 trunk
Commit 8e02c26ad2baf8553290376aa7ec1e7e7a715b5d
2 files changed +153 -15 +10 -7
+153 -15
--- src/search.c
+++ src/search.c
@@ -411,10 +411,40 @@
411411
}
412412
}else{
413413
sqlite3_result_int(context, score);
414414
}
415415
}
416
+
417
+/*
418
+** This is an SQLite function that computes the searchable text.
419
+** It is a wrapper around the search_stext() routine. See the
420
+** search_stext() routine for further detail.
421
+*/
422
+static void search_stext_sqlfunc(
423
+ sqlite3_context *context,
424
+ int argc,
425
+ sqlite3_value **argv
426
+){
427
+ Blob txt;
428
+ const char *zType = (const char*)sqlite3_value_text(argv[0]);
429
+ const char *zArg1 = (const char*)sqlite3_value_text(argv[1]);
430
+ const char *zArg2 = (const char*)sqlite3_value_text(argv[2]);
431
+ search_stext(zType[0], zArg1, zArg2, &txt);
432
+ sqlite3_result_text(context, blob_materialize(&txt), -1, fossil_free);
433
+}
434
+
435
+/*
436
+** Encode a string for use as a query parameter in a URL
437
+*/
438
+static void search_urlencode_sqlfunc(
439
+ sqlite3_context *context,
440
+ int argc,
441
+ sqlite3_value **argv
442
+){
443
+ char *z = mprintf("%T",sqlite3_value_text(argv[0]));
444
+ sqlite3_result_text(context, z, -1, fossil_free);
445
+}
416446
417447
/*
418448
** Register the "score()" SQL function to score its input text
419449
** using the given Search object. Once this function is registered,
420450
** do not delete the Search object.
@@ -424,10 +454,14 @@
424454
search_score_sqlfunc, 0, 0);
425455
sqlite3_create_function(db, "snippet", -1, SQLITE_UTF8, &gSearch,
426456
search_score_sqlfunc, 0, 0);
427457
sqlite3_create_function(db, "search_init", -1, SQLITE_UTF8, 0,
428458
search_init_sqlfunc, 0, 0);
459
+ sqlite3_create_function(db, "stext", 3, SQLITE_UTF8, 0,
460
+ search_stext_sqlfunc, 0, 0);
461
+ sqlite3_create_function(db, "urlencode", 1, SQLITE_UTF8, 0,
462
+ search_urlencode_sqlfunc, 0, 0);
429463
}
430464
431465
/*
432466
** Testing the search function.
433467
**
@@ -522,10 +556,11 @@
522556
** * Search wiki
523557
*/
524558
void search_page(void){
525559
const char *zPattern = PD("s","");
526560
Stmt q;
561
+ const char *zSrchEnable = "dwc";
527562
528563
login_check_credentials();
529564
if( !g.perm.Read ){ login_needed(); return; }
530565
style_header("Search");
531566
@ <form method="GET" action="search"><center>
@@ -537,30 +572,72 @@
537572
search_sql_setup(g.db);
538573
add_content_sql_commands(g.db);
539574
search_init(zPattern, "<b>", "</b>", " ... ",
540575
SRCHFLG_STATIC|SRCHFLG_HTML|SRCHFLG_SCORE);
541576
db_multi_exec(
542
- "CREATE VIRTUAL TABLE temp.foci USING files_of_checkin;"
543
- "CREATE TEMP TABLE x(fn TEXT,url TEXT,snip TEXT);"
544
- "INSERT INTO x(fn,url,snip)"
545
- " SELECT filename, printf('%R/doc/trunk/%%s',filename),"
546
- " snippet(content(uuid))"
547
- " FROM foci"
548
- " WHERE checkinID=symbolic_name_to_rid('trunk')"
549
- " AND (filename GLOB '*.wiki' OR"
550
- " filename GLOB '*.md' OR"
551
- " filename GLOB '*.txt' OR"
552
- " filename GLOB '*.html');"
577
+ "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;"
578
+ "CREATE TEMP TABLE x(label TEXT,url TEXT,date TEXT,snip TEXT);"
553579
);
554
- db_prepare(&q, "SELECT url, substr(snip,8)"
580
+ if( strchr(zSrchEnable, 'd') ){
581
+ db_multi_exec(
582
+ "INSERT INTO x(label,url,date,snip)"
583
+ " SELECT printf('Document: %%s',foci.filename),"
584
+ " printf('%R/doc/trunk/%%s',foci.filename),"
585
+ " (SELECT datetime(event.mtime) FROM event"
586
+ " WHERE objid=symbolic_name_to_rid('trunk')),"
587
+ " snippet(stext('d',blob.rid,foci.filename))"
588
+ " FROM foci CROSS JOIN blob"
589
+ " WHERE checkinID=symbolic_name_to_rid('trunk')"
590
+ " AND blob.uuid=foci.uuid"
591
+ " AND (filename GLOB '*.wiki' OR"
592
+ " filename GLOB '*.md' OR"
593
+ " filename GLOB '*.txt' OR"
594
+ " filename GLOB '*.html');"
595
+ );
596
+ }
597
+ if( strchr(zSrchEnable, 'w') ){
598
+ db_multi_exec(
599
+ "WITH wiki(name,rid,mtime) AS ("
600
+ " SELECT substr(tagname,6), tagxref.rid, max(tagxref.mtime)"
601
+ " FROM tag, tagxref"
602
+ " WHERE tag.tagname GLOB 'wiki-*'"
603
+ " AND tagxref.tagid=tag.tagid"
604
+ " GROUP BY 1"
605
+ ")"
606
+ "INSERT INTO x(label,url,date,snip)"
607
+ " SELECT printf('Wiki: %%s',name),"
608
+ " printf('%R/wiki?name=%%s',urlencode(name)),"
609
+ " datetime(mtime),"
610
+ " snippet(stext('w',rid,name))"
611
+ " FROM wiki;"
612
+ );
613
+ }
614
+ if( strchr(zSrchEnable, 'c') ){
615
+ db_multi_exec(
616
+ "WITH ckin(uuid,rid,mtime) AS ("
617
+ " SELECT blob.uuid, event.objid, event.mtime"
618
+ " FROM event, blob"
619
+ " WHERE event.type='ci'"
620
+ " AND blob.rid=event.objid"
621
+ ")"
622
+ "INSERT INTO x(label,url,date,snip)"
623
+ " SELECT printf('Check-in [%%.10s] on %%s',uuid,datetime(mtime)),"
624
+ " printf('%R/timeline?c=%%s&n=8&y=ci',uuid),"
625
+ " datetime(mtime),"
626
+ " snippet(stext('c',rid,NULL))"
627
+ " FROM ckin;"
628
+ );
629
+ }
630
+ db_prepare(&q, "SELECT url, substr(snip,9), label"
555631
" FROM x WHERE snip IS NOT NULL"
556
- " ORDER BY substr(snip,1,8) DESC, fn;");
632
+ " ORDER BY substr(snip,1,9) DESC, date DESC;");
557633
@ <ol>
558634
while( db_step(&q)==SQLITE_ROW ){
559635
const char *zUrl = db_column_text(&q, 0);
560636
const char *zSnippet = db_column_text(&q, 1);
561
- @ <li><p>%s(href("%s",zUrl))%h(zUrl)</a><br>%s(zSnippet)</li>
637
+ const char *zLabel = db_column_text(&q, 2);
638
+ @ <li><p>%s(href("%s",zUrl))%h(zLabel)</a><br>%s(zSnippet)</li>
562639
}
563640
db_finalize(&q);
564641
@ </ol>
565642
}
566643
style_footer();
@@ -639,21 +716,82 @@
639716
pOut);
640717
blob_reset(&wiki);
641718
manifest_destroy(pWiki);
642719
break;
643720
}
721
+ case 'c': { /* Ckeckin: zArg1: RID of the checkin. zArg2: Not used */
722
+ int rid = atoi(zArg1);
723
+ static Stmt q;
724
+ db_static_prepare(&q,
725
+ "SELECT coalesce(ecomment,comment)"
726
+ " ||' (user: '||coalesce(euser,user,'?')"
727
+ " ||', tags: '||"
728
+ " (SELECT group_concat(substr(tag.tagname,5),',')"
729
+ " FROM tag, tagxref"
730
+ " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
731
+ " AND tagxref.rid=event.objid AND tagxref.tagtype>0)"
732
+ " ||')'"
733
+ " FROM event WHERE objid=:x AND type='ci'");
734
+ db_bind_int(&q, ":x", rid);
735
+ if( db_step(&q)==SQLITE_ROW ){
736
+ db_column_blob(&q, 0, pOut);
737
+ blob_append(pOut, "\n", 1);
738
+ }
739
+ db_reset(&q);
740
+ break;
741
+ }
644742
}
645743
}
744
+
745
+/*
746
+** The arguments cType,zArg1,zArg2 define an object that can be searched
747
+** for. Return a URL (relative to the root of the Fossil project) that
748
+** will jump to that document.
749
+**
750
+** Space to hold the returned string is obtained from mprintf() and should
751
+** be freed by the caller using fossil_free() or the equivalent.
752
+*/
753
+char *search_url(
754
+ char cType, /* Type of document */
755
+ const char *zArg1, /* First parameter */
756
+ const char *zArg2 /* Second parameter */
757
+){
758
+ char *zUrl = 0;
759
+ switch( cType ){
760
+ case 'd': { /* Doc. zArg1: RID of the file. zArg2: Filename */
761
+ case 's': /* Source. zArg1: RID of the file. zArg2: Filename */
762
+ zUrl = db_text(0,
763
+ "SELECT printf('/doc/%%s%%s', substr(blob.uuid,20), %Q)"
764
+ " FROM mlink, blob"
765
+ " WHERE mlink.fid=%d AND mlink.mid=blob.rid",
766
+ zArg2, atoi(zArg1));
767
+ break;
768
+ }
769
+ case 'w': { /* Wiki. zArg1: RID of the page. zArg2: Page name */
770
+ char *zId = db_text(0, "SELECT uuid FROM blob WHERE rid=%d",atoi(zArg1));
771
+ zUrl = mprintf("/wiki?id=%z&name=%t", zId, zArg2);
772
+ break;
773
+ }
774
+ case 'c': { /* Ckeckin: zArg1: RID of the checkin. zArg2: Not used */
775
+ char *zId = db_text(0, "SELECT uuid FROM blob WHERE rid=%d",atoi(zArg1));
776
+ zUrl = mprintf("/info/%z", zId);
777
+ break;
778
+ }
779
+ }
780
+ return zUrl;
781
+}
646782
647783
/*
648784
** COMMAND: test-search-stext
649785
**
650786
** Usage: fossil test-search-stext TYPE ARG1 ARG2
651787
*/
652788
void test_search_stext(void){
653789
Blob out;
790
+ char *zUrl;
654791
db_find_and_open_repository(0,0);
655792
if( g.argc!=5 ) usage("TYPE ARG1 ARG2");
656793
search_stext(g.argv[2][0], g.argv[3], g.argv[4], &out);
657
- fossil_print("%s",blob_str(&out));
794
+ zUrl = search_url(g.argv[2][0], g.argv[3], g.argv[4]);
795
+ fossil_print("%s\n%z\n",blob_str(&out),zUrl);
658796
blob_reset(&out);
659797
}
660798
--- src/search.c
+++ src/search.c
@@ -411,10 +411,40 @@
411 }
412 }else{
413 sqlite3_result_int(context, score);
414 }
415 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
416
417 /*
418 ** Register the "score()" SQL function to score its input text
419 ** using the given Search object. Once this function is registered,
420 ** do not delete the Search object.
@@ -424,10 +454,14 @@
424 search_score_sqlfunc, 0, 0);
425 sqlite3_create_function(db, "snippet", -1, SQLITE_UTF8, &gSearch,
426 search_score_sqlfunc, 0, 0);
427 sqlite3_create_function(db, "search_init", -1, SQLITE_UTF8, 0,
428 search_init_sqlfunc, 0, 0);
 
 
 
 
429 }
430
431 /*
432 ** Testing the search function.
433 **
@@ -522,10 +556,11 @@
522 ** * Search wiki
523 */
524 void search_page(void){
525 const char *zPattern = PD("s","");
526 Stmt q;
 
527
528 login_check_credentials();
529 if( !g.perm.Read ){ login_needed(); return; }
530 style_header("Search");
531 @ <form method="GET" action="search"><center>
@@ -537,30 +572,72 @@
537 search_sql_setup(g.db);
538 add_content_sql_commands(g.db);
539 search_init(zPattern, "<b>", "</b>", " ... ",
540 SRCHFLG_STATIC|SRCHFLG_HTML|SRCHFLG_SCORE);
541 db_multi_exec(
542 "CREATE VIRTUAL TABLE temp.foci USING files_of_checkin;"
543 "CREATE TEMP TABLE x(fn TEXT,url TEXT,snip TEXT);"
544 "INSERT INTO x(fn,url,snip)"
545 " SELECT filename, printf('%R/doc/trunk/%%s',filename),"
546 " snippet(content(uuid))"
547 " FROM foci"
548 " WHERE checkinID=symbolic_name_to_rid('trunk')"
549 " AND (filename GLOB '*.wiki' OR"
550 " filename GLOB '*.md' OR"
551 " filename GLOB '*.txt' OR"
552 " filename GLOB '*.html');"
553 );
554 db_prepare(&q, "SELECT url, substr(snip,8)"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
555 " FROM x WHERE snip IS NOT NULL"
556 " ORDER BY substr(snip,1,8) DESC, fn;");
557 @ <ol>
558 while( db_step(&q)==SQLITE_ROW ){
559 const char *zUrl = db_column_text(&q, 0);
560 const char *zSnippet = db_column_text(&q, 1);
561 @ <li><p>%s(href("%s",zUrl))%h(zUrl)</a><br>%s(zSnippet)</li>
 
562 }
563 db_finalize(&q);
564 @ </ol>
565 }
566 style_footer();
@@ -639,21 +716,82 @@
639 pOut);
640 blob_reset(&wiki);
641 manifest_destroy(pWiki);
642 break;
643 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
644 }
645 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
646
647 /*
648 ** COMMAND: test-search-stext
649 **
650 ** Usage: fossil test-search-stext TYPE ARG1 ARG2
651 */
652 void test_search_stext(void){
653 Blob out;
 
654 db_find_and_open_repository(0,0);
655 if( g.argc!=5 ) usage("TYPE ARG1 ARG2");
656 search_stext(g.argv[2][0], g.argv[3], g.argv[4], &out);
657 fossil_print("%s",blob_str(&out));
 
658 blob_reset(&out);
659 }
660
--- src/search.c
+++ src/search.c
@@ -411,10 +411,40 @@
411 }
412 }else{
413 sqlite3_result_int(context, score);
414 }
415 }
416
417 /*
418 ** This is an SQLite function that computes the searchable text.
419 ** It is a wrapper around the search_stext() routine. See the
420 ** search_stext() routine for further detail.
421 */
422 static void search_stext_sqlfunc(
423 sqlite3_context *context,
424 int argc,
425 sqlite3_value **argv
426 ){
427 Blob txt;
428 const char *zType = (const char*)sqlite3_value_text(argv[0]);
429 const char *zArg1 = (const char*)sqlite3_value_text(argv[1]);
430 const char *zArg2 = (const char*)sqlite3_value_text(argv[2]);
431 search_stext(zType[0], zArg1, zArg2, &txt);
432 sqlite3_result_text(context, blob_materialize(&txt), -1, fossil_free);
433 }
434
435 /*
436 ** Encode a string for use as a query parameter in a URL
437 */
438 static void search_urlencode_sqlfunc(
439 sqlite3_context *context,
440 int argc,
441 sqlite3_value **argv
442 ){
443 char *z = mprintf("%T",sqlite3_value_text(argv[0]));
444 sqlite3_result_text(context, z, -1, fossil_free);
445 }
446
447 /*
448 ** Register the "score()" SQL function to score its input text
449 ** using the given Search object. Once this function is registered,
450 ** do not delete the Search object.
@@ -424,10 +454,14 @@
454 search_score_sqlfunc, 0, 0);
455 sqlite3_create_function(db, "snippet", -1, SQLITE_UTF8, &gSearch,
456 search_score_sqlfunc, 0, 0);
457 sqlite3_create_function(db, "search_init", -1, SQLITE_UTF8, 0,
458 search_init_sqlfunc, 0, 0);
459 sqlite3_create_function(db, "stext", 3, SQLITE_UTF8, 0,
460 search_stext_sqlfunc, 0, 0);
461 sqlite3_create_function(db, "urlencode", 1, SQLITE_UTF8, 0,
462 search_urlencode_sqlfunc, 0, 0);
463 }
464
465 /*
466 ** Testing the search function.
467 **
@@ -522,10 +556,11 @@
556 ** * Search wiki
557 */
558 void search_page(void){
559 const char *zPattern = PD("s","");
560 Stmt q;
561 const char *zSrchEnable = "dwc";
562
563 login_check_credentials();
564 if( !g.perm.Read ){ login_needed(); return; }
565 style_header("Search");
566 @ <form method="GET" action="search"><center>
@@ -537,30 +572,72 @@
572 search_sql_setup(g.db);
573 add_content_sql_commands(g.db);
574 search_init(zPattern, "<b>", "</b>", " ... ",
575 SRCHFLG_STATIC|SRCHFLG_HTML|SRCHFLG_SCORE);
576 db_multi_exec(
577 "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;"
578 "CREATE TEMP TABLE x(label TEXT,url TEXT,date TEXT,snip TEXT);"
 
 
 
 
 
 
 
 
 
579 );
580 if( strchr(zSrchEnable, 'd') ){
581 db_multi_exec(
582 "INSERT INTO x(label,url,date,snip)"
583 " SELECT printf('Document: %%s',foci.filename),"
584 " printf('%R/doc/trunk/%%s',foci.filename),"
585 " (SELECT datetime(event.mtime) FROM event"
586 " WHERE objid=symbolic_name_to_rid('trunk')),"
587 " snippet(stext('d',blob.rid,foci.filename))"
588 " FROM foci CROSS JOIN blob"
589 " WHERE checkinID=symbolic_name_to_rid('trunk')"
590 " AND blob.uuid=foci.uuid"
591 " AND (filename GLOB '*.wiki' OR"
592 " filename GLOB '*.md' OR"
593 " filename GLOB '*.txt' OR"
594 " filename GLOB '*.html');"
595 );
596 }
597 if( strchr(zSrchEnable, 'w') ){
598 db_multi_exec(
599 "WITH wiki(name,rid,mtime) AS ("
600 " SELECT substr(tagname,6), tagxref.rid, max(tagxref.mtime)"
601 " FROM tag, tagxref"
602 " WHERE tag.tagname GLOB 'wiki-*'"
603 " AND tagxref.tagid=tag.tagid"
604 " GROUP BY 1"
605 ")"
606 "INSERT INTO x(label,url,date,snip)"
607 " SELECT printf('Wiki: %%s',name),"
608 " printf('%R/wiki?name=%%s',urlencode(name)),"
609 " datetime(mtime),"
610 " snippet(stext('w',rid,name))"
611 " FROM wiki;"
612 );
613 }
614 if( strchr(zSrchEnable, 'c') ){
615 db_multi_exec(
616 "WITH ckin(uuid,rid,mtime) AS ("
617 " SELECT blob.uuid, event.objid, event.mtime"
618 " FROM event, blob"
619 " WHERE event.type='ci'"
620 " AND blob.rid=event.objid"
621 ")"
622 "INSERT INTO x(label,url,date,snip)"
623 " SELECT printf('Check-in [%%.10s] on %%s',uuid,datetime(mtime)),"
624 " printf('%R/timeline?c=%%s&n=8&y=ci',uuid),"
625 " datetime(mtime),"
626 " snippet(stext('c',rid,NULL))"
627 " FROM ckin;"
628 );
629 }
630 db_prepare(&q, "SELECT url, substr(snip,9), label"
631 " FROM x WHERE snip IS NOT NULL"
632 " ORDER BY substr(snip,1,9) DESC, date DESC;");
633 @ <ol>
634 while( db_step(&q)==SQLITE_ROW ){
635 const char *zUrl = db_column_text(&q, 0);
636 const char *zSnippet = db_column_text(&q, 1);
637 const char *zLabel = db_column_text(&q, 2);
638 @ <li><p>%s(href("%s",zUrl))%h(zLabel)</a><br>%s(zSnippet)</li>
639 }
640 db_finalize(&q);
641 @ </ol>
642 }
643 style_footer();
@@ -639,21 +716,82 @@
716 pOut);
717 blob_reset(&wiki);
718 manifest_destroy(pWiki);
719 break;
720 }
721 case 'c': { /* Ckeckin: zArg1: RID of the checkin. zArg2: Not used */
722 int rid = atoi(zArg1);
723 static Stmt q;
724 db_static_prepare(&q,
725 "SELECT coalesce(ecomment,comment)"
726 " ||' (user: '||coalesce(euser,user,'?')"
727 " ||', tags: '||"
728 " (SELECT group_concat(substr(tag.tagname,5),',')"
729 " FROM tag, tagxref"
730 " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
731 " AND tagxref.rid=event.objid AND tagxref.tagtype>0)"
732 " ||')'"
733 " FROM event WHERE objid=:x AND type='ci'");
734 db_bind_int(&q, ":x", rid);
735 if( db_step(&q)==SQLITE_ROW ){
736 db_column_blob(&q, 0, pOut);
737 blob_append(pOut, "\n", 1);
738 }
739 db_reset(&q);
740 break;
741 }
742 }
743 }
744
745 /*
746 ** The arguments cType,zArg1,zArg2 define an object that can be searched
747 ** for. Return a URL (relative to the root of the Fossil project) that
748 ** will jump to that document.
749 **
750 ** Space to hold the returned string is obtained from mprintf() and should
751 ** be freed by the caller using fossil_free() or the equivalent.
752 */
753 char *search_url(
754 char cType, /* Type of document */
755 const char *zArg1, /* First parameter */
756 const char *zArg2 /* Second parameter */
757 ){
758 char *zUrl = 0;
759 switch( cType ){
760 case 'd': { /* Doc. zArg1: RID of the file. zArg2: Filename */
761 case 's': /* Source. zArg1: RID of the file. zArg2: Filename */
762 zUrl = db_text(0,
763 "SELECT printf('/doc/%%s%%s', substr(blob.uuid,20), %Q)"
764 " FROM mlink, blob"
765 " WHERE mlink.fid=%d AND mlink.mid=blob.rid",
766 zArg2, atoi(zArg1));
767 break;
768 }
769 case 'w': { /* Wiki. zArg1: RID of the page. zArg2: Page name */
770 char *zId = db_text(0, "SELECT uuid FROM blob WHERE rid=%d",atoi(zArg1));
771 zUrl = mprintf("/wiki?id=%z&name=%t", zId, zArg2);
772 break;
773 }
774 case 'c': { /* Ckeckin: zArg1: RID of the checkin. zArg2: Not used */
775 char *zId = db_text(0, "SELECT uuid FROM blob WHERE rid=%d",atoi(zArg1));
776 zUrl = mprintf("/info/%z", zId);
777 break;
778 }
779 }
780 return zUrl;
781 }
782
783 /*
784 ** COMMAND: test-search-stext
785 **
786 ** Usage: fossil test-search-stext TYPE ARG1 ARG2
787 */
788 void test_search_stext(void){
789 Blob out;
790 char *zUrl;
791 db_find_and_open_repository(0,0);
792 if( g.argc!=5 ) usage("TYPE ARG1 ARG2");
793 search_stext(g.argv[2][0], g.argv[3], g.argv[4], &out);
794 zUrl = search_url(g.argv[2][0], g.argv[3], g.argv[4]);
795 fossil_print("%s\n%z\n",blob_str(&out),zUrl);
796 blob_reset(&out);
797 }
798
+10 -7
--- src/wiki.c
+++ src/wiki.c
@@ -264,17 +264,20 @@
264264
if( isSandbox ){
265265
zBody = db_get("sandbox",zBody);
266266
zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki");
267267
rid = 0;
268268
}else{
269
- zTag = mprintf("wiki-%s", zPageName);
270
- rid = db_int(0,
271
- "SELECT rid FROM tagxref"
272
- " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
273
- " ORDER BY mtime DESC", zTag
274
- );
275
- free(zTag);
269
+ const char *zUuid = P("id");
270
+ if( zUuid==0 || (rid = symbolic_name_to_rid(zUuid,"w"))==0 ){
271
+ zTag = mprintf("wiki-%s", zPageName);
272
+ rid = db_int(0,
273
+ "SELECT rid FROM tagxref"
274
+ " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
275
+ " ORDER BY mtime DESC", zTag
276
+ );
277
+ free(zTag);
278
+ }
276279
pWiki = manifest_get(rid, CFTYPE_WIKI, 0);
277280
if( pWiki ){
278281
zBody = pWiki->zWiki;
279282
zMimetype = pWiki->zMimetype;
280283
}
281284
--- src/wiki.c
+++ src/wiki.c
@@ -264,17 +264,20 @@
264 if( isSandbox ){
265 zBody = db_get("sandbox",zBody);
266 zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki");
267 rid = 0;
268 }else{
269 zTag = mprintf("wiki-%s", zPageName);
270 rid = db_int(0,
271 "SELECT rid FROM tagxref"
272 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
273 " ORDER BY mtime DESC", zTag
274 );
275 free(zTag);
 
 
 
276 pWiki = manifest_get(rid, CFTYPE_WIKI, 0);
277 if( pWiki ){
278 zBody = pWiki->zWiki;
279 zMimetype = pWiki->zMimetype;
280 }
281
--- src/wiki.c
+++ src/wiki.c
@@ -264,17 +264,20 @@
264 if( isSandbox ){
265 zBody = db_get("sandbox",zBody);
266 zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki");
267 rid = 0;
268 }else{
269 const char *zUuid = P("id");
270 if( zUuid==0 || (rid = symbolic_name_to_rid(zUuid,"w"))==0 ){
271 zTag = mprintf("wiki-%s", zPageName);
272 rid = db_int(0,
273 "SELECT rid FROM tagxref"
274 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
275 " ORDER BY mtime DESC", zTag
276 );
277 free(zTag);
278 }
279 pWiki = manifest_get(rid, CFTYPE_WIKI, 0);
280 if( pWiki ){
281 zBody = pWiki->zWiki;
282 zMimetype = pWiki->zMimetype;
283 }
284

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button