Fossil SCM

Improved interfaces for Ticket and Wiki searching.

drh 2015-02-02 03:42 trunk
Commit 6714c9471b3508514399c812d67fd86096e09612
--- skins/black_and_white/header.txt
+++ skins/black_and_white/header.txt
@@ -34,11 +34,11 @@
3434
if {[hascap o]} {
3535
html "<a href='$home/brlist'>Branches</a>\n"
3636
html "<a href='$home/taglist'>Tags</a>\n"
3737
}
3838
if {[hascap r]} {
39
- html "<a href='$home/reportlist'>Tickets</a>\n"
39
+ html "<a href='$home/ticket'>Tickets</a>\n"
4040
}
4141
if {[hascap j]} {
4242
html "<a href='$home/wiki'>Wiki</a>\n"
4343
}
4444
if {[hascap s]} {
4545
--- skins/black_and_white/header.txt
+++ skins/black_and_white/header.txt
@@ -34,11 +34,11 @@
34 if {[hascap o]} {
35 html "<a href='$home/brlist'>Branches</a>\n"
36 html "<a href='$home/taglist'>Tags</a>\n"
37 }
38 if {[hascap r]} {
39 html "<a href='$home/reportlist'>Tickets</a>\n"
40 }
41 if {[hascap j]} {
42 html "<a href='$home/wiki'>Wiki</a>\n"
43 }
44 if {[hascap s]} {
45
--- skins/black_and_white/header.txt
+++ skins/black_and_white/header.txt
@@ -34,11 +34,11 @@
34 if {[hascap o]} {
35 html "<a href='$home/brlist'>Branches</a>\n"
36 html "<a href='$home/taglist'>Tags</a>\n"
37 }
38 if {[hascap r]} {
39 html "<a href='$home/ticket'>Tickets</a>\n"
40 }
41 if {[hascap j]} {
42 html "<a href='$home/wiki'>Wiki</a>\n"
43 }
44 if {[hascap s]} {
45
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -33,11 +33,11 @@
3333
if {[hascap o]} {
3434
html "<a href='$home/brlist'>Branches</a>\n"
3535
html "<a href='$home/taglist'>Tags</a>\n"
3636
}
3737
if {[hascap r]} {
38
- html "<a href='$home/reportlist'>Tickets</a>\n"
38
+ html "<a href='$home/ticket'>Tickets</a>\n"
3939
}
4040
if {[hascap j]} {
4141
html "<a href='$home/wiki'>Wiki</a>\n"
4242
}
4343
if {[hascap s]} {
4444
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -33,11 +33,11 @@
33 if {[hascap o]} {
34 html "<a href='$home/brlist'>Branches</a>\n"
35 html "<a href='$home/taglist'>Tags</a>\n"
36 }
37 if {[hascap r]} {
38 html "<a href='$home/reportlist'>Tickets</a>\n"
39 }
40 if {[hascap j]} {
41 html "<a href='$home/wiki'>Wiki</a>\n"
42 }
43 if {[hascap s]} {
44
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -33,11 +33,11 @@
33 if {[hascap o]} {
34 html "<a href='$home/brlist'>Branches</a>\n"
35 html "<a href='$home/taglist'>Tags</a>\n"
36 }
37 if {[hascap r]} {
38 html "<a href='$home/ticket'>Tickets</a>\n"
39 }
40 if {[hascap j]} {
41 html "<a href='$home/wiki'>Wiki</a>\n"
42 }
43 if {[hascap s]} {
44
--- skins/eagle/header.txt
+++ skins/eagle/header.txt
@@ -114,11 +114,11 @@
114114
if {[hascap o]} {
115115
html "<a href='$home/brlist'>Branches</a>\n"
116116
html "<a href='$home/taglist'>Tags</a>\n"
117117
}
118118
if {[hascap r]} {
119
- html "<a href='$home/reportlist'>Tickets</a>\n"
119
+ html "<a href='$home/ticket'>Tickets</a>\n"
120120
}
121121
if {[hascap j]} {
122122
html "<a href='$home/wiki'>Wiki</a>\n"
123123
}
124124
if {[hascap s]} {
125125
--- skins/eagle/header.txt
+++ skins/eagle/header.txt
@@ -114,11 +114,11 @@
114 if {[hascap o]} {
115 html "<a href='$home/brlist'>Branches</a>\n"
116 html "<a href='$home/taglist'>Tags</a>\n"
117 }
118 if {[hascap r]} {
119 html "<a href='$home/reportlist'>Tickets</a>\n"
120 }
121 if {[hascap j]} {
122 html "<a href='$home/wiki'>Wiki</a>\n"
123 }
124 if {[hascap s]} {
125
--- skins/eagle/header.txt
+++ skins/eagle/header.txt
@@ -114,11 +114,11 @@
114 if {[hascap o]} {
115 html "<a href='$home/brlist'>Branches</a>\n"
116 html "<a href='$home/taglist'>Tags</a>\n"
117 }
118 if {[hascap r]} {
119 html "<a href='$home/ticket'>Tickets</a>\n"
120 }
121 if {[hascap j]} {
122 html "<a href='$home/wiki'>Wiki</a>\n"
123 }
124 if {[hascap s]} {
125
--- skins/enhanced1/header.txt
+++ skins/enhanced1/header.txt
@@ -114,11 +114,11 @@
114114
if {[hascap o]} {
115115
html "<a href='$home/brlist'>Branches</a>\n"
116116
html "<a href='$home/taglist'>Tags</a>\n"
117117
}
118118
if {[hascap r]} {
119
- html "<a href='$home/reportlist'>Tickets</a>\n"
119
+ html "<a href='$home/ticket'>Tickets</a>\n"
120120
}
121121
if {[hascap j]} {
122122
html "<a href='$home/wiki'>Wiki</a>\n"
123123
}
124124
if {[hascap s]} {
125125
--- skins/enhanced1/header.txt
+++ skins/enhanced1/header.txt
@@ -114,11 +114,11 @@
114 if {[hascap o]} {
115 html "<a href='$home/brlist'>Branches</a>\n"
116 html "<a href='$home/taglist'>Tags</a>\n"
117 }
118 if {[hascap r]} {
119 html "<a href='$home/reportlist'>Tickets</a>\n"
120 }
121 if {[hascap j]} {
122 html "<a href='$home/wiki'>Wiki</a>\n"
123 }
124 if {[hascap s]} {
125
--- skins/enhanced1/header.txt
+++ skins/enhanced1/header.txt
@@ -114,11 +114,11 @@
114 if {[hascap o]} {
115 html "<a href='$home/brlist'>Branches</a>\n"
116 html "<a href='$home/taglist'>Tags</a>\n"
117 }
118 if {[hascap r]} {
119 html "<a href='$home/ticket'>Tickets</a>\n"
120 }
121 if {[hascap j]} {
122 html "<a href='$home/wiki'>Wiki</a>\n"
123 }
124 if {[hascap s]} {
125
--- skins/khaki/header.txt
+++ skins/khaki/header.txt
@@ -32,11 +32,11 @@
3232
if {[hascap o]} {
3333
html "<a href='$home/brlist'>Branches</a>\n"
3434
html "<a href='$home/taglist'>Tags</a>\n"
3535
}
3636
if {[hascap r]} {
37
- html "<a href='$home/reportlist'>Tickets</a>\n"
37
+ html "<a href='$home/ticket'>Tickets</a>\n"
3838
}
3939
if {[hascap j]} {
4040
html "<a href='$home/wiki'>Wiki</a>\n"
4141
}
4242
if {[hascap s]} {
4343
--- skins/khaki/header.txt
+++ skins/khaki/header.txt
@@ -32,11 +32,11 @@
32 if {[hascap o]} {
33 html "<a href='$home/brlist'>Branches</a>\n"
34 html "<a href='$home/taglist'>Tags</a>\n"
35 }
36 if {[hascap r]} {
37 html "<a href='$home/reportlist'>Tickets</a>\n"
38 }
39 if {[hascap j]} {
40 html "<a href='$home/wiki'>Wiki</a>\n"
41 }
42 if {[hascap s]} {
43
--- skins/khaki/header.txt
+++ skins/khaki/header.txt
@@ -32,11 +32,11 @@
32 if {[hascap o]} {
33 html "<a href='$home/brlist'>Branches</a>\n"
34 html "<a href='$home/taglist'>Tags</a>\n"
35 }
36 if {[hascap r]} {
37 html "<a href='$home/ticket'>Tickets</a>\n"
38 }
39 if {[hascap j]} {
40 html "<a href='$home/wiki'>Wiki</a>\n"
41 }
42 if {[hascap s]} {
43
--- skins/plain_gray/header.txt
+++ skins/plain_gray/header.txt
@@ -30,11 +30,11 @@
3030
if {[hascap o]} {
3131
html "<a href='$home/brlist'>Branches</a>\n"
3232
html "<a href='$home/taglist'>Tags</a>\n"
3333
}
3434
if {[hascap r]} {
35
- html "<a href='$home/reportlist'>Tickets</a>\n"
35
+ html "<a href='$home/ticket'>Tickets</a>\n"
3636
}
3737
if {[hascap j]} {
3838
html "<a href='$home/wiki'>Wiki</a>\n"
3939
}
4040
if {[hascap s]} {
4141
--- skins/plain_gray/header.txt
+++ skins/plain_gray/header.txt
@@ -30,11 +30,11 @@
30 if {[hascap o]} {
31 html "<a href='$home/brlist'>Branches</a>\n"
32 html "<a href='$home/taglist'>Tags</a>\n"
33 }
34 if {[hascap r]} {
35 html "<a href='$home/reportlist'>Tickets</a>\n"
36 }
37 if {[hascap j]} {
38 html "<a href='$home/wiki'>Wiki</a>\n"
39 }
40 if {[hascap s]} {
41
--- skins/plain_gray/header.txt
+++ skins/plain_gray/header.txt
@@ -30,11 +30,11 @@
30 if {[hascap o]} {
31 html "<a href='$home/brlist'>Branches</a>\n"
32 html "<a href='$home/taglist'>Tags</a>\n"
33 }
34 if {[hascap r]} {
35 html "<a href='$home/ticket'>Tickets</a>\n"
36 }
37 if {[hascap j]} {
38 html "<a href='$home/wiki'>Wiki</a>\n"
39 }
40 if {[hascap s]} {
41
--- skins/rounded1/header.txt
+++ skins/rounded1/header.txt
@@ -34,11 +34,11 @@
3434
if {[hascap o]} {
3535
html "<a href='$home/brlist'>Branches</a>\n"
3636
html "<a href='$home/taglist'>Tags</a>\n"
3737
}
3838
if {[hascap r]} {
39
- html "<a href='$home/reportlist'>Tickets</a>\n"
39
+ html "<a href='$home/ticket'>Tickets</a>\n"
4040
}
4141
if {[hascap j]} {
4242
html "<a href='$home/wiki'>Wiki</a>\n"
4343
}
4444
if {[hascap s]} {
4545
--- skins/rounded1/header.txt
+++ skins/rounded1/header.txt
@@ -34,11 +34,11 @@
34 if {[hascap o]} {
35 html "<a href='$home/brlist'>Branches</a>\n"
36 html "<a href='$home/taglist'>Tags</a>\n"
37 }
38 if {[hascap r]} {
39 html "<a href='$home/reportlist'>Tickets</a>\n"
40 }
41 if {[hascap j]} {
42 html "<a href='$home/wiki'>Wiki</a>\n"
43 }
44 if {[hascap s]} {
45
--- skins/rounded1/header.txt
+++ skins/rounded1/header.txt
@@ -34,11 +34,11 @@
34 if {[hascap o]} {
35 html "<a href='$home/brlist'>Branches</a>\n"
36 html "<a href='$home/taglist'>Tags</a>\n"
37 }
38 if {[hascap r]} {
39 html "<a href='$home/ticket'>Tickets</a>\n"
40 }
41 if {[hascap j]} {
42 html "<a href='$home/wiki'>Wiki</a>\n"
43 }
44 if {[hascap s]} {
45
--- src/report.c
+++ src/report.c
@@ -29,10 +29,12 @@
2929
# define SQLITE_RECURSIVE 33
3030
#endif
3131
3232
/*
3333
** WEBPAGE: /reportlist
34
+**
35
+** Main menu for Tickets.
3436
*/
3537
void view_list(void){
3638
const char *zScript;
3739
Blob ril; /* Report Item List */
3840
Stmt q;
@@ -40,10 +42,11 @@
4042
int cnt = 0;
4143
4244
login_check_credentials();
4345
if( !g.perm.RdTkt && !g.perm.NewTkt ){ login_needed(); return; }
4446
style_header("Ticket Main Menu");
47
+ ticket_standard_submenu(T_ALL_BUT(T_REPLIST));
4548
if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST<br />\n", -1);
4649
zScript = ticket_reportlist_code();
4750
if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST_SCRIPT<br />\n", -1);
4851
4952
blob_zero(&ril);
5053
--- src/report.c
+++ src/report.c
@@ -29,10 +29,12 @@
29 # define SQLITE_RECURSIVE 33
30 #endif
31
32 /*
33 ** WEBPAGE: /reportlist
 
 
34 */
35 void view_list(void){
36 const char *zScript;
37 Blob ril; /* Report Item List */
38 Stmt q;
@@ -40,10 +42,11 @@
40 int cnt = 0;
41
42 login_check_credentials();
43 if( !g.perm.RdTkt && !g.perm.NewTkt ){ login_needed(); return; }
44 style_header("Ticket Main Menu");
 
45 if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST<br />\n", -1);
46 zScript = ticket_reportlist_code();
47 if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST_SCRIPT<br />\n", -1);
48
49 blob_zero(&ril);
50
--- src/report.c
+++ src/report.c
@@ -29,10 +29,12 @@
29 # define SQLITE_RECURSIVE 33
30 #endif
31
32 /*
33 ** WEBPAGE: /reportlist
34 **
35 ** Main menu for Tickets.
36 */
37 void view_list(void){
38 const char *zScript;
39 Blob ril; /* Report Item List */
40 Stmt q;
@@ -40,10 +42,11 @@
42 int cnt = 0;
43
44 login_check_credentials();
45 if( !g.perm.RdTkt && !g.perm.NewTkt ){ login_needed(); return; }
46 style_header("Ticket Main Menu");
47 ticket_standard_submenu(T_ALL_BUT(T_REPLIST));
48 if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST<br />\n", -1);
49 zScript = ticket_reportlist_code();
50 if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST_SCRIPT<br />\n", -1);
51
52 blob_zero(&ril);
53
+155 -110
--- src/search.c
+++ src/search.c
@@ -535,139 +535,184 @@
535535
db_prepare(&q, "%s", blob_sql_text(&sql));
536536
blob_reset(&sql);
537537
print_timeline(&q, nLimit, width, 0);
538538
db_finalize(&q);
539539
}
540
+
541
+#if INTERFACE
542
+/* What to search for */
543
+#define SRCH_CKIN 0x0001 /* Search over check-in comments */
544
+#define SRCH_DOC 0x0002 /* Search over embedded documents */
545
+#define SRCH_TKT 0x0004 /* Search over tickets */
546
+#define SRCH_WIKI 0x0008 /* Search over wiki */
547
+#define SRCH_ALL 0x000f /* Search over everything */
548
+#endif
549
+
550
+/*
551
+** Remove bits from srchFlags which are disallowed by either the
552
+** current server configuration or by user permissions.
553
+*/
554
+unsigned int search_restrict(unsigned int srchFlags){
555
+ if( (srchFlags & SRCH_CKIN)!=0
556
+ && (g.perm.Read==0 || db_get_boolean("search-ci",0)==0) ){
557
+ srchFlags &= ~SRCH_CKIN;
558
+ }
559
+ if( (srchFlags & SRCH_DOC)!=0
560
+ && (g.perm.Read==0 || db_get_boolean("search-doc",0)==0) ){
561
+ srchFlags &= ~SRCH_DOC;
562
+ }
563
+ if( (srchFlags & SRCH_TKT)!=0
564
+ && (g.perm.RdTkt==0 || db_get_boolean("search-tkt",0)==0) ){
565
+ srchFlags &= ~SRCH_TKT;
566
+ }
567
+ if( (srchFlags & SRCH_WIKI)!=0
568
+ && (g.perm.RdWiki==0 || db_get_boolean("search-wiki",0)==0) ){
569
+ srchFlags &= ~SRCH_WIKI;
570
+ }
571
+ return srchFlags;
572
+}
573
+
574
+/*
575
+** This routine generates web-page output for a search operation.
576
+** Other web-pages can invoke this routine to add search results
577
+** in the middle of the page.
578
+**
579
+** Return the number of rows.
580
+*/
581
+int search_run_and_output(
582
+ const char *zPattern, /* The query pattern */
583
+ unsigned int srchFlags /* What to search over */
584
+){
585
+ Stmt q;
586
+ int nRow = 0;
587
+
588
+ srchFlags = search_restrict(srchFlags);
589
+ if( srchFlags==0 ) return 0;
590
+ search_sql_setup(g.db);
591
+ add_content_sql_commands(g.db);
592
+ search_init(zPattern, "<b>", "</b>", " ... ",
593
+ SRCHFLG_STATIC|SRCHFLG_HTML|SRCHFLG_SCORE);
594
+ db_multi_exec(
595
+ "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;"
596
+ "CREATE TEMP TABLE x(label TEXT,url TEXT,date TEXT,snip TEXT);"
597
+ );
598
+ if( (srchFlags & SRCH_DOC)!=0 ){
599
+ char *zDocGlob = db_get("doc-glob","");
600
+ char *zDocBr = db_get("doc-branch","trunk");
601
+ if( zDocGlob && zDocGlob[0] && zDocBr && zDocBr[0] ){
602
+ db_multi_exec(
603
+ "INSERT INTO x(label,url,date,snip)"
604
+ " SELECT printf('Document: %%s',foci.filename),"
605
+ " printf('%R/doc/%T/%%s',foci.filename),"
606
+ " (SELECT datetime(event.mtime) FROM event"
607
+ " WHERE objid=symbolic_name_to_rid('trunk')),"
608
+ " snippet(stext('d',blob.rid,foci.filename))"
609
+ " FROM foci CROSS JOIN blob"
610
+ " WHERE checkinID=symbolic_name_to_rid('trunk')"
611
+ " AND blob.uuid=foci.uuid"
612
+ " AND %z",
613
+ zDocBr, glob_expr("foci.filename", zDocGlob)
614
+ );
615
+ }
616
+ }
617
+ if( (srchFlags & SRCH_WIKI)!=0 ){
618
+ db_multi_exec(
619
+ "WITH wiki(name,rid,mtime) AS ("
620
+ " SELECT substr(tagname,6), tagxref.rid, max(tagxref.mtime)"
621
+ " FROM tag, tagxref"
622
+ " WHERE tag.tagname GLOB 'wiki-*'"
623
+ " AND tagxref.tagid=tag.tagid"
624
+ " GROUP BY 1"
625
+ ")"
626
+ "INSERT INTO x(label,url,date,snip)"
627
+ " SELECT printf('Wiki: %%s',name),"
628
+ " printf('%R/wiki?name=%%s',urlencode(name)),"
629
+ " datetime(mtime),"
630
+ " snippet(stext('w',rid,name))"
631
+ " FROM wiki;"
632
+ );
633
+ }
634
+ if( (srchFlags & SRCH_CKIN)!=0 ){
635
+ db_multi_exec(
636
+ "WITH ckin(uuid,rid,mtime) AS ("
637
+ " SELECT blob.uuid, event.objid, event.mtime"
638
+ " FROM event, blob"
639
+ " WHERE event.type='ci'"
640
+ " AND blob.rid=event.objid"
641
+ ")"
642
+ "INSERT INTO x(label,url,date,snip)"
643
+ " SELECT printf('Check-in [%%.10s] on %%s',uuid,datetime(mtime)),"
644
+ " printf('%R/timeline?c=%%s&n=8&y=ci',uuid),"
645
+ " datetime(mtime),"
646
+ " snippet(stext('c',rid,NULL))"
647
+ " FROM ckin;"
648
+ );
649
+ }
650
+ if( (srchFlags & SRCH_TKT)!=0 ){
651
+ db_multi_exec(
652
+ "INSERT INTO x(label,url,date,snip)"
653
+ " SELECT printf('Ticket [%%.17s] on %%s',"
654
+ "tkt_uuid,datetime(tkt_mtime)),"
655
+ " printf('%R/tktview/%%.20s',tkt_uuid),"
656
+ " datetime(tkt_mtime),"
657
+ " snippet(stext('t',tkt_id,NULL))"
658
+ " FROM ticket;"
659
+ );
660
+ }
661
+ db_prepare(&q, "SELECT url, substr(snip,9), label"
662
+ " FROM x WHERE snip IS NOT NULL"
663
+ " ORDER BY substr(snip,1,8) DESC, date DESC;");
664
+ while( db_step(&q)==SQLITE_ROW ){
665
+ const char *zUrl = db_column_text(&q, 0);
666
+ const char *zSnippet = db_column_text(&q, 1);
667
+ const char *zLabel = db_column_text(&q, 2);
668
+ if( nRow==0 ){
669
+ @ <ol>
670
+ }
671
+ nRow++;
672
+ @ <li><p>%s(href("%s",zUrl))%h(zLabel)</a><br>%s(zSnippet)</li>
673
+ }
674
+ db_finalize(&q);
675
+ if( nRow ){
676
+ @ </ol>
677
+ }
678
+ return nRow;
679
+}
540680
541681
/*
542682
** WEBPAGE: /search
543683
**
544
-** This is an EXPERIMENTAL page for doing search across a repository.
545
-**
546
-** The current implementation does a full text search over embedded
547
-** documentation files on the tip of the "trunk" branch. Only files
548
-** ending in ".wiki", ".md", ".html", and ".txt" are searched.
549
-**
550
-** The entire text is scanned. There is no full-text index. This is
551
-** experimental. We may change to using a full-text index depending
552
-** on performance.
553
-**
554
-** Other pending enhancements:
555
-** * Search tickets
556
-** * Search wiki
684
+** Search for check-in comments, documents, tickets, or wiki that
685
+** match a user-supplied pattern.
557686
*/
558687
void search_page(void){
559688
const char *zPattern = PD("s","");
560
- Stmt q;
561
- int okCheckin;
562
- int okDoc;
563
- int okTicket;
564
- int okWiki;
565
- int allOff;
689
+ unsigned srchFlags = 0;
566690
const char *zDisable;
567691
568692
login_check_credentials();
569
- okCheckin = g.perm.Read && db_get_boolean("search-ci",0);
570
- okDoc = g.perm.Read && db_get_boolean("search-doc",0);
571
- okTicket = g.perm.RdTkt && db_get_boolean("search-tkt",0);
572
- okWiki = g.perm.RdWiki && db_get_boolean("search-wiki",0);
573
- allOff = (okCheckin + okDoc + okTicket + okWiki == 0);
574
- zDisable = allOff ? " disabled" : "";
575
- zPattern = allOff ? "" : PD("s","");
693
+ srchFlags = search_restrict(SRCH_ALL);
694
+ if( srchFlags==0 ){
695
+ zDisable = " disabled";
696
+ zPattern = "";
697
+ }else{
698
+ zDisable = "";
699
+ zPattern = PD("s","");
700
+ }
576701
style_header("Search");
577702
@ <form method="GET" action="search"><center>
578703
@ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable)>
579704
@ <input type="submit" value="Search"%s(zDisable)>
580
- if( allOff ){
705
+ if( srchFlags==0 ){
581706
@ <p class="generalError">Search is disabled</p>
582707
}
583708
@ </center></form>
584709
while( fossil_isspace(zPattern[0]) ) zPattern++;
585710
if( zPattern[0] ){
586
- search_sql_setup(g.db);
587
- add_content_sql_commands(g.db);
588
- search_init(zPattern, "<b>", "</b>", " ... ",
589
- SRCHFLG_STATIC|SRCHFLG_HTML|SRCHFLG_SCORE);
590
- db_multi_exec(
591
- "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;"
592
- "CREATE TEMP TABLE x(label TEXT,url TEXT,date TEXT,snip TEXT);"
593
- );
594
- if( okDoc ){
595
- char *zDocGlob = db_get("doc-glob","");
596
- char *zDocBr = db_get("doc-branch","trunk");
597
- if( zDocGlob && zDocGlob[0] && zDocBr && zDocBr[0] ){
598
- db_multi_exec(
599
- "INSERT INTO x(label,url,date,snip)"
600
- " SELECT printf('Document: %%s',foci.filename),"
601
- " printf('%R/doc/%T/%%s',foci.filename),"
602
- " (SELECT datetime(event.mtime) FROM event"
603
- " WHERE objid=symbolic_name_to_rid('trunk')),"
604
- " snippet(stext('d',blob.rid,foci.filename))"
605
- " FROM foci CROSS JOIN blob"
606
- " WHERE checkinID=symbolic_name_to_rid('trunk')"
607
- " AND blob.uuid=foci.uuid"
608
- " AND %z",
609
- zDocBr, glob_expr("foci.filename", zDocGlob)
610
- );
611
- }
612
- }
613
- if( okWiki ){
614
- db_multi_exec(
615
- "WITH wiki(name,rid,mtime) AS ("
616
- " SELECT substr(tagname,6), tagxref.rid, max(tagxref.mtime)"
617
- " FROM tag, tagxref"
618
- " WHERE tag.tagname GLOB 'wiki-*'"
619
- " AND tagxref.tagid=tag.tagid"
620
- " GROUP BY 1"
621
- ")"
622
- "INSERT INTO x(label,url,date,snip)"
623
- " SELECT printf('Wiki: %%s',name),"
624
- " printf('%R/wiki?name=%%s',urlencode(name)),"
625
- " datetime(mtime),"
626
- " snippet(stext('w',rid,name))"
627
- " FROM wiki;"
628
- );
629
- }
630
- if( okCheckin ){
631
- db_multi_exec(
632
- "WITH ckin(uuid,rid,mtime) AS ("
633
- " SELECT blob.uuid, event.objid, event.mtime"
634
- " FROM event, blob"
635
- " WHERE event.type='ci'"
636
- " AND blob.rid=event.objid"
637
- ")"
638
- "INSERT INTO x(label,url,date,snip)"
639
- " SELECT printf('Check-in [%%.10s] on %%s',uuid,datetime(mtime)),"
640
- " printf('%R/timeline?c=%%s&n=8&y=ci',uuid),"
641
- " datetime(mtime),"
642
- " snippet(stext('c',rid,NULL))"
643
- " FROM ckin;"
644
- );
645
- }
646
- if( okTicket ){
647
- db_multi_exec(
648
- "INSERT INTO x(label,url,date,snip)"
649
- " SELECT printf('Ticket [%%.17s] on %%s',"
650
- "tkt_uuid,datetime(tkt_mtime)),"
651
- " printf('%R/tktview/%%.20s',tkt_uuid),"
652
- " datetime(tkt_mtime),"
653
- " snippet(stext('t',tkt_id,NULL))"
654
- " FROM ticket;"
655
- );
656
- }
657
- db_prepare(&q, "SELECT url, substr(snip,9), label"
658
- " FROM x WHERE snip IS NOT NULL"
659
- " ORDER BY substr(snip,1,8) DESC, date DESC;");
660
- @ <ol>
661
- while( db_step(&q)==SQLITE_ROW ){
662
- const char *zUrl = db_column_text(&q, 0);
663
- const char *zSnippet = db_column_text(&q, 1);
664
- const char *zLabel = db_column_text(&q, 2);
665
- @ <li><p>%s(href("%s",zUrl))%h(zLabel)</a><br>%s(zSnippet)</li>
666
- }
667
- db_finalize(&q);
668
- @ </ol>
711
+ if( search_run_and_output(zPattern, srchFlags)==0 ){
712
+ @ <p><i>No matches for: "%h(zPattern)"</i></p>
713
+ }
669714
}
670715
style_footer();
671716
}
672717
673718
674719
--- src/search.c
+++ src/search.c
@@ -535,139 +535,184 @@
535 db_prepare(&q, "%s", blob_sql_text(&sql));
536 blob_reset(&sql);
537 print_timeline(&q, nLimit, width, 0);
538 db_finalize(&q);
539 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
540
541 /*
542 ** WEBPAGE: /search
543 **
544 ** This is an EXPERIMENTAL page for doing search across a repository.
545 **
546 ** The current implementation does a full text search over embedded
547 ** documentation files on the tip of the "trunk" branch. Only files
548 ** ending in ".wiki", ".md", ".html", and ".txt" are searched.
549 **
550 ** The entire text is scanned. There is no full-text index. This is
551 ** experimental. We may change to using a full-text index depending
552 ** on performance.
553 **
554 ** Other pending enhancements:
555 ** * Search tickets
556 ** * Search wiki
557 */
558 void search_page(void){
559 const char *zPattern = PD("s","");
560 Stmt q;
561 int okCheckin;
562 int okDoc;
563 int okTicket;
564 int okWiki;
565 int allOff;
566 const char *zDisable;
567
568 login_check_credentials();
569 okCheckin = g.perm.Read && db_get_boolean("search-ci",0);
570 okDoc = g.perm.Read && db_get_boolean("search-doc",0);
571 okTicket = g.perm.RdTkt && db_get_boolean("search-tkt",0);
572 okWiki = g.perm.RdWiki && db_get_boolean("search-wiki",0);
573 allOff = (okCheckin + okDoc + okTicket + okWiki == 0);
574 zDisable = allOff ? " disabled" : "";
575 zPattern = allOff ? "" : PD("s","");
 
576 style_header("Search");
577 @ <form method="GET" action="search"><center>
578 @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable)>
579 @ <input type="submit" value="Search"%s(zDisable)>
580 if( allOff ){
581 @ <p class="generalError">Search is disabled</p>
582 }
583 @ </center></form>
584 while( fossil_isspace(zPattern[0]) ) zPattern++;
585 if( zPattern[0] ){
586 search_sql_setup(g.db);
587 add_content_sql_commands(g.db);
588 search_init(zPattern, "<b>", "</b>", " ... ",
589 SRCHFLG_STATIC|SRCHFLG_HTML|SRCHFLG_SCORE);
590 db_multi_exec(
591 "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;"
592 "CREATE TEMP TABLE x(label TEXT,url TEXT,date TEXT,snip TEXT);"
593 );
594 if( okDoc ){
595 char *zDocGlob = db_get("doc-glob","");
596 char *zDocBr = db_get("doc-branch","trunk");
597 if( zDocGlob && zDocGlob[0] && zDocBr && zDocBr[0] ){
598 db_multi_exec(
599 "INSERT INTO x(label,url,date,snip)"
600 " SELECT printf('Document: %%s',foci.filename),"
601 " printf('%R/doc/%T/%%s',foci.filename),"
602 " (SELECT datetime(event.mtime) FROM event"
603 " WHERE objid=symbolic_name_to_rid('trunk')),"
604 " snippet(stext('d',blob.rid,foci.filename))"
605 " FROM foci CROSS JOIN blob"
606 " WHERE checkinID=symbolic_name_to_rid('trunk')"
607 " AND blob.uuid=foci.uuid"
608 " AND %z",
609 zDocBr, glob_expr("foci.filename", zDocGlob)
610 );
611 }
612 }
613 if( okWiki ){
614 db_multi_exec(
615 "WITH wiki(name,rid,mtime) AS ("
616 " SELECT substr(tagname,6), tagxref.rid, max(tagxref.mtime)"
617 " FROM tag, tagxref"
618 " WHERE tag.tagname GLOB 'wiki-*'"
619 " AND tagxref.tagid=tag.tagid"
620 " GROUP BY 1"
621 ")"
622 "INSERT INTO x(label,url,date,snip)"
623 " SELECT printf('Wiki: %%s',name),"
624 " printf('%R/wiki?name=%%s',urlencode(name)),"
625 " datetime(mtime),"
626 " snippet(stext('w',rid,name))"
627 " FROM wiki;"
628 );
629 }
630 if( okCheckin ){
631 db_multi_exec(
632 "WITH ckin(uuid,rid,mtime) AS ("
633 " SELECT blob.uuid, event.objid, event.mtime"
634 " FROM event, blob"
635 " WHERE event.type='ci'"
636 " AND blob.rid=event.objid"
637 ")"
638 "INSERT INTO x(label,url,date,snip)"
639 " SELECT printf('Check-in [%%.10s] on %%s',uuid,datetime(mtime)),"
640 " printf('%R/timeline?c=%%s&n=8&y=ci',uuid),"
641 " datetime(mtime),"
642 " snippet(stext('c',rid,NULL))"
643 " FROM ckin;"
644 );
645 }
646 if( okTicket ){
647 db_multi_exec(
648 "INSERT INTO x(label,url,date,snip)"
649 " SELECT printf('Ticket [%%.17s] on %%s',"
650 "tkt_uuid,datetime(tkt_mtime)),"
651 " printf('%R/tktview/%%.20s',tkt_uuid),"
652 " datetime(tkt_mtime),"
653 " snippet(stext('t',tkt_id,NULL))"
654 " FROM ticket;"
655 );
656 }
657 db_prepare(&q, "SELECT url, substr(snip,9), label"
658 " FROM x WHERE snip IS NOT NULL"
659 " ORDER BY substr(snip,1,8) DESC, date DESC;");
660 @ <ol>
661 while( db_step(&q)==SQLITE_ROW ){
662 const char *zUrl = db_column_text(&q, 0);
663 const char *zSnippet = db_column_text(&q, 1);
664 const char *zLabel = db_column_text(&q, 2);
665 @ <li><p>%s(href("%s",zUrl))%h(zLabel)</a><br>%s(zSnippet)</li>
666 }
667 db_finalize(&q);
668 @ </ol>
669 }
670 style_footer();
671 }
672
673
674
--- src/search.c
+++ src/search.c
@@ -535,139 +535,184 @@
535 db_prepare(&q, "%s", blob_sql_text(&sql));
536 blob_reset(&sql);
537 print_timeline(&q, nLimit, width, 0);
538 db_finalize(&q);
539 }
540
541 #if INTERFACE
542 /* What to search for */
543 #define SRCH_CKIN 0x0001 /* Search over check-in comments */
544 #define SRCH_DOC 0x0002 /* Search over embedded documents */
545 #define SRCH_TKT 0x0004 /* Search over tickets */
546 #define SRCH_WIKI 0x0008 /* Search over wiki */
547 #define SRCH_ALL 0x000f /* Search over everything */
548 #endif
549
550 /*
551 ** Remove bits from srchFlags which are disallowed by either the
552 ** current server configuration or by user permissions.
553 */
554 unsigned int search_restrict(unsigned int srchFlags){
555 if( (srchFlags & SRCH_CKIN)!=0
556 && (g.perm.Read==0 || db_get_boolean("search-ci",0)==0) ){
557 srchFlags &= ~SRCH_CKIN;
558 }
559 if( (srchFlags & SRCH_DOC)!=0
560 && (g.perm.Read==0 || db_get_boolean("search-doc",0)==0) ){
561 srchFlags &= ~SRCH_DOC;
562 }
563 if( (srchFlags & SRCH_TKT)!=0
564 && (g.perm.RdTkt==0 || db_get_boolean("search-tkt",0)==0) ){
565 srchFlags &= ~SRCH_TKT;
566 }
567 if( (srchFlags & SRCH_WIKI)!=0
568 && (g.perm.RdWiki==0 || db_get_boolean("search-wiki",0)==0) ){
569 srchFlags &= ~SRCH_WIKI;
570 }
571 return srchFlags;
572 }
573
574 /*
575 ** This routine generates web-page output for a search operation.
576 ** Other web-pages can invoke this routine to add search results
577 ** in the middle of the page.
578 **
579 ** Return the number of rows.
580 */
581 int search_run_and_output(
582 const char *zPattern, /* The query pattern */
583 unsigned int srchFlags /* What to search over */
584 ){
585 Stmt q;
586 int nRow = 0;
587
588 srchFlags = search_restrict(srchFlags);
589 if( srchFlags==0 ) return 0;
590 search_sql_setup(g.db);
591 add_content_sql_commands(g.db);
592 search_init(zPattern, "<b>", "</b>", " ... ",
593 SRCHFLG_STATIC|SRCHFLG_HTML|SRCHFLG_SCORE);
594 db_multi_exec(
595 "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;"
596 "CREATE TEMP TABLE x(label TEXT,url TEXT,date TEXT,snip TEXT);"
597 );
598 if( (srchFlags & SRCH_DOC)!=0 ){
599 char *zDocGlob = db_get("doc-glob","");
600 char *zDocBr = db_get("doc-branch","trunk");
601 if( zDocGlob && zDocGlob[0] && zDocBr && zDocBr[0] ){
602 db_multi_exec(
603 "INSERT INTO x(label,url,date,snip)"
604 " SELECT printf('Document: %%s',foci.filename),"
605 " printf('%R/doc/%T/%%s',foci.filename),"
606 " (SELECT datetime(event.mtime) FROM event"
607 " WHERE objid=symbolic_name_to_rid('trunk')),"
608 " snippet(stext('d',blob.rid,foci.filename))"
609 " FROM foci CROSS JOIN blob"
610 " WHERE checkinID=symbolic_name_to_rid('trunk')"
611 " AND blob.uuid=foci.uuid"
612 " AND %z",
613 zDocBr, glob_expr("foci.filename", zDocGlob)
614 );
615 }
616 }
617 if( (srchFlags & SRCH_WIKI)!=0 ){
618 db_multi_exec(
619 "WITH wiki(name,rid,mtime) AS ("
620 " SELECT substr(tagname,6), tagxref.rid, max(tagxref.mtime)"
621 " FROM tag, tagxref"
622 " WHERE tag.tagname GLOB 'wiki-*'"
623 " AND tagxref.tagid=tag.tagid"
624 " GROUP BY 1"
625 ")"
626 "INSERT INTO x(label,url,date,snip)"
627 " SELECT printf('Wiki: %%s',name),"
628 " printf('%R/wiki?name=%%s',urlencode(name)),"
629 " datetime(mtime),"
630 " snippet(stext('w',rid,name))"
631 " FROM wiki;"
632 );
633 }
634 if( (srchFlags & SRCH_CKIN)!=0 ){
635 db_multi_exec(
636 "WITH ckin(uuid,rid,mtime) AS ("
637 " SELECT blob.uuid, event.objid, event.mtime"
638 " FROM event, blob"
639 " WHERE event.type='ci'"
640 " AND blob.rid=event.objid"
641 ")"
642 "INSERT INTO x(label,url,date,snip)"
643 " SELECT printf('Check-in [%%.10s] on %%s',uuid,datetime(mtime)),"
644 " printf('%R/timeline?c=%%s&n=8&y=ci',uuid),"
645 " datetime(mtime),"
646 " snippet(stext('c',rid,NULL))"
647 " FROM ckin;"
648 );
649 }
650 if( (srchFlags & SRCH_TKT)!=0 ){
651 db_multi_exec(
652 "INSERT INTO x(label,url,date,snip)"
653 " SELECT printf('Ticket [%%.17s] on %%s',"
654 "tkt_uuid,datetime(tkt_mtime)),"
655 " printf('%R/tktview/%%.20s',tkt_uuid),"
656 " datetime(tkt_mtime),"
657 " snippet(stext('t',tkt_id,NULL))"
658 " FROM ticket;"
659 );
660 }
661 db_prepare(&q, "SELECT url, substr(snip,9), label"
662 " FROM x WHERE snip IS NOT NULL"
663 " ORDER BY substr(snip,1,8) DESC, date DESC;");
664 while( db_step(&q)==SQLITE_ROW ){
665 const char *zUrl = db_column_text(&q, 0);
666 const char *zSnippet = db_column_text(&q, 1);
667 const char *zLabel = db_column_text(&q, 2);
668 if( nRow==0 ){
669 @ <ol>
670 }
671 nRow++;
672 @ <li><p>%s(href("%s",zUrl))%h(zLabel)</a><br>%s(zSnippet)</li>
673 }
674 db_finalize(&q);
675 if( nRow ){
676 @ </ol>
677 }
678 return nRow;
679 }
680
681 /*
682 ** WEBPAGE: /search
683 **
684 ** Search for check-in comments, documents, tickets, or wiki that
685 ** match a user-supplied pattern.
 
 
 
 
 
 
 
 
 
 
 
686 */
687 void search_page(void){
688 const char *zPattern = PD("s","");
689 unsigned srchFlags = 0;
 
 
 
 
 
690 const char *zDisable;
691
692 login_check_credentials();
693 srchFlags = search_restrict(SRCH_ALL);
694 if( srchFlags==0 ){
695 zDisable = " disabled";
696 zPattern = "";
697 }else{
698 zDisable = "";
699 zPattern = PD("s","");
700 }
701 style_header("Search");
702 @ <form method="GET" action="search"><center>
703 @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable)>
704 @ <input type="submit" value="Search"%s(zDisable)>
705 if( srchFlags==0 ){
706 @ <p class="generalError">Search is disabled</p>
707 }
708 @ </center></form>
709 while( fossil_isspace(zPattern[0]) ) zPattern++;
710 if( zPattern[0] ){
711 if( search_run_and_output(zPattern, srchFlags)==0 ){
712 @ <p><i>No matches for: "%h(zPattern)"</i></p>
713 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
714 }
715 style_footer();
716 }
717
718
719
+1 -1
--- src/setup.c
+++ src/setup.c
@@ -2173,11 +2173,11 @@
21732173
style_header("Search Configuration");
21742174
@ <form action="%s(g.zTop)/srchsetup" method="post"><div>
21752175
login_insert_csrf_secret();
21762176
@ <div style="text-align:center;font-weight:bold;">
21772177
@ Server-specific settings that affect the
2178
- @ <a href="%R/help?cmd=/search">/search</a> webpage.
2178
+ @ <a href="%R/search">/search</a> webpage.
21792179
@ </div>
21802180
@ <hr />
21812181
textarea_attribute("Document Glob List", 3, 35, "doc-glob", "dg", "", 0);
21822182
@ <p>The "Document Glob List" is a comma- or newline-separated list
21832183
@ of GLOB expressions that identify all documents within the source
21842184
--- src/setup.c
+++ src/setup.c
@@ -2173,11 +2173,11 @@
2173 style_header("Search Configuration");
2174 @ <form action="%s(g.zTop)/srchsetup" method="post"><div>
2175 login_insert_csrf_secret();
2176 @ <div style="text-align:center;font-weight:bold;">
2177 @ Server-specific settings that affect the
2178 @ <a href="%R/help?cmd=/search">/search</a> webpage.
2179 @ </div>
2180 @ <hr />
2181 textarea_attribute("Document Glob List", 3, 35, "doc-glob", "dg", "", 0);
2182 @ <p>The "Document Glob List" is a comma- or newline-separated list
2183 @ of GLOB expressions that identify all documents within the source
2184
--- src/setup.c
+++ src/setup.c
@@ -2173,11 +2173,11 @@
2173 style_header("Search Configuration");
2174 @ <form action="%s(g.zTop)/srchsetup" method="post"><div>
2175 login_insert_csrf_secret();
2176 @ <div style="text-align:center;font-weight:bold;">
2177 @ Server-specific settings that affect the
2178 @ <a href="%R/search">/search</a> webpage.
2179 @ </div>
2180 @ <hr />
2181 textarea_attribute("Document Glob List", 3, 35, "doc-glob", "dg", "", 0);
2182 @ <p>The "Document Glob List" is a comma- or newline-separated list
2183 @ of GLOB expressions that identify all documents within the source
2184
+71
--- src/tkt.c
+++ src/tkt.c
@@ -691,10 +691,11 @@
691691
if( !g.perm.NewTkt ){ login_needed(); return; }
692692
if( P("cancel") ){
693693
cgi_redirect("home");
694694
}
695695
style_header("New Ticket");
696
+ ticket_standard_submenu(T_ALL_BUT(T_NEW));
696697
if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
697698
ticket_init();
698699
initializeVariablesFromCGI();
699700
getAllTicketFields();
700701
initializeVariablesFromDb();
@@ -1380,5 +1381,75 @@
13801381
(eCmd==set?"set":"add"),zTktUuid);
13811382
}
13821383
}
13831384
}
13841385
}
1386
+
1387
+
1388
+#if INTERFACE
1389
+/* Standard submenu items for wiki pages */
1390
+#define T_SRCH 0x00001
1391
+#define T_REPLIST 0x00002
1392
+#define T_NEW 0x00004
1393
+#define T_ALL 0x00007
1394
+#define T_ALL_BUT(x) (T_ALL&~(x))
1395
+#endif
1396
+
1397
+/*
1398
+** Add some standard submenu elements for ticket screens.
1399
+*/
1400
+void ticket_standard_submenu(unsigned int ok){
1401
+ if( (ok & T_SRCH)!=0 && search_restrict(SRCH_TKT)!=0 ){
1402
+ style_submenu_element("Search","Search","/tktsrch");
1403
+ }
1404
+ if( (ok & T_REPLIST)!=0 ){
1405
+ style_submenu_element("Reports","Reports","/reportlist");
1406
+ }
1407
+ if( (ok & T_NEW)!=0 && g.perm.NewTkt ){
1408
+ style_submenu_element("New","New","/tktnew");
1409
+ }
1410
+}
1411
+
1412
+
1413
+
1414
+/*
1415
+** Note: The /ticket webpage is intended to be the page that the
1416
+** main menu links to. We might move that page around in the future.
1417
+** Use /tktsrch if you really want a ticket search.
1418
+**
1419
+** WEBPAGE: tktsrch
1420
+** WEBPAGE: ticket
1421
+** Usage: /tktsrch?s=PATTERN
1422
+**
1423
+** Full-text search of all current tickets
1424
+*/
1425
+void tkt_srchpage(void){
1426
+ const char *zPattern = PD("s","");
1427
+ unsigned srchFlags = 0;
1428
+ const char *zDisable;
1429
+
1430
+ login_check_credentials();
1431
+ srchFlags = search_restrict(SRCH_TKT);
1432
+ if( srchFlags==0 ){
1433
+ zDisable = " disabled";
1434
+ zPattern = "";
1435
+ }else{
1436
+ zDisable = "";
1437
+ zPattern = PD("s","");
1438
+ }
1439
+ style_header("Ticket Search");
1440
+ ticket_standard_submenu(T_ALL_BUT(T_SRCH));
1441
+ @ <form method="GET" action="tktsrch"><center>
1442
+ @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable)>
1443
+ @ <input type="submit" value="Search Tickets"%s(zDisable)>
1444
+ if( srchFlags==0 ){
1445
+ @ <p class="generalError">Ticket search is disabled</p>
1446
+ }
1447
+ @ </center></form>
1448
+ while( fossil_isspace(zPattern[0]) ) zPattern++;
1449
+ if( zPattern[0] ){
1450
+ if( search_run_and_output(zPattern, srchFlags)==0 ){
1451
+ @ <p><i>No matches for: "%h(zPattern)"</i></p>
1452
+ }
1453
+ }
1454
+ style_footer();
1455
+}
13851456
--- src/tkt.c
+++ src/tkt.c
@@ -691,10 +691,11 @@
691 if( !g.perm.NewTkt ){ login_needed(); return; }
692 if( P("cancel") ){
693 cgi_redirect("home");
694 }
695 style_header("New Ticket");
 
696 if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
697 ticket_init();
698 initializeVariablesFromCGI();
699 getAllTicketFields();
700 initializeVariablesFromDb();
@@ -1380,5 +1381,75 @@
1380 (eCmd==set?"set":"add"),zTktUuid);
1381 }
1382 }
1383 }
1384 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1385
--- src/tkt.c
+++ src/tkt.c
@@ -691,10 +691,11 @@
691 if( !g.perm.NewTkt ){ login_needed(); return; }
692 if( P("cancel") ){
693 cgi_redirect("home");
694 }
695 style_header("New Ticket");
696 ticket_standard_submenu(T_ALL_BUT(T_NEW));
697 if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
698 ticket_init();
699 initializeVariablesFromCGI();
700 getAllTicketFields();
701 initializeVariablesFromDb();
@@ -1380,5 +1381,75 @@
1381 (eCmd==set?"set":"add"),zTktUuid);
1382 }
1383 }
1384 }
1385 }
1386
1387
1388 #if INTERFACE
1389 /* Standard submenu items for wiki pages */
1390 #define T_SRCH 0x00001
1391 #define T_REPLIST 0x00002
1392 #define T_NEW 0x00004
1393 #define T_ALL 0x00007
1394 #define T_ALL_BUT(x) (T_ALL&~(x))
1395 #endif
1396
1397 /*
1398 ** Add some standard submenu elements for ticket screens.
1399 */
1400 void ticket_standard_submenu(unsigned int ok){
1401 if( (ok & T_SRCH)!=0 && search_restrict(SRCH_TKT)!=0 ){
1402 style_submenu_element("Search","Search","/tktsrch");
1403 }
1404 if( (ok & T_REPLIST)!=0 ){
1405 style_submenu_element("Reports","Reports","/reportlist");
1406 }
1407 if( (ok & T_NEW)!=0 && g.perm.NewTkt ){
1408 style_submenu_element("New","New","/tktnew");
1409 }
1410 }
1411
1412
1413
1414 /*
1415 ** Note: The /ticket webpage is intended to be the page that the
1416 ** main menu links to. We might move that page around in the future.
1417 ** Use /tktsrch if you really want a ticket search.
1418 **
1419 ** WEBPAGE: tktsrch
1420 ** WEBPAGE: ticket
1421 ** Usage: /tktsrch?s=PATTERN
1422 **
1423 ** Full-text search of all current tickets
1424 */
1425 void tkt_srchpage(void){
1426 const char *zPattern = PD("s","");
1427 unsigned srchFlags = 0;
1428 const char *zDisable;
1429
1430 login_check_credentials();
1431 srchFlags = search_restrict(SRCH_TKT);
1432 if( srchFlags==0 ){
1433 zDisable = " disabled";
1434 zPattern = "";
1435 }else{
1436 zDisable = "";
1437 zPattern = PD("s","");
1438 }
1439 style_header("Ticket Search");
1440 ticket_standard_submenu(T_ALL_BUT(T_SRCH));
1441 @ <form method="GET" action="tktsrch"><center>
1442 @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable)>
1443 @ <input type="submit" value="Search Tickets"%s(zDisable)>
1444 if( srchFlags==0 ){
1445 @ <p class="generalError">Ticket search is disabled</p>
1446 }
1447 @ </center></form>
1448 while( fossil_isspace(zPattern[0]) ) zPattern++;
1449 if( zPattern[0] ){
1450 if( search_run_and_output(zPattern, srchFlags)==0 ){
1451 @ <p><i>No matches for: "%h(zPattern)"</i></p>
1452 }
1453 }
1454 style_footer();
1455 }
1456
+138 -41
--- src/wiki.c
+++ src/wiki.c
@@ -198,10 +198,130 @@
198198
if( localUser ){
199199
return 0;
200200
}
201201
return g.perm.ModWiki==0 && db_get_boolean("modreq-wiki",0)==1;
202202
}
203
+
204
+/* Standard submenu items for wiki pages */
205
+#define W_SRCH 0x00001
206
+#define W_LIST 0x00002
207
+#define W_HELP 0x00004
208
+#define W_NEW 0x00008
209
+#define W_BLOG 0x00010
210
+#define W_SANDBOX 0x00020
211
+#define W_ALL 0x0001f
212
+#define W_ALL_BUT(x) (W_ALL&~(x))
213
+
214
+/*
215
+** Add some standard submenu elements for wiki screens.
216
+*/
217
+static void wiki_standard_submenu(unsigned int ok){
218
+ if( (ok & W_SRCH)!=0 && search_restrict(SRCH_WIKI)!=0 ){
219
+ style_submenu_element("Search","Search","/wikisrch");
220
+ }
221
+ if( (ok & W_LIST)!=0 ){
222
+ style_submenu_element("List","List","/wcontent");
223
+ }
224
+ if( (ok & W_HELP)!=0 ){
225
+ style_submenu_element("Help","Help","/wikihelp");
226
+ }
227
+ if( (ok & W_NEW)!=0 && g.perm.NewWiki ){
228
+ style_submenu_element("New","New","/wikinew");
229
+ }
230
+#if 0
231
+ if( (ok & W_BLOG)!=0
232
+#endif
233
+ if( (ok & W_SANDBOX)!=0 ){
234
+ style_submenu_element("Sandbox", "Sandbox", "/wiki?name=Sandbox");
235
+ }
236
+}
237
+
238
+/*
239
+** WEBPAGE: wikihelp
240
+** A generic landing page for wiki.
241
+*/
242
+void wiki_helppage(void){
243
+ login_check_credentials();
244
+ if( !g.perm.RdWiki ){ login_needed(); return; }
245
+ style_header("Wiki Help");
246
+ wiki_standard_submenu(W_ALL_BUT(W_HELP));
247
+ @ <h2>Wiki Links</h2>
248
+ @ <ul>
249
+ { char *zWikiHomePageName = db_get("index-page",0);
250
+ if( zWikiHomePageName ){
251
+ @ <li> %z(href("%R%s",zWikiHomePageName))
252
+ @ %h(zWikiHomePageName)</a> wiki home page.</li>
253
+ }
254
+ }
255
+ { char *zHomePageName = db_get("project-name",0);
256
+ if( zHomePageName ){
257
+ @ <li> %z(href("%R/wiki?name=%t",zHomePageName))
258
+ @ %h(zHomePageName)</a> project home page.</li>
259
+ }
260
+ }
261
+ @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li>
262
+ @ <li> Formatting rules for %z(href("%R/wiki_rules"))Fossil Wiki</a> and for
263
+ @ %z(href("%R/md_rules"))Markdown Wiki</a>.</li>
264
+ @ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a>
265
+ @ to experiment.</li>
266
+ if( g.perm.NewWiki ){
267
+ @ <li> Create a %z(href("%R/wikinew"))new wiki page</a>.</li>
268
+ if( g.perm.Write ){
269
+ @ <li> Create a %z(href("%R/eventedit"))new blog entry</a>.</li>
270
+ }
271
+ }
272
+ @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
273
+ @ available on this server.</li>
274
+ if( g.perm.ModWiki ){
275
+ @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
276
+ }
277
+ if( search_restrict(SRCH_WIKI)!=0 ){
278
+ @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key
279
+ @ words</li>
280
+ }
281
+ @ </ul>
282
+ style_footer();
283
+ return;
284
+}
285
+
286
+/*
287
+** WEBPAGE: wikisrch
288
+** Usage: /wikisrch?s=PATTERN
289
+**
290
+** Full-text search of all current wiki text
291
+*/
292
+void wiki_srchpage(void){
293
+ const char *zPattern = PD("s","");
294
+ unsigned srchFlags = 0;
295
+ const char *zDisable;
296
+
297
+ login_check_credentials();
298
+ srchFlags = search_restrict(SRCH_WIKI);
299
+ if( srchFlags==0 ){
300
+ zDisable = " disabled";
301
+ zPattern = "";
302
+ }else{
303
+ zDisable = "";
304
+ zPattern = PD("s","");
305
+ }
306
+ style_header("Wiki Search");
307
+ wiki_standard_submenu(W_HELP|W_LIST|W_SANDBOX);
308
+ @ <form method="GET" action="wikisrch"><center>
309
+ @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable)>
310
+ @ <input type="submit" value="Search Wiki"%s(zDisable)>
311
+ if( srchFlags==0 ){
312
+ @ <p class="generalError">Wiki search is disabled</p>
313
+ }
314
+ @ </center></form>
315
+ while( fossil_isspace(zPattern[0]) ) zPattern++;
316
+ if( zPattern[0] ){
317
+ if( search_run_and_output(zPattern, srchFlags)==0 ){
318
+ @ <p><i>No matches for: "%h(zPattern)"</i></p>
319
+ }
320
+ }
321
+ style_footer();
322
+}
203323
204324
/*
205325
** WEBPAGE: wiki
206326
** URL: /wiki?name=PAGENAME
207327
*/
@@ -208,10 +328,11 @@
208328
void wiki_page(void){
209329
char *zTag;
210330
int rid = 0;
211331
int isSandbox;
212332
char *zUuid;
333
+ unsigned submenuFlags = W_ALL;
213334
Blob wiki;
214335
Manifest *pWiki = 0;
215336
const char *zPageName;
216337
const char *zMimetype = 0;
217338
char *zBody = mprintf("%s","<i>Empty Page</i>");
@@ -218,52 +339,21 @@
218339
219340
login_check_credentials();
220341
if( !g.perm.RdWiki ){ login_needed(); return; }
221342
zPageName = P("name");
222343
if( zPageName==0 ){
223
- style_header("Wiki");
224
- @ <ul>
225
- { char *zWikiHomePageName = db_get("index-page",0);
226
- if( zWikiHomePageName ){
227
- @ <li> %z(href("%R%s",zWikiHomePageName))
228
- @ %h(zWikiHomePageName)</a> wiki home page.</li>
229
- }
230
- }
231
- { char *zHomePageName = db_get("project-name",0);
232
- if( zHomePageName ){
233
- @ <li> %z(href("%R/wiki?name=%t",zHomePageName))
234
- @ %h(zHomePageName)</a> project home page.</li>
235
- }
236
- }
237
- @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li>
238
- @ <li> Formatting rules for %z(href("%R/wiki_rules"))Fossil Wiki</a> and for
239
- @ %z(href("%R/md_rules"))Markdown Wiki</a>.</li>
240
- @ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a>
241
- @ to experiment.</li>
242
- if( g.perm.NewWiki ){
243
- @ <li> Create a %z(href("%R/wikinew"))new wiki page</a>.</li>
244
- if( g.perm.Write ){
245
- @ <li> Create a %z(href("%R/eventedit"))new event</a>.</li>
246
- }
247
- }
248
- @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
249
- @ available on this server.</li>
250
- if( g.perm.ModWiki ){
251
- @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
252
- }
253
- @ <li>
254
- form_begin(0, "%R/wfind");
255
- @ <div>Search wiki titles: <input type="text" name="title"/>
256
- @ &nbsp; <input type="submit" /></div></form>
257
- @ </li>
258
- @ </ul>
259
- style_footer();
344
+ if( search_restrict(SRCH_WIKI)!=0 ){
345
+ wiki_srchpage();
346
+ }else{
347
+ wiki_helppage();
348
+ }
260349
return;
261350
}
262351
if( check_name(zPageName) ) return;
263352
isSandbox = is_sandbox(zPageName);
264353
if( isSandbox ){
354
+ submenuFlags &= ~W_SANDBOX;
265355
zBody = db_get("sandbox",zBody);
266356
zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki");
267357
rid = 0;
268358
}else{
269359
const char *zUuid = P("id");
@@ -317,10 +407,11 @@
317407
g.zTop, zPageName);
318408
}
319409
}
320410
style_set_current_page("%T?name=%T", g.zPath, zPageName);
321411
style_header("%s", zPageName);
412
+ wiki_standard_submenu(submenuFlags);
322413
blob_init(&wiki, zBody, -1);
323414
wiki_render_by_mimetype(&wiki, zMimetype);
324415
blob_reset(&wiki);
325416
attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
326417
manifest_destroy(pWiki);
@@ -573,10 +664,11 @@
573664
}else{
574665
cgi_redirectf("wikiedit?name=%T&mimetype=%s", zName, zMimetype);
575666
}
576667
}
577668
style_header("Create A New Wiki Page");
669
+ wiki_standard_submenu(W_ALL_BUT(W_NEW));
578670
@ <p>Rules for wiki page names:</p>
579671
well_formed_wiki_name_rules();
580672
form_begin(0, "%R/wikinew");
581673
@ <p>Name of new wiki page:
582674
@ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br />
@@ -849,11 +941,12 @@
849941
*/
850942
void wiki_prepare_page_list( Stmt * pStmt ){
851943
db_prepare(pStmt,
852944
"SELECT"
853945
" substr(tagname, 6) as name,"
854
- " (SELECT value FROM tagxref WHERE tagid=tag.tagid ORDER BY mtime DESC) as tagXref"
946
+ " (SELECT value FROM tagxref WHERE tagid=tag.tagid"
947
+ " ORDER BY mtime DESC) as tagXref"
855948
" FROM tag WHERE tagname GLOB 'wiki-*'"
856949
" ORDER BY lower(tagname) /*sort*/"
857950
);
858951
}
859952
/*
@@ -873,10 +966,11 @@
873966
if( showAll ){
874967
style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
875968
}else{
876969
style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
877970
}
971
+ wiki_standard_submenu(W_ALL_BUT(W_LIST));
878972
@ <ul>
879973
wiki_prepare_page_list(&q);
880974
while( db_step(&q)==SQLITE_ROW ){
881975
const char *zName = db_column_text(&q, 0);
882976
int size = db_column_int(&q, 1);
@@ -938,11 +1032,12 @@
9381032
@ </ol>
9391033
@ <p>We call the first five rules above "wiki" formatting rules. The
9401034
@ last two rules are the HTML formatting rule.</p>
9411035
@ <h2>Formatting Rule Details</h2>
9421036
@ <ol>
943
- @ <li> <p><span class="wikiruleHead">Paragraphs</span>. Any sequence of one or more blank lines forms
1037
+ @ <li> <p><span class="wikiruleHead">Paragraphs</span>.
1038
+ @ Any sequence of one or more blank lines forms
9441039
@ a paragraph break. Centered or right-justified paragraphs are not
9451040
@ supported by wiki markup, but you can do these things if you need them
9461041
@ using HTML.</p></li>
9471042
@ <li> <p><span class="wikiruleHead">Bullet Lists</span>.
9481043
@ A bullet list item is a line that begins with a single "*" character
@@ -963,11 +1058,12 @@
9631058
@ Text within square brackets ("[...]") becomes a hyperlink. The
9641059
@ target can be a wiki page name, the artifact ID of a check-in or ticket,
9651060
@ the name of an image, or a URL. By default, the target is displayed
9661061
@ as the text of the hyperlink. But you can specify alternative text
9671062
@ after the target name separated by a "|" character.</p>
968
- @ <p>You can also link to internal anchor names using [#anchor-name], providing
1063
+ @ <p>You can also link to internal anchor names using [#anchor-name],
1064
+ @ providing
9691065
@ you have added the necessary "&lt;a name='anchor-name'&gt;&lt;/a&gt;"
9701066
@ tag to your wiki page.</p></li>
9711067
@ <li> <p><span class="wikiruleHead">HTML</span>.
9721068
@ The following standard HTML elements may be used:
9731069
show_allowed_wiki_markup();
@@ -1169,11 +1265,12 @@
11691265
}else if( strncmp(g.argv[2],"delete",n)==0 ){
11701266
if( g.argc!=5 ){
11711267
usage("delete PAGENAME");
11721268
}
11731269
fossil_fatal("delete not yet implemented.");
1174
- }else if(( strncmp(g.argv[2],"list",n)==0 ) || ( strncmp(g.argv[2],"ls",n)==0 )){
1270
+ }else if(( strncmp(g.argv[2],"list",n)==0 )
1271
+ || ( strncmp(g.argv[2],"ls",n)==0 )){
11751272
Stmt q;
11761273
db_prepare(&q,
11771274
"SELECT substr(tagname, 6) FROM tag WHERE tagname GLOB 'wiki-*'"
11781275
" ORDER BY lower(tagname) /*sort*/"
11791276
);
11801277
--- src/wiki.c
+++ src/wiki.c
@@ -198,10 +198,130 @@
198 if( localUser ){
199 return 0;
200 }
201 return g.perm.ModWiki==0 && db_get_boolean("modreq-wiki",0)==1;
202 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
204 /*
205 ** WEBPAGE: wiki
206 ** URL: /wiki?name=PAGENAME
207 */
@@ -208,10 +328,11 @@
208 void wiki_page(void){
209 char *zTag;
210 int rid = 0;
211 int isSandbox;
212 char *zUuid;
 
213 Blob wiki;
214 Manifest *pWiki = 0;
215 const char *zPageName;
216 const char *zMimetype = 0;
217 char *zBody = mprintf("%s","<i>Empty Page</i>");
@@ -218,52 +339,21 @@
218
219 login_check_credentials();
220 if( !g.perm.RdWiki ){ login_needed(); return; }
221 zPageName = P("name");
222 if( zPageName==0 ){
223 style_header("Wiki");
224 @ <ul>
225 { char *zWikiHomePageName = db_get("index-page",0);
226 if( zWikiHomePageName ){
227 @ <li> %z(href("%R%s",zWikiHomePageName))
228 @ %h(zWikiHomePageName)</a> wiki home page.</li>
229 }
230 }
231 { char *zHomePageName = db_get("project-name",0);
232 if( zHomePageName ){
233 @ <li> %z(href("%R/wiki?name=%t",zHomePageName))
234 @ %h(zHomePageName)</a> project home page.</li>
235 }
236 }
237 @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li>
238 @ <li> Formatting rules for %z(href("%R/wiki_rules"))Fossil Wiki</a> and for
239 @ %z(href("%R/md_rules"))Markdown Wiki</a>.</li>
240 @ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a>
241 @ to experiment.</li>
242 if( g.perm.NewWiki ){
243 @ <li> Create a %z(href("%R/wikinew"))new wiki page</a>.</li>
244 if( g.perm.Write ){
245 @ <li> Create a %z(href("%R/eventedit"))new event</a>.</li>
246 }
247 }
248 @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
249 @ available on this server.</li>
250 if( g.perm.ModWiki ){
251 @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
252 }
253 @ <li>
254 form_begin(0, "%R/wfind");
255 @ <div>Search wiki titles: <input type="text" name="title"/>
256 @ &nbsp; <input type="submit" /></div></form>
257 @ </li>
258 @ </ul>
259 style_footer();
260 return;
261 }
262 if( check_name(zPageName) ) return;
263 isSandbox = is_sandbox(zPageName);
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");
@@ -317,10 +407,11 @@
317 g.zTop, zPageName);
318 }
319 }
320 style_set_current_page("%T?name=%T", g.zPath, zPageName);
321 style_header("%s", zPageName);
 
