Fossil

Check-in [50a58adb76]
Login

Check-in [50a58adb76]

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

Overview
Comment:Many changes and bug fixes in the wiki processing. Moving toward a workable wiki system. The "Home" menu option now takes you to the wiki page whose name is the same as the Project Name. There is a "wcontent" page, but no link to it yet. Many other changes.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 50a58adb76175391f3179deff1fb954a204b0f93
User & Date: drh 2007-10-10 21:15:17.000
Context
2007-10-10
23:10
Get rid of the "locking" capability on wiki pages. Assume that anybody who can write or append to a wiki page can do so to any wiki page. Add the /wikiappend page for appending comments to the end of wiki. ... (check-in: 61ce5e3685 user: drh tags: trunk)
21:15
Many changes and bug fixes in the wiki processing. Moving toward a workable wiki system. The "Home" menu option now takes you to the wiki page whose name is the same as the Project Name. There is a "wcontent" page, but no link to it yet. Many other changes. ... (check-in: 50a58adb76 user: drh tags: trunk)
15:21
Fix the wiki editor so that it can handle wiki page names that include spaces. ... (check-in: 9f89a8e68e user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/info.c.
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
    if( g.okSetup ){
      @ <tr><th>Record ID:</th><td>%d(rid)</td></tr>
    }
    @ <tr><th>Original&nbsp;User:</th><td>%s(db_column_text(&q, 3))</td></tr>
    @ <tr><th>Commands:</th>
    @   <td>
/*    @     <a href="%s(g.zBaseURL)/wdiff/%d(rid)">diff</a> | */
    @     <a href="%s(g.zBaseURL)/wlist/%t(zName)">history</a>
    @     | <a href="%s(g.zBaseURL)/fview/%d(rid)">raw-text</a>
    @   </td>
    @ </tr>
    @ </table></p>
  }else{
    style_header("Wiki Information");
  }







|







419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
    if( g.okSetup ){
      @ <tr><th>Record ID:</th><td>%d(rid)</td></tr>
    }
    @ <tr><th>Original&nbsp;User:</th><td>%s(db_column_text(&q, 3))</td></tr>
    @ <tr><th>Commands:</th>
    @   <td>
/*    @     <a href="%s(g.zBaseURL)/wdiff/%d(rid)">diff</a> | */
    @     <a href="%s(g.zBaseURL)/whistory/%t(zName)">history</a>
    @     | <a href="%s(g.zBaseURL)/fview/%d(rid)">raw-text</a>
    @   </td>
    @ </tr>
    @ </table></p>
  }else{
    style_header("Wiki Information");
  }
