Fossil

Check-in [b1f92572]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Started refactoring some of the timeline/artifact components into reusable parts. Comment edited only to test json responses which differentiate between pristine and edited commits.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | json-multitag-test | json
Files: files | file ages | folders
SHA1: b1f9257213c4447b0a072e73f7845b43e85074cd
User & Date: stephan 2011-10-01 06:45:18
Original Comment: Started refactoring some of the timeline/artifact components into reusable parts.
Context
2011-10-01
07:04
more timeline/artifact refactoring. check-in: 22fc0ab8 user: stephan tags: json-multitag-test, json
06:45
Started refactoring some of the timeline/artifact components into reusable parts. Comment edited only to test json responses which differentiate between pristine and edited commits. check-in: b1f92572 user: stephan tags: json-multitag-test, json
05:58
Started adding artifact-type-dependent result data to /json/artifact output. check-in: a893fff3 user: stephan tags: json-multitag-test, json
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ajax/index.html.

218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
<input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/timeline/ci?showFiles=true")' />
<input type='button' value='timeline/wiki' onclick='TheApp.cgi.sendCommand("/json/timeline/wiki")' />
<input type='button' value='timeline/ticket' onclick='TheApp.cgi.sendCommand("/json/timeline/ticket")' />
<input type='button' value='wiki/list' onclick='TheApp.cgi.sendCommand("/json/wiki/list")' />
<input type='button' value='wiki/get Fossil' onclick='TheApp.cgi.sendCommand("/json/wiki/get",{page:"Fossil"})' />
<input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/user/list")' />
<input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/user/get?name=anonymous")' />
<input type='button' value='artifact/XYZ' onclick='TheApp.cgi.sendCommand("/json/artifact?uuid=trunk")' />

<!--
<input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' />
<input type='button' value='get more' onclick='TheApp.cgi.getPages("HelloWorld/WhikiNews")' />
<input type='button' value='get client data' onclick='TheApp.cgi.getPageClientData("HelloWorld/whiki/WhikiCommands")' />
<input type='button' value='save client data' onclick='TheApp.cgi.savePageClientData({"HelloWorld":[1,3,5]})' />
-->







|







218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
<input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/timeline/ci?showFiles=true")' />
<input type='button' value='timeline/wiki' onclick='TheApp.cgi.sendCommand("/json/timeline/wiki")' />
<input type='button' value='timeline/ticket' onclick='TheApp.cgi.sendCommand("/json/timeline/ticket")' />
<input type='button' value='wiki/list' onclick='TheApp.cgi.sendCommand("/json/wiki/list")' />
<input type='button' value='wiki/get Fossil' onclick='TheApp.cgi.sendCommand("/json/wiki/get",{page:"Fossil"})' />
<input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/user/list")' />
<input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/user/get?name=anonymous")' />
<input type='button' value='artifact/XYZ' onclick='TheApp.cgi.sendCommand("/json/artifact?uuid=json")' />

<!--
<input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' />
<input type='button' value='get more' onclick='TheApp.cgi.getPages("HelloWorld/WhikiNews")' />
<input type='button' value='get client data' onclick='TheApp.cgi.getPageClientData("HelloWorld/whiki/WhikiCommands")' />
<input type='button' value='save client data' onclick='TheApp.cgi.savePageClientData({"HelloWorld":[1,3,5]})' />
-->

Changes to src/json.c.

825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854

855
856
857
858
859
860
861
....
1320
1321
1322
1323
1324
1325
1326




























1327
1328
1329
1330
1331
1332
1333
    ++len;
  }
  return rc;
}

/*
** Wrapper around json_string_split(), taking the same first 3
** parameters as this function, but returns the results as
** a JSON Array (if splitting produced tokens)
** OR a JSON null value (if splitting produced no tokens)
** OR NULL (if splitting failed in any way).
**
** The returned value is owned by the caller. If not NULL then it
** _will_ have a JSON type of Array or Null.
*/
cson_value * json_string_split2( char const * zStr,
                                 char separator,
                                 char doDeHttp ){
  cson_value * v = cson_value_new_array();
  cson_array * a = cson_value_get_array(v);
  int rc = json_string_split( zStr, separator, doDeHttp, a );
  if( 0 == rc ){
    cson_value_free(v);
    v = cson_value_null();
  }else if(rc<0){
    cson_value_free(v);
    v = NULL;
  }
  return v;
}


/*
** Performs some common initialization of JSON-related state.  Must be
** called by the json_page_top() and json_cmd_top() dispatching
** functions to set up the JSON stat used by the dispatched functions.
**
** Implicitly sets up the login information state in CGI mode, but
................................................................................
    cson_array_append(a, row);
  }
  if(warnMsg){
    json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, warnMsg );
  }
  return v;  
}





























/*
** /json/version implementation.
**
** Returns the payload object (owned by the caller).
*/
cson_value * json_page_version(){







|
|
|
<












|






>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







825
826
827
828
829
830
831
832
833
834

835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
....
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
    ++len;
  }
  return rc;
}

