Fossil SCM
Implemented /json/timeline/wiki. Fixed --limit/-n handling of timeline/ci.
Commit
12545465e98da0733d6240b7cfdb9b85e9ccb00e
Parent
1df648abfeb97d3…
2 files changed
+2
+146
-9
+2
| --- ajax/index.html | ||
| +++ ajax/index.html | ||
| @@ -203,10 +203,12 @@ | ||
| 203 | 203 | <input type='button' value='version' onclick='TheApp.cgi.sendCommand("/json/version")' /> |
| 204 | 204 | <input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat")' /> |
| 205 | 205 | <input type='button' value='whoami' onclick='TheApp.cgi.sendCommand("/json/whoami")' /> |
| 206 | 206 | <input type='button' value='cap' onclick='TheApp.cgi.sendCommand("/json/cap")' /> |
| 207 | 207 | <input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/branch/list")' /> |
| 208 | +<input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/timeline/ci")' /> | |
| 209 | +<input type='button' value='timeline/wiki' onclick='TheApp.cgi.sendCommand("/json/timeline/wiki")' /> | |
| 208 | 210 | <input type='button' value='wiki/list' onclick='TheApp.cgi.sendCommand("/json/wiki/list")' /> |
| 209 | 211 | <input type='button' value='wiki/get Fossil' onclick='TheApp.cgi.sendCommand("/json/wiki/get",{"page":"Fossil"})' /> |
| 210 | 212 | |
| 211 | 213 | <!-- |
| 212 | 214 | <input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' /> |
| 213 | 215 |
| --- ajax/index.html | |
| +++ ajax/index.html | |
| @@ -203,10 +203,12 @@ | |
| 203 | <input type='button' value='version' onclick='TheApp.cgi.sendCommand("/json/version")' /> |
| 204 | <input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat")' /> |
| 205 | <input type='button' value='whoami' onclick='TheApp.cgi.sendCommand("/json/whoami")' /> |
| 206 | <input type='button' value='cap' onclick='TheApp.cgi.sendCommand("/json/cap")' /> |
| 207 | <input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/branch/list")' /> |
| 208 | <input type='button' value='wiki/list' onclick='TheApp.cgi.sendCommand("/json/wiki/list")' /> |
| 209 | <input type='button' value='wiki/get Fossil' onclick='TheApp.cgi.sendCommand("/json/wiki/get",{"page":"Fossil"})' /> |
| 210 | |
| 211 | <!-- |
| 212 | <input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' /> |
| 213 |
| --- ajax/index.html | |
| +++ ajax/index.html | |
| @@ -203,10 +203,12 @@ | |
| 203 | <input type='button' value='version' onclick='TheApp.cgi.sendCommand("/json/version")' /> |
| 204 | <input type='button' value='stat' onclick='TheApp.cgi.sendCommand("/json/stat")' /> |
| 205 | <input type='button' value='whoami' onclick='TheApp.cgi.sendCommand("/json/whoami")' /> |
| 206 | <input type='button' value='cap' onclick='TheApp.cgi.sendCommand("/json/cap")' /> |
| 207 | <input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/branch/list")' /> |
| 208 | <input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/timeline/ci")' /> |
| 209 | <input type='button' value='timeline/wiki' onclick='TheApp.cgi.sendCommand("/json/timeline/wiki")' /> |
| 210 | <input type='button' value='wiki/list' onclick='TheApp.cgi.sendCommand("/json/wiki/list")' /> |
| 211 | <input type='button' value='wiki/get Fossil' onclick='TheApp.cgi.sendCommand("/json/wiki/get",{"page":"Fossil"})' /> |
| 212 | |
| 213 | <!-- |
| 214 | <input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' /> |
| 215 |
+146
-9
| --- src/json.c | ||
| +++ src/json.c | ||
| @@ -721,10 +721,14 @@ | ||
| 721 | 721 | g.json.cmd.offset = -1; |
| 722 | 722 | if( !g.isHTTP && g.fullHttpReply ){ |
| 723 | 723 | /* workaround for server mode, so we see it as CGI mode. */ |
| 724 | 724 | g.isHTTP = 1; |
| 725 | 725 | } |
| 726 | + if( !g.isHTTP ){ | |
| 727 | + g.json.errorDetailParanoia = 0 /*disable error code dumb-down for CLI mode*/; | |
| 728 | + } | |
| 729 | + | |
| 726 | 730 | if(! g.json.post.v ){ |
| 727 | 731 | /* If cgi_init() reads POSTed JSON then it sets the content type. |
| 728 | 732 | If it did not then we need to set it. |
| 729 | 733 | */ |
| 730 | 734 | cgi_set_content_type(json_guess_content_type()); |
| @@ -1865,15 +1869,17 @@ | ||
| 1865 | 1869 | } |
| 1866 | 1870 | return payV; |
| 1867 | 1871 | } |
| 1868 | 1872 | |
| 1869 | 1873 | static cson_value * json_timeline_ci(unsigned int depth); |
| 1874 | +static cson_value * json_timeline_wiki(unsigned int depth); | |
| 1870 | 1875 | /* |
| 1871 | 1876 | ** Mapping of /json/timeline/XXX commands/paths to callbacks. |
| 1872 | 1877 | */ |
| 1873 | 1878 | static const JsonPageDef JsonPageDefs_Timeline[] = { |
| 1874 | 1879 | {"ci", json_timeline_ci, 0}, |
| 1880 | +{"wiki", json_timeline_wiki, 0}, | |
| 1875 | 1881 | /* Last entry MUST have a NULL name. */ |
| 1876 | 1882 | {NULL,NULL,0} |
| 1877 | 1883 | }; |
| 1878 | 1884 | |
| 1879 | 1885 | /* |
| @@ -1909,13 +1915,13 @@ | ||
| 1909 | 1915 | db_multi_exec(zSql); |
| 1910 | 1916 | } |
| 1911 | 1917 | |
| 1912 | 1918 | /* |
| 1913 | 1919 | ** Return a pointer to a constant string that forms the basis |
| 1914 | -** for a timeline query for the JSON "checkin" interface. | |
| 1920 | +** for a timeline query for the JSON interface. | |
| 1915 | 1921 | */ |
| 1916 | -const char const * json_timeline_query_ci(void){ | |
| 1922 | +const char const * json_timeline_query(void){ | |
| 1917 | 1923 | /* Field order MUST match that from json_timeline_temp_table()!!! */ |
| 1918 | 1924 | static const char zBaseSql[] = |
| 1919 | 1925 | @ SELECT |
| 1920 | 1926 | @ blob.rid, |
| 1921 | 1927 | @ uuid, |
| @@ -1935,37 +1941,59 @@ | ||
| 1935 | 1941 | @ WHERE blob.rid=event.objid |
| 1936 | 1942 | ; |
| 1937 | 1943 | return zBaseSql; |
| 1938 | 1944 | } |
| 1939 | 1945 | |
| 1940 | - | |
| 1941 | 1946 | /* |
| 1942 | -** /json/timeline/ci | |
| 1947 | +** Tries to figure out a timeline query length limit base on | |
| 1948 | +** environment parameters. If it can it returns that value, | |
| 1949 | +** else it returns some statically defined default value. | |
| 1943 | 1950 | ** |
| 1944 | -** Far from complete. | |
| 1951 | +** Never returns a negative value. 0 means no limit. | |
| 1945 | 1952 | */ |
| 1946 | -static cson_value * json_timeline_ci(unsigned int depth){ | |
| 1953 | +static int json_timeline_limit(){ | |
| 1947 | 1954 | static const int defaultLimit = 20; |
| 1955 | + int limit = -1; | |
| 1956 | + if( g.isHTTP ){ | |
| 1957 | + limit = json_getenv_int("limit",-1); | |
| 1958 | + if(limit<0){ | |
| 1959 | + limit = json_getenv_int("n",-1); | |
| 1960 | + } | |
| 1961 | + }else{/* CLI mode */ | |
| 1962 | + char const * arg = find_option("limit","n",1); | |
| 1963 | + if(arg && *arg){ | |
| 1964 | + limit = atoi(arg); | |
| 1965 | + } | |
| 1966 | + } | |
| 1967 | + return (limit<0) ? defaultLimit : limit; | |
| 1968 | +} | |
| 1969 | +/* | |
| 1970 | +** Implementation of /json/timeline/ci. | |
| 1971 | +** | |
| 1972 | +** Still a few TODOs (like figuring out how to structure | |
| 1973 | +** inheritance info). | |
| 1974 | +*/ | |
| 1975 | +static cson_value * json_timeline_ci(unsigned int depth){ | |
| 1948 | 1976 | cson_value * payV = NULL; |
| 1949 | 1977 | cson_object * pay = NULL; |
| 1950 | 1978 | cson_value * tmp = NULL; |
| 1951 | 1979 | cson_value * listV = NULL; |
| 1952 | 1980 | cson_array * list = NULL; |
| 1953 | - int limit = json_getenv_int("n",defaultLimit); | |
| 1981 | + int limit; | |
| 1954 | 1982 | int check = 0; |
| 1955 | 1983 | Stmt q; |
| 1956 | 1984 | Blob sql = empty_blob; |
| 1957 | 1985 | if( !g.perm.Read/* && !g.perm.RdTkt && !g.perm.RdWiki*/ ){ |
| 1958 | 1986 | g.json.resultCode = FSL_JSON_E_DENIED; |
| 1959 | 1987 | return NULL; |
| 1960 | 1988 | } |
| 1989 | + limit = json_timeline_limit(); | |
| 1961 | 1990 | payV = cson_value_new_object(); |
| 1962 | 1991 | pay = cson_value_get_object(payV); |
| 1963 | - if( limit < 0 ) limit = defaultLimit; | |
| 1964 | 1992 | json_timeline_temp_table(); |
| 1965 | 1993 | blob_append(&sql, "INSERT OR IGNORE INTO json_timeline ", -1); |
| 1966 | - blob_append(&sql, json_timeline_query_ci(), -1 ); | |
| 1994 | + blob_append(&sql, json_timeline_query(), -1 ); | |
| 1967 | 1995 | blob_append(&sql, "AND event.type IN('ci') ", -1); |
| 1968 | 1996 | blob_append(&sql, "ORDER BY mtime DESC ", -1); |
| 1969 | 1997 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 1970 | 1998 | g.json.resultCode = (cson_rc.AllocError==check) \ |
| 1971 | 1999 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ |
| @@ -2052,10 +2080,119 @@ | ||
| 2052 | 2080 | cson_value_free(payV); |
| 2053 | 2081 | payV = NULL; |
| 2054 | 2082 | ok: |
| 2055 | 2083 | return payV; |
| 2056 | 2084 | } |
| 2085 | + | |
| 2086 | +/* | |
| 2087 | +** Implementation of /json/timeline/wiki. | |
| 2088 | +** | |
| 2089 | +*/ | |
| 2090 | +static cson_value * json_timeline_wiki(unsigned int depth){ | |
| 2091 | + /* This code is 95% the same as json_timeline_ci(), by the way. */ | |
| 2092 | + cson_value * payV = NULL; | |
| 2093 | + cson_object * pay = NULL; | |
| 2094 | + cson_value * tmp = NULL; | |
| 2095 | + cson_value * listV = NULL; | |
| 2096 | + cson_array * list = NULL; | |
| 2097 | + int limit; | |
| 2098 | + int check = 0; | |
| 2099 | + Stmt q; | |
| 2100 | + Blob sql = empty_blob; | |
| 2101 | + if( !g.perm.Read || !g.perm.RdWiki ){ | |
| 2102 | + g.json.resultCode = FSL_JSON_E_DENIED; | |
| 2103 | + return NULL; | |
| 2104 | + } | |
| 2105 | + limit = json_timeline_limit(); | |
| 2106 | + payV = cson_value_new_object(); | |
| 2107 | + pay = cson_value_get_object(payV); | |
| 2108 | + json_timeline_temp_table(); | |
| 2109 | + blob_append(&sql, "INSERT OR IGNORE INTO json_timeline ", -1); | |
| 2110 | + blob_append(&sql, json_timeline_query(), -1 ); | |
| 2111 | + blob_append(&sql, "AND event.type IN('w') ", -1); | |
| 2112 | + blob_append(&sql, "ORDER BY mtime DESC ", -1); | |
| 2113 | +#define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ | |
| 2114 | + g.json.resultCode = (cson_rc.AllocError==check) \ | |
| 2115 | + ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ | |
| 2116 | + goto error;\ | |
| 2117 | + } | |
| 2118 | + if(limit){ | |
| 2119 | + blob_appendf(&sql,"LIMIT %d ",limit); | |
| 2120 | + tmp = cson_value_new_integer(limit); | |
| 2121 | + SET("limit"); | |
| 2122 | + } | |
| 2123 | + db_multi_exec(blob_buffer(&sql)); | |
| 2124 | + | |
| 2125 | +#if 0 | |
| 2126 | + /* only for testing! */ | |
| 2127 | + tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); | |
| 2128 | + SET("timelineSql"); | |
| 2129 | +#endif | |
| 2130 | + | |
| 2131 | + blob_reset(&sql); | |
| 2132 | + blob_append(&sql, "SELECT rid AS rid," | |
| 2133 | + " uuid AS uuid," | |
| 2134 | + " mtime AS timestamp," | |
| 2135 | + " timestampString AS timestampString," | |
| 2136 | + " comment AS comment, " | |
| 2137 | + " user AS user," | |
| 2138 | + " eventType AS eventType" | |
| 2139 | +#if 0 | |
| 2140 | + /* can wiki pages have tags? */ | |
| 2141 | + " tags AS tags," /*FIXME: split this into | |
| 2142 | + a JSON array*/ | |
| 2143 | + " tagId AS tagId," | |
| 2144 | +#endif | |
| 2145 | + " FROM json_timeline" | |
| 2146 | + " ORDER BY mtime DESC", | |
| 2147 | + -1); | |
| 2148 | + db_prepare(&q,blob_buffer(&sql)); | |
| 2149 | + listV = cson_value_new_array(); | |
| 2150 | + list = cson_value_get_array(listV); | |
| 2151 | + tmp = listV; | |
| 2152 | + SET("timeline"); | |
| 2153 | + while( (SQLITE_ROW == db_step(&q) )){ | |
| 2154 | + /* convert each row into a JSON object...*/ | |
| 2155 | + cson_value * rowV = cson_sqlite3_row_to_object(q.pStmt); | |
| 2156 | + cson_object * row = cson_value_get_object(rowV); | |
| 2157 | + cson_string const * tagsStr = NULL; | |
| 2158 | + if(!row){ | |
| 2159 | + json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, | |
| 2160 | + "Could not convert at least one timeline result row to JSON." ); | |
| 2161 | + continue; | |
| 2162 | + } | |
| 2163 | + /* Split tags string field into JSON Array... */ | |
| 2164 | + cson_array_append(list, rowV); | |
| 2165 | +#if 0 | |
| 2166 | + tagsStr = cson_value_get_string(cson_object_get(row,"tags")); | |
| 2167 | + if(tagsStr){ | |
| 2168 | + cson_value * tags = json_string_split2( cson_string_cstr(tagsStr), | |
| 2169 | + ',', 0); | |
| 2170 | + if( tags ){ | |
| 2171 | + if(0 != cson_object_set(row,"tags",tags)){ | |
| 2172 | + cson_value_free(tags); | |
| 2173 | + }else{ | |
| 2174 | + /*replaced/deleted old tags value, invalidating tagsStr*/; | |
| 2175 | + tagsStr = NULL; | |
| 2176 | + } | |
| 2177 | + }else{ | |
| 2178 | + json_warn(FSL_JSON_W_STRING_TO_ARRAY_FAILED, | |
| 2179 | + "Could not convert tags string to array."); | |
| 2180 | + } | |
| 2181 | + } | |
| 2182 | +#endif | |
| 2183 | + } | |
| 2184 | + db_finalize(&q); | |
| 2185 | +#undef SET | |
| 2186 | + goto ok; | |
| 2187 | + error: | |
| 2188 | + cson_value_free(payV); | |
| 2189 | + payV = NULL; | |
| 2190 | + ok: | |
| 2191 | + return payV; | |
| 2192 | +} | |
| 2193 | + | |
| 2057 | 2194 | |
| 2058 | 2195 | /* |
| 2059 | 2196 | ** Implements the /json/whoami page/command. |
| 2060 | 2197 | */ |
| 2061 | 2198 | static cson_value * json_page_whoami(unsigned int depth){ |
| 2062 | 2199 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -721,10 +721,14 @@ | |
| 721 | g.json.cmd.offset = -1; |
| 722 | if( !g.isHTTP && g.fullHttpReply ){ |
| 723 | /* workaround for server mode, so we see it as CGI mode. */ |
| 724 | g.isHTTP = 1; |
| 725 | } |
| 726 | if(! g.json.post.v ){ |
| 727 | /* If cgi_init() reads POSTed JSON then it sets the content type. |
| 728 | If it did not then we need to set it. |
| 729 | */ |
| 730 | cgi_set_content_type(json_guess_content_type()); |
| @@ -1865,15 +1869,17 @@ | |
| 1865 | } |
| 1866 | return payV; |
| 1867 | } |
| 1868 | |
| 1869 | static cson_value * json_timeline_ci(unsigned int depth); |
| 1870 | /* |
| 1871 | ** Mapping of /json/timeline/XXX commands/paths to callbacks. |
| 1872 | */ |
| 1873 | static const JsonPageDef JsonPageDefs_Timeline[] = { |
| 1874 | {"ci", json_timeline_ci, 0}, |
| 1875 | /* Last entry MUST have a NULL name. */ |
| 1876 | {NULL,NULL,0} |
| 1877 | }; |
| 1878 | |
| 1879 | /* |
| @@ -1909,13 +1915,13 @@ | |
| 1909 | db_multi_exec(zSql); |
| 1910 | } |
| 1911 | |
| 1912 | /* |
| 1913 | ** Return a pointer to a constant string that forms the basis |
| 1914 | ** for a timeline query for the JSON "checkin" interface. |
| 1915 | */ |
| 1916 | const char const * json_timeline_query_ci(void){ |
| 1917 | /* Field order MUST match that from json_timeline_temp_table()!!! */ |
| 1918 | static const char zBaseSql[] = |
| 1919 | @ SELECT |
| 1920 | @ blob.rid, |
| 1921 | @ uuid, |
| @@ -1935,37 +1941,59 @@ | |
| 1935 | @ WHERE blob.rid=event.objid |
| 1936 | ; |
| 1937 | return zBaseSql; |
| 1938 | } |
| 1939 | |
| 1940 | |
| 1941 | /* |
| 1942 | ** /json/timeline/ci |
| 1943 | ** |
| 1944 | ** Far from complete. |
| 1945 | */ |
| 1946 | static cson_value * json_timeline_ci(unsigned int depth){ |
| 1947 | static const int defaultLimit = 20; |
| 1948 | cson_value * payV = NULL; |
| 1949 | cson_object * pay = NULL; |
| 1950 | cson_value * tmp = NULL; |
| 1951 | cson_value * listV = NULL; |
| 1952 | cson_array * list = NULL; |
| 1953 | int limit = json_getenv_int("n",defaultLimit); |
| 1954 | int check = 0; |
| 1955 | Stmt q; |
| 1956 | Blob sql = empty_blob; |
| 1957 | if( !g.perm.Read/* && !g.perm.RdTkt && !g.perm.RdWiki*/ ){ |
| 1958 | g.json.resultCode = FSL_JSON_E_DENIED; |
| 1959 | return NULL; |
| 1960 | } |
| 1961 | payV = cson_value_new_object(); |
| 1962 | pay = cson_value_get_object(payV); |
| 1963 | if( limit < 0 ) limit = defaultLimit; |
| 1964 | json_timeline_temp_table(); |
| 1965 | blob_append(&sql, "INSERT OR IGNORE INTO json_timeline ", -1); |
| 1966 | blob_append(&sql, json_timeline_query_ci(), -1 ); |
| 1967 | blob_append(&sql, "AND event.type IN('ci') ", -1); |
| 1968 | blob_append(&sql, "ORDER BY mtime DESC ", -1); |
| 1969 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 1970 | g.json.resultCode = (cson_rc.AllocError==check) \ |
| 1971 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ |
| @@ -2052,10 +2080,119 @@ | |
| 2052 | cson_value_free(payV); |
| 2053 | payV = NULL; |
| 2054 | ok: |
| 2055 | return payV; |
| 2056 | } |
| 2057 | |
| 2058 | /* |
| 2059 | ** Implements the /json/whoami page/command. |
| 2060 | */ |
| 2061 | static cson_value * json_page_whoami(unsigned int depth){ |
| 2062 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -721,10 +721,14 @@ | |
| 721 | g.json.cmd.offset = -1; |
| 722 | if( !g.isHTTP && g.fullHttpReply ){ |
| 723 | /* workaround for server mode, so we see it as CGI mode. */ |
| 724 | g.isHTTP = 1; |
| 725 | } |
| 726 | if( !g.isHTTP ){ |
| 727 | g.json.errorDetailParanoia = 0 /*disable error code dumb-down for CLI mode*/; |
| 728 | } |
| 729 | |
| 730 | if(! g.json.post.v ){ |
| 731 | /* If cgi_init() reads POSTed JSON then it sets the content type. |
| 732 | If it did not then we need to set it. |
| 733 | */ |
| 734 | cgi_set_content_type(json_guess_content_type()); |
| @@ -1865,15 +1869,17 @@ | |
| 1869 | } |
| 1870 | return payV; |
| 1871 | } |
| 1872 | |
| 1873 | static cson_value * json_timeline_ci(unsigned int depth); |
| 1874 | static cson_value * json_timeline_wiki(unsigned int depth); |
| 1875 | /* |
| 1876 | ** Mapping of /json/timeline/XXX commands/paths to callbacks. |
| 1877 | */ |
| 1878 | static const JsonPageDef JsonPageDefs_Timeline[] = { |
| 1879 | {"ci", json_timeline_ci, 0}, |
| 1880 | {"wiki", json_timeline_wiki, 0}, |
| 1881 | /* Last entry MUST have a NULL name. */ |
| 1882 | {NULL,NULL,0} |
| 1883 | }; |
| 1884 | |
| 1885 | /* |
| @@ -1909,13 +1915,13 @@ | |
| 1915 | db_multi_exec(zSql); |
| 1916 | } |
| 1917 | |
| 1918 | /* |
| 1919 | ** Return a pointer to a constant string that forms the basis |
| 1920 | ** for a timeline query for the JSON interface. |
| 1921 | */ |
| 1922 | const char const * json_timeline_query(void){ |
| 1923 | /* Field order MUST match that from json_timeline_temp_table()!!! */ |
| 1924 | static const char zBaseSql[] = |
| 1925 | @ SELECT |
| 1926 | @ blob.rid, |
| 1927 | @ uuid, |
| @@ -1935,37 +1941,59 @@ | |
| 1941 | @ WHERE blob.rid=event.objid |
| 1942 | ; |
| 1943 | return zBaseSql; |
| 1944 | } |
| 1945 | |
| 1946 | /* |
| 1947 | ** Tries to figure out a timeline query length limit base on |
| 1948 | ** environment parameters. If it can it returns that value, |
| 1949 | ** else it returns some statically defined default value. |
| 1950 | ** |
| 1951 | ** Never returns a negative value. 0 means no limit. |
| 1952 | */ |
| 1953 | static int json_timeline_limit(){ |
| 1954 | static const int defaultLimit = 20; |
| 1955 | int limit = -1; |
| 1956 | if( g.isHTTP ){ |
| 1957 | limit = json_getenv_int("limit",-1); |
| 1958 | if(limit<0){ |
| 1959 | limit = json_getenv_int("n",-1); |
| 1960 | } |
| 1961 | }else{/* CLI mode */ |
| 1962 | char const * arg = find_option("limit","n",1); |
| 1963 | if(arg && *arg){ |
| 1964 | limit = atoi(arg); |
| 1965 | } |
| 1966 | } |
| 1967 | return (limit<0) ? defaultLimit : limit; |
| 1968 | } |
| 1969 | /* |
| 1970 | ** Implementation of /json/timeline/ci. |
| 1971 | ** |
| 1972 | ** Still a few TODOs (like figuring out how to structure |
| 1973 | ** inheritance info). |
| 1974 | */ |
| 1975 | static cson_value * json_timeline_ci(unsigned int depth){ |
| 1976 | cson_value * payV = NULL; |
| 1977 | cson_object * pay = NULL; |
| 1978 | cson_value * tmp = NULL; |
| 1979 | cson_value * listV = NULL; |
| 1980 | cson_array * list = NULL; |
| 1981 | int limit; |
| 1982 | int check = 0; |
| 1983 | Stmt q; |
| 1984 | Blob sql = empty_blob; |
| 1985 | if( !g.perm.Read/* && !g.perm.RdTkt && !g.perm.RdWiki*/ ){ |
| 1986 | g.json.resultCode = FSL_JSON_E_DENIED; |
| 1987 | return NULL; |
| 1988 | } |
| 1989 | limit = json_timeline_limit(); |
| 1990 | payV = cson_value_new_object(); |
| 1991 | pay = cson_value_get_object(payV); |
| 1992 | json_timeline_temp_table(); |
| 1993 | blob_append(&sql, "INSERT OR IGNORE INTO json_timeline ", -1); |
| 1994 | blob_append(&sql, json_timeline_query(), -1 ); |
| 1995 | blob_append(&sql, "AND event.type IN('ci') ", -1); |
| 1996 | blob_append(&sql, "ORDER BY mtime DESC ", -1); |
| 1997 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 1998 | g.json.resultCode = (cson_rc.AllocError==check) \ |
| 1999 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ |
| @@ -2052,10 +2080,119 @@ | |
| 2080 | cson_value_free(payV); |
| 2081 | payV = NULL; |
| 2082 | ok: |
| 2083 | return payV; |
| 2084 | } |
| 2085 | |
| 2086 | /* |
| 2087 | ** Implementation of /json/timeline/wiki. |
| 2088 | ** |
| 2089 | */ |
| 2090 | static cson_value * json_timeline_wiki(unsigned int depth){ |
| 2091 | /* This code is 95% the same as json_timeline_ci(), by the way. */ |
| 2092 | cson_value * payV = NULL; |
| 2093 | cson_object * pay = NULL; |
| 2094 | cson_value * tmp = NULL; |
| 2095 | cson_value * listV = NULL; |
| 2096 | cson_array * list = NULL; |
| 2097 | int limit; |
| 2098 | int check = 0; |
| 2099 | Stmt q; |
| 2100 | Blob sql = empty_blob; |
| 2101 | if( !g.perm.Read || !g.perm.RdWiki ){ |
| 2102 | g.json.resultCode = FSL_JSON_E_DENIED; |
| 2103 | return NULL; |
| 2104 | } |
| 2105 | limit = json_timeline_limit(); |
| 2106 | payV = cson_value_new_object(); |
| 2107 | pay = cson_value_get_object(payV); |
| 2108 | json_timeline_temp_table(); |
| 2109 | blob_append(&sql, "INSERT OR IGNORE INTO json_timeline ", -1); |
| 2110 | blob_append(&sql, json_timeline_query(), -1 ); |
| 2111 | blob_append(&sql, "AND event.type IN('w') ", -1); |
| 2112 | blob_append(&sql, "ORDER BY mtime DESC ", -1); |
| 2113 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 2114 | g.json.resultCode = (cson_rc.AllocError==check) \ |
| 2115 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ |
| 2116 | goto error;\ |
| 2117 | } |
| 2118 | if(limit){ |
| 2119 | blob_appendf(&sql,"LIMIT %d ",limit); |
| 2120 | tmp = cson_value_new_integer(limit); |
| 2121 | SET("limit"); |
| 2122 | } |
| 2123 | db_multi_exec(blob_buffer(&sql)); |
| 2124 | |
| 2125 | #if 0 |
| 2126 | /* only for testing! */ |
| 2127 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| 2128 | SET("timelineSql"); |
| 2129 | #endif |
| 2130 | |
| 2131 | blob_reset(&sql); |
| 2132 | blob_append(&sql, "SELECT rid AS rid," |
| 2133 | " uuid AS uuid," |
| 2134 | " mtime AS timestamp," |
| 2135 | " timestampString AS timestampString," |
| 2136 | " comment AS comment, " |
| 2137 | " user AS user," |
| 2138 | " eventType AS eventType" |
| 2139 | #if 0 |
| 2140 | /* can wiki pages have tags? */ |
| 2141 | " tags AS tags," /*FIXME: split this into |
| 2142 | a JSON array*/ |
| 2143 | " tagId AS tagId," |
| 2144 | #endif |
| 2145 | " FROM json_timeline" |
| 2146 | " ORDER BY mtime DESC", |
| 2147 | -1); |
| 2148 | db_prepare(&q,blob_buffer(&sql)); |
| 2149 | listV = cson_value_new_array(); |
| 2150 | list = cson_value_get_array(listV); |
| 2151 | tmp = listV; |
| 2152 | SET("timeline"); |
| 2153 | while( (SQLITE_ROW == db_step(&q) )){ |
| 2154 | /* convert each row into a JSON object...*/ |
| 2155 | cson_value * rowV = cson_sqlite3_row_to_object(q.pStmt); |
| 2156 | cson_object * row = cson_value_get_object(rowV); |
| 2157 | cson_string const * tagsStr = NULL; |
| 2158 | if(!row){ |
| 2159 | json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, |
| 2160 | "Could not convert at least one timeline result row to JSON." ); |
| 2161 | continue; |
| 2162 | } |
| 2163 | /* Split tags string field into JSON Array... */ |
| 2164 | cson_array_append(list, rowV); |
| 2165 | #if 0 |
| 2166 | tagsStr = cson_value_get_string(cson_object_get(row,"tags")); |
| 2167 | if(tagsStr){ |
| 2168 | cson_value * tags = json_string_split2( cson_string_cstr(tagsStr), |
| 2169 | ',', 0); |
| 2170 | if( tags ){ |
| 2171 | if(0 != cson_object_set(row,"tags",tags)){ |
| 2172 | cson_value_free(tags); |
| 2173 | }else{ |
| 2174 | /*replaced/deleted old tags value, invalidating tagsStr*/; |
| 2175 | tagsStr = NULL; |
| 2176 | } |
| 2177 | }else{ |
| 2178 | json_warn(FSL_JSON_W_STRING_TO_ARRAY_FAILED, |
| 2179 | "Could not convert tags string to array."); |
| 2180 | } |
| 2181 | } |
| 2182 | #endif |
| 2183 | } |
| 2184 | db_finalize(&q); |
| 2185 | #undef SET |
| 2186 | goto ok; |
| 2187 | error: |
| 2188 | cson_value_free(payV); |
| 2189 | payV = NULL; |
| 2190 | ok: |
| 2191 | return payV; |
| 2192 | } |
| 2193 | |
| 2194 | |
| 2195 | /* |
| 2196 | ** Implements the /json/whoami page/command. |
| 2197 | */ |
| 2198 | static cson_value * json_page_whoami(unsigned int depth){ |
| 2199 |