Changes to src/login.c.
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/*
** Set the global capability flags based on a capability string.
*/
void login_set_capabilities(const char *zCap){
  int i;
  for(i=0; zCap[i]; i++){
    switch( zCap[i] ){
      case 's':   g.okSetup = g.okDelete = 1;
      case 'a':   g.okAdmin = g.okRdTkt = g.okWrTkt = g.okQuery =
                              g.okRdWiki = g.okWrWiki = g.okNewWiki =
                              g.okHistory = g.okClone = 
                              g.okNewTkt = g.okPassword = 1;
      case 'i':   g.okRead = g.okWrite = 1;                     break;
      case 'o':   g.okRead = 1;                                 break;








|







285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/*
** Set the global capability flags based on a capability string.
*/
void login_set_capabilities(const char *zCap){
  int i;
  for(i=0; zCap[i]; i++){
    switch( zCap[i] ){
      case 's':   g.okSetup = 1;
      case 'a':   g.okAdmin = g.okRdTkt = g.okWrTkt = g.okQuery =
                              g.okRdWiki = g.okWrWiki = g.okNewWiki =
                              g.okHistory = g.okClone = 
                              g.okNewTkt = g.okPassword = 1;
      case 'i':   g.okRead = g.okWrite = 1;                     break;
      case 'o':   g.okRead = 1;                                 break;

Changes to src/main.c.
57
58
59
60
61
62
63

64
65
66
67
68
69
70
  int minPrefix;          /* Number of digits needed for a distinct UUID */
  int fSqlTrace;          /* True if -sqltrace flag is present */
  int fSqlPrint;          /* True if -sqlprint flag is present */
  int fHttpTrace;         /* Trace outbound HTTP requests */
  char *zPath;            /* Name of webpage being served */
  char *zExtra;           /* Extra path information past the webpage name */
  char *zBaseURL;         /* Full text of the URL being served */

  const char *zContentType;  /* The content type of the input HTTP request */
  int iErrPriority;       /* Priority of current error message */
  char *zErrMsg;          /* Text of an error message */
  Blob cgiIn;             /* Input to an xfer www method */
  int cgiPanic;           /* Write error messages to CGI */

  int *aCommitFile;







>







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
  int minPrefix;          /* Number of digits needed for a distinct UUID */
  int fSqlTrace;          /* True if -sqltrace flag is present */
  int fSqlPrint;          /* True if -sqlprint flag is present */
  int fHttpTrace;         /* Trace outbound HTTP requests */
  char *zPath;            /* Name of webpage being served */
  char *zExtra;           /* Extra path information past the webpage name */
  char *zBaseURL;         /* Full text of the URL being served */
  char *zTop;             /* Parent directory of zPath */
  const char *zContentType;  /* The content type of the input HTTP request */
  int iErrPriority;       /* Priority of current error message */
  char *zErrMsg;          /* Text of an error message */
  Blob cgiIn;             /* Input to an xfer www method */
  int cgiPanic;           /* Write error messages to CGI */

  int *aCommitFile;
96
97
98
99
100
101
102

103
104
105
106
107
108
109
  int okRead;             /* o: xfer outbound. checkout */
  int okHistory;          /* h: access historical information. */
  int okClone;            /* g: clone */
  int okRdWiki;           /* j: view wiki via web */
  int okNewWiki;          /* f: create new wiki via web */
  int okApndWiki;         /* m: append to wiki via web */
  int okWrWiki;           /* k: edit wiki via web */

  int okRdTkt;            /* r: view tickets via web */
  int okNewTkt;           /* n: create new tickets */
  int okApndTkt;          /* c: append to tickets via the web */
  int okWrTkt;            /* w: make changes to tickets via web */

  FILE *fDebug;           /* Write debug information here, if the file exists */
};







>







97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
  int okRead;             /* o: xfer outbound. checkout */
  int okHistory;          /* h: access historical information. */
  int okClone;            /* g: clone */
  int okRdWiki;           /* j: view wiki via web */
  int okNewWiki;          /* f: create new wiki via web */
  int okApndWiki;         /* m: append to wiki via web */
  int okWrWiki;           /* k: edit wiki via web */
  int okLockWiki;         /* l: lock and unlock wiki via web */
  int okRdTkt;            /* r: view tickets via web */
  int okNewTkt;           /* n: create new tickets */
  int okApndTkt;          /* c: append to tickets via the web */
  int okWrTkt;            /* w: make changes to tickets via web */

  FILE *fDebug;           /* Write debug information here, if the file exists */
};
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441

442
443


444
445
446
447
448
449
450
      z++;
    }
  }
  putchar('\n');
}

/*
** RSS feeds need to reference absolute URLs so we need to calculate
** the base URL onto which we add components. This is basically
** cgi_redirect() stripped down and always returning an absolute URL.
*/
static char *get_base_url(void){
  int i;
  const char *zHost = PD("HTTP_HOST","");
  const char *zMode = PD("HTTPS","off");
  const char *zCur = PD("REQUEST_URI","/");

  for(i=0; zCur[i] && zCur[i]!='?' && zCur[i]!='#'; i++){}
  if( g.zExtra ){
    /* Skip to start of extra stuff, then pass over any /'s that might
    ** have separated the document root from the extra stuff. This
    ** ensures that the redirection actually redirects the root, not
    ** something deep down at the bottom of a URL.
    */
    i -= strlen(g.zExtra);
    while( i>0 && zCur[i-1]=='/' ){ i--; }
  }
  while( i>0 && zCur[i-1]!='/' ){ i--; }
  while( i>0 && zCur[i-1]=='/' ){ i--; }

  if( strcmp(zMode,"on")==0 ){
    return mprintf("https://%s%.*s", zHost, i, zCur);

  }
  return mprintf("http://%s%.*s", zHost, i, zCur);


}

/*
** Preconditions:
**
**    * Environment various are set up according to the CGI standard.
**    * The respository database has been located and opened.







|
|
|

|



















|
>
|
|
>
>







412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
      z++;
    }
  }
  putchar('\n');
}

/*
** Set the g.zBaseURL value to the full URL for the toplevel of
** the fossil tree.  Set g.zHomeURL to g.zBaseURL without the
** leading "http://" and the host and port.
*/
void set_base_url(void){
  int i;
  const char *zHost = PD("HTTP_HOST","");
  const char *zMode = PD("HTTPS","off");
  const char *zCur = PD("REQUEST_URI","/");

  for(i=0; zCur[i] && zCur[i]!='?' && zCur[i]!='#'; i++){}
  if( g.zExtra ){
    /* Skip to start of extra stuff, then pass over any /'s that might
    ** have separated the document root from the extra stuff. This
    ** ensures that the redirection actually redirects the root, not
    ** something deep down at the bottom of a URL.
    */
    i -= strlen(g.zExtra);
    while( i>0 && zCur[i-1]=='/' ){ i--; }
  }
  while( i>0 && zCur[i-1]!='/' ){ i--; }
  while( i>0 && zCur[i-1]=='/' ){ i--; }

  if( strcmp(zMode,"on")==0 ){
    g.zBaseURL = mprintf("https://%s%.*s", zHost, i, zCur);
    g.zTop = &g.zBaseURL[8+strlen(zHost)+i];
  }else{
    g.zBaseURL = mprintf("http://%s%.*s", zHost, i, zCur);
    g.zTop = &g.zBaseURL[7+strlen(zHost)+i];
  }
}

/*
** Preconditions:
**
**    * Environment various are set up according to the CGI standard.
**    * The respository database has been located and opened.
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
  for(i=1; zPath[i] && zPath[i]!='/'; i++){}
  if( zPath[i]=='/' ){
    zPath[i] = 0;
    g.zExtra = &zPath[i+1];
  }else{
    g.zExtra = 0;
  }
  g.zBaseURL = get_base_url();
  if( g.zExtra ){
    /* CGI parameters get this treatment elsewhere, but places like getfile
    ** will use g.zExtra directly.
    */
    dehttpize(g.zExtra);
  }








|