/*
** Wrapper around json_string_split(), taking the same first 3
** parameters as this function, but returns the results as a JSON
** Array (if splitting produced tokens) or NULL (if splitting failed
** in any way or produced no tokens).

**
** The returned value is owned by the caller. If not NULL then it
** _will_ have a JSON type of Array or Null.
*/
cson_value * json_string_split2( char const * zStr,
                                 char separator,
                                 char doDeHttp ){
  cson_value * v = cson_value_new_array();
  cson_array * a = cson_value_get_array(v);
  int rc = json_string_split( zStr, separator, doDeHttp, a );
  if( 0 == rc ){
    cson_value_free(v);
    v = NULL;
  }else if(rc<0){
    cson_value_free(v);
    v = NULL;
  }
  return v;
}


/*
** Performs some common initialization of JSON-related state.  Must be
** called by the json_page_top() and json_cmd_top() dispatching
** functions to set up the JSON stat used by the dispatched functions.
**
** Implicitly sets up the login information state in CGI mode, but
................................................................................
    cson_array_append(a, row);
  }
  if(warnMsg){
    json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, warnMsg );
  }
  return v;  
}


/*
** If the given rid has any tags associated with it, this function
** returns a JSON Array containing the tag names, else it returns
** NULL.
*/
cson_value * json_tags_for_rid(int rid){
  cson_value * v = NULL;
  Stmt q;
  assert((rid>0) && "rid is invalid");
  db_prepare(&q,"SELECT group_concat(substr(tagname,5), ',')"
             " FROM tag t, tagxref x "
             " WHERE x.rid=%d "
             " AND tagname GLOB 'sym-*' "
             " AND t.tagid=x.tagid AND x.tagtype>0",
             rid);
  if( (SQLITE_ROW==db_step(&q)) ){
    char const * tags;
    tags = db_column_text(&q,0);
    if(tags && *tags){
      v = json_string_split2(tags,',',0);
    }
  }
  db_finalize(&q);
  return v;  
}


/*
** /json/version implementation.
**
** Returns the payload object (owned by the caller).
*/
cson_value * json_page_version(){

Changes to src/json_artifact.c.

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

46
47
48
49
50
51
52
53
54
55
..
57
58
59
60
61
62
63




64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
..
97
98
99
100
101
102
103

104
105
106






107
108
109
110
111
112
113
114
115
116







117
118
119
120
121
122
123
} ArtifactDispatchEntry;


/*
** Generates an artifact Object for the given rid/zUuid. rid
** must refer to a Checkin.
**
**
** TODO: consolidate the result structure (and its generation) with
** /json/timeline/ci.
*/
static cson_value * json_artifact_ci( int rid, char const * zUuid ){
  cson_value * v = cson_value_new_object();
  cson_object * o = cson_value_get_object(v);
  char const * zParent = NULL;

  Stmt q;
  assert( NULL != zUuid );
  cson_object_set(o,"isLeaf", cson_value_new_bool(is_a_leaf(rid)));
  zParent = db_text(0,
    "SELECT uuid FROM plink, blob"
    " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
    rid
  );

  db_prepare(&q, 
................................................................................
             "       omtime"
             "  FROM blob, event"
             " WHERE blob.rid=%d"
             "   AND event.objid=%d",
             rid, rid
             );
  if( db_step(&q)==SQLITE_ROW ){




    /*const char *zUuid = db_column_text(&q, 0);*/
    char * zTmp;
    const char *zUser;
    const char *zComment;
    char * zEUser, * zEComment;
    int mtime, omtime;
    cson_value * fileList = NULL;
#define SET(K,V) cson_object_set(o,(K), (V))

    SET("uuid",json_new_string(zUuid));
    zUser = db_column_text(&q,2);
    SET("user",json_new_string(zUser));
    zEUser = db_text(0,
                   "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
                   TAG_USER, rid);
    if(zEUser){
................................................................................
      SET("omtime",json_new_int(omtime));
    }

    if(zParent){
      SET("parentUuid", json_new_string(zParent));
    }


    fileList = json_timeline_get_changed_files(rid);
    if(fileList){
      SET("files",fileList);






    }

#undef SET
  }else{
    cson_value_free(v);
    v = NULL;
  }
  db_finalize(&q);
  return v;
}








static char perms_can_read(){
  return g.perm.Read ? 1 : 0;
}

static ArtifactDispatchEntry ArtifactDispatchList[] = {
{"checkin", json_artifact_ci, perms_can_read},







|
<
<

|
<
<

>


<







 







>
>
>
>






<

>







 







>
|
|
|
>
>
>
>
>
>



<
<
<




>
>
>
>
>
>
>







31
32
33
34
35
36
37
38


39
40


41
42
43
44

45
46
47
48
49
50
51
..
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
77
78
..
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
} ArtifactDispatchEntry;


