Fossil SCM
Merge recent experimental changes (the attachment enhancement and the ability to delete wiki) into the trunk.
Commit
f4a25366a76d89dce3dc191246bbea3babb53505
Parent
6953184210e92dc…
11 files changed
+17
+42
+4
-2
+1
+12
-2
+1
+56
-10
+6
-3
+21
-10
+92
-24
+63
-6
+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 @@ | ||
| 732 | 732 | } |
| 733 | 733 | cnt++; |
| 734 | 734 | } |
| 735 | 735 | db_finalize(&q); |
| 736 | 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); | |
| 737 | 779 | if( cnt==0 ){ |
| 738 | 780 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 739 | 781 | @ Control artifact. |
| 740 | 782 | if( pDownloadName && blob_size(pDownloadName)==0 ){ |
| 741 | 783 | blob_append(pDownloadName, zUuid, -1); |
| 742 | 784 |
| --- 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 @@ | ||
| 476 | 476 | case 's': g.okSetup = 1; /* Fall thru into Admin */ |
| 477 | 477 | case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okZip = |
| 478 | 478 | g.okRdWiki = g.okWrWiki = g.okNewWiki = |
| 479 | 479 | g.okApndWiki = g.okHistory = g.okClone = |
| 480 | 480 | 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 */ | |
| 482 | 483 | case 'i': g.okRead = g.okWrite = 1; break; |
| 483 | 484 | case 'o': g.okRead = 1; break; |
| 484 | 485 | case 'z': g.okZip = 1; break; |
| 485 | 486 | |
| 486 | 487 | case 'd': g.okDelete = 1; break; |
| @@ -498,10 +499,11 @@ | ||
| 498 | 499 | case 'n': g.okNewTkt = 1; break; |
| 499 | 500 | case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt = |
| 500 | 501 | g.okApndTkt = 1; break; |
| 501 | 502 | case 'c': g.okApndTkt = 1; break; |
| 502 | 503 | case 't': g.okTktFmt = 1; break; |
| 504 | + case 'b': g.okAttach = 1; break; | |
| 503 | 505 | |
| 504 | 506 | /* The "u" privileges is a little different. It recursively |
| 505 | 507 | ** inherits all privileges of the user named "reader" */ |
| 506 | 508 | case 'u': { |
| 507 | 509 | if( zUser==0 ){ |
| @@ -534,11 +536,11 @@ | ||
| 534 | 536 | int rc = 1; |
| 535 | 537 | if( nCap<0 ) nCap = strlen(zCap); |
| 536 | 538 | for(i=0; i<nCap && rc && zCap[i]; i++){ |
| 537 | 539 | switch( zCap[i] ){ |
| 538 | 540 | case 'a': rc = g.okAdmin; break; |
| 539 | - /* case 'b': */ | |
| 541 | + case 'b': rc = g.okAttach; break; | |
| 540 | 542 | case 'c': rc = g.okApndTkt; break; |
| 541 | 543 | case 'd': rc = g.okDelete; break; |
| 542 | 544 | case 'e': rc = g.okRdAddr; break; |
| 543 | 545 | case 'f': rc = g.okNewWiki; break; |
| 544 | 546 | case 'g': rc = g.okClone; break; |
| 545 | 547 |
| --- 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 @@ | ||
| 130 | 130 | int okWrWiki; /* k: edit wiki via web */ |
| 131 | 131 | int okRdTkt; /* r: view tickets via web */ |
| 132 | 132 | int okNewTkt; /* n: create new tickets */ |
| 133 | 133 | int okApndTkt; /* c: append to tickets via the web */ |
| 134 | 134 | int okWrTkt; /* w: make changes to tickets via web */ |
| 135 | + int okAttach; /* b: add attachments */ | |
| 135 | 136 | int okTktFmt; /* t: create new ticket report formats */ |
| 136 | 137 | int okRdAddr; /* e: read email addresses or other private data */ |
| 137 | 138 | int okZip; /* z: download zipped artifact via /zip URL */ |
| 138 | 139 | |
| 139 | 140 | /* For defense against Cross-site Request Forgery attacks */ |
| 140 | 141 |
| --- 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 @@ | ||
| 13 | 13 | |
| 14 | 14 | |
| 15 | 15 | SRC = \ |
| 16 | 16 | $(SRCDIR)/add.c \ |
| 17 | 17 | $(SRCDIR)/allrepo.c \ |
| 18 | + $(SRCDIR)/attach.c \ | |
| 18 | 19 | $(SRCDIR)/bag.c \ |
| 19 | 20 | $(SRCDIR)/blob.c \ |
| 20 | 21 | $(SRCDIR)/branch.c \ |
| 21 | 22 | $(SRCDIR)/browse.c \ |
| 22 | 23 | $(SRCDIR)/captcha.c \ |
| @@ -86,10 +87,11 @@ | ||
| 86 | 87 | $(SRCDIR)/zip.c |
| 87 | 88 | |
| 88 | 89 | TRANS_SRC = \ |
| 89 | 90 | add_.c \ |
| 90 | 91 | allrepo_.c \ |
| 92 | + attach_.c \ | |
| 91 | 93 | bag_.c \ |
| 92 | 94 | blob_.c \ |
| 93 | 95 | branch_.c \ |
| 94 | 96 | browse_.c \ |
| 95 | 97 | captcha_.c \ |
| @@ -159,10 +161,11 @@ | ||
| 159 | 161 | zip_.c |
| 160 | 162 | |
| 161 | 163 | OBJ = \ |
| 162 | 164 | $(OBJDIR)/add.o \ |
| 163 | 165 | $(OBJDIR)/allrepo.o \ |
| 166 | + $(OBJDIR)/attach.o \ | |
| 164 | 167 | $(OBJDIR)/bag.o \ |
| 165 | 168 | $(OBJDIR)/blob.o \ |
| 166 | 169 | $(OBJDIR)/branch.o \ |
| 167 | 170 | $(OBJDIR)/browse.o \ |
| 168 | 171 | $(OBJDIR)/captcha.o \ |
| @@ -273,16 +276,16 @@ | ||
| 273 | 276 | # noop |
| 274 | 277 | |
| 275 | 278 | clean: |
| 276 | 279 | rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h |
| 277 | 280 | 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 | |
| 279 | 282 | |
| 280 | 283 | page_index.h: $(TRANS_SRC) mkindex |
| 281 | 284 | ./mkindex $(TRANS_SRC) >$@ |
| 282 | 285 | 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 | |
| 284 | 287 | touch headers |
| 285 | 288 | headers: Makefile |
| 286 | 289 | Makefile: |
| 287 | 290 | add_.c: $(SRCDIR)/add.c translate |
| 288 | 291 | ./translate $(SRCDIR)/add.c >add_.c |
| @@ -296,10 +299,17 @@ | ||
| 296 | 299 | |
| 297 | 300 | $(OBJDIR)/allrepo.o: allrepo_.c allrepo.h $(SRCDIR)/config.h |
| 298 | 301 | $(XTCC) -o $(OBJDIR)/allrepo.o -c allrepo_.c |
| 299 | 302 | |
| 300 | 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 | |
| 301 | 311 | bag_.c: $(SRCDIR)/bag.c translate |
| 302 | 312 | ./translate $(SRCDIR)/bag.c >bag_.c |
| 303 | 313 | |
| 304 | 314 | $(OBJDIR)/bag.o: bag_.c bag.h $(SRCDIR)/config.h |
| 305 | 315 | $(XTCC) -o $(OBJDIR)/bag.o -c bag_.c |
| 306 | 316 |
| --- 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 |
+1
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -7,10 +7,11 @@ | ||
| 7 | 7 | # "translate" and "makeheaders" |
| 8 | 8 | # |
| 9 | 9 | set src { |
| 10 | 10 | add |
| 11 | 11 | allrepo |
| 12 | + attach | |
| 12 | 13 | bag |
| 13 | 14 | blob |
| 14 | 15 | branch |
| 15 | 16 | browse |
| 16 | 17 | captcha |
| 17 | 18 |
| --- 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 @@ | ||
| 196 | 196 | } |
| 197 | 197 | if( blob_size(&a3)>0 |
| 198 | 198 | && (blob_size(&a3)!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){ |
| 199 | 199 | goto manifest_syntax_error; |
| 200 | 200 | } |
| 201 | - p->zAttachName = zName; | |
| 201 | + p->zAttachName = (char*)file_tail(zName); | |
| 202 | 202 | p->zAttachSrc = zSrc; |
| 203 | 203 | p->zAttachTarget = zTarget; |
| 204 | 204 | break; |
| 205 | 205 | } |
| 206 | 206 | |
| @@ -1080,11 +1080,16 @@ | ||
| 1080 | 1080 | if( m.type==CFTYPE_WIKI ){ |
| 1081 | 1081 | char *zTag = mprintf("wiki-%s", m.zWikiTitle); |
| 1082 | 1082 | int tagid = tag_findid(zTag, 1); |
| 1083 | 1083 | int prior; |
| 1084 | 1084 | 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); | |
| 1086 | 1091 | free(zTag); |
| 1087 | 1092 | prior = db_int(0, |
| 1088 | 1093 | "SELECT rid FROM tagxref" |
| 1089 | 1094 | " WHERE tagid=%d AND mtime<%.17g" |
| 1090 | 1095 | " ORDER BY mtime DESC", |
| @@ -1091,11 +1096,15 @@ | ||
| 1091 | 1096 | tagid, m.rDate |
| 1092 | 1097 | ); |
| 1093 | 1098 | if( prior ){ |
| 1094 | 1099 | content_deltify(prior, rid, 0); |
| 1095 | 1100 | } |
| 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 | + } | |
| 1097 | 1106 | db_multi_exec( |
| 1098 | 1107 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 1099 | 1108 | " bgcolor,euser,ecomment)" |
| 1100 | 1109 | "VALUES('w',%.17g,%d,%Q,%Q," |
| 1101 | 1110 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," |
| @@ -1119,20 +1128,57 @@ | ||
| 1119 | 1128 | db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", |
| 1120 | 1129 | m.zTicketUuid); |
| 1121 | 1130 | } |
| 1122 | 1131 | if( m.type==CFTYPE_ATTACHMENT ){ |
| 1123 | 1132 | 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 | |
| 1127 | 1138 | ); |
| 1128 | 1139 | 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 | |
| 1133 | 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 | + } | |
| 1134 | 1180 | } |
| 1135 | 1181 | db_end_transaction(0); |
| 1136 | 1182 | manifest_clear(&m); |
| 1137 | 1183 | return 1; |
| 1138 | 1184 | } |
| 1139 | 1185 |
| --- 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 @@ | ||
| 327 | 327 | @ |
| 328 | 328 | @ -- Each attachment is an entry in the following table. Only |
| 329 | 329 | @ -- the most recent attachment (identified by the D card) is saved. |
| 330 | 330 | @ -- |
| 331 | 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 | |
| 332 | 334 | @ mtime TIMESTAMP, -- Time when attachment last changed |
| 333 | 335 | @ 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 | |
| 335 | 337 | @ filename TEXT, -- Filename for the attachment |
| 336 | 338 | @ 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 | |
| 339 | 340 | @ ); |
| 341 | +@ CREATE INDEX attachment_idx1 ON attachment(target, filename, mtime); | |
| 342 | +@ CREATE INDEX attachment_idx2 ON attachment(src); | |
| 340 | 343 | @ |
| 341 | 344 | @ -- Template for the TICKET table |
| 342 | 345 | @ -- |
| 343 | 346 | @ -- NB: when changing the schema of the TICKET table here, also make the |
| 344 | 347 | @ -- same change in tktsetup.c. |
| 345 | 348 |
| --- 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 @@ | ||
| 147 | 147 | @ <ol> |
| 148 | 148 | @ <li><p>The permission flags are as follows:</p> |
| 149 | 149 | @ <table> |
| 150 | 150 | @ <tr><td valign="top"><b>a</b></td> |
| 151 | 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> | |
| 152 | 154 | @ <tr><td valign="top"><b>c</b></td> |
| 153 | 155 | @ <td><i>Append-Tkt:</i> Append to tickets</td></tr> |
| 154 | 156 | @ <tr><td valign="top"><b>d</b></td> |
| 155 | 157 | @ <td><i>Delete:</i> Delete wiki and tickets</td></tr> |
| 156 | 158 | @ <tr><td valign="top"><b>e</b></td> |
| @@ -239,11 +241,11 @@ | ||
| 239 | 241 | */ |
| 240 | 242 | void user_edit(void){ |
| 241 | 243 | const char *zId, *zLogin, *zInfo, *zCap, *zPw; |
| 242 | 244 | char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap; |
| 243 | 245 | char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae; |
| 244 | - char *oat, *oau, *oav, *oaz; | |
| 246 | + char *oat, *oau, *oav, *oab, *oaz; | |
| 245 | 247 | const char *inherit[128]; |
| 246 | 248 | int doWrite; |
| 247 | 249 | int uid; |
| 248 | 250 | int higherUser = 0; /* True if user being edited is SETUP and the */ |
| 249 | 251 | /* user doing the editing is ADMIN. Disallow editing */ |
| @@ -276,10 +278,11 @@ | ||
| 276 | 278 | doWrite = cgi_all("login","info","pw") && !higherUser; |
| 277 | 279 | if( doWrite ){ |
| 278 | 280 | char zCap[50]; |
| 279 | 281 | int i = 0; |
| 280 | 282 | int aa = P("aa")!=0; |
| 283 | + int ab = P("ab")!=0; | |
| 281 | 284 | int ad = P("ad")!=0; |
| 282 | 285 | int ae = P("ae")!=0; |
| 283 | 286 | int ai = P("ai")!=0; |
| 284 | 287 | int aj = P("aj")!=0; |
| 285 | 288 | int ak = P("ak")!=0; |
| @@ -297,10 +300,11 @@ | ||
| 297 | 300 | int at = P("at")!=0; |
| 298 | 301 | int au = P("au")!=0; |
| 299 | 302 | int av = P("av")!=0; |
| 300 | 303 | int az = P("az")!=0; |
| 301 | 304 | if( aa ){ zCap[i++] = 'a'; } |
| 305 | + if( ab ){ zCap[i++] = 'b'; } | |
| 302 | 306 | if( ac ){ zCap[i++] = 'c'; } |
| 303 | 307 | if( ad ){ zCap[i++] = 'd'; } |
| 304 | 308 | if( ae ){ zCap[i++] = 'e'; } |
| 305 | 309 | if( af ){ zCap[i++] = 'f'; } |
| 306 | 310 | if( ah ){ zCap[i++] = 'h'; } |
| @@ -353,18 +357,19 @@ | ||
| 353 | 357 | */ |
| 354 | 358 | zLogin = ""; |
| 355 | 359 | zInfo = ""; |
| 356 | 360 | zCap = ""; |
| 357 | 361 | 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 = | |
| 359 | 363 | oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = ""; |
| 360 | 364 | if( uid ){ |
| 361 | 365 | zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); |
| 362 | 366 | zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); |
| 363 | 367 | zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid); |
| 364 | 368 | zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid); |
| 365 | 369 | if( strchr(zCap, 'a') ) oaa = " checked"; |
| 370 | + if( strchr(zCap, 'b') ) oab = " checked"; | |
| 366 | 371 | if( strchr(zCap, 'c') ) oac = " checked"; |
| 367 | 372 | if( strchr(zCap, 'd') ) oad = " checked"; |
| 368 | 373 | if( strchr(zCap, 'e') ) oae = " checked"; |
| 369 | 374 | if( strchr(zCap, 'f') ) oaf = " checked"; |
| 370 | 375 | if( strchr(zCap, 'g') ) oag = " checked"; |
| @@ -467,15 +472,16 @@ | ||
| 467 | 472 | @ <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br> |
| 468 | 473 | @ <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br> |
| 469 | 474 | @ <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br> |
| 470 | 475 | @ <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br> |
| 471 | 476 | @ <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> | |
| 477 | 483 | @ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip |
| 478 | 484 | @ </td> |
| 479 | 485 | @ </tr> |
| 480 | 486 | @ <tr> |
| 481 | 487 | @ <td align="right">Password:</td> |
| @@ -564,13 +570,13 @@ | ||
| 564 | 570 | @ </li><p> |
| 565 | 571 | @ |
| 566 | 572 | @ <li><p> |
| 567 | 573 | @ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and |
| 568 | 574 | @ <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 | |
| 572 | 578 | @ ticket report formats. |
| 573 | 579 | @ </p></li> |
| 574 | 580 | @ |
| 575 | 581 | @ <li><p> |
| 576 | 582 | @ Users with the <b>Password</b> privilege are allowed to change their |
| @@ -583,10 +589,15 @@ | ||
| 583 | 589 | @ such as the email address of users and contact information on tickets. |
| 584 | 590 | @ Recommended OFF for "anonymous" and for "nobody" but ON for |
| 585 | 591 | @ "developer". |
| 586 | 592 | @ </p></li> |
| 587 | 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 | + @ | |
| 588 | 599 | @ <li><p> |
| 589 | 600 | @ Login is prohibited if the password is an empty string. |
| 590 | 601 | @ </p></li> |
| 591 | 602 | @ </ul> |
| 592 | 603 | @ |
| 593 | 604 |
| --- 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 @@ | ||
| 296 | 296 | ** |
| 297 | 297 | ** View a ticket. |
| 298 | 298 | */ |
| 299 | 299 | void tktview_page(void){ |
| 300 | 300 | const char *zScript; |
| 301 | + char *zFullName; | |
| 302 | + const char *zUuid = PD("name",""); | |
| 303 | + | |
| 301 | 304 | login_check_credentials(); |
| 302 | 305 | if( !g.okRdTkt ){ login_needed(); return; } |
| 303 | 306 | if( g.okWrTkt || g.okApndTkt ){ |
| 304 | 307 | style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T", |
| 305 | 308 | g.zTop, PD("name","")); |
| 306 | 309 | } |
| 307 | 310 | if( g.okHistory ){ |
| 308 | - const char *zUuid = PD("name",""); | |
| 309 | 311 | style_submenu_element("History", "History Of This Ticket", |
| 310 | 312 | "%s/tkthistory/%T", g.zTop, zUuid); |
| 311 | 313 | style_submenu_element("Timeline", "Timeline Of This Ticket", |
| 312 | 314 | "%s/tkttimeline/%T", g.zTop, zUuid); |
| 313 | 315 | style_submenu_element("Check-ins", "Check-ins Of This Ticket", |
| @@ -315,18 +317,58 @@ | ||
| 315 | 317 | } |
| 316 | 318 | if( g.okNewTkt ){ |
| 317 | 319 | style_submenu_element("New Ticket", "Create a new ticket", |
| 318 | 320 | "%s/tktnew", g.zTop); |
| 319 | 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 | + } | |
| 320 | 327 | style_header("View Ticket"); |
| 321 | 328 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1); |
| 322 | 329 | ticket_init(); |
| 323 | 330 | initializeVariablesFromDb(); |
| 324 | 331 | zScript = ticket_viewpage_code(); |
| 325 | 332 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1); |
| 326 | 333 | Th_Render(zScript); |
| 327 | 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 | + | |
| 328 | 370 | style_footer(); |
| 329 | 371 | } |
| 330 | 372 | |
| 331 | 373 | /* |
| 332 | 374 | ** TH command: append_field FIELD STRING |
| @@ -641,15 +683,18 @@ | ||
| 641 | 683 | timeline_query_for_www(), zFullUuid, zFullUuid |
| 642 | 684 | ); |
| 643 | 685 | }else{ |
| 644 | 686 | zSQL = mprintf( |
| 645 | 687 | "%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) " | |
| 649 | 694 | "ORDER BY mtime DESC", |
| 650 | - timeline_query_for_www(), tagid, zFullUuid, zFullUuid | |
| 695 | + timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid | |
| 651 | 696 | ); |
| 652 | 697 | } |
| 653 | 698 | db_prepare(&q, zSQL); |
| 654 | 699 | free(zSQL); |
| 655 | 700 | www_print_timeline(&q, TIMELINE_ARTID, 0); |
| @@ -687,37 +732,60 @@ | ||
| 687 | 732 | @ No such ticket: %h(zUuid) |
| 688 | 733 | style_footer(); |
| 689 | 734 | return; |
| 690 | 735 | } |
| 691 | 736 | db_prepare(&q, |
| 692 | - "SELECT objid, uuid FROM event, blob" | |
| 737 | + "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL" | |
| 738 | + " FROM event, blob" | |
| 693 | 739 | " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)" |
| 694 | 740 | " 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 | |
| 697 | 748 | ); |
| 698 | 749 | while( db_step(&q)==SQLITE_ROW ){ |
| 699 | 750 | Blob content; |
| 700 | 751 | 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>] | |
| 712 | 770 | @ (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); | |
| 717 | 786 | } |
| 718 | - manifest_clear(&m); | |
| 719 | 787 | } |
| 720 | 788 | db_finalize(&q); |
| 721 | 789 | style_footer(); |
| 722 | 790 | } |
| 723 | 791 | |
| 724 | 792 |
| --- 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 @@ | ||
| 127 | 127 | int isSandbox; |
| 128 | 128 | Blob wiki; |
| 129 | 129 | Manifest m; |
| 130 | 130 | const char *zPageName; |
| 131 | 131 | char *zBody = mprintf("%s","<i>Empty Page</i>"); |
| 132 | + Stmt q; | |
| 133 | + int cnt = 0; | |
| 132 | 134 | |
| 133 | 135 | login_check_credentials(); |
| 134 | 136 | if( !g.okRdWiki ){ login_needed(); return; } |
| 135 | 137 | zPageName = P("name"); |
| 136 | 138 | if( zPageName==0 ){ |
| @@ -152,11 +154,12 @@ | ||
| 152 | 154 | @ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li> |
| 153 | 155 | } |
| 154 | 156 | @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a> |
| 155 | 157 | @ available on this server.</li> |
| 156 | 158 | @ <li> <form method="GET" action="%s(g.zBaseURL)/wfind"> |
| 157 | - @ Search wiki titles: <input type="text" name="title"/> <input type="submit" /> | |
| 159 | + @ Search wiki titles: <input type="text" name="title"/> | |
| 160 | + @ <input type="submit" /> | |
| 158 | 161 | @ </li> |
| 159 | 162 | @ </ul> |
| 160 | 163 | style_footer(); |
| 161 | 164 | return; |
| 162 | 165 | } |
| @@ -186,10 +189,15 @@ | ||
| 186 | 189 | if( !g.isHome ){ |
| 187 | 190 | if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){ |
| 188 | 191 | style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T", |
| 189 | 192 | g.zTop, zPageName); |
| 190 | 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 | + } | |
| 191 | 199 | if( rid && g.okApndWiki ){ |
| 192 | 200 | style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T", |
| 193 | 201 | g.zTop, zPageName); |
| 194 | 202 | } |
| 195 | 203 | if( g.okHistory ){ |
| @@ -199,10 +207,38 @@ | ||
| 199 | 207 | } |
| 200 | 208 | style_header(zPageName); |
| 201 | 209 | blob_init(&wiki, zBody, -1); |
| 202 | 210 | wiki_convert(&wiki, 0, 0); |
| 203 | 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 | + | |
| 204 | 240 | if( !isSandbox ){ |
| 205 | 241 | manifest_clear(&m); |
| 206 | 242 | } |
| 207 | 243 | style_footer(); |
| 208 | 244 | } |
| @@ -515,11 +551,13 @@ | ||
| 515 | 551 | /* |
| 516 | 552 | ** Function called to output extra text at the end of each line in |
| 517 | 553 | ** a wiki history listing. |
| 518 | 554 | */ |
| 519 | 555 | 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 | + } | |
| 521 | 559 | } |
| 522 | 560 | |
| 523 | 561 | /* |
| 524 | 562 | ** WEBPAGE: whistory |
| 525 | 563 | ** URL: /whistory?name=PAGENAME |
| @@ -538,13 +576,15 @@ | ||
| 538 | 576 | style_header(zTitle); |
| 539 | 577 | free(zTitle); |
| 540 | 578 | |
| 541 | 579 | zSQL = mprintf("%s AND event.objid IN " |
| 542 | 580 | " (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)" | |
| 544 | 584 | "ORDER BY mtime DESC", |
| 545 | - timeline_query_for_www(), zPageName); | |
| 585 | + timeline_query_for_www(), zPageName, zPageName); | |
| 546 | 586 | db_prepare(&q, zSQL); |
| 547 | 587 | free(zSQL); |
| 548 | 588 | zWikiPageName = zPageName; |
| 549 | 589 | www_print_timeline(&q, TIMELINE_ARTID, wiki_history_extra); |
| 550 | 590 | db_finalize(&q); |
| @@ -604,26 +644,43 @@ | ||
| 604 | 644 | style_footer(); |
| 605 | 645 | } |
| 606 | 646 | |
| 607 | 647 | /* |
| 608 | 648 | ** WEBPAGE: wcontent |
| 649 | +** | |
| 650 | +** all=1 Show deleted pages | |
| 609 | 651 | ** |
| 610 | 652 | ** List all available wiki pages with date created and last modified. |
| 611 | 653 | */ |
| 612 | 654 | void wcontent_page(void){ |
| 613 | 655 | Stmt q; |
| 656 | + int showAll = P("all")!=0; | |
| 657 | + | |
| 614 | 658 | login_check_credentials(); |
| 615 | 659 | if( !g.okRdWiki ){ login_needed(); return; } |
| 616 | 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 | + } | |
| 617 | 666 | @ <ul> |
| 618 | 667 | 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-*'" | |
| 620 | 672 | " ORDER BY lower(tagname)" |
| 621 | 673 | ); |
| 622 | 674 | while( db_step(&q)==SQLITE_ROW ){ |
| 623 | 675 | 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 | + } | |
| 625 | 682 | } |
| 626 | 683 | db_finalize(&q); |
| 627 | 684 | @ </ul> |
| 628 | 685 | style_footer(); |
| 629 | 686 | } |
| 630 | 687 |
| --- 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"/> <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 | @ <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 |