487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
  for(i=1; zPath[i] && zPath[i]!='/'; i++){}
  if( zPath[i]=='/' ){
    zPath[i] = 0;
    g.zExtra = &zPath[i+1];
  }else{
    g.zExtra = 0;
  }
  set_base_url();
  if( g.zExtra ){
    /* CGI parameters get this treatment elsewhere, but places like getfile
    ** will use g.zExtra directly.
    */
    dehttpize(g.zExtra);
  }

Changes to src/setup.c.
128
129
130
131
132
133
134

135
136
137
138
139
140
141
  @ <li value="4"><b>Delete</b>: Delete wiki and tickets</li>
  @ <li value="6"><b>New-Wiki</b>: Create new wiki pages</li>
  @ <li value="7"><b>Clone</b>: Clone the repository</li>
  @ <li value="8"><b>History</b>: View detail repository history</li>
  @ <li value="9"><b>Check-In</b>: Commit new versions in the repository</li>
  @ <li value="10"><b>Read-Wiki</b>: View wiki pages</li>
  @ <li value="11"><b>Write-Wiki</b>: Edit wiki pages</li>

  @ <li value="13"><b>Append-Wiki</b>: Append to wiki pages</li>
  @ <li value="14"><b>New-Tkt</b>: Create new tickets</li>
  @ <li value="15"><b>Check-Out</b>: Check out versions</li>
  @ <li value="16"><b>Password</b>: Change your own password</li>
  @ <li value="17"><b>Query</b>: Create new queries against tickets</li>
  @ <li value="18"><b>Read-Tkt</b>: View tickets</li>
  @ <li value="19"><b>Setup:</b> Setup and configure this website</li>







>







128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
  @ <li value="4"><b>Delete</b>: Delete wiki and tickets</li>
  @ <li value="6"><b>New-Wiki</b>: Create new wiki pages</li>
  @ <li value="7"><b>Clone</b>: Clone the repository</li>
  @ <li value="8"><b>History</b>: View detail repository history</li>
  @ <li value="9"><b>Check-In</b>: Commit new versions in the repository</li>
  @ <li value="10"><b>Read-Wiki</b>: View wiki pages</li>
  @ <li value="11"><b>Write-Wiki</b>: Edit wiki pages</li>
  @ <li value="12"><b>Lock-Wiki</b>: Lock or unlock wiki pages</li>
  @ <li value="13"><b>Append-Wiki</b>: Append to wiki pages</li>
  @ <li value="14"><b>New-Tkt</b>: Create new tickets</li>
  @ <li value="15"><b>Check-Out</b>: Check out versions</li>
  @ <li value="16"><b>Password</b>: Change your own password</li>
  @ <li value="17"><b>Query</b>: Create new queries against tickets</li>
  @ <li value="18"><b>Read-Tkt</b>: View tickets</li>
  @ <li value="19"><b>Setup:</b> Setup and configure this website</li>
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171