/*
** Generates an artifact Object for the given rid/zUuid. rid
** must refer to a Checkin.
**
** Returned value is NULL or an Object owned by the caller.


*/
cson_value * json_artifact_for_ci( int rid, char const * zUuid, char showFiles ){


  char const * zParent = NULL;
  cson_value * v = NULL;
  Stmt q;
  assert( NULL != zUuid );

  zParent = db_text(0,
    "SELECT uuid FROM plink, blob"
    " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
    rid
  );

  db_prepare(&q, 
................................................................................
             "       omtime"
             "  FROM blob, event"
             " WHERE blob.rid=%d"
             "   AND event.objid=%d",
             rid, rid
             );
  if( db_step(&q)==SQLITE_ROW ){
    cson_object * o;
    cson_value * tmpV = NULL;
    v = cson_value_new_object();
    o = cson_value_get_object(v);
    /*const char *zUuid = db_column_text(&q, 0);*/
    char * zTmp;
    const char *zUser;
    const char *zComment;
    char * zEUser, * zEComment;
    int mtime, omtime;

#define SET(K,V) cson_object_set(o,(K), (V))
    SET("isLeaf", cson_value_new_bool(is_a_leaf(rid)));
    SET("uuid",json_new_string(zUuid));
    zUser = db_column_text(&q,2);
    SET("user",json_new_string(zUser));
    zEUser = db_text(0,
                   "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
                   TAG_USER, rid);
    if(zEUser){
................................................................................
      SET("omtime",json_new_int(omtime));
    }

    if(zParent){
      SET("parentUuid", json_new_string(zParent));
    }

    if( showFiles ){
      cson_value * fileList = json_timeline_get_changed_files(rid);
      if(fileList){
        SET("files",fileList);
      }
    }

    tmpV = json_tags_for_rid(rid);
    if(tmpV){
      SET("tags",tmpV);
    }

#undef SET



  }
  db_finalize(&q);
  return v;
}

/*
** Sub-impl of /json/artifact for checkins.
*/
static cson_value * json_artifact_ci( int rid, char const * zUuid ){
  return json_artifact_for_ci(rid, zUuid, 1);
}

static char perms_can_read(){
  return g.perm.Read ? 1 : 0;
}

static ArtifactDispatchEntry ArtifactDispatchList[] = {
{"checkin", json_artifact_ci, perms_can_read},

Changes to src/json_timeline.c.

318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  tmp = listV;
  SET("timeline");
  while( (SQLITE_ROW == db_step(&q) )){
    /* convert each row into a JSON object...*/
    int const rid = db_column_int(&q,0);
    cson_value * rowV = cson_sqlite3_row_to_object(q.pStmt);
    cson_object * row = cson_value_get_object(rowV);
    cson_string const * tagsStr = NULL;
    if(!row && !warnRowToJsonFailed){
      warnRowToJsonFailed = 1;
      json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
                 "Could not convert at least one timeline result row to JSON." );
      continue;
    }
    /* Split tags string field into JSON Array... */
    cson_array_append(list, rowV);
    tagsStr = cson_value_get_string(cson_object_get(row,"tags"));
    if(tagsStr){
      cson_value * tags = json_string_split2( cson_string_cstr(tagsStr),
                                              ',', 0);
      if( tags ){
        if(0 != cson_object_set(row,"tags",tags)){
          cson_value_free(tags);
        }else{
          /*replaced/deleted old tags value, invalidating tagsStr*/;
          tagsStr = NULL;
        }
      }else if(!warnStringToArrayFailed){
        warnStringToArrayFailed = 1;
        json_warn(FSL_JSON_W_STRING_TO_ARRAY_FAILED,
                  "Could not convert tags string to array.");
      }
    }

    /* replace isLeaf int w/ JSON bool */
    tmp = cson_object_get(row,"isLeaf");
    if(tmp && cson_value_is_integer(tmp)){
      cson_object_set(row,"isLeaf",
                      cson_value_get_integer(tmp)







|








|
|
<
<
<
|
<
<
<
<
<
<
<
<
<
<







318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335



336










337
338
339
340
341
342
343
  tmp = listV;
  SET("timeline");
  while( (SQLITE_ROW == db_step(&q) )){
    /* convert each row into a JSON object...*/
    int const rid = db_column_int(&q,0);
    cson_value * rowV = cson_sqlite3_row_to_object(q.pStmt);
    cson_object * row = cson_value_get_object(rowV);
    cson_value * tagList = NULL;
    if(!row && !warnRowToJsonFailed){
      warnRowToJsonFailed = 1;
      json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
                 "Could not convert at least one timeline result row to JSON." );
      continue;
    }
    /* Split tags string field into JSON Array... */
    cson_array_append(list, rowV);
    tmp = json_tags_for_rid(rid);
    if(tmp){



        cson_object_set(row,"tags",tmp);










    }

    /* replace isLeaf int w/ JSON bool */
    tmp = cson_object_get(row,"isLeaf");
    if(tmp && cson_value_is_integer(tmp)){
      cson_object_set(row,"isLeaf",
                      cson_value_get_integer(tmp)