Fossil

Check-in [01c0180b]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add the ability to delete attachments and fix issues with timelines.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | experimental
Files: files | file ages | folders
SHA1: 01c0180b3f084975f5fbb9f251681df02f694fb5
User & Date: drh 2010-03-18 02:11:47
Context
2010-03-18
10:39
Show only non-empty wiki pages in the list of all wiki pages. A rebuild is required after updating in order for this feature to work. check-in: 012d5e4f user: drh tags: experimental
02:11
Add the ability to delete attachments and fix issues with timelines. check-in: 01c0180b user: drh tags: experimental
01:09
Show attachment in timelines. check-in: 2978e430 user: drh tags: experimental
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/attach.c.

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
..
83
84
85
86
87
88
89


90

91





92
93
94
95
96
97
98
99
100
101
102



103

104
105
106
107
108
109
110
...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
...
173
174
175
176
177
178
179

180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
...
213
214
215
216
217
218
219




220
221
222
223
224
225
226
...
268
269
270
271
272
273
274
275
276
277
278














































































    if( g.okRdTkt==0 && g.okRdWiki==0 ) login_needed();
    style_header("All Attachments");
  }
  blob_appendf(&sql, " ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_str(&sql));
  while( db_step(&q)==SQLITE_ROW ){
    const char *zDate = db_column_text(&q, 0);
    /* const char *zSrc = db_column_text(&q, 1); */
    const char *zTarget = db_column_text(&q, 2);
    const char *zFilename = db_column_text(&q, 3);
    const char *zComment = db_column_text(&q, 4);
    const char *zUser = db_column_text(&q, 5);
    int i;
    char *zUrlTail;
    for(i=0; zFilename[i]; i++){
................................................................................
      zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename);
    }else{
      zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
    }
    @
    @ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
    @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br>


    @ %w(zComment)<br>

    if( zPage==0 && zTkt==0 ){





      if( strlen(zTarget)==UUID_SIZE && validate16(zTarget, UUID_SIZE) ){
        char zShort[20];
        memcpy(zShort, zTarget, 10);
        zShort[10] = 0;
        @ Added to ticket <a href="%s(g.zTop)/tktview?name=%s(zTarget)">
        @ %s(zShort)</a>
      }else{
        @ Added to wiki page <a href="%s(g.zTop)/wiki?name=%t(zTarget)">
        @ %h(zTarget)</a>
      }
    }else{



      @ Add

    }
    @ by %h(zUser) on
    hyperlink_to_date(zDate, ".");
    free(zUrlTail);
  }
  db_finalize(&q);
  style_footer();
................................................................................
    zUUID = db_text(0,
       "SELECT coalesce(src,'x') FROM attachment"
       " WHERE target=%Q AND filename=%Q"
       " ORDER BY mtime DESC LIMIT 1",
       zTarget, zFile
    );
  }
  if( zUUID==0 ){
    style_header("No Such Attachment");
    @ No such attachment....
    style_footer();
    return;
  }else if( zUUID[0]=='x' ){
    style_header("Missing");
    @ Attachment has been deleted
................................................................................
  if( strcmp(g.zPath,"attachview")==0 ){
    artifact_page();
  }else{
    cgi_replace_parameter("m", mimetype_from_name(zFile));
    rawartifact_page();
  }
}


/*
** WEBPAGE: attachadd
**
**    tkt=TICKETUUID
**    page=WIKIPAGE
**    from=URL
**
** Add a new attachment.
*/
void attachadd_page(void){
  const char *zPage = P("page");
  const char *zTkt = P("tkt");
  const char *zFrom = PD("from", "/home");
  const char *aContent = P("f");
  const char *zName = PD("f:filename","unknown");
  const char *zTarget;
  const char *zTargetType;
  int szContent = atoi(PD("f:bytes","0"));

  if( P("cancel") ) cgi_redirect(zFrom);
................................................................................
    if( g.okApndTkt==0 || g.okAttach==0 ) login_needed();
    if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", zTkt) ){
      fossil_redirect_home();
    }
    zTarget = zTkt;
    zTargetType = mprintf("Ticket <a href=\"%s/tktview?name=%.10s\">%.10s</a>",
                          g.zTop, zTkt, zTkt);




  }
  if( P("ok") && szContent>0 ){
    Blob content;
    Blob manifest;
    Blob cksum;
    char *zUUID;
    const char *zComment;
................................................................................
  if( zTkt ){
    @ <input type="hidden" name="tkt" value="%h(zTkt)">
  }else{
    @ <input type="hidden" name="page" value="%h(zPage)">
  }
  @ <input type="hidden" name="from" value="%h(zFrom)">
  @ <input type="submit" name="ok" value="Add Attachment">
  @ <input type="submit" name="can" value="Cancel">
  @ </form>
  style_footer();
}





















































