/*
** WEBPAGE: /setup_uedit
*/
void user_edit(void){
  const char *zId, *zLogin, *zInfo, *zCap;
  char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap ;
  char *oak, *oad, *oaq, *oac, *oaf, *oam, *oah, *oag;
  int doWrite;
  int uid;
  int higherUser = 0;  /* True if user being edited is SETUP and the */
                       /* user doing the editing is ADMIN.  Disallow editing */

  /* Must have ADMIN privleges to access this page
  */







|







158
159
160
161
162
163
164
165
166
167
168
169
170
171
172

/*
** WEBPAGE: /setup_uedit
*/
void user_edit(void){
  const char *zId, *zLogin, *zInfo, *zCap;
  char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap ;
  char *oak, *oad, *oaq, *oac, *oaf, *oam, *oah, *oag, *oal;
  int doWrite;
  int uid;
  int higherUser = 0;  /* True if user being edited is SETUP and the */
                       /* user doing the editing is ADMIN.  Disallow editing */

  /* Must have ADMIN privleges to access this page
  */
211
212
213
214
215
216
217

218
219
220
221
222
223
224
225
226

227
228
229
230
231
232
233
    int as = g.okSetup && P("as")!=0;
    int aw = P("aw")!=0;
    int ac = P("ac")!=0;
    int af = P("af")!=0;
    int am = P("am")!=0;
    int ah = P("ah")!=0;
    int ag = P("ag")!=0;

    if( aa ){ zCap[i++] = 'a'; }
    if( ac ){ zCap[i++] = 'c'; }
    if( ad ){ zCap[i++] = 'd'; }
    if( af ){ zCap[i++] = 'f'; }
    if( ah ){ zCap[i++] = 'h'; }
    if( ag ){ zCap[i++] = 'g'; }
    if( ai ){ zCap[i++] = 'i'; }
    if( aj ){ zCap[i++] = 'j'; }
    if( ak ){ zCap[i++] = 'k'; }

    if( am ){ zCap[i++] = 'm'; }
    if( an ){ zCap[i++] = 'n'; }
    if( ao ){ zCap[i++] = 'o'; }
    if( ap ){ zCap[i++] = 'p'; }
    if( aq ){ zCap[i++] = 'q'; }
    if( ar ){ zCap[i++] = 'r'; }
    if( as ){ zCap[i++] = 's'; }







>









>







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
    int as = g.okSetup && P("as")!=0;
    int aw = P("aw")!=0;
    int ac = P("ac")!=0;
    int af = P("af")!=0;
    int am = P("am")!=0;
    int ah = P("ah")!=0;
    int ag = P("ag")!=0;
    int al = P("al")!=0;
    if( aa ){ zCap[i++] = 'a'; }
    if( ac ){ zCap[i++] = 'c'; }
    if( ad ){ zCap[i++] = 'd'; }
    if( af ){ zCap[i++] = 'f'; }
    if( ah ){ zCap[i++] = 'h'; }
    if( ag ){ zCap[i++] = 'g'; }
    if( ai ){ zCap[i++] = 'i'; }
    if( aj ){ zCap[i++] = 'j'; }
    if( ak ){ zCap[i++] = 'k'; }
    if( al ){ zCap[i++] = 'l'; }
    if( am ){ zCap[i++] = 'm'; }
    if( an ){ zCap[i++] = 'n'; }
    if( ao ){ zCap[i++] = 'o'; }
    if( ap ){ zCap[i++] = 'p'; }
    if( aq ){ zCap[i++] = 'q'; }
    if( ar ){ zCap[i++] = 'r'; }
    if( as ){ zCap[i++] = 's'; }
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281

282
283
284
285
286
287
288
  }

  /* Load the existing information about the user, if any
  */
  zLogin = "";
  zInfo = "";
  zCap = "";
  oaa = oac = oad = oaf = oag = oah = oai = oaj = oak = oam =
        oan = oao = oap = oaq = oar = oas = oaw = "";
  if( uid ){
    zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
    zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
    zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
    if( strchr(zCap, 'a') ) oaa = " checked";
    if( strchr(zCap, 'c') ) oac = " checked";
    if( strchr(zCap, 'd') ) oad = " checked";
    if( strchr(zCap, 'f') ) oaf = " checked";
    if( strchr(zCap, 'g') ) oag = " checked";
    if( strchr(zCap, 'h') ) oah = " checked";
    if( strchr(zCap, 'i') ) oai = " checked";
    if( strchr(zCap, 'j') ) oaj = " checked";
    if( strchr(zCap, 'k') ) oak = " checked";

    if( strchr(zCap, 'm') ) oam = " checked";
    if( strchr(zCap, 'n') ) oan = " checked";
    if( strchr(zCap, 'o') ) oao = " checked";
    if( strchr(zCap, 'p') ) oap = " checked";
    if( strchr(zCap, 'q') ) oaq = " checked";
    if( strchr(zCap, 'r') ) oar = " checked";
    if( strchr(zCap, 's') ) oas = " checked";







|














>







263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
  }

  /* Load the existing information about the user, if any
  */
  zLogin = "";
  zInfo = "";
  zCap = "";
  oaa = oac = oad = oaf = oag = oah = oai = oaj = oak = oal = oam =
        oan = oao = oap = oaq = oar = oas = oaw = "";
  if( uid ){
    zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
    zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
    zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
    if( strchr(zCap, 'a') ) oaa = " checked";
    if( strchr(zCap, 'c') ) oac = " checked";
    if( strchr(zCap, 'd') ) oad = " checked";
    if( strchr(zCap, 'f') ) oaf = " checked";
    if( strchr(zCap, 'g') ) oag = " checked";
    if( strchr(zCap, 'h') ) oah = " checked";
    if( strchr(zCap, 'i') ) oai = " checked";
    if( strchr(zCap, 'j') ) oaj = " checked";
    if( strchr(zCap, 'k') ) oak = " checked";
    if( strchr(zCap, 'l') ) oal = " checked";
    if( strchr(zCap, 'm') ) oam = " checked";
    if( strchr(zCap, 'n') ) oan = " checked";
    if( strchr(zCap, 'o') ) oao = " checked";
    if( strchr(zCap, 'p') ) oap = " checked";
    if( strchr(zCap, 'q') ) oaq = " checked";
    if( strchr(zCap, 'r') ) oar = " checked";
    if( strchr(zCap, 's') ) oas = " checked";
330
331
332
333
334
335
336

337
338
339
340
341
342
343
  @     <input type="checkbox" name="ao"%s(oao)>Check-Out</input><br>
  @     <input type="checkbox" name="ah"%s(oah)>History</input><br>
  @     <input type="checkbox" name="ag"%s(oag)>Clone</input><br>
  @     <input type="checkbox" name="aj"%s(oaj)>Read Wiki</input><br>
  @     <input type="checkbox" name="af"%s(oaf)>New Wiki</input><br>
  @     <input type="checkbox" name="am"%s(oam)>Append Wiki</input><br>
  @     <input type="checkbox" name="ak"%s(oak)>Write Wiki</input><br>

  @     <input type="checkbox" name="ar"%s(oar)>Read Tkt</input><br>
  @     <input type="checkbox" name="an"%s(oan)>New Tkt</input><br>
  @     <input type="checkbox" name="ac"%s(oac)>Append Tkt</input><br>
  @     <input type="checkbox" name="aw"%s(oaw)>Write Tkt</input>
  @   </td>
  @ </tr>
  @ <tr>







>







334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
  @     <input type="checkbox" name="ao"%s(oao)>Check-Out</input><br>
  @     <input type="checkbox" name="ah"%s(oah)>History</input><br>
  @     <input type="checkbox" name="ag"%s(oag)>Clone</input><br>
  @     <input type="checkbox" name="aj"%s(oaj)>Read Wiki</input><br>
  @     <input type="checkbox" name="af"%s(oaf)>New Wiki</input><br>
  @     <input type="checkbox" name="am"%s(oam)>Append Wiki</input><br>
  @     <input type="checkbox" name="ak"%s(oak)>Write Wiki</input><br>
  @     <input type="checkbox" name="al"%s(oak)>Lock Wiki</input><br>
  @     <input type="checkbox" name="ar"%s(oar)>Read Tkt</input><br>
  @     <input type="checkbox" name="an"%s(oan)>New Tkt</input><br>
  @     <input type="checkbox" name="ac"%s(oac)>Append Tkt</input><br>
  @     <input type="checkbox" name="aw"%s(oaw)>Write Tkt</input>
  @   </td>
  @ </tr>
  @ <tr>
360
361
362
363
364
365
366
367


368
369
370
371
372
373
374
    @ </p></li>
    @
  }
  @
  @ <li><p>
  @ The <b>Delete</b> privilege give the user the ability to erase
  @ wiki, tickets, and atttachments that have been added by anonymous
  @ users.  This capability is intended for deletion of spam.


  @ </p></li>
  @
  @ <li><p>
  @ The <b>Query</b> privilege allows the user to create or edit
  @ report formats by specifying appropriate SQL.  Users can run
  @ existing reports without the Query privilege.
  @ </p></li>







|
>
>







365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
    @ </p></li>
    @
  }
  @
  @ <li><p>
  @ The <b>Delete</b> privilege give the user the ability to erase
  @ wiki, tickets, and atttachments that have been added by anonymous
  @ users.  This capability is intended for deletion of spam.  The
  @ delete capability is only in effect for 24 hours after the item
  @ is first posted.  The Setup user can delete anything at any time.
  @ </p></li>
  @
  @ <li><p>
  @ The <b>Query</b> privilege allows the user to create or edit
  @ report formats by specifying appropriate SQL.  Users can run
  @ existing reports without the Query privilege.
  @ </p></li>
