Fossil SCM

Merge recent experimental changes (the attachment enhancement and the ability to delete wiki) into the trunk.

drh 2010-03-18 13:26 trunk merge
Commit f4a25366a76d89dce3dc191246bbea3babb53505
+17
--- a/src/attach.c
+++ b/src/attach.c
@@ -0,0 +1,17 @@
1
+[] Oif(zM[] Oif(zMime =Name);[] O .10s", z] Oif(zMime =Name);[] O .10s", zTkt Oif(zMime[] Oif(zMime =Name);[] O
2
+
3
+
4
+}
5
+S[SSg.zLogin ? g.zLogin : "noboG all[] Oif(zMimes", z] Oif(zMime =Name);[] O .10s", zTkt Oif(zMime[] Oif(zMime =Name);[] O
6
+
7
+
8
+}
9
+S[SSg.zLogin ? g.zLogin : "noboG=Name);[] O .10s", z] Oif(zMime =Name);[] O .10s", zTkt Oif(zMime[] Oif(zMime =Name);[] O
10
+
11
+
12
+}
13
+S[SSg.zLogin ? g.zLogin : "noboG all[] Oif(zMimes", z] Oif(zMime =Name);[] O .10s", zTkt Oif(zMime[] Oif(zMime =Name);[] O
14
+
15
+
16
+}
17
+S[SSg.zLogin ? g.zLogin : "noboG
--- a/src/attach.c
+++ b/src/attach.c
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/attach.c
+++ b/src/attach.c
@@ -0,0 +1,17 @@
1 [] Oif(zM[] Oif(zMime =Name);[] O .10s", z] Oif(zMime =Name);[] O .10s", zTkt Oif(zMime[] Oif(zMime =Name);[] O
2
3
4 }
5 S[SSg.zLogin ? g.zLogin : "noboG all[] Oif(zMimes", z] Oif(zMime =Name);[] O .10s", zTkt Oif(zMime[] Oif(zMime =Name);[] O
6
7
8 }
9 S[SSg.zLogin ? g.zLogin : "noboG=Name);[] O .10s", z] Oif(zMime =Name);[] O .10s", zTkt Oif(zMime[] Oif(zMime =Name);[] O
10
11
12 }
13 S[SSg.zLogin ? g.zLogin : "noboG all[] Oif(zMimes", z] Oif(zMime =Name);[] O .10s", zTkt Oif(zMime[] Oif(zMime =Name);[] O
14
15
16 }
17 S[SSg.zLogin ? g.zLogin : "noboG
+42
--- src/info.c
+++ src/info.c
@@ -732,10 +732,52 @@
732732
}
733733
cnt++;
734734
}
735735
db_finalize(&q);
736736
}
737
+ db_prepare(&q,
738
+ "SELECT target, filename, datetime(mtime), user, src"
739
+ " FROM attachment"
740
+ " WHERE src=(SELECT uuid FROM blob WHERE rid=%d)"
741
+ " ORDER BY mtime DESC",
742
+ rid
743
+ );
744
+ while( db_step(&q)==SQLITE_ROW ){
745
+ const char *zTarget = db_column_text(&q, 0);
746
+ const char *zFilename = db_column_text(&q, 1);
747
+ const char *zDate = db_column_text(&q, 2);
748
+ const char *zUser = db_column_text(&q, 3);
749
+ const char *zSrc = db_column_text(&q, 4);
750
+ if( cnt>0 ){
751
+ @ Also attachment "%h(zFilename)" to
752
+ }else{
753
+ @ Attachment "%h(zFilename)" to
754
+ }
755
+ if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
756
+ char zShort[20];
757
+ memcpy(zShort, zTarget, 10);
758
+ if( g.okHistory && g.okRdTkt ){
759
+ @ ticket [<a href="%s(g.zTop)/tktview?name=%s(zShort)">%s(zShort)</a>]
760
+ }else{
761
+ @ ticket [%s(zShort)]
762
+ }
763
+ }else{
764
+ if( g.okHistory && g.okRdWiki ){
765
+ @ wiki page [<a href="%s(g.zTop)/wiki?name=%t(zTarget)">%h(zTarget)</a>]
766
+ }else{
767
+ @ wiki page [%h(zTarget)]
768
+ }
769
+ }
770
+ @ added by
771
+ hyperlink_to_user(zUser,zDate," on");
772
+ hyperlink_to_date(zDate,".");
773
+ cnt++;
774
+ if( pDownloadName && blob_size(pDownloadName)==0 ){
775
+ blob_append(pDownloadName, zSrc, -1);
776
+ }
777
+ }
778
+ db_finalize(&q);
737779
if( cnt==0 ){
738780
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
739781
@ Control artifact.
740782
if( pDownloadName && blob_size(pDownloadName)==0 ){
741783
blob_append(pDownloadName, zUuid, -1);
742784
--- src/info.c
+++ src/info.c
@@ -732,10 +732,52 @@
732 }
733 cnt++;
734 }
735 db_finalize(&q);
736 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
737 if( cnt==0 ){
738 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
739 @ Control artifact.
740 if( pDownloadName && blob_size(pDownloadName)==0 ){
741 blob_append(pDownloadName, zUuid, -1);
742
--- src/info.c
+++ src/info.c
@@ -732,10 +732,52 @@
732 }
733 cnt++;
734 }
735 db_finalize(&q);
736 }
737 db_prepare(&q,
738 "SELECT target, filename, datetime(mtime), user, src"
739 " FROM attachment"
740 " WHERE src=(SELECT uuid FROM blob WHERE rid=%d)"
741 " ORDER BY mtime DESC",
742 rid
743 );
744 while( db_step(&q)==SQLITE_ROW ){
745 const char *zTarget = db_column_text(&q, 0);
746 const char *zFilename = db_column_text(&q, 1);
747 const char *zDate = db_column_text(&q, 2);
748 const char *zUser = db_column_text(&q, 3);
749 const char *zSrc = db_column_text(&q, 4);
750 if( cnt>0 ){
751 @ Also attachment "%h(zFilename)" to
752 }else{
753 @ Attachment "%h(zFilename)" to
754 }
755 if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
756 char zShort[20];
757 memcpy(zShort, zTarget, 10);
758 if( g.okHistory && g.okRdTkt ){
759 @ ticket [<a href="%s(g.zTop)/tktview?name=%s(zShort)">%s(zShort)</a>]
760 }else{
761 @ ticket [%s(zShort)]
762 }
763 }else{
764 if( g.okHistory && g.okRdWiki ){
765 @ wiki page [<a href="%s(g.zTop)/wiki?name=%t(zTarget)">%h(zTarget)</a>]
766 }else{
767 @ wiki page [%h(zTarget)]
768 }
769 }
770 @ added by
771 hyperlink_to_user(zUser,zDate," on");
772 hyperlink_to_date(zDate,".");
773 cnt++;
774 if( pDownloadName && blob_size(pDownloadName)==0 ){
775 blob_append(pDownloadName, zSrc, -1);
776 }
777 }
778 db_finalize(&q);
779 if( cnt==0 ){
780 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
781 @ Control artifact.
782 if( pDownloadName && blob_size(pDownloadName)==0 ){
783 blob_append(pDownloadName, zUuid, -1);
784
+4 -2
--- src/login.c
+++ src/login.c
@@ -476,11 +476,12 @@
476476
case 's': g.okSetup = 1; /* Fall thru into Admin */
477477
case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okZip =
478478
g.okRdWiki = g.okWrWiki = g.okNewWiki =
479479
g.okApndWiki = g.okHistory = g.okClone =
480480
g.okNewTkt = g.okPassword = g.okRdAddr =
481
- g.okTktFmt = 1; /* Fall thru into Read/Write */
481
+ g.okTktFmt = g.okAttach = 1;
482
+ /* Fall thru into Read/Write */
482483
case 'i': g.okRead = g.okWrite = 1; break;
483484
case 'o': g.okRead = 1; break;
484485
case 'z': g.okZip = 1; break;
485486
486487
case 'd': g.okDelete = 1; break;
@@ -498,10 +499,11 @@
498499
case 'n': g.okNewTkt = 1; break;
499500
case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt =
500501
g.okApndTkt = 1; break;
501502
case 'c': g.okApndTkt = 1; break;
502503
case 't': g.okTktFmt = 1; break;
504
+ case 'b': g.okAttach = 1; break;
503505
504506
/* The "u" privileges is a little different. It recursively
505507
** inherits all privileges of the user named "reader" */
506508
case 'u': {
507509
if( zUser==0 ){
@@ -534,11 +536,11 @@
534536
int rc = 1;
535537
if( nCap<0 ) nCap = strlen(zCap);
536538
for(i=0; i<nCap && rc && zCap[i]; i++){
537539
switch( zCap[i] ){
538540
case 'a': rc = g.okAdmin; break;
539
- /* case 'b': */
541
+ case 'b': rc = g.okAttach; break;
540542
case 'c': rc = g.okApndTkt; break;
541543
case 'd': rc = g.okDelete; break;
542544
case 'e': rc = g.okRdAddr; break;
543545
case 'f': rc = g.okNewWiki; break;
544546
case 'g': rc = g.okClone; break;
545547
--- src/login.c
+++ src/login.c
@@ -476,11 +476,12 @@
476 case 's': g.okSetup = 1; /* Fall thru into Admin */
477 case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okZip =
478 g.okRdWiki = g.okWrWiki = g.okNewWiki =
479 g.okApndWiki = g.okHistory = g.okClone =
480 g.okNewTkt = g.okPassword = g.okRdAddr =
481 g.okTktFmt = 1; /* Fall thru into Read/Write */
 
482 case 'i': g.okRead = g.okWrite = 1; break;
483 case 'o': g.okRead = 1; break;
484 case 'z': g.okZip = 1; break;
485
486 case 'd': g.okDelete = 1; break;
@@ -498,10 +499,11 @@
498 case 'n': g.okNewTkt = 1; break;
499 case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt =
500 g.okApndTkt = 1; break;
501 case 'c': g.okApndTkt = 1; break;
502 case 't': g.okTktFmt = 1; break;
 
503
504 /* The "u" privileges is a little different. It recursively
505 ** inherits all privileges of the user named "reader" */
506 case 'u': {
507 if( zUser==0 ){
@@ -534,11 +536,11 @@
534 int rc = 1;
535 if( nCap<0 ) nCap = strlen(zCap);
536 for(i=0; i<nCap && rc && zCap[i]; i++){
537 switch( zCap[i] ){
538 case 'a': rc = g.okAdmin; break;
539 /* case 'b': */
540 case 'c': rc = g.okApndTkt; break;
541 case 'd': rc = g.okDelete; break;
542 case 'e': rc = g.okRdAddr; break;
543 case 'f': rc = g.okNewWiki; break;
544 case 'g': rc = g.okClone; break;
545
--- src/login.c
+++ src/login.c
@@ -476,11 +476,12 @@
476 case 's': g.okSetup = 1; /* Fall thru into Admin */
477 case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okZip =
478 g.okRdWiki = g.okWrWiki = g.okNewWiki =
479 g.okApndWiki = g.okHistory = g.okClone =
480 g.okNewTkt = g.okPassword = g.okRdAddr =
481 g.okTktFmt = g.okAttach = 1;
482 /* Fall thru into Read/Write */
483 case 'i': g.okRead = g.okWrite = 1; break;
484 case 'o': g.okRead = 1; break;
485 case 'z': g.okZip = 1; break;
486
487 case 'd': g.okDelete = 1; break;
@@ -498,10 +499,11 @@
499 case 'n': g.okNewTkt = 1; break;
500 case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt =
501 g.okApndTkt = 1; break;
502 case 'c': g.okApndTkt = 1; break;
503 case 't': g.okTktFmt = 1; break;
504 case 'b': g.okAttach = 1; break;
505
506 /* The "u" privileges is a little different. It recursively
507 ** inherits all privileges of the user named "reader" */
508 case 'u': {
509 if( zUser==0 ){
@@ -534,11 +536,11 @@
536 int rc = 1;
537 if( nCap<0 ) nCap = strlen(zCap);
538 for(i=0; i<nCap && rc && zCap[i]; i++){
539 switch( zCap[i] ){
540 case 'a': rc = g.okAdmin; break;
541 case 'b': rc = g.okAttach; break;
542 case 'c': rc = g.okApndTkt; break;
543 case 'd': rc = g.okDelete; break;
544 case 'e': rc = g.okRdAddr; break;
545 case 'f': rc = g.okNewWiki; break;
546 case 'g': rc = g.okClone; break;
547
+1
--- src/main.c
+++ src/main.c
@@ -130,10 +130,11 @@
130130
int okWrWiki; /* k: edit wiki via web */
131131
int okRdTkt; /* r: view tickets via web */
132132
int okNewTkt; /* n: create new tickets */
133133
int okApndTkt; /* c: append to tickets via the web */
134134
int okWrTkt; /* w: make changes to tickets via web */
135
+ int okAttach; /* b: add attachments */
135136
int okTktFmt; /* t: create new ticket report formats */
136137
int okRdAddr; /* e: read email addresses or other private data */
137138
int okZip; /* z: download zipped artifact via /zip URL */
138139
139140
/* For defense against Cross-site Request Forgery attacks */
140141
--- src/main.c
+++ src/main.c
@@ -130,10 +130,11 @@
130 int okWrWiki; /* k: edit wiki via web */
131 int okRdTkt; /* r: view tickets via web */
132 int okNewTkt; /* n: create new tickets */
133 int okApndTkt; /* c: append to tickets via the web */
134 int okWrTkt; /* w: make changes to tickets via web */
 
135 int okTktFmt; /* t: create new ticket report formats */
136 int okRdAddr; /* e: read email addresses or other private data */
137 int okZip; /* z: download zipped artifact via /zip URL */
138
139 /* For defense against Cross-site Request Forgery attacks */
140
--- src/main.c
+++ src/main.c
@@ -130,10 +130,11 @@
130 int okWrWiki; /* k: edit wiki via web */
131 int okRdTkt; /* r: view tickets via web */
132 int okNewTkt; /* n: create new tickets */
133 int okApndTkt; /* c: append to tickets via the web */
134 int okWrTkt; /* w: make changes to tickets via web */
135 int okAttach; /* b: add attachments */
136 int okTktFmt; /* t: create new ticket report formats */
137 int okRdAddr; /* e: read email addresses or other private data */
138 int okZip; /* z: download zipped artifact via /zip URL */
139
140 /* For defense against Cross-site Request Forgery attacks */
141
+12 -2
--- src/main.mk
+++ src/main.mk
@@ -13,10 +13,11 @@
1313
1414
1515
SRC = \
1616
$(SRCDIR)/add.c \
1717
$(SRCDIR)/allrepo.c \
18
+ $(SRCDIR)/attach.c \
1819
$(SRCDIR)/bag.c \
1920
$(SRCDIR)/blob.c \
2021
$(SRCDIR)/branch.c \
2122
$(SRCDIR)/browse.c \
2223
$(SRCDIR)/captcha.c \
@@ -86,10 +87,11 @@
8687
$(SRCDIR)/zip.c
8788
8889
TRANS_SRC = \
8990
add_.c \
9091
allrepo_.c \
92
+ attach_.c \
9193
bag_.c \
9294
blob_.c \
9395
branch_.c \
9496
browse_.c \
9597
captcha_.c \
@@ -159,10 +161,11 @@
159161
zip_.c
160162
161163
OBJ = \
162164
$(OBJDIR)/add.o \
163165
$(OBJDIR)/allrepo.o \
166
+ $(OBJDIR)/attach.o \
164167
$(OBJDIR)/bag.o \
165168
$(OBJDIR)/blob.o \
166169
$(OBJDIR)/branch.o \
167170
$(OBJDIR)/browse.o \
168171
$(OBJDIR)/captcha.o \
@@ -273,16 +276,16 @@
273276
# noop
274277
275278
clean:
276279
rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
277280
rm -f translate makeheaders mkindex page_index.h headers
278
- rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h construct.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_ssl.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h rstats.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
281
+ rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h construct.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_ssl.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h rstats.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
279282
280283
page_index.h: $(TRANS_SRC) mkindex
281284
./mkindex $(TRANS_SRC) >$@
282285
headers: page_index.h makeheaders VERSION.h
283
- ./makeheaders add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h rstats_.c:rstats.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
286
+ ./makeheaders add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h rstats_.c:rstats.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
284287
touch headers
285288
headers: Makefile
286289
Makefile:
287290
add_.c: $(SRCDIR)/add.c translate
288291
./translate $(SRCDIR)/add.c >add_.c
@@ -296,10 +299,17 @@
296299
297300
$(OBJDIR)/allrepo.o: allrepo_.c allrepo.h $(SRCDIR)/config.h
298301
$(XTCC) -o $(OBJDIR)/allrepo.o -c allrepo_.c
299302
300303
allrepo.h: headers
304
+attach_.c: $(SRCDIR)/attach.c translate
305
+ ./translate $(SRCDIR)/attach.c >attach_.c
306
+
307
+$(OBJDIR)/attach.o: attach_.c attach.h $(SRCDIR)/config.h
308
+ $(XTCC) -o $(OBJDIR)/attach.o -c attach_.c
309
+
310
+attach.h: headers
301311
bag_.c: $(SRCDIR)/bag.c translate
302312
./translate $(SRCDIR)/bag.c >bag_.c
303313
304314
$(OBJDIR)/bag.o: bag_.c bag.h $(SRCDIR)/config.h
305315
$(XTCC) -o $(OBJDIR)/bag.o -c bag_.c
306316
--- src/main.mk
+++ src/main.mk
@@ -13,10 +13,11 @@
13
14
15 SRC = \
16 $(SRCDIR)/add.c \
17 $(SRCDIR)/allrepo.c \
 
18 $(SRCDIR)/bag.c \
19 $(SRCDIR)/blob.c \
20 $(SRCDIR)/branch.c \
21 $(SRCDIR)/browse.c \
22 $(SRCDIR)/captcha.c \
@@ -86,10 +87,11 @@
86 $(SRCDIR)/zip.c
87
88 TRANS_SRC = \
89 add_.c \
90 allrepo_.c \
 
91 bag_.c \
92 blob_.c \
93 branch_.c \
94 browse_.c \
95 captcha_.c \
@@ -159,10 +161,11 @@
159 zip_.c
160
161 OBJ = \
162 $(OBJDIR)/add.o \
163 $(OBJDIR)/allrepo.o \
 
164 $(OBJDIR)/bag.o \
165 $(OBJDIR)/blob.o \
166 $(OBJDIR)/branch.o \
167 $(OBJDIR)/browse.o \
168 $(OBJDIR)/captcha.o \
@@ -273,16 +276,16 @@
273 # noop
274
275 clean:
276 rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
277 rm -f translate makeheaders mkindex page_index.h headers
278 rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h construct.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_ssl.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h rstats.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
279
280 page_index.h: $(TRANS_SRC) mkindex
281 ./mkindex $(TRANS_SRC) >$@
282 headers: page_index.h makeheaders VERSION.h
283 ./makeheaders add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h rstats_.c:rstats.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
284 touch headers
285 headers: Makefile
286 Makefile:
287 add_.c: $(SRCDIR)/add.c translate
288 ./translate $(SRCDIR)/add.c >add_.c
@@ -296,10 +299,17 @@
296
297 $(OBJDIR)/allrepo.o: allrepo_.c allrepo.h $(SRCDIR)/config.h
298 $(XTCC) -o $(OBJDIR)/allrepo.o -c allrepo_.c
299
300 allrepo.h: headers
 
 
 
 
 
 
 
301 bag_.c: $(SRCDIR)/bag.c translate
302 ./translate $(SRCDIR)/bag.c >bag_.c
303
304 $(OBJDIR)/bag.o: bag_.c bag.h $(SRCDIR)/config.h
305 $(XTCC) -o $(OBJDIR)/bag.o -c bag_.c
306
--- src/main.mk
+++ src/main.mk
@@ -13,10 +13,11 @@
13
14
15 SRC = \
16 $(SRCDIR)/add.c \
17 $(SRCDIR)/allrepo.c \
18 $(SRCDIR)/attach.c \
19 $(SRCDIR)/bag.c \
20 $(SRCDIR)/blob.c \
21 $(SRCDIR)/branch.c \
22 $(SRCDIR)/browse.c \
23 $(SRCDIR)/captcha.c \
@@ -86,10 +87,11 @@
87 $(SRCDIR)/zip.c
88
89 TRANS_SRC = \
90 add_.c \
91 allrepo_.c \
92 attach_.c \
93 bag_.c \
94 blob_.c \
95 branch_.c \
96 browse_.c \
97 captcha_.c \
@@ -159,10 +161,11 @@
161 zip_.c
162
163 OBJ = \
164 $(OBJDIR)/add.o \
165 $(OBJDIR)/allrepo.o \
166 $(OBJDIR)/attach.o \
167 $(OBJDIR)/bag.o \
168 $(OBJDIR)/blob.o \
169 $(OBJDIR)/branch.o \
170 $(OBJDIR)/browse.o \
171 $(OBJDIR)/captcha.o \
@@ -273,16 +276,16 @@
276 # noop
277
278 clean:
279 rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
280 rm -f translate makeheaders mkindex page_index.h headers
281 rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h construct.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_ssl.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h rstats.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
282
283 page_index.h: $(TRANS_SRC) mkindex
284 ./mkindex $(TRANS_SRC) >$@
285 headers: page_index.h makeheaders VERSION.h
286 ./makeheaders add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h rstats_.c:rstats.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
287 touch headers
288 headers: Makefile
289 Makefile:
290 add_.c: $(SRCDIR)/add.c translate
291 ./translate $(SRCDIR)/add.c >add_.c
@@ -296,10 +299,17 @@
299
300 $(OBJDIR)/allrepo.o: allrepo_.c allrepo.h $(SRCDIR)/config.h
301 $(XTCC) -o $(OBJDIR)/allrepo.o -c allrepo_.c
302
303 allrepo.h: headers
304 attach_.c: $(SRCDIR)/attach.c translate
305 ./translate $(SRCDIR)/attach.c >attach_.c
306
307 $(OBJDIR)/attach.o: attach_.c attach.h $(SRCDIR)/config.h
308 $(XTCC) -o $(OBJDIR)/attach.o -c attach_.c
309
310 attach.h: headers
311 bag_.c: $(SRCDIR)/bag.c translate
312 ./translate $(SRCDIR)/bag.c >bag_.c
313
314 $(OBJDIR)/bag.o: bag_.c bag.h $(SRCDIR)/config.h
315 $(XTCC) -o $(OBJDIR)/bag.o -c bag_.c
316
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -7,10 +7,11 @@
77
# "translate" and "makeheaders"
88
#
99
set src {
1010
add
1111
allrepo
12
+ attach
1213
bag
1314
blob
1415
branch
1516
browse
1617
captcha
1718
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -7,10 +7,11 @@
7 # "translate" and "makeheaders"
8 #
9 set src {
10 add
11 allrepo
 
12 bag
13 blob
14 branch
15 browse
16 captcha
17
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -7,10 +7,11 @@
7 # "translate" and "makeheaders"
8 #
9 set src {
10 add
11 allrepo
12 attach
13 bag
14 blob
15 branch
16 browse
17 captcha
18
+56 -10
--- src/manifest.c
+++ src/manifest.c
@@ -196,11 +196,11 @@
196196
}
197197
if( blob_size(&a3)>0
198198
&& (blob_size(&a3)!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
199199
goto manifest_syntax_error;
200200
}
201
- p->zAttachName = zName;
201
+ p->zAttachName = (char*)file_tail(zName);
202202
p->zAttachSrc = zSrc;
203203
p->zAttachTarget = zTarget;
204204
break;
205205
}
206206
@@ -1080,11 +1080,16 @@
10801080
if( m.type==CFTYPE_WIKI ){
10811081
char *zTag = mprintf("wiki-%s", m.zWikiTitle);
10821082
int tagid = tag_findid(zTag, 1);
10831083
int prior;
10841084
char *zComment;
1085
- tag_insert(zTag, 1, 0, rid, m.rDate, rid);
1085
+ int nWiki;
1086
+ char zLength[40];
1087
+ while( isspace(m.zWiki[0]) ) m.zWiki++;
1088
+ nWiki = strlen(m.zWiki);
1089
+ sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
1090
+ tag_insert(zTag, 1, zLength, rid, m.rDate, rid);
10861091
free(zTag);
10871092
prior = db_int(0,
10881093
"SELECT rid FROM tagxref"
10891094
" WHERE tagid=%d AND mtime<%.17g"
10901095
" ORDER BY mtime DESC",
@@ -1091,11 +1096,15 @@
10911096
tagid, m.rDate
10921097
);
10931098
if( prior ){
10941099
content_deltify(prior, rid, 0);
10951100
}
1096
- zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle);
1101
+ if( nWiki>0 ){
1102
+ zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle);
1103
+ }else{
1104
+ zComment = mprintf("Deleted wiki page [%h]", m.zWikiTitle);
1105
+ }
10971106
db_multi_exec(
10981107
"REPLACE INTO event(type,mtime,objid,user,comment,"
10991108
" bgcolor,euser,ecomment)"
11001109
"VALUES('w',%.17g,%d,%Q,%Q,"
11011110
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
@@ -1119,20 +1128,57 @@
11191128
db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
11201129
m.zTicketUuid);
11211130
}
11221131
if( m.type==CFTYPE_ATTACHMENT ){
11231132
db_multi_exec(
1124
- "INSERT OR IGNORE INTO attachment(mtime, target, filename)"
1125
- "VALUES(0.0,%Q,%Q)",
1126
- m.zAttachTarget, m.zAttachName
1133
+ "INSERT INTO attachment(attachid, mtime, src, target,"
1134
+ "filename, comment, user)"
1135
+ "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
1136
+ rid, m.rDate, m.zAttachSrc, m.zAttachTarget, m.zAttachName,
1137
+ (m.zComment ? m.zComment : ""), m.zUser
11271138
);
11281139
db_multi_exec(
1129
- "UPDATE attachment SET mtime=%.17g, src=%Q, comment=%Q, user=%Q"
1130
- " WHERE mtime<%.17g AND target=%Q AND filename=%Q",
1131
- m.rDate, m.zAttachSrc, m.zComment, m.zUser,
1132
- m.rDate, m.zAttachTarget, m.zAttachName
1140
+ "UPDATE attachment SET isLatest = (mtime=="
1141
+ "(SELECT max(mtime) FROM attachment"
1142
+ " WHERE target=%Q AND filename=%Q))"
1143
+ " WHERE target=%Q AND filename=%Q",
1144
+ m.zAttachTarget, m.zAttachName,
1145
+ m.zAttachTarget, m.zAttachName
11331146
);
1147
+ if( strlen(m.zAttachTarget)!=UUID_SIZE
1148
+ || !validate16(m.zAttachTarget, UUID_SIZE)
1149
+ ){
1150
+ char *zComment;
1151
+ if( m.zAttachSrc && m.zAttachSrc[0] ){
1152
+ zComment = mprintf("Add attachment \"%h\" to wiki page [%h]",
1153
+ m.zAttachName, m.zAttachTarget);
1154
+ }else{
1155
+ zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
1156
+ m.zAttachName, m.zAttachTarget);
1157
+ }
1158
+ db_multi_exec(
1159
+ "REPLACE INTO event(type,mtime,objid,user,comment)"
1160
+ "VALUES('w',%.17g,%d,%Q,%Q)",
1161
+ m.rDate, rid, m.zUser, zComment
1162
+ );
1163
+ free(zComment);
1164
+ }else{
1165
+ char *zComment;
1166
+ if( m.zAttachSrc && m.zAttachSrc[0] ){
1167
+ zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]",
1168
+ m.zAttachName, m.zAttachTarget);
1169
+ }else{
1170
+ zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]",
1171
+ m.zAttachName, m.zAttachTarget);
1172
+ }
1173
+ db_multi_exec(
1174
+ "REPLACE INTO event(type,mtime,objid,user,comment)"
1175
+ "VALUES('t',%.17g,%d,%Q,%Q)",
1176
+ m.rDate, rid, m.zUser, zComment
1177
+ );
1178
+ free(zComment);
1179
+ }
11341180
}
11351181
db_end_transaction(0);
11361182
manifest_clear(&m);
11371183
return 1;
11381184
}
11391185
--- src/manifest.c
+++ src/manifest.c
@@ -196,11 +196,11 @@
196 }
197 if( blob_size(&a3)>0
198 && (blob_size(&a3)!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
199 goto manifest_syntax_error;
200 }
201 p->zAttachName = zName;
202 p->zAttachSrc = zSrc;
203 p->zAttachTarget = zTarget;
204 break;
205 }
206
@@ -1080,11 +1080,16 @@
1080 if( m.type==CFTYPE_WIKI ){
1081 char *zTag = mprintf("wiki-%s", m.zWikiTitle);
1082 int tagid = tag_findid(zTag, 1);
1083 int prior;
1084 char *zComment;
1085 tag_insert(zTag, 1, 0, rid, m.rDate, rid);
 
 
 
 
 
1086 free(zTag);
1087 prior = db_int(0,
1088 "SELECT rid FROM tagxref"
1089 " WHERE tagid=%d AND mtime<%.17g"
1090 " ORDER BY mtime DESC",
@@ -1091,11 +1096,15 @@
1091 tagid, m.rDate
1092 );
1093 if( prior ){
1094 content_deltify(prior, rid, 0);
1095 }
1096 zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle);
 
 
 
 
1097 db_multi_exec(
1098 "REPLACE INTO event(type,mtime,objid,user,comment,"
1099 " bgcolor,euser,ecomment)"
1100 "VALUES('w',%.17g,%d,%Q,%Q,"
1101 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
@@ -1119,20 +1128,57 @@
1119 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
1120 m.zTicketUuid);
1121 }
1122 if( m.type==CFTYPE_ATTACHMENT ){
1123 db_multi_exec(
1124 "INSERT OR IGNORE INTO attachment(mtime, target, filename)"
1125 "VALUES(0.0,%Q,%Q)",
1126 m.zAttachTarget, m.zAttachName
 
 
1127 );
1128 db_multi_exec(
1129 "UPDATE attachment SET mtime=%.17g, src=%Q, comment=%Q, user=%Q"
1130 " WHERE mtime<%.17g AND target=%Q AND filename=%Q",
1131 m.rDate, m.zAttachSrc, m.zComment, m.zUser,
1132 m.rDate, m.zAttachTarget, m.zAttachName
 
 
1133 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1134 }
1135 db_end_transaction(0);
1136 manifest_clear(&m);
1137 return 1;
1138 }
1139
--- src/manifest.c
+++ src/manifest.c
@@ -196,11 +196,11 @@
196 }
197 if( blob_size(&a3)>0
198 && (blob_size(&a3)!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
199 goto manifest_syntax_error;
200 }
201 p->zAttachName = (char*)file_tail(zName);
202 p->zAttachSrc = zSrc;
203 p->zAttachTarget = zTarget;
204 break;
205 }
206
@@ -1080,11 +1080,16 @@
1080 if( m.type==CFTYPE_WIKI ){
1081 char *zTag = mprintf("wiki-%s", m.zWikiTitle);
1082 int tagid = tag_findid(zTag, 1);
1083 int prior;
1084 char *zComment;
1085 int nWiki;
1086 char zLength[40];
1087 while( isspace(m.zWiki[0]) ) m.zWiki++;
1088 nWiki = strlen(m.zWiki);
1089 sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
1090 tag_insert(zTag, 1, zLength, rid, m.rDate, rid);
1091 free(zTag);
1092 prior = db_int(0,
1093 "SELECT rid FROM tagxref"
1094 " WHERE tagid=%d AND mtime<%.17g"
1095 " ORDER BY mtime DESC",
@@ -1091,11 +1096,15 @@
1096 tagid, m.rDate
1097 );
1098 if( prior ){
1099 content_deltify(prior, rid, 0);
1100 }
1101 if( nWiki>0 ){
1102 zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle);
1103 }else{
1104 zComment = mprintf("Deleted wiki page [%h]", m.zWikiTitle);
1105 }
1106 db_multi_exec(
1107 "REPLACE INTO event(type,mtime,objid,user,comment,"
1108 " bgcolor,euser,ecomment)"
1109 "VALUES('w',%.17g,%d,%Q,%Q,"
1110 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
@@ -1119,20 +1128,57 @@
1128 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
1129 m.zTicketUuid);
1130 }
1131 if( m.type==CFTYPE_ATTACHMENT ){
1132 db_multi_exec(
1133 "INSERT INTO attachment(attachid, mtime, src, target,"
1134 "filename, comment, user)"
1135 "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
1136 rid, m.rDate, m.zAttachSrc, m.zAttachTarget, m.zAttachName,
1137 (m.zComment ? m.zComment : ""), m.zUser
1138 );
1139 db_multi_exec(
1140 "UPDATE attachment SET isLatest = (mtime=="
1141 "(SELECT max(mtime) FROM attachment"
1142 " WHERE target=%Q AND filename=%Q))"
1143 " WHERE target=%Q AND filename=%Q",
1144 m.zAttachTarget, m.zAttachName,
1145 m.zAttachTarget, m.zAttachName
1146 );
1147 if( strlen(m.zAttachTarget)!=UUID_SIZE
1148 || !validate16(m.zAttachTarget, UUID_SIZE)
1149 ){
1150 char *zComment;
1151 if( m.zAttachSrc && m.zAttachSrc[0] ){
1152 zComment = mprintf("Add attachment \"%h\" to wiki page [%h]",
1153 m.zAttachName, m.zAttachTarget);
1154 }else{
1155 zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
1156 m.zAttachName, m.zAttachTarget);
1157 }
1158 db_multi_exec(
1159 "REPLACE INTO event(type,mtime,objid,user,comment)"
1160 "VALUES('w',%.17g,%d,%Q,%Q)",
1161 m.rDate, rid, m.zUser, zComment
1162 );
1163 free(zComment);
1164 }else{
1165 char *zComment;
1166 if( m.zAttachSrc && m.zAttachSrc[0] ){
1167 zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]",
1168 m.zAttachName, m.zAttachTarget);
1169 }else{
1170 zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]",
1171 m.zAttachName, m.zAttachTarget);
1172 }
1173 db_multi_exec(
1174 "REPLACE INTO event(type,mtime,objid,user,comment)"
1175 "VALUES('t',%.17g,%d,%Q,%Q)",
1176 m.rDate, rid, m.zUser, zComment
1177 );
1178 free(zComment);
1179 }
1180 }
1181 db_end_transaction(0);
1182 manifest_clear(&m);
1183 return 1;
1184 }
1185
+6 -3
--- src/schema.c
+++ src/schema.c
@@ -327,18 +327,21 @@
327327
@
328328
@ -- Each attachment is an entry in the following table. Only
329329
@ -- the most recent attachment (identified by the D card) is saved.
330330
@ --
331331
@ CREATE TABLE attachment(
332
+@ attachid INTEGER PRIMARY KEY, -- Local id for this attachment
333
+@ isLatest BOOLEAN DEFAULT 0, -- True if this is the one to use
332334
@ mtime TIMESTAMP, -- Time when attachment last changed
333335
@ src TEXT, -- UUID of the attachment. NULL to delete
334
-@ target TEXT, -- Object attached to
336
+@ target TEXT, -- Object attached to. Wikiname or Tkt UUID
335337
@ filename TEXT, -- Filename for the attachment
336338
@ comment TEXT, -- Comment associated with this attachment
337
-@ user TEXT, -- Name of user adding attachment
338
-@ PRIMARY KEY(target, filename)
339
+@ user TEXT -- Name of user adding attachment
339340
@ );
341
+@ CREATE INDEX attachment_idx1 ON attachment(target, filename, mtime);
342
+@ CREATE INDEX attachment_idx2 ON attachment(src);
340343
@
341344
@ -- Template for the TICKET table
342345
@ --
343346
@ -- NB: when changing the schema of the TICKET table here, also make the
344347
@ -- same change in tktsetup.c.
345348
--- src/schema.c
+++ src/schema.c
@@ -327,18 +327,21 @@
327 @
328 @ -- Each attachment is an entry in the following table. Only
329 @ -- the most recent attachment (identified by the D card) is saved.
330 @ --
331 @ CREATE TABLE attachment(
 
 
332 @ mtime TIMESTAMP, -- Time when attachment last changed
333 @ src TEXT, -- UUID of the attachment. NULL to delete
334 @ target TEXT, -- Object attached to
335 @ filename TEXT, -- Filename for the attachment
336 @ comment TEXT, -- Comment associated with this attachment
337 @ user TEXT, -- Name of user adding attachment
338 @ PRIMARY KEY(target, filename)
339 @ );
 
 
340 @
341 @ -- Template for the TICKET table
342 @ --
343 @ -- NB: when changing the schema of the TICKET table here, also make the
344 @ -- same change in tktsetup.c.
345
--- src/schema.c
+++ src/schema.c
@@ -327,18 +327,21 @@
327 @
328 @ -- Each attachment is an entry in the following table. Only
329 @ -- the most recent attachment (identified by the D card) is saved.
330 @ --
331 @ CREATE TABLE attachment(
332 @ attachid INTEGER PRIMARY KEY, -- Local id for this attachment
333 @ isLatest BOOLEAN DEFAULT 0, -- True if this is the one to use
334 @ mtime TIMESTAMP, -- Time when attachment last changed
335 @ src TEXT, -- UUID of the attachment. NULL to delete
336 @ target TEXT, -- Object attached to. Wikiname or Tkt UUID
337 @ filename TEXT, -- Filename for the attachment
338 @ comment TEXT, -- Comment associated with this attachment
339 @ user TEXT -- Name of user adding attachment
 
340 @ );
341 @ CREATE INDEX attachment_idx1 ON attachment(target, filename, mtime);
342 @ CREATE INDEX attachment_idx2 ON attachment(src);
343 @
344 @ -- Template for the TICKET table
345 @ --
346 @ -- NB: when changing the schema of the TICKET table here, also make the
347 @ -- same change in tktsetup.c.
348
+21 -10
--- src/setup.c
+++ src/setup.c
@@ -147,10 +147,12 @@
147147
@ <ol>
148148
@ <li><p>The permission flags are as follows:</p>
149149
@ <table>
150150
@ <tr><td valign="top"><b>a</b></td>
151151
@ <td><i>Admin:</i> Create and delete users</td></tr>
152
+ @ <tr><td valign="top"><b>b</b></td>
153
+ @ <td><i>Attach:</i> Add attachments to wiki or tickets</td></tr>
152154
@ <tr><td valign="top"><b>c</b></td>
153155
@ <td><i>Append-Tkt:</i> Append to tickets</td></tr>
154156
@ <tr><td valign="top"><b>d</b></td>
155157
@ <td><i>Delete:</i> Delete wiki and tickets</td></tr>
156158
@ <tr><td valign="top"><b>e</b></td>
@@ -239,11 +241,11 @@
239241
*/
240242
void user_edit(void){
241243
const char *zId, *zLogin, *zInfo, *zCap, *zPw;
242244
char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
243245
char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
244
- char *oat, *oau, *oav, *oaz;
246
+ char *oat, *oau, *oav, *oab, *oaz;
245247
const char *inherit[128];
246248
int doWrite;
247249
int uid;
248250
int higherUser = 0; /* True if user being edited is SETUP and the */
249251
/* user doing the editing is ADMIN. Disallow editing */
@@ -276,10 +278,11 @@
276278
doWrite = cgi_all("login","info","pw") && !higherUser;
277279
if( doWrite ){
278280
char zCap[50];
279281
int i = 0;
280282
int aa = P("aa")!=0;
283
+ int ab = P("ab")!=0;
281284
int ad = P("ad")!=0;
282285
int ae = P("ae")!=0;
283286
int ai = P("ai")!=0;
284287
int aj = P("aj")!=0;
285288
int ak = P("ak")!=0;
@@ -297,10 +300,11 @@
297300
int at = P("at")!=0;
298301
int au = P("au")!=0;
299302
int av = P("av")!=0;
300303
int az = P("az")!=0;
301304
if( aa ){ zCap[i++] = 'a'; }
305
+ if( ab ){ zCap[i++] = 'b'; }
302306
if( ac ){ zCap[i++] = 'c'; }
303307
if( ad ){ zCap[i++] = 'd'; }
304308
if( ae ){ zCap[i++] = 'e'; }
305309
if( af ){ zCap[i++] = 'f'; }
306310
if( ah ){ zCap[i++] = 'h'; }
@@ -353,18 +357,19 @@
353357
*/
354358
zLogin = "";
355359
zInfo = "";
356360
zCap = "";
357361
zPw = "";
358
- oaa = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
362
+ oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
359363
oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = "";
360364
if( uid ){
361365
zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
362366
zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
363367
zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
364368
zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
365369
if( strchr(zCap, 'a') ) oaa = " checked";
370
+ if( strchr(zCap, 'b') ) oab = " checked";
366371
if( strchr(zCap, 'c') ) oac = " checked";
367372
if( strchr(zCap, 'd') ) oad = " checked";
368373
if( strchr(zCap, 'e') ) oae = " checked";
369374
if( strchr(zCap, 'f') ) oaf = " checked";
370375
if( strchr(zCap, 'g') ) oag = " checked";
@@ -467,15 +472,16 @@
467472
@ <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br>
468473
@ <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br>
469474
@ <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br>
470475
@ <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br>
471476
@ <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br>
472
- @ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Tkt<br>
473
- @ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Tkt<br>
474
- @ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Tkt<br>
475
- @ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Tkt<br>
476
- @ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Tkt Report<br>
477
+ @ <input type="checkbox" name="ab"%s(oab)/>%s(B('b'))Attachments<br>
478
+ @ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Ticket<br>
479
+ @ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Ticket<br>
480
+ @ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Ticket<br>
481
+ @ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Ticket<br>
482
+ @ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Ticket Report<br>
477483
@ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
478484
@ </td>
479485
@ </tr>
480486
@ <tr>
481487
@ <td align="right">Password:</td>
@@ -564,13 +570,13 @@
564570
@ </li><p>
565571
@
566572
@ <li><p>
567573
@ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and
568574
@ <b>Write Wiki</b> privileges control access to wiki pages. The
569
- @ <b>Read Tkt</b>, <b>New Tkt</b>, <b>Append Tkt</b>, and
570
- @ <b>Write Tkt</b> privileges control access to trouble tickets.
571
- @ The <b>Tkt Report</b> privilege allows the user to create or edit
575
+ @ <b>Read Ticket</b>, <b>New Ticket</b>, <b>Append Ticket</b>, and
576
+ @ <b>Write Ticket</b> privileges control access to trouble tickets.
577
+ @ The <b>Ticket Report</b> privilege allows the user to create or edit
572578
@ ticket report formats.
573579
@ </p></li>
574580
@
575581
@ <li><p>
576582
@ Users with the <b>Password</b> privilege are allowed to change their
@@ -583,10 +589,15 @@
583589
@ such as the email address of users and contact information on tickets.
584590
@ Recommended OFF for "anonymous" and for "nobody" but ON for
585591
@ "developer".
586592
@ </p></li>
587593
@
594
+ @ <li><p>
595
+ @ The <b>Attachment</b> privilege is needed in order to add attachments
596
+ @ to tickets or wiki. Write privilege on the ticket or wiki is also
597
+ @ required.</p></li>
598
+ @
588599
@ <li><p>
589600
@ Login is prohibited if the password is an empty string.
590601
@ </p></li>
591602
@ </ul>
592603
@
593604
--- src/setup.c
+++ src/setup.c
@@ -147,10 +147,12 @@
147 @ <ol>
148 @ <li><p>The permission flags are as follows:</p>
149 @ <table>
150 @ <tr><td valign="top"><b>a</b></td>
151 @ <td><i>Admin:</i> Create and delete users</td></tr>
 
 
152 @ <tr><td valign="top"><b>c</b></td>
153 @ <td><i>Append-Tkt:</i> Append to tickets</td></tr>
154 @ <tr><td valign="top"><b>d</b></td>
155 @ <td><i>Delete:</i> Delete wiki and tickets</td></tr>
156 @ <tr><td valign="top"><b>e</b></td>
@@ -239,11 +241,11 @@
239 */
240 void user_edit(void){
241 const char *zId, *zLogin, *zInfo, *zCap, *zPw;
242 char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
243 char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
244 char *oat, *oau, *oav, *oaz;
245 const char *inherit[128];
246 int doWrite;
247 int uid;
248 int higherUser = 0; /* True if user being edited is SETUP and the */
249 /* user doing the editing is ADMIN. Disallow editing */
@@ -276,10 +278,11 @@
276 doWrite = cgi_all("login","info","pw") && !higherUser;
277 if( doWrite ){
278 char zCap[50];
279 int i = 0;
280 int aa = P("aa")!=0;
 
281 int ad = P("ad")!=0;
282 int ae = P("ae")!=0;
283 int ai = P("ai")!=0;
284 int aj = P("aj")!=0;
285 int ak = P("ak")!=0;
@@ -297,10 +300,11 @@
297 int at = P("at")!=0;
298 int au = P("au")!=0;
299 int av = P("av")!=0;
300 int az = P("az")!=0;
301 if( aa ){ zCap[i++] = 'a'; }
 
302 if( ac ){ zCap[i++] = 'c'; }
303 if( ad ){ zCap[i++] = 'd'; }
304 if( ae ){ zCap[i++] = 'e'; }
305 if( af ){ zCap[i++] = 'f'; }
306 if( ah ){ zCap[i++] = 'h'; }
@@ -353,18 +357,19 @@
353 */
354 zLogin = "";
355 zInfo = "";
356 zCap = "";
357 zPw = "";
358 oaa = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
359 oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = "";
360 if( uid ){
361 zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
362 zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
363 zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
364 zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
365 if( strchr(zCap, 'a') ) oaa = " checked";
 
366 if( strchr(zCap, 'c') ) oac = " checked";
367 if( strchr(zCap, 'd') ) oad = " checked";
368 if( strchr(zCap, 'e') ) oae = " checked";
369 if( strchr(zCap, 'f') ) oaf = " checked";
370 if( strchr(zCap, 'g') ) oag = " checked";
@@ -467,15 +472,16 @@
467 @ <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br>
468 @ <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br>
469 @ <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br>
470 @ <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br>
471 @ <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br>
472 @ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Tkt<br>
473 @ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Tkt<br>
474 @ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Tkt<br>
475 @ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Tkt<br>
476 @ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Tkt Report<br>
 
477 @ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
478 @ </td>
479 @ </tr>
480 @ <tr>
481 @ <td align="right">Password:</td>
@@ -564,13 +570,13 @@
564 @ </li><p>
565 @
566 @ <li><p>
567 @ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and
568 @ <b>Write Wiki</b> privileges control access to wiki pages. The
569 @ <b>Read Tkt</b>, <b>New Tkt</b>, <b>Append Tkt</b>, and
570 @ <b>Write Tkt</b> privileges control access to trouble tickets.
571 @ The <b>Tkt Report</b> privilege allows the user to create or edit
572 @ ticket report formats.
573 @ </p></li>
574 @
575 @ <li><p>
576 @ Users with the <b>Password</b> privilege are allowed to change their
@@ -583,10 +589,15 @@
583 @ such as the email address of users and contact information on tickets.
584 @ Recommended OFF for "anonymous" and for "nobody" but ON for
585 @ "developer".
586 @ </p></li>
587 @
 
 
 
 
 
588 @ <li><p>
589 @ Login is prohibited if the password is an empty string.
590 @ </p></li>
591 @ </ul>
592 @
593
--- src/setup.c
+++ src/setup.c
@@ -147,10 +147,12 @@
147 @ <ol>
148 @ <li><p>The permission flags are as follows:</p>
149 @ <table>
150 @ <tr><td valign="top"><b>a</b></td>
151 @ <td><i>Admin:</i> Create and delete users</td></tr>
152 @ <tr><td valign="top"><b>b</b></td>
153 @ <td><i>Attach:</i> Add attachments to wiki or tickets</td></tr>
154 @ <tr><td valign="top"><b>c</b></td>
155 @ <td><i>Append-Tkt:</i> Append to tickets</td></tr>
156 @ <tr><td valign="top"><b>d</b></td>
157 @ <td><i>Delete:</i> Delete wiki and tickets</td></tr>
158 @ <tr><td valign="top"><b>e</b></td>
@@ -239,11 +241,11 @@
241 */
242 void user_edit(void){
243 const char *zId, *zLogin, *zInfo, *zCap, *zPw;
244 char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
245 char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
246 char *oat, *oau, *oav, *oab, *oaz;
247 const char *inherit[128];
248 int doWrite;
249 int uid;
250 int higherUser = 0; /* True if user being edited is SETUP and the */
251 /* user doing the editing is ADMIN. Disallow editing */
@@ -276,10 +278,11 @@
278 doWrite = cgi_all("login","info","pw") && !higherUser;
279 if( doWrite ){
280 char zCap[50];
281 int i = 0;
282 int aa = P("aa")!=0;
283 int ab = P("ab")!=0;
284 int ad = P("ad")!=0;
285 int ae = P("ae")!=0;
286 int ai = P("ai")!=0;
287 int aj = P("aj")!=0;
288 int ak = P("ak")!=0;
@@ -297,10 +300,11 @@
300 int at = P("at")!=0;
301 int au = P("au")!=0;
302 int av = P("av")!=0;
303 int az = P("az")!=0;
304 if( aa ){ zCap[i++] = 'a'; }
305 if( ab ){ zCap[i++] = 'b'; }
306 if( ac ){ zCap[i++] = 'c'; }
307 if( ad ){ zCap[i++] = 'd'; }
308 if( ae ){ zCap[i++] = 'e'; }
309 if( af ){ zCap[i++] = 'f'; }
310 if( ah ){ zCap[i++] = 'h'; }
@@ -353,18 +357,19 @@
357 */
358 zLogin = "";
359 zInfo = "";
360 zCap = "";
361 zPw = "";
362 oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
363 oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = "";
364 if( uid ){
365 zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
366 zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
367 zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
368 zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
369 if( strchr(zCap, 'a') ) oaa = " checked";
370 if( strchr(zCap, 'b') ) oab = " checked";
371 if( strchr(zCap, 'c') ) oac = " checked";
372 if( strchr(zCap, 'd') ) oad = " checked";
373 if( strchr(zCap, 'e') ) oae = " checked";
374 if( strchr(zCap, 'f') ) oaf = " checked";
375 if( strchr(zCap, 'g') ) oag = " checked";
@@ -467,15 +472,16 @@
472 @ <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br>
473 @ <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br>
474 @ <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br>
475 @ <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br>
476 @ <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br>
477 @ <input type="checkbox" name="ab"%s(oab)/>%s(B('b'))Attachments<br>
478 @ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Ticket<br>
479 @ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Ticket<br>
480 @ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Ticket<br>
481 @ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Ticket<br>
482 @ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Ticket Report<br>
483 @ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
484 @ </td>
485 @ </tr>
486 @ <tr>
487 @ <td align="right">Password:</td>
@@ -564,13 +570,13 @@
570 @ </li><p>
571 @
572 @ <li><p>
573 @ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and
574 @ <b>Write Wiki</b> privileges control access to wiki pages. The
575 @ <b>Read Ticket</b>, <b>New Ticket</b>, <b>Append Ticket</b>, and
576 @ <b>Write Ticket</b> privileges control access to trouble tickets.
577 @ The <b>Ticket Report</b> privilege allows the user to create or edit
578 @ ticket report formats.
579 @ </p></li>
580 @
581 @ <li><p>
582 @ Users with the <b>Password</b> privilege are allowed to change their
@@ -583,10 +589,15 @@
589 @ such as the email address of users and contact information on tickets.
590 @ Recommended OFF for "anonymous" and for "nobody" but ON for
591 @ "developer".
592 @ </p></li>
593 @
594 @ <li><p>
595 @ The <b>Attachment</b> privilege is needed in order to add attachments
596 @ to tickets or wiki. Write privilege on the ticket or wiki is also
597 @ required.</p></li>
598 @
599 @ <li><p>
600 @ Login is prohibited if the password is an empty string.
601 @ </p></li>
602 @ </ul>
603 @
604
+92 -24
--- src/tkt.c
+++ src/tkt.c
@@ -296,18 +296,20 @@
296296
**
297297
** View a ticket.
298298
*/
299299
void tktview_page(void){
300300
const char *zScript;
301
+ char *zFullName;
302
+ const char *zUuid = PD("name","");
303
+
301304
login_check_credentials();
302305
if( !g.okRdTkt ){ login_needed(); return; }
303306
if( g.okWrTkt || g.okApndTkt ){
304307
style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
305308
g.zTop, PD("name",""));
306309
}
307310
if( g.okHistory ){
308
- const char *zUuid = PD("name","");
309311
style_submenu_element("History", "History Of This Ticket",
310312
"%s/tkthistory/%T", g.zTop, zUuid);
311313
style_submenu_element("Timeline", "Timeline Of This Ticket",
312314
"%s/tkttimeline/%T", g.zTop, zUuid);
313315
style_submenu_element("Check-ins", "Check-ins Of This Ticket",
@@ -315,18 +317,58 @@
315317
}
316318
if( g.okNewTkt ){
317319
style_submenu_element("New Ticket", "Create a new ticket",
318320
"%s/tktnew", g.zTop);
319321
}
322
+ if( g.okApndTkt && g.okAttach ){
323
+ style_submenu_element("Attach", "Add An Attachment",
324
+ "%s/attachadd?tkt=%T&from=%s/tktview%%3fname=%t",
325
+ g.zTop, zUuid, g.zTop, zUuid);
326
+ }
320327
style_header("View Ticket");
321328
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
322329
ticket_init();
323330
initializeVariablesFromDb();
324331
zScript = ticket_viewpage_code();
325332
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1);
326333
Th_Render(zScript);
327334
if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
335
+
336
+ zFullName = db_text(0,
337
+ "SELECT tkt_uuid FROM ticket"
338
+ " WHERE tkt_uuid GLOB '%q*'", zUuid);
339
+ if( zFullName ){
340
+ int cnt = 0;
341
+ Stmt q;
342
+ db_prepare(&q,
343
+ "SELECT datetime(mtime,'localtime'), filename, user"
344
+ " FROM attachment"
345
+ " WHERE isLatest AND src!='' AND target=%Q"
346
+ " ORDER BY mtime DESC",
347
+ zFullName);
348
+ while( db_step(&q)==SQLITE_ROW ){
349
+ const char *zDate = db_column_text(&q, 0);
350
+ const char *zFile = db_column_text(&q, 1);
351
+ const char *zUser = db_column_text(&q, 2);
352
+ if( cnt==0 ){
353
+ @ <hr><h2>Attachments:</h2>
354
+ @ <ul>
355
+ }
356
+ cnt++;
357
+ @ <li><a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&file=%t(zFile)">
358
+ @ %h(zFile)</a> add by %h(zUser) on
359
+ hyperlink_to_date(zDate, ".");
360
+ if( g.okWrTkt && g.okAttach ){
361
+ @ [<a href="%s(g.zTop)/attachdelete?tkt=%s(zFullName)&file=%t(zFile)&from=%s(g.zTop)/tktview%%3fname=%s(zFullName)">delete</a>]
362
+ }
363
+ }
364
+ if( cnt ){
365
+ @ </ul>
366
+ }
367
+ db_finalize(&q);
368
+ }
369
+
328370
style_footer();
329371
}
330372
331373
/*
332374
** TH command: append_field FIELD STRING
@@ -641,15 +683,18 @@
641683
timeline_query_for_www(), zFullUuid, zFullUuid
642684
);
643685
}else{
644686
zSQL = mprintf(
645687
"%s AND event.objid IN "
646
- " (SELECT rid FROM tagxref WHERE tagid=%d UNION"
647
- " SELECT srcid FROM backlink WHERE target GLOB '%.4s*' "
648
- "AND '%s' GLOB (target||'*')) "
688
+ " (SELECT rid FROM tagxref WHERE tagid=%d"
689
+ " UNION SELECT srcid FROM backlink"
690
+ " WHERE target GLOB '%.4s*'"
691
+ " AND '%s' GLOB (target||'*')"
692
+ " UNION SELECT attachid FROM attachment"
693
+ " WHERE target=%Q) "
649694
"ORDER BY mtime DESC",
650
- timeline_query_for_www(), tagid, zFullUuid, zFullUuid
695
+ timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid
651696
);
652697
}
653698
db_prepare(&q, zSQL);
654699
free(zSQL);
655700
www_print_timeline(&q, TIMELINE_ARTID, 0);
@@ -687,37 +732,60 @@
687732
@ No such ticket: %h(zUuid)
688733
style_footer();
689734
return;
690735
}
691736
db_prepare(&q,
692
- "SELECT objid, uuid FROM event, blob"
737
+ "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
738
+ " FROM event, blob"
693739
" WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
694740
" AND blob.rid=event.objid"
695
- " ORDER BY mtime DESC",
696
- tagid
741
+ " UNION "
742
+ "SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user"
743
+ " FROM attachment, blob"
744
+ " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
745
+ " AND blob.rid=attachid"
746
+ " ORDER BY 1 DESC",
747
+ tagid, tagid
697748
);
698749
while( db_step(&q)==SQLITE_ROW ){
699750
Blob content;
700751
Manifest m;
701
- int rid = db_column_int(&q, 0);
702
- const char *zChngUuid = db_column_text(&q, 1);
703
- content_get(rid, &content);
704
- if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
705
- char *zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate);
706
- char zUuid[12];
707
- memcpy(zUuid, zChngUuid, 10);
708
- zUuid[10] = 0;
709
- @
710
- @ Ticket change
711
- @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zUuid)</a>]</a>
752
+ char zShort[12];
753
+ const char *zDate = db_column_text(&q, 0);
754
+ int rid = db_column_int(&q, 1);
755
+ const char *zChngUuid = db_column_text(&q, 2);
756
+ const char *zFile = db_column_text(&q, 4);
757
+ memcpy(zShort, zChngUuid, 10);
758
+ zShort[10] = 0;
759
+ if( zFile!=0 ){
760
+ const char *zSrc = db_column_text(&q, 3);
761
+ const char *zUser = db_column_text(&q, 5);
762
+ if( zSrc==0 || zSrc[0]==0 ){
763
+ @
764
+ @ <p>Delete attachment "%h(zFile)"
765
+ }else{
766
+ @
767
+ @ <p>Add attachment "%h(zFile)"
768
+ }
769
+ @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
712770
@ (rid %d(rid)) by
713
- hyperlink_to_user(m.zUser,zDate," on");
714
- hyperlink_to_date(zDate, ":");
715
- free(zDate);
716
- ticket_output_change_artifact(&m);
771
+ hyperlink_to_user(zUser,zDate," on");
772
+ hyperlink_to_date(zDate, ".</p>");
773
+ }else{
774
+ content_get(rid, &content);
775
+ if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
776
+ @
777
+ @ <p>Ticket change
778
+ @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
779
+ @ (rid %d(rid)) by
780
+ hyperlink_to_user(m.zUser,zDate," on");
781
+ hyperlink_to_date(zDate, ":");
782
+ ticket_output_change_artifact(&m);
783
+ @ </p>
784
+ }
785
+ manifest_clear(&m);
717786
}
718
- manifest_clear(&m);
719787
}
720788
db_finalize(&q);
721789
style_footer();
722790
}
723791
724792
--- src/tkt.c
+++ src/tkt.c
@@ -296,18 +296,20 @@
296 **
297 ** View a ticket.
298 */
299 void tktview_page(void){
300 const char *zScript;
 
 
 
301 login_check_credentials();
302 if( !g.okRdTkt ){ login_needed(); return; }
303 if( g.okWrTkt || g.okApndTkt ){
304 style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
305 g.zTop, PD("name",""));
306 }
307 if( g.okHistory ){
308 const char *zUuid = PD("name","");
309 style_submenu_element("History", "History Of This Ticket",
310 "%s/tkthistory/%T", g.zTop, zUuid);
311 style_submenu_element("Timeline", "Timeline Of This Ticket",
312 "%s/tkttimeline/%T", g.zTop, zUuid);
313 style_submenu_element("Check-ins", "Check-ins Of This Ticket",
@@ -315,18 +317,58 @@
315 }
316 if( g.okNewTkt ){
317 style_submenu_element("New Ticket", "Create a new ticket",
318 "%s/tktnew", g.zTop);
319 }
 
 
 
 
 
320 style_header("View Ticket");
321 if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
322 ticket_init();
323 initializeVariablesFromDb();
324 zScript = ticket_viewpage_code();
325 if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1);
326 Th_Render(zScript);
327 if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328 style_footer();
329 }
330
331 /*
332 ** TH command: append_field FIELD STRING
@@ -641,15 +683,18 @@
641 timeline_query_for_www(), zFullUuid, zFullUuid
642 );
643 }else{
644 zSQL = mprintf(
645 "%s AND event.objid IN "
646 " (SELECT rid FROM tagxref WHERE tagid=%d UNION"
647 " SELECT srcid FROM backlink WHERE target GLOB '%.4s*' "
648 "AND '%s' GLOB (target||'*')) "
 
 
 
649 "ORDER BY mtime DESC",
650 timeline_query_for_www(), tagid, zFullUuid, zFullUuid
651 );
652 }
653 db_prepare(&q, zSQL);
654 free(zSQL);
655 www_print_timeline(&q, TIMELINE_ARTID, 0);
@@ -687,37 +732,60 @@
687 @ No such ticket: %h(zUuid)
688 style_footer();
689 return;
690 }
691 db_prepare(&q,
692 "SELECT objid, uuid FROM event, blob"
 
693 " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
694 " AND blob.rid=event.objid"
695 " ORDER BY mtime DESC",
696 tagid
 
 
 
 
 
697 );
698 while( db_step(&q)==SQLITE_ROW ){
699 Blob content;
700 Manifest m;
701 int rid = db_column_int(&q, 0);
702 const char *zChngUuid = db_column_text(&q, 1);
703 content_get(rid, &content);
704 if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
705 char *zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate);
706 char zUuid[12];
707 memcpy(zUuid, zChngUuid, 10);
708 zUuid[10] = 0;
709 @
710 @ Ticket change
711 @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zUuid)</a>]</a>
 
 
 
 
 
 
 
712 @ (rid %d(rid)) by
713 hyperlink_to_user(m.zUser,zDate," on");
714 hyperlink_to_date(zDate, ":");
715 free(zDate);
716 ticket_output_change_artifact(&m);
 
 
 
 
 
 
 
 
 
 
 
717 }
718 manifest_clear(&m);
719 }
720 db_finalize(&q);
721 style_footer();
722 }
723
724
--- src/tkt.c
+++ src/tkt.c
@@ -296,18 +296,20 @@
296 **
297 ** View a ticket.
298 */
299 void tktview_page(void){
300 const char *zScript;
301 char *zFullName;
302 const char *zUuid = PD("name","");
303
304 login_check_credentials();
305 if( !g.okRdTkt ){ login_needed(); return; }
306 if( g.okWrTkt || g.okApndTkt ){
307 style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
308 g.zTop, PD("name",""));
309 }
310 if( g.okHistory ){
 
311 style_submenu_element("History", "History Of This Ticket",
312 "%s/tkthistory/%T", g.zTop, zUuid);
313 style_submenu_element("Timeline", "Timeline Of This Ticket",
314 "%s/tkttimeline/%T", g.zTop, zUuid);
315 style_submenu_element("Check-ins", "Check-ins Of This Ticket",
@@ -315,18 +317,58 @@
317 }
318 if( g.okNewTkt ){
319 style_submenu_element("New Ticket", "Create a new ticket",
320 "%s/tktnew", g.zTop);
321 }
322 if( g.okApndTkt && g.okAttach ){
323 style_submenu_element("Attach", "Add An Attachment",
324 "%s/attachadd?tkt=%T&from=%s/tktview%%3fname=%t",
325 g.zTop, zUuid, g.zTop, zUuid);
326 }
327 style_header("View Ticket");
328 if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
329 ticket_init();
330 initializeVariablesFromDb();
331 zScript = ticket_viewpage_code();
332 if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1);
333 Th_Render(zScript);
334 if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
335
336 zFullName = db_text(0,
337 "SELECT tkt_uuid FROM ticket"
338 " WHERE tkt_uuid GLOB '%q*'", zUuid);
339 if( zFullName ){
340 int cnt = 0;
341 Stmt q;
342 db_prepare(&q,
343 "SELECT datetime(mtime,'localtime'), filename, user"
344 " FROM attachment"
345 " WHERE isLatest AND src!='' AND target=%Q"
346 " ORDER BY mtime DESC",
347 zFullName);
348 while( db_step(&q)==SQLITE_ROW ){
349 const char *zDate = db_column_text(&q, 0);
350 const char *zFile = db_column_text(&q, 1);
351 const char *zUser = db_column_text(&q, 2);
352 if( cnt==0 ){
353 @ <hr><h2>Attachments:</h2>
354 @ <ul>
355 }
356 cnt++;
357 @ <li><a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&file=%t(zFile)">
358 @ %h(zFile)</a> add by %h(zUser) on
359 hyperlink_to_date(zDate, ".");
360 if( g.okWrTkt && g.okAttach ){
361 @ [<a href="%s(g.zTop)/attachdelete?tkt=%s(zFullName)&file=%t(zFile)&from=%s(g.zTop)/tktview%%3fname=%s(zFullName)">delete</a>]
362 }
363 }
364 if( cnt ){
365 @ </ul>
366 }
367 db_finalize(&q);
368 }
369
370 style_footer();
371 }
372
373 /*
374 ** TH command: append_field FIELD STRING
@@ -641,15 +683,18 @@
683 timeline_query_for_www(), zFullUuid, zFullUuid
684 );
685 }else{
686 zSQL = mprintf(
687 "%s AND event.objid IN "
688 " (SELECT rid FROM tagxref WHERE tagid=%d"
689 " UNION SELECT srcid FROM backlink"
690 " WHERE target GLOB '%.4s*'"
691 " AND '%s' GLOB (target||'*')"
692 " UNION SELECT attachid FROM attachment"
693 " WHERE target=%Q) "
694 "ORDER BY mtime DESC",
695 timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid
696 );
697 }
698 db_prepare(&q, zSQL);
699 free(zSQL);
700 www_print_timeline(&q, TIMELINE_ARTID, 0);
@@ -687,37 +732,60 @@
732 @ No such ticket: %h(zUuid)
733 style_footer();
734 return;
735 }
736 db_prepare(&q,
737 "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
738 " FROM event, blob"
739 " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
740 " AND blob.rid=event.objid"
741 " UNION "
742 "SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user"
743 " FROM attachment, blob"
744 " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
745 " AND blob.rid=attachid"
746 " ORDER BY 1 DESC",
747 tagid, tagid
748 );
749 while( db_step(&q)==SQLITE_ROW ){
750 Blob content;
751 Manifest m;
752 char zShort[12];
753 const char *zDate = db_column_text(&q, 0);
754 int rid = db_column_int(&q, 1);
755 const char *zChngUuid = db_column_text(&q, 2);
756 const char *zFile = db_column_text(&q, 4);
757 memcpy(zShort, zChngUuid, 10);
758 zShort[10] = 0;
759 if( zFile!=0 ){
760 const char *zSrc = db_column_text(&q, 3);
761 const char *zUser = db_column_text(&q, 5);
762 if( zSrc==0 || zSrc[0]==0 ){
763 @
764 @ <p>Delete attachment "%h(zFile)"
765 }else{
766 @
767 @ <p>Add attachment "%h(zFile)"
768 }
769 @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
770 @ (rid %d(rid)) by
771 hyperlink_to_user(zUser,zDate," on");
772 hyperlink_to_date(zDate, ".</p>");
773 }else{
774 content_get(rid, &content);
775 if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
776 @
777 @ <p>Ticket change
778 @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
779 @ (rid %d(rid)) by
780 hyperlink_to_user(m.zUser,zDate," on");
781 hyperlink_to_date(zDate, ":");
782 ticket_output_change_artifact(&m);
783 @ </p>
784 }
785 manifest_clear(&m);
786 }
 
787 }
788 db_finalize(&q);
789 style_footer();
790 }
791
792
+63 -6
--- src/wiki.c
+++ src/wiki.c
@@ -127,10 +127,12 @@
127127
int isSandbox;
128128
Blob wiki;
129129
Manifest m;
130130
const char *zPageName;
131131
char *zBody = mprintf("%s","<i>Empty Page</i>");
132
+ Stmt q;
133
+ int cnt = 0;
132134
133135
login_check_credentials();
134136
if( !g.okRdWiki ){ login_needed(); return; }
135137
zPageName = P("name");
136138
if( zPageName==0 ){
@@ -152,11 +154,12 @@
152154
@ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
153155
}
154156
@ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
155157
@ available on this server.</li>
156158
@ <li> <form method="GET" action="%s(g.zBaseURL)/wfind">
157
- @ Search wiki titles: <input type="text" name="title"/> &nbsp; <input type="submit" />
159
+ @ Search wiki titles: <input type="text" name="title"/>
160
+ @ &nbsp; <input type="submit" />
158161
@ </li>
159162
@ </ul>
160163
style_footer();
161164
return;
162165
}
@@ -186,10 +189,15 @@
186189
if( !g.isHome ){
187190
if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
188191
style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
189192
g.zTop, zPageName);
190193
}
194
+ if( rid && g.okWrWiki && g.okAttach ){
195
+ style_submenu_element("Attach", "Add An Attachment",
196
+ "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T",
197
+ g.zTop, zPageName, g.zTop, zPageName);
198
+ }
191199
if( rid && g.okApndWiki ){
192200
style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
193201
g.zTop, zPageName);
194202
}
195203
if( g.okHistory ){
@@ -199,10 +207,38 @@
199207
}
200208
style_header(zPageName);
201209
blob_init(&wiki, zBody, -1);
202210
wiki_convert(&wiki, 0, 0);
203211
blob_reset(&wiki);
212
+
213
+ db_prepare(&q,
214
+ "SELECT datetime(mtime,'localtime'), filename, user"
215
+ " FROM attachment"
216
+ " WHERE isLatest AND src!='' AND target=%Q"
217
+ " ORDER BY mtime DESC",
218
+ zPageName);
219
+ while( db_step(&q)==SQLITE_ROW ){
220
+ const char *zDate = db_column_text(&q, 0);
221
+ const char *zFile = db_column_text(&q, 1);
222
+ const char *zUser = db_column_text(&q, 2);
223
+ if( cnt==0 ){
224
+ @ <hr><h2>Attachments:</h2>
225
+ @ <ul>
226
+ }
227
+ cnt++;
228
+ @ <li><a href="%s(g.zTop)/attachview?page=%s(zPageName)&file=%t(zFile)">
229
+ @ %h(zFile)</a> add by %h(zUser) on
230
+ hyperlink_to_date(zDate, ".");
231
+ if( g.okWrWiki && g.okAttach ){
232
+ @ [<a href="%s(g.zTop)/attachdelete?page=%s(zPageName)&file=%t(zFile)&from=%s(g.zTop)/wiki%%3fname=%s(zPageName)">delete</a>]
233
+ }
234
+ }
235
+ if( cnt ){
236
+ @ </ul>
237
+ }
238
+ db_finalize(&q);
239
+
204240
if( !isSandbox ){
205241
manifest_clear(&m);
206242
}
207243
style_footer();
208244
}
@@ -515,11 +551,13 @@
515551
/*
516552
** Function called to output extra text at the end of each line in
517553
** a wiki history listing.
518554
*/
519555
static void wiki_history_extra(int rid){
520
- @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
556
+ if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d", rid) ){
557
+ @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
558
+ }
521559
}
522560
523561
/*
524562
** WEBPAGE: whistory
525563
** URL: /whistory?name=PAGENAME
@@ -538,13 +576,15 @@
538576
style_header(zTitle);
539577
free(zTitle);
540578
541579
zSQL = mprintf("%s AND event.objid IN "
542580
" (SELECT rid FROM tagxref WHERE tagid="
543
- "(SELECT tagid FROM tag WHERE tagname='wiki-%q'))"
581
+ "(SELECT tagid FROM tag WHERE tagname='wiki-%q')"
582
+ " UNION SELECT attachid FROM attachment"
583
+ " WHERE target=%Q)"
544584
"ORDER BY mtime DESC",
545
- timeline_query_for_www(), zPageName);
585
+ timeline_query_for_www(), zPageName, zPageName);
546586
db_prepare(&q, zSQL);
547587
free(zSQL);
548588
zWikiPageName = zPageName;
549589
www_print_timeline(&q, TIMELINE_ARTID, wiki_history_extra);
550590
db_finalize(&q);
@@ -604,26 +644,43 @@
604644
style_footer();
605645
}
606646
607647
/*
608648
** WEBPAGE: wcontent
649
+**
650
+** all=1 Show deleted pages
609651
**
610652
** List all available wiki pages with date created and last modified.
611653
*/
612654
void wcontent_page(void){
613655
Stmt q;
656
+ int showAll = P("all")!=0;
657
+
614658
login_check_credentials();
615659
if( !g.okRdWiki ){ login_needed(); return; }
616660
style_header("Available Wiki Pages");
661
+ if( showAll ){
662
+ style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
663
+ }else{
664
+ style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
665
+ }
617666
@ <ul>
618667
db_prepare(&q,
619
- "SELECT substr(tagname, 6, 1000) FROM tag WHERE tagname GLOB 'wiki-*'"
668
+ "SELECT"
669
+ " substr(tagname, 6),"
670
+ " (SELECT value FROM tagxref WHERE tagid=tag.tagid ORDER BY mtime DESC)"
671
+ " FROM tag WHERE tagname GLOB 'wiki-*'"
620672
" ORDER BY lower(tagname)"
621673
);
622674
while( db_step(&q)==SQLITE_ROW ){
623675
const char *zName = db_column_text(&q, 0);
624
- @ <li><a href="%s(g.zBaseURL)/wiki?name=%T(zName)">%h(zName)</a></li>
676
+ int size = db_column_int(&q, 1);
677
+ if( size>0 ){
678
+ @ <li><a href="%s(g.zTop)/wiki?name=%T(zName)">%h(zName)</a></li>
679
+ }else if( showAll ){
680
+ @ <li><a href="%s(g.zTop)/wiki?name=%T(zName)"><s>%h(zName)</s></a></li>
681
+ }
625682
}
626683
db_finalize(&q);
627684
@ </ul>
628685
style_footer();
629686
}
630687
--- src/wiki.c
+++ src/wiki.c
@@ -127,10 +127,12 @@
127 int isSandbox;
128 Blob wiki;
129 Manifest m;
130 const char *zPageName;
131 char *zBody = mprintf("%s","<i>Empty Page</i>");
 
 
132
133 login_check_credentials();
134 if( !g.okRdWiki ){ login_needed(); return; }
135 zPageName = P("name");
136 if( zPageName==0 ){
@@ -152,11 +154,12 @@
152 @ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
153 }
154 @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
155 @ available on this server.</li>
156 @ <li> <form method="GET" action="%s(g.zBaseURL)/wfind">
157 @ Search wiki titles: <input type="text" name="title"/> &nbsp; <input type="submit" />
 
158 @ </li>
159 @ </ul>
160 style_footer();
161 return;
162 }
@@ -186,10 +189,15 @@
186 if( !g.isHome ){
187 if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
188 style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
189 g.zTop, zPageName);
190 }
 
 
 
 
 
191 if( rid && g.okApndWiki ){
192 style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
193 g.zTop, zPageName);
194 }
195 if( g.okHistory ){
@@ -199,10 +207,38 @@
199 }
200 style_header(zPageName);
201 blob_init(&wiki, zBody, -1);
202 wiki_convert(&wiki, 0, 0);
203 blob_reset(&wiki);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204 if( !isSandbox ){
205 manifest_clear(&m);
206 }
207 style_footer();
208 }
@@ -515,11 +551,13 @@
515 /*
516 ** Function called to output extra text at the end of each line in
517 ** a wiki history listing.
518 */
519 static void wiki_history_extra(int rid){
520 @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
 
 
521 }
522
523 /*
524 ** WEBPAGE: whistory
525 ** URL: /whistory?name=PAGENAME
@@ -538,13 +576,15 @@
538 style_header(zTitle);
539 free(zTitle);
540
541 zSQL = mprintf("%s AND event.objid IN "
542 " (SELECT rid FROM tagxref WHERE tagid="
543 "(SELECT tagid FROM tag WHERE tagname='wiki-%q'))"
 
 
544 "ORDER BY mtime DESC",
545 timeline_query_for_www(), zPageName);
546 db_prepare(&q, zSQL);
547 free(zSQL);
548 zWikiPageName = zPageName;
549 www_print_timeline(&q, TIMELINE_ARTID, wiki_history_extra);
550 db_finalize(&q);
@@ -604,26 +644,43 @@
604 style_footer();
605 }
606
607 /*
608 ** WEBPAGE: wcontent
 
 
609 **
610 ** List all available wiki pages with date created and last modified.
611 */
612 void wcontent_page(void){
613 Stmt q;
 
 
614 login_check_credentials();
615 if( !g.okRdWiki ){ login_needed(); return; }
616 style_header("Available Wiki Pages");
 
 
 
 
 
617 @ <ul>
618 db_prepare(&q,
619 "SELECT substr(tagname, 6, 1000) FROM tag WHERE tagname GLOB 'wiki-*'"
 
 
 
620 " ORDER BY lower(tagname)"
621 );
622 while( db_step(&q)==SQLITE_ROW ){
623 const char *zName = db_column_text(&q, 0);
624 @ <li><a href="%s(g.zBaseURL)/wiki?name=%T(zName)">%h(zName)</a></li>
 
 
 
 
 
625 }
626 db_finalize(&q);
627 @ </ul>
628 style_footer();
629 }
630
--- src/wiki.c
+++ src/wiki.c
@@ -127,10 +127,12 @@
127 int isSandbox;
128 Blob wiki;
129 Manifest m;
130 const char *zPageName;
131 char *zBody = mprintf("%s","<i>Empty Page</i>");
132 Stmt q;
133 int cnt = 0;
134
135 login_check_credentials();
136 if( !g.okRdWiki ){ login_needed(); return; }
137 zPageName = P("name");
138 if( zPageName==0 ){
@@ -152,11 +154,12 @@
154 @ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
155 }
156 @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
157 @ available on this server.</li>
158 @ <li> <form method="GET" action="%s(g.zBaseURL)/wfind">
159 @ Search wiki titles: <input type="text" name="title"/>
160 @ &nbsp; <input type="submit" />
161 @ </li>
162 @ </ul>
163 style_footer();
164 return;
165 }
@@ -186,10 +189,15 @@
189 if( !g.isHome ){
190 if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
191 style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
192 g.zTop, zPageName);
193 }
194 if( rid && g.okWrWiki && g.okAttach ){
195 style_submenu_element("Attach", "Add An Attachment",
196 "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T",
197 g.zTop, zPageName, g.zTop, zPageName);
198 }
199 if( rid && g.okApndWiki ){
200 style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
201 g.zTop, zPageName);
202 }
203 if( g.okHistory ){
@@ -199,10 +207,38 @@
207 }
208 style_header(zPageName);
209 blob_init(&wiki, zBody, -1);
210 wiki_convert(&wiki, 0, 0);
211 blob_reset(&wiki);
212
213 db_prepare(&q,
214 "SELECT datetime(mtime,'localtime'), filename, user"
215 " FROM attachment"
216 " WHERE isLatest AND src!='' AND target=%Q"
217 " ORDER BY mtime DESC",
218 zPageName);
219 while( db_step(&q)==SQLITE_ROW ){
220 const char *zDate = db_column_text(&q, 0);
221 const char *zFile = db_column_text(&q, 1);
222 const char *zUser = db_column_text(&q, 2);
223 if( cnt==0 ){
224 @ <hr><h2>Attachments:</h2>
225 @ <ul>
226 }
227 cnt++;
228 @ <li><a href="%s(g.zTop)/attachview?page=%s(zPageName)&file=%t(zFile)">
229 @ %h(zFile)</a> add by %h(zUser) on
230 hyperlink_to_date(zDate, ".");
231 if( g.okWrWiki && g.okAttach ){
232 @ [<a href="%s(g.zTop)/attachdelete?page=%s(zPageName)&file=%t(zFile)&from=%s(g.zTop)/wiki%%3fname=%s(zPageName)">delete</a>]
233 }
234 }
235 if( cnt ){
236 @ </ul>
237 }
238 db_finalize(&q);
239
240 if( !isSandbox ){
241 manifest_clear(&m);
242 }
243 style_footer();
244 }
@@ -515,11 +551,13 @@
551 /*
552 ** Function called to output extra text at the end of each line in
553 ** a wiki history listing.
554 */
555 static void wiki_history_extra(int rid){
556 if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d", rid) ){
557 @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
558 }
559 }
560
561 /*
562 ** WEBPAGE: whistory
563 ** URL: /whistory?name=PAGENAME
@@ -538,13 +576,15 @@
576 style_header(zTitle);
577 free(zTitle);
578
579 zSQL = mprintf("%s AND event.objid IN "
580 " (SELECT rid FROM tagxref WHERE tagid="
581 "(SELECT tagid FROM tag WHERE tagname='wiki-%q')"
582 " UNION SELECT attachid FROM attachment"
583 " WHERE target=%Q)"
584 "ORDER BY mtime DESC",
585 timeline_query_for_www(), zPageName, zPageName);
586 db_prepare(&q, zSQL);
587 free(zSQL);
588 zWikiPageName = zPageName;
589 www_print_timeline(&q, TIMELINE_ARTID, wiki_history_extra);
590 db_finalize(&q);
@@ -604,26 +644,43 @@
644 style_footer();
645 }
646
647 /*
648 ** WEBPAGE: wcontent
649 **
650 ** all=1 Show deleted pages
651 **
652 ** List all available wiki pages with date created and last modified.
653 */
654 void wcontent_page(void){
655 Stmt q;
656 int showAll = P("all")!=0;
657
658 login_check_credentials();
659 if( !g.okRdWiki ){ login_needed(); return; }
660 style_header("Available Wiki Pages");
661 if( showAll ){
662 style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
663 }else{
664 style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
665 }
666 @ <ul>
667 db_prepare(&q,
668 "SELECT"
669 " substr(tagname, 6),"
670 " (SELECT value FROM tagxref WHERE tagid=tag.tagid ORDER BY mtime DESC)"
671 " FROM tag WHERE tagname GLOB 'wiki-*'"
672 " ORDER BY lower(tagname)"
673 );
674 while( db_step(&q)==SQLITE_ROW ){
675 const char *zName = db_column_text(&q, 0);
676 int size = db_column_int(&q, 1);
677 if( size>0 ){
678 @ <li><a href="%s(g.zTop)/wiki?name=%T(zName)">%h(zName)</a></li>
679 }else if( showAll ){
680 @ <li><a href="%s(g.zTop)/wiki?name=%T(zName)"><s>%h(zName)</s></a></li>
681 }
682 }
683 db_finalize(&q);
684 @ </ul>
685 style_footer();
686 }
687

Keyboard Shortcuts

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