Fossil

Check-in [6b7805e056]
Login

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

Overview
Comment:Renamed the file=filename, r=checkin query params to filename=..., checkin=..., for consistency with the annotate/blame URLs and possibly others.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | fileedit-ajaxify
Files: files | file ages | folders
SHA3-256: 6b7805e056ddd9eb86cf3e9e98b574af2beb93cb34d6584dcb258c9bb8dfdb27
User & Date: stephan 2020-05-06 17:52:44.681
Context
2020-05-06
22:57
Improved the tab control styling, and no longer use buttons so that their :focus style doesn't break the appearance. Fixed some CSS which no longer matched after removal of the outer wrapping FORM element. check-in: a84f6458c0 user: stephan tags: fileedit-ajaxify
17:52
Renamed the file=filename, r=checkin query params to filename=..., checkin=..., for consistency with the annotate/blame URLs and possibly others. check-in: 6b7805e056 user: stephan tags: fileedit-ajaxify
17:37
s/f-post-/f-preview-/. check-in: 99bd288bce user: stephan tags: fileedit-ajaxify
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/fileedit.c.
1070
1071
1072
1073
1074
1075
1076
1077
1078

1079
1080
1081
1082
1083
1084
1085
                        "fileedit-glob setting.");
    return 0;
  }
  return 1;
}