Changes to src/style.c.
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
}

/*
** Compare two submenu items for sorting purposes
*/
static int submenuCompare(const void *a, const void *b){
  const struct Submenu *A = (const struct Submenu*)a;
  const struct Submenu *B = (const struct Submenu*)B;
  return strcmp(A->zLabel, B->zLabel);
}

/*
** Draw the header.
*/
void style_header(const char *zTitle){







|







58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
}

/*
** Compare two submenu items for sorting purposes
*/
static int submenuCompare(const void *a, const void *b){
  const struct Submenu *A = (const struct Submenu*)a;
  const struct Submenu *B = (const struct Submenu*)b;
  return strcmp(A->zLabel, B->zLabel);
}

/*
** Draw the header.
*/
void style_header(const char *zTitle){
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
    @ not logged in
    zLogInOut = "Login";
  }else{
    @ logged in as %h(g.zLogin)
  }
  @ </div>
  @ <div id="main-menu">
  @ <a href="%s(g.zBaseURL)/index">Home</a>
  if( g.okRead ){
    @ | <a href="%s(g.zBaseURL)/leaves">Leaves</a>
    @ | <a href="%s(g.zBaseURL)/timeline">Timeline</a>
  }
#if 0
  @ | <font color="#888888">Search</font>
  @ | <font color="#888888">Ticket</font>







|







89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
    @ not logged in
    zLogInOut = "Login";
  }else{
    @ logged in as %h(g.zLogin)
  }
  @ </div>
  @ <div id="main-menu">
  @ <a href="%s(g.zBaseURL)/home">Home</a>
  if( g.okRead ){
    @ | <a href="%s(g.zBaseURL)/leaves">Leaves</a>
    @ | <a href="%s(g.zBaseURL)/timeline">Timeline</a>
  }
#if 0
  @ | <font color="#888888">Search</font>
  @ | <font color="#888888">Ticket</font>
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
167
168
169
170
171
172
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
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
  /* end the <div id="page"> from style_header() */
  @ </div>
  @ <div id="style-footer">
  @ Fossil version %s(MANIFEST_VERSION) %s(MANIFEST_DATE)
  @ </div>
}

/*
** WEBPAGE: index
** WEBPAGE: home
** WEBPAGE: not_found
*/
void page_index(void){
  char *zHome = "Home";
  if( zHome ){
    g.zExtra = zHome;
    g.okRdWiki = 1;
    wiki_page();
  }else{
    style_header("Main Title Page");
    @ No homepage configured for this server
    style_footer();
  }
}

/*
** TODO: COPIED FROM WIKI.C... BAD
*/
/*
** Create a fake replicate of the "vfile" table as a TEMP table
** using the manifest identified by manid.
*/
static void style_create_fake_vfile(int manid){
  static const char zVfileDef[] =
    @ CREATE TEMP TABLE vfile(
    @   id INTEGER PRIMARY KEY,     -- ID of the checked out file
    @   vid INTEGER REFERENCES blob, -- The version this file is part of.
    @   chnged INT DEFAULT 0,       -- 0:unchnged 1:edited 2:m-chng 3:m-add
    @   deleted BOOLEAN DEFAULT 0,  -- True if deleted
    @   rid INTEGER,                -- Originally from this repository record
    @   mrid INTEGER,               -- Based on this record due to a merge
    @   pathname TEXT,              -- Full pathname
    @   UNIQUE(pathname,vid)
    @ );
    ;
  db_multi_exec(zVfileDef);
  load_vfile_from_rid(manid);
}


