Fossil

Check-in [fd36718ad9]
Login

Check-in [fd36718ad9]

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

Overview
Comment:Add the new "history" permission. Merge in changes that require permissions to view the timeline.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: fd36718ad98c4a01632eeccb777fec847c1de7af
User & Date: drh 2007-07-31 23:33:15.000
Context
2007-08-01
00:40
Improvements to the vinfo webpage. Show descendents, ancestors, and files changed. ... (check-in: 30d7afe328 user: drh tags: trunk)
2007-07-31
23:33
Add the new "history" permission. Merge in changes that require permissions to view the timeline. ... (check-in: fd36718ad9 user: drh tags: trunk)
22:59
Separate "nobody" and "anonymous" logins. ... (check-in: 9c952d247e user: drh tags: trunk)
16:34
Require read permission (permission to read tickets and configuration histories) to view the global timeline. ... (check-in: 5ebcedc33e user: dan tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/info.c.
114
115
116
117
118
119
120


121
122
123
124
125
126
127
*/
void vinfo_page(void){
  Stmt q;
  int rid;
  char cType;
  char *zType;



  style_header("Version Information");
  rid = name_to_rid(g.zExtra);
  if( rid==0 ){
    @ No such object: %h(g.argv[2])
    style_footer();
    return;
  }







>
>







114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
*/
void vinfo_page(void){
  Stmt q;
  int rid;
  char cType;
  char *zType;

  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }
  style_header("Version Information");
  rid = name_to_rid(g.zExtra);
  if( rid==0 ){
    @ No such object: %h(g.argv[2])
    style_footer();
    return;
  }
Changes to src/login.c.
258
259
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
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
      zCap = "";
    }
  }
  if( zCap==0 ){
    if( uid ){
      Stmt s;
      db_prepare(&s, "SELECT login, cap FROM user WHERE uid=%d", uid);
      db_step(&s);
      g.zLogin = db_column_malloc(&s, 0);
      zCap = db_column_malloc(&s, 1);

      db_finalize(&s);
    }
    if( zCap==0 ){
      zCap = "";
    }
  }
  g.userUid = uid;



  login_set_capabilities(zCap);
}

/*
** 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.okNewTkt = g.okPassword = 1;
      case 'i':   g.okRead = g.okWrite = 1;                     break;
      case 'o':   g.okRead = 1;                                 break;

      case 'd':   g.okDelete = 1;                               break;

      case 'p':   g.okPassword = 1;                             break;
      case 'q':   g.okQuery = 1;                                break;

      case 'j':   g.okRdWiki = 1;                               break;
      case 'k':   g.okWrWiki = g.okRdWiki = g.okApndWiki =1;    break;
      case 'm':   g.okApndWiki = 1;                             break;
      case 'f':   g.okNewWiki = 1;                              break;

      case 'r':   g.okRdTkt = 1;                                break;
      case 'n':   g.okNewTkt = 1;                               break;
      case 'w':   g.okWrTkt = g.okRdTkt = g.okNewTkt =
                  g.okApndTkt = 1;                              break;
      case 'c':   g.okApndTkt = 1;                              break;
    }
  }
}

/*
** Call this routine when the credential check fails.  It causes
** a redirect to the "login" page.
*/
void login_needed(void){
  const char *zUrl = PD("REQUEST_URI", "index");
  cgi_redirect(mprintf("login?nxp=%T", zUrl));
  /* NOTREACHED */
  assert(0);
}







|
|
|
>







>
>
>












|





>










|












|



258
259
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
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
      zCap = "";
    }
  }
  if( zCap==0 ){
    if( uid ){
      Stmt s;
      db_prepare(&s, "SELECT login, cap FROM user WHERE uid=%d", uid);
      if( db_step(&s)==SQLITE_ROW ){
        g.zLogin = db_column_malloc(&s, 0);
        zCap = db_column_malloc(&s, 1);
      }
      db_finalize(&s);
    }
    if( zCap==0 ){
      zCap = "";
    }
  }
  g.userUid = uid;
  if( g.zLogin && strcmp(g.zLogin,"nobody")==0 ){
    g.zLogin = 0;
  }
  login_set_capabilities(zCap);
}

