Fossil SCM
Implemented /json/timeline/ticket, cleaned up timeline/ci|wiki.
Commit
42900f3029d129d97f9e8c73f68a37ea9792b394
Parent
c24b44501290ad1…
1 file changed
+133
-48
+133
-48
| --- src/json.c | ||
| +++ src/json.c | ||
| @@ -1922,15 +1922,24 @@ | ||
| 1922 | 1922 | return payV; |
| 1923 | 1923 | } |
| 1924 | 1924 | |
| 1925 | 1925 | static cson_value * json_timeline_ci(); |
| 1926 | 1926 | static cson_value * json_timeline_wiki(); |
| 1927 | +static cson_value * json_timeline_ticket(); | |
| 1927 | 1928 | /* |
| 1928 | 1929 | ** Mapping of /json/timeline/XXX commands/paths to callbacks. |
| 1929 | 1930 | */ |
| 1930 | 1931 | static const JsonPageDef JsonPageDefs_Timeline[] = { |
| 1932 | +{"c", json_timeline_ci, 0}, | |
| 1931 | 1933 | {"ci", json_timeline_ci, 0}, |
| 1934 | +{"com", json_timeline_ci, 0}, | |
| 1935 | +{"commit", json_timeline_ci, 0}, | |
| 1936 | +{"t", json_timeline_ticket, 0}, | |
| 1937 | +{"ticket", json_timeline_ticket, 0}, | |
| 1938 | +{"w", json_timeline_wiki, 0}, | |
| 1939 | +{"wi", json_timeline_wiki, 0}, | |
| 1940 | +{"wik", json_timeline_wiki, 0}, | |
| 1932 | 1941 | {"wiki", json_timeline_wiki, 0}, |
| 1933 | 1942 | /* Last entry MUST have a NULL name. */ |
| 1934 | 1943 | {NULL,NULL,0} |
| 1935 | 1944 | }; |
| 1936 | 1945 | |
| @@ -2069,10 +2078,39 @@ | ||
| 2069 | 2078 | if(arg && *arg){ |
| 2070 | 2079 | limit = atoi(arg); |
| 2071 | 2080 | } |
| 2072 | 2081 | } |
| 2073 | 2082 | return (limit<0) ? defaultLimit : limit; |
| 2083 | +} | |
| 2084 | + | |
| 2085 | +/* | |
| 2086 | +** Internal helper for the json_timeline_EVENTTYPE() family of | |
| 2087 | +** functions. zEventType must be one of (ci, w, t). pSql must be a | |
| 2088 | +** cleanly-initialized, empty Blob to store the sql in. If pPayload is | |
| 2089 | +** not NULL it is assumed to be the pending response payload. If | |
| 2090 | +** json_timeline_limit() returns non-0, this function adds a LIMIT | |
| 2091 | +** clause to the generated SQL and (if pPayload is not NULL) adds the | |
| 2092 | +** limit value as the "limit" property of pPayload. | |
| 2093 | +*/ | |
| 2094 | +static void json_timeline_setup_sql( char const * zEventType, | |
| 2095 | + Blob * pSql, | |
| 2096 | + cson_object * pPayload ){ | |
| 2097 | + int limit; | |
| 2098 | + assert( zEventType && *zEventType && pSql ); | |
| 2099 | + json_timeline_temp_table(); | |
| 2100 | + blob_append(pSql, "INSERT OR IGNORE INTO json_timeline ", -1); | |
| 2101 | + blob_append(pSql, json_timeline_query(), -1 ); | |
| 2102 | + blob_appendf(pSql, " AND event.type IN(%Q) ", zEventType); | |
| 2103 | + json_timeline_add_time_clause(pSql); | |
| 2104 | + limit = json_timeline_limit(); | |
| 2105 | + if(limit){ | |
| 2106 | + blob_appendf(pSql,"LIMIT %d ",limit); | |
| 2107 | + } | |
| 2108 | + if(pPayload){ | |
| 2109 | + cson_object_set(pPayload, "limit",cson_value_new_integer(limit)); | |
| 2110 | + } | |
| 2111 | + | |
| 2074 | 2112 | } |
| 2075 | 2113 | /* |
| 2076 | 2114 | ** Implementation of /json/timeline/ci. |
| 2077 | 2115 | ** |
| 2078 | 2116 | ** Still a few TODOs (like figuring out how to structure |
| @@ -2082,36 +2120,25 @@ | ||
| 2082 | 2120 | cson_value * payV = NULL; |
| 2083 | 2121 | cson_object * pay = NULL; |
| 2084 | 2122 | cson_value * tmp = NULL; |
| 2085 | 2123 | cson_value * listV = NULL; |
| 2086 | 2124 | cson_array * list = NULL; |
| 2087 | - int limit; | |
| 2088 | 2125 | int check = 0; |
| 2089 | 2126 | Stmt q; |
| 2090 | 2127 | Blob sql = empty_blob; |
| 2091 | 2128 | if( !g.perm.Read/* && !g.perm.RdTkt && !g.perm.RdWiki*/ ){ |
| 2092 | 2129 | g.json.resultCode = FSL_JSON_E_DENIED; |
| 2093 | 2130 | return NULL; |
| 2094 | 2131 | } |
| 2095 | - limit = json_timeline_limit(); | |
| 2096 | 2132 | payV = cson_value_new_object(); |
| 2097 | 2133 | pay = cson_value_get_object(payV); |
| 2098 | - json_timeline_temp_table(); | |
| 2099 | - blob_append(&sql, "INSERT OR IGNORE INTO json_timeline ", -1); | |
| 2100 | - blob_append(&sql, json_timeline_query(), -1 ); | |
| 2101 | - blob_append(&sql, " AND event.type IN('ci') ", -1); | |
| 2102 | - json_timeline_add_time_clause(&sql); | |
| 2134 | + json_timeline_setup_sql( "ci", &sql, pay ); | |
| 2103 | 2135 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 2104 | 2136 | g.json.resultCode = (cson_rc.AllocError==check) \ |
| 2105 | 2137 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ |
| 2106 | 2138 | goto error;\ |
| 2107 | 2139 | } |
| 2108 | - if(limit){ | |
| 2109 | - blob_appendf(&sql,"LIMIT %d ",limit); | |
| 2110 | - tmp = cson_value_new_integer(limit); | |
| 2111 | - SET("limit"); | |
| 2112 | - } | |
| 2113 | 2140 | db_multi_exec(blob_buffer(&sql)); |
| 2114 | 2141 | |
| 2115 | 2142 | #if 0 |
| 2116 | 2143 | /* only for testing! */ |
| 2117 | 2144 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| @@ -2125,16 +2152,15 @@ | ||
| 2125 | 2152 | " mtime AS timestamp," |
| 2126 | 2153 | " timestampString AS timestampString," |
| 2127 | 2154 | " comment AS comment, " |
| 2128 | 2155 | " user AS user," |
| 2129 | 2156 | " isLeaf AS isLeaf," /*FIXME: convert to JSON bool */ |
| 2130 | - " bgColor AS bgColor," | |
| 2157 | + " bgColor AS bgColor," /* why always null? */ | |
| 2131 | 2158 | " eventType AS eventType," |
| 2132 | 2159 | " tags AS tags," /*FIXME: split this into |
| 2133 | 2160 | a JSON array*/ |
| 2134 | - " tagId AS tagId," | |
| 2135 | - " brief AS briefText" | |
| 2161 | + " tagId AS tagId" | |
| 2136 | 2162 | " FROM json_timeline" |
| 2137 | 2163 | " ORDER BY sortId", |
| 2138 | 2164 | -1); |
| 2139 | 2165 | db_prepare(&q,blob_buffer(&sql)); |
| 2140 | 2166 | listV = cson_value_new_array(); |
| @@ -2182,10 +2208,11 @@ | ||
| 2182 | 2208 | } |
| 2183 | 2209 | db_finalize(&q); |
| 2184 | 2210 | #undef SET |
| 2185 | 2211 | goto ok; |
| 2186 | 2212 | error: |
| 2213 | + assert( 0 != g.json.resultCode ); | |
| 2187 | 2214 | cson_value_free(payV); |
| 2188 | 2215 | payV = NULL; |
| 2189 | 2216 | ok: |
| 2190 | 2217 | return payV; |
| 2191 | 2218 | } |
| @@ -2199,36 +2226,25 @@ | ||
| 2199 | 2226 | cson_value * payV = NULL; |
| 2200 | 2227 | cson_object * pay = NULL; |
| 2201 | 2228 | cson_value * tmp = NULL; |
| 2202 | 2229 | cson_value * listV = NULL; |
| 2203 | 2230 | cson_array * list = NULL; |
| 2204 | - int limit; | |
| 2205 | 2231 | int check = 0; |
| 2206 | 2232 | Stmt q; |
| 2207 | 2233 | Blob sql = empty_blob; |
| 2208 | 2234 | if( !g.perm.Read || !g.perm.RdWiki ){ |
| 2209 | 2235 | g.json.resultCode = FSL_JSON_E_DENIED; |
| 2210 | 2236 | return NULL; |
| 2211 | 2237 | } |
| 2212 | - limit = json_timeline_limit(); | |
| 2213 | 2238 | payV = cson_value_new_object(); |
| 2214 | 2239 | pay = cson_value_get_object(payV); |
| 2215 | - json_timeline_temp_table(); | |
| 2216 | - blob_append(&sql, "INSERT OR IGNORE INTO json_timeline ", -1); | |
| 2217 | - blob_append(&sql, json_timeline_query(), -1 ); | |
| 2218 | - blob_append(&sql, "AND event.type IN('w') ", -1); | |
| 2219 | - json_timeline_add_time_clause(&sql); | |
| 2240 | + json_timeline_setup_sql( "w", &sql, pay ); | |
| 2220 | 2241 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 2221 | 2242 | g.json.resultCode = (cson_rc.AllocError==check) \ |
| 2222 | 2243 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ |
| 2223 | 2244 | goto error;\ |
| 2224 | 2245 | } |
| 2225 | - if(limit){ | |
| 2226 | - blob_appendf(&sql,"LIMIT %d ",limit); | |
| 2227 | - tmp = cson_value_new_integer(limit); | |
| 2228 | - SET("limit"); | |
| 2229 | - } | |
| 2230 | 2246 | db_multi_exec(blob_buffer(&sql)); |
| 2231 | 2247 | |
| 2232 | 2248 | #if 0 |
| 2233 | 2249 | /* only for testing! */ |
| 2234 | 2250 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| @@ -2259,41 +2275,110 @@ | ||
| 2259 | 2275 | SET("timeline"); |
| 2260 | 2276 | while( (SQLITE_ROW == db_step(&q) )){ |
| 2261 | 2277 | /* convert each row into a JSON object...*/ |
| 2262 | 2278 | cson_value * rowV = cson_sqlite3_row_to_object(q.pStmt); |
| 2263 | 2279 | cson_object * row = cson_value_get_object(rowV); |
| 2264 | - cson_string const * tagsStr = NULL; | |
| 2280 | + int rc; | |
| 2281 | + if(!row){ | |
| 2282 | + json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, | |
| 2283 | + "Could not convert at least one timeline result row to JSON." ); | |
| 2284 | + continue; | |
| 2285 | + } | |
| 2286 | + rc = cson_array_append( list, rowV ); | |
| 2287 | + if( 0 != rc ){ | |
| 2288 | + cson_value_free(rowV); | |
| 2289 | + g.json.resultCode = (cson_rc.AllocError==rc) | |
| 2290 | + ? FSL_JSON_E_ALLOC | |
| 2291 | + : FSL_JSON_E_UNKNOWN; | |
| 2292 | + goto error; | |
| 2293 | + } | |
| 2294 | + } | |
| 2295 | + db_finalize(&q); | |
| 2296 | +#undef SET | |
| 2297 | + goto ok; | |
| 2298 | + error: | |
| 2299 | + assert( 0 != g.json.resultCode ); | |
| 2300 | + cson_value_free(payV); | |
| 2301 | + payV = NULL; | |
| 2302 | + ok: | |
| 2303 | + return payV; | |
| 2304 | +} | |
| 2305 | + | |
| 2306 | +/* | |
| 2307 | +** Implementation of /json/timeline/ticket. | |
| 2308 | +** | |
| 2309 | +*/ | |
| 2310 | +static cson_value * json_timeline_ticket(){ | |
| 2311 | + /* This code is 95% the same as json_timeline_ci(), by the way. */ | |
| 2312 | + cson_value * payV = NULL; | |
| 2313 | + cson_object * pay = NULL; | |
| 2314 | + cson_value * tmp = NULL; | |
| 2315 | + cson_value * listV = NULL; | |
| 2316 | + cson_array * list = NULL; | |
| 2317 | + int check = 0; | |
| 2318 | + Stmt q; | |
| 2319 | + Blob sql = empty_blob; | |
| 2320 | + if( !g.perm.Read || !g.perm.RdTkt ){ | |
| 2321 | + g.json.resultCode = FSL_JSON_E_DENIED; | |
| 2322 | + return NULL; | |
| 2323 | + } | |
| 2324 | + payV = cson_value_new_object(); | |
| 2325 | + pay = cson_value_get_object(payV); | |
| 2326 | + json_timeline_setup_sql( "t", &sql, pay ); | |
| 2327 | + db_multi_exec(blob_buffer(&sql)); | |
| 2328 | +#define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ | |
| 2329 | + g.json.resultCode = (cson_rc.AllocError==check) \ | |
| 2330 | + ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ | |
| 2331 | + goto error;\ | |
| 2332 | + } | |
| 2333 | + | |
| 2334 | +#if 0 | |
| 2335 | + /* only for testing! */ | |
| 2336 | + tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); | |
| 2337 | + SET("timelineSql"); | |
| 2338 | +#endif | |
| 2339 | + | |
| 2340 | + blob_reset(&sql); | |
| 2341 | + blob_append(&sql, "SELECT rid AS rid," | |
| 2342 | + " uuid AS uuid," | |
| 2343 | + " mtime AS timestamp," | |
| 2344 | + " timestampString AS timestampString," | |
| 2345 | + " user AS user," | |
| 2346 | + " eventType AS eventType," | |
| 2347 | + " brief AS briefText" | |
| 2348 | + " FROM json_timeline" | |
| 2349 | + " ORDER BY sortId", | |
| 2350 | + -1); | |
| 2351 | + db_prepare(&q,blob_buffer(&sql)); | |
| 2352 | + listV = cson_value_new_array(); | |
| 2353 | + list = cson_value_get_array(listV); | |
| 2354 | + tmp = listV; | |
| 2355 | + SET("timeline"); | |
| 2356 | + while( (SQLITE_ROW == db_step(&q) )){ | |
| 2357 | + /* convert each row into a JSON object...*/ | |
| 2358 | + int rc; | |
| 2359 | + cson_value * rowV = cson_sqlite3_row_to_object(q.pStmt); | |
| 2360 | + cson_object * row = cson_value_get_object(rowV); | |
| 2265 | 2361 | if(!row){ |
| 2266 | 2362 | json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, |
| 2267 | 2363 | "Could not convert at least one timeline result row to JSON." ); |
| 2268 | 2364 | continue; |
| 2269 | 2365 | } |
| 2270 | - /* Split tags string field into JSON Array... */ | |
| 2271 | - cson_array_append(list, rowV); | |
| 2272 | -#if 0 | |
| 2273 | - tagsStr = cson_value_get_string(cson_object_get(row,"tags")); | |
| 2274 | - if(tagsStr){ | |
| 2275 | - cson_value * tags = json_string_split2( cson_string_cstr(tagsStr), | |
| 2276 | - ',', 0); | |
| 2277 | - if( tags ){ | |
| 2278 | - if(0 != cson_object_set(row,"tags",tags)){ | |
| 2279 | - cson_value_free(tags); | |
| 2280 | - }else{ | |
| 2281 | - /*replaced/deleted old tags value, invalidating tagsStr*/; | |
| 2282 | - tagsStr = NULL; | |
| 2283 | - } | |
| 2284 | - }else{ | |
| 2285 | - json_warn(FSL_JSON_W_STRING_TO_ARRAY_FAILED, | |
| 2286 | - "Could not convert tags string to array."); | |
| 2287 | - } | |
| 2288 | - } | |
| 2289 | -#endif | |
| 2366 | + rc = cson_array_append( list, rowV ); | |
| 2367 | + if( 0 != rc ){ | |
| 2368 | + cson_value_free(rowV); | |
| 2369 | + g.json.resultCode = (cson_rc.AllocError==rc) | |
| 2370 | + ? FSL_JSON_E_ALLOC | |
| 2371 | + : FSL_JSON_E_UNKNOWN; | |
| 2372 | + goto error; | |
| 2373 | + } | |
| 2290 | 2374 | } |
| 2291 | 2375 | db_finalize(&q); |
| 2292 | 2376 | #undef SET |
| 2293 | 2377 | goto ok; |
| 2294 | 2378 | error: |
| 2379 | + assert( 0 != g.json.resultCode ); | |
| 2295 | 2380 | cson_value_free(payV); |
| 2296 | 2381 | payV = NULL; |
| 2297 | 2382 | ok: |
| 2298 | 2383 | return payV; |
| 2299 | 2384 | } |
| 2300 | 2385 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -1922,15 +1922,24 @@ | |
| 1922 | return payV; |
| 1923 | } |
| 1924 | |
| 1925 | static cson_value * json_timeline_ci(); |
| 1926 | static cson_value * json_timeline_wiki(); |
| 1927 | /* |
| 1928 | ** Mapping of /json/timeline/XXX commands/paths to callbacks. |
| 1929 | */ |
| 1930 | static const JsonPageDef JsonPageDefs_Timeline[] = { |
| 1931 | {"ci", json_timeline_ci, 0}, |
| 1932 | {"wiki", json_timeline_wiki, 0}, |
| 1933 | /* Last entry MUST have a NULL name. */ |
| 1934 | {NULL,NULL,0} |
| 1935 | }; |
| 1936 | |
| @@ -2069,10 +2078,39 @@ | |
| 2069 | if(arg && *arg){ |
| 2070 | limit = atoi(arg); |
| 2071 | } |
| 2072 | } |
| 2073 | return (limit<0) ? defaultLimit : limit; |
| 2074 | } |
| 2075 | /* |
| 2076 | ** Implementation of /json/timeline/ci. |
| 2077 | ** |
| 2078 | ** Still a few TODOs (like figuring out how to structure |
| @@ -2082,36 +2120,25 @@ | |
| 2082 | cson_value * payV = NULL; |
| 2083 | cson_object * pay = NULL; |
| 2084 | cson_value * tmp = NULL; |
| 2085 | cson_value * listV = NULL; |
| 2086 | cson_array * list = NULL; |
| 2087 | int limit; |
| 2088 | int check = 0; |
| 2089 | Stmt q; |
| 2090 | Blob sql = empty_blob; |
| 2091 | if( !g.perm.Read/* && !g.perm.RdTkt && !g.perm.RdWiki*/ ){ |
| 2092 | g.json.resultCode = FSL_JSON_E_DENIED; |
| 2093 | return NULL; |
| 2094 | } |
| 2095 | limit = json_timeline_limit(); |
| 2096 | payV = cson_value_new_object(); |
| 2097 | pay = cson_value_get_object(payV); |
| 2098 | json_timeline_temp_table(); |
| 2099 | blob_append(&sql, "INSERT OR IGNORE INTO json_timeline ", -1); |
| 2100 | blob_append(&sql, json_timeline_query(), -1 ); |
| 2101 | blob_append(&sql, " AND event.type IN('ci') ", -1); |
| 2102 | json_timeline_add_time_clause(&sql); |
| 2103 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 2104 | g.json.resultCode = (cson_rc.AllocError==check) \ |
| 2105 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ |
| 2106 | goto error;\ |
| 2107 | } |
| 2108 | if(limit){ |
| 2109 | blob_appendf(&sql,"LIMIT %d ",limit); |
| 2110 | tmp = cson_value_new_integer(limit); |
| 2111 | SET("limit"); |
| 2112 | } |
| 2113 | db_multi_exec(blob_buffer(&sql)); |
| 2114 | |
| 2115 | #if 0 |
| 2116 | /* only for testing! */ |
| 2117 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| @@ -2125,16 +2152,15 @@ | |
| 2125 | " mtime AS timestamp," |
| 2126 | " timestampString AS timestampString," |
| 2127 | " comment AS comment, " |
| 2128 | " user AS user," |
| 2129 | " isLeaf AS isLeaf," /*FIXME: convert to JSON bool */ |
| 2130 | " bgColor AS bgColor," |
| 2131 | " eventType AS eventType," |
| 2132 | " tags AS tags," /*FIXME: split this into |
| 2133 | a JSON array*/ |
| 2134 | " tagId AS tagId," |
| 2135 | " brief AS briefText" |
| 2136 | " FROM json_timeline" |
| 2137 | " ORDER BY sortId", |
| 2138 | -1); |
| 2139 | db_prepare(&q,blob_buffer(&sql)); |
| 2140 | listV = cson_value_new_array(); |
| @@ -2182,10 +2208,11 @@ | |
| 2182 | } |
| 2183 | db_finalize(&q); |
| 2184 | #undef SET |
| 2185 | goto ok; |
| 2186 | error: |
| 2187 | cson_value_free(payV); |
| 2188 | payV = NULL; |
| 2189 | ok: |
| 2190 | return payV; |
| 2191 | } |
| @@ -2199,36 +2226,25 @@ | |
| 2199 | cson_value * payV = NULL; |
| 2200 | cson_object * pay = NULL; |
| 2201 | cson_value * tmp = NULL; |
| 2202 | cson_value * listV = NULL; |
| 2203 | cson_array * list = NULL; |
| 2204 | int limit; |
| 2205 | int check = 0; |
| 2206 | Stmt q; |
| 2207 | Blob sql = empty_blob; |
| 2208 | if( !g.perm.Read || !g.perm.RdWiki ){ |
| 2209 | g.json.resultCode = FSL_JSON_E_DENIED; |
| 2210 | return NULL; |
| 2211 | } |
| 2212 | limit = json_timeline_limit(); |
| 2213 | payV = cson_value_new_object(); |
| 2214 | pay = cson_value_get_object(payV); |
| 2215 | json_timeline_temp_table(); |
| 2216 | blob_append(&sql, "INSERT OR IGNORE INTO json_timeline ", -1); |
| 2217 | blob_append(&sql, json_timeline_query(), -1 ); |
| 2218 | blob_append(&sql, "AND event.type IN('w') ", -1); |
| 2219 | json_timeline_add_time_clause(&sql); |
| 2220 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 2221 | g.json.resultCode = (cson_rc.AllocError==check) \ |
| 2222 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ |
| 2223 | goto error;\ |
| 2224 | } |
| 2225 | if(limit){ |
| 2226 | blob_appendf(&sql,"LIMIT %d ",limit); |
| 2227 | tmp = cson_value_new_integer(limit); |
| 2228 | SET("limit"); |
| 2229 | } |
| 2230 | db_multi_exec(blob_buffer(&sql)); |
| 2231 | |
| 2232 | #if 0 |
| 2233 | /* only for testing! */ |
| 2234 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| @@ -2259,41 +2275,110 @@ | |
| 2259 | SET("timeline"); |
| 2260 | while( (SQLITE_ROW == db_step(&q) )){ |
| 2261 | /* convert each row into a JSON object...*/ |
| 2262 | cson_value * rowV = cson_sqlite3_row_to_object(q.pStmt); |
| 2263 | cson_object * row = cson_value_get_object(rowV); |
| 2264 | cson_string const * tagsStr = NULL; |
| 2265 | if(!row){ |
| 2266 | json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, |
| 2267 | "Could not convert at least one timeline result row to JSON." ); |
| 2268 | continue; |
| 2269 | } |
| 2270 | /* Split tags string field into JSON Array... */ |
| 2271 | cson_array_append(list, rowV); |
| 2272 | #if 0 |
| 2273 | tagsStr = cson_value_get_string(cson_object_get(row,"tags")); |
| 2274 | if(tagsStr){ |
| 2275 | cson_value * tags = json_string_split2( cson_string_cstr(tagsStr), |
| 2276 | ',', 0); |
| 2277 | if( tags ){ |
| 2278 | if(0 != cson_object_set(row,"tags",tags)){ |
| 2279 | cson_value_free(tags); |
| 2280 | }else{ |
| 2281 | /*replaced/deleted old tags value, invalidating tagsStr*/; |
| 2282 | tagsStr = NULL; |
| 2283 | } |
| 2284 | }else{ |
| 2285 | json_warn(FSL_JSON_W_STRING_TO_ARRAY_FAILED, |
| 2286 | "Could not convert tags string to array."); |
| 2287 | } |
| 2288 | } |
| 2289 | #endif |
| 2290 | } |
| 2291 | db_finalize(&q); |
| 2292 | #undef SET |
| 2293 | goto ok; |
| 2294 | error: |
| 2295 | cson_value_free(payV); |
| 2296 | payV = NULL; |
| 2297 | ok: |
| 2298 | return payV; |
| 2299 | } |
| 2300 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -1922,15 +1922,24 @@ | |
| 1922 | return payV; |
| 1923 | } |
| 1924 | |
| 1925 | static cson_value * json_timeline_ci(); |
| 1926 | static cson_value * json_timeline_wiki(); |
| 1927 | static cson_value * json_timeline_ticket(); |
| 1928 | /* |
| 1929 | ** Mapping of /json/timeline/XXX commands/paths to callbacks. |
| 1930 | */ |
| 1931 | static const JsonPageDef JsonPageDefs_Timeline[] = { |
| 1932 | {"c", json_timeline_ci, 0}, |
| 1933 | {"ci", json_timeline_ci, 0}, |
| 1934 | {"com", json_timeline_ci, 0}, |
| 1935 | {"commit", json_timeline_ci, 0}, |
| 1936 | {"t", json_timeline_ticket, 0}, |
| 1937 | {"ticket", json_timeline_ticket, 0}, |
| 1938 | {"w", json_timeline_wiki, 0}, |
| 1939 | {"wi", json_timeline_wiki, 0}, |
| 1940 | {"wik", json_timeline_wiki, 0}, |
| 1941 | {"wiki", json_timeline_wiki, 0}, |
| 1942 | /* Last entry MUST have a NULL name. */ |
| 1943 | {NULL,NULL,0} |
| 1944 | }; |
| 1945 | |
| @@ -2069,10 +2078,39 @@ | |
| 2078 | if(arg && *arg){ |
| 2079 | limit = atoi(arg); |
| 2080 | } |
| 2081 | } |
| 2082 | return (limit<0) ? defaultLimit : limit; |
| 2083 | } |
| 2084 | |
| 2085 | /* |
| 2086 | ** Internal helper for the json_timeline_EVENTTYPE() family of |
| 2087 | ** functions. zEventType must be one of (ci, w, t). pSql must be a |
| 2088 | ** cleanly-initialized, empty Blob to store the sql in. If pPayload is |
| 2089 | ** not NULL it is assumed to be the pending response payload. If |
| 2090 | ** json_timeline_limit() returns non-0, this function adds a LIMIT |
| 2091 | ** clause to the generated SQL and (if pPayload is not NULL) adds the |
| 2092 | ** limit value as the "limit" property of pPayload. |
| 2093 | */ |
| 2094 | static void json_timeline_setup_sql( char const * zEventType, |
| 2095 | Blob * pSql, |
| 2096 | cson_object * pPayload ){ |
| 2097 | int limit; |
| 2098 | assert( zEventType && *zEventType && pSql ); |
| 2099 | json_timeline_temp_table(); |
| 2100 | blob_append(pSql, "INSERT OR IGNORE INTO json_timeline ", -1); |
| 2101 | blob_append(pSql, json_timeline_query(), -1 ); |
| 2102 | blob_appendf(pSql, " AND event.type IN(%Q) ", zEventType); |
| 2103 | json_timeline_add_time_clause(pSql); |
| 2104 | limit = json_timeline_limit(); |
| 2105 | if(limit){ |
| 2106 | blob_appendf(pSql,"LIMIT %d ",limit); |
| 2107 | } |
| 2108 | if(pPayload){ |
| 2109 | cson_object_set(pPayload, "limit",cson_value_new_integer(limit)); |
| 2110 | } |
| 2111 | |
| 2112 | } |
| 2113 | /* |
| 2114 | ** Implementation of /json/timeline/ci. |
| 2115 | ** |
| 2116 | ** Still a few TODOs (like figuring out how to structure |
| @@ -2082,36 +2120,25 @@ | |
| 2120 | cson_value * payV = NULL; |
| 2121 | cson_object * pay = NULL; |
| 2122 | cson_value * tmp = NULL; |
| 2123 | cson_value * listV = NULL; |
| 2124 | cson_array * list = NULL; |
| 2125 | int check = 0; |
| 2126 | Stmt q; |
| 2127 | Blob sql = empty_blob; |
| 2128 | if( !g.perm.Read/* && !g.perm.RdTkt && !g.perm.RdWiki*/ ){ |
| 2129 | g.json.resultCode = FSL_JSON_E_DENIED; |
| 2130 | return NULL; |
| 2131 | } |
| 2132 | payV = cson_value_new_object(); |
| 2133 | pay = cson_value_get_object(payV); |
| 2134 | json_timeline_setup_sql( "ci", &sql, pay ); |
| 2135 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 2136 | g.json.resultCode = (cson_rc.AllocError==check) \ |
| 2137 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ |
| 2138 | goto error;\ |
| 2139 | } |
| 2140 | db_multi_exec(blob_buffer(&sql)); |
| 2141 | |
| 2142 | #if 0 |
| 2143 | /* only for testing! */ |
| 2144 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| @@ -2125,16 +2152,15 @@ | |
| 2152 | " mtime AS timestamp," |
| 2153 | " timestampString AS timestampString," |
| 2154 | " comment AS comment, " |
| 2155 | " user AS user," |
| 2156 | " isLeaf AS isLeaf," /*FIXME: convert to JSON bool */ |
| 2157 | " bgColor AS bgColor," /* why always null? */ |
| 2158 | " eventType AS eventType," |
| 2159 | " tags AS tags," /*FIXME: split this into |
| 2160 | a JSON array*/ |
| 2161 | " tagId AS tagId" |
| 2162 | " FROM json_timeline" |
| 2163 | " ORDER BY sortId", |
| 2164 | -1); |
| 2165 | db_prepare(&q,blob_buffer(&sql)); |
| 2166 | listV = cson_value_new_array(); |
| @@ -2182,10 +2208,11 @@ | |
| 2208 | } |
| 2209 | db_finalize(&q); |
| 2210 | #undef SET |
| 2211 | goto ok; |
| 2212 | error: |
| 2213 | assert( 0 != g.json.resultCode ); |
| 2214 | cson_value_free(payV); |
| 2215 | payV = NULL; |
| 2216 | ok: |
| 2217 | return payV; |
| 2218 | } |
| @@ -2199,36 +2226,25 @@ | |
| 2226 | cson_value * payV = NULL; |
| 2227 | cson_object * pay = NULL; |
| 2228 | cson_value * tmp = NULL; |
| 2229 | cson_value * listV = NULL; |
| 2230 | cson_array * list = NULL; |
| 2231 | int check = 0; |
| 2232 | Stmt q; |
| 2233 | Blob sql = empty_blob; |
| 2234 | if( !g.perm.Read || !g.perm.RdWiki ){ |
| 2235 | g.json.resultCode = FSL_JSON_E_DENIED; |
| 2236 | return NULL; |
| 2237 | } |
| 2238 | payV = cson_value_new_object(); |
| 2239 | pay = cson_value_get_object(payV); |
| 2240 | json_timeline_setup_sql( "w", &sql, pay ); |
| 2241 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 2242 | g.json.resultCode = (cson_rc.AllocError==check) \ |
| 2243 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ |
| 2244 | goto error;\ |
| 2245 | } |
| 2246 | db_multi_exec(blob_buffer(&sql)); |
| 2247 | |
| 2248 | #if 0 |
| 2249 | /* only for testing! */ |
| 2250 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| @@ -2259,41 +2275,110 @@ | |
| 2275 | SET("timeline"); |
| 2276 | while( (SQLITE_ROW == db_step(&q) )){ |
| 2277 | /* convert each row into a JSON object...*/ |
| 2278 | cson_value * rowV = cson_sqlite3_row_to_object(q.pStmt); |
| 2279 | cson_object * row = cson_value_get_object(rowV); |
| 2280 | int rc; |
| 2281 | if(!row){ |
| 2282 | json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, |
| 2283 | "Could not convert at least one timeline result row to JSON." ); |
| 2284 | continue; |
| 2285 | } |
| 2286 | rc = cson_array_append( list, rowV ); |
| 2287 | if( 0 != rc ){ |
| 2288 | cson_value_free(rowV); |
| 2289 | g.json.resultCode = (cson_rc.AllocError==rc) |
| 2290 | ? FSL_JSON_E_ALLOC |
| 2291 | : FSL_JSON_E_UNKNOWN; |
| 2292 | goto error; |
| 2293 | } |
| 2294 | } |
| 2295 | db_finalize(&q); |
| 2296 | #undef SET |
| 2297 | goto ok; |
| 2298 | error: |
| 2299 | assert( 0 != g.json.resultCode ); |
| 2300 | cson_value_free(payV); |
| 2301 | payV = NULL; |
| 2302 | ok: |
| 2303 | return payV; |
| 2304 | } |
| 2305 | |
| 2306 | /* |
| 2307 | ** Implementation of /json/timeline/ticket. |
| 2308 | ** |
| 2309 | */ |
| 2310 | static cson_value * json_timeline_ticket(){ |
| 2311 | /* This code is 95% the same as json_timeline_ci(), by the way. */ |
| 2312 | cson_value * payV = NULL; |
| 2313 | cson_object * pay = NULL; |
| 2314 | cson_value * tmp = NULL; |
| 2315 | cson_value * listV = NULL; |
| 2316 | cson_array * list = NULL; |
| 2317 | int check = 0; |
| 2318 | Stmt q; |
| 2319 | Blob sql = empty_blob; |
| 2320 | if( !g.perm.Read || !g.perm.RdTkt ){ |
| 2321 | g.json.resultCode = FSL_JSON_E_DENIED; |
| 2322 | return NULL; |
| 2323 | } |
| 2324 | payV = cson_value_new_object(); |
| 2325 | pay = cson_value_get_object(payV); |
| 2326 | json_timeline_setup_sql( "t", &sql, pay ); |
| 2327 | db_multi_exec(blob_buffer(&sql)); |
| 2328 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 2329 | g.json.resultCode = (cson_rc.AllocError==check) \ |
| 2330 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ |
| 2331 | goto error;\ |
| 2332 | } |
| 2333 | |
| 2334 | #if 0 |
| 2335 | /* only for testing! */ |
| 2336 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| 2337 | SET("timelineSql"); |
| 2338 | #endif |
| 2339 | |
| 2340 | blob_reset(&sql); |
| 2341 | blob_append(&sql, "SELECT rid AS rid," |
| 2342 | " uuid AS uuid," |
| 2343 | " mtime AS timestamp," |
| 2344 | " timestampString AS timestampString," |
| 2345 | " user AS user," |
| 2346 | " eventType AS eventType," |
| 2347 | " brief AS briefText" |
| 2348 | " FROM json_timeline" |
| 2349 | " ORDER BY sortId", |
| 2350 | -1); |
| 2351 | db_prepare(&q,blob_buffer(&sql)); |
| 2352 | listV = cson_value_new_array(); |
| 2353 | list = cson_value_get_array(listV); |
| 2354 | tmp = listV; |
| 2355 | SET("timeline"); |
| 2356 | while( (SQLITE_ROW == db_step(&q) )){ |
| 2357 | /* convert each row into a JSON object...*/ |
| 2358 | int rc; |
| 2359 | cson_value * rowV = cson_sqlite3_row_to_object(q.pStmt); |
| 2360 | cson_object * row = cson_value_get_object(rowV); |
| 2361 | if(!row){ |
| 2362 | json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, |
| 2363 | "Could not convert at least one timeline result row to JSON." ); |
| 2364 | continue; |
| 2365 | } |
| 2366 | rc = cson_array_append( list, rowV ); |
| 2367 | if( 0 != rc ){ |
| 2368 | cson_value_free(rowV); |
| 2369 | g.json.resultCode = (cson_rc.AllocError==rc) |
| 2370 | ? FSL_JSON_E_ALLOC |
| 2371 | : FSL_JSON_E_UNKNOWN; |
| 2372 | goto error; |
| 2373 | } |
| 2374 | } |
| 2375 | db_finalize(&q); |
| 2376 | #undef SET |
| 2377 | goto ok; |
| 2378 | error: |
| 2379 | assert( 0 != g.json.resultCode ); |
| 2380 | cson_value_free(payV); |
| 2381 | payV = NULL; |
| 2382 | ok: |
| 2383 | return payV; |
| 2384 | } |
| 2385 |