/*
** WEBPAGE: style.css
*/
void page_style_css(void){
  Stmt q;
  int id = 0;
  int rid = 0;
  int chnged = 0;
  char *zPathname = 0;
  char *z;

  cgi_set_content_type("text/css");

  login_check_credentials();
  if( !g.localOpen ){
    int headid = db_int(0,
       "SELECT cid FROM plink ORDER BY mtime DESC LIMIT 1"
    );
    style_create_fake_vfile(headid);
  }

  db_prepare(&q,
     "SELECT id, rid, chnged, pathname FROM vfile"
     " WHERE (pathname='style.css' OR pathname LIKE '%%/style.css')"
     "   AND NOT deleted"
  );
  if( db_step(&q)==SQLITE_ROW ){
    id = db_column_int(&q, 0);
    rid = db_column_int(&q, 1);
    chnged = db_column_int(&q, 2);
    if( chnged || rid==0 ){
      zPathname = db_column_malloc(&q, 3);
    }
  }
  db_finalize(&q);
  if( id ){
    Blob src;
    blob_zero(&src);
    if( zPathname ){
      zPathname = mprintf("%s/%z", g.zLocalRoot, zPathname);
      blob_read_from_file(&src, zPathname);
      free(zPathname);
    }else{
      content_get(rid, &src);
    }

    z = blob_str(&src);
    @ %s(z)
  }else{
    /* No CSS file found, use our own */
    /*
    ** Selector order: tags, ids, classes, other
    ** Content order: margin, borders, padding, fonts, colors, other
    ** Note: Once things are finialize a bit we can collapse this and
    **       make it much smaller, if necessary. Right now, it's verbose







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

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



<
<
<
<
<
|


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







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
  /* end the <div id="page"> from style_header() */
  @ </div>
  @ <div id="style-footer">
  @ Fossil version %s(MANIFEST_VERSION) %s(MANIFEST_DATE)
  @ </div>
}



















/*

























** WEBPAGE: style.css
*/
void page_style_css(void){





  char *zCSS = 0;

  cgi_set_content_type("text/css");
  zCSS = db_get("css",0);






















  if( zCSS ){









    cgi_append_content(zCSS, -1);


  }else{
    /* No CSS file found, use our own */
    /*
    ** Selector order: tags, ids, classes, other
    ** Content order: margin, borders, padding, fonts, colors, other
    ** Note: Once things are finialize a bit we can collapse this and
    **       make it much smaller, if necessary. Right now, it's verbose
Changes to src/timeline.c.
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
273
**
**    d=STARTDATE    date in iso8601 notation.          dflt: newest event
**    n=INTEGER      number of events to show.          dflt: 25
**    e=INTEGER      starting event id.                 dflt: nil
**    u=NAME         show only events from user.        dflt: nil
**    a              show events after and including.   dflt: false
**    r              show only related events.          dflt: false

*/
void page_timeline(void){
  Stmt q;
  char *zSQL;
  Blob scriptInit;
  char zDate[100];
  const char *zStart = P("d");
  int nEntry = atoi(PD("n","20"));
  const char *zUser = P("u");
  int objid = atoi(PD("e","0"));
  int relatedEvents = P("r")!=0;
  int afterFlag = P("a")!=0;

  int firstEvent;
  int lastEvent;

  /* To view the timeline, must have permission to read project data.
  */
  login_check_credentials();
  if( !g.okRead ){ login_needed(); return; }

  style_header("Timeline");
  if( !g.okHistory &&
      db_exists("SELECT 1 FROM user"
                " WHERE login='anonymous'"
                "   AND cap LIKE '%%h%%'") ){
    @ <p><b>Note:</b> You will be able to access <u>much</u> more
    @ historical information if <a href="%s(g.zBaseURL)/login">login</a>.</p>
  }
  zSQL = mprintf("%s", timeline_query_for_www());



  if( zUser ){
    zSQL = mprintf("%z AND event.user=%Q", zSQL, zUser);
  }
  if( objid ){
    char *z = db_text(0, "SELECT datetime(event.mtime) FROM event"
                         " WHERE objid=%d", objid);
    if( z ){







>












>














|


>
>
>







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
273
274
275
276
277
278
**
**    d=STARTDATE    date in iso8601 notation.          dflt: newest event
**    n=INTEGER      number of events to show.          dflt: 25
**    e=INTEGER      starting event id.                 dflt: nil
**    u=NAME         show only events from user.        dflt: nil
**    a              show events after and including.   dflt: false
**    r              show only related events.          dflt: false
**    y=TYPE         show only TYPE ('ci' or 'w')       dflt: nil
*/
void page_timeline(void){
  Stmt q;
  char *zSQL;
  Blob scriptInit;
  char zDate[100];
  const char *zStart = P("d");
  int nEntry = atoi(PD("n","20"));
  const char *zUser = P("u");
  int objid = atoi(PD("e","0"));
  int relatedEvents = P("r")!=0;
  int afterFlag = P("a")!=0;
  const char *zType = P("y");
  int firstEvent;
  int lastEvent;

  /* To view the timeline, must have permission to read project data.
  */
  login_check_credentials();
  if( !g.okRead ){ login_needed(); return; }

  style_header("Timeline");
  if( !g.okHistory &&
      db_exists("SELECT 1 FROM user"
                " WHERE login='anonymous'"
                "   AND cap LIKE '%%h%%'") ){
    @ <p><b>Note:</b> You will be able to access <u>much</u> more
    @ historical information if <a href="%s(g.zTop)/login">login</a>.</p>
  }
  zSQL = mprintf("%s", timeline_query_for_www());
  if( zType ){
    zSQL = mprintf("%z AND event.type=%Q", zSQL, zType);
  }
  if( zUser ){
    zSQL = mprintf("%z AND event.user=%Q", zSQL, zUser);
  }
  if( objid ){
    char *z = db_text(0, "SELECT datetime(event.mtime) FROM event"
                         " WHERE objid=%d", objid);
    if( z ){
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
  @ <input type="hidden" value="1" name="a">
  @ <input type="hidden" value="%d(nEntry)" name="n">
  @ <input type="submit" value="Previous %d(nEntry) Rows">
  @ </form>
  style_footer();
}


/*
** WEBPAGE: wlist
**
** Show the complete change history for a single wiki page.  The name
** of the wiki is in g.zExtra
*/
void wlist_page(void){
  Stmt q;
  char *zTitle;
  char *zSQL;
  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }
  zTitle = mprintf("History Of %h", g.zExtra);
  style_header(zTitle);
  free(zTitle);

  zSQL = mprintf("%s AND event.objid IN "
                 "  (SELECT rid FROM tagxref WHERE tagid="
                       "(SELECT tagid FROM tag WHERE tagname='wiki-%q'))",
                 timeline_query_for_www(), g.zExtra);
  db_prepare(&q, zSQL);
  free(zSQL);
  www_print_timeline(&q, 0, 0, 0, 0);
  db_finalize(&q);
  style_footer();
}

/*
** The input query q selects various records.  Print a human-readable
** summary of those records.
**
** Limit the number of entries printed to nLine.
** 
** The query should return these columns:







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







392
393
394
395
396
397
398




























399
400
401
402
403
404
405
  @ <input type="hidden" value="1" name="a">
  @ <input type="hidden" value="%d(nEntry)" name="n">
  @ <input type="submit" value="Previous %d(nEntry) Rows">
  @ </form>
  style_footer();
}





























/*
** The input query q selects various records.  Print a human-readable
** summary of those records.
**
** Limit the number of entries printed to nLine.
** 
** The query should return these columns:
Changes to src/wiki.c.
66
67
68
69
70
71
72



























73
74
75
76
77
78
79
80
81
82

83
84
85
86
87
88
89
    @ <li> Must be between 3 and 100 characters in length.
    @ </ul>
    style_footer();
    return 1;
  }
  return 0;
}




























/*
** WEBPAGE: wiki
** URL: /wiki/PAGENAME
*/
void wiki_page(void){
  char *zTag;
  int rid;
  Blob wiki;
  Manifest m;

  char *zPageName;
  char *zHtmlPageName;
  char *zBody = mprintf("%s","<i>Empty Page</i>");

  login_check_credentials();
  if( !g.okRdWiki ){ login_needed(); return; }
  zPageName = mprintf("%s", g.zExtra);







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










>







66
67
68
69
70
71
72
73
74
75
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
    @ <li> Must be between 3 and 100 characters in length.
    @ </ul>
    style_footer();
    return 1;
  }
  return 0;
}

/*
** WEBPAGE: home
** WEBPAGE: index
** WEBPAGE: not_found
*/
void home_page(void){
  char *zPageName = db_get("project-name",0);
  if( zPageName ){
    login_check_credentials();
    g.zExtra = zPageName;
    g.okRdWiki = 1;
    g.okApndWiki = 0;
    g.okWrWiki = 0;
    g.okHistory = 0;
    wiki_page();
    return;
  }
  style_header("Home");
  @ <p>This is a stub home-page for the project.
  @ To fill in this page, first go to
  @ <a href="%s(g.zBaseURL)/setup_config">setup/config</a>
  @ and establish a "Project Name".  Then create a
  @ wiki page with that name.  The content of that wiki page
  @ will be displayed in place of this message.
  style_footer();
}

/*
** WEBPAGE: wiki
** URL: /wiki/PAGENAME
*/
void wiki_page(void){
  char *zTag;
  int rid;
  Blob wiki;
  Manifest m;
  int seenHr = 0;
  char *zPageName;
  char *zHtmlPageName;
  char *zBody = mprintf("%s","<i>Empty Page</i>");

  login_check_credentials();
  if( !g.okRdWiki ){ login_needed(); return; }
  zPageName = mprintf("%s", g.zExtra);
102
103
104
105
106
107
108








109
110
111
112
113
114

115
116
117

118








119
120
121
122
123
124
125
    Blob content;
    content_get(rid, &content);
    manifest_parse(&m, &content);
    if( m.type==CFTYPE_WIKI ){
      zBody = m.zWiki;
    }
  }








  zHtmlPageName = mprintf("%h", zPageName);
  style_header(zHtmlPageName);
  blob_init(&wiki, zBody, -1);
  wiki_convert(&wiki, 0);
  blob_reset(&wiki);
  manifest_clear(&m);

  if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
    @ <hr>
    @ [<a href="%s(g.zBaseURL)/wikiedit/%s(g.zExtra)">Edit</a>]

  }








  style_footer();
}

/*
** WEBPAGE: wikiedit
** URL: /wikiedit/PAGENAME
*/







>
>
>
>
>
>
>
>






>



>

>
>
>
>
>
>
>
>







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
167
168
169
170
171
    Blob content;
    content_get(rid, &content);
    manifest_parse(&m, &content);
    if( m.type==CFTYPE_WIKI ){
      zBody = m.zWiki;
    }
  }
  if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
    style_submenu_element("Edit", "Edit Wiki Page", 
         mprintf("%s/wikiedit/%s", g.zTop, g.zExtra));
  }
  if( g.okHistory ){
    style_submenu_element("History", "History", 
         mprintf("%s/whistory/%s", g.zTop, g.zExtra));
  }
  zHtmlPageName = mprintf("%h", zPageName);
  style_header(zHtmlPageName);
  blob_init(&wiki, zBody, -1);
  wiki_convert(&wiki, 0);
  blob_reset(&wiki);
  manifest_clear(&m);
#if 0
  if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
    @ <hr>
    @ [<a href="%s(g.zBaseURL)/wikiedit/%s(g.zExtra)">Edit</a>]
    seenHr = 1;
  }
  if( g.okHistory ){
    if( !seenHr ){
      @ <hr>
      seenHr = 1;
    }
    @ [<a href="%s(g.zBaseUrl)/whistory/%s(g.zExtra)">History</a>]
  }
#endif
  style_footer();
}