|







 







>
>
|
>

>
>
>
>
>




|


|



>
>
>
|
>







 







|







 







>













|







 







>
>
>
>







 







|



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
..
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
...
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
...
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
...
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
...
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
    if( g.okRdTkt==0 && g.okRdWiki==0 ) login_needed();
    style_header("All Attachments");
  }
  blob_appendf(&sql, " ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_str(&sql));
  while( db_step(&q)==SQLITE_ROW ){
    const char *zDate = db_column_text(&q, 0);
    const char *zSrc = db_column_text(&q, 1);
    const char *zTarget = db_column_text(&q, 2);
    const char *zFilename = db_column_text(&q, 3);
    const char *zComment = db_column_text(&q, 4);
    const char *zUser = db_column_text(&q, 5);
    int i;
    char *zUrlTail;
    for(i=0; zFilename[i]; i++){
................................................................................
      zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename);
    }else{
      zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
    }
    @
    @ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
    @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br>
    if( zComment ) while( isspace(zComment[0]) ) zComment++;
    if( zComment && zComment[0] ){
      @ %w(zComment)<br>
    }
    if( zPage==0 && zTkt==0 ){
      if( zSrc==0 || zSrc[0]==0 ){
        zSrc = "Deleted from";
      }else {
        zSrc = "Added to";
      }
      if( strlen(zTarget)==UUID_SIZE && validate16(zTarget, UUID_SIZE) ){
        char zShort[20];
        memcpy(zShort, zTarget, 10);
        zShort[10] = 0;
        @ %s(zSrc) ticket <a href="%s(g.zTop)/tktview?name=%s(zTarget)">
        @ %s(zShort)</a>
      }else{
        @ %s(zSrc) wiki page <a href="%s(g.zTop)/wiki?name=%t(zTarget)">
        @ %h(zTarget)</a>
      }
    }else{
      if( zSrc==0 || zSrc[0]==0 ){
        @ Deleted
      }else {
        @ Added
      }
    }
    @ by %h(zUser) on
    hyperlink_to_date(zDate, ".");
    free(zUrlTail);
  }
  db_finalize(&q);
  style_footer();
................................................................................
    zUUID = db_text(0,
       "SELECT coalesce(src,'x') FROM attachment"
       " WHERE target=%Q AND filename=%Q"
       " ORDER BY mtime DESC LIMIT 1",
       zTarget, zFile
    );
  }
  if( zUUID==0 || zUUID[0]==0 ){
    style_header("No Such Attachment");
    @ No such attachment....
    style_footer();
    return;
  }else if( zUUID[0]=='x' ){
    style_header("Missing");
    @ Attachment has been deleted
................................................................................
  if( strcmp(g.zPath,"attachview")==0 ){
    artifact_page();
  }else{
    cgi_replace_parameter("m", mimetype_from_name(zFile));
    rawartifact_page();
  }
}


/*
** WEBPAGE: attachadd
**
**    tkt=TICKETUUID
**    page=WIKIPAGE
**    from=URL
**
** Add a new attachment.
*/
void attachadd_page(void){
  const char *zPage = P("page");
  const char *zTkt = P("tkt");
  const char *zFrom = P("from");
  const char *aContent = P("f");
  const char *zName = PD("f:filename","unknown");
  const char *zTarget;
  const char *zTargetType;
  int szContent = atoi(PD("f:bytes","0"));

  if( P("cancel") ) cgi_redirect(zFrom);
................................................................................
    if( g.okApndTkt==0 || g.okAttach==0 ) login_needed();
    if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", zTkt) ){
      fossil_redirect_home();
    }
    zTarget = zTkt;
    zTargetType = mprintf("Ticket <a href=\"%s/tktview?name=%.10s\">%.10s</a>",
                          g.zTop, zTkt, zTkt);
  }
  if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
  if( P("cancel") ){
    cgi_redirect(zFrom);
  }
  if( P("ok") && szContent>0 ){
    Blob content;
    Blob manifest;
    Blob cksum;
    char *zUUID;
    const char *zComment;
................................................................................
  if( zTkt ){
    @ <input type="hidden" name="tkt" value="%h(zTkt)">
  }else{
    @ <input type="hidden" name="page" value="%h(zPage)">
  }
  @ <input type="hidden" name="from" value="%h(zFrom)">
  @ <input type="submit" name="ok" value="Add Attachment">
  @ <input type="submit" name="cancel" value="Cancel">
  @ </form>
  style_footer();
}