/*
** 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.okHistory =
                              g.okNewTkt = g.okPassword = 1;
      case 'i':   g.okRead = g.okWrite = 1;                     break;
      case 'o':   g.okRead = 1;                                 break;

      case 'd':   g.okDelete = 1;                               break;
      case 'h':   g.okHistory = 1;                              break;
      case 'p':   g.okPassword = 1;                             break;
      case 'q':   g.okQuery = 1;                                break;

      case 'j':   g.okRdWiki = 1;                               break;
      case 'k':   g.okWrWiki = g.okRdWiki = g.okApndWiki =1;    break;
      case 'm':   g.okApndWiki = 1;                             break;
      case 'f':   g.okNewWiki = 1;                              break;

      case 'r':   g.okRdTkt = 1;                                break;
      case 'n':   g.okNewTkt = 1;                               break;
      case 'w':   g.okWrTkt = g.okRdTkt = g.okNewTkt = 
                  g.okApndTkt = 1;                              break;
      case 'c':   g.okApndTkt = 1;                              break;
    }
  }
}

/*
** Call this routine when the credential check fails.  It causes
** a redirect to the "login" page.
*/
void login_needed(void){
  const char *zUrl = PD("REQUEST_URI", "index");
  cgi_redirect(mprintf("login?g=%T", zUrl));
  /* NOTREACHED */
  assert(0);
}
Changes to src/main.c.
96
97
98
99
100
101
102

103
104
105
106
107
108
109
  int okWrWiki;           /* edit wiki via web */
  int okNewWiki;          /* create new wiki via web */
  int okApndWiki;         /* append to wiki via web */
  int okPassword;         /* change password */
  int okAdmin;            /* administrative permission */
  int okDelete;           /* delete wiki or tickets */
  int okQuery;            /* create new reports */


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

/*
** Macro for debugging:
*/







>







96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  int okWrWiki;           /* edit wiki via web */
  int okNewWiki;          /* create new wiki via web */
  int okApndWiki;         /* append to wiki via web */
  int okPassword;         /* change password */
  int okAdmin;            /* administrative permission */
  int okDelete;           /* delete wiki or tickets */
  int okQuery;            /* create new reports */
  int okHistory;          /* access historical information */

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

/*
** Macro for debugging:
*/
Changes to src/setup.c.
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125


126
127
128
129
130
131
132
    }
    @ </td>
    @ <td align="center">%s(db_column_text(&s,2))</td>
    @ <td align="left">%s(db_column_text(&s,3))</td>
    @ </tr>
  }
  @ </table></td></tr></table>
  @ <p>
  @ <b>Notes:</b>
  @ <ol>
  @ <li><p>The permission flags are as follows:</p>
  @ <table>
  @ <tr><td>a</td><td width="10"></td>
  @     <td>Admin: Create or delete users and ticket report formats</td></tr>
  @ <tr><td>d</td><td></td>
  @     <td>Delete: Erase anonymous wiki, tickets, and attachments</td></tr>


  @ <tr><td>i</td><td></td>
  @     <td>Check-in: Add new code to the repository</td></tr>
  @ <tr><td>j</td><td></td><td>Read-Wiki: View wiki pages</td></tr>
  @ <tr><td>k</td><td></td><td>Wiki: Create or modify wiki pages</td></tr>
  @ <tr><td>n</td><td></td><td>New: Create new tickets</td></tr>
  @ <tr><td>o</td><td></td>
  @     <td>Check-out: Read code out of the repository</td></tr>







|








>
>







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
    }
    @ </td>
    @ <td align="center">%s(db_column_text(&s,2))</td>
    @ <td align="left">%s(db_column_text(&s,3))</td>
    @ </tr>
  }
  @ </table></td></tr></table>
  @ <p style="clear:both">
  @ <b>Notes:</b>
  @ <ol>
  @ <li><p>The permission flags are as follows:</p>
  @ <table>
  @ <tr><td>a</td><td width="10"></td>
  @     <td>Admin: Create or delete users and ticket report formats</td></tr>
  @ <tr><td>d</td><td></td>
  @     <td>Delete: Erase anonymous wiki, tickets, and attachments</td></tr>
  @ <tr><td>h</td><td></td>
  @     <td>History: Access older version of code, tickets, or wiki</td></tr>
  @ <tr><td>i</td><td></td>
  @     <td>Check-in: Add new code to the repository</td></tr>
  @ <tr><td>j</td><td></td><td>Read-Wiki: View wiki pages</td></tr>
  @ <tr><td>k</td><td></td><td>Wiki: Create or modify wiki pages</td></tr>
  @ <tr><td>n</td><td></td><td>New: Create new tickets</td></tr>
  @ <tr><td>o</td><td></td>
  @     <td>Check-out: Read code out of the repository</td></tr>
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165

/*
** 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;
  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
  */







|







153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