/*
** WEBPAGE: wikiedit
** URL: /wikiedit/PAGENAME
*/
223
224
225
226
227
228
229



















































230
231
232
233
234
235
236
  @ <input type="submit" name="submit" value="Apply These Changes">
  @ <input type="submit" name="cancel" value="Cancel">
  @ </form>
  manifest_clear(&m);
  style_footer();

}




















































/*
** WEBPAGE: ambiguous
**
** This is the destination for UUID hyperlinks that are ambiguous.
** Show all possible choices for the destination with links to each.
**







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







269
270
271
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
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
  @ <input type="submit" name="submit" value="Apply These Changes">
  @ <input type="submit" name="cancel" value="Cancel">
  @ </form>
  manifest_clear(&m);
  style_footer();

}

/*
** WEBPAGE: whistory
**
** Show the complete change history for a single wiki page.  The name
** of the wiki is in g.zExtra
*/
void whistory_page(void){
  Stmt q;
  char *zTitle;
  char *zSQL;
  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }
  zTitle = mprintf("History Of %h", g.zExtra);
  style_header(zTitle);
  free(zTitle);

  zSQL = mprintf("%s AND event.objid IN "
                 "  (SELECT rid FROM tagxref WHERE tagid="
                       "(SELECT tagid FROM tag WHERE tagname='wiki-%q'))"
                 "ORDER BY mtime DESC",
                 timeline_query_for_www(), g.zExtra);
  db_prepare(&q, zSQL);
  free(zSQL);
  www_print_timeline(&q, 0, 0, 0, 0);
  db_finalize(&q);
  style_footer();
}