/*
** WEBPAGE: attachdelete
**
**    tkt=TICKETUUID
**    page=WIKIPAGE
**    file=FILENAME
**
** "Delete" an attachment.  Because objects in Fossil are immutable
** the attachment isn't really deleted.  Instead, we change the content
** of the attachment to NULL, which the system understands as being
** deleted.  Historical values of the attachment are preserved.
*/
void attachdel_page(void){
  const char *zPage = P("page");
  const char *zTkt = P("tkt");
  const char *zFile = P("file");
  const char *zFrom = P("from");
  const char *zTarget;

  if( zPage && zTkt ) fossil_redirect_home();
  if( zPage==0 && zTkt==0 ) fossil_redirect_home();
  if( zFile==0 ) fossil_redirect_home();
  login_check_credentials();
  if( zPage ){
    if( g.okWrWiki==0 || g.okAttach==0 ) login_needed();
    zTarget = zPage;
  }else{
    if( g.okWrTkt==0 || g.okAttach==0 ) login_needed();
    zTarget = zTkt;
  }
  if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
  if( P("cancel") ){
    cgi_redirect(zFrom);
  }
  if( P("confirm") ){
    int i, n, rid;
    char *zDate;
    Blob manifest;
    Blob cksum;

    db_begin_transaction();
    blob_zero(&manifest);
    for(i=n=0; zFile[i]; i++){
      if( zFile[i]=='/' || zFile[i]=='\\' ) n = i;
    }
    zFile += n;
    if( zFile[0]==0 ) zFile = "unknown";
    blob_appendf(&manifest, "A %F %F\n", zFile, zTarget);
    zDate = db_text(0, "SELECT datetime('now')");
    zDate[10] = 'T';
    blob_appendf(&manifest, "D %s\n", zDate);
    blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody");
    md5sum_blob(&manifest, &cksum);
    blob_appendf(&manifest, "Z %b\n", &cksum);
    rid = content_put(&manifest, 0, 0);
    manifest_crosslink(rid, &manifest);
    db_end_transaction(0);
    cgi_redirect(zFrom);
  }    
  style_header("Delete Attachment");
  @ <form action="%s(g.zBaseURL)/attachdelete" method="POST">
  @ <p>Confirm that you want to delete the attachment named
  @ "%h(zFile)" on %s(zTkt?"ticket":"wiki page") %h(zTarget):<br>
  if( zTkt ){
    @ <input type="hidden" name="tkt" value="%h(zTkt)">
  }else{
    @ <input type="hidden" name="page" value="%h(zPage)">
  }
  @ <input type="hidden" name="file" value="%h(zFile)">
  @ <input type="hidden" name="from" value="%h(zFrom)">
  @ <input type="submit" name="confirm" value="Delete">
  @ <input type="submit" name="cancel" value="Cancel">
  @ </form>
  style_footer();

}

Changes to src/manifest.c.

1134
1135
1136
1137
1138
1139
1140
1141
1142


1143
1144

1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156















1157
1158
1159
1160
1161
1162
1163
       " WHERE target=%Q AND filename=%Q",
       m.zAttachTarget, m.zAttachName,
       m.zAttachTarget, m.zAttachName
    );
    if( strlen(m.zAttachTarget)!=UUID_SIZE
     || !validate16(m.zAttachTarget, UUID_SIZE) 
    ){
      char *zTag = mprintf("wiki-%s", m.zAttachTarget);
      char *zComment;


      tag_findid(zTag, 1);
      free(zTag);

      if( m.zAttachSrc ){
        zComment = mprintf("Add attachment \"%h\" to wiki page [%h]",
             m.zAttachName, m.zAttachTarget);
      }else{
        zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
             m.zAttachName, m.zAttachTarget);
      }
      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment)"
        "VALUES('w',%.17g,%d,%Q,%Q)",
        m.rDate, rid, m.zUser, zComment
      );















      free(zComment);
    }
  }
  db_end_transaction(0);
  manifest_clear(&m);
  return 1;
}







<

>
>


>
|











>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1134
1135
1136
1137
1138
1139
1140

