Fossil SCM

Add 'attachment-size-limit' setting and honor it in /attachadd. Update generation of timeline messages for attachment artifacts to distinguish forum post attachments.

stephan 2026-05-22 17:54 UTC forum-attachments
Commit fed106e8b6121f9e8ce3f929625228afa11cafdf92bcd0621efa099c0a27bdf1
3 files changed +49 -19 +4 +69 -47
+49 -19
--- src/attach.c
+++ src/attach.c
@@ -18,10 +18,37 @@
1818
** This file contains code for dealing with attachments.
1919
*/
2020
#include "config.h"
2121
#include "attach.h"
2222
#include <assert.h>
23
+
24
+/*
25
+** Given a presumedly legal attachment target name, this guesses the
26
+** target type and returns one of CFTYPE_FORUM, CFTYPE_WIKI,
27
+** CFTYPE_TICKET, or CFTYPE_TECHNOTE. Returns 0 if it cannot
28
+** distinguish the target type.
29
+**
30
+** In the case of CFTYPE_FORUM, it is up to the caller to ensure that,
31
+** if needed, they resolve zTarget using forumpost_head_rid2() so that
32
+** they get the RID of the earliest version of the post, as that is
33
+** the only one which attachments should target.
34
+*/
35
+int attachment_target_type(const char *zTarget){
36
+ if( forumpost_head_rid2(zTarget)>0 ){
37
+ return CFTYPE_FORUM;
38
+ }
39
+ return db_int(0,
40
+ "SELECT CASE "
41
+ "WHEN 'tkt-'||%Q IN (SELECT tagname FROM tag) THEN %d "
42
+ "WHEN 'event-'||%Q IN (SELECT tagname FROM tag) THEN %d "
43
+ "WHEN 'wiki-'||%Q IN (SELECT tagname FROM tag) THEN %d "
44
+ "ELSE 0 END",
45
+ zTarget, CFTYPE_TICKET,
46
+ zTarget, CFTYPE_TECHNOTE,
47
+ zTarget, CFTYPE_WIKI
48
+ );
49
+}
2350
2451
/*
2552
** WEBPAGE: attachlist
2653
** List attachments.
2754
**
@@ -52,18 +79,11 @@
5279
style_set_current_feature("attach");
5380
blob_zero(&sql);
5481
blob_append_sql(&sql,
5582
"SELECT datetime(mtime,toLocal()), src, target, filename,"
5683
" comment, user,"
57
- " (SELECT uuid FROM blob WHERE rid=attachid), attachid,"
58
- " (CASE WHEN 'tkt-'||target IN (SELECT tagname FROM tag)"
59
- " THEN 1"
60
- " WHEN 'event-'||target IN (SELECT tagname FROM tag)"
61
- " THEN 2"
62
- " WHEN 'wiki-'||target IN (SELECT tagname FROM tag)"
63
- " THEN 3"
64
- " ELSE 0 END)"
84
+ " (SELECT uuid FROM blob WHERE rid=attachid), attachid"
6585
" FROM attachment"
6686
);
6787
if( zForumPost ){
6888
int fnid;
6989
char *zUuid;
@@ -105,13 +125,13 @@
105125
const char *zTarget = db_column_text(&q, 2);
106126
const char *zFilename = db_column_text(&q, 3);
107127
const char *zComment = db_column_text(&q, 4);
108128
const char *zUser = db_column_text(&q, 5);
109129
const char *zUuid = db_column_text(&q, 6);
110
- int attachid = db_column_int(&q, 7);
130
+ const int attachid = db_column_int(&q, 7);
111131
/* type 0 is a wiki page, 1 is a ticket, 2 is a tech note */
112
- int type = db_column_int(&q, 8);
132
+ const int type = attachment_target_type(zTarget);
113133
const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
114134
int i;
115135
char *zUrlTail;
116136
for(i=0; zFilename[i]; i++){
117137
if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
@@ -140,25 +160,29 @@
140160
zSrc = "Deleted from";
141161
}else {
142162
zSrc = "Added to";
143163
}
144164
switch( type ){
145
- case 1:
165
+ case CFTYPE_TICKET:
146166
@ %s(zSrc) ticket <a href="%R/tktview?name=%s(zTarget)">
147167
@ %S(zTarget)</a>
148168
break;
149
- case 2:
169
+ case CFTYPE_TECHNOTE:
150170
@ %s(zSrc) tech note <a href="%R/technote/%s(zTarget)">
151171
@ %S(zTarget)</a>
152172
break;
153
- case 3:
154
- @ %s(zSrc) wiki page <a href="%R/wiki?name=%t(zTarget)">
155
- @ %h(zTarget)</a>
173
+ case CFTYPE_WIKI:
174
+ @ %s(zSrc) wiki page <a href="%R/wiki?name=%t(zTarget)">
175
+ @ %h(zTarget)</a>
176
+ break;
177
+ case CFTYPE_FORUM:
178
+ @ %s(zSrc) forum post <a href="%R/forumpost/%s(zTarget)">
179
+ @ %h(zTarget)</a>
156180
break;
157
- case 0:
158
- @ %s(zSrc) forum post <a href="%R/forumpost/%s(zTarget)">
159
- @ %h(zTarget)</a>
181
+ default:
182
+ @ <span class='error'>%s(zSrc) cannot determine target type
183
+ @ of %h(zTarget)</span>
160184
break;
161185
}
162186
}else{
163187
if( zSrc==0 || zSrc[0]==0 ){
164188
@ Deleted
@@ -355,18 +379,24 @@
355379
const char *zTarget;
356380
char *zTargetType;
357381
char *zExtraFree = 0;
358382
int szContent = atoi(PD("f:bytes","0"));
359383
int goodCaptcha = 1;
384
+ int szLimit = 0;
360385
361386
if( zFrom==0 ) zFrom = mprintf("%R/home");
362387
if( P("cancel") ) cgi_redirect(zFrom);
363388
if( (!!zPage + !!zTkt + !!zTechNote + !!zForumPost)!=1 ){
364389
//fossil_redirect_home();
365390
fossil_fatal("Requires exactly one one: page=X, tkt=X, forumpost=X, or technote=X");
366391
}
367392
login_check_credentials();
393
+ szLimit = db_get_int("attachment-size-limit", 0);
394
+ if( szContent<0 || (szLimit && szContent>szLimit) ){
395
+ fossil_fatal("Attachment %s is too large. Limit is %d bytes.", zName,
396
+ (szLimit>0 && szContent>0) ? szLimit : 0x7fffffff);
397
+ }
368398
if( zForumPost ){
369399
int fpid;
370400
if( g.perm.AttachForum==0 ){
371401
login_needed(g.anon.AttachForum);
372402
return;
@@ -549,11 +579,11 @@
549579
for(i=n=0; zFile[i]; i++){
550580
if( zFile[i]=='/' || zFile[i]=='\\' ) n = i;
551581
}
552582
zFile += n;
553583
if( zFile[0]==0 ) zFile = "unknown";
554
- blob_appendf(&manifest, "A %F\n", zFile);
584
+ blob_appendf(&manifest, "A %F %F\n", zFile, zTarget);
555585
zNewDate = date_in_standard_format("now");
556586
blob_appendf(&manifest, "D %s\n", zNewDate);
557587
blob_appendf(&manifest, "U %F\n", login_name());
558588
md5sum_blob(&manifest, &cksum);
559589
blob_appendf(&manifest, "Z %b\n", &cksum);
560590
--- src/attach.c
+++ src/attach.c
@@ -18,10 +18,37 @@
18 ** This file contains code for dealing with attachments.
19 */
20 #include "config.h"
21 #include "attach.h"
22 #include <assert.h>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
24 /*
25 ** WEBPAGE: attachlist
26 ** List attachments.
27 **
@@ -52,18 +79,11 @@
52 style_set_current_feature("attach");
53 blob_zero(&sql);
54 blob_append_sql(&sql,
55 "SELECT datetime(mtime,toLocal()), src, target, filename,"
56 " comment, user,"
57 " (SELECT uuid FROM blob WHERE rid=attachid), attachid,"
58 " (CASE WHEN 'tkt-'||target IN (SELECT tagname FROM tag)"
59 " THEN 1"
60 " WHEN 'event-'||target IN (SELECT tagname FROM tag)"
61 " THEN 2"
62 " WHEN 'wiki-'||target IN (SELECT tagname FROM tag)"
63 " THEN 3"
64 " ELSE 0 END)"
65 " FROM attachment"
66 );
67 if( zForumPost ){
68 int fnid;
69 char *zUuid;
@@ -105,13 +125,13 @@
105 const char *zTarget = db_column_text(&q, 2);
106 const char *zFilename = db_column_text(&q, 3);
107 const char *zComment = db_column_text(&q, 4);
108 const char *zUser = db_column_text(&q, 5);
109 const char *zUuid = db_column_text(&q, 6);
110 int attachid = db_column_int(&q, 7);
111 /* type 0 is a wiki page, 1 is a ticket, 2 is a tech note */
112 int type = db_column_int(&q, 8);
113 const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
114 int i;
115 char *zUrlTail;
116 for(i=0; zFilename[i]; i++){
117 if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
@@ -140,25 +160,29 @@
140 zSrc = "Deleted from";
141 }else {
142 zSrc = "Added to";
143 }
144 switch( type ){
145 case 1:
146 @ %s(zSrc) ticket <a href="%R/tktview?name=%s(zTarget)">
147 @ %S(zTarget)</a>
148 break;
149 case 2:
150 @ %s(zSrc) tech note <a href="%R/technote/%s(zTarget)">
151 @ %S(zTarget)</a>
152 break;
153 case 3:
154 @ %s(zSrc) wiki page <a href="%R/wiki?name=%t(zTarget)">
155 @ %h(zTarget)</a>
 
 
 
 
156 break;
157 case 0:
158 @ %s(zSrc) forum post <a href="%R/forumpost/%s(zTarget)">
159 @ %h(zTarget)</a>
160 break;
161 }
162 }else{
163 if( zSrc==0 || zSrc[0]==0 ){
164 @ Deleted
@@ -355,18 +379,24 @@
355 const char *zTarget;
356 char *zTargetType;
357 char *zExtraFree = 0;
358 int szContent = atoi(PD("f:bytes","0"));
359 int goodCaptcha = 1;
 
360
361 if( zFrom==0 ) zFrom = mprintf("%R/home");
362 if( P("cancel") ) cgi_redirect(zFrom);
363 if( (!!zPage + !!zTkt + !!zTechNote + !!zForumPost)!=1 ){
364 //fossil_redirect_home();
365 fossil_fatal("Requires exactly one one: page=X, tkt=X, forumpost=X, or technote=X");
366 }
367 login_check_credentials();
 
 
 
 
 
368 if( zForumPost ){
369 int fpid;
370 if( g.perm.AttachForum==0 ){
371 login_needed(g.anon.AttachForum);
372 return;
@@ -549,11 +579,11 @@
549 for(i=n=0; zFile[i]; i++){
550 if( zFile[i]=='/' || zFile[i]=='\\' ) n = i;
551 }
552 zFile += n;
553 if( zFile[0]==0 ) zFile = "unknown";
554 blob_appendf(&manifest, "A %F\n", zFile);
555 zNewDate = date_in_standard_format("now");
556 blob_appendf(&manifest, "D %s\n", zNewDate);
557 blob_appendf(&manifest, "U %F\n", login_name());
558 md5sum_blob(&manifest, &cksum);
559 blob_appendf(&manifest, "Z %b\n", &cksum);
560
--- src/attach.c
+++ src/attach.c
@@ -18,10 +18,37 @@
18 ** This file contains code for dealing with attachments.
19 */
20 #include "config.h"
21 #include "attach.h"
22 #include <assert.h>
23
24 /*
25 ** Given a presumedly legal attachment target name, this guesses the
26 ** target type and returns one of CFTYPE_FORUM, CFTYPE_WIKI,
27 ** CFTYPE_TICKET, or CFTYPE_TECHNOTE. Returns 0 if it cannot
28 ** distinguish the target type.
29 **
30 ** In the case of CFTYPE_FORUM, it is up to the caller to ensure that,
31 ** if needed, they resolve zTarget using forumpost_head_rid2() so that
32 ** they get the RID of the earliest version of the post, as that is
33 ** the only one which attachments should target.
34 */
35 int attachment_target_type(const char *zTarget){
36 if( forumpost_head_rid2(zTarget)>0 ){
37 return CFTYPE_FORUM;
38 }
39 return db_int(0,
40 "SELECT CASE "
41 "WHEN 'tkt-'||%Q IN (SELECT tagname FROM tag) THEN %d "
42 "WHEN 'event-'||%Q IN (SELECT tagname FROM tag) THEN %d "
43 "WHEN 'wiki-'||%Q IN (SELECT tagname FROM tag) THEN %d "
44 "ELSE 0 END",
45 zTarget, CFTYPE_TICKET,
46 zTarget, CFTYPE_TECHNOTE,
47 zTarget, CFTYPE_WIKI
48 );
49 }
50
51 /*
52 ** WEBPAGE: attachlist
53 ** List attachments.
54 **
@@ -52,18 +79,11 @@
79 style_set_current_feature("attach");
80 blob_zero(&sql);
81 blob_append_sql(&sql,
82 "SELECT datetime(mtime,toLocal()), src, target, filename,"
83 " comment, user,"
84 " (SELECT uuid FROM blob WHERE rid=attachid), attachid"
 
 
 
 
 
 
 
85 " FROM attachment"
86 );
87 if( zForumPost ){
88 int fnid;
89 char *zUuid;
@@ -105,13 +125,13 @@
125 const char *zTarget = db_column_text(&q, 2);
126 const char *zFilename = db_column_text(&q, 3);
127 const char *zComment = db_column_text(&q, 4);
128 const char *zUser = db_column_text(&q, 5);
129 const char *zUuid = db_column_text(&q, 6);
130 const int attachid = db_column_int(&q, 7);
131 /* type 0 is a wiki page, 1 is a ticket, 2 is a tech note */
132 const int type = attachment_target_type(zTarget);
133 const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
134 int i;
135 char *zUrlTail;
136 for(i=0; zFilename[i]; i++){
137 if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
@@ -140,25 +160,29 @@
160 zSrc = "Deleted from";
161 }else {
162 zSrc = "Added to";
163 }
164 switch( type ){
165 case CFTYPE_TICKET:
166 @ %s(zSrc) ticket <a href="%R/tktview?name=%s(zTarget)">
167 @ %S(zTarget)</a>
168 break;
169 case CFTYPE_TECHNOTE:
170 @ %s(zSrc) tech note <a href="%R/technote/%s(zTarget)">
171 @ %S(zTarget)</a>
172 break;
173 case CFTYPE_WIKI:
174 @ %s(zSrc) wiki page <a href="%R/wiki?name=%t(zTarget)">
175 @ %h(zTarget)</a>
176 break;
177 case CFTYPE_FORUM:
178 @ %s(zSrc) forum post <a href="%R/forumpost/%s(zTarget)">
179 @ %h(zTarget)</a>
180 break;
181 default:
182 @ <span class='error'>%s(zSrc) cannot determine target type
183 @ of %h(zTarget)</span>
184 break;
185 }
186 }else{
187 if( zSrc==0 || zSrc[0]==0 ){
188 @ Deleted
@@ -355,18 +379,24 @@
379 const char *zTarget;
380 char *zTargetType;
381 char *zExtraFree = 0;
382 int szContent = atoi(PD("f:bytes","0"));
383 int goodCaptcha = 1;
384 int szLimit = 0;
385
386 if( zFrom==0 ) zFrom = mprintf("%R/home");
387 if( P("cancel") ) cgi_redirect(zFrom);
388 if( (!!zPage + !!zTkt + !!zTechNote + !!zForumPost)!=1 ){
389 //fossil_redirect_home();
390 fossil_fatal("Requires exactly one one: page=X, tkt=X, forumpost=X, or technote=X");
391 }
392 login_check_credentials();
393 szLimit = db_get_int("attachment-size-limit", 0);
394 if( szContent<0 || (szLimit && szContent>szLimit) ){
395 fossil_fatal("Attachment %s is too large. Limit is %d bytes.", zName,
396 (szLimit>0 && szContent>0) ? szLimit : 0x7fffffff);
397 }
398 if( zForumPost ){
399 int fpid;
400 if( g.perm.AttachForum==0 ){
401 login_needed(g.anon.AttachForum);
402 return;
@@ -549,11 +579,11 @@
579 for(i=n=0; zFile[i]; i++){
580 if( zFile[i]=='/' || zFile[i]=='\\' ) n = i;
581 }
582 zFile += n;
583 if( zFile[0]==0 ) zFile = "unknown";
584 blob_appendf(&manifest, "A %F %F\n", zFile, zTarget);
585 zNewDate = date_in_standard_format("now");
586 blob_appendf(&manifest, "D %s\n", zNewDate);
587 blob_appendf(&manifest, "U %F\n", login_name());
588 md5sum_blob(&manifest, &cksum);
589 blob_appendf(&manifest, "Z %b\n", &cksum);
590
--- src/forum.c
+++ src/forum.c
@@ -1826,10 +1826,14 @@
18261826
** SETTING: forum-title width=20 default=Forum
18271827
** This is the name or "title" of the Forum for this repository. The
18281828
** default is just "Forum". But in some setups, admins might want to
18291829
** change it to "Developer Forum" or "User Forum" or whatever other name
18301830
** seems more appropriate for the particular usage.
1831
+**
1832
+** SETTING: attachment-size-limit width=16
1833
+** The maximum number of bytes for an attachment. The default is unlimited
1834
+** but a limit may be imposed by the web server or proxy.
18311835
*/
18321836
18331837
/*
18341838
** WEBPAGE: setup_forum
18351839
**
18361840
--- src/forum.c
+++ src/forum.c
@@ -1826,10 +1826,14 @@
1826 ** SETTING: forum-title width=20 default=Forum
1827 ** This is the name or "title" of the Forum for this repository. The
1828 ** default is just "Forum". But in some setups, admins might want to
1829 ** change it to "Developer Forum" or "User Forum" or whatever other name
1830 ** seems more appropriate for the particular usage.
 
 
 
 
1831 */
1832
1833 /*
1834 ** WEBPAGE: setup_forum
1835 **
1836
--- src/forum.c
+++ src/forum.c
@@ -1826,10 +1826,14 @@
1826 ** SETTING: forum-title width=20 default=Forum
1827 ** This is the name or "title" of the Forum for this repository. The
1828 ** default is just "Forum". But in some setups, admins might want to
1829 ** change it to "Developer Forum" or "User Forum" or whatever other name
1830 ** seems more appropriate for the particular usage.
1831 **
1832 ** SETTING: attachment-size-limit width=16
1833 ** The maximum number of bytes for an attachment. The default is unlimited
1834 ** but a limit may be imposed by the web server or proxy.
1835 */
1836
1837 /*
1838 ** WEBPAGE: setup_forum
1839 **
1840
+69 -47
--- src/manifest.c
+++ src/manifest.c
@@ -33,11 +33,12 @@
3333
#define CFTYPE_CLUSTER 2
3434
#define CFTYPE_CONTROL 3
3535
#define CFTYPE_WIKI 4
3636
#define CFTYPE_TICKET 5
3737
#define CFTYPE_ATTACHMENT 6
38
-#define CFTYPE_EVENT 7
38
+#define CFTYPE_EVENT 7 /* older name for CFTYPE_TECHNOTE */
39
+#define CFTYPE_TECHNOTE 7
3940
#define CFTYPE_FORUM 8
4041
4142
/*
4243
** File permissions used by Fossil internally.
4344
*/
@@ -2630,26 +2631,11 @@
26302631
db_finalize(&qatt);
26312632
}
26322633
if( p->type==CFTYPE_ATTACHMENT ){
26332634
char *zComment = 0;
26342635
const char isAdd = (p->zAttachSrc && p->zAttachSrc[0]) ? 1 : 0;
2635
- /* We assume that we're attaching to a wiki page until we
2636
- ** prove otherwise (which could on a later artifact if we
2637
- ** process the attachment artifact before the artifact to
2638
- ** which it is attached!) */
2639
- char attachToType = 'w';
2640
- if( fossil_is_artifact_hash(p->zAttachTarget) ){
2641
- if( db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'",
2642
- p->zAttachTarget)
2643
- ){
2644
- attachToType = 't'; /* Attaching to known ticket */
2645
- }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'",
2646
- p->zAttachTarget)
2647
- ){
2648
- attachToType = 'e'; /* Attaching to known tech note */
2649
- }
2650
- }
2636
+ char attachToType = 0;
26512637
db_multi_exec(
26522638
"INSERT INTO attachment(attachid, mtime, src, target,"
26532639
"filename, comment, user)"
26542640
"VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
26552641
rid, p->rDate, p->zAttachSrc, p->zAttachTarget, p->zAttachName,
@@ -2661,40 +2647,76 @@
26612647
" WHERE target=%Q AND filename=%Q))"
26622648
" WHERE target=%Q AND filename=%Q",
26632649
p->zAttachTarget, p->zAttachName,
26642650
p->zAttachTarget, p->zAttachName
26652651
);
2666
- if( 'w' == attachToType ){
2667
- if( isAdd ){
2668
- zComment = mprintf(
2669
- "Add attachment [/artifact/%!S|%h] to wiki page [%h]",
2670
- p->zAttachSrc, p->zAttachName, p->zAttachTarget);
2671
- }else{
2672
- zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
2673
- p->zAttachName, p->zAttachTarget);
2674
- }
2675
- }else if( 'e' == attachToType ){
2676
- if( isAdd ){
2677
- zComment = mprintf(
2678
- "Add attachment [/artifact/%!S|%h] to tech note [/technote/%!S|%S]",
2679
- p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2680
- }else{
2681
- zComment = mprintf(
2682
- "Delete attachment \"/artifact/%!S|%h\" from"
2683
- " tech note [/technote/%!S|%S]",
2684
- p->zAttachName, p->zAttachName,
2685
- p->zAttachTarget,p->zAttachTarget);
2686
- }
2687
- }else{
2688
- if( isAdd ){
2689
- zComment = mprintf(
2690
- "Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]",
2691
- p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2692
- }else{
2693
- zComment = mprintf("Delete attachment \"%h\" from ticket [%!S|%S]",
2694
- p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2695
- }
2652
+ switch( attachment_target_type(p->zAttachTarget) ){
2653
+ case 0:
2654
+ attachToType = 'a';
2655
+ if( isAdd ){
2656
+ zComment = mprintf(
2657
+ "Add attachment [/artifact/%!S|%h] to [/artifact/%!S|%h]",
2658
+ p->zAttachSrc, p->zAttachName, p->zAttachTarget);
2659
+ }else{
2660
+ zComment = mprintf("Delete attachment \"%h\" from "
2661
+ "[/artifact/%!S|%h",
2662
+ p->zAttachName, p->zAttachTarget);
2663
+ }
2664
+ break;
2665
+ case CFTYPE_WIKI:
2666
+ attachToType = 'w';
2667
+ if( isAdd ){
2668
+ zComment = mprintf(
2669
+ "Add attachment [/artifact/%!S|%h] to wiki page [%h]",
2670
+ p->zAttachSrc, p->zAttachName, p->zAttachTarget);
2671
+ }else{
2672
+ zComment = mprintf("Delete attachment \"%h\" from "
2673
+ "wiki page [%h]",
2674
+ p->zAttachName, p->zAttachTarget);
2675
+ }
2676
+ break;
2677
+ case CFTYPE_EVENT:
2678
+ attachToType = 'e';
2679
+ if( isAdd ){
2680
+ zComment = mprintf(
2681
+ "Add attachment [/artifact/%!S|%h] to tech note "
2682
+ "[/technote/%!S|%S]",
2683
+ p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2684
+ }else{
2685
+ zComment = mprintf(
2686
+ "Delete attachment \"/artifact/%!S|%h\" from"
2687
+ " tech note [/technote/%!S|%S]",
2688
+ p->zAttachName, p->zAttachName,
2689
+ p->zAttachTarget,p->zAttachTarget);
2690
+ }
2691
+ break;
2692
+ case CFTYPE_TICKET:
2693
+ attachToType = 't';
2694
+ if( isAdd ){
2695
+ zComment = mprintf(
2696
+ "Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]",
2697
+ p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2698
+ }else{
2699
+ zComment = mprintf(
2700
+ "Delete attachment \"%h\" from ticket [%!S|%S]",
2701
+ p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2702
+ }
2703
+ break;
2704
+ case CFTYPE_FORUM:
2705
+ attachToType = 'f';
2706
+ if( isAdd ){
2707
+ zComment = mprintf(
2708
+ "Add attachment [/artifact/%!S|%h] to forum post "
2709
+ "[/forumpost/%!S|%S]",
2710
+ p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2711
+ }else{
2712
+ zComment = mprintf(
2713
+ "Delete attachment \"%h\" from forum post "
2714
+ "[/forumpost/%!S|%S]",
2715
+ p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2716
+ }
2717
+ break;
26962718
}
26972719
assert( manifest_event_triggers_are_enabled );
26982720
db_multi_exec(
26992721
"REPLACE INTO event(type,mtime,objid,user,comment)"
27002722
"VALUES('%c',%.17g,%d,%Q,%Q)",
27012723
--- src/manifest.c
+++ src/manifest.c
@@ -33,11 +33,12 @@
33 #define CFTYPE_CLUSTER 2
34 #define CFTYPE_CONTROL 3
35 #define CFTYPE_WIKI 4
36 #define CFTYPE_TICKET 5
37 #define CFTYPE_ATTACHMENT 6
38 #define CFTYPE_EVENT 7
 
39 #define CFTYPE_FORUM 8
40
41 /*
42 ** File permissions used by Fossil internally.
43 */
@@ -2630,26 +2631,11 @@
2630 db_finalize(&qatt);
2631 }
2632 if( p->type==CFTYPE_ATTACHMENT ){
2633 char *zComment = 0;
2634 const char isAdd = (p->zAttachSrc && p->zAttachSrc[0]) ? 1 : 0;
2635 /* We assume that we're attaching to a wiki page until we
2636 ** prove otherwise (which could on a later artifact if we
2637 ** process the attachment artifact before the artifact to
2638 ** which it is attached!) */
2639 char attachToType = 'w';
2640 if( fossil_is_artifact_hash(p->zAttachTarget) ){
2641 if( db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'",
2642 p->zAttachTarget)
2643 ){
2644 attachToType = 't'; /* Attaching to known ticket */
2645 }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'",
2646 p->zAttachTarget)
2647 ){
2648 attachToType = 'e'; /* Attaching to known tech note */
2649 }
2650 }
2651 db_multi_exec(
2652 "INSERT INTO attachment(attachid, mtime, src, target,"
2653 "filename, comment, user)"
2654 "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
2655 rid, p->rDate, p->zAttachSrc, p->zAttachTarget, p->zAttachName,
@@ -2661,40 +2647,76 @@
2661 " WHERE target=%Q AND filename=%Q))"
2662 " WHERE target=%Q AND filename=%Q",
2663 p->zAttachTarget, p->zAttachName,
2664 p->zAttachTarget, p->zAttachName
2665 );
2666 if( 'w' == attachToType ){
2667 if( isAdd ){
2668 zComment = mprintf(
2669 "Add attachment [/artifact/%!S|%h] to wiki page [%h]",
2670 p->zAttachSrc, p->zAttachName, p->zAttachTarget);
2671 }else{
2672 zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
2673 p->zAttachName, p->zAttachTarget);
2674 }
2675 }else if( 'e' == attachToType ){
2676 if( isAdd ){
2677 zComment = mprintf(
2678 "Add attachment [/artifact/%!S|%h] to tech note [/technote/%!S|%S]",
2679 p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2680 }else{
2681 zComment = mprintf(
2682 "Delete attachment \"/artifact/%!S|%h\" from"
2683 " tech note [/technote/%!S|%S]",
2684 p->zAttachName, p->zAttachName,
2685 p->zAttachTarget,p->zAttachTarget);
2686 }
2687 }else{
2688 if( isAdd ){
2689 zComment = mprintf(
2690 "Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]",
2691 p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2692 }else{
2693 zComment = mprintf("Delete attachment \"%h\" from ticket [%!S|%S]",
2694 p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2695 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2696 }
2697 assert( manifest_event_triggers_are_enabled );
2698 db_multi_exec(
2699 "REPLACE INTO event(type,mtime,objid,user,comment)"
2700 "VALUES('%c',%.17g,%d,%Q,%Q)",
2701
--- src/manifest.c
+++ src/manifest.c
@@ -33,11 +33,12 @@
33 #define CFTYPE_CLUSTER 2
34 #define CFTYPE_CONTROL 3
35 #define CFTYPE_WIKI 4
36 #define CFTYPE_TICKET 5
37 #define CFTYPE_ATTACHMENT 6
38 #define CFTYPE_EVENT 7 /* older name for CFTYPE_TECHNOTE */
39 #define CFTYPE_TECHNOTE 7
40 #define CFTYPE_FORUM 8
41
42 /*
43 ** File permissions used by Fossil internally.
44 */
@@ -2630,26 +2631,11 @@
2631 db_finalize(&qatt);
2632 }
2633 if( p->type==CFTYPE_ATTACHMENT ){
2634 char *zComment = 0;
2635 const char isAdd = (p->zAttachSrc && p->zAttachSrc[0]) ? 1 : 0;
2636 char attachToType = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2637 db_multi_exec(
2638 "INSERT INTO attachment(attachid, mtime, src, target,"
2639 "filename, comment, user)"
2640 "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
2641 rid, p->rDate, p->zAttachSrc, p->zAttachTarget, p->zAttachName,
@@ -2661,40 +2647,76 @@
2647 " WHERE target=%Q AND filename=%Q))"
2648 " WHERE target=%Q AND filename=%Q",
2649 p->zAttachTarget, p->zAttachName,
2650 p->zAttachTarget, p->zAttachName
2651 );
2652 switch( attachment_target_type(p->zAttachTarget) ){
2653 case 0:
2654 attachToType = 'a';
2655 if( isAdd ){
2656 zComment = mprintf(
2657 "Add attachment [/artifact/%!S|%h] to [/artifact/%!S|%h]",
2658 p->zAttachSrc, p->zAttachName, p->zAttachTarget);
2659 }else{
2660 zComment = mprintf("Delete attachment \"%h\" from "
2661 "[/artifact/%!S|%h",
2662 p->zAttachName, p->zAttachTarget);
2663 }
2664 break;
2665 case CFTYPE_WIKI:
2666 attachToType = 'w';
2667 if( isAdd ){
2668 zComment = mprintf(
2669 "Add attachment [/artifact/%!S|%h] to wiki page [%h]",
2670 p->zAttachSrc, p->zAttachName, p->zAttachTarget);
2671 }else{
2672 zComment = mprintf("Delete attachment \"%h\" from "
2673 "wiki page [%h]",
2674 p->zAttachName, p->zAttachTarget);
2675 }
2676 break;
2677 case CFTYPE_EVENT:
2678 attachToType = 'e';
2679 if( isAdd ){
2680 zComment = mprintf(
2681 "Add attachment [/artifact/%!S|%h] to tech note "
2682 "[/technote/%!S|%S]",
2683 p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2684 }else{
2685 zComment = mprintf(
2686 "Delete attachment \"/artifact/%!S|%h\" from"
2687 " tech note [/technote/%!S|%S]",
2688 p->zAttachName, p->zAttachName,
2689 p->zAttachTarget,p->zAttachTarget);
2690 }
2691 break;
2692 case CFTYPE_TICKET:
2693 attachToType = 't';
2694 if( isAdd ){
2695 zComment = mprintf(
2696 "Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]",
2697 p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2698 }else{
2699 zComment = mprintf(
2700 "Delete attachment \"%h\" from ticket [%!S|%S]",
2701 p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2702 }
2703 break;
2704 case CFTYPE_FORUM:
2705 attachToType = 'f';
2706 if( isAdd ){
2707 zComment = mprintf(
2708 "Add attachment [/artifact/%!S|%h] to forum post "
2709 "[/forumpost/%!S|%S]",
2710 p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2711 }else{
2712 zComment = mprintf(
2713 "Delete attachment \"%h\" from forum post "
2714 "[/forumpost/%!S|%S]",
2715 p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2716 }
2717 break;
2718 }
2719 assert( manifest_event_triggers_are_enabled );
2720 db_multi_exec(
2721 "REPLACE INTO event(type,mtime,objid,user,comment)"
2722 "VALUES('%c',%.17g,%d,%Q,%Q)",
2723

Keyboard Shortcuts

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