/*
** WEBPAGE: wcontent
**
** List all available wiki pages with date created and last modified.
*/
void wcontent_page(void){
  Stmt q;
  login_check_credentials();
  if( !g.okRdWiki ){ login_needed(); return; }
  style_header("Available Wiki Pages");
  @ <ul>
  db_prepare(&q, 
    "SELECT substr(tagname, 6, 1000) FROM tag WHERE tagname GLOB 'wiki-*'"
    " ORDER BY lower(tagname)"
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    @ <li><a href="%s(g.zBaseURL)/wiki/%t(zName)">%h(zName)</a></li>
  }
  db_finalize(&q);
  style_footer();
}

/*
** WEBPAGE: ambiguous
**
** This is the destination for UUID hyperlinks that are ambiguous.
** Show all possible choices for the destination with links to each.
**
Changes to src/wikiformat.c.
758
759
760
761
762
763
764


765
766
767
768
769
770
771
static void resolveHyperlink(const char *zTarget, Renderer *p){
  if( strncmp(zTarget, "http:", 5)==0 
   || strncmp(zTarget, "https:", 6)==0
   || strncmp(zTarget, "ftp:", 4)==0 
   || strncmp(zTarget, "mailto:", 7)==0
  ){
    blob_appendf(p->pOut, zTarget);


  }else if( wiki_name_is_wellformed(zTarget) ){
    blob_appendf(p->pOut, "%s/wiki/%T", g.zBaseURL, zTarget);
  }else{
    blob_appendf(p->pOut, "error");
  }
}








>
>







758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
static void resolveHyperlink(const char *zTarget, Renderer *p){
  if( strncmp(zTarget, "http:", 5)==0 
   || strncmp(zTarget, "https:", 6)==0
   || strncmp(zTarget, "ftp:", 4)==0 
   || strncmp(zTarget, "mailto:", 7)==0
  ){
    blob_appendf(p->pOut, zTarget);
  }else if( zTarget[0]=='/' ){
    blob_appendf(p->pOut, "%s%h", g.zBaseURL, zTarget);
  }else if( wiki_name_is_wellformed(zTarget) ){
    blob_appendf(p->pOut, "%s/wiki/%T", g.zBaseURL, zTarget);
  }else{
    blob_appendf(p->pOut, "error");
  }
}