1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
       " WHERE target=%Q AND filename=%Q",
       m.zAttachTarget, m.zAttachName,
       m.zAttachTarget, m.zAttachName
    );
    if( strlen(m.zAttachTarget)!=UUID_SIZE
     || !validate16(m.zAttachTarget, UUID_SIZE) 
    ){

      char *zComment;
#if 0
      char *zTag = mprintf("wiki-%s", m.zAttachTarget);
      tag_findid(zTag, 1);
      free(zTag);
#endif
      if( m.zAttachSrc && m.zAttachSrc[0] ){
        zComment = mprintf("Add attachment \"%h\" to wiki page [%h]",
             m.zAttachName, m.zAttachTarget);
      }else{
        zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
             m.zAttachName, m.zAttachTarget);
      }
      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment)"
        "VALUES('w',%.17g,%d,%Q,%Q)",
        m.rDate, rid, m.zUser, zComment
      );
      free(zComment);
    }else{
      char *zComment;
      if( m.zAttachSrc && m.zAttachSrc[0] ){
        zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]",
             m.zAttachName, m.zAttachTarget);
      }else{
        zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]",
             m.zAttachName, m.zAttachTarget);
      }
      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment)"
        "VALUES('t',%.17g,%d,%Q,%Q)",
        m.rDate, rid, m.zUser, zComment
      );
      free(zComment);
    }
  }
  db_end_transaction(0);
  manifest_clear(&m);
  return 1;
}

Changes to src/tkt.c.

240
241
242
243
244
245
246

247
248
249
250
251
252
253
...
269
270
271
272
273
274
275

276
277
278
279
280
281
282
...
347
348
349
350
351
352
353

354
355
356
357
358
359
360
361
...
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388



389
390
391
392
393
394
395
...
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
...
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
    ticket_insert(&manifest, createFlag, rid);
    manifest_ticket_event(rid, &manifest, createFlag, tagid);
    manifest_clear(&manifest);
    createFlag = 0;
  }
  db_finalize(&q);


  db_prepare(&q,
    "SELECT attachid, mtime, src IS NULL, filename, user"
    "  FROM attachment"
    " WHERE target=%Q",
    zTktUuid
  );
  while( db_step(&q)==SQLITE_ROW ){
................................................................................
      "REPLACE INTO event(type,tagid,mtime,objid,user,comment,brief)"
      "VALUES('t',%d,%.17g,%d,%Q,%Q,%Q)",
      tagid, mtime, attachid, zUser, zCom, zCom
    );
    free(zCom);
  }
  db_finalize(&q);

}

/*
** Create the subscript interpreter and load the "common" code.
*/
void ticket_init(void){
  const char *zConfig;
................................................................................
  }
  if( g.okNewTkt ){
    style_submenu_element("New Ticket", "Create a new ticket",
        "%s/tktnew", g.zTop);
  }
  if( g.okApndTkt && g.okAttach ){
    style_submenu_element("Attach", "Add An Attachment",

        "%s/attachadd?tkt=%T", g.zTop, zUuid);
  }
  style_header("View Ticket");
  if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
  ticket_init();
  initializeVariablesFromDb();
  zScript = ticket_viewpage_code();
  if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1);
................................................................................
       " WHERE tkt_uuid GLOB '%q*'", zUuid);
  if( zFullName ){
    int cnt = 0;
    Stmt q;
    db_prepare(&q,
       "SELECT datetime(mtime,'localtime'), filename, user"
       "  FROM attachment"
       " WHERE isLatest AND src NOT NULL AND target=%Q"
       " ORDER BY mtime DESC",
       zFullName);
    while( db_step(&q)==SQLITE_ROW ){
      const char *zDate = db_column_text(&q, 0);
      const char *zFile = db_column_text(&q, 1);
      const char *zUser = db_column_text(&q, 2);
      if( cnt==0 ){
        @ <hr><h2>Attachments:</h2>
        @ <ul>
      }
      cnt++;
      @ <li><a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&file=%t(zFile)">
      @ %h(zFile)</a> add by %h(zUser) on
      hyperlink_to_date(zDate, ".</li>");



    }
    if( cnt ){
      @ </ul>
    }
    db_finalize(&q);
  }
 