/*
** 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;
  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
  */
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
  ** modified user record.  After writing the user record, redirect
  ** to the page that displays a list of users.
  */
  doWrite = cgi_all("login","info","pw") && !higherUser;
  if( doWrite ){
    const char *zPw;
    const char *zLogin;
    char zCap[20];
    int i = 0;
    int aa = P("aa")!=0;
    int ad = P("ad")!=0;
    int ai = P("ai")!=0;
    int aj = P("aj")!=0;
    int ak = P("ak")!=0;
    int an = P("an")!=0;
    int ao = P("ao")!=0;
    int ap = P("ap")!=0;
    int aq = P("aq")!=0;
    int ar = P("ar")!=0;
    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;
#if 0
    if( as ) aa = 1;
    if( aa ) ai = aw = ap = 1;
    if( aw ) an = ar = 1;
    if( ai ) ao = 1;
    if( ak ) aj = 1;
#endif
    if( aa ){ zCap[i++] = 'a'; }
    if( ac ){ zCap[i++] = 'c'; }
    if( ad ){ zCap[i++] = 'd'; }
    if( af ){ zCap[i++] = 'f'; }

    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'; }







|
















<
|
<
<
<
<
<




>







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
  ** modified user record.  After writing the user record, redirect
  ** to the page that displays a list of users.
  */
  doWrite = cgi_all("login","info","pw") && !higherUser;
  if( doWrite ){
    const char *zPw;
    const char *zLogin;
    char zCap[30];
    int i = 0;
    int aa = P("aa")!=0;
    int ad = P("ad")!=0;
    int ai = P("ai")!=0;
    int aj = P("aj")!=0;
    int ak = P("ak")!=0;
    int an = P("an")!=0;
    int ao = P("ao")!=0;
    int ap = P("ap")!=0;
    int aq = P("aq")!=0;
    int ar = P("ar")!=0;
    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;





    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( 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'; }
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273

274
275
276
277
278
279
280
  }

  /* Load the existing information about the user, if any
  */
  zLogin = "";
  zInfo = "";
  zCap = "";
  oaa = oac = oad = oaf = 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, '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";







|









>







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
  }

  /* Load the existing information about the user, if any
  */
  zLogin = "";
  zInfo = "";
  zCap = "";
  oaa = oac = oad = oaf = 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, '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";
319
320
321
322
323
324
325

326
327
328
329
330
331
332
  }
  @     <input type="checkbox" name="aa"%s(oaa)>Admin</input><br>
  @     <input type="checkbox" name="ad"%s(oad)>Delete</input><br>
  @     <input type="checkbox" name="ap"%s(oap)>Password</input><br>
  @     <input type="checkbox" name="aq"%s(oaq)>Query</input><br>
  @     <input type="checkbox" name="ai"%s(oai)>Check-In</input><br>
  @     <input type="checkbox" name="ao"%s(oao)>Check-Out</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>







>







317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
  }
  @     <input type="checkbox" name="aa"%s(oaa)>Admin</input><br>
  @     <input type="checkbox" name="ad"%s(oad)>Delete</input><br>
  @     <input type="checkbox" name="ap"%s(oap)>Password</input><br>
  @     <input type="checkbox" name="aq"%s(oaq)>Query</input><br>
  @     <input type="checkbox" name="ai"%s(oai)>Check-In</input><br>
  @     <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="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>
374
375
376
377
378
379
380
381


382
383
384
385
386
387
388
  @ </p></li>
  @
  @ <li><p>
  @ No login is required for user "<b>nobody</b>".  The capabilities
  @ of this user are available to anyone without supplying a username or
  @ password.  To disable nobody access, make sure there is no user
  @ with an ID of <b>nobody</b> or that the nobody user has no
  @ capabilities enabled.  The password for the noloing user is ignore.


  @ </p></li>
  @
  @ <li><p>
  @ Login is required for user "<b>anonymous</b>" but the password
  @ is displayed on the login screen beside the password entry box
  @ so anybody who can read should be able to login as anonymous.
  @ On the other hand, spiders and web-crawlers will typically not







|
>
>







373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
  @ </p></li>
  @
  @ <li><p>
  @ No login is required for user "<b>nobody</b>".  The capabilities
  @ of this user are available to anyone without supplying a username or
  @ password.  To disable nobody access, make sure there is no user
  @ with an ID of <b>nobody</b> or that the nobody user has no
  @ capabilities enabled.  The password for nobody is ignore.  To
  @ avoid problems with spiders overloading the server, it is suggested
  @ that the 'h' (History) capability be turned off for user nobody.
  @ </p></li>
  @
  @ <li><p>
  @ Login is required for user "<b>anonymous</b>" but the password
  @ is displayed on the login screen beside the password entry box
  @ so anybody who can read should be able to login as anonymous.
  @ On the other hand, spiders and web-crawlers will typically not