/*
** Passed the values of the "r" and "file" request properties,
** this function verifies that they are valid and populates:

**
** - *zRevUuid = the fully-expanded value of zRev (owned by the
**    caller). zRevUuid may be NULL.
**
** - *vid = the RID of zRevUuid. May not be NULL.
**
** - *frid = the RID of zFilename's blob content. May not be NULL.







|
|
>







1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
                        "fileedit-glob setting.");
    return 0;
  }
  return 1;
}

/*
** Passed the values of the "checkin" and "filename" request
** properties, this function verifies that they are valid and
** populates:
**
** - *zRevUuid = the fully-expanded value of zRev (owned by the
**    caller). zRevUuid may be NULL.
**
** - *vid = the RID of zRevUuid. May not be NULL.
**
** - *frid = the RID of zFilename's blob content. May not be NULL.
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
}

/*
** WEBPAGE: fileedit_content
**
** Query parameters:
**
** file=FILENAME
** r=CHECKIN_NAME
**
** User must have Write access to use this page.
**
** Responds with the raw content of the given page. On error it
** produces a JSON response as documented for fileedit_ajax_error().
*/
void fileedit_ajax_content(){
  const char * zFilename = PD("file",P("name"));
  const char * zRev = P("r");
  int vid, frid;
  Blob content = empty_blob;
  const char * zMime;

  if(!fileedit_ajax_boostrap()
     || !fileedit_ajax_setup_filerev(zRev, 0, &vid,
                                     zFilename, &frid)){







|
|







|
|







1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
}

/*
** WEBPAGE: fileedit_content
**
** Query parameters:
**
** filename=FILENAME
** checkin=CHECKIN_NAME
**
** User must have Write access to use this page.
**
** Responds with the raw content of the given page. On error it
** produces a JSON response as documented for fileedit_ajax_error().
*/
void fileedit_ajax_content(){
  const char * zFilename = PD("filename",P("name"));
  const char * zRev = P("checkin");
  int vid, frid;
  Blob content = empty_blob;
  const char * zMime;

  if(!fileedit_ajax_boostrap()
     || !fileedit_ajax_setup_filerev(zRev, 0, &vid,
                                     zFilename, &frid)){
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
}

/*
** WEBPAGE: fileedit_preview
**
** Required query parameters:
**
** file=FILENAME
** content=text
**
** Optional query parameters:
**
** render_mode=integer (FE_RENDER_xxx) (default=FE_RENDER_GUESS)
**
** ln=0 or 1 to disable/enable line number mode in
** FE_RENDER_PLAIN_TEXT mode.
**
** iframe_height=integer (default=40) Height, in EMs of HTML preview
** iframe.
**
** User must have Write access to use this page.
**
** Responds with the HTML content of the preview. On error it produces
** a JSON response as documented for fileedit_ajax_error().
*/
void fileedit_ajax_preview(){
  const char * zFilename = PD("file",P("name"));
  const char * zContent = P("content");
  int renderMode = atoi(PD("render_mode","0"));
  int ln = atoi(PD("ln","0"));
  int iframeHeight = atoi(PD("iframe_height","40"));
  Blob content = empty_blob;

  if(!fileedit_ajax_boostrap()







|


















|







1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
}

/*
** WEBPAGE: fileedit_preview
**
** Required query parameters:
**
** filename=FILENAME
** content=text
**
** Optional query parameters:
**
** render_mode=integer (FE_RENDER_xxx) (default=FE_RENDER_GUESS)
**
** ln=0 or 1 to disable/enable line number mode in
** FE_RENDER_PLAIN_TEXT mode.
**
** iframe_height=integer (default=40) Height, in EMs of HTML preview
** iframe.
**
** User must have Write access to use this page.
**
** Responds with the HTML content of the preview. On error it produces
** a JSON response as documented for fileedit_ajax_error().
*/
void fileedit_ajax_preview(){
  const char * zFilename = PD("filename",P("name"));
  const char * zContent = P("content");
  int renderMode = atoi(PD("render_mode","0"));
  int ln = atoi(PD("ln","0"));
  int iframeHeight = atoi(PD("iframe_height","40"));
  Blob content = empty_blob;

  if(!fileedit_ajax_boostrap()
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
}

/*
** WEBPAGE: fileedit_diff
**
** Required query parameters:
**
** file=FILENAME
** content=text
** r=checkin version
**
** Optional parameters:
**
** sbs=integer (1=side-by-side or 0=unified, default=0)
**
** User must have Write access to use this page.
**
** Responds with the HTML content of the diff. On error it produces a
** JSON response as documented for fileedit_ajax_error().
*/
void fileedit_ajax_diff(){
  /*
  ** Reminder: we only need the filename to perform valdiation
  ** against fileedit_is_editable(), else this route could be
  ** abused to get diffs against content disallowed by the
  ** whitelist.
  */
  const char * zFilename = PD("file",P("name"));
  const char * zRev = P("r");
  const char * zContent = P("content");
  char * zRevUuid = 0;
  int isSbs = atoi(PD("sbs","0"));
  int vid, frid;
  Blob content = empty_blob;

  if(!fileedit_ajax_boostrap()







|

|

















|
|







1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
}

/*
** WEBPAGE: fileedit_diff
**
** Required query parameters:
**
** filename=FILENAME
** content=text
** checkin=checkin version
**
** Optional parameters:
**
** sbs=integer (1=side-by-side or 0=unified, default=0)
**
** User must have Write access to use this page.
**
** Responds with the HTML content of the diff. On error it produces a
** JSON response as documented for fileedit_ajax_error().
*/
void fileedit_ajax_diff(){
  /*
  ** Reminder: we only need the filename to perform valdiation
  ** against fileedit_is_editable(), else this route could be
  ** abused to get diffs against content disallowed by the
  ** whitelist.
  */
  const char * zFilename = PD("filename",P("name"));
  const char * zRev = P("checkin");
  const char * zContent = P("content");
  char * zRevUuid = 0;
  int isSbs = atoi(PD("sbs","0"));
  int vid, frid;
  Blob content = empty_blob;

  if(!fileedit_ajax_boostrap()
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
*/
static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr){
  char * zFileUuid = 0;          /* UUID of file content */
  const char * zFlag;            /* generic flag */
  int rc = 0, vid = 0, frid = 0; /* result code, checkin/file rids */ 

#define fail(EXPR) blob_appendf EXPR; goto end_fail
  zFlag = PD("file",P("name"));
  if(zFlag==0 || !*zFlag){
    rc = 400;
    fail((pErr,"Missing required 'file' parameter."));
  }
  p->zFilename = mprintf("%s",zFlag);

  if(0==fileedit_is_editable(p->zFilename)){
    rc = 403;
    fail((pErr,"Filename [%h] is disallowed "
          "by the [fileedit-glob] repository "
          "setting.",
          p->zFilename));
  }

  zFlag = P("r");
  if(!zFlag){
    rc = 400;
    fail((pErr,"Missing required 'r' parameter."));
  }
  vid = symbolic_name_to_rid(zFlag, "ci");
  if(0==vid){
    rc = 404;







|














|







1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
*/
static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr){
  char * zFileUuid = 0;          /* UUID of file content */
  const char * zFlag;            /* generic flag */
  int rc = 0, vid = 0, frid = 0; /* result code, checkin/file rids */ 

#define fail(EXPR) blob_appendf EXPR; goto end_fail
  zFlag = PD("filename",P("name"));
  if(zFlag==0 || !*zFlag){
    rc = 400;
    fail((pErr,"Missing required 'file' parameter."));
  }
  p->zFilename = mprintf("%s",zFlag);

  if(0==fileedit_is_editable(p->zFilename)){
    rc = 403;
    fail((pErr,"Filename [%h] is disallowed "
          "by the [fileedit-glob] repository "
          "setting.",
          p->zFilename));
  }

  zFlag = P("checkin");
  if(!zFlag){
    rc = 400;
    fail((pErr,"Missing required 'r' parameter."));
  }
  vid = symbolic_name_to_rid(zFlag, "ci");
  if(0==vid){
    rc = 404;
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
}

/*
** WEBPAGE: fileedit_commit
**
** Required query parameters:
** 
** file=FILENAME
** r=Parent checkin UUID
** content=text
** comment=text
**
** Optional query parameters:
**
** comment_mimetype=text
** dry_run=int (1 or 0)







|
|







1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
}

/*
** WEBPAGE: fileedit_commit
**
** Required query parameters:
** 
** filename=FILENAME
** checkin=Parent checkin UUID
** content=text
** comment=text
**
** Optional query parameters:
**
** comment_mimetype=text
** dry_run=int (1 or 0)
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
** WEBPAGE: fileedit
**
** EXPERIMENTAL and subject to change and removal at any time. The goal
** is to allow online edits of files.
**
** Query parameters:
**
**    file=FILENAME    Repo-relative path to the file.
**    r=VERSION        Checkin version, using any unambiguous
**                     supported symbolic version name.
**
** All other parameters are for internal use only, submitted via the
** form-submission process, and may change with any given revision of
** this code.
*/
void fileedit_page(){
  const char * zFilename;               /* filename. We'll accept 'name'







|
|
|







1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
** WEBPAGE: fileedit
**
** EXPERIMENTAL and subject to change and removal at any time. The goal
** is to allow online edits of files.
**
** Query parameters:
**
**    filename=FILENAME    Repo-relative path to the file.
**    checkin=VERSION      Checkin version, using any unambiguous
**                         supported symbolic version name.
**
** All other parameters are for internal use only, submitted via the
** form-submission process, and may change with any given revision of
** this code.
*/
void fileedit_page(){
  const char * zFilename;               /* filename. We'll accept 'name'
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
  ** rendering the page, then output the error message at the end.
  ********************************************************************/
  CX("<p>This page is <em>NEW AND EXPERIMENTAL</em>. "
     "USE AT YOUR OWN RISK, preferably on a test "
     "repo.</p>\n");

  /******* Hidden fields *******/
  CX("<input type='hidden' name='r' value='%s'>",
     cimi.zParentUuid);
  CX("<input type='hidden' name='file' value='%T'>",
     zFilename);

  /* Status bar */
  CX("<div id='fossil-status-bar'>Async. status messages will go "
     "here.</div>\n"/* will be moved into the tab container via JS */);

  /* Main tab container... */







|

|







1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
  ** rendering the page, then output the error message at the end.
  ********************************************************************/
  CX("<p>This page is <em>NEW AND EXPERIMENTAL</em>. "
     "USE AT YOUR OWN RISK, preferably on a test "
     "repo.</p>\n");

  /******* Hidden fields *******/
  CX("<input type='hidden' name='checkin' value='%s'>",
     cimi.zParentUuid);
  CX("<input type='hidden' name='filename' value='%T'>",
     zFilename);

  /* Status bar */
  CX("<div id='fossil-status-bar'>Async. status messages will go "
     "here.</div>\n"/* will be moved into the tab container via JS */);

  /* Main tab container... */
Changes to src/finfo.c.
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
      @ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin))
      @ [blame]</a>
      @ %z(href("%R/timeline?n=all&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
      if( fpid>0 ){
        @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a>
      }
      if( fileedit_is_editable(zFilename) ){
        @ %z(href("%R/fileedit?file=%T&r=%!S",zFilename,zCkin))[edit]</a>
      }
      @ </span></span>
    }
    if( fDebug & FINFO_DEBUG_MLINK ){
      int ii;
      char *zAncLink;
      @ <br />fid=%d(frid) pid=%d(fpid) mid=%d(fmid)







|







620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
      @ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin))
      @ [blame]</a>
      @ %z(href("%R/timeline?n=all&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
      if( fpid>0 ){
        @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a>
      }
      if( fileedit_is_editable(zFilename) ){
        @ %z(href("%R/fileedit?filename=%T&checkin=%!S",zFilename,zCkin))[edit]</a>
      }
      @ </span></span>
    }
    if( fDebug & FINFO_DEBUG_MLINK ){
      int ii;
      char *zAncLink;
      @ <br />fid=%d(frid) pid=%d(fpid) mid=%d(fmid)
Changes to src/fossil.page.fileedit.js.
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
  /**
     updateVersion() updates the filename and version in various UI
     elements...

     Returns this object.
  */
  P.updateVersion = function(file,rev){
    this.finfo = {file,r:rev};
    const E = (s)=>document.querySelector(s),
          euc = encodeURIComponent,
          rShort = rev.substr(0,16);
    E('#r-label').innerText=rev;
    E('#finfo-link').setAttribute(
      'href',
      F.repoUrl('finfo',{name:file, m:rShort})
    );
    E('#finfo-file-name').innerText=file;
    E('#r-link').setAttribute(
      'href',
      F.repoUrl('info/'+rev)
    );
    E('#r-label').innerText = rev;
    const purlArgs = F.encodeUrlArgs({file, r:rShort},false,true);



    const purl = F.repoUrl('fileedit',purlArgs);
    const e = E('#permalink');
    e.innerText='fileedit?'+purlArgs;
    e.setAttribute('href',purl);
    return this;
  };

  /**
     loadFile() loads (file,checkinVersion) and updates the relevant
     UI elements to reflect the loaded state.

     Returns this object, noting that the load is async.
  */
  P.loadFile = function(file,rev){
    if(0===arguments.length){
      if(!this.finfo) return this;
      file = this.finfo.file;
      rev = this.finfo.r;
    }
    delete this.finfo;
    const self = this;
    F.message("Loading content...");
    F.fetch('fileedit_content',{
      urlParams:{file:file,r:rev},
      onload:(r)=>{
        F.message('Loaded content.');
        self.e.taEditor.value = r;
        self.updateVersion(file,rev);
        self.preview();
        self.tabs.switchToTab(self.e.tabs.content);
      }







|














|
>
>
>
















|
|





|







110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  /**
     updateVersion() updates the filename and version in various UI
     elements...

     Returns this object.
  */
  P.updateVersion = function(file,rev){
    this.finfo = {filename:file,checkin:rev};
    const E = (s)=>document.querySelector(s),
          euc = encodeURIComponent,
          rShort = rev.substr(0,16);
    E('#r-label').innerText=rev;
    E('#finfo-link').setAttribute(
      'href',
      F.repoUrl('finfo',{name:file, m:rShort})
    );
    E('#finfo-file-name').innerText=file;
    E('#r-link').setAttribute(
      'href',
      F.repoUrl('info/'+rev)
    );
    E('#r-label').innerText = rev;
    const purlArgs = F.encodeUrlArgs({
      filename: this.finfo.filename,
      checkin: this.finfo.checkin
    },false,true);
    const purl = F.repoUrl('fileedit',purlArgs);
    const e = E('#permalink');
    e.innerText='fileedit?'+purlArgs;
    e.setAttribute('href',purl);
    return this;
  };

  /**
     loadFile() loads (file,checkinVersion) and updates the relevant
     UI elements to reflect the loaded state.

     Returns this object, noting that the load is async.
  */
  P.loadFile = function(file,rev){
    if(0===arguments.length){
      if(!this.finfo) return this;
      file = this.finfo.filename;
      rev = this.finfo.checkin;
    }
    delete this.finfo;
    const self = this;
    F.message("Loading content...");
    F.fetch('fileedit_content',{
      urlParams: {filename:file,checkin:rev},
      onload:(r)=>{
        F.message('Loaded content.');
        self.e.taEditor.value = r;
        self.updateVersion(file,rev);
        self.preview();
        self.tabs.switchToTab(self.e.tabs.content);
      }
182
183
184
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
214
215
216



217
218
219
220
221
222
223
    );
    const self = this;
    const updateView = function(c){
      D.clearElement(target);
      if('string'===typeof c){
        target.innerHTML = c;
      }
      F.message('Updated preview.');
      if(switchToTab) self.tabs.switchToTab(self.e.tabs.preview);
    };
    if(!content){
      updateView('');
      return this;
    }
    this._postPreview(this.e.taEditor.value, updateView);
    return this;
  };

  /**
     Callback for use with F.connectPagePreviewers()
  */
  P._postPreview = function(content,callback){
    if(!content){
      callback(content);
      return;
    }
    const fd = new FormData();
    fd.append('render_mode',E('select[name=preview_render_mode]').value);
    fd.append('file',this.finfo.file);
    fd.append('ln',E('[name=preview_ln]').checked ? 1 : 0);
    fd.append('iframe_height', E('[name=preview_html_ems]').value);
    fd.append('content',content || '');
    fossil.fetch('fileedit_preview',{
      payload: fd,
      onload: callback,



      onerror: (e)=>{
        fossil.fetch.onerror(e);
        callback("Error fetching preview: "+e);
      }
    });
  };








<




















|





|
>
>
>







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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
    );
    const self = this;
    const updateView = function(c){
      D.clearElement(target);
      if('string'===typeof c){
        target.innerHTML = c;
      }

      if(switchToTab) self.tabs.switchToTab(self.e.tabs.preview);
    };
    if(!content){
      updateView('');
      return this;
    }
    this._postPreview(this.e.taEditor.value, updateView);
    return this;
  };

  /**
     Callback for use with F.connectPagePreviewers()
  */
  P._postPreview = function(content,callback){
    if(!content){
      callback(content);
      return;
    }
    const fd = new FormData();
    fd.append('render_mode',E('select[name=preview_render_mode]').value);
    fd.append('filename',this.finfo.filename);
    fd.append('ln',E('[name=preview_ln]').checked ? 1 : 0);
    fd.append('iframe_height', E('[name=preview_html_ems]').value);
    fd.append('content',content || '');
    fossil.fetch('fileedit_preview',{
      payload: fd,
      onload: (r)=>{
        callback(r);
        F.message('Updated preview.');
      },
      onerror: (e)=>{
        fossil.fetch.onerror(e);
        callback("Error fetching preview: "+e);
      }
    });
  };

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
    }
    const content = this.e.taEditor.value,
          target = this.e.tabs.diff.querySelector(
            '#fileedit-tab-diff-wrapper'
          ),
          self = this;
    const fd = new FormData();
    fd.append('file',this.finfo.file);
    fd.append('r', this.finfo.r);
    fd.append('sbs', sbs ? 1 : 0);
    fd.append('content',content);
    F.message(
      "Fetching diff..."
    ).fetch('fileedit_diff',{
      payload: fd,
      onload: function(c){
        target.innerHTML = [
          "<div>Diff <code>[",
          self.finfo.r,
          "]</code> &rarr; Local Edits</div>",
          c||'No changes.'
        ].join('');
        F.message('Updated diff.');
        self.tabs.switchToTab(self.e.tabs.diff);
      }
    });







|
|









|







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
    }
    const content = this.e.taEditor.value,
          target = this.e.tabs.diff.querySelector(
            '#fileedit-tab-diff-wrapper'
          ),
          self = this;
    const fd = new FormData();
    fd.append('filename',this.finfo.filename);
    fd.append('checkin', this.finfo.checkin);
    fd.append('sbs', sbs ? 1 : 0);
    fd.append('content',content);
    F.message(
      "Fetching diff..."
    ).fetch('fileedit_diff',{
      payload: fd,
      onload: function(c){
        target.innerHTML = [
          "<div>Diff <code>[",
          self.finfo.checkin,
          "]</code> &rarr; Local Edits</div>",
          c||'No changes.'
        ].join('');
        F.message('Updated diff.');
        self.tabs.switchToTab(self.e.tabs.diff);
      }
    });
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
      return this;
    }
    const self = this;
    const content = this.e.taEditor.value,
          target = document.querySelector('#fileedit-manifest'),
          cbDryRun = E('[name=dry_run]'),
          isDryRun = cbDryRun.checked,
          filename = this.finfo.file;
    if(!f.updateView){
      f.updateView = function(c){
        target.innerHTML = [
          "<h3>Manifest",
          (c.dryRun?" (dry run)":""),
          ": ", c.uuid.substring(0,16),"</h3>",
          "<code class='fileedit-manifest'>",







|







278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
      return this;
    }
    const self = this;
    const content = this.e.taEditor.value,
          target = document.querySelector('#fileedit-manifest'),
          cbDryRun = E('[name=dry_run]'),
          isDryRun = cbDryRun.checked,
          filename = this.finfo.filename;
    if(!f.updateView){
      f.updateView = function(c){
        target.innerHTML = [
          "<h3>Manifest",
          (c.dryRun?" (dry run)":""),
          ": ", c.uuid.substring(0,16),"</h3>",
          "<code class='fileedit-manifest'>",
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
      };
    }
    if(!content){
      f.updateView('');
      return this;
    }
    const fd = new FormData();
    fd.append('file',filename);
    fd.append('r', this.finfo.r);
    fd.append('content',content);
    fd.append('dry_run',isDryRun ? 1 : 0);
    /* Text fields or select lists... */
    ['comment_mimetype',
     'comment'
    ].forEach(function(name){
      var e = E('[name='+name+']');







|
|







309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
      };
    }
    if(!content){
      f.updateView('');
      return this;
    }
    const fd = new FormData();
    fd.append('filename',filename);
    fd.append('checkin', this.finfo.checkin);
    fd.append('content',content);
    fd.append('dry_run',isDryRun ? 1 : 0);
    /* Text fields or select lists... */
    ['comment_mimetype',
     'comment'
    ].forEach(function(name){
      var e = E('[name='+name+']');
Changes to src/info.c.
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
    if( g.perm.Hyperlink ){
      @ %z(href("%R/annotate?filename=%T&checkin=%!S",zName,zVers))
      @ [annotate]</a>
      @ %z(href("%R/blame?filename=%T&checkin=%!S",zName,zVers))
      @ [blame]</a>
      @ %z(href("%R/timeline?n=all&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
      if( fileedit_is_editable(zName) ){
        @ %z(href("%R/fileedit?file=%T&r=%!S",zName,zVers))[edit]</a>
      }
    }
    cnt++;
    if( pDownloadName && blob_size(pDownloadName)==0 ){
      blob_append(pDownloadName, zName, -1);
    }
  }







|







1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
    if( g.perm.Hyperlink ){
      @ %z(href("%R/annotate?filename=%T&checkin=%!S",zName,zVers))
      @ [annotate]</a>
      @ %z(href("%R/blame?filename=%T&checkin=%!S",zName,zVers))
      @ [blame]</a>
      @ %z(href("%R/timeline?n=all&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
      if( fileedit_is_editable(zName) ){
        @ %z(href("%R/fileedit?filename=%T&checkin=%!S",zName,zVers))[edit]</a>
      }
    }
    cnt++;
    if( pDownloadName && blob_size(pDownloadName)==0 ){
      blob_append(pDownloadName, zName, -1);
    }
  }