Fossil SCM

[c541b6e734] Add attachments to technotes

dave.vines 2015-12-31 11:31 technoteattach
Commit 67288e935a01213ce4fd11496b763e2eb99f089c
3 files changed +86 -22 +12 -1 +83 -3
+86 -22
--- src/attach.c
+++ src/attach.c
@@ -26,28 +26,36 @@
2626
** List attachments.
2727
**
2828
** tkt=TICKETUUID
2929
** page=WIKIPAGE
3030
**
31
-** Either one of tkt= or page= are supplied or neither but not both.
32
-** If neither are given, all attachments are listed. If one is given,
33
-** only attachments for the designated ticket or wiki page are shown.
34
-** TICKETUUID must be complete
31
+** At most one of technote=, tkt= or page= are supplied.
32
+** If none is given, all attachments are listed. If one is given,
33
+** only attachments for the designated technote, ticket or wiki page
34
+** are shown. TECHNOTEUUID and TICKETUUID may be just a prefix of the
35
+** relevant tech note or ticket, in which case all attachments of all
36
+** tech notes or tickets with the prefix will be listed.
3537
*/
3638
void attachlist_page(void){
3739
const char *zPage = P("page");
3840
const char *zTkt = P("tkt");
41
+ const char *zTechNote = P("technote");
3942
Blob sql;
4043
Stmt q;
4144
4245
if( zPage && zTkt ) zTkt = 0;
4346
login_check_credentials();
4447
blob_zero(&sql);
4548
blob_append_sql(&sql,
4649
"SELECT datetime(mtime,toLocal()), src, target, filename,"
4750
" comment, user,"
48
- " (SELECT uuid FROM blob WHERE rid=attachid), attachid"
51
+ " (SELECT uuid FROM blob WHERE rid=attachid), attachid,"
52
+ " (CASE WHEN 'tkt-'||target IN (SELECT tagname FROM tag)"
53
+ " THEN 1"
54
+ " WHEN 'event-'||target IN (SELECT tagname FROM tag)"
55
+ " THEN 2"
56
+ " ELSE 0 END)"
4957
" FROM attachment"
5058
);
5159
if( zPage ){
5260
if( g.perm.RdWiki==0 ){ login_needed(g.anon.RdWiki); return; }
5361
style_header("Attachments To %h", zPage);
@@ -54,10 +62,15 @@
5462
blob_append_sql(&sql, " WHERE target=%Q", zPage);
5563
}else if( zTkt ){
5664
if( g.perm.RdTkt==0 ){ login_needed(g.anon.RdTkt); return; }
5765
style_header("Attachments To Ticket %S", zTkt);
5866
blob_append_sql(&sql, " WHERE target GLOB '%q*'", zTkt);
67
+ }else if( zTechNote ){
68
+ if( g.perm.RdWiki==0 ){ login_needed(g.anon.RdWiki); return; }
69
+ style_header("Attachments to Tech Note %S", zTechNote);
70
+ blob_append_sql(&sql, " WHERE target GLOB '%q*'",
71
+ zTechNote);
5972
}else{
6073
if( g.perm.RdTkt==0 && g.perm.RdWiki==0 ){
6174
login_needed(g.anon.RdTkt || g.anon.RdWiki);
6275
return;
6376
}
@@ -73,21 +86,25 @@
7386
const char *zFilename = db_column_text(&q, 3);
7487
const char *zComment = db_column_text(&q, 4);
7588
const char *zUser = db_column_text(&q, 5);
7689
const char *zUuid = db_column_text(&q, 6);
7790
int attachid = db_column_int(&q, 7);
91
+ // type 0 is a wiki page, 1 is a ticket, 2 is a tech note
92
+ int type = db_column_int(&q, 8);
7893
const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
7994
int i;
8095
char *zUrlTail;
8196
for(i=0; zFilename[i]; i++){
8297
if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
8398
zFilename = &zFilename[i+1];
8499
i = -1;
85100
}
86101
}
87
- if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
102
+ if( type==1 ){
88103
zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename);
104
+ }else if( type==2 ){
105
+ zUrlTail = mprintf("technote=%s&file=%t", zTarget, zFilename);
89106
}else{
90107
zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
91108
}
92109
@ <li><p>
93110
@ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a>
@@ -98,19 +115,22 @@
98115
@ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br />
99116
if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++;
100117
if( zComment && zComment[0] ){
101118
@ %!W(zComment)<br />
102119
}
103
- if( zPage==0 && zTkt==0 ){
120
+ if( zPage==0 && zTkt==0 && zTechNote==0 ){
104121
if( zSrc==0 || zSrc[0]==0 ){
105122
zSrc = "Deleted from";
106123
}else {
107124
zSrc = "Added to";
108125
}
109
- if( strlen(zTarget)==UUID_SIZE && validate16(zTarget, UUID_SIZE) ){
126
+ if( type==1 ){
110127
@ %s(zSrc) ticket <a href="%R/tktview?name=%s(zTarget)">
111128
@ %S(zTarget)</a>
129
+ }else if( type==2 ){
130
+ @ %s(zSrc) tech note <a href="%R/technote/%s(zTarget)">
131
+ @ %S(zTarget)</a>
112132
}else{
113133
@ %s(zSrc) wiki page <a href="%R/wiki?name=%t(zTarget)">
114134
@ %h(zTarget)</a>
115135
}
116136
}else{
@@ -138,31 +158,35 @@
138158
** Download or display an attachment.
139159
** Query parameters:
140160
**
141161
** tkt=TICKETUUID
142162
** page=WIKIPAGE
163
+** technote=TECHNOTEUUID
143164
** file=FILENAME
144165
** attachid=ID
145166
**
146167
*/
147168
void attachview_page(void){
148169
const char *zPage = P("page");
149170
const char *zTkt = P("tkt");
171
+ const char *zTechNote = P("technote");
150172
const char *zFile = P("file");
151173
const char *zTarget = 0;
152174
int attachid = atoi(PD("attachid","0"));
153175
char *zUUID;
154176
155
- if( zPage && zTkt ) zTkt = 0;
156177
if( zFile==0 ) fossil_redirect_home();
157178
login_check_credentials();
158179
if( zPage ){
159180
if( g.perm.RdWiki==0 ){ login_needed(g.anon.RdWiki); return; }
160181
zTarget = zPage;
161182
}else if( zTkt ){
162183
if( g.perm.RdTkt==0 ){ login_needed(g.anon.RdTkt); return; }
163184
zTarget = zTkt;
185
+ }else if( zTechNote ){
186
+ if( g.perm.RdWiki==0 ){ login_needed(g.anon.RdWiki); return; }
187
+ zTarget = zTechNote;
164188
}else{
165189
fossil_redirect_home();
166190
}
167191
if( attachid>0 ){
168192
zUUID = db_text(0,
@@ -186,18 +210,19 @@
186210
}else if( zUUID[0]=='x' ){
187211
style_header("Missing");
188212
@ Attachment has been deleted
189213
style_footer();
190214
return;
191
- }
192
- g.perm.Read = 1;
193
- cgi_replace_parameter("name",zUUID);
194
- if( fossil_strcmp(g.zPath,"attachview")==0 ){
195
- artifact_page();
196215
}else{
197
- cgi_replace_parameter("m", mimetype_from_name(zFile));
198
- rawartifact_page();
216
+ g.perm.Read = 1;
217
+ cgi_replace_parameter("name",zUUID);
218
+ if( fossil_strcmp(g.zPath,"attachview")==0 ){
219
+ artifact_page();
220
+ }else{
221
+ cgi_replace_parameter("m", mimetype_from_name(zFile));
222
+ rawartifact_page();
223
+ }
199224
}
200225
}
201226
202227
/*
203228
** Save an attachment control artifact into the repository
@@ -228,27 +253,34 @@
228253
** WEBPAGE: attachadd
229254
** Add a new attachment.
230255
**
231256
** tkt=TICKETUUID
232257
** page=WIKIPAGE
258
+** technote=TECHNOTEUUID
233259
** from=URL
234260
**
235261
*/
236262
void attachadd_page(void){
237263
const char *zPage = P("page");
238264
const char *zTkt = P("tkt");
265
+ const char *zTechNote = P("technote");
239266
const char *zFrom = P("from");
240267
const char *aContent = P("f");
241268
const char *zName = PD("f:filename","unknown");
242269
const char *zTarget;
243
- const char *zTargetType;
270
+ char *zTargetType;
244271
int szContent = atoi(PD("f:bytes","0"));
245272
int goodCaptcha = 1;
246273
247274
if( P("cancel") ) cgi_redirect(zFrom);
248
- if( zPage && zTkt ) fossil_redirect_home();
249
- if( zPage==0 && zTkt==0 ) fossil_redirect_home();
275
+ if( (zPage && zTkt)
276
+ || (zPage && zTechNote)
277
+ || (zTkt && zTechNote)
278
+ ){
279
+ fossil_redirect_home();
280
+ }
281
+ if( zPage==0 && zTkt==0 && zTechNote==0) fossil_redirect_home();
250282
login_check_credentials();
251283
if( zPage ){
252284
if( g.perm.ApndWiki==0 || g.perm.Attach==0 ){
253285
login_needed(g.anon.ApndWiki && g.anon.Attach);
254286
return;
@@ -257,10 +289,24 @@
257289
fossil_redirect_home();
258290
}
259291
zTarget = zPage;
260292
zTargetType = mprintf("Wiki Page <a href=\"%R/wiki?name=%h\">%h</a>",
261293
zPage, zPage);
294
+ }else if ( zTechNote ){
295
+ if( g.perm.Write==0 || g.perm.ApndWiki==0 || g.perm.Attach==0 ){
296
+ login_needed(g.anon.Write && g.anon.ApndWiki && g.anon.Attach);
297
+ return;
298
+ }
299
+ if( !db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'", zTechNote) ){
300
+ zTechNote = db_text(0, "SELECT substr(tagname,7) FROM tag"
301
+ " WHERE tagname GLOB 'event-%q*'", zTechNote);
302
+ if( zTechNote==0) fossil_redirect_home();
303
+ }
304
+ zTarget = zTechNote;
305
+ zTargetType = mprintf("Tech Note <a href=\"%R/technote/%h\">%h</a>",
306
+ zTechNote, zTechNote);
307
+
262308
}else{
263309
if( g.perm.ApndTkt==0 || g.perm.Attach==0 ){
264310
login_needed(g.anon.ApndTkt && g.anon.Attach);
265311
return;
266312
}
@@ -340,10 +386,12 @@
340386
@ <input type="file" name="f" size="60" /><br />
341387
@ Description:<br />
342388
@ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br />
343389
if( zTkt ){
344390
@ <input type="hidden" name="tkt" value="%h(zTkt)" />
391
+ }else if( zTechNote ){
392
+ @ <input type="hidden" name="technote" value="%h(zTechNote)" />
345393
}else{
346394
@ <input type="hidden" name="page" value="%h(zPage)" />
347395
}
348396
@ <input type="hidden" name="from" value="%h(zFrom)" />
349397
@ <input type="submit" name="ok" value="Add Attachment" />
@@ -350,10 +398,11 @@
350398
@ <input type="submit" name="cancel" value="Cancel" />
351399
@ </div>
352400
captcha_generate(0);
353401
@ </form>
354402
style_footer();
403
+ fossil_free(zTargetType);
355404
}
356405
357406
/*
358407
** WEBPAGE: ainfo
359408
** URL: /ainfo?name=ARTIFACTID
@@ -364,15 +413,16 @@
364413
int rid; /* RID for the control artifact */
365414
int ridSrc; /* RID for the attached file */
366415
char *zDate; /* Date attached */
367416
const char *zUuid; /* UUID of the control artifact */
368417
Manifest *pAttach; /* Parse of the control artifact */
369
- const char *zTarget; /* Wiki or ticket attached to */
418
+ const char *zTarget; /* Wiki, ticket or tech note attached to */
370419
const char *zSrc; /* UUID of the attached file */
371420
const char *zName; /* Name of the attached file */
372421
const char *zDesc; /* Description of the attached file */
373422
const char *zWikiName = 0; /* Wiki page name when attached to Wiki */
423
+ const char *zTNUuid = 0; /* Tech Note ID when attached to tech note */
374424
const char *zTktUuid = 0; /* Ticket ID when attached to a ticket */
375425
int modPending; /* True if awaiting moderation */
376426
const char *zModAction; /* Moderation action or NULL */
377427
int isModerator; /* TRUE if user is the moderator */
378428
const char *zMime; /* MIME Type */
@@ -422,15 +472,23 @@
422472
zWikiName = zTarget;
423473
if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
424474
if( g.perm.WrWiki ){
425475
style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid);
426476
}
477
+ }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'",zTarget) ){
478
+ zTNUuid = zTarget;
479
+ if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
480
+ if( g.perm.Write && g.perm.WrWiki ){
481
+ style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid);
482
+ }
427483
}
428484
zDate = db_text(0, "SELECT datetime(%.12f)", pAttach->rDate);
429485
430486
if( P("confirm")
431
- && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
487
+ && ((zTktUuid && g.perm.WrTkt) ||
488
+ (zWikiName && g.perm.WrWiki) ||
489
+ (zTNUuid && g.perm.Write && g.perm.WrWiki))
432490
){
433491
int i, n, rid;
434492
char *zDate;
435493
Blob manifest;
436494
Blob cksum;
@@ -454,11 +512,13 @@
454512
db_end_transaction(0);
455513
@ <p>The attachment below has been deleted.</p>
456514
}
457515
458516
if( P("del")
459
- && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
517
+ && ((zTktUuid && g.perm.WrTkt) ||
518
+ (zWikiName && g.perm.WrWiki) ||
519
+ (zTNUuid && g.perm.Write && g.perm.WrWiki))
460520
){
461521
form_begin(0, "%R/ainfo/%!S", zUuid);
462522
@ <p>Confirm you want to delete the attachment shown below.
463523
@ <input type="submit" name="confirm" value="Confirm">
464524
@ </form>
@@ -502,10 +562,14 @@
502562
}
503563
if( zTktUuid ){
504564
@ <tr><th>Ticket:</th>
505565
@ <td>%z(href("%R/tktview/%s",zTktUuid))%s(zTktUuid)</a></td></tr>
506566
}
567
+ if( zTNUuid ){
568
+ @ <tr><th>Tech Note:</th>
569
+ @ <td>%z(href("%R/technote/%s",zTNUuid))%s(zTNUuid)</a></td></tr>
570
+ }
507571
if( zWikiName ){
508572
@ <tr><th>Wiki&nbsp;Page:</th>
509573
@ <td>%z(href("%R/wiki?name=%t",zWikiName))%h(zWikiName)</a></td></tr>
510574
}
511575
@ <tr><th>Date:</th><td>
512576
--- src/attach.c
+++ src/attach.c
@@ -26,28 +26,36 @@
26 ** List attachments.
27 **
28 ** tkt=TICKETUUID
29 ** page=WIKIPAGE
30 **
31 ** Either one of tkt= or page= are supplied or neither but not both.
32 ** If neither are given, all attachments are listed. If one is given,
33 ** only attachments for the designated ticket or wiki page are shown.
34 ** TICKETUUID must be complete
 
 
35 */
36 void attachlist_page(void){
37 const char *zPage = P("page");
38 const char *zTkt = P("tkt");
 
39 Blob sql;
40 Stmt q;
41
42 if( zPage && zTkt ) zTkt = 0;
43 login_check_credentials();
44 blob_zero(&sql);
45 blob_append_sql(&sql,
46 "SELECT datetime(mtime,toLocal()), src, target, filename,"
47 " comment, user,"
48 " (SELECT uuid FROM blob WHERE rid=attachid), attachid"
 
 
 
 
 
49 " FROM attachment"
50 );
51 if( zPage ){
52 if( g.perm.RdWiki==0 ){ login_needed(g.anon.RdWiki); return; }
53 style_header("Attachments To %h", zPage);
@@ -54,10 +62,15 @@
54 blob_append_sql(&sql, " WHERE target=%Q", zPage);
55 }else if( zTkt ){
56 if( g.perm.RdTkt==0 ){ login_needed(g.anon.RdTkt); return; }
57 style_header("Attachments To Ticket %S", zTkt);
58 blob_append_sql(&sql, " WHERE target GLOB '%q*'", zTkt);
 
 
 
 
 
59 }else{
60 if( g.perm.RdTkt==0 && g.perm.RdWiki==0 ){
61 login_needed(g.anon.RdTkt || g.anon.RdWiki);
62 return;
63 }
@@ -73,21 +86,25 @@
73 const char *zFilename = db_column_text(&q, 3);
74 const char *zComment = db_column_text(&q, 4);
75 const char *zUser = db_column_text(&q, 5);
76 const char *zUuid = db_column_text(&q, 6);
77 int attachid = db_column_int(&q, 7);
 
 
78 const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
79 int i;
80 char *zUrlTail;
81 for(i=0; zFilename[i]; i++){
82 if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
83 zFilename = &zFilename[i+1];
84 i = -1;
85 }
86 }
87 if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
88 zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename);
 
 
89 }else{
90 zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
91 }
92 @ <li><p>
93 @ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a>
@@ -98,19 +115,22 @@
98 @ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br />
99 if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++;
100 if( zComment && zComment[0] ){
101 @ %!W(zComment)<br />
102 }
103 if( zPage==0 && zTkt==0 ){
104 if( zSrc==0 || zSrc[0]==0 ){
105 zSrc = "Deleted from";
106 }else {
107 zSrc = "Added to";
108 }
109 if( strlen(zTarget)==UUID_SIZE && validate16(zTarget, UUID_SIZE) ){
110 @ %s(zSrc) ticket <a href="%R/tktview?name=%s(zTarget)">
111 @ %S(zTarget)</a>
 
 
 
112 }else{
113 @ %s(zSrc) wiki page <a href="%R/wiki?name=%t(zTarget)">
114 @ %h(zTarget)</a>
115 }
116 }else{
@@ -138,31 +158,35 @@
138 ** Download or display an attachment.
139 ** Query parameters:
140 **
141 ** tkt=TICKETUUID
142 ** page=WIKIPAGE
 
143 ** file=FILENAME
144 ** attachid=ID
145 **
146 */
147 void attachview_page(void){
148 const char *zPage = P("page");
149 const char *zTkt = P("tkt");
 
150 const char *zFile = P("file");
151 const char *zTarget = 0;
152 int attachid = atoi(PD("attachid","0"));
153 char *zUUID;
154
155 if( zPage && zTkt ) zTkt = 0;
156 if( zFile==0 ) fossil_redirect_home();
157 login_check_credentials();
158 if( zPage ){
159 if( g.perm.RdWiki==0 ){ login_needed(g.anon.RdWiki); return; }
160 zTarget = zPage;
161 }else if( zTkt ){
162 if( g.perm.RdTkt==0 ){ login_needed(g.anon.RdTkt); return; }
163 zTarget = zTkt;
 
 
 
164 }else{
165 fossil_redirect_home();
166 }
167 if( attachid>0 ){
168 zUUID = db_text(0,
@@ -186,18 +210,19 @@
186 }else if( zUUID[0]=='x' ){
187 style_header("Missing");
188 @ Attachment has been deleted
189 style_footer();
190 return;
191 }
192 g.perm.Read = 1;
193 cgi_replace_parameter("name",zUUID);
194 if( fossil_strcmp(g.zPath,"attachview")==0 ){
195 artifact_page();
196 }else{
197 cgi_replace_parameter("m", mimetype_from_name(zFile));
198 rawartifact_page();
 
 
 
 
 
 
199 }
200 }
201
202 /*
203 ** Save an attachment control artifact into the repository
@@ -228,27 +253,34 @@
228 ** WEBPAGE: attachadd
229 ** Add a new attachment.
230 **
231 ** tkt=TICKETUUID
232 ** page=WIKIPAGE
 
233 ** from=URL
234 **
235 */
236 void attachadd_page(void){
237 const char *zPage = P("page");
238 const char *zTkt = P("tkt");
 
239 const char *zFrom = P("from");
240 const char *aContent = P("f");
241 const char *zName = PD("f:filename","unknown");
242 const char *zTarget;
243 const char *zTargetType;
244 int szContent = atoi(PD("f:bytes","0"));
245 int goodCaptcha = 1;
246
247 if( P("cancel") ) cgi_redirect(zFrom);
248 if( zPage && zTkt ) fossil_redirect_home();
249 if( zPage==0 && zTkt==0 ) fossil_redirect_home();
 
 
 
 
 
250 login_check_credentials();
251 if( zPage ){
252 if( g.perm.ApndWiki==0 || g.perm.Attach==0 ){
253 login_needed(g.anon.ApndWiki && g.anon.Attach);
254 return;
@@ -257,10 +289,24 @@
257 fossil_redirect_home();
258 }
259 zTarget = zPage;
260 zTargetType = mprintf("Wiki Page <a href=\"%R/wiki?name=%h\">%h</a>",
261 zPage, zPage);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262 }else{
263 if( g.perm.ApndTkt==0 || g.perm.Attach==0 ){
264 login_needed(g.anon.ApndTkt && g.anon.Attach);
265 return;
266 }
@@ -340,10 +386,12 @@
340 @ <input type="file" name="f" size="60" /><br />
341 @ Description:<br />
342 @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br />
343 if( zTkt ){
344 @ <input type="hidden" name="tkt" value="%h(zTkt)" />
 
 
345 }else{
346 @ <input type="hidden" name="page" value="%h(zPage)" />
347 }
348 @ <input type="hidden" name="from" value="%h(zFrom)" />
349 @ <input type="submit" name="ok" value="Add Attachment" />
@@ -350,10 +398,11 @@
350 @ <input type="submit" name="cancel" value="Cancel" />
351 @ </div>
352 captcha_generate(0);
353 @ </form>
354 style_footer();
 
355 }
356
357 /*
358 ** WEBPAGE: ainfo
359 ** URL: /ainfo?name=ARTIFACTID
@@ -364,15 +413,16 @@
364 int rid; /* RID for the control artifact */
365 int ridSrc; /* RID for the attached file */
366 char *zDate; /* Date attached */
367 const char *zUuid; /* UUID of the control artifact */
368 Manifest *pAttach; /* Parse of the control artifact */
369 const char *zTarget; /* Wiki or ticket attached to */
370 const char *zSrc; /* UUID of the attached file */
371 const char *zName; /* Name of the attached file */
372 const char *zDesc; /* Description of the attached file */
373 const char *zWikiName = 0; /* Wiki page name when attached to Wiki */
 
374 const char *zTktUuid = 0; /* Ticket ID when attached to a ticket */
375 int modPending; /* True if awaiting moderation */
376 const char *zModAction; /* Moderation action or NULL */
377 int isModerator; /* TRUE if user is the moderator */
378 const char *zMime; /* MIME Type */
@@ -422,15 +472,23 @@
422 zWikiName = zTarget;
423 if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
424 if( g.perm.WrWiki ){
425 style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid);
426 }
 
 
 
 
 
 
427 }
428 zDate = db_text(0, "SELECT datetime(%.12f)", pAttach->rDate);
429
430 if( P("confirm")
431 && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
 
 
432 ){
433 int i, n, rid;
434 char *zDate;
435 Blob manifest;
436 Blob cksum;
@@ -454,11 +512,13 @@
454 db_end_transaction(0);
455 @ <p>The attachment below has been deleted.</p>
456 }
457
458 if( P("del")
459 && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
 
 
460 ){
461 form_begin(0, "%R/ainfo/%!S", zUuid);
462 @ <p>Confirm you want to delete the attachment shown below.
463 @ <input type="submit" name="confirm" value="Confirm">
464 @ </form>
@@ -502,10 +562,14 @@
502 }
503 if( zTktUuid ){
504 @ <tr><th>Ticket:</th>
505 @ <td>%z(href("%R/tktview/%s",zTktUuid))%s(zTktUuid)</a></td></tr>
506 }
 
 
 
 
507 if( zWikiName ){
508 @ <tr><th>Wiki&nbsp;Page:</th>
509 @ <td>%z(href("%R/wiki?name=%t",zWikiName))%h(zWikiName)</a></td></tr>
510 }
511 @ <tr><th>Date:</th><td>
512
--- src/attach.c
+++ src/attach.c
@@ -26,28 +26,36 @@
26 ** List attachments.
27 **
28 ** tkt=TICKETUUID
29 ** page=WIKIPAGE
30 **
31 ** At most one of technote=, tkt= or page= are supplied.
32 ** If none is given, all attachments are listed. If one is given,
33 ** only attachments for the designated technote, ticket or wiki page
34 ** are shown. TECHNOTEUUID and TICKETUUID may be just a prefix of the
35 ** relevant tech note or ticket, in which case all attachments of all
36 ** tech notes or tickets with the prefix will be listed.
37 */
38 void attachlist_page(void){
39 const char *zPage = P("page");
40 const char *zTkt = P("tkt");
41 const char *zTechNote = P("technote");
42 Blob sql;
43 Stmt q;
44
45 if( zPage && zTkt ) zTkt = 0;
46 login_check_credentials();
47 blob_zero(&sql);
48 blob_append_sql(&sql,
49 "SELECT datetime(mtime,toLocal()), src, target, filename,"
50 " comment, user,"
51 " (SELECT uuid FROM blob WHERE rid=attachid), attachid,"
52 " (CASE WHEN 'tkt-'||target IN (SELECT tagname FROM tag)"
53 " THEN 1"
54 " WHEN 'event-'||target IN (SELECT tagname FROM tag)"
55 " THEN 2"
56 " ELSE 0 END)"
57 " FROM attachment"
58 );
59 if( zPage ){
60 if( g.perm.RdWiki==0 ){ login_needed(g.anon.RdWiki); return; }
61 style_header("Attachments To %h", zPage);
@@ -54,10 +62,15 @@
62 blob_append_sql(&sql, " WHERE target=%Q", zPage);
63 }else if( zTkt ){
64 if( g.perm.RdTkt==0 ){ login_needed(g.anon.RdTkt); return; }
65 style_header("Attachments To Ticket %S", zTkt);
66 blob_append_sql(&sql, " WHERE target GLOB '%q*'", zTkt);
67 }else if( zTechNote ){
68 if( g.perm.RdWiki==0 ){ login_needed(g.anon.RdWiki); return; }
69 style_header("Attachments to Tech Note %S", zTechNote);
70 blob_append_sql(&sql, " WHERE target GLOB '%q*'",
71 zTechNote);
72 }else{
73 if( g.perm.RdTkt==0 && g.perm.RdWiki==0 ){
74 login_needed(g.anon.RdTkt || g.anon.RdWiki);
75 return;
76 }
@@ -73,21 +86,25 @@
86 const char *zFilename = db_column_text(&q, 3);
87 const char *zComment = db_column_text(&q, 4);
88 const char *zUser = db_column_text(&q, 5);
89 const char *zUuid = db_column_text(&q, 6);
90 int attachid = db_column_int(&q, 7);
91 // type 0 is a wiki page, 1 is a ticket, 2 is a tech note
92 int type = db_column_int(&q, 8);
93 const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
94 int i;
95 char *zUrlTail;
96 for(i=0; zFilename[i]; i++){
97 if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
98 zFilename = &zFilename[i+1];
99 i = -1;
100 }
101 }
102 if( type==1 ){
103 zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename);
104 }else if( type==2 ){
105 zUrlTail = mprintf("technote=%s&file=%t", zTarget, zFilename);
106 }else{
107 zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
108 }
109 @ <li><p>
110 @ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a>
@@ -98,19 +115,22 @@
115 @ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br />
116 if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++;
117 if( zComment && zComment[0] ){
118 @ %!W(zComment)<br />
119 }
120 if( zPage==0 && zTkt==0 && zTechNote==0 ){
121 if( zSrc==0 || zSrc[0]==0 ){
122 zSrc = "Deleted from";
123 }else {
124 zSrc = "Added to";
125 }
126 if( type==1 ){
127 @ %s(zSrc) ticket <a href="%R/tktview?name=%s(zTarget)">
128 @ %S(zTarget)</a>
129 }else if( type==2 ){
130 @ %s(zSrc) tech note <a href="%R/technote/%s(zTarget)">
131 @ %S(zTarget)</a>
132 }else{
133 @ %s(zSrc) wiki page <a href="%R/wiki?name=%t(zTarget)">
134 @ %h(zTarget)</a>
135 }
136 }else{
@@ -138,31 +158,35 @@
158 ** Download or display an attachment.
159 ** Query parameters:
160 **
161 ** tkt=TICKETUUID
162 ** page=WIKIPAGE
163 ** technote=TECHNOTEUUID
164 ** file=FILENAME
165 ** attachid=ID
166 **
167 */
168 void attachview_page(void){
169 const char *zPage = P("page");
170 const char *zTkt = P("tkt");
171 const char *zTechNote = P("technote");
172 const char *zFile = P("file");
173 const char *zTarget = 0;
174 int attachid = atoi(PD("attachid","0"));
175 char *zUUID;
176
 
177 if( zFile==0 ) fossil_redirect_home();
178 login_check_credentials();
179 if( zPage ){
180 if( g.perm.RdWiki==0 ){ login_needed(g.anon.RdWiki); return; }
181 zTarget = zPage;
182 }else if( zTkt ){
183 if( g.perm.RdTkt==0 ){ login_needed(g.anon.RdTkt); return; }
184 zTarget = zTkt;
185 }else if( zTechNote ){
186 if( g.perm.RdWiki==0 ){ login_needed(g.anon.RdWiki); return; }
187 zTarget = zTechNote;
188 }else{
189 fossil_redirect_home();
190 }
191 if( attachid>0 ){
192 zUUID = db_text(0,
@@ -186,18 +210,19 @@
210 }else if( zUUID[0]=='x' ){
211 style_header("Missing");
212 @ Attachment has been deleted
213 style_footer();
214 return;
 
 
 
 
 
215 }else{
216 g.perm.Read = 1;
217 cgi_replace_parameter("name",zUUID);
218 if( fossil_strcmp(g.zPath,"attachview")==0 ){
219 artifact_page();
220 }else{
221 cgi_replace_parameter("m", mimetype_from_name(zFile));
222 rawartifact_page();
223 }
224 }
225 }
226
227 /*
228 ** Save an attachment control artifact into the repository
@@ -228,27 +253,34 @@
253 ** WEBPAGE: attachadd
254 ** Add a new attachment.
255 **
256 ** tkt=TICKETUUID
257 ** page=WIKIPAGE
258 ** technote=TECHNOTEUUID
259 ** from=URL
260 **
261 */
262 void attachadd_page(void){
263 const char *zPage = P("page");
264 const char *zTkt = P("tkt");
265 const char *zTechNote = P("technote");
266 const char *zFrom = P("from");
267 const char *aContent = P("f");
268 const char *zName = PD("f:filename","unknown");
269 const char *zTarget;
270 char *zTargetType;
271 int szContent = atoi(PD("f:bytes","0"));
272 int goodCaptcha = 1;
273
274 if( P("cancel") ) cgi_redirect(zFrom);
275 if( (zPage && zTkt)
276 || (zPage && zTechNote)
277 || (zTkt && zTechNote)
278 ){
279 fossil_redirect_home();
280 }
281 if( zPage==0 && zTkt==0 && zTechNote==0) fossil_redirect_home();
282 login_check_credentials();
283 if( zPage ){
284 if( g.perm.ApndWiki==0 || g.perm.Attach==0 ){
285 login_needed(g.anon.ApndWiki && g.anon.Attach);
286 return;
@@ -257,10 +289,24 @@
289 fossil_redirect_home();
290 }
291 zTarget = zPage;
292 zTargetType = mprintf("Wiki Page <a href=\"%R/wiki?name=%h\">%h</a>",
293 zPage, zPage);
294 }else if ( zTechNote ){
295 if( g.perm.Write==0 || g.perm.ApndWiki==0 || g.perm.Attach==0 ){
296 login_needed(g.anon.Write && g.anon.ApndWiki && g.anon.Attach);
297 return;
298 }
299 if( !db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'", zTechNote) ){
300 zTechNote = db_text(0, "SELECT substr(tagname,7) FROM tag"
301 " WHERE tagname GLOB 'event-%q*'", zTechNote);
302 if( zTechNote==0) fossil_redirect_home();
303 }
304 zTarget = zTechNote;
305 zTargetType = mprintf("Tech Note <a href=\"%R/technote/%h\">%h</a>",
306 zTechNote, zTechNote);
307
308 }else{
309 if( g.perm.ApndTkt==0 || g.perm.Attach==0 ){
310 login_needed(g.anon.ApndTkt && g.anon.Attach);
311 return;
312 }
@@ -340,10 +386,12 @@
386 @ <input type="file" name="f" size="60" /><br />
387 @ Description:<br />
388 @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br />
389 if( zTkt ){
390 @ <input type="hidden" name="tkt" value="%h(zTkt)" />
391 }else if( zTechNote ){
392 @ <input type="hidden" name="technote" value="%h(zTechNote)" />
393 }else{
394 @ <input type="hidden" name="page" value="%h(zPage)" />
395 }
396 @ <input type="hidden" name="from" value="%h(zFrom)" />
397 @ <input type="submit" name="ok" value="Add Attachment" />
@@ -350,10 +398,11 @@
398 @ <input type="submit" name="cancel" value="Cancel" />
399 @ </div>
400 captcha_generate(0);
401 @ </form>
402 style_footer();
403 fossil_free(zTargetType);
404 }
405
406 /*
407 ** WEBPAGE: ainfo
408 ** URL: /ainfo?name=ARTIFACTID
@@ -364,15 +413,16 @@
413 int rid; /* RID for the control artifact */
414 int ridSrc; /* RID for the attached file */
415 char *zDate; /* Date attached */
416 const char *zUuid; /* UUID of the control artifact */
417 Manifest *pAttach; /* Parse of the control artifact */
418 const char *zTarget; /* Wiki, ticket or tech note attached to */
419 const char *zSrc; /* UUID of the attached file */
420 const char *zName; /* Name of the attached file */
421 const char *zDesc; /* Description of the attached file */
422 const char *zWikiName = 0; /* Wiki page name when attached to Wiki */
423 const char *zTNUuid = 0; /* Tech Note ID when attached to tech note */
424 const char *zTktUuid = 0; /* Ticket ID when attached to a ticket */
425 int modPending; /* True if awaiting moderation */
426 const char *zModAction; /* Moderation action or NULL */
427 int isModerator; /* TRUE if user is the moderator */
428 const char *zMime; /* MIME Type */
@@ -422,15 +472,23 @@
472 zWikiName = zTarget;
473 if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
474 if( g.perm.WrWiki ){
475 style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid);
476 }
477 }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'",zTarget) ){
478 zTNUuid = zTarget;
479 if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
480 if( g.perm.Write && g.perm.WrWiki ){
481 style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid);
482 }
483 }
484 zDate = db_text(0, "SELECT datetime(%.12f)", pAttach->rDate);
485
486 if( P("confirm")
487 && ((zTktUuid && g.perm.WrTkt) ||
488 (zWikiName && g.perm.WrWiki) ||
489 (zTNUuid && g.perm.Write && g.perm.WrWiki))
490 ){
491 int i, n, rid;
492 char *zDate;
493 Blob manifest;
494 Blob cksum;
@@ -454,11 +512,13 @@
512 db_end_transaction(0);
513 @ <p>The attachment below has been deleted.</p>
514 }
515
516 if( P("del")
517 && ((zTktUuid && g.perm.WrTkt) ||
518 (zWikiName && g.perm.WrWiki) ||
519 (zTNUuid && g.perm.Write && g.perm.WrWiki))
520 ){
521 form_begin(0, "%R/ainfo/%!S", zUuid);
522 @ <p>Confirm you want to delete the attachment shown below.
523 @ <input type="submit" name="confirm" value="Confirm">
524 @ </form>
@@ -502,10 +562,14 @@
562 }
563 if( zTktUuid ){
564 @ <tr><th>Ticket:</th>
565 @ <td>%z(href("%R/tktview/%s",zTktUuid))%s(zTktUuid)</a></td></tr>
566 }
567 if( zTNUuid ){
568 @ <tr><th>Tech Note:</th>
569 @ <td>%z(href("%R/technote/%s",zTNUuid))%s(zTNUuid)</a></td></tr>
570 }
571 if( zWikiName ){
572 @ <tr><th>Wiki&nbsp;Page:</th>
573 @ <td>%z(href("%R/wiki?name=%t",zWikiName))%h(zWikiName)</a></td></tr>
574 }
575 @ <tr><th>Date:</th><td>
576
+12 -1
--- src/event.c
+++ src/event.c
@@ -62,11 +62,11 @@
6262
** Display an existing event identified by EVENTID
6363
*/
6464
void event_page(void){
6565
int rid = 0; /* rid of the event artifact */
6666
char *zUuid; /* UUID corresponding to rid */
67
- const char *zId; /* Event identifier */
67
+ const char *zId; /* Event identifier */
6868
const char *zVerbose; /* Value of verbose option */
6969
char *zETime; /* Time of the tech-note */
7070
char *zATime; /* Time the artifact was created */
7171
int specRid; /* rid specified by aid= parameter */
7272
int prevRid, nextRid; /* Previous or next edits of this tech-note */
@@ -75,10 +75,11 @@
7575
Blob title; /* Title extracted from the technote body */
7676
Blob tail; /* Event body that comes after the title */
7777
Stmt q1; /* Query to search for the technote */
7878
int verboseFlag; /* True to show details */
7979
const char *zMimetype = 0; /* Mimetype of the document */
80
+ const char *zFullId; /* Full event identifier */
8081
8182
8283
/* wiki-read privilege is needed in order to read tech-notes.
8384
*/
8485
login_check_credentials();
@@ -150,10 +151,15 @@
150151
tail = fullbody;
151152
}
152153
style_header("%s", blob_str(&title));
153154
if( g.perm.WrWiki && g.perm.Write && nextRid==0 ){
154155
style_submenu_element("Edit", 0, "%R/technoteedit?name=%!S", zId);
156
+ if( g.perm.Attach ){
157
+ style_submenu_element("Attach", "Add an attachment",
158
+ "%R/attachadd?technote=%!S&from=%R/technote/%!S",
159
+ zId, zId);
160
+ }
155161
}
156162
zETime = db_text(0, "SELECT datetime(%.17g)", pTNote->rEventDate);
157163
style_submenu_element("Context", 0, "%R/timeline?c=%.20s", zId);
158164
if( g.perm.Hyperlink ){
159165
if( verboseFlag ){
@@ -216,10 +222,15 @@
216222
}else{
217223
@ <pre>
218224
@ %h(blob_str(&fullbody))
219225
@ </pre>
220226
}
227
+ zFullId = db_text(0, "SELECT SUBSTR(tagname,7)"
228
+ " FROM tag"
229
+ " WHERE tagname GLOB 'event-%q*'",
230
+ zId);
231
+ attachment_list(zFullId, "<hr /><h2>Attachments:</h2><ul>");
221232
style_footer();
222233
manifest_destroy(pTNote);
223234
}
224235
225236
/*
226237
--- src/event.c
+++ src/event.c
@@ -62,11 +62,11 @@
62 ** Display an existing event identified by EVENTID
63 */
64 void event_page(void){
65 int rid = 0; /* rid of the event artifact */
66 char *zUuid; /* UUID corresponding to rid */
67 const char *zId; /* Event identifier */
68 const char *zVerbose; /* Value of verbose option */
69 char *zETime; /* Time of the tech-note */
70 char *zATime; /* Time the artifact was created */
71 int specRid; /* rid specified by aid= parameter */
72 int prevRid, nextRid; /* Previous or next edits of this tech-note */
@@ -75,10 +75,11 @@
75 Blob title; /* Title extracted from the technote body */
76 Blob tail; /* Event body that comes after the title */
77 Stmt q1; /* Query to search for the technote */
78 int verboseFlag; /* True to show details */
79 const char *zMimetype = 0; /* Mimetype of the document */
 
80
81
82 /* wiki-read privilege is needed in order to read tech-notes.
83 */
84 login_check_credentials();
@@ -150,10 +151,15 @@
150 tail = fullbody;
151 }
152 style_header("%s", blob_str(&title));
153 if( g.perm.WrWiki && g.perm.Write && nextRid==0 ){
154 style_submenu_element("Edit", 0, "%R/technoteedit?name=%!S", zId);
 
 
 
 
 
155 }
156 zETime = db_text(0, "SELECT datetime(%.17g)", pTNote->rEventDate);
157 style_submenu_element("Context", 0, "%R/timeline?c=%.20s", zId);
158 if( g.perm.Hyperlink ){
159 if( verboseFlag ){
@@ -216,10 +222,15 @@
216 }else{
217 @ <pre>
218 @ %h(blob_str(&fullbody))
219 @ </pre>
220 }
 
 
 
 
 
221 style_footer();
222 manifest_destroy(pTNote);
223 }
224
225 /*
226
--- src/event.c
+++ src/event.c
@@ -62,11 +62,11 @@
62 ** Display an existing event identified by EVENTID
63 */
64 void event_page(void){
65 int rid = 0; /* rid of the event artifact */
66 char *zUuid; /* UUID corresponding to rid */
67 const char *zId; /* Event identifier */
68 const char *zVerbose; /* Value of verbose option */
69 char *zETime; /* Time of the tech-note */
70 char *zATime; /* Time the artifact was created */
71 int specRid; /* rid specified by aid= parameter */
72 int prevRid, nextRid; /* Previous or next edits of this tech-note */
@@ -75,10 +75,11 @@
75 Blob title; /* Title extracted from the technote body */
76 Blob tail; /* Event body that comes after the title */
77 Stmt q1; /* Query to search for the technote */
78 int verboseFlag; /* True to show details */
79 const char *zMimetype = 0; /* Mimetype of the document */
80 const char *zFullId; /* Full event identifier */
81
82
83 /* wiki-read privilege is needed in order to read tech-notes.
84 */
85 login_check_credentials();
@@ -150,10 +151,15 @@
151 tail = fullbody;
152 }
153 style_header("%s", blob_str(&title));
154 if( g.perm.WrWiki && g.perm.Write && nextRid==0 ){
155 style_submenu_element("Edit", 0, "%R/technoteedit?name=%!S", zId);
156 if( g.perm.Attach ){
157 style_submenu_element("Attach", "Add an attachment",
158 "%R/attachadd?technote=%!S&from=%R/technote/%!S",
159 zId, zId);
160 }
161 }
162 zETime = db_text(0, "SELECT datetime(%.17g)", pTNote->rEventDate);
163 style_submenu_element("Context", 0, "%R/timeline?c=%.20s", zId);
164 if( g.perm.Hyperlink ){
165 if( verboseFlag ){
@@ -216,10 +222,15 @@
222 }else{
223 @ <pre>
224 @ %h(blob_str(&fullbody))
225 @ </pre>
226 }
227 zFullId = db_text(0, "SELECT SUBSTR(tagname,7)"
228 " FROM tag"
229 " WHERE tagname GLOB 'event-%q*'",
230 zId);
231 attachment_list(zFullId, "<hr /><h2>Attachments:</h2><ul>");
232 style_footer();
233 manifest_destroy(pTNote);
234 }
235
236 /*
237
+83 -3
--- src/manifest.c
+++ src/manifest.c
@@ -2008,10 +2008,11 @@
20082008
char *zTag = mprintf("event-%s", p->zEventId);
20092009
int tagid = tag_findid(zTag, 1);
20102010
int prior, subsequent;
20112011
int nWiki;
20122012
char zLength[40];
2013
+ Stmt qatt;
20132014
while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
20142015
nWiki = strlen(p->zWiki);
20152016
sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
20162017
tag_insert(zTag, 1, zLength, rid, p->rDate, rid);
20172018
fossil_free(zTag);
@@ -2049,26 +2050,95 @@
20492050
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
20502051
p->rEventDate, rid, tagid, p->zUser, p->zComment,
20512052
TAG_BGCOLOR, rid
20522053
);
20532054
}
2055
+ /* Locate and update comment for any attachments */
2056
+ db_prepare(&qatt,
2057
+ "SELECT attachid, src, target, filename FROM attachment"
2058
+ " WHERE target=%Q",
2059
+ p->zEventId
2060
+ );
2061
+ while( db_step(&qatt)==SQLITE_ROW ){
2062
+ const char *zAttachId = db_column_text(&qatt, 0);
2063
+ const char *zSrc = db_column_text(&qatt, 1);
2064
+ const char *zTarget = db_column_text(&qatt, 2);
2065
+ const char *zName = db_column_text(&qatt, 3);
2066
+ const char isAdd = (zSrc && zSrc[0]) ? 1 : 0;
2067
+ char *zComment;
2068
+ if( isAdd ){
2069
+ zComment = mprintf(
2070
+ "Add attachment [/artifact/%!S|%h] to"
2071
+ " tech note [/technote/%h|%.10h]",
2072
+ zSrc, zName, zTarget, zTarget);
2073
+ }else{
2074
+ zComment = mprintf("Delete attachment \"%h\" from tech note [%.10h]",
2075
+ zName, zTarget);
2076
+ }
2077
+ db_multi_exec("UPDATE event SET comment=%Q, type='e"
2078
+ " WHERE objid=%Q",
2079
+ zComment, zAttachId);
2080
+ fossil_free(zComment);
2081
+ }
2082
+ db_finalize(&qatt);
20542083
}
20552084
if( p->type==CFTYPE_TICKET ){
20562085
char *zTag;
2086
+ Stmt qatt;
20572087
assert( manifest_crosslink_busy==1 );
20582088
zTag = mprintf("tkt-%s", p->zTicketUuid);
20592089
tag_insert(zTag, 1, 0, rid, p->rDate, rid);
20602090
fossil_free(zTag);
20612091
db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
20622092
p->zTicketUuid);
2093
+ /* Locate and update comment for any attachments */
2094
+ db_prepare(&qatt,
2095
+ "SELECT attachid, src, target, filename FROM attachment"
2096
+ " WHERE target=%Q",
2097
+ p->zTicketUuid
2098
+ );
2099
+ while( db_step(&qatt)==SQLITE_ROW ){
2100
+ const char *zAttachId = db_column_text(&qatt, 0);
2101
+ const char *zSrc = db_column_text(&qatt, 1);
2102
+ const char *zTarget = db_column_text(&qatt, 2);
2103
+ const char *zName = db_column_text(&qatt, 3);
2104
+ const char isAdd = (zSrc && zSrc[0]) ? 1 : 0;
2105
+ char *zComment;
2106
+ if( isAdd ){
2107
+ zComment = mprintf(
2108
+ "Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]",
2109
+ zSrc, zName, zTarget, zTarget);
2110
+ }else{
2111
+ zComment = mprintf("Delete attachment \"%h\" from ticket [%!S|%S]",
2112
+ zName, zTarget, zTarget);
2113
+ }
2114
+ db_multi_exec("UPDATE event SET comment=%Q, type='t"
2115
+ " WHERE objid=%Q",
2116
+ zComment, zAttachId);
2117
+ fossil_free(zComment);
2118
+ }
2119
+ db_finalize(&qatt);
20632120
}
20642121
if( p->type==CFTYPE_ATTACHMENT ){
20652122
char *zComment = 0;
20662123
const char isAdd = (p->zAttachSrc && p->zAttachSrc[0]) ? 1 : 0;
2067
- const char attachToType = fossil_is_uuid(p->zAttachTarget)
2068
- ? 't' /* attach to ticket */
2069
- : 'w' /* attach to wiki page */;
2124
+ /* We assume that we're attaching to a wiki page until we
2125
+ ** prove otherwise (which could on a later artifact if we
2126
+ ** process the attachment artifact before the artifact to
2127
+ ** which it is attached!) */
2128
+ char attachToType = 'w';
2129
+ if( fossil_is_uuid(p->zAttachTarget) ){
2130
+ if( db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'",
2131
+ p->zAttachTarget)
2132
+ ){
2133
+ attachToType = 't'; /* Attaching to known ticket */
2134
+ }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'",
2135
+ p->zAttachTarget)
2136
+ ){
2137
+ attachToType = 'e'; /* Attaching to known tech note */
2138
+ }
2139
+ }
20702140
db_multi_exec(
20712141
"INSERT INTO attachment(attachid, mtime, src, target,"
20722142
"filename, comment, user)"
20732143
"VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
20742144
rid, p->rDate, p->zAttachSrc, p->zAttachTarget, p->zAttachName,
@@ -2089,10 +2159,20 @@
20892159
p->zAttachSrc, p->zAttachName, p->zAttachTarget);
20902160
}else{
20912161
zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
20922162
p->zAttachName, p->zAttachTarget);
20932163
}
2164
+ }else if( 'e' == attachToType ){
2165
+ if( isAdd ){
2166
+ zComment = mprintf(
2167
+ "Add attachment [/artifact/%!S|%h] to"
2168
+ " tech note [/technote/%h|%.10h]",
2169
+ p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2170
+ }else{
2171
+ zComment = mprintf("Delete attachment \"%h\" from tech note [%.10h]",
2172
+ p->zAttachName, p->zAttachTarget);
2173
+ }
20942174
}else{
20952175
if( isAdd ){
20962176
zComment = mprintf(
20972177
"Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]",
20982178
p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
20992179
--- src/manifest.c
+++ src/manifest.c
@@ -2008,10 +2008,11 @@
2008 char *zTag = mprintf("event-%s", p->zEventId);
2009 int tagid = tag_findid(zTag, 1);
2010 int prior, subsequent;
2011 int nWiki;
2012 char zLength[40];
 
2013 while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
2014 nWiki = strlen(p->zWiki);
2015 sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
2016 tag_insert(zTag, 1, zLength, rid, p->rDate, rid);
2017 fossil_free(zTag);
@@ -2049,26 +2050,95 @@
2049 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
2050 p->rEventDate, rid, tagid, p->zUser, p->zComment,
2051 TAG_BGCOLOR, rid
2052 );
2053 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2054 }
2055 if( p->type==CFTYPE_TICKET ){
2056 char *zTag;
 
2057 assert( manifest_crosslink_busy==1 );
2058 zTag = mprintf("tkt-%s", p->zTicketUuid);
2059 tag_insert(zTag, 1, 0, rid, p->rDate, rid);
2060 fossil_free(zTag);
2061 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
2062 p->zTicketUuid);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2063 }
2064 if( p->type==CFTYPE_ATTACHMENT ){
2065 char *zComment = 0;
2066 const char isAdd = (p->zAttachSrc && p->zAttachSrc[0]) ? 1 : 0;
2067 const char attachToType = fossil_is_uuid(p->zAttachTarget)
2068 ? 't' /* attach to ticket */
2069 : 'w' /* attach to wiki page */;
 
 
 
 
 
 
 
 
 
 
 
 
 
2070 db_multi_exec(
2071 "INSERT INTO attachment(attachid, mtime, src, target,"
2072 "filename, comment, user)"
2073 "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
2074 rid, p->rDate, p->zAttachSrc, p->zAttachTarget, p->zAttachName,
@@ -2089,10 +2159,20 @@
2089 p->zAttachSrc, p->zAttachName, p->zAttachTarget);
2090 }else{
2091 zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
2092 p->zAttachName, p->zAttachTarget);
2093 }
 
 
 
 
 
 
 
 
 
 
2094 }else{
2095 if( isAdd ){
2096 zComment = mprintf(
2097 "Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]",
2098 p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2099
--- src/manifest.c
+++ src/manifest.c
@@ -2008,10 +2008,11 @@
2008 char *zTag = mprintf("event-%s", p->zEventId);
2009 int tagid = tag_findid(zTag, 1);
2010 int prior, subsequent;
2011 int nWiki;
2012 char zLength[40];
2013 Stmt qatt;
2014 while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
2015 nWiki = strlen(p->zWiki);
2016 sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
2017 tag_insert(zTag, 1, zLength, rid, p->rDate, rid);
2018 fossil_free(zTag);
@@ -2049,26 +2050,95 @@
2050 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
2051 p->rEventDate, rid, tagid, p->zUser, p->zComment,
2052 TAG_BGCOLOR, rid
2053 );
2054 }
2055 /* Locate and update comment for any attachments */
2056 db_prepare(&qatt,
2057 "SELECT attachid, src, target, filename FROM attachment"
2058 " WHERE target=%Q",
2059 p->zEventId
2060 );
2061 while( db_step(&qatt)==SQLITE_ROW ){
2062 const char *zAttachId = db_column_text(&qatt, 0);
2063 const char *zSrc = db_column_text(&qatt, 1);
2064 const char *zTarget = db_column_text(&qatt, 2);
2065 const char *zName = db_column_text(&qatt, 3);
2066 const char isAdd = (zSrc && zSrc[0]) ? 1 : 0;
2067 char *zComment;
2068 if( isAdd ){
2069 zComment = mprintf(
2070 "Add attachment [/artifact/%!S|%h] to"
2071 " tech note [/technote/%h|%.10h]",
2072 zSrc, zName, zTarget, zTarget);
2073 }else{
2074 zComment = mprintf("Delete attachment \"%h\" from tech note [%.10h]",
2075 zName, zTarget);
2076 }
2077 db_multi_exec("UPDATE event SET comment=%Q, type='e"
2078 " WHERE objid=%Q",
2079 zComment, zAttachId);
2080 fossil_free(zComment);
2081 }
2082 db_finalize(&qatt);
2083 }
2084 if( p->type==CFTYPE_TICKET ){
2085 char *zTag;
2086 Stmt qatt;
2087 assert( manifest_crosslink_busy==1 );
2088 zTag = mprintf("tkt-%s", p->zTicketUuid);
2089 tag_insert(zTag, 1, 0, rid, p->rDate, rid);
2090 fossil_free(zTag);
2091 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
2092 p->zTicketUuid);
2093 /* Locate and update comment for any attachments */
2094 db_prepare(&qatt,
2095 "SELECT attachid, src, target, filename FROM attachment"
2096 " WHERE target=%Q",
2097 p->zTicketUuid
2098 );
2099 while( db_step(&qatt)==SQLITE_ROW ){
2100 const char *zAttachId = db_column_text(&qatt, 0);
2101 const char *zSrc = db_column_text(&qatt, 1);
2102 const char *zTarget = db_column_text(&qatt, 2);
2103 const char *zName = db_column_text(&qatt, 3);
2104 const char isAdd = (zSrc && zSrc[0]) ? 1 : 0;
2105 char *zComment;
2106 if( isAdd ){
2107 zComment = mprintf(
2108 "Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]",
2109 zSrc, zName, zTarget, zTarget);
2110 }else{
2111 zComment = mprintf("Delete attachment \"%h\" from ticket [%!S|%S]",
2112 zName, zTarget, zTarget);
2113 }
2114 db_multi_exec("UPDATE event SET comment=%Q, type='t"
2115 " WHERE objid=%Q",
2116 zComment, zAttachId);
2117 fossil_free(zComment);
2118 }
2119 db_finalize(&qatt);
2120 }
2121 if( p->type==CFTYPE_ATTACHMENT ){
2122 char *zComment = 0;
2123 const char isAdd = (p->zAttachSrc && p->zAttachSrc[0]) ? 1 : 0;
2124 /* We assume that we're attaching to a wiki page until we
2125 ** prove otherwise (which could on a later artifact if we
2126 ** process the attachment artifact before the artifact to
2127 ** which it is attached!) */
2128 char attachToType = 'w';
2129 if( fossil_is_uuid(p->zAttachTarget) ){
2130 if( db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'",
2131 p->zAttachTarget)
2132 ){
2133 attachToType = 't'; /* Attaching to known ticket */
2134 }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'",
2135 p->zAttachTarget)
2136 ){
2137 attachToType = 'e'; /* Attaching to known tech note */
2138 }
2139 }
2140 db_multi_exec(
2141 "INSERT INTO attachment(attachid, mtime, src, target,"
2142 "filename, comment, user)"
2143 "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
2144 rid, p->rDate, p->zAttachSrc, p->zAttachTarget, p->zAttachName,
@@ -2089,10 +2159,20 @@
2159 p->zAttachSrc, p->zAttachName, p->zAttachTarget);
2160 }else{
2161 zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
2162 p->zAttachName, p->zAttachTarget);
2163 }
2164 }else if( 'e' == attachToType ){
2165 if( isAdd ){
2166 zComment = mprintf(
2167 "Add attachment [/artifact/%!S|%h] to"
2168 " tech note [/technote/%h|%.10h]",
2169 p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2170 }else{
2171 zComment = mprintf("Delete attachment \"%h\" from tech note [%.10h]",
2172 p->zAttachName, p->zAttachTarget);
2173 }
2174 }else{
2175 if( isAdd ){
2176 zComment = mprintf(
2177 "Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]",
2178 p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2179

Keyboard Shortcuts

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