322 blob_init(&wiki, zBody, -1);
323 wiki_render_by_mimetype(&wiki, zMimetype);
324 blob_reset(&wiki);
325 attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
326 manifest_destroy(pWiki);
@@ -573,10 +664,11 @@
573 }else{
574 cgi_redirectf("wikiedit?name=%T&mimetype=%s", zName, zMimetype);
575 }
576 }
577 style_header("Create A New Wiki Page");
 
578 @ <p>Rules for wiki page names:</p>
579 well_formed_wiki_name_rules();
580 form_begin(0, "%R/wikinew");
581 @ <p>Name of new wiki page:
582 @ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br />
@@ -849,11 +941,12 @@
849 */
850 void wiki_prepare_page_list( Stmt * pStmt ){
851 db_prepare(pStmt,
852 "SELECT"
853 " substr(tagname, 6) as name,"
854 " (SELECT value FROM tagxref WHERE tagid=tag.tagid ORDER BY mtime DESC) as tagXref"
 
855 " FROM tag WHERE tagname GLOB 'wiki-*'"
856 " ORDER BY lower(tagname) /*sort*/"
857 );
858 }
859 /*
@@ -873,10 +966,11 @@
873 if( showAll ){
874 style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
875 }else{
876 style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
877 }
 
878 @ <ul>
879 wiki_prepare_page_list(&q);
880 while( db_step(&q)==SQLITE_ROW ){
881 const char *zName = db_column_text(&q, 0);
882 int size = db_column_int(&q, 1);
@@ -938,11 +1032,12 @@
938 @ </ol>
939 @ <p>We call the first five rules above "wiki" formatting rules. The
940 @ last two rules are the HTML formatting rule.</p>
941 @ <h2>Formatting Rule Details</h2>
942 @ <ol>
943 @ <li> <p><span class="wikiruleHead">Paragraphs</span>. Any sequence of one or more blank lines forms
 
944 @ a paragraph break. Centered or right-justified paragraphs are not
945 @ supported by wiki markup, but you can do these things if you need them
946 @ using HTML.</p></li>
947 @ <li> <p><span class="wikiruleHead">Bullet Lists</span>.
948 @ A bullet list item is a line that begins with a single "*" character
@@ -963,11 +1058,12 @@
963 @ Text within square brackets ("[...]") becomes a hyperlink. The
964 @ target can be a wiki page name, the artifact ID of a check-in or ticket,
965 @ the name of an image, or a URL. By default, the target is displayed
966 @ as the text of the hyperlink. But you can specify alternative text
967 @ after the target name separated by a "|" character.</p>
968 @ <p>You can also link to internal anchor names using [#anchor-name], providing
 
969 @ you have added the necessary "&lt;a name='anchor-name'&gt;&lt;/a&gt;"
970 @ tag to your wiki page.</p></li>
971 @ <li> <p><span class="wikiruleHead">HTML</span>.
972 @ The following standard HTML elements may be used:
973 show_allowed_wiki_markup();
@@ -1169,11 +1265,12 @@
1169 }else if( strncmp(g.argv[2],"delete",n)==0 ){
1170 if( g.argc!=5 ){
1171 usage("delete PAGENAME");
1172 }
1173 fossil_fatal("delete not yet implemented.");
1174 }else if(( strncmp(g.argv[2],"list",n)==0 ) || ( strncmp(g.argv[2],"ls",n)==0 )){
 
1175 Stmt q;
1176 db_prepare(&q,
1177 "SELECT substr(tagname, 6) FROM tag WHERE tagname GLOB 'wiki-*'"
1178 " ORDER BY lower(tagname) /*sort*/"
1179 );
1180
--- src/wiki.c
+++ src/wiki.c
@@ -198,10 +198,130 @@
198 if( localUser ){
199 return 0;
200 }
201 return g.perm.ModWiki==0 && db_get_boolean("modreq-wiki",0)==1;
202 }
203
204 /* Standard submenu items for wiki pages */
205 #define W_SRCH 0x00001
206 #define W_LIST 0x00002
207 #define W_HELP 0x00004
208 #define W_NEW 0x00008
209 #define W_BLOG 0x00010
210 #define W_SANDBOX 0x00020
211 #define W_ALL 0x0001f
212 #define W_ALL_BUT(x) (W_ALL&~(x))
213
214 /*
215 ** Add some standard submenu elements for wiki screens.
216 */
217 static void wiki_standard_submenu(unsigned int ok){
218 if( (ok & W_SRCH)!=0 && search_restrict(SRCH_WIKI)!=0 ){
219 style_submenu_element("Search","Search","/wikisrch");
220 }
221 if( (ok & W_LIST)!=0 ){
222 style_submenu_element("List","List","/wcontent");
223 }
224 if( (ok & W_HELP)!=0 ){
225 style_submenu_element("Help","Help","/wikihelp");
226 }
227 if( (ok & W_NEW)!=0 && g.perm.NewWiki ){
228 style_submenu_element("New","New","/wikinew");
229 }
230 #if 0
231 if( (ok & W_BLOG)!=0
232 #endif
233 if( (ok & W_SANDBOX)!=0 ){
234 style_submenu_element("Sandbox", "Sandbox", "/wiki?name=Sandbox");
235 }
236 }
237
238 /*
239 ** WEBPAGE: wikihelp
240 ** A generic landing page for wiki.
241 */
242 void wiki_helppage(void){
243 login_check_credentials();
244 if( !g.perm.RdWiki ){ login_needed(); return; }
245 style_header("Wiki Help");
246 wiki_standard_submenu(W_ALL_BUT(W_HELP));
247 @ <h2>Wiki Links</h2>
248 @ <ul>
249 { char *zWikiHomePageName = db_get("index-page",0);
250 if( zWikiHomePageName ){
251 @ <li> %z(href("%R%s",zWikiHomePageName))
252 @ %h(zWikiHomePageName)</a> wiki home page.</li>
253 }
254 }
255 { char *zHomePageName = db_get("project-name",0);
256 if( zHomePageName ){
257 @ <li> %z(href("%R/wiki?name=%t",zHomePageName))
258 @ %h(zHomePageName)</a> project home page.</li>
259 }
260 }
261 @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li>
262 @ <li> Formatting rules for %z(href("%R/wiki_rules"))Fossil Wiki</a> and for
263 @ %z(href("%R/md_rules"))Markdown Wiki</a>.</li>
264 @ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a>
265 @ to experiment.</li>
266 if( g.perm.NewWiki ){
267 @ <li> Create a %z(href("%R/wikinew"))new wiki page</a>.</li>
268 if( g.perm.Write ){
269 @ <li> Create a %z(href("%R/eventedit"))new blog entry</a>.</li>
270 }
271 }
272 @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
273 @ available on this server.</li>
274 if( g.perm.ModWiki ){
275 @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
276 }
277 if( search_restrict(SRCH_WIKI)!=0 ){
278 @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key
279 @ words</li>
280 }
281 @ </ul>
282 style_footer();
283 return;
284 }
285
286 /*
287 ** WEBPAGE: wikisrch
288 ** Usage: /wikisrch?s=PATTERN
289 **
290 ** Full-text search of all current wiki text
291 */
292 void wiki_srchpage(void){
293 const char *zPattern = PD("s","");
294 unsigned srchFlags = 0;
295 const char *zDisable;
296
297 login_check_credentials();
298 srchFlags = search_restrict(SRCH_WIKI);
299 if( srchFlags==0 ){
300 zDisable = " disabled";
301 zPattern = "";
302 }else{
303 zDisable = "";
304 zPattern = PD("s","");
305 }
306 style_header("Wiki Search");
307 wiki_standard_submenu(W_HELP|W_LIST|W_SANDBOX);
308 @ <form method="GET" action="wikisrch"><center>
309 @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable)>
310 @ <input type="submit" value="Search Wiki"%s(zDisable)>
311 if( srchFlags==0 ){
312 @ <p class="generalError">Wiki search is disabled</p>
313 }
314 @ </center></form>
315 while( fossil_isspace(zPattern[0]) ) zPattern++;
316 if( zPattern[0] ){
317 if( search_run_and_output(zPattern, srchFlags)==0 ){
318 @ <p><i>No matches for: "%h(zPattern)"</i></p>
319 }
320 }
321 style_footer();
322 }
323
324 /*
325 ** WEBPAGE: wiki
326 ** URL: /wiki?name=PAGENAME
327 */
@@ -208,10 +328,11 @@
328 void wiki_page(void){
329 char *zTag;
330 int rid = 0;
331 int isSandbox;
332 char *zUuid;
333 unsigned submenuFlags = W_ALL;
334 Blob wiki;
335 Manifest *pWiki = 0;
336 const char *zPageName;
337 const char *zMimetype = 0;
338 char *zBody = mprintf("%s","<i>Empty Page</i>");
@@ -218,52 +339,21 @@
339
340 login_check_credentials();
341 if( !g.perm.RdWiki ){ login_needed(); return; }
342 zPageName = P("name");
343 if( zPageName==0 ){
344 if( search_restrict(SRCH_WIKI)!=0 ){
345 wiki_srchpage();
346 }else{
347 wiki_helppage();
348 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349 return;
350 }
351 if( check_name(zPageName) ) return;
352 isSandbox = is_sandbox(zPageName);
353 if( isSandbox ){
354 submenuFlags &= ~W_SANDBOX;
355 zBody = db_get("sandbox",zBody);
356 zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki");
357 rid = 0;
358 }else{
359 const char *zUuid = P("id");
@@ -317,10 +407,11 @@
407 g.zTop, zPageName);
408 }
409 }
410 style_set_current_page("%T?name=%T", g.zPath, zPageName);
411 style_header("%s", zPageName);
412 wiki_standard_submenu(submenuFlags);
413 blob_init(&wiki, zBody, -1);
414 wiki_render_by_mimetype(&wiki, zMimetype);
415 blob_reset(&wiki);
416 attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
417 manifest_destroy(pWiki);
@@ -573,10 +664,11 @@
664 }else{
665 cgi_redirectf("wikiedit?name=%T&mimetype=%s", zName, zMimetype);
666 }
667 }
668 style_header("Create A New Wiki Page");
669 wiki_standard_submenu(W_ALL_BUT(W_NEW));
670 @ <p>Rules for wiki page names:</p>
671 well_formed_wiki_name_rules();
672 form_begin(0, "%R/wikinew");
673 @ <p>Name of new wiki page:
674 @ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br />
@@ -849,11 +941,12 @@
941 */
942 void wiki_prepare_page_list( Stmt * pStmt ){
943 db_prepare(pStmt,
944 "SELECT"
945 " substr(tagname, 6) as name,"
946 " (SELECT value FROM tagxref WHERE tagid=tag.tagid"
947 " ORDER BY mtime DESC) as tagXref"
948 " FROM tag WHERE tagname GLOB 'wiki-*'"
949 " ORDER BY lower(tagname) /*sort*/"
950 );
951 }
952 /*
@@ -873,10 +966,11 @@
966 if( showAll ){
967 style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
968 }else{
969 style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
970 }
971 wiki_standard_submenu(W_ALL_BUT(W_LIST));
972 @ <ul>
973 wiki_prepare_page_list(&q);
974 while( db_step(&q)==SQLITE_ROW ){
975 const char *zName = db_column_text(&q, 0);
976 int size = db_column_int(&q, 1);
@@ -938,11 +1032,12 @@
1032 @ </ol>
1033 @ <p>We call the first five rules above "wiki" formatting rules. The
1034 @ last two rules are the HTML formatting rule.</p>
1035 @ <h2>Formatting Rule Details</h2>
1036 @ <ol>
1037 @ <li> <p><span class="wikiruleHead">Paragraphs</span>.
1038 @ Any sequence of one or more blank lines forms
1039 @ a paragraph break. Centered or right-justified paragraphs are not
1040 @ supported by wiki markup, but you can do these things if you need them
1041 @ using HTML.</p></li>
1042 @ <li> <p><span class="wikiruleHead">Bullet Lists</span>.
1043 @ A bullet list item is a line that begins with a single "*" character
@@ -963,11 +1058,12 @@
1058 @ Text within square brackets ("[...]") becomes a hyperlink. The
1059 @ target can be a wiki page name, the artifact ID of a check-in or ticket,
1060 @ the name of an image, or a URL. By default, the target is displayed
1061 @ as the text of the hyperlink. But you can specify alternative text
1062 @ after the target name separated by a "|" character.</p>
1063 @ <p>You can also link to internal anchor names using [#anchor-name],
1064 @ providing
1065 @ you have added the necessary "&lt;a name='anchor-name'&gt;&lt;/a&gt;"
1066 @ tag to your wiki page.</p></li>
1067 @ <li> <p><span class="wikiruleHead">HTML</span>.
1068 @ The following standard HTML elements may be used:
1069 show_allowed_wiki_markup();
@@ -1169,11 +1265,12 @@
1265 }else if( strncmp(g.argv[2],"delete",n)==0 ){
1266 if( g.argc!=5 ){
1267 usage("delete PAGENAME");
1268 }
1269 fossil_fatal("delete not yet implemented.");
1270 }else if(( strncmp(g.argv[2],"list",n)==0 )
1271 || ( strncmp(g.argv[2],"ls",n)==0 )){
1272 Stmt q;
1273 db_prepare(&q,
1274 "SELECT substr(tagname, 6) FROM tag WHERE tagname GLOB 'wiki-*'"
1275 " ORDER BY lower(tagname) /*sort*/"
1276 );
1277

Keyboard Shortcuts

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