Fossil SCM
Allow /wikiedit's page-list-fetch operation to silently skip over mysteriously missing (shunned but not yet rebuilt?) wiki pages, to resolve an issue on the core fossil site where such a missing/invisible page named 'Security Desk Technician' is causing /wikiedit to fail to load.
Commit
37409e7dbeddf00cca9ba8c1640b635c4d01d549df95ddae7ccf66237aee71a7
Parent
0d7d54e870160b1…
1 file changed
+34
-11
+34
-11
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -725,12 +725,24 @@ | ||
| 725 | 725 | ** Loads the given wiki page, sets the response type to |
| 726 | 726 | ** application/json, and emits it as a JSON object. If zPageName is a |
| 727 | 727 | ** sandbox page then a "fake" object is emitted, as the wikiajax API |
| 728 | 728 | ** does not permit saving the sandbox. |
| 729 | 729 | ** |
| 730 | -** Returns true on success, false on error, and on error it | |
| 731 | -** queues up a JSON-format error response. | |
| 730 | +** If includeLeadingComma is true then a comma will be emitted | |
| 731 | +** preceeding the object. This parameter is a bit of a hack to allow | |
| 732 | +** wiki page list generation to recover from an unusual (so far unique | |
| 733 | +** to one repo) error mode. | |
| 734 | +** | |
| 735 | +** Returns true on success, false on error. Its behaviour on error | |
| 736 | +** depends on the 4th argument: if it's true, this function simply | |
| 737 | +** returns false, else it queues up a JSON response and returns false. | |
| 738 | +** The intention of this flag is to avoid a weird corner case seen, so | |
| 739 | +** far, only on the main fossil repo (not clones of it) in which | |
| 740 | +** loading fails for a page named "Security Desk Technician". The | |
| 741 | +** hypothesis is that that page was once shunned on the core repo, but | |
| 742 | +** a reference to it still exists in the tables from which we pull the | |
| 743 | +** wiki list. | |
| 732 | 744 | ** |
| 733 | 745 | ** Output JSON format: |
| 734 | 746 | ** |
| 735 | 747 | ** { name: "page name", |
| 736 | 748 | ** type: "normal" | "tag" | "checkin" | "branch" | "sandbox", |
| @@ -743,18 +755,23 @@ | ||
| 743 | 755 | ** } |
| 744 | 756 | ** |
| 745 | 757 | ** If includeContent is false then the content member is elided. |
| 746 | 758 | */ |
| 747 | 759 | static int wiki_ajax_emit_page_object(const char *zPageName, |
| 748 | - int includeContent){ | |
| 760 | + int includeContent, | |
| 761 | + int includeLeadingComma, | |
| 762 | + int ignoreLoadError){ | |
| 749 | 763 | Manifest * pWiki = 0; |
| 750 | 764 | char * zUuid; |
| 751 | 765 | |
| 752 | 766 | if( is_sandbox(zPageName) ){ |
| 753 | 767 | char * zMimetype = |
| 754 | 768 | db_get("sandbox-mimetype","text/x-fossil-wiki"); |
| 755 | 769 | char * zBody = db_get("sandbox",""); |
| 770 | + if(includeLeadingComma){ | |
| 771 | + CX(","); | |
| 772 | + } | |
| 756 | 773 | CX("{\"name\": %!j, \"type\": \"sandbox\", " |
| 757 | 774 | "\"mimetype\": %!j, \"version\": null, \"parent\": null", |
| 758 | 775 | zPageName, zMimetype); |
| 759 | 776 | if(includeContent){ |
| 760 | 777 | CX(", \"content\": %!j", |
| @@ -763,14 +780,19 @@ | ||
| 763 | 780 | CX("}"); |
| 764 | 781 | fossil_free(zMimetype); |
| 765 | 782 | fossil_free(zBody); |
| 766 | 783 | return 1; |
| 767 | 784 | }else if( !wiki_fetch_by_name(zPageName, 0, 0, &pWiki) ){ |
| 768 | - ajax_route_error(404, "Wiki page could not be loaded: %s", | |
| 769 | - zPageName); | |
| 785 | + if(ignoreLoadError!=0){ | |
| 786 | + ajax_route_error(404, "Wiki page could not be loaded: %s", | |
| 787 | + zPageName); | |
| 788 | + } | |
| 770 | 789 | return 0; |
| 771 | 790 | }else{ |
| 791 | + if(includeLeadingComma){ | |
| 792 | + CX(","); | |
| 793 | + } | |
| 772 | 794 | zUuid = rid_to_uuid(pWiki->rid); |
| 773 | 795 | CX("{\"name\": %!j, \"type\": %!j, " |
| 774 | 796 | "\"version\": %!j, " |
| 775 | 797 | "\"mimetype\": %!j, ", |
| 776 | 798 | pWiki->zWikiTitle, |
| @@ -853,11 +875,11 @@ | ||
| 853 | 875 | } |
| 854 | 876 | blob_init(&content, zContent ? zContent : "", -1); |
| 855 | 877 | cgi_set_content_type("application/json"); |
| 856 | 878 | db_begin_transaction(); |
| 857 | 879 | wiki_cmd_commit(zPageName, parentRid, &content, zMimetype, 0); |
| 858 | - rollback = wiki_ajax_emit_page_object(zPageName, 1) ? 0 : 1; | |
| 880 | + rollback = wiki_ajax_emit_page_object(zPageName, 1, 0, 0) ? 0 : 1; | |
| 859 | 881 | db_end_transaction(rollback); |
| 860 | 882 | } |
| 861 | 883 | |
| 862 | 884 | /* |
| 863 | 885 | ** Ajax route handler for /wikiajax/fetch. |
| @@ -876,11 +898,11 @@ | ||
| 876 | 898 | if( zPageName==0 || zPageName[0]==0 ){ |
| 877 | 899 | ajax_route_error(400,"Missing page name."); |
| 878 | 900 | return; |
| 879 | 901 | } |
| 880 | 902 | cgi_set_content_type("application/json"); |
| 881 | - wiki_ajax_emit_page_object(zPageName, 1); | |
| 903 | + wiki_ajax_emit_page_object(zPageName, 1, 0, 0); | |
| 882 | 904 | } |
| 883 | 905 | |
| 884 | 906 | /* |
| 885 | 907 | ** Ajax route handler for /wikiajax/diff. |
| 886 | 908 | ** |
| @@ -984,17 +1006,18 @@ | ||
| 984 | 1006 | " UNION SELECT 'Sandbox' AS name" |
| 985 | 1007 | " ORDER BY name COLLATE NOCASE"); |
| 986 | 1008 | CX("["); |
| 987 | 1009 | while( SQLITE_ROW==db_step(&q) ){ |
| 988 | 1010 | char const * zName = db_column_text(&q,0); |
| 989 | - if(n++){ | |
| 990 | - CX(","); | |
| 991 | - } | |
| 992 | 1011 | if(verbose==0){ |
| 1012 | + if(n++){ | |
| 1013 | + CX(","); | |
| 1014 | + } | |
| 993 | 1015 | CX("%!j", zName); |
| 994 | 1016 | }else{ |
| 995 | - wiki_ajax_emit_page_object(zName, includeContent); | |
| 1017 | + wiki_ajax_emit_page_object(zName, includeContent, | |
| 1018 | + n++>0, 1); | |
| 996 | 1019 | } |
| 997 | 1020 | } |
| 998 | 1021 | CX("]"); |
| 999 | 1022 | db_finalize(&q); |
| 1000 | 1023 | db_end_transaction(0); |
| 1001 | 1024 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -725,12 +725,24 @@ | |
| 725 | ** Loads the given wiki page, sets the response type to |
| 726 | ** application/json, and emits it as a JSON object. If zPageName is a |
| 727 | ** sandbox page then a "fake" object is emitted, as the wikiajax API |
| 728 | ** does not permit saving the sandbox. |
| 729 | ** |
| 730 | ** Returns true on success, false on error, and on error it |
| 731 | ** queues up a JSON-format error response. |
| 732 | ** |
| 733 | ** Output JSON format: |
| 734 | ** |
| 735 | ** { name: "page name", |
| 736 | ** type: "normal" | "tag" | "checkin" | "branch" | "sandbox", |
| @@ -743,18 +755,23 @@ | |
| 743 | ** } |
| 744 | ** |
| 745 | ** If includeContent is false then the content member is elided. |
| 746 | */ |
| 747 | static int wiki_ajax_emit_page_object(const char *zPageName, |
| 748 | int includeContent){ |
| 749 | Manifest * pWiki = 0; |
| 750 | char * zUuid; |
| 751 | |
| 752 | if( is_sandbox(zPageName) ){ |
| 753 | char * zMimetype = |
| 754 | db_get("sandbox-mimetype","text/x-fossil-wiki"); |
| 755 | char * zBody = db_get("sandbox",""); |
| 756 | CX("{\"name\": %!j, \"type\": \"sandbox\", " |
| 757 | "\"mimetype\": %!j, \"version\": null, \"parent\": null", |
| 758 | zPageName, zMimetype); |
| 759 | if(includeContent){ |
| 760 | CX(", \"content\": %!j", |
| @@ -763,14 +780,19 @@ | |
| 763 | CX("}"); |
| 764 | fossil_free(zMimetype); |
| 765 | fossil_free(zBody); |
| 766 | return 1; |
| 767 | }else if( !wiki_fetch_by_name(zPageName, 0, 0, &pWiki) ){ |
| 768 | ajax_route_error(404, "Wiki page could not be loaded: %s", |
| 769 | zPageName); |
| 770 | return 0; |
| 771 | }else{ |
| 772 | zUuid = rid_to_uuid(pWiki->rid); |
| 773 | CX("{\"name\": %!j, \"type\": %!j, " |
| 774 | "\"version\": %!j, " |
| 775 | "\"mimetype\": %!j, ", |
| 776 | pWiki->zWikiTitle, |
| @@ -853,11 +875,11 @@ | |
| 853 | } |
| 854 | blob_init(&content, zContent ? zContent : "", -1); |
| 855 | cgi_set_content_type("application/json"); |
| 856 | db_begin_transaction(); |
| 857 | wiki_cmd_commit(zPageName, parentRid, &content, zMimetype, 0); |
| 858 | rollback = wiki_ajax_emit_page_object(zPageName, 1) ? 0 : 1; |
| 859 | db_end_transaction(rollback); |
| 860 | } |
| 861 | |
| 862 | /* |
| 863 | ** Ajax route handler for /wikiajax/fetch. |
| @@ -876,11 +898,11 @@ | |
| 876 | if( zPageName==0 || zPageName[0]==0 ){ |
| 877 | ajax_route_error(400,"Missing page name."); |
| 878 | return; |
| 879 | } |
| 880 | cgi_set_content_type("application/json"); |
| 881 | wiki_ajax_emit_page_object(zPageName, 1); |
| 882 | } |
| 883 | |
| 884 | /* |
| 885 | ** Ajax route handler for /wikiajax/diff. |
| 886 | ** |
| @@ -984,17 +1006,18 @@ | |
| 984 | " UNION SELECT 'Sandbox' AS name" |
| 985 | " ORDER BY name COLLATE NOCASE"); |
| 986 | CX("["); |
| 987 | while( SQLITE_ROW==db_step(&q) ){ |
| 988 | char const * zName = db_column_text(&q,0); |
| 989 | if(n++){ |
| 990 | CX(","); |
| 991 | } |
| 992 | if(verbose==0){ |
| 993 | CX("%!j", zName); |
| 994 | }else{ |
| 995 | wiki_ajax_emit_page_object(zName, includeContent); |
| 996 | } |
| 997 | } |
| 998 | CX("]"); |
| 999 | db_finalize(&q); |
| 1000 | db_end_transaction(0); |
| 1001 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -725,12 +725,24 @@ | |
| 725 | ** Loads the given wiki page, sets the response type to |
| 726 | ** application/json, and emits it as a JSON object. If zPageName is a |
| 727 | ** sandbox page then a "fake" object is emitted, as the wikiajax API |
| 728 | ** does not permit saving the sandbox. |
| 729 | ** |
| 730 | ** If includeLeadingComma is true then a comma will be emitted |
| 731 | ** preceeding the object. This parameter is a bit of a hack to allow |
| 732 | ** wiki page list generation to recover from an unusual (so far unique |
| 733 | ** to one repo) error mode. |
| 734 | ** |
| 735 | ** Returns true on success, false on error. Its behaviour on error |
| 736 | ** depends on the 4th argument: if it's true, this function simply |
| 737 | ** returns false, else it queues up a JSON response and returns false. |
| 738 | ** The intention of this flag is to avoid a weird corner case seen, so |
| 739 | ** far, only on the main fossil repo (not clones of it) in which |
| 740 | ** loading fails for a page named "Security Desk Technician". The |
| 741 | ** hypothesis is that that page was once shunned on the core repo, but |
| 742 | ** a reference to it still exists in the tables from which we pull the |
| 743 | ** wiki list. |
| 744 | ** |
| 745 | ** Output JSON format: |
| 746 | ** |
| 747 | ** { name: "page name", |
| 748 | ** type: "normal" | "tag" | "checkin" | "branch" | "sandbox", |
| @@ -743,18 +755,23 @@ | |
| 755 | ** } |
| 756 | ** |
| 757 | ** If includeContent is false then the content member is elided. |
| 758 | */ |
| 759 | static int wiki_ajax_emit_page_object(const char *zPageName, |
| 760 | int includeContent, |
| 761 | int includeLeadingComma, |
| 762 | int ignoreLoadError){ |
| 763 | Manifest * pWiki = 0; |
| 764 | char * zUuid; |
| 765 | |
| 766 | if( is_sandbox(zPageName) ){ |
| 767 | char * zMimetype = |
| 768 | db_get("sandbox-mimetype","text/x-fossil-wiki"); |
| 769 | char * zBody = db_get("sandbox",""); |
| 770 | if(includeLeadingComma){ |
| 771 | CX(","); |
| 772 | } |
| 773 | CX("{\"name\": %!j, \"type\": \"sandbox\", " |
| 774 | "\"mimetype\": %!j, \"version\": null, \"parent\": null", |
| 775 | zPageName, zMimetype); |
| 776 | if(includeContent){ |
| 777 | CX(", \"content\": %!j", |
| @@ -763,14 +780,19 @@ | |
| 780 | CX("}"); |
| 781 | fossil_free(zMimetype); |
| 782 | fossil_free(zBody); |
| 783 | return 1; |
| 784 | }else if( !wiki_fetch_by_name(zPageName, 0, 0, &pWiki) ){ |
| 785 | if(ignoreLoadError!=0){ |
| 786 | ajax_route_error(404, "Wiki page could not be loaded: %s", |
| 787 | zPageName); |
| 788 | } |
| 789 | return 0; |
| 790 | }else{ |
| 791 | if(includeLeadingComma){ |
| 792 | CX(","); |
| 793 | } |
| 794 | zUuid = rid_to_uuid(pWiki->rid); |
| 795 | CX("{\"name\": %!j, \"type\": %!j, " |
| 796 | "\"version\": %!j, " |
| 797 | "\"mimetype\": %!j, ", |
| 798 | pWiki->zWikiTitle, |
| @@ -853,11 +875,11 @@ | |
| 875 | } |
| 876 | blob_init(&content, zContent ? zContent : "", -1); |
| 877 | cgi_set_content_type("application/json"); |
| 878 | db_begin_transaction(); |
| 879 | wiki_cmd_commit(zPageName, parentRid, &content, zMimetype, 0); |
| 880 | rollback = wiki_ajax_emit_page_object(zPageName, 1, 0, 0) ? 0 : 1; |
| 881 | db_end_transaction(rollback); |
| 882 | } |
| 883 | |
| 884 | /* |
| 885 | ** Ajax route handler for /wikiajax/fetch. |
| @@ -876,11 +898,11 @@ | |
| 898 | if( zPageName==0 || zPageName[0]==0 ){ |
| 899 | ajax_route_error(400,"Missing page name."); |
| 900 | return; |
| 901 | } |
| 902 | cgi_set_content_type("application/json"); |
| 903 | wiki_ajax_emit_page_object(zPageName, 1, 0, 0); |
| 904 | } |
| 905 | |
| 906 | /* |
| 907 | ** Ajax route handler for /wikiajax/diff. |
| 908 | ** |
| @@ -984,17 +1006,18 @@ | |
| 1006 | " UNION SELECT 'Sandbox' AS name" |
| 1007 | " ORDER BY name COLLATE NOCASE"); |
| 1008 | CX("["); |
| 1009 | while( SQLITE_ROW==db_step(&q) ){ |
| 1010 | char const * zName = db_column_text(&q,0); |
| 1011 | if(verbose==0){ |
| 1012 | if(n++){ |
| 1013 | CX(","); |
| 1014 | } |
| 1015 | CX("%!j", zName); |
| 1016 | }else{ |
| 1017 | wiki_ajax_emit_page_object(zName, includeContent, |
| 1018 | n++>0, 1); |
| 1019 | } |
| 1020 | } |
| 1021 | CX("]"); |
| 1022 | db_finalize(&q); |
| 1023 | db_end_transaction(0); |
| 1024 |