Fossil SCM
Add submenu entries on timeline pages for selecting options such as "tickets only" and "200 entries per page" and so forth.
Commit
c9cd128c2c2749739e6423fd668a5682395f7e76
Parent
0b36f02f158e464…
2 files changed
+52
-11
+72
+52
-11
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -211,10 +211,24 @@ | ||
| 211 | 211 | @ FROM event JOIN blob |
| 212 | 212 | @ WHERE blob.rid=event.objid |
| 213 | 213 | ; |
| 214 | 214 | return zBaseSql; |
| 215 | 215 | } |
| 216 | + | |
| 217 | +/* | |
| 218 | +** Generate a submenu element with a single parameter change. | |
| 219 | +*/ | |
| 220 | +static void timeline_submenu( | |
| 221 | + HQuery *pUrl, /* Base URL */ | |
| 222 | + const char *zMenuName, /* Submenu name */ | |
| 223 | + const char *zParam, /* Parameter value to add or change */ | |
| 224 | + const char *zValue, /* Value of the new parameter */ | |
| 225 | + const char *zRemove /* Parameter to omit */ | |
| 226 | +){ | |
| 227 | + style_submenu_element(zMenuName, zMenuName, "%s", | |
| 228 | + url_render(pUrl, zParam, zValue, zRemove, 0)); | |
| 229 | +} | |
| 216 | 230 | |
| 217 | 231 | /* |
| 218 | 232 | ** WEBPAGE: timeline |
| 219 | 233 | ** |
| 220 | 234 | ** Query parameters: |
| @@ -223,11 +237,11 @@ | ||
| 223 | 237 | ** b=TIMESTAMP before this date. |
| 224 | 238 | ** n=COUNT number of events in output |
| 225 | 239 | ** p=RID artifact RID and up to COUNT parents and ancestors |
| 226 | 240 | ** d=RID artifact RID and up to COUNT descendants |
| 227 | 241 | ** u=USER only if belonging to this user |
| 228 | -** y=TYPE 'ci', 'w', 'tkt' | |
| 242 | +** y=TYPE 'ci', 'w', 't' | |
| 229 | 243 | ** |
| 230 | 244 | ** p= and d= can appear individually or together. If either p= or d= |
| 231 | 245 | ** appear, then u=, y=, a=, and b= are ignored. |
| 232 | 246 | ** |
| 233 | 247 | ** If a= and b= appear, only a= is used. If neither appear, the most |
| @@ -241,13 +255,14 @@ | ||
| 241 | 255 | Blob desc; /* Description of the timeline */ |
| 242 | 256 | int nEntry = atoi(PD("n","20")); /* Max number of entries on timeline */ |
| 243 | 257 | int p_rid = atoi(PD("p","0")); /* artifact p and its parents */ |
| 244 | 258 | int d_rid = atoi(PD("d","0")); /* artifact d and its descendants */ |
| 245 | 259 | const char *zUser = P("u"); /* All entries by this user if not NULL */ |
| 246 | - const char *zType = P("y"); /* Type of events. All if NULL */ | |
| 260 | + const char *zType = PD("y","all"); /* Type of events. All if NULL */ | |
| 247 | 261 | const char *zAfter = P("a"); /* Events after this time */ |
| 248 | 262 | const char *zBefore = P("b"); /* Events before this time */ |
| 263 | + HQuery url; /* URL for various branch links */ | |
| 249 | 264 | |
| 250 | 265 | /* To view the timeline, must have permission to read project data. |
| 251 | 266 | */ |
| 252 | 267 | login_check_credentials(); |
| 253 | 268 | if( !g.okRead ){ login_needed(); return; } |
| @@ -297,18 +312,18 @@ | ||
| 297 | 312 | blob_appendf(&desc, " of [%.10s]", zUuid); |
| 298 | 313 | } |
| 299 | 314 | db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC"); |
| 300 | 315 | }else{ |
| 301 | 316 | int n; |
| 302 | - Blob url; | |
| 303 | 317 | const char *zEType = "event"; |
| 304 | - const char *zDate; | |
| 305 | - blob_zero(&url); | |
| 306 | - blob_appendf(&url, "%s/timeline?n=%d", g.zBaseURL, nEntry); | |
| 307 | - if( zType ){ | |
| 318 | + char *zDate; | |
| 319 | + char *zNEntry = mprintf("%d", nEntry); | |
| 320 | + url_initialize(&url, "timeline"); | |
| 321 | + url_add_parameter(&url, "n", zNEntry); | |
| 322 | + if( zType[0]!='a' ){ | |
| 308 | 323 | blob_appendf(&sql, " AND event.type=%Q", zType); |
| 309 | - blob_appendf(&url, "&y=%T", zType); | |
| 324 | + url_add_parameter(&url, "y", zType); | |
| 310 | 325 | if( zType[0]=='c' ){ |
| 311 | 326 | zEType = "checkin"; |
| 312 | 327 | }else if( zType[0]=='w' ){ |
| 313 | 328 | zEType = "wiki edit"; |
| 314 | 329 | }else if( zType[0]=='t' ){ |
| @@ -315,18 +330,19 @@ | ||
| 315 | 330 | zEType = "ticket change"; |
| 316 | 331 | } |
| 317 | 332 | } |
| 318 | 333 | if( zUser ){ |
| 319 | 334 | blob_appendf(&sql, " AND event.user=%Q", zUser); |
| 320 | - blob_appendf(&url, "&u=%T", zUser); | |
| 335 | + url_add_parameter(&url, "u", zUser); | |
| 321 | 336 | } |
| 322 | 337 | if( zAfter ){ |
| 323 | 338 | while( isspace(zAfter[0]) ){ zAfter++; } |
| 324 | 339 | if( zAfter[0] ){ |
| 325 | 340 | blob_appendf(&sql, |
| 326 | 341 | " AND event.mtime>=(SELECT julianday(%Q, 'utc'))" |
| 327 | 342 | " ORDER BY event.mtime ASC", zAfter); |
| 343 | + url_add_parameter(&url, "a", zAfter); | |
| 328 | 344 | zBefore = 0; |
| 329 | 345 | }else{ |
| 330 | 346 | zAfter = 0; |
| 331 | 347 | } |
| 332 | 348 | }else if( zBefore ){ |
| @@ -333,10 +349,11 @@ | ||
| 333 | 349 | while( isspace(zBefore[0]) ){ zBefore++; } |
| 334 | 350 | if( zBefore[0] ){ |
| 335 | 351 | blob_appendf(&sql, |
| 336 | 352 | " AND event.mtime<=(SELECT julianday(%Q, 'utc'))" |
| 337 | 353 | " ORDER BY event.mtime DESC", zBefore); |
| 354 | + url_add_parameter(&url, "b", zBefore); | |
| 338 | 355 | }else{ |
| 339 | 356 | zBefore = 0; |
| 340 | 357 | } |
| 341 | 358 | }else{ |
| 342 | 359 | blob_appendf(&sql, " ORDER BY event.mtime DESC"); |
| @@ -343,10 +360,13 @@ | ||
| 343 | 360 | } |
| 344 | 361 | blob_appendf(&sql, " LIMIT %d", nEntry); |
| 345 | 362 | db_multi_exec("%s", blob_str(&sql)); |
| 346 | 363 | |
| 347 | 364 | n = db_int(0, "SELECT count(*) FROM timeline"); |
| 365 | + if( n<nEntry && zAfter ){ | |
| 366 | + cgi_redirect(url_render(&url, "a", 0, "b", 0)); | |
| 367 | + } | |
| 348 | 368 | if( zAfter==0 && zBefore==0 ){ |
| 349 | 369 | blob_appendf(&desc, "%d most recent %ss", n, zEType); |
| 350 | 370 | }else{ |
| 351 | 371 | blob_appendf(&desc, "%d %ss", n, zEType); |
| 352 | 372 | } |
| @@ -359,15 +379,36 @@ | ||
| 359 | 379 | blob_appendf(&desc, " occurring on or before %h.<br>", zBefore); |
| 360 | 380 | } |
| 361 | 381 | if( g.okHistory ){ |
| 362 | 382 | if( zAfter || n==nEntry ){ |
| 363 | 383 | zDate = db_text(0, "SELECT min(timestamp) FROM timeline"); |
| 364 | - blob_appendf(&desc, " <a href='%b&b=%s'>[older]</a>", &url, zDate); | |
| 384 | + timeline_submenu(&url, "Older", "b", zDate, "a"); | |
| 385 | + free(zDate); | |
| 365 | 386 | } |
| 366 | 387 | if( zBefore || (zAfter && n==nEntry) ){ |
| 367 | 388 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline"); |
| 368 | - blob_appendf(&desc, " <a href='%b&a=%s'>[more recent]</a>", &url,zDate); | |
| 389 | + timeline_submenu(&url, "Newer", "a", zDate, "b"); | |
| 390 | + free(zDate); | |
| 391 | + }else{ | |
| 392 | + if( zType[0]!='a' ){ | |
| 393 | + timeline_submenu(&url, "All Types", "y", "all", 0); | |
| 394 | + } | |
| 395 | + if( zType[0]!='w' ){ | |
| 396 | + timeline_submenu(&url, "Wiki Only", "y", "w", 0); | |
| 397 | + } | |
| 398 | + if( zType[0]!='c' ){ | |
| 399 | + timeline_submenu(&url, "Checkins Only", "y", "ci", 0); | |
| 400 | + } | |
| 401 | + if( zType[0]!='t' ){ | |
| 402 | + timeline_submenu(&url, "Tickets Only", "y", "t", 0); | |
| 403 | + } | |
| 404 | + } | |
| 405 | + if( nEntry>20 ){ | |
| 406 | + timeline_submenu(&url, "20 Events", "n", "20", 0); | |
| 407 | + } | |
| 408 | + if( nEntry<200 ){ | |
| 409 | + timeline_submenu(&url, "200 Events", "n", "200", 0); | |
| 369 | 410 | } |
| 370 | 411 | } |
| 371 | 412 | } |
| 372 | 413 | blob_zero(&sql); |
| 373 | 414 | db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC"); |
| 374 | 415 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -211,10 +211,24 @@ | |
| 211 | @ FROM event JOIN blob |
| 212 | @ WHERE blob.rid=event.objid |
| 213 | ; |
| 214 | return zBaseSql; |
| 215 | } |
| 216 | |
| 217 | /* |
| 218 | ** WEBPAGE: timeline |
| 219 | ** |
| 220 | ** Query parameters: |
| @@ -223,11 +237,11 @@ | |
| 223 | ** b=TIMESTAMP before this date. |
| 224 | ** n=COUNT number of events in output |
| 225 | ** p=RID artifact RID and up to COUNT parents and ancestors |
| 226 | ** d=RID artifact RID and up to COUNT descendants |
| 227 | ** u=USER only if belonging to this user |
| 228 | ** y=TYPE 'ci', 'w', 'tkt' |
| 229 | ** |
| 230 | ** p= and d= can appear individually or together. If either p= or d= |
| 231 | ** appear, then u=, y=, a=, and b= are ignored. |
| 232 | ** |
| 233 | ** If a= and b= appear, only a= is used. If neither appear, the most |
| @@ -241,13 +255,14 @@ | |
| 241 | Blob desc; /* Description of the timeline */ |
| 242 | int nEntry = atoi(PD("n","20")); /* Max number of entries on timeline */ |
| 243 | int p_rid = atoi(PD("p","0")); /* artifact p and its parents */ |
| 244 | int d_rid = atoi(PD("d","0")); /* artifact d and its descendants */ |
| 245 | const char *zUser = P("u"); /* All entries by this user if not NULL */ |
| 246 | const char *zType = P("y"); /* Type of events. All if NULL */ |
| 247 | const char *zAfter = P("a"); /* Events after this time */ |
| 248 | const char *zBefore = P("b"); /* Events before this time */ |
| 249 | |
| 250 | /* To view the timeline, must have permission to read project data. |
| 251 | */ |
| 252 | login_check_credentials(); |
| 253 | if( !g.okRead ){ login_needed(); return; } |
| @@ -297,18 +312,18 @@ | |
| 297 | blob_appendf(&desc, " of [%.10s]", zUuid); |
| 298 | } |
| 299 | db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC"); |
| 300 | }else{ |
| 301 | int n; |
| 302 | Blob url; |
| 303 | const char *zEType = "event"; |
| 304 | const char *zDate; |
| 305 | blob_zero(&url); |
| 306 | blob_appendf(&url, "%s/timeline?n=%d", g.zBaseURL, nEntry); |
| 307 | if( zType ){ |
| 308 | blob_appendf(&sql, " AND event.type=%Q", zType); |
| 309 | blob_appendf(&url, "&y=%T", zType); |
| 310 | if( zType[0]=='c' ){ |
| 311 | zEType = "checkin"; |
| 312 | }else if( zType[0]=='w' ){ |
| 313 | zEType = "wiki edit"; |
| 314 | }else if( zType[0]=='t' ){ |
| @@ -315,18 +330,19 @@ | |
| 315 | zEType = "ticket change"; |
| 316 | } |
| 317 | } |
| 318 | if( zUser ){ |
| 319 | blob_appendf(&sql, " AND event.user=%Q", zUser); |
| 320 | blob_appendf(&url, "&u=%T", zUser); |
| 321 | } |
| 322 | if( zAfter ){ |
| 323 | while( isspace(zAfter[0]) ){ zAfter++; } |
| 324 | if( zAfter[0] ){ |
| 325 | blob_appendf(&sql, |
| 326 | " AND event.mtime>=(SELECT julianday(%Q, 'utc'))" |
| 327 | " ORDER BY event.mtime ASC", zAfter); |
| 328 | zBefore = 0; |
| 329 | }else{ |
| 330 | zAfter = 0; |
| 331 | } |
| 332 | }else if( zBefore ){ |
| @@ -333,10 +349,11 @@ | |
| 333 | while( isspace(zBefore[0]) ){ zBefore++; } |
| 334 | if( zBefore[0] ){ |
| 335 | blob_appendf(&sql, |
| 336 | " AND event.mtime<=(SELECT julianday(%Q, 'utc'))" |
| 337 | " ORDER BY event.mtime DESC", zBefore); |
| 338 | }else{ |
| 339 | zBefore = 0; |
| 340 | } |
| 341 | }else{ |
| 342 | blob_appendf(&sql, " ORDER BY event.mtime DESC"); |
| @@ -343,10 +360,13 @@ | |
| 343 | } |
| 344 | blob_appendf(&sql, " LIMIT %d", nEntry); |
| 345 | db_multi_exec("%s", blob_str(&sql)); |
| 346 | |
| 347 | n = db_int(0, "SELECT count(*) FROM timeline"); |
| 348 | if( zAfter==0 && zBefore==0 ){ |
| 349 | blob_appendf(&desc, "%d most recent %ss", n, zEType); |
| 350 | }else{ |
| 351 | blob_appendf(&desc, "%d %ss", n, zEType); |
| 352 | } |
| @@ -359,15 +379,36 @@ | |
| 359 | blob_appendf(&desc, " occurring on or before %h.<br>", zBefore); |
| 360 | } |
| 361 | if( g.okHistory ){ |
| 362 | if( zAfter || n==nEntry ){ |
| 363 | zDate = db_text(0, "SELECT min(timestamp) FROM timeline"); |
| 364 | blob_appendf(&desc, " <a href='%b&b=%s'>[older]</a>", &url, zDate); |
| 365 | } |
| 366 | if( zBefore || (zAfter && n==nEntry) ){ |
| 367 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline"); |
| 368 | blob_appendf(&desc, " <a href='%b&a=%s'>[more recent]</a>", &url,zDate); |
| 369 | } |
| 370 | } |
| 371 | } |
| 372 | blob_zero(&sql); |
| 373 | db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC"); |
| 374 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -211,10 +211,24 @@ | |
| 211 | @ FROM event JOIN blob |
| 212 | @ WHERE blob.rid=event.objid |
| 213 | ; |
| 214 | return zBaseSql; |
| 215 | } |
| 216 | |
| 217 | /* |
| 218 | ** Generate a submenu element with a single parameter change. |
| 219 | */ |
| 220 | static void timeline_submenu( |
| 221 | HQuery *pUrl, /* Base URL */ |
| 222 | const char *zMenuName, /* Submenu name */ |
| 223 | const char *zParam, /* Parameter value to add or change */ |
| 224 | const char *zValue, /* Value of the new parameter */ |
| 225 | const char *zRemove /* Parameter to omit */ |
| 226 | ){ |
| 227 | style_submenu_element(zMenuName, zMenuName, "%s", |
| 228 | url_render(pUrl, zParam, zValue, zRemove, 0)); |
| 229 | } |
| 230 | |
| 231 | /* |
| 232 | ** WEBPAGE: timeline |
| 233 | ** |
| 234 | ** Query parameters: |
| @@ -223,11 +237,11 @@ | |
| 237 | ** b=TIMESTAMP before this date. |
| 238 | ** n=COUNT number of events in output |
| 239 | ** p=RID artifact RID and up to COUNT parents and ancestors |
| 240 | ** d=RID artifact RID and up to COUNT descendants |
| 241 | ** u=USER only if belonging to this user |
| 242 | ** y=TYPE 'ci', 'w', 't' |
| 243 | ** |
| 244 | ** p= and d= can appear individually or together. If either p= or d= |
| 245 | ** appear, then u=, y=, a=, and b= are ignored. |
| 246 | ** |
| 247 | ** If a= and b= appear, only a= is used. If neither appear, the most |
| @@ -241,13 +255,14 @@ | |
| 255 | Blob desc; /* Description of the timeline */ |
| 256 | int nEntry = atoi(PD("n","20")); /* Max number of entries on timeline */ |
| 257 | int p_rid = atoi(PD("p","0")); /* artifact p and its parents */ |
| 258 | int d_rid = atoi(PD("d","0")); /* artifact d and its descendants */ |
| 259 | const char *zUser = P("u"); /* All entries by this user if not NULL */ |
| 260 | const char *zType = PD("y","all"); /* Type of events. All if NULL */ |
| 261 | const char *zAfter = P("a"); /* Events after this time */ |
| 262 | const char *zBefore = P("b"); /* Events before this time */ |
| 263 | HQuery url; /* URL for various branch links */ |
| 264 | |
| 265 | /* To view the timeline, must have permission to read project data. |
| 266 | */ |
| 267 | login_check_credentials(); |
| 268 | if( !g.okRead ){ login_needed(); return; } |
| @@ -297,18 +312,18 @@ | |
| 312 | blob_appendf(&desc, " of [%.10s]", zUuid); |
| 313 | } |
| 314 | db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC"); |
| 315 | }else{ |
| 316 | int n; |
| 317 | const char *zEType = "event"; |
| 318 | char *zDate; |
| 319 | char *zNEntry = mprintf("%d", nEntry); |
| 320 | url_initialize(&url, "timeline"); |
| 321 | url_add_parameter(&url, "n", zNEntry); |
| 322 | if( zType[0]!='a' ){ |
| 323 | blob_appendf(&sql, " AND event.type=%Q", zType); |
| 324 | url_add_parameter(&url, "y", zType); |
| 325 | if( zType[0]=='c' ){ |
| 326 | zEType = "checkin"; |
| 327 | }else if( zType[0]=='w' ){ |
| 328 | zEType = "wiki edit"; |
| 329 | }else if( zType[0]=='t' ){ |
| @@ -315,18 +330,19 @@ | |
| 330 | zEType = "ticket change"; |
| 331 | } |
| 332 | } |
| 333 | if( zUser ){ |
| 334 | blob_appendf(&sql, " AND event.user=%Q", zUser); |
| 335 | url_add_parameter(&url, "u", zUser); |
| 336 | } |
| 337 | if( zAfter ){ |
| 338 | while( isspace(zAfter[0]) ){ zAfter++; } |
| 339 | if( zAfter[0] ){ |
| 340 | blob_appendf(&sql, |
| 341 | " AND event.mtime>=(SELECT julianday(%Q, 'utc'))" |
| 342 | " ORDER BY event.mtime ASC", zAfter); |
| 343 | url_add_parameter(&url, "a", zAfter); |
| 344 | zBefore = 0; |
| 345 | }else{ |
| 346 | zAfter = 0; |
| 347 | } |
| 348 | }else if( zBefore ){ |
| @@ -333,10 +349,11 @@ | |
| 349 | while( isspace(zBefore[0]) ){ zBefore++; } |
| 350 | if( zBefore[0] ){ |
| 351 | blob_appendf(&sql, |
| 352 | " AND event.mtime<=(SELECT julianday(%Q, 'utc'))" |
| 353 | " ORDER BY event.mtime DESC", zBefore); |
| 354 | url_add_parameter(&url, "b", zBefore); |
| 355 | }else{ |
| 356 | zBefore = 0; |
| 357 | } |
| 358 | }else{ |
| 359 | blob_appendf(&sql, " ORDER BY event.mtime DESC"); |
| @@ -343,10 +360,13 @@ | |
| 360 | } |
| 361 | blob_appendf(&sql, " LIMIT %d", nEntry); |
| 362 | db_multi_exec("%s", blob_str(&sql)); |
| 363 | |
| 364 | n = db_int(0, "SELECT count(*) FROM timeline"); |
| 365 | if( n<nEntry && zAfter ){ |
| 366 | cgi_redirect(url_render(&url, "a", 0, "b", 0)); |
| 367 | } |
| 368 | if( zAfter==0 && zBefore==0 ){ |
| 369 | blob_appendf(&desc, "%d most recent %ss", n, zEType); |
| 370 | }else{ |
| 371 | blob_appendf(&desc, "%d %ss", n, zEType); |
| 372 | } |
| @@ -359,15 +379,36 @@ | |
| 379 | blob_appendf(&desc, " occurring on or before %h.<br>", zBefore); |
| 380 | } |
| 381 | if( g.okHistory ){ |
| 382 | if( zAfter || n==nEntry ){ |
| 383 | zDate = db_text(0, "SELECT min(timestamp) FROM timeline"); |
| 384 | timeline_submenu(&url, "Older", "b", zDate, "a"); |
| 385 | free(zDate); |
| 386 | } |
| 387 | if( zBefore || (zAfter && n==nEntry) ){ |
| 388 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline"); |
| 389 | timeline_submenu(&url, "Newer", "a", zDate, "b"); |
| 390 | free(zDate); |
| 391 | }else{ |
| 392 | if( zType[0]!='a' ){ |
| 393 | timeline_submenu(&url, "All Types", "y", "all", 0); |
| 394 | } |
| 395 | if( zType[0]!='w' ){ |
| 396 | timeline_submenu(&url, "Wiki Only", "y", "w", 0); |
| 397 | } |
| 398 | if( zType[0]!='c' ){ |
| 399 | timeline_submenu(&url, "Checkins Only", "y", "ci", 0); |
| 400 | } |
| 401 | if( zType[0]!='t' ){ |
| 402 | timeline_submenu(&url, "Tickets Only", "y", "t", 0); |
| 403 | } |
| 404 | } |
| 405 | if( nEntry>20 ){ |
| 406 | timeline_submenu(&url, "20 Events", "n", "20", 0); |
| 407 | } |
| 408 | if( nEntry<200 ){ |
| 409 | timeline_submenu(&url, "200 Events", "n", "200", 0); |
| 410 | } |
| 411 | } |
| 412 | } |
| 413 | blob_zero(&sql); |
| 414 | db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC"); |
| 415 |
+72
| --- src/url.c | ||
| +++ src/url.c | ||
| @@ -173,5 +173,77 @@ | ||
| 173 | 173 | url_parse(zProxy); |
| 174 | 174 | g.urlPath = zOriginalUrl; |
| 175 | 175 | g.urlHostname = zOriginalHost; |
| 176 | 176 | } |
| 177 | 177 | } |
| 178 | + | |
| 179 | +#if INTERFACE | |
| 180 | +/* | |
| 181 | +** An instance of this object is used to build a URL with query parameters. | |
| 182 | +*/ | |
| 183 | +struct HQuery { | |
| 184 | + Blob url; /* The URL */ | |
| 185 | + const char *zBase; /* The base URL */ | |
| 186 | + int nParam; /* Number of parameters. Max 10 */ | |
| 187 | + const char *azName[10]; /* Parameter names */ | |
| 188 | + const char *azValue[10]; /* Parameter values */ | |
| 189 | +}; | |
| 190 | +#endif | |
| 191 | + | |
| 192 | +/* | |
| 193 | +** Initialize the URL object. | |
| 194 | +*/ | |
| 195 | +void url_initialize(HQuery *p, const char *zBase){ | |
| 196 | + blob_zero(&p->url); | |
| 197 | + p->zBase = zBase; | |
| 198 | + p->nParam = 0; | |
| 199 | +} | |
| 200 | + | |
| 201 | +/* | |
| 202 | +** Add a fixed parameter to an HQuery. | |
| 203 | +*/ | |
| 204 | +void url_add_parameter(HQuery *p, const char *zName, const char *zValue){ | |
| 205 | + assert( p->nParam < count(p->azName) ); | |
| 206 | + assert( p->nParam < count(p->azValue) ); | |
| 207 | + p->azName[p->nParam] = zName; | |
| 208 | + p->azValue[p->nParam] = zValue; | |
| 209 | + p->nParam++; | |
| 210 | +} | |
| 211 | + | |
| 212 | +/* | |
| 213 | +** Render the URL with a parameter override. | |
| 214 | +*/ | |
| 215 | +char *url_render( | |
| 216 | + HQuery *p, /* Base URL */ | |
| 217 | + const char *zName1, /* First override */ | |
| 218 | + const char *zValue1, /* First override value */ | |
| 219 | + const char *zName2, /* Second override */ | |
| 220 | + const char *zValue2 /* Second override value */ | |
| 221 | +){ | |
| 222 | + const char *zSep = "?"; | |
| 223 | + int i; | |
| 224 | + | |
| 225 | + blob_reset(&p->url); | |
| 226 | + blob_appendf(&p->url, "%s/%s", g.zBaseURL, p->zBase); | |
| 227 | + for(i=0; i<p->nParam; i++){ | |
| 228 | + const char *z = p->azValue[i]; | |
| 229 | + if( zName1 && strcmp(zName1,p->azName[i])==0 ){ | |
| 230 | + zName1 = 0; | |
| 231 | + z = zValue1; | |
| 232 | + if( z==0 ) continue; | |
| 233 | + } | |
| 234 | + if( zName2 && strcmp(zName2,p->azName[i])==0 ){ | |
| 235 | + zName2 = 0; | |
| 236 | + z = zValue2; | |
| 237 | + if( z==0 ) continue; | |
| 238 | + } | |
| 239 | + blob_appendf(&p->url, "%s%s=%T", zSep, p->azName[i], z); | |
| 240 | + zSep = "&"; | |
| 241 | + } | |
| 242 | + if( zName1 && zValue1 ){ | |
| 243 | + blob_appendf(&p->url, "%s%s=%T", zSep, zName1, zValue1); | |
| 244 | + } | |
| 245 | + if( zName2 && zValue2 ){ | |
| 246 | + blob_appendf(&p->url, "%s%s=%T", zSep, zName2, zValue2); | |
| 247 | + } | |
| 248 | + return blob_str(&p->url); | |
| 249 | +} | |
| 178 | 250 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -173,5 +173,77 @@ | |
| 173 | url_parse(zProxy); |
| 174 | g.urlPath = zOriginalUrl; |
| 175 | g.urlHostname = zOriginalHost; |
| 176 | } |
| 177 | } |
| 178 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -173,5 +173,77 @@ | |
| 173 | url_parse(zProxy); |
| 174 | g.urlPath = zOriginalUrl; |
| 175 | g.urlHostname = zOriginalHost; |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | #if INTERFACE |
| 180 | /* |
| 181 | ** An instance of this object is used to build a URL with query parameters. |
| 182 | */ |
| 183 | struct HQuery { |
| 184 | Blob url; /* The URL */ |
| 185 | const char *zBase; /* The base URL */ |
| 186 | int nParam; /* Number of parameters. Max 10 */ |
| 187 | const char *azName[10]; /* Parameter names */ |
| 188 | const char *azValue[10]; /* Parameter values */ |
| 189 | }; |
| 190 | #endif |
| 191 | |
| 192 | /* |
| 193 | ** Initialize the URL object. |
| 194 | */ |
| 195 | void url_initialize(HQuery *p, const char *zBase){ |
| 196 | blob_zero(&p->url); |
| 197 | p->zBase = zBase; |
| 198 | p->nParam = 0; |
| 199 | } |
| 200 | |
| 201 | /* |
| 202 | ** Add a fixed parameter to an HQuery. |
| 203 | */ |
| 204 | void url_add_parameter(HQuery *p, const char *zName, const char *zValue){ |
| 205 | assert( p->nParam < count(p->azName) ); |
| 206 | assert( p->nParam < count(p->azValue) ); |
| 207 | p->azName[p->nParam] = zName; |
| 208 | p->azValue[p->nParam] = zValue; |
| 209 | p->nParam++; |
| 210 | } |
| 211 | |
| 212 | /* |
| 213 | ** Render the URL with a parameter override. |
| 214 | */ |
| 215 | char *url_render( |
| 216 | HQuery *p, /* Base URL */ |
| 217 | const char *zName1, /* First override */ |
| 218 | const char *zValue1, /* First override value */ |
| 219 | const char *zName2, /* Second override */ |
| 220 | const char *zValue2 /* Second override value */ |
| 221 | ){ |
| 222 | const char *zSep = "?"; |
| 223 | int i; |
| 224 | |
| 225 | blob_reset(&p->url); |
| 226 | blob_appendf(&p->url, "%s/%s", g.zBaseURL, p->zBase); |
| 227 | for(i=0; i<p->nParam; i++){ |
| 228 | const char *z = p->azValue[i]; |
| 229 | if( zName1 && strcmp(zName1,p->azName[i])==0 ){ |
| 230 | zName1 = 0; |
| 231 | z = zValue1; |
| 232 | if( z==0 ) continue; |
| 233 | } |
| 234 | if( zName2 && strcmp(zName2,p->azName[i])==0 ){ |
| 235 | zName2 = 0; |
| 236 | z = zValue2; |
| 237 | if( z==0 ) continue; |
| 238 | } |
| 239 | blob_appendf(&p->url, "%s%s=%T", zSep, p->azName[i], z); |
| 240 | zSep = "&"; |
| 241 | } |
| 242 | if( zName1 && zValue1 ){ |
| 243 | blob_appendf(&p->url, "%s%s=%T", zSep, zName1, zValue1); |
| 244 | } |
| 245 | if( zName2 && zValue2 ){ |
| 246 | blob_appendf(&p->url, "%s%s=%T", zSep, zName2, zValue2); |
| 247 | } |
| 248 | return blob_str(&p->url); |
| 249 | } |
| 250 |