Changes to src/style.c.
82
83
84
85
86
87
88

89

90
91
92
93
94
95
96
    @ <small>not logged in</small>
    zLogInOut = "Login";
  }else{
    @ <small>logged in as %h(g.zLogin)</small>
  }
  @ </td><td valign="top" align="right">
  @ <a href="%s(g.zBaseURL)/index">Home</a>

  @ | <a href="%s(g.zBaseURL)/timeline">Timeline</a>

  if( g.okRdWiki ){
    @ | <a href="%s(g.zBaseURL)/wiki">Wiki</a>
  }
#if 0
  @ | <font color="#888888">Search</font>
  @ | <font color="#888888">Ticket</font>
  @ | <font color="#888888">Reports</font>







>
|
>







82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
    @ <small>not logged in</small>
    zLogInOut = "Login";
  }else{
    @ <small>logged in as %h(g.zLogin)</small>
  }
  @ </td><td valign="top" align="right">
  @ <a href="%s(g.zBaseURL)/index">Home</a>
  if( g.okRead ){
    @ | <a href="%s(g.zBaseURL)/timeline">Timeline</a>
  }
  if( g.okRdWiki ){
    @ | <a href="%s(g.zBaseURL)/wiki">Wiki</a>
  }
#if 0
  @ | <font color="#888888">Search</font>
  @ | <font color="#888888">Ticket</font>
  @ | <font color="#888888">Reports</font>
Changes to src/timeline.c.
29
30
31
32
33
34
35

36



37
38
39
40
41
42

43
44
45
46

47
48
49
50
51
52
53
54
55
56






57
58
59
60
61
62
63

/*
** Generate a hyperlink to a version.
*/
void hyperlink_to_uuid(const char *zUuid){
  char zShortUuid[UUID_SIZE+1];
  sprintf(zShortUuid, "%.10s", zUuid);

  @ <a href="%s(g.zBaseURL)/vinfo/%s(zUuid)">[%s(zShortUuid)]</a>



}

/*
** Generate a hyperlink to a diff between two versions.
*/
void hyperlink_to_diff(const char *zV1, const char *zV2){

  if( zV2==0 ){
    @ <a href="%s(g.zBaseURL)/diff?v2=%s(zV1)">[diff]</a>
  }else{
    @ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&v2=%s(zV2)">[diff]</a>

  }
}


/*
** WEBPAGE: timeline
*/
void page_timeline(void){
  Stmt q;
  char zPrevDate[20];






  style_header("Timeline");
  zPrevDate[0] = 0;
  db_prepare(&q,
    "SELECT uuid, datetime(event.mtime,'localtime'), comment, user"
    "  FROM event, blob"
    " WHERE event.type='ci' AND blob.rid=event.objid"
    " ORDER BY event.mtime DESC"







>
|
>
>
>






>
|
|
|
|
>










>
>
>
>
>
>







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

/*
** Generate a hyperlink to a version.
*/
void hyperlink_to_uuid(const char *zUuid){
  char zShortUuid[UUID_SIZE+1];
  sprintf(zShortUuid, "%.10s", zUuid);
  if( g.okHistory ){
    @ <a href="%s(g.zBaseURL)/vinfo/%s(zUuid)">[%s(zShortUuid)]</a>
  }else{
    @ <b>[%s(zShortUuid)]</b>
  }
}

/*
** Generate a hyperlink to a diff between two versions.
*/
void hyperlink_to_diff(const char *zV1, const char *zV2){
  if( g.okHistory ){
    if( zV2==0 ){
      @ <a href="%s(g.zBaseURL)/diff?v2=%s(zV1)">[diff]</a>
    }else{
      @ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&v2=%s(zV2)">[diff]</a>
    }
  }
}


/*
** WEBPAGE: timeline
*/
void page_timeline(void){
  Stmt q;
  char zPrevDate[20];

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

  style_header("Timeline");
  zPrevDate[0] = 0;
  db_prepare(&q,
    "SELECT uuid, datetime(event.mtime,'localtime'), comment, user"
    "  FROM event, blob"
    " WHERE event.type='ci' AND blob.rid=event.objid"
    " ORDER BY event.mtime DESC"