Fossil SCM
Added first go at /json/wiki/diff. Not yet configurable but proves the concept.
Commit
c6c38a522faf1cca8622788f73a24b387d2926eb
Parent
6f2a51602e19356…
1 file changed
+119
-10
+119
-10
| --- src/json_wiki.c | ||
| +++ src/json_wiki.c | ||
| @@ -25,19 +25,20 @@ | ||
| 25 | 25 | |
| 26 | 26 | static cson_value * json_wiki_create(); |
| 27 | 27 | static cson_value * json_wiki_get(); |
| 28 | 28 | static cson_value * json_wiki_list(); |
| 29 | 29 | static cson_value * json_wiki_save(); |
| 30 | - | |
| 30 | +static cson_value * json_wiki_diff(); | |
| 31 | 31 | /* |
| 32 | 32 | ** Mapping of /json/wiki/XXX commands/paths to callbacks. |
| 33 | 33 | */ |
| 34 | 34 | static const JsonPageDef JsonPageDefs_Wiki[] = { |
| 35 | -{"create", json_wiki_create, 1}, | |
| 35 | +{"create", json_wiki_create, 0}, | |
| 36 | +{"diff", json_wiki_diff, 0}, | |
| 36 | 37 | {"get", json_wiki_get, 0}, |
| 37 | 38 | {"list", json_wiki_list, 0}, |
| 38 | -{"save", json_wiki_save, 1}, | |
| 39 | +{"save", json_wiki_save, 0}, | |
| 39 | 40 | {"timeline", json_timeline_wiki,0}, |
| 40 | 41 | /* Last entry MUST have a NULL name. */ |
| 41 | 42 | {NULL,NULL,0} |
| 42 | 43 | }; |
| 43 | 44 | |
| @@ -48,10 +49,20 @@ | ||
| 48 | 49 | */ |
| 49 | 50 | cson_value * json_page_wiki(){ |
| 50 | 51 | return json_page_dispatch_helper(&JsonPageDefs_Wiki[0]); |
| 51 | 52 | } |
| 52 | 53 | |
| 54 | +char * json_wiki_get_uuid_for_rid( int rid ) | |
| 55 | +{ | |
| 56 | + return db_text(NULL, | |
| 57 | + "SELECT b.uuid FROM tag t, tagxref x, blob b" | |
| 58 | + " WHERE x.tagid=t.tagid AND t.tagname GLOB 'wiki-*' " | |
| 59 | + " AND b.rid=x.rid AND b.rid=%d" | |
| 60 | + " ORDER BY x.mtime DESC LIMIT 1", | |
| 61 | + rid | |
| 62 | + ); | |
| 63 | +} | |
| 53 | 64 | |
| 54 | 65 | /* |
| 55 | 66 | ** Tries to load a wiki page from the given rid creates a JSON object |
| 56 | 67 | ** representation of it. If the page is not found then NULL is |
| 57 | 68 | ** returned. If contentFormat is positive then the page content |
| @@ -75,17 +86,11 @@ | ||
| 75 | 86 | /*char const * zFormat = NULL;*/ |
| 76 | 87 | unsigned int len = 0; |
| 77 | 88 | cson_object * pay = cson_new_object(); |
| 78 | 89 | char const * zBody = pWiki->zWiki; |
| 79 | 90 | char const * zFormat = NULL; |
| 80 | - char * zUuid = db_text(NULL, | |
| 81 | - "SELECT b.uuid FROM tag t, tagxref x, blob b" | |
| 82 | - " WHERE x.tagid=t.tagid AND t.tagname GLOB 'wiki-*' " | |
| 83 | - " AND b.rid=x.rid AND b.rid=%d" | |
| 84 | - " ORDER BY x.mtime DESC LIMIT 1", | |
| 85 | - rid | |
| 86 | - ); | |
| 91 | + char * zUuid = json_wiki_get_uuid_for_rid(rid); | |
| 87 | 92 | cson_object_set(pay,"name",json_new_string(pWiki->zWikiTitle)); |
| 88 | 93 | cson_object_set(pay,"uuid",json_new_string(zUuid)); |
| 89 | 94 | free(zUuid); |
| 90 | 95 | zUuid = NULL; |
| 91 | 96 | if( pWiki->nParent > 0 ){ |
| @@ -422,6 +427,110 @@ | ||
| 422 | 427 | listV = NULL; |
| 423 | 428 | end: |
| 424 | 429 | db_finalize(&q); |
| 425 | 430 | return listV; |
| 426 | 431 | } |
| 432 | + | |
| 433 | +static cson_value * json_wiki_diff(){ | |
| 434 | + char const * zV1 = NULL; | |
| 435 | + char const * zV2 = NULL; | |
| 436 | + char const * zContent = NULL; | |
| 437 | + cson_object * pay = NULL; | |
| 438 | + int argPos = g.json.dispatchDepth; | |
| 439 | + int r1 = 0, r2 = 0; | |
| 440 | + Manifest * pW1 = NULL, *pW2 = NULL; | |
| 441 | + Blob w1 = empty_blob, w2 = empty_blob, d = empty_blob; | |
| 442 | + char const * zErrTag = NULL; | |
| 443 | + int diffFlags; | |
| 444 | + char * zUuid = NULL; | |
| 445 | + if( !g.perm.History ){ | |
| 446 | + json_set_err(FSL_JSON_E_DENIED, | |
| 447 | + "Requires 'h' permissions."); | |
| 448 | + return NULL; | |
| 449 | + } | |
| 450 | + | |
| 451 | + | |
| 452 | + zV1 = json_find_option_cstr2( "v1",NULL, NULL, ++argPos ); | |
| 453 | + zV2 = json_find_option_cstr2( "v2",NULL, NULL, ++argPos ); | |
| 454 | + if(!zV1 || !*zV1 || !zV2 || !*zV2) { | |
| 455 | + json_set_err(FSL_JSON_E_INVALID_ARGS, | |
| 456 | + "Requires both 'v1' and 'v2' arguments."); | |
| 457 | + return NULL; | |
| 458 | + } | |
| 459 | + | |
| 460 | + r1 = symbolic_name_to_rid( zV1, "w" ); | |
| 461 | + zErrTag = zV1; | |
| 462 | + if(r1<0){ | |
| 463 | + goto invalid; | |
| 464 | + }else if(0==r1){ | |
| 465 | + goto ambiguous; | |
| 466 | + } | |
| 467 | + | |
| 468 | + r2 = symbolic_name_to_rid( zV2, "w" ); | |
| 469 | + zErrTag = zV2; | |
| 470 | + if(r2<0){ | |
| 471 | + goto invalid; | |
| 472 | + }else if(0==r2){ | |
| 473 | + goto ambiguous; | |
| 474 | + } | |
| 475 | + | |
| 476 | + zErrTag = zV1; | |
| 477 | + pW1 = manifest_get(r1, CFTYPE_WIKI); | |
| 478 | + if( pW1==0 ) { | |
| 479 | + goto manifest; | |
| 480 | + } | |
| 481 | + zErrTag = zV2; | |
| 482 | + pW2 = manifest_get(r2, CFTYPE_WIKI); | |
| 483 | + if( pW2==0 ) { | |
| 484 | + goto manifest; | |
| 485 | + } | |
| 486 | + | |
| 487 | + blob_init(&w1, pW1->zWiki, -1); | |
| 488 | + blob_zero(&w2); | |
| 489 | + blob_init(&w2, pW2->zWiki, -1); | |
| 490 | + blob_zero(&d); | |
| 491 | + diffFlags = DIFF_IGNORE_EOLWS | DIFF_INLINE; | |
| 492 | + text_diff(&w2, &w1, &d, diffFlags); | |
| 493 | + blob_reset(&w1); | |
| 494 | + blob_reset(&w2); | |
| 495 | + | |
| 496 | + pay = cson_new_object(); | |
| 497 | + | |
| 498 | + zUuid = json_wiki_get_uuid_for_rid( pW1->rid ); | |
| 499 | + cson_object_set(pay, "v1", json_new_string(zUuid) ); | |
| 500 | + free(zUuid); | |
| 501 | + zUuid = json_wiki_get_uuid_for_rid( pW2->rid ); | |
| 502 | + cson_object_set(pay, "v2", json_new_string(zUuid) ); | |
| 503 | + free(zUuid); | |
| 504 | + zUuid = NULL; | |
| 505 | + | |
| 506 | + manifest_destroy(pW1); | |
| 507 | + manifest_destroy(pW2); | |
| 508 | + | |
| 509 | + cson_object_set(pay, "diff", | |
| 510 | + cson_value_new_string( blob_str(&d), | |
| 511 | + (unsigned int)blob_size(&d))); | |
| 512 | + | |
| 513 | + return cson_object_value(pay); | |
| 514 | + | |
| 515 | + manifest: | |
| 516 | + json_set_err(FSL_JSON_E_UNKNOWN, | |
| 517 | + "Could not load wiki manifest for UUID [%s].", | |
| 518 | + zErrTag); | |
| 519 | + goto end; | |
| 520 | + | |
| 521 | + ambiguous: | |
| 522 | + json_set_err(FSL_JSON_E_AMBIGUOUS_UUID, | |
| 523 | + "UUID [%s] is ambiguous.", zErrTag); | |
| 524 | + goto end; | |
| 525 | + | |
| 526 | + invalid: | |
| 527 | + json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, | |
| 528 | + "UUID [%s] not found.", zErrTag); | |
| 529 | + goto end; | |
| 530 | + | |
| 531 | + end: | |
| 532 | + cson_free_object(pay); | |
| 533 | + return NULL; | |
| 534 | +} | |
| 535 | + | |
| 427 | 536 | #endif /* FOSSIL_ENABLE_JSON */ |
| 428 | 537 |
| --- src/json_wiki.c | |
| +++ src/json_wiki.c | |
| @@ -25,19 +25,20 @@ | |
| 25 | |
| 26 | static cson_value * json_wiki_create(); |
| 27 | static cson_value * json_wiki_get(); |
| 28 | static cson_value * json_wiki_list(); |
| 29 | static cson_value * json_wiki_save(); |
| 30 | |
| 31 | /* |
| 32 | ** Mapping of /json/wiki/XXX commands/paths to callbacks. |
| 33 | */ |
| 34 | static const JsonPageDef JsonPageDefs_Wiki[] = { |
| 35 | {"create", json_wiki_create, 1}, |
| 36 | {"get", json_wiki_get, 0}, |
| 37 | {"list", json_wiki_list, 0}, |
| 38 | {"save", json_wiki_save, 1}, |
| 39 | {"timeline", json_timeline_wiki,0}, |
| 40 | /* Last entry MUST have a NULL name. */ |
| 41 | {NULL,NULL,0} |
| 42 | }; |
| 43 | |
| @@ -48,10 +49,20 @@ | |
| 48 | */ |
| 49 | cson_value * json_page_wiki(){ |
| 50 | return json_page_dispatch_helper(&JsonPageDefs_Wiki[0]); |
| 51 | } |
| 52 | |
| 53 | |
| 54 | /* |
| 55 | ** Tries to load a wiki page from the given rid creates a JSON object |
| 56 | ** representation of it. If the page is not found then NULL is |
| 57 | ** returned. If contentFormat is positive then the page content |
| @@ -75,17 +86,11 @@ | |
| 75 | /*char const * zFormat = NULL;*/ |
| 76 | unsigned int len = 0; |
| 77 | cson_object * pay = cson_new_object(); |
| 78 | char const * zBody = pWiki->zWiki; |
| 79 | char const * zFormat = NULL; |
| 80 | char * zUuid = db_text(NULL, |
| 81 | "SELECT b.uuid FROM tag t, tagxref x, blob b" |
| 82 | " WHERE x.tagid=t.tagid AND t.tagname GLOB 'wiki-*' " |
| 83 | " AND b.rid=x.rid AND b.rid=%d" |
| 84 | " ORDER BY x.mtime DESC LIMIT 1", |
| 85 | rid |
| 86 | ); |
| 87 | cson_object_set(pay,"name",json_new_string(pWiki->zWikiTitle)); |
| 88 | cson_object_set(pay,"uuid",json_new_string(zUuid)); |
| 89 | free(zUuid); |
| 90 | zUuid = NULL; |
| 91 | if( pWiki->nParent > 0 ){ |
| @@ -422,6 +427,110 @@ | |
| 422 | listV = NULL; |
| 423 | end: |
| 424 | db_finalize(&q); |
| 425 | return listV; |
| 426 | } |
| 427 | #endif /* FOSSIL_ENABLE_JSON */ |
| 428 |
| --- src/json_wiki.c | |
| +++ src/json_wiki.c | |
| @@ -25,19 +25,20 @@ | |
| 25 | |
| 26 | static cson_value * json_wiki_create(); |
| 27 | static cson_value * json_wiki_get(); |
| 28 | static cson_value * json_wiki_list(); |
| 29 | static cson_value * json_wiki_save(); |
| 30 | static cson_value * json_wiki_diff(); |
| 31 | /* |
| 32 | ** Mapping of /json/wiki/XXX commands/paths to callbacks. |
| 33 | */ |
| 34 | static const JsonPageDef JsonPageDefs_Wiki[] = { |
| 35 | {"create", json_wiki_create, 0}, |
| 36 | {"diff", json_wiki_diff, 0}, |
| 37 | {"get", json_wiki_get, 0}, |
| 38 | {"list", json_wiki_list, 0}, |
| 39 | {"save", json_wiki_save, 0}, |
| 40 | {"timeline", json_timeline_wiki,0}, |
| 41 | /* Last entry MUST have a NULL name. */ |
| 42 | {NULL,NULL,0} |
| 43 | }; |
| 44 | |
| @@ -48,10 +49,20 @@ | |
| 49 | */ |
| 50 | cson_value * json_page_wiki(){ |
| 51 | return json_page_dispatch_helper(&JsonPageDefs_Wiki[0]); |
| 52 | } |
| 53 | |
| 54 | char * json_wiki_get_uuid_for_rid( int rid ) |
| 55 | { |
| 56 | return db_text(NULL, |
| 57 | "SELECT b.uuid FROM tag t, tagxref x, blob b" |
| 58 | " WHERE x.tagid=t.tagid AND t.tagname GLOB 'wiki-*' " |
| 59 | " AND b.rid=x.rid AND b.rid=%d" |
| 60 | " ORDER BY x.mtime DESC LIMIT 1", |
| 61 | rid |
| 62 | ); |
| 63 | } |
| 64 | |
| 65 | /* |
| 66 | ** Tries to load a wiki page from the given rid creates a JSON object |
| 67 | ** representation of it. If the page is not found then NULL is |
| 68 | ** returned. If contentFormat is positive then the page content |
| @@ -75,17 +86,11 @@ | |
| 86 | /*char const * zFormat = NULL;*/ |
| 87 | unsigned int len = 0; |
| 88 | cson_object * pay = cson_new_object(); |
| 89 | char const * zBody = pWiki->zWiki; |
| 90 | char const * zFormat = NULL; |
| 91 | char * zUuid = json_wiki_get_uuid_for_rid(rid); |
| 92 | cson_object_set(pay,"name",json_new_string(pWiki->zWikiTitle)); |
| 93 | cson_object_set(pay,"uuid",json_new_string(zUuid)); |
| 94 | free(zUuid); |
| 95 | zUuid = NULL; |
| 96 | if( pWiki->nParent > 0 ){ |
| @@ -422,6 +427,110 @@ | |
| 427 | listV = NULL; |
| 428 | end: |
| 429 | db_finalize(&q); |
| 430 | return listV; |
| 431 | } |
| 432 | |
| 433 | static cson_value * json_wiki_diff(){ |
| 434 | char const * zV1 = NULL; |
| 435 | char const * zV2 = NULL; |
| 436 | char const * zContent = NULL; |
| 437 | cson_object * pay = NULL; |
| 438 | int argPos = g.json.dispatchDepth; |
| 439 | int r1 = 0, r2 = 0; |
| 440 | Manifest * pW1 = NULL, *pW2 = NULL; |
| 441 | Blob w1 = empty_blob, w2 = empty_blob, d = empty_blob; |
| 442 | char const * zErrTag = NULL; |
| 443 | int diffFlags; |
| 444 | char * zUuid = NULL; |
| 445 | if( !g.perm.History ){ |
| 446 | json_set_err(FSL_JSON_E_DENIED, |
| 447 | "Requires 'h' permissions."); |
| 448 | return NULL; |
| 449 | } |
| 450 | |
| 451 | |
| 452 | zV1 = json_find_option_cstr2( "v1",NULL, NULL, ++argPos ); |
| 453 | zV2 = json_find_option_cstr2( "v2",NULL, NULL, ++argPos ); |
| 454 | if(!zV1 || !*zV1 || !zV2 || !*zV2) { |
| 455 | json_set_err(FSL_JSON_E_INVALID_ARGS, |
| 456 | "Requires both 'v1' and 'v2' arguments."); |
| 457 | return NULL; |
| 458 | } |
| 459 | |
| 460 | r1 = symbolic_name_to_rid( zV1, "w" ); |
| 461 | zErrTag = zV1; |
| 462 | if(r1<0){ |
| 463 | goto invalid; |
| 464 | }else if(0==r1){ |
| 465 | goto ambiguous; |
| 466 | } |
| 467 | |
| 468 | r2 = symbolic_name_to_rid( zV2, "w" ); |
| 469 | zErrTag = zV2; |
| 470 | if(r2<0){ |
| 471 | goto invalid; |
| 472 | }else if(0==r2){ |
| 473 | goto ambiguous; |
| 474 | } |
| 475 | |
| 476 | zErrTag = zV1; |
| 477 | pW1 = manifest_get(r1, CFTYPE_WIKI); |
| 478 | if( pW1==0 ) { |
| 479 | goto manifest; |
| 480 | } |
| 481 | zErrTag = zV2; |
| 482 | pW2 = manifest_get(r2, CFTYPE_WIKI); |
| 483 | if( pW2==0 ) { |
| 484 | goto manifest; |
| 485 | } |
| 486 | |
| 487 | blob_init(&w1, pW1->zWiki, -1); |
| 488 | blob_zero(&w2); |
| 489 | blob_init(&w2, pW2->zWiki, -1); |
| 490 | blob_zero(&d); |
| 491 | diffFlags = DIFF_IGNORE_EOLWS | DIFF_INLINE; |
| 492 | text_diff(&w2, &w1, &d, diffFlags); |
| 493 | blob_reset(&w1); |
| 494 | blob_reset(&w2); |
| 495 | |
| 496 | pay = cson_new_object(); |
| 497 | |
| 498 | zUuid = json_wiki_get_uuid_for_rid( pW1->rid ); |
| 499 | cson_object_set(pay, "v1", json_new_string(zUuid) ); |
| 500 | free(zUuid); |
| 501 | zUuid = json_wiki_get_uuid_for_rid( pW2->rid ); |
| 502 | cson_object_set(pay, "v2", json_new_string(zUuid) ); |
| 503 | free(zUuid); |
| 504 | zUuid = NULL; |
| 505 | |
| 506 | manifest_destroy(pW1); |
| 507 | manifest_destroy(pW2); |
| 508 | |
| 509 | cson_object_set(pay, "diff", |
| 510 | cson_value_new_string( blob_str(&d), |
| 511 | (unsigned int)blob_size(&d))); |
| 512 | |
| 513 | return cson_object_value(pay); |
| 514 | |
| 515 | manifest: |
| 516 | json_set_err(FSL_JSON_E_UNKNOWN, |
| 517 | "Could not load wiki manifest for UUID [%s].", |
| 518 | zErrTag); |
| 519 | goto end; |
| 520 | |
| 521 | ambiguous: |
| 522 | json_set_err(FSL_JSON_E_AMBIGUOUS_UUID, |
| 523 | "UUID [%s] is ambiguous.", zErrTag); |
| 524 | goto end; |
| 525 | |
| 526 | invalid: |
| 527 | json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, |
| 528 | "UUID [%s] not found.", zErrTag); |
| 529 | goto end; |
| 530 | |
| 531 | end: |
| 532 | cson_free_object(pay); |
| 533 | return NULL; |
| 534 | } |
| 535 | |
| 536 | #endif /* FOSSIL_ENABLE_JSON */ |
| 537 |