Fossil

Check-in [aeaef8fb]
Login

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

Overview
Comment:add TECHNOTEID to fossil wiki commit and allow creation of multiple tech notes with the same timestamp (as already permitted by the GUI).
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | technoteattachcli
Files: files | file ages | folders
SHA1:aeaef8fbb1d8c1a289e9c68cd0d45646ed08091c
User & Date: dave.vines 2016-04-18 08:17:57
Context
2016-04-18
13:36
add TECHNOTEID to fossil attach command check-in: 70131d08 user: dave.vines tags: technoteattachcli
08:17
add TECHNOTEID to fossil wiki commit and allow creation of multiple tech notes with the same timestamp (as already permitted by the GUI). check-in: aeaef8fb user: dave.vines tags: technoteattachcli
2016-04-12
19:14
add TECHNOTEID to fossil wiki export. Rename --show-artifact-ids to --show-technote-ids on fossil wiki list (and ignore option for wiki pages as the page name uniquely identifies wiki page name). Rename done as tech note ids are not artifact ids (e.g. they cannot be used on fossil artifact) check-in: f486d0f0 user: dave.vines tags: technoteattachcli
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/event.c.

536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
  @ </td></tr></table>
  @ </div></form>
  style_footer();
}

/*
** Add a new tech note to the repository.  The timestamp is
** given by the zETime parameter.  isNew must be true to create
** a new page.  If no previous page with the name zPageName exists
** and isNew is false, then this routine throws an error.
*/
void event_cmd_commit(
  char *zETime,             /* timestamp */
  int isNew,                /* true to create a new page */
  Blob *pContent,           /* content of the new page */
  const char *zMimeType,    /* mimetype of the content */
  const char *zComment,     /* comment to go on the timeline */
  const char *zTags,        /* tags */
  const char *zClr          /* background color */
){
  int rid;                /* Artifact id of the tech note */
  const char *zId;        /* id of the tech note */
  rid = db_int(0, "SELECT objid FROM event"
        " WHERE datetime(mtime)=datetime('%q') AND type = 'e'"
           " AND tagid IS NOT NULL"
        " ORDER BY mtime DESC"
        " LIMIT 1",
        zETime
  );
  if( rid==0 && !isNew ){
#ifdef FOSSIL_ENABLE_JSON
    g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND;
#endif
    fossil_fatal("no such tech note: %s", zETime);
  }
  if( rid!=0 && isNew ){
#ifdef FOSSIL_ENABLE_JSON
    g.json.resultCode = FSL_JSON_E_RESOURCE_ALREADY_EXISTS;
#endif
    fossil_fatal("tech note %s already exists", zETime);
  }

  if ( isNew ){
    zId = db_text(0, "SELECT lower(hex(randomblob(20)))");
  }else{
    zId = db_text(0,
      "SELECT substr(tagname,7) FROM tag"
      " WHERE tagid=(SELECT tagid FROM event WHERE objid='%d')",
      rid
    );







|





|






<

<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
|







536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555

556












557







558
559
560
561
562
563
564
565
  @ </td></tr></table>
  @ </div></form>
  style_footer();
}

/*
** Add a new tech note to the repository.  The timestamp is
** given by the zETime parameter.  rid must be zero to create
** a new page.  If no previous page with the name zPageName exists
** and isNew is false, then this routine throws an error.
*/
void event_cmd_commit(
  char *zETime,             /* timestamp */
  int   rid,                /* Artifact id of the tech note */
  Blob *pContent,           /* content of the new page */
  const char *zMimeType,    /* mimetype of the content */
  const char *zComment,     /* comment to go on the timeline */
  const char *zTags,        /* tags */
  const char *zClr          /* background color */
){

  const char *zId;        /* id of the tech note */




















  if ( rid==0 ){
    zId = db_text(0, "SELECT lower(hex(randomblob(20)))");
  }else{
    zId = db_text(0,
      "SELECT substr(tagname,7) FROM tag"
      " WHERE tagid=(SELECT tagid FROM event WHERE objid='%d')",
      rid
    );

Changes to src/json_wiki.c.

373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
  contentLen = (int)cson_string_length_bytes(jstr);
  if(contentLen){
    blob_append(&content, cson_string_cstr(jstr),contentLen);
  }

  zMimeType = json_find_option_cstr("mimetype","mimetype","M");

  wiki_cmd_commit(zPageName, 0==rid, &content, zMimeType, 0);
  blob_reset(&content);
  /*
    Our return value here has a race condition: if this operation
    is called concurrently for the same wiki page via two requests,
    payV could reflect the results of the other save operation.
  */
  payV = json_get_wiki_page_by_name(







|







373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
  contentLen = (int)cson_string_length_bytes(jstr);
  if(contentLen){
    blob_append(&content, cson_string_cstr(jstr),contentLen);
  }

  zMimeType = json_find_option_cstr("mimetype","mimetype","M");

  wiki_cmd_commit(zPageName, rid, &content, zMimeType, 0);
  blob_reset(&content);
  /*
    Our return value here has a race condition: if this operation
    is called concurrently for the same wiki page via two requests,
    payV could reflect the results of the other save operation.
  */
  payV = json_get_wiki_page_by_name(

Changes to src/wiki.c.

1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
....
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
....
1202
1203
1204
1205
1206
1207
1208




1209
1210
1211
1212
1213
1214
1215
1216
....
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
















1341

1342
1343
1344
1345
1346
1347
1348
1349

1350
1351
1352
1353
1354
1355

1356
1357
1358
1359
1360
1361
1362



1363
1364
1365
1366
1367
1368
1369
  @ through the matching &lt;/verbatim&gt;.</p></li>
  @ </ol>
  style_footer();
}

/*
** Add a new wiki page to the repository.  The page name is
** given by the zPageName parameter.  isNew must be true to create
** a new page.  If no previous page with the name zPageName exists
** and isNew is false, then this routine throws an error.
**
** The content of the new page is given by the blob pContent.
**
** zMimeType specifies the N-card for the wiki page. If it is 0,
** empty, or "text/x-fossil-wiki" (the default format) then it is
** ignored.
*/
int wiki_cmd_commit(const char *zPageName, int isNew, Blob *pContent,
                    const char *zMimeType, int localUser){
  Blob wiki;              /* Wiki page content */
  Blob cksum;             /* wiki checksum */
  int rid;                /* artifact ID of parent page */
  char *zDate;            /* timestamp */
  char *zUuid;            /* uuid for rid */

  rid = db_int(0,
     "SELECT x.rid FROM tag t, tagxref x"
     " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
     " ORDER BY x.mtime DESC LIMIT 1",
     zPageName
  );
  if( rid==0 && !isNew ){
#ifdef FOSSIL_ENABLE_JSON
    g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND;
#endif
    fossil_fatal("no such wiki page: %s", zPageName);
  }
  if( rid!=0 && isNew ){
#ifdef FOSSIL_ENABLE_JSON
    g.json.resultCode = FSL_JSON_E_RESOURCE_ALREADY_EXISTS;
#endif
    fossil_fatal("wiki page %s already exists", zPageName);
  }

  blob_zero(&wiki);
  zDate = date_in_standard_format("now");
  blob_appendf(&wiki, "D %s\n", zDate);
  free(zDate);
  blob_appendf(&wiki, "L %F\n", zPageName );
  if( zMimeType && *zMimeType
      && 0!=fossil_strcmp(zMimeType,"text/x-fossil-wiki") ){
................................................................................
  wiki_put(&wiki, 0, wiki_need_moderation(localUser));
  db_end_transaction(0);
  return 1;
}

/*
** Determine the rid for a tech note given either its id or its
** timestamp. Returns 0 if there is no such item and-1 if the details 
** are ambiguous and could refer to multiple items
*/
int wiki_technote_to_rid(const char *zETime) {
  int rid=0;                    /* Artifact ID of the tech note */
  int nETime = strlen(zETime);
  Stmt q;
  if( nETime>=4 && nETime<=UUID_SIZE && validate16(zETime, nETime) ){
    char zUuid[UUID_SIZE+1];
................................................................................
**       Options:
**         -M|--mimetype TEXT-FORMAT   The mime type of the update.
**                                     Defaults to the type used by
**                                     the previous version of the
**                                     page, or text/x-fossil-wiki.
**         -t|--technote DATETIME      Specifies the timestamp of
**                                     the technote to be created or




**                                     updated.
**         --technote-tags TAGS        The set of tags for a technote.
**         --technote-bgcolor COLOR    The color used for the technote
**                                     on the timeline.
**
**    %fossil wiki list ?OPTIONS?
**    %fossil wiki ls ?OPTIONS?
**
................................................................................
                     zPageName
                     );
        if(rid>0 && (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0
           && (pWiki->zMimetype && *pWiki->zMimetype)){
          zMimeType = pWiki->zMimetype;
        }
      }else{
        rid = db_int(0, "SELECT objid FROM event"
                     " WHERE datetime(mtime)=datetime('%q') AND type='e'"
                        " AND tagid IS NOT NULL"
                     " ORDER BY mtime DESC LIMIT 1",
                     zPageName
                     );
        if(rid>0 && (pWiki = manifest_get(rid, CFTYPE_EVENT, 0))!=0
           && (pWiki->zMimetype && *pWiki->zMimetype)){
          zMimeType = pWiki->zMimetype;
        }
      }
    }
















    if( !zETime ){

      if( g.argv[2][1]=='r' ){
        wiki_cmd_commit(zPageName, 1, &content, zMimeType, 1);
        fossil_print("Created new wiki page %s.\n", zPageName);
      }else{
        wiki_cmd_commit(zPageName, 0, &content, zMimeType, 1);
        fossil_print("Updated wiki page %s.\n", zPageName);
      }
    }else{

      char *zMETime;          /* Normalized, mutable version of zETime */
      zMETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))",
                        zETime);
      if( g.argv[2][1]=='r' ){
        event_cmd_commit(zMETime, 1, &content, zMimeType, zPageName,
                         zTags, zClr);

        fossil_print("Created new tech note %s.\n", zMETime);
      }else{
        event_cmd_commit(zMETime, 0, &content, zMimeType, zPageName,
                         zTags, zClr);
        fossil_print("Updated tech note %s.\n", zMETime);
      }
      free(zMETime);



    }
    manifest_destroy(pWiki);
    blob_reset(&content);
  }else if( strncmp(g.argv[2],"delete",n)==0 ){
    if( g.argc!=5 ){
      usage("delete PAGENAME");
    }







|
|
<







|



<



<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|
|







 







>
>
>
>
|







 







|
<
<
<
<
<






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

>

<


<



>
|
|
|
<
|

>
|
|
<
<
|
|
|
>
>
>







1066
1067
1068
1069
1070
1071
1072
1073
1074

1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085

1086
1087
1088



















1089
1090
1091
1092
1093
1094
1095
....
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
....
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
....
1305
1306
1307
1308
1309
1310
1311
1312





1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337

1338
1339

1340
1341
1342
1343
1344
1345
1346

1347
1348
1349
1350
1351


1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
  @ through the matching &lt;/verbatim&gt;.</p></li>
  @ </ol>
  style_footer();
}

/*
** Add a new wiki page to the repository.  The page name is
** given by the zPageName parameter.  rid must be zero to create
** a new page otherwise the page identified by rid is updated.

**
** The content of the new page is given by the blob pContent.
**
** zMimeType specifies the N-card for the wiki page. If it is 0,
** empty, or "text/x-fossil-wiki" (the default format) then it is
** ignored.
*/
int wiki_cmd_commit(const char *zPageName, int rid, Blob *pContent,
                    const char *zMimeType, int localUser){
  Blob wiki;              /* Wiki page content */
  Blob cksum;             /* wiki checksum */

  char *zDate;            /* timestamp */
  char *zUuid;            /* uuid for rid */




















  blob_zero(&wiki);
  zDate = date_in_standard_format("now");
  blob_appendf(&wiki, "D %s\n", zDate);
  free(zDate);
  blob_appendf(&wiki, "L %F\n", zPageName );
  if( zMimeType && *zMimeType
      && 0!=fossil_strcmp(zMimeType,"text/x-fossil-wiki") ){
................................................................................
  wiki_put(&wiki, 0, wiki_need_moderation(localUser));
  db_end_transaction(0);
  return 1;
}

/*
** Determine the rid for a tech note given either its id or its
** timestamp. Returns 0 if there is no such item and -1 if the details
** are ambiguous and could refer to multiple items.
*/
int wiki_technote_to_rid(const char *zETime) {
  int rid=0;                    /* Artifact ID of the tech note */
  int nETime = strlen(zETime);
  Stmt q;
  if( nETime>=4 && nETime<=UUID_SIZE && validate16(zETime, nETime) ){
    char zUuid[UUID_SIZE+1];
................................................................................
**       Options:
**         -M|--mimetype TEXT-FORMAT   The mime type of the update.
**                                     Defaults to the type used by
**                                     the previous version of the
**                                     page, or text/x-fossil-wiki.
**         -t|--technote DATETIME      Specifies the timestamp of
**                                     the technote to be created or
**                                     updated. When updating a tech note
**                                     the most recently modified with the
**                                     specified timestamp will be updated.
**         -t|--technote TECHNOTE-ID   Specifies the technote to be
**                                     updated by its technote id.
**         --technote-tags TAGS        The set of tags for a technote.
**         --technote-bgcolor COLOR    The color used for the technote
**                                     on the timeline.
**
**    %fossil wiki list ?OPTIONS?
**    %fossil wiki ls ?OPTIONS?
**
................................................................................
                     zPageName
                     );
        if(rid>0 && (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0
           && (pWiki->zMimetype && *pWiki->zMimetype)){
          zMimeType = pWiki->zMimetype;
        }
      }else{
        rid = wiki_technote_to_rid(zETime);





        if(rid>0 && (pWiki = manifest_get(rid, CFTYPE_EVENT, 0))!=0
           && (pWiki->zMimetype && *pWiki->zMimetype)){
          zMimeType = pWiki->zMimetype;
        }
      }
    }
    if( g.argv[2][1]=='r' && rid>0 ){
      if ( !zETime ){
        fossil_fatal("wiki page %s already exists", zPageName);
      }else{
        /* Creating a tech note with same timestamp is permitted
           and should create a new tech note */
        rid = 0; 
      }
    }else if( g.argv[2][1]=='o' && rid == 0 ){
      if ( !zETime ){
        fossil_fatal("no such wiki page: %s", zPageName);
      }else{
        fossil_fatal("no such tech note: %s", zETime);
      }
    }

    if( !zETime ){
      wiki_cmd_commit(zPageName, rid, &content, zMimeType, 1);
      if( g.argv[2][1]=='r' ){

        fossil_print("Created new wiki page %s.\n", zPageName);
      }else{

        fossil_print("Updated wiki page %s.\n", zPageName);
      }
    }else{
      if( rid != -1 ){
        char *zMETime;          /* Normalized, mutable version of zETime */
        zMETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))",
                          zETime);

        event_cmd_commit(zMETime, rid, &content, zMimeType, zPageName,
                         zTags, zClr);
        if( g.argv[2][1]=='r' ){
          fossil_print("Created new tech note %s.\n", zMETime);
        }else{


          fossil_print("Updated tech note %s.\n", zMETime);
        }
        free(zMETime);
      }else{
        fossil_fatal("ambiguous tech note id: %s", zETime);
      }
    }
    manifest_destroy(pWiki);
    blob_reset(&content);
  }else if( strncmp(g.argv[2],"delete",n)==0 ){
    if( g.argc!=5 ){
      usage("delete PAGENAME");
    }

Changes to test/wiki.test.

76
77
78
79
80
81
82



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
...
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
...
160
161
162
163
164
165
166
167
168
169
170

171
172
173
174
175
176
177
178
...
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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
...
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290


291
292



















293
294
295
296
###############################################################################
# Creating a tech note with a specified timestamp should add a technote
write_file f3 "A technote"
fossil wiki create technote f3 --technote {2016-01-01 12:34}
test wiki-9 {$CODE == 0}
fossil wiki list --technote
test wiki-10 {[normalize_result] eq {2016-01-01 12:34:00}}




###############################################################################
# exporting that technote should give back similar text
fossil wiki export a3 --technote {2016-01-01 12:34:00}
test wiki-11 {[similar_file f3 a3]}

###############################################################################
# Trying to add a technote with the same timestamp should fail

fossil wiki create 2ndnote f3 -technote {2016-01-01 12:34} -expectError
test wiki-13 {$CODE != 0}




###############################################################################
# commiting a change to an existing technote should replace the page on export


write_file f4 "technote 2nd variant"
fossil wiki commit technote f4 --technote {2016-01-01 12:34}
test wiki-14 {$CODE == 0}
fossil wiki export a4 --technote {2016-01-01 12:34}
test wiki-15 {[similar_file f4 a4]}





###############################################################################
# But we shouldn't be able to update non-existant pages
fossil wiki commit doesntexist f1 -expectError
test wiki-16 {$CODE != 0}

###############################################################################
................................................................................
write_file fa "This is a file to be attached"
fossil attachment add doesntexist fa -expectError
test wiki-21 {$CODE != 0}
fossil attachment add tcltest fa
test wiki-22 {$CODE == 0}

###############################################################################
# Test adding an attachment to borg a non-existant (should fail) and existing tech note
fossil attachment add fa --technote {2016-07-22 12:00} -expectError
test wiki-23 {$CODE != 0}
fossil attachment add fa --technote {2016-01-03 12:34}
test wiki-24 {$CODE == 0}

###############################################################################
# Check that a wiki page with an attachment can be updated
................................................................................
###############################################################################
# Check a technote appears on the timeline
write_file f8 "Contents of a 'unique' tech note"
fossil wiki create {Unique technote} f8 --technote {2016-01-05 01:02:03}
fossil timeline
test wiki-30 {[string match *Unique*technote* $RESULT]}


###############################################################################
# Check for a collision between an attachment and a note, this is a
# bug that results from some code apparently treating the attachment

# entry as if it were a technote when it isn't really. 
#
# First, wait for the top of the next second so the attachment
# happens at a known time, then add an attachment to an existing note
# and a new note immediately after. 

set t0 [clock seconds]
while {$t0 == [clock seconds]} {
................................................................................
fossil timeline
test wiki-36-1 {$CODE == 0}
fossil wiki list -t
test wiki-36-2 {$CODE == 0}

###############################################################################
# Check that we have the expected number of tech notes on the list (and not 
# extra ones from other events (such as the attachments) - 7 tech notes 
# expected created by tests 9, 17, 19, 29, 31, 32 and 34 
fossil wiki list --technote
set technotelist [split $RESULT "\n"] 
test wiki-37 {[llength $technotelist] == 7}

###############################################################################
# Check that using the show-technote-ids shows the same tech notes in the same
# order (with the technote id as the first word of the line
fossil wiki list --technote --show-technote-ids
set technoteidlist [split $RESULT "\n"]
test wiki-38 {[llength $technotelist] == 7}
for {set i 0} {$i < [llength $technotelist]} {incr i} {
  set match "???????????????????????????????????????? "
  append match [lindex $technotelist $i]
  test "wiki-39-$i" {[string match $match [lindex $technoteidlist $i]]}
}

###############################################################################
# Create new tech note with a old timestamp so that it is oldest and then check that
# the contents of the oldest tech note (by tech note id, both full and short) match up
write_file f12 "A really old tech note"
fossil wiki create {Old tech note} f12 --technote {2001-07-07 09:08:07}
fossil wiki list --technote --show-technote-ids
set technotelist [split $RESULT "\n"]
set oldesttechnoteid [lindex [split [lindex $technotelist [llength $technotelist]-1]] 0]
fossil wiki export a12 --technote $oldesttechnoteid
test wiki-40 {[similar_file f12 a12]}

###############################################################################
# Also check that we can specify a prefix of the tech note id (note: with
# 8 items in the tech note at this point there is a chance of a collision.
# However with the chance of the collision being 1 in approx 10^22 if I use a 20
# character prefix I'll ignore that possibility for this test.
fossil wiki export a12.1 --technote [string range $oldesttechnoteid 0 20]
test wiki-41 {[similar_file f12 a12.1]}

###############################################################################
# Now we need to force a collision in the first four characters of the tech
# note id if we don't already have one so we can check we get an error if the
# tech note id is ambiguous
set idcounts [dict create]
................................................................................
}
# get i so that, as a julian date, it is in the 1800s, i.e., older than
# any other tech note, but after 1 AD
set i 2400000
while {$maxcount < 2} {
  # keep getting older
  incr i -1
  write_file collision "A tech note with timestamp of jday=$i"
  fossil wiki create "timestamp of $i" collision --technote "$i"
  fossil wiki list --technote --show-technote-ids
  set technotelist [split $RESULT "\n"]
  set oldesttechnoteid [lindex [split [lindex $technotelist [llength $technotelist]-1]] 0]
  set id [string range $oldesttechnoteid 0 3]
  dict incr idcounts $id
  if {[dict get $idcounts $id] > $maxcount} {
    set maxid $id
    incr maxcount 
  }
}


fossil wiki export a13 --technote $maxid -expectError
test wiki-42 {$CODE != 0}




















###############################################################################
test_cleanup








>
>
>







|
>
|
|
>
>
>



>
>





>
>
>
>







 







|







 







<

|
<
>
|







 







|
|


|



|


|













|
|




|
|
|
|







 







|
|










>
>
|

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




76
77
78
79
80
81
82
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
...
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
...
173
174
175
176
177
178
179

180
181

182
183
184
185
186
187
188
189
190
...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
...
284
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
###############################################################################
# Creating a tech note with a specified timestamp should add a technote
write_file f3 "A technote"
fossil wiki create technote f3 --technote {2016-01-01 12:34}
test wiki-9 {$CODE == 0}
fossil wiki list --technote
test wiki-10 {[normalize_result] eq {2016-01-01 12:34:00}}
fossil wiki list --technote --show-technote-ids
set technotelist [split $RESULT "\n"]
set veryfirsttechnoteid [lindex [split [lindex $technotelist 0]] 0]

###############################################################################
# exporting that technote should give back similar text
fossil wiki export a3 --technote {2016-01-01 12:34:00}
test wiki-11 {[similar_file f3 a3]}

###############################################################################
# Trying to add a technote with the same timestamp should succeed and create a
# second tech note
fossil wiki create 2ndnote f3 -technote {2016-01-01 12:34}
test wiki-13 {$CODE == 0}
fossil wiki list --technote
set technotelist [split $RESULT "\n"] 
test wiki-13.1 {[llength $technotelist] == 2}

###############################################################################
# commiting a change to an existing technote should replace the page on export
# (this should update the tech note from wiki-13 as that the most recently
# updated one, that should also be the one exported by the export command)
write_file f4 "technote 2nd variant"
fossil wiki commit technote f4 --technote {2016-01-01 12:34}
test wiki-14 {$CODE == 0}
fossil wiki export a4 --technote {2016-01-01 12:34}
test wiki-15 {[similar_file f4 a4]}
# Also check that the tech note with the same timestamp, but modified less
# recently still has its original text
fossil wiki export a4.1 --technote $veryfirsttechnoteid
test wiki-15.1 {[similar_file f3 a4.1]}

###############################################################################
# But we shouldn't be able to update non-existant pages
fossil wiki commit doesntexist f1 -expectError
test wiki-16 {$CODE != 0}

###############################################################################
................................................................................
write_file fa "This is a file to be attached"
fossil attachment add doesntexist fa -expectError
test wiki-21 {$CODE != 0}
fossil attachment add tcltest fa
test wiki-22 {$CODE == 0}

###############################################################################
# Test adding an attachment to both a non-existant (should fail) and existing tech note
fossil attachment add fa --technote {2016-07-22 12:00} -expectError
test wiki-23 {$CODE != 0}
fossil attachment add fa --technote {2016-01-03 12:34}
test wiki-24 {$CODE == 0}

###############################################################################
# Check that a wiki page with an attachment can be updated
................................................................................
###############################################################################
# Check a technote appears on the timeline
write_file f8 "Contents of a 'unique' tech note"
fossil wiki create {Unique technote} f8 --technote {2016-01-05 01:02:03}
fossil timeline
test wiki-30 {[string match *Unique*technote* $RESULT]}


###############################################################################
# Check for a collision between an attachment and a note, this was a

# bug that resulted from some code treating the attachment entry as if it 
# were a technote when it isn't really. 
#
# First, wait for the top of the next second so the attachment
# happens at a known time, then add an attachment to an existing note
# and a new note immediately after. 

set t0 [clock seconds]
while {$t0 == [clock seconds]} {
................................................................................
fossil timeline
test wiki-36-1 {$CODE == 0}
fossil wiki list -t
test wiki-36-2 {$CODE == 0}

###############################################################################
# Check that we have the expected number of tech notes on the list (and not 
# extra ones from other events (such as the attachments) - 8 tech notes 
# expected created by tests 9, 13, 17, 19, 29, 31, 32 and 34 
fossil wiki list --technote
set technotelist [split $RESULT "\n"] 
test wiki-37 {[llength $technotelist] == 8}

###############################################################################
# Check that using the show-technote-ids shows the same tech notes in the same
# order (with the technote id as the first word of the line)
fossil wiki list --technote --show-technote-ids
set technoteidlist [split $RESULT "\n"]
test wiki-38 {[llength $technotelist] == 8}
for {set i 0} {$i < [llength $technotelist]} {incr i} {
  set match "???????????????????????????????????????? "
  append match [lindex $technotelist $i]
  test "wiki-39-$i" {[string match $match [lindex $technoteidlist $i]]}
}

###############################################################################
# Create new tech note with a old timestamp so that it is oldest and then check that
# the contents of the oldest tech note (by tech note id, both full and short) match up
write_file f12 "A really old tech note"
fossil wiki create {Old tech note} f12 --technote {2001-07-07 09:08:07}
fossil wiki list --technote --show-technote-ids
set technotelist [split $RESULT "\n"]
set anoldtechnoteid [lindex [split [lindex $technotelist [llength $technotelist]-1]] 0]
fossil wiki export a12 --technote $anoldtechnoteid
test wiki-40 {[similar_file f12 a12]}

###############################################################################
# Also check that we can specify a prefix of the tech note id (note: with
# 9 items in the tech note at this point there is a chance of a collision.
# However with a 20 character prefix the chance of the collision is 
# approximately 1 in 10^22 so this test ignores that possibility.)
fossil wiki export a12.1 --technote [string range $anoldtechnoteid 0 20]
test wiki-41 {[similar_file f12 a12.1]}

###############################################################################
# Now we need to force a collision in the first four characters of the tech
# note id if we don't already have one so we can check we get an error if the
# tech note id is ambiguous
set idcounts [dict create]
................................................................................
}
# get i so that, as a julian date, it is in the 1800s, i.e., older than
# any other tech note, but after 1 AD
set i 2400000
while {$maxcount < 2} {
  # keep getting older
  incr i -1
  write_file f13 "A tech note with timestamp of jday=$i"
  fossil wiki create "timestamp of $i" f13 --technote "$i"
  fossil wiki list --technote --show-technote-ids
  set technotelist [split $RESULT "\n"]
  set oldesttechnoteid [lindex [split [lindex $technotelist [llength $technotelist]-1]] 0]
  set id [string range $oldesttechnoteid 0 3]
  dict incr idcounts $id
  if {[dict get $idcounts $id] > $maxcount} {
    set maxid $id
    incr maxcount 
  }
}
# Save the duplicate id for this and later tests
set duplicateid $maxid
fossil wiki export a13 --technote $duplicateid -expectError
test wiki-42 {$CODE != 0}

###############################################################################
# Check we can update technote by its id
write_file f14 "Updated text for the really old tech note"
fossil wiki commit {Old tech note} f14 --technote $anoldtechnoteid
fossil wiki export a14 --technote $anoldtechnoteid
test wiki-43 {[similar_file f14 a14]}

###############################################################################
# Also check that we can specify a prefix of the tech note id
write_file f15 "Updated text for the really old tech note specified by its id"
fossil wiki commit {Old tech note} f15 --technote [string range $anoldtechnoteid 0 20]
fossil wiki export a15 --technote $anoldtechnoteid
test wiki-44 {[similar_file f15 a15]}

###############################################################################
# And we get an error for the ambiguous tech note id
fossil wiki commit {Old tech note} f15 --technote $duplicateid -expectError
test wiki-45  {$CODE != 0}

###############################################################################
test_cleanup