................................................................................
    "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
    "  FROM event, blob"
    " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
    "   AND blob.rid=event.objid"
    " UNION "
    "SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user"
    "  FROM attachment, blob"
    " WHERE isLatest AND target=(SELECT substr(tagname,5) FROM tag"
                                " WHERE tagid=%d)"
    "   AND blob.rid=attachid"
    " ORDER BY 1 DESC",
    tagid, tagid
  );
  while( db_step(&q)==SQLITE_ROW ){
    Blob content;
    Manifest m;
................................................................................
    const char *zChngUuid = db_column_text(&q, 2);
    const char *zFile = db_column_text(&q, 4);
    memcpy(zShort, zChngUuid, 10);
    zShort[10] = 0;
    if( zFile!=0 ){
      const char *zSrc = db_column_text(&q, 3);
      const char *zUser = db_column_text(&q, 5);
      if( zSrc==0 ){
        @ 
        @ <p>Delete attachment "%h(zFile)"
      }else{
        @ 
        @ <p>Add attachment "%h(zFile)"
      }
      @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]







>







 







>







 







>
|







 







|













|
>
>
>







 







|
<







 







|







240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
...
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
...
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
...
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
...
769
770
771
772
773
774
775
776

777
778
779
780
781
782
783
...
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
    ticket_insert(&manifest, createFlag, rid);
    manifest_ticket_event(rid, &manifest, createFlag, tagid);
    manifest_clear(&manifest);
    createFlag = 0;
  }
  db_finalize(&q);

#if 0
  db_prepare(&q,
    "SELECT attachid, mtime, src IS NULL, filename, user"
    "  FROM attachment"
    " WHERE target=%Q",
    zTktUuid
  );
  while( db_step(&q)==SQLITE_ROW ){
................................................................................
      "REPLACE INTO event(type,tagid,mtime,objid,user,comment,brief)"
      "VALUES('t',%d,%.17g,%d,%Q,%Q,%Q)",
      tagid, mtime, attachid, zUser, zCom, zCom
    );
    free(zCom);
  }
  db_finalize(&q);
#endif
}

/*
** Create the subscript interpreter and load the "common" code.
*/
void ticket_init(void){
  const char *zConfig;
................................................................................
  }
  if( g.okNewTkt ){
    style_submenu_element("New Ticket", "Create a new ticket",
        "%s/tktnew", g.zTop);
  }
  if( g.okApndTkt && g.okAttach ){
    style_submenu_element("Attach", "Add An Attachment",
        "%s/attachadd?tkt=%T&from=%s/tktview%%3fname=%t",
        g.zTop, zUuid, g.zTop, zUuid);
  }
  style_header("View Ticket");
  if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
  ticket_init();
  initializeVariablesFromDb();
  zScript = ticket_viewpage_code();
  if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1);
................................................................................
       " WHERE tkt_uuid GLOB '%q*'", zUuid);
  if( zFullName ){
    int cnt = 0;
    Stmt q;
    db_prepare(&q,
       "SELECT datetime(mtime,'localtime'), filename, user"
       "  FROM attachment"
       " WHERE isLatest AND src!='' AND target=%Q"
       " ORDER BY mtime DESC",
       zFullName);
    while( db_step(&q)==SQLITE_ROW ){
      const char *zDate = db_column_text(&q, 0);
      const char *zFile = db_column_text(&q, 1);
      const char *zUser = db_column_text(&q, 2);
      if( cnt==0 ){
        @ <hr><h2>Attachments:</h2>
        @ <ul>
      }
      cnt++;
      @ <li><a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&file=%t(zFile)">
      @ %h(zFile)</a> add by %h(zUser) on
      hyperlink_to_date(zDate, ".");
      if( g.okWrTkt && g.okAttach ){
        @ [<a href="%s(g.zTop)/attachdelete?tkt=%s(zFullName)&file=%t(zFile)&from=%s(g.zTop)/tktview%%3fname=%s(zFullName)">delete</a>]
      }
    }
    if( cnt ){
      @ </ul>
    }
    db_finalize(&q);
  }
 
................................................................................
    "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
    "  FROM event, blob"
    " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
    "   AND blob.rid=event.objid"
    " UNION "
    "SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user"
    "  FROM attachment, blob"
    " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"

    "   AND blob.rid=attachid"
    " ORDER BY 1 DESC",
    tagid, tagid
  );
  while( db_step(&q)==SQLITE_ROW ){
    Blob content;
    Manifest m;
................................................................................
    const char *zChngUuid = db_column_text(&q, 2);
    const char *zFile = db_column_text(&q, 4);
    memcpy(zShort, zChngUuid, 10);
    zShort[10] = 0;
    if( zFile!=0 ){
      const char *zSrc = db_column_text(&q, 3);
      const char *zUser = db_column_text(&q, 5);
      if( zSrc==0 || zSrc[0]==0 ){
        @ 
        @ <p>Delete attachment "%h(zFile)"
      }else{
        @ 
        @ <p>Add attachment "%h(zFile)"
      }
      @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]

Changes to src/wiki.c.

189
190
191
192
193
194
195
196

197
198
199
200
201
202
203
...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229



230
231
232
233
234
235
236
...
545
546
547
548
549
550
551

552

553
554
555
556
557
558
559
  if( !g.isHome ){
    if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
      style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
           g.zTop, zPageName);
    }
    if( rid && g.okWrWiki && g.okAttach ){
      style_submenu_element("Attach", "Add An Attachment",
           "%s/attachadd?page=%T", g.zTop, zPageName);

    }
    if( rid && g.okApndWiki ){
      style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
           g.zTop, zPageName);
    }
    if( g.okHistory ){
      style_submenu_element("History", "History", "%s/whistory?name=%T",
................................................................................
  blob_init(&wiki, zBody, -1);
  wiki_convert(&wiki, 0, 0);
  blob_reset(&wiki);

  db_prepare(&q,
     "SELECT datetime(mtime,'localtime'), filename, user"
     "  FROM attachment"
     " WHERE isLatest AND src NOT NULL AND target=%Q"
     " ORDER BY mtime DESC",
     zPageName);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zDate = db_column_text(&q, 0);
    const char *zFile = db_column_text(&q, 1);
    const char *zUser = db_column_text(&q, 2);
    if( cnt==0 ){
      @ <hr><h2>Attachments:</h2>
      @ <ul>
    }
    cnt++;
    @ <li><a href="%s(g.zTop)/attachview?page=%s(zPageName)&file=%t(zFile)">
    @ %h(zFile)</a> add by %h(zUser) on
    hyperlink_to_date(zDate, ".</li>");



  }
  if( cnt ){
    @ </ul>
  }
  db_finalize(&q);
 
  if( !isSandbox ){
................................................................................
static const char *zWikiPageName;

/*
** Function called to output extra text at the end of each line in
** a wiki history listing.
*/
static void wiki_history_extra(int rid){

  @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>

}

/*
** WEBPAGE: whistory
** URL: /whistory?name=PAGENAME
**
** Show the complete change history for a single wiki page.







|
>







 







|













|
>
>
>







 







>
|
>







189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
...
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
...
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
  if( !g.isHome ){
    if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
      style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
           g.zTop, zPageName);
    }
    if( rid && g.okWrWiki && g.okAttach ){
      style_submenu_element("Attach", "Add An Attachment",
           "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T",
           g.zTop, zPageName, g.zTop, zPageName);
    }
    if( rid && g.okApndWiki ){
      style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
           g.zTop, zPageName);
    }
    if( g.okHistory ){
      style_submenu_element("History", "History", "%s/whistory?name=%T",
................................................................................
  blob_init(&wiki, zBody, -1);
  wiki_convert(&wiki, 0, 0);
  blob_reset(&wiki);

  db_prepare(&q,
     "SELECT datetime(mtime,'localtime'), filename, user"
     "  FROM attachment"
     " WHERE isLatest AND src!='' AND target=%Q"
     " ORDER BY mtime DESC",
     zPageName);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zDate = db_column_text(&q, 0);
    const char *zFile = db_column_text(&q, 1);
    const char *zUser = db_column_text(&q, 2);
    if( cnt==0 ){
      @ <hr><h2>Attachments:</h2>
      @ <ul>
    }
    cnt++;
    @ <li><a href="%s(g.zTop)/attachview?page=%s(zPageName)&file=%t(zFile)">
    @ %h(zFile)</a> add by %h(zUser) on
    hyperlink_to_date(zDate, ".");
    if( g.okWrWiki && g.okAttach ){
      @ [<a href="%s(g.zTop)/attachdelete?page=%s(zPageName)&file=%t(zFile)&from=%s(g.zTop)/wiki%%3fname=%s(zPageName)">delete</a>]
    }
  }
  if( cnt ){
    @ </ul>
  }
  db_finalize(&q);
 
  if( !isSandbox ){
................................................................................
static const char *zWikiPageName;

/*
** Function called to output extra text at the end of each line in
** a wiki history listing.
*/
static void wiki_history_extra(int rid){
  if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d", rid) ){
    @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
  }
}

/*
** WEBPAGE: whistory
** URL: /whistory?name=PAGENAME
**
** Show the complete change history for a single wiki page.