Fossil SCM

Merge the "ticket" command into the trunk.

drh 2010-10-07 19:17 trunk merge
Commit 2f5d45eca39bf16125504255b79615aa2746c592
+165 -1
--- src/report.c
+++ src/report.c
@@ -145,11 +145,11 @@
145145
** This is the SQLite authorizer callback used to make sure that the
146146
** SQL statements entered by users do not try to do anything untoward.
147147
** If anything suspicious is tried, set *(char**)pError to an error
148148
** message obtained from malloc.
149149
*/
150
-static int report_query_authorizer(
150
+int report_query_authorizer(
151151
void *pError,
152152
int code,
153153
const char *zArg1,
154154
const char *zArg2,
155155
const char *zArg3,
@@ -942,5 +942,169 @@
942942
sqlite3_exec(g.db, zSql, output_tab_separated, &count, &zErr2);
943943
sqlite3_set_authorizer(g.db, 0, 0);
944944
cgi_set_content_type("text/plain");
945945
}
946946
}
947
+
948
+/*
949
+** report number for full table ticket export
950
+*/
951
+static const char zFullTicketRptRn[] = "0";
952
+
953
+/*
954
+** report title for full table ticket export
955
+*/
956
+static const char zFullTicketRptTitle[] = "full ticket export";
957
+
958
+/*
959
+** show all reports, which can be used for ticket show.
960
+** Output is written to stdout as tab delimited table
961
+*/
962
+void rpt_list_reports(void){
963
+ Stmt q;
964
+ char const aRptOutFrmt[] = "%s\t%s\n";
965
+
966
+ printf("Available reports:\n");
967
+ printf(aRptOutFrmt,"report number","report title");
968
+ printf(aRptOutFrmt,zFullTicketRptRn,zFullTicketRptTitle);
969
+ db_prepare(&q,"SELECT rn,title FROM reportfmt ORDER BY rn");
970
+ while( db_step(&q)==SQLITE_ROW ){
971
+ const char *zRn = db_column_text(&q, 0);
972
+ const char *zTitle = db_column_text(&q, 1);
973
+
974
+ printf(aRptOutFrmt,zRn,zTitle);
975
+ }
976
+ db_finalize(&q);
977
+}
978
+
979
+/*
980
+** user defined separator used by ticket show command
981
+*/
982
+static const char *zSep = 0;
983
+
984
+/*
985
+** select the quoting algorithm for "ticket show"
986
+*/
987
+#if INTERFACE
988
+typedef enum eTktShowEnc { tktNoTab=0, tktFossilize=1 } tTktShowEncoding;
989
+#endif
990
+static tTktShowEncoding tktEncode = tktNoTab;
991
+
992
+/*
993
+** Output the text given in the argument. Convert tabs and newlines into
994
+** spaces.
995
+*/
996
+static void output_no_tabs_file(const char *z){
997
+ switch( tktEncode ){
998
+ case tktFossilize:
999
+ { char *zFosZ;
1000
+
1001
+ if( z && *z ){
1002
+ zFosZ = fossilize(z,-1);
1003
+ printf("%s",zFosZ);
1004
+ free(zFosZ);
1005
+ }
1006
+ break;
1007
+ }
1008
+ default:
1009
+ while( z && z[0] ){
1010
+ int i, j;
1011
+ for(i=0; z[i] && (!isspace(z[i]) || z[i]==' '); i++){}
1012
+ if( i>0 ){
1013
+ printf("%.*s", i, z);
1014
+ }
1015
+ for(j=i; isspace(z[j]); j++){}
1016
+ if( j>i ){
1017
+ printf("%*s", j-i, "");
1018
+ }
1019
+ z += j;
1020
+ }
1021
+ break;
1022
+ }
1023
+}
1024
+
1025
+/*
1026
+** Output a row as a tab-separated line of text.
1027
+*/
1028
+int output_separated_file(
1029
+ void *pUser, /* Pointer to row-count integer */
1030
+ int nArg, /* Number of columns in this result row */
1031
+ char **azArg, /* Text of data in all columns */
1032
+ char **azName /* Names of the columns */
1033
+){
1034
+ int *pCount = (int*)pUser;
1035
+ int i;
1036
+
1037
+ if( *pCount==0 ){
1038
+ for(i=0; i<nArg; i++){
1039
+ output_no_tabs_file(azName[i]);
1040
+ printf("%s", i<nArg-1 ? (zSep?zSep:"\t") : "\n");
1041
+ }
1042
+ }
1043
+ ++*pCount;
1044
+ for(i=0; i<nArg; i++){
1045
+ output_no_tabs_file(azArg[i]);
1046
+ printf("%s", i<nArg-1 ? (zSep?zSep:"\t") : "\n");
1047
+ }
1048
+ return 0;
1049
+}
1050
+
1051
+/*
1052
+** Generate a report. The rn query parameter is the report number.
1053
+** The output is written to stdout as flat file. The zFilter paramater
1054
+** is a full WHERE-condition.
1055
+*/
1056
+void rptshow(
1057
+ const char *zRep,
1058
+ const char *zSepIn,
1059
+ const char *zFilter,
1060
+ tTktShowEncoding enc
1061
+){
1062
+ Stmt q;
1063
+ char *zSql;
1064
+ const char *zTitle;
1065
+ const char *zOwner;
1066
+ const char *zClrKey;
1067
+ char *zErr1 = 0;
1068
+ char *zErr2 = 0;
1069
+ int count = 0;
1070
+ int rn;
1071
+
1072
+ if (!zRep || !strcmp(zRep,zFullTicketRptRn) || !strcmp(zRep,zFullTicketRptTitle) ){
1073
+ zTitle = zFullTicketRptTitle;
1074
+ zSql = "SELECT * FROM ticket";
1075
+ zOwner = g.zLogin;
1076
+ zClrKey = "";
1077
+ }else{
1078
+ rn = atoi(zRep);
1079
+ if( rn ){
1080
+ db_prepare(&q,
1081
+ "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE rn=%d", rn);
1082
+ }else{
1083
+ db_prepare(&q,
1084
+ "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE title='%s'", zRep);
1085
+ }
1086
+ if( db_step(&q)!=SQLITE_ROW ){
1087
+ db_finalize(&q);
1088
+ rpt_list_reports();
1089
+ fossil_fatal("unkown report format(%s)!",zRep);
1090
+ }
1091
+ zTitle = db_column_malloc(&q, 0);
1092
+ zSql = db_column_malloc(&q, 1);
1093
+ zOwner = db_column_malloc(&q, 2);
1094
+ zClrKey = db_column_malloc(&q, 3);
1095
+ db_finalize(&q);
1096
+ }
1097
+ if( zFilter ){
1098
+ zSql = mprintf("SELECT * FROM (%s) WHERE %s",zSql,zFilter);
1099
+ }
1100
+ count = 0;
1101
+ tktEncode = enc;
1102
+ zSep = zSepIn;
1103
+ sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1);
1104
+ sqlite3_exec(g.db, zSql, output_separated_file, &count, &zErr2);
1105
+ sqlite3_set_authorizer(g.db, 0, 0);
1106
+ if( zFilter ){
1107
+ free(zSql);
1108
+ }
1109
+}
1110
+
9471111
--- src/report.c
+++ src/report.c
@@ -145,11 +145,11 @@
145 ** This is the SQLite authorizer callback used to make sure that the
146 ** SQL statements entered by users do not try to do anything untoward.
147 ** If anything suspicious is tried, set *(char**)pError to an error
148 ** message obtained from malloc.
149 */
150 static int report_query_authorizer(
151 void *pError,
152 int code,
153 const char *zArg1,
154 const char *zArg2,
155 const char *zArg3,
@@ -942,5 +942,169 @@
942 sqlite3_exec(g.db, zSql, output_tab_separated, &count, &zErr2);
943 sqlite3_set_authorizer(g.db, 0, 0);
944 cgi_set_content_type("text/plain");
945 }
946 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
947
--- src/report.c
+++ src/report.c
@@ -145,11 +145,11 @@
145 ** This is the SQLite authorizer callback used to make sure that the
146 ** SQL statements entered by users do not try to do anything untoward.
147 ** If anything suspicious is tried, set *(char**)pError to an error
148 ** message obtained from malloc.
149 */
150 int report_query_authorizer(
151 void *pError,
152 int code,
153 const char *zArg1,
154 const char *zArg2,
155 const char *zArg3,
@@ -942,5 +942,169 @@
942 sqlite3_exec(g.db, zSql, output_tab_separated, &count, &zErr2);
943 sqlite3_set_authorizer(g.db, 0, 0);
944 cgi_set_content_type("text/plain");
945 }
946 }
947
948 /*
949 ** report number for full table ticket export
950 */
951 static const char zFullTicketRptRn[] = "0";
952
953 /*
954 ** report title for full table ticket export
955 */
956 static const char zFullTicketRptTitle[] = "full ticket export";
957
958 /*
959 ** show all reports, which can be used for ticket show.
960 ** Output is written to stdout as tab delimited table
961 */
962 void rpt_list_reports(void){
963 Stmt q;
964 char const aRptOutFrmt[] = "%s\t%s\n";
965
966 printf("Available reports:\n");
967 printf(aRptOutFrmt,"report number","report title");
968 printf(aRptOutFrmt,zFullTicketRptRn,zFullTicketRptTitle);
969 db_prepare(&q,"SELECT rn,title FROM reportfmt ORDER BY rn");
970 while( db_step(&q)==SQLITE_ROW ){
971 const char *zRn = db_column_text(&q, 0);
972 const char *zTitle = db_column_text(&q, 1);
973
974 printf(aRptOutFrmt,zRn,zTitle);
975 }
976 db_finalize(&q);
977 }
978
979 /*
980 ** user defined separator used by ticket show command
981 */
982 static const char *zSep = 0;
983
984 /*
985 ** select the quoting algorithm for "ticket show"
986 */
987 #if INTERFACE
988 typedef enum eTktShowEnc { tktNoTab=0, tktFossilize=1 } tTktShowEncoding;
989 #endif
990 static tTktShowEncoding tktEncode = tktNoTab;
991
992 /*
993 ** Output the text given in the argument. Convert tabs and newlines into
994 ** spaces.
995 */
996 static void output_no_tabs_file(const char *z){
997 switch( tktEncode ){
998 case tktFossilize:
999 { char *zFosZ;
1000
1001 if( z && *z ){
1002 zFosZ = fossilize(z,-1);
1003 printf("%s",zFosZ);
1004 free(zFosZ);
1005 }
1006 break;
1007 }
1008 default:
1009 while( z && z[0] ){
1010 int i, j;
1011 for(i=0; z[i] && (!isspace(z[i]) || z[i]==' '); i++){}
1012 if( i>0 ){
1013 printf("%.*s", i, z);
1014 }
1015 for(j=i; isspace(z[j]); j++){}
1016 if( j>i ){
1017 printf("%*s", j-i, "");
1018 }
1019 z += j;
1020 }
1021 break;
1022 }
1023 }
1024
1025 /*
1026 ** Output a row as a tab-separated line of text.
1027 */
1028 int output_separated_file(
1029 void *pUser, /* Pointer to row-count integer */
1030 int nArg, /* Number of columns in this result row */
1031 char **azArg, /* Text of data in all columns */
1032 char **azName /* Names of the columns */
1033 ){
1034 int *pCount = (int*)pUser;
1035 int i;
1036
1037 if( *pCount==0 ){
1038 for(i=0; i<nArg; i++){
1039 output_no_tabs_file(azName[i]);
1040 printf("%s", i<nArg-1 ? (zSep?zSep:"\t") : "\n");
1041 }
1042 }
1043 ++*pCount;
1044 for(i=0; i<nArg; i++){
1045 output_no_tabs_file(azArg[i]);
1046 printf("%s", i<nArg-1 ? (zSep?zSep:"\t") : "\n");
1047 }
1048 return 0;
1049 }
1050
1051 /*
1052 ** Generate a report. The rn query parameter is the report number.
1053 ** The output is written to stdout as flat file. The zFilter paramater
1054 ** is a full WHERE-condition.
1055 */
1056 void rptshow(
1057 const char *zRep,
1058 const char *zSepIn,
1059 const char *zFilter,
1060 tTktShowEncoding enc
1061 ){
1062 Stmt q;
1063 char *zSql;
1064 const char *zTitle;
1065 const char *zOwner;
1066 const char *zClrKey;
1067 char *zErr1 = 0;
1068 char *zErr2 = 0;
1069 int count = 0;
1070 int rn;
1071
1072 if (!zRep || !strcmp(zRep,zFullTicketRptRn) || !strcmp(zRep,zFullTicketRptTitle) ){
1073 zTitle = zFullTicketRptTitle;
1074 zSql = "SELECT * FROM ticket";
1075 zOwner = g.zLogin;
1076 zClrKey = "";
1077 }else{
1078 rn = atoi(zRep);
1079 if( rn ){
1080 db_prepare(&q,
1081 "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE rn=%d", rn);
1082 }else{
1083 db_prepare(&q,
1084 "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE title='%s'", zRep);
1085 }
1086 if( db_step(&q)!=SQLITE_ROW ){
1087 db_finalize(&q);
1088 rpt_list_reports();
1089 fossil_fatal("unkown report format(%s)!",zRep);
1090 }
1091 zTitle = db_column_malloc(&q, 0);
1092 zSql = db_column_malloc(&q, 1);
1093 zOwner = db_column_malloc(&q, 2);
1094 zClrKey = db_column_malloc(&q, 3);
1095 db_finalize(&q);
1096 }
1097 if( zFilter ){
1098 zSql = mprintf("SELECT * FROM (%s) WHERE %s",zSql,zFilter);
1099 }
1100 count = 0;
1101 tktEncode = enc;
1102 zSep = zSepIn;
1103 sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1);
1104 sqlite3_exec(g.db, zSql, output_separated_file, &count, &zErr2);
1105 sqlite3_set_authorizer(g.db, 0, 0);
1106 if( zFilter ){
1107 free(zSql);
1108 }
1109 }
1110
1111
+1 -1
--- src/tag.c
+++ src/tag.c
@@ -355,11 +355,11 @@
355355
** fossil update tag:decaf
356356
**
357357
** will assume that "decaf" is a tag/branch name.
358358
**
359359
** only allow --date-override and --user-override in
360
-** %fossil tag add --date-override 'YYYY-MMM-DD HH:MM:SS' \
360
+** %fossil tag add --date-override 'YYYY-MMM-DD HH:MM:SS' \\
361361
** --user-override user
362362
** in order to import history from other scm systems
363363
*/
364364
void tag_cmd(void){
365365
int n;
366366
--- src/tag.c
+++ src/tag.c
@@ -355,11 +355,11 @@
355 ** fossil update tag:decaf
356 **
357 ** will assume that "decaf" is a tag/branch name.
358 **
359 ** only allow --date-override and --user-override in
360 ** %fossil tag add --date-override 'YYYY-MMM-DD HH:MM:SS' \
361 ** --user-override user
362 ** in order to import history from other scm systems
363 */
364 void tag_cmd(void){
365 int n;
366
--- src/tag.c
+++ src/tag.c
@@ -355,11 +355,11 @@
355 ** fossil update tag:decaf
356 **
357 ** will assume that "decaf" is a tag/branch name.
358 **
359 ** only allow --date-override and --user-override in
360 ** %fossil tag add --date-override 'YYYY-MMM-DD HH:MM:SS' \\
361 ** --user-override user
362 ** in order to import history from other scm systems
363 */
364 void tag_cmd(void){
365 int n;
366
+234
--- src/tkt.c
+++ src/tkt.c
@@ -830,5 +830,239 @@
830830
}
831831
blob_reset(&val);
832832
}
833833
@ </ol>
834834
}
835
+
836
+/*
837
+** COMMAND: ticket
838
+** Usage: %fossil ticket SUBCOMMAND ...
839
+**
840
+** Run various subcommands to control tickets
841
+**
842
+** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options?
843
+**
844
+** options can be:
845
+** ?-l|--limit LIMITCHAR?
846
+** ?-q|--quote?
847
+**
848
+** Run the ticket report, identified by the report format title
849
+** used in the gui. The data is written as flat file on stdout,
850
+** using "," as separator. The seperator "," can be changed using
851
+** the -l or --limit option.
852
+** If TICKETFILTER is given on the commandline, the query is
853
+** limited with a new WHERE-condition.
854
+** example: Report lists a column # with the uuid
855
+** TICKETFILTER may be [#]='uuuuuuuuu'
856
+** example: Report only lists rows with status not open
857
+** TICKETFILTER: status != 'open'
858
+** If the option -q|--quote is used, the tickets are encoded by
859
+** quoting special chars(space -> \\s, tab -> \\t, newline -> \\n,
860
+** cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\).
861
+** Otherwise, the simplified encoding as on the show report raw
862
+** page in the gui is used.
863
+**
864
+** Instead of the report title its possible to use the report
865
+** number. Using the special report number 0 list all columns,
866
+** defined in the ticket table.
867
+**
868
+** %fossil ticket list fields
869
+**
870
+** list all fields, defined for ticket in the fossil repository
871
+**
872
+** %fossil ticket list reports
873
+**
874
+** list all ticket reports, defined in the fossil repository
875
+**
876
+** %fossil ticket set TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
877
+** %fossil ticket change TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
878
+**
879
+** change ticket identified by TICKETUUID and set the value of
880
+** field FIELD to VALUE. Valid field descriptions are:
881
+** status, type, severity, priority, resolution,
882
+** foundin, private_contact, resolution, title or comment
883
+** Field names given above are the ones, defined in a standard
884
+** fossil environment. If you have added, deleted columns, you
885
+** change the all your configured columns.
886
+** You can use more than one field/value pair on the commandline.
887
+** Using -q|--quote enables the special character decoding as
888
+** in "ticket show". So it's possible, to set multiline text or
889
+** text with special characters.
890
+**
891
+** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
892
+**
893
+** like set, but create a new ticket with the given values.
894
+**
895
+** The values in set|add are not validated against the definitions
896
+** given in "Ticket Common Script".
897
+*/
898
+void ticket_cmd(void){
899
+ int n;
900
+
901
+ /* do some ints, we want to be inside a checkout */
902
+ db_must_be_within_tree();
903
+ db_find_and_open_repository(1);
904
+ user_select();
905
+ /*
906
+ ** Check that the user exists.
907
+ */
908
+ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
909
+ fossil_fatal("no such user: %s", g.zLogin);
910
+ }
911
+
912
+ if( g.argc<3 ){
913
+ usage("add|fieldlist|set|show");
914
+ }else{
915
+ n = strlen(g.argv[2]);
916
+ if( n==1 && g.argv[2][0]=='s' ){
917
+ /* set/show cannot be distinguished, so show the usage */
918
+ usage("add|fieldlist|set|show");
919
+ }else if( strncmp(g.argv[2],"list",n)==0 ){
920
+ if( g.argc==3 ){
921
+ usage("list fields|reports");
922
+ }else{
923
+ n = strlen(g.argv[3]);
924
+ if( !strncmp(g.argv[3],"fields",n) ){
925
+ /* simply show all field names */
926
+ int i;
927
+
928
+ /* read all available ticket fields */
929
+ getAllTicketFields();
930
+ for(i=0; i<nField; i++){
931
+ printf("%s\n",azField[i]);
932
+ }
933
+ }else if( !strncmp(g.argv[3],"reports",n) ){
934
+ rpt_list_reports();
935
+ }else{
936
+ fossil_fatal("unknown ticket list option '%s'!",g.argv[3]);
937
+ }
938
+ }
939
+ }else{
940
+ /* add a new ticket or set fields on existing tickets */
941
+ tTktShowEncoding tktEncoding;
942
+
943
+ tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab;
944
+
945
+ if( strncmp(g.argv[2],"show",n)==0 ){
946
+ if( g.argc==3 ){
947
+ usage("show REPORTNR");
948
+ }else{
949
+ const char *zRep = 0;
950
+ const char *zSep = 0;
951
+ const char *zFilterUuid = 0;
952
+
953
+ zSep = find_option("limit","l",1);
954
+ zRep = g.argv[3];
955
+ if( !strcmp(zRep,"0") ){
956
+ zRep = 0;
957
+ }
958
+ if( g.argc>4 ){
959
+ zFilterUuid = g.argv[4];
960
+ }
961
+
962
+ rptshow( zRep, zSep, zFilterUuid, tktEncoding );
963
+
964
+ }
965
+ }else{
966
+ /* add a new ticket or update an existing ticket */
967
+ enum { set,add,err } eCmd = err;
968
+ int i = 0;
969
+ int rid;
970
+ const char *zTktUuid = 0;
971
+ Blob tktchng, cksum;
972
+
973
+ /* get command type (set/add) and get uuid, if needed for set */
974
+ if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ){
975
+ eCmd = set;
976
+ if( g.argc==3 ){
977
+ usage("set TICKETUUID");
978
+ }
979
+ zTktUuid = db_text(0,
980
+ "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
981
+ );
982
+ if( !zTktUuid ){
983
+ fossil_fatal("unknown ticket: '%s'!",g.argv[3]);
984
+ }
985
+ i=4;
986
+ }else if( strncmp(g.argv[2],"add",n)==0 ){
987
+ eCmd = add;
988
+ i = 3;
989
+ zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))");
990
+ }
991
+ /* none of set/add, so show the usage! */
992
+ if( eCmd==err ){
993
+ usage("add|fieldlist|set|show");
994
+ }
995
+
996
+ /* read all given ticket field/value pairs from command line */
997
+ if( i==g.argc ){
998
+ fossil_fatal("empty %s command aborted!",g.argv[2]);
999
+ }
1000
+ getAllTicketFields();
1001
+ /* read commandline and assign fields in the azValue array */
1002
+ while( i<g.argc ){
1003
+ char *zFName;
1004
+ char *zFValue;
1005
+ int j;
1006
+
1007
+ zFName = g.argv[i++];
1008
+ if( i==g.argc ){
1009
+ fossil_fatal("missing value for '%s'!",zFName);
1010
+ }
1011
+ zFValue = g.argv[i++];
1012
+ j = fieldId(zFName);
1013
+ if( tktEncoding == tktFossilize ){
1014
+ zFValue=mprintf("%s",zFValue);
1015
+ defossilize(zFValue);
1016
+ }
1017
+ if( j == -1 ){
1018
+ fossil_fatal("unknown field name '%s'!",zFName);
1019
+ }else{
1020
+ azValue[j] = zFValue;
1021
+ }
1022
+ }
1023
+
1024
+ /* now add the needed artifacts to the repository */
1025
+ blob_zero(&tktchng);
1026
+ { /* add the time to the ticket manifest */
1027
+ char *zDate;
1028
+
1029
+ zDate = db_text(0, "SELECT datetime('now')");
1030
+ zDate[10] = 'T';
1031
+ blob_appendf(&tktchng, "D %s\n", zDate);
1032
+ free(zDate);
1033
+ }
1034
+ /* append defined elements */
1035
+ for(i=0; i<nField; i++){
1036
+ char *zValue;
1037
+
1038
+ zValue = azValue[i];
1039
+ if( azValue[i] && azValue[i][0] ){
1040
+ if( strncmp(azField[i], "private_", 8)==0 ){
1041
+ zValue = db_conceal(zValue, strlen(zValue));
1042
+ blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue);
1043
+ }else{
1044
+ blob_appendf(&tktchng, "J %s %#F\n",
1045
+ azField[i], strlen(zValue), zValue);
1046
+ }
1047
+ if( tktEncoding == tktFossilize ){
1048
+ free(azValue[i]);
1049
+ }
1050
+ }
1051
+ }
1052
+ blob_appendf(&tktchng, "K %s\n", zTktUuid);
1053
+ blob_appendf(&tktchng, "U %F\n", g.zLogin);
1054
+ md5sum_blob(&tktchng, &cksum);
1055
+ blob_appendf(&tktchng, "Z %b\n", &cksum);
1056
+ rid = content_put(&tktchng, 0, 0);
1057
+ if( rid==0 ){
1058
+ fossil_panic("trouble committing ticket: %s", g.zErrMsg);
1059
+ }
1060
+ manifest_crosslink_begin();
1061
+ manifest_crosslink(rid, &tktchng);
1062
+ manifest_crosslink_end();
1063
+ printf("ticket %s succeeded for UID %s\n",
1064
+ (eCmd==set?"set":"add"),zTktUuid);
1065
+ }
1066
+ }
1067
+ }
1068
+}
8351069
--- src/tkt.c
+++ src/tkt.c
@@ -830,5 +830,239 @@
830 }
831 blob_reset(&val);
832 }
833 @ </ol>
834 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
835
--- src/tkt.c
+++ src/tkt.c
@@ -830,5 +830,239 @@
830 }
831 blob_reset(&val);
832 }
833 @ </ol>
834 }
835
836 /*
837 ** COMMAND: ticket
838 ** Usage: %fossil ticket SUBCOMMAND ...
839 **
840 ** Run various subcommands to control tickets
841 **
842 ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options?
843 **
844 ** options can be:
845 ** ?-l|--limit LIMITCHAR?
846 ** ?-q|--quote?
847 **
848 ** Run the ticket report, identified by the report format title
849 ** used in the gui. The data is written as flat file on stdout,
850 ** using "," as separator. The seperator "," can be changed using
851 ** the -l or --limit option.
852 ** If TICKETFILTER is given on the commandline, the query is
853 ** limited with a new WHERE-condition.
854 ** example: Report lists a column # with the uuid
855 ** TICKETFILTER may be [#]='uuuuuuuuu'
856 ** example: Report only lists rows with status not open
857 ** TICKETFILTER: status != 'open'
858 ** If the option -q|--quote is used, the tickets are encoded by
859 ** quoting special chars(space -> \\s, tab -> \\t, newline -> \\n,
860 ** cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\).
861 ** Otherwise, the simplified encoding as on the show report raw
862 ** page in the gui is used.
863 **
864 ** Instead of the report title its possible to use the report
865 ** number. Using the special report number 0 list all columns,
866 ** defined in the ticket table.
867 **
868 ** %fossil ticket list fields
869 **
870 ** list all fields, defined for ticket in the fossil repository
871 **
872 ** %fossil ticket list reports
873 **
874 ** list all ticket reports, defined in the fossil repository
875 **
876 ** %fossil ticket set TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
877 ** %fossil ticket change TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
878 **
879 ** change ticket identified by TICKETUUID and set the value of
880 ** field FIELD to VALUE. Valid field descriptions are:
881 ** status, type, severity, priority, resolution,
882 ** foundin, private_contact, resolution, title or comment
883 ** Field names given above are the ones, defined in a standard
884 ** fossil environment. If you have added, deleted columns, you
885 ** change the all your configured columns.
886 ** You can use more than one field/value pair on the commandline.
887 ** Using -q|--quote enables the special character decoding as
888 ** in "ticket show". So it's possible, to set multiline text or
889 ** text with special characters.
890 **
891 ** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
892 **
893 ** like set, but create a new ticket with the given values.
894 **
895 ** The values in set|add are not validated against the definitions
896 ** given in "Ticket Common Script".
897 */
898 void ticket_cmd(void){
899 int n;
900
901 /* do some ints, we want to be inside a checkout */
902 db_must_be_within_tree();
903 db_find_and_open_repository(1);
904 user_select();
905 /*
906 ** Check that the user exists.
907 */
908 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
909 fossil_fatal("no such user: %s", g.zLogin);
910 }
911
912 if( g.argc<3 ){
913 usage("add|fieldlist|set|show");
914 }else{
915 n = strlen(g.argv[2]);
916 if( n==1 && g.argv[2][0]=='s' ){
917 /* set/show cannot be distinguished, so show the usage */
918 usage("add|fieldlist|set|show");
919 }else if( strncmp(g.argv[2],"list",n)==0 ){
920 if( g.argc==3 ){
921 usage("list fields|reports");
922 }else{
923 n = strlen(g.argv[3]);
924 if( !strncmp(g.argv[3],"fields",n) ){
925 /* simply show all field names */
926 int i;
927
928 /* read all available ticket fields */
929 getAllTicketFields();
930 for(i=0; i<nField; i++){
931 printf("%s\n",azField[i]);
932 }
933 }else if( !strncmp(g.argv[3],"reports",n) ){
934 rpt_list_reports();
935 }else{
936 fossil_fatal("unknown ticket list option '%s'!",g.argv[3]);
937 }
938 }
939 }else{
940 /* add a new ticket or set fields on existing tickets */
941 tTktShowEncoding tktEncoding;
942
943 tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab;
944
945 if( strncmp(g.argv[2],"show",n)==0 ){
946 if( g.argc==3 ){
947 usage("show REPORTNR");
948 }else{
949 const char *zRep = 0;
950 const char *zSep = 0;
951 const char *zFilterUuid = 0;
952
953 zSep = find_option("limit","l",1);
954 zRep = g.argv[3];
955 if( !strcmp(zRep,"0") ){
956 zRep = 0;
957 }
958 if( g.argc>4 ){
959 zFilterUuid = g.argv[4];
960 }
961
962 rptshow( zRep, zSep, zFilterUuid, tktEncoding );
963
964 }
965 }else{
966 /* add a new ticket or update an existing ticket */
967 enum { set,add,err } eCmd = err;
968 int i = 0;
969 int rid;
970 const char *zTktUuid = 0;
971 Blob tktchng, cksum;
972
973 /* get command type (set/add) and get uuid, if needed for set */
974 if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ){
975 eCmd = set;
976 if( g.argc==3 ){
977 usage("set TICKETUUID");
978 }
979 zTktUuid = db_text(0,
980 "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
981 );
982 if( !zTktUuid ){
983 fossil_fatal("unknown ticket: '%s'!",g.argv[3]);
984 }
985 i=4;
986 }else if( strncmp(g.argv[2],"add",n)==0 ){
987 eCmd = add;
988 i = 3;
989 zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))");
990 }
991 /* none of set/add, so show the usage! */
992 if( eCmd==err ){
993 usage("add|fieldlist|set|show");
994 }
995
996 /* read all given ticket field/value pairs from command line */
997 if( i==g.argc ){
998 fossil_fatal("empty %s command aborted!",g.argv[2]);
999 }
1000 getAllTicketFields();
1001 /* read commandline and assign fields in the azValue array */
1002 while( i<g.argc ){
1003 char *zFName;
1004 char *zFValue;
1005 int j;
1006
1007 zFName = g.argv[i++];
1008 if( i==g.argc ){
1009 fossil_fatal("missing value for '%s'!",zFName);
1010 }
1011 zFValue = g.argv[i++];
1012 j = fieldId(zFName);
1013 if( tktEncoding == tktFossilize ){
1014 zFValue=mprintf("%s",zFValue);
1015 defossilize(zFValue);
1016 }
1017 if( j == -1 ){
1018 fossil_fatal("unknown field name '%s'!",zFName);
1019 }else{
1020 azValue[j] = zFValue;
1021 }
1022 }
1023
1024 /* now add the needed artifacts to the repository */
1025 blob_zero(&tktchng);
1026 { /* add the time to the ticket manifest */
1027 char *zDate;
1028
1029 zDate = db_text(0, "SELECT datetime('now')");
1030 zDate[10] = 'T';
1031 blob_appendf(&tktchng, "D %s\n", zDate);
1032 free(zDate);
1033 }
1034 /* append defined elements */
1035 for(i=0; i<nField; i++){
1036 char *zValue;
1037
1038 zValue = azValue[i];
1039 if( azValue[i] && azValue[i][0] ){
1040 if( strncmp(azField[i], "private_", 8)==0 ){
1041 zValue = db_conceal(zValue, strlen(zValue));
1042 blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue);
1043 }else{
1044 blob_appendf(&tktchng, "J %s %#F\n",
1045 azField[i], strlen(zValue), zValue);
1046 }
1047 if( tktEncoding == tktFossilize ){
1048 free(azValue[i]);
1049 }
1050 }
1051 }
1052 blob_appendf(&tktchng, "K %s\n", zTktUuid);
1053 blob_appendf(&tktchng, "U %F\n", g.zLogin);
1054 md5sum_blob(&tktchng, &cksum);
1055 blob_appendf(&tktchng, "Z %b\n", &cksum);
1056 rid = content_put(&tktchng, 0, 0);
1057 if( rid==0 ){
1058 fossil_panic("trouble committing ticket: %s", g.zErrMsg);
1059 }
1060 manifest_crosslink_begin();
1061 manifest_crosslink(rid, &tktchng);
1062 manifest_crosslink_end();
1063 printf("ticket %s succeeded for UID %s\n",
1064 (eCmd==set?"set":"add"),zTktUuid);
1065 }
1066 }
1067 }
1068 }
1069
+234
--- src/tkt.c
+++ src/tkt.c
@@ -830,5 +830,239 @@
830830
}
831831
blob_reset(&val);
832832
}
833833
@ </ol>
834834
}
835
+
836
+/*
837
+** COMMAND: ticket
838
+** Usage: %fossil ticket SUBCOMMAND ...
839
+**
840
+** Run various subcommands to control tickets
841
+**
842
+** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options?
843
+**
844
+** options can be:
845
+** ?-l|--limit LIMITCHAR?
846
+** ?-q|--quote?
847
+**
848
+** Run the ticket report, identified by the report format title
849
+** used in the gui. The data is written as flat file on stdout,
850
+** using "," as separator. The seperator "," can be changed using
851
+** the -l or --limit option.
852
+** If TICKETFILTER is given on the commandline, the query is
853
+** limited with a new WHERE-condition.
854
+** example: Report lists a column # with the uuid
855
+** TICKETFILTER may be [#]='uuuuuuuuu'
856
+** example: Report only lists rows with status not open
857
+** TICKETFILTER: status != 'open'
858
+** If the option -q|--quote is used, the tickets are encoded by
859
+** quoting special chars(space -> \\s, tab -> \\t, newline -> \\n,
860
+** cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\).
861
+** Otherwise, the simplified encoding as on the show report raw
862
+** page in the gui is used.
863
+**
864
+** Instead of the report title its possible to use the report
865
+** number. Using the special report number 0 list all columns,
866
+** defined in the ticket table.
867
+**
868
+** %fossil ticket list fields
869
+**
870
+** list all fields, defined for ticket in the fossil repository
871
+**
872
+** %fossil ticket list reports
873
+**
874
+** list all ticket reports, defined in the fossil repository
875
+**
876
+** %fossil ticket set TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
877
+** %fossil ticket change TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
878
+**
879
+** change ticket identified by TICKETUUID and set the value of
880
+** field FIELD to VALUE. Valid field descriptions are:
881
+** status, type, severity, priority, resolution,
882
+** foundin, private_contact, resolution, title or comment
883
+** Field names given above are the ones, defined in a standard
884
+** fossil environment. If you have added, deleted columns, you
885
+** change the all your configured columns.
886
+** You can use more than one field/value pair on the commandline.
887
+** Using -q|--quote enables the special character decoding as
888
+** in "ticket show". So it's possible, to set multiline text or
889
+** text with special characters.
890
+**
891
+** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
892
+**
893
+** like set, but create a new ticket with the given values.
894
+**
895
+** The values in set|add are not validated against the definitions
896
+** given in "Ticket Common Script".
897
+*/
898
+void ticket_cmd(void){
899
+ int n;
900
+
901
+ /* do some ints, we want to be inside a checkout */
902
+ db_must_be_within_tree();
903
+ db_find_and_open_repository(1);
904
+ user_select();
905
+ /*
906
+ ** Check that the user exists.
907
+ */
908
+ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
909
+ fossil_fatal("no such user: %s", g.zLogin);
910
+ }
911
+
912
+ if( g.argc<3 ){
913
+ usage("add|fieldlist|set|show");
914
+ }else{
915
+ n = strlen(g.argv[2]);
916
+ if( n==1 && g.argv[2][0]=='s' ){
917
+ /* set/show cannot be distinguished, so show the usage */
918
+ usage("add|fieldlist|set|show");
919
+ }else if( strncmp(g.argv[2],"list",n)==0 ){
920
+ if( g.argc==3 ){
921
+ usage("list fields|reports");
922
+ }else{
923
+ n = strlen(g.argv[3]);
924
+ if( !strncmp(g.argv[3],"fields",n) ){
925
+ /* simply show all field names */
926
+ int i;
927
+
928
+ /* read all available ticket fields */
929
+ getAllTicketFields();
930
+ for(i=0; i<nField; i++){
931
+ printf("%s\n",azField[i]);
932
+ }
933
+ }else if( !strncmp(g.argv[3],"reports",n) ){
934
+ rpt_list_reports();
935
+ }else{
936
+ fossil_fatal("unknown ticket list option '%s'!",g.argv[3]);
937
+ }
938
+ }
939
+ }else{
940
+ /* add a new ticket or set fields on existing tickets */
941
+ tTktShowEncoding tktEncoding;
942
+
943
+ tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab;
944
+
945
+ if( strncmp(g.argv[2],"show",n)==0 ){
946
+ if( g.argc==3 ){
947
+ usage("show REPORTNR");
948
+ }else{
949
+ const char *zRep = 0;
950
+ const char *zSep = 0;
951
+ const char *zFilterUuid = 0;
952
+
953
+ zSep = find_option("limit","l",1);
954
+ zRep = g.argv[3];
955
+ if( !strcmp(zRep,"0") ){
956
+ zRep = 0;
957
+ }
958
+ if( g.argc>4 ){
959
+ zFilterUuid = g.argv[4];
960
+ }
961
+
962
+ rptshow( zRep, zSep, zFilterUuid, tktEncoding );
963
+
964
+ }
965
+ }else{
966
+ /* add a new ticket or update an existing ticket */
967
+ enum { set,add,err } eCmd = err;
968
+ int i = 0;
969
+ int rid;
970
+ const char *zTktUuid = 0;
971
+ Blob tktchng, cksum;
972
+
973
+ /* get command type (set/add) and get uuid, if needed for set */
974
+ if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ){
975
+ eCmd = set;
976
+ if( g.argc==3 ){
977
+ usage("set TICKETUUID");
978
+ }
979
+ zTktUuid = db_text(0,
980
+ "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
981
+ );
982
+ if( !zTktUuid ){
983
+ fossil_fatal("unknown ticket: '%s'!",g.argv[3]);
984
+ }
985
+ i=4;
986
+ }else if( strncmp(g.argv[2],"add",n)==0 ){
987
+ eCmd = add;
988
+ i = 3;
989
+ zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))");
990
+ }
991
+ /* none of set/add, so show the usage! */
992
+ if( eCmd==err ){
993
+ usage("add|fieldlist|set|show");
994
+ }
995
+
996
+ /* read all given ticket field/value pairs from command line */
997
+ if( i==g.argc ){
998
+ fossil_fatal("empty %s command aborted!",g.argv[2]);
999
+ }
1000
+ getAllTicketFields();
1001
+ /* read commandline and assign fields in the azValue array */
1002
+ while( i<g.argc ){
1003
+ char *zFName;
1004
+ char *zFValue;
1005
+ int j;
1006
+
1007
+ zFName = g.argv[i++];
1008
+ if( i==g.argc ){
1009
+ fossil_fatal("missing value for '%s'!",zFName);
1010
+ }
1011
+ zFValue = g.argv[i++];
1012
+ j = fieldId(zFName);
1013
+ if( tktEncoding == tktFossilize ){
1014
+ zFValue=mprintf("%s",zFValue);
1015
+ defossilize(zFValue);
1016
+ }
1017
+ if( j == -1 ){
1018
+ fossil_fatal("unknown field name '%s'!",zFName);
1019
+ }else{
1020
+ azValue[j] = zFValue;
1021
+ }
1022
+ }
1023
+
1024
+ /* now add the needed artifacts to the repository */
1025
+ blob_zero(&tktchng);
1026
+ { /* add the time to the ticket manifest */
1027
+ char *zDate;
1028
+
1029
+ zDate = db_text(0, "SELECT datetime('now')");
1030
+ zDate[10] = 'T';
1031
+ blob_appendf(&tktchng, "D %s\n", zDate);
1032
+ free(zDate);
1033
+ }
1034
+ /* append defined elements */
1035
+ for(i=0; i<nField; i++){
1036
+ char *zValue;
1037
+
1038
+ zValue = azValue[i];
1039
+ if( azValue[i] && azValue[i][0] ){
1040
+ if( strncmp(azField[i], "private_", 8)==0 ){
1041
+ zValue = db_conceal(zValue, strlen(zValue));
1042
+ blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue);
1043
+ }else{
1044
+ blob_appendf(&tktchng, "J %s %#F\n",
1045
+ azField[i], strlen(zValue), zValue);
1046
+ }
1047
+ if( tktEncoding == tktFossilize ){
1048
+ free(azValue[i]);
1049
+ }
1050
+ }
1051
+ }
1052
+ blob_appendf(&tktchng, "K %s\n", zTktUuid);
1053
+ blob_appendf(&tktchng, "U %F\n", g.zLogin);
1054
+ md5sum_blob(&tktchng, &cksum);
1055
+ blob_appendf(&tktchng, "Z %b\n", &cksum);
1056
+ rid = content_put(&tktchng, 0, 0);
1057
+ if( rid==0 ){
1058
+ fossil_panic("trouble committing ticket: %s", g.zErrMsg);
1059
+ }
1060
+ manifest_crosslink_begin();
1061
+ manifest_crosslink(rid, &tktchng);
1062
+ manifest_crosslink_end();
1063
+ printf("ticket %s succeeded for UID %s\n",
1064
+ (eCmd==set?"set":"add"),zTktUuid);
1065
+ }
1066
+ }
1067
+ }
1068
+}
8351069
--- src/tkt.c
+++ src/tkt.c
@@ -830,5 +830,239 @@
830 }
831 blob_reset(&val);
832 }
833 @ </ol>
834 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
835
--- src/tkt.c
+++ src/tkt.c
@@ -830,5 +830,239 @@
830 }
831 blob_reset(&val);
832 }
833 @ </ol>
834 }
835
836 /*
837 ** COMMAND: ticket
838 ** Usage: %fossil ticket SUBCOMMAND ...
839 **
840 ** Run various subcommands to control tickets
841 **
842 ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options?
843 **
844 ** options can be:
845 ** ?-l|--limit LIMITCHAR?
846 ** ?-q|--quote?
847 **
848 ** Run the ticket report, identified by the report format title
849 ** used in the gui. The data is written as flat file on stdout,
850 ** using "," as separator. The seperator "," can be changed using
851 ** the -l or --limit option.
852 ** If TICKETFILTER is given on the commandline, the query is
853 ** limited with a new WHERE-condition.
854 ** example: Report lists a column # with the uuid
855 ** TICKETFILTER may be [#]='uuuuuuuuu'
856 ** example: Report only lists rows with status not open
857 ** TICKETFILTER: status != 'open'
858 ** If the option -q|--quote is used, the tickets are encoded by
859 ** quoting special chars(space -> \\s, tab -> \\t, newline -> \\n,
860 ** cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\).
861 ** Otherwise, the simplified encoding as on the show report raw
862 ** page in the gui is used.
863 **
864 ** Instead of the report title its possible to use the report
865 ** number. Using the special report number 0 list all columns,
866 ** defined in the ticket table.
867 **
868 ** %fossil ticket list fields
869 **
870 ** list all fields, defined for ticket in the fossil repository
871 **
872 ** %fossil ticket list reports
873 **
874 ** list all ticket reports, defined in the fossil repository
875 **
876 ** %fossil ticket set TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
877 ** %fossil ticket change TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
878 **
879 ** change ticket identified by TICKETUUID and set the value of
880 ** field FIELD to VALUE. Valid field descriptions are:
881 ** status, type, severity, priority, resolution,
882 ** foundin, private_contact, resolution, title or comment
883 ** Field names given above are the ones, defined in a standard
884 ** fossil environment. If you have added, deleted columns, you
885 ** change the all your configured columns.
886 ** You can use more than one field/value pair on the commandline.
887 ** Using -q|--quote enables the special character decoding as
888 ** in "ticket show". So it's possible, to set multiline text or
889 ** text with special characters.
890 **
891 ** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
892 **
893 ** like set, but create a new ticket with the given values.
894 **
895 ** The values in set|add are not validated against the definitions
896 ** given in "Ticket Common Script".
897 */
898 void ticket_cmd(void){
899 int n;
900
901 /* do some ints, we want to be inside a checkout */
902 db_must_be_within_tree();
903 db_find_and_open_repository(1);
904 user_select();
905 /*
906 ** Check that the user exists.
907 */
908 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
909 fossil_fatal("no such user: %s", g.zLogin);
910 }
911
912 if( g.argc<3 ){
913 usage("add|fieldlist|set|show");
914 }else{
915 n = strlen(g.argv[2]);
916 if( n==1 && g.argv[2][0]=='s' ){
917 /* set/show cannot be distinguished, so show the usage */
918 usage("add|fieldlist|set|show");
919 }else if( strncmp(g.argv[2],"list",n)==0 ){
920 if( g.argc==3 ){
921 usage("list fields|reports");
922 }else{
923 n = strlen(g.argv[3]);
924 if( !strncmp(g.argv[3],"fields",n) ){
925 /* simply show all field names */
926 int i;
927
928 /* read all available ticket fields */
929 getAllTicketFields();
930 for(i=0; i<nField; i++){
931 printf("%s\n",azField[i]);
932 }
933 }else if( !strncmp(g.argv[3],"reports",n) ){
934 rpt_list_reports();
935 }else{
936 fossil_fatal("unknown ticket list option '%s'!",g.argv[3]);
937 }
938 }
939 }else{
940 /* add a new ticket or set fields on existing tickets */
941 tTktShowEncoding tktEncoding;
942
943 tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab;
944
945 if( strncmp(g.argv[2],"show",n)==0 ){
946 if( g.argc==3 ){
947 usage("show REPORTNR");
948 }else{
949 const char *zRep = 0;
950 const char *zSep = 0;
951 const char *zFilterUuid = 0;
952
953 zSep = find_option("limit","l",1);
954 zRep = g.argv[3];
955 if( !strcmp(zRep,"0") ){
956 zRep = 0;
957 }
958 if( g.argc>4 ){
959 zFilterUuid = g.argv[4];
960 }
961
962 rptshow( zRep, zSep, zFilterUuid, tktEncoding );
963
964 }
965 }else{
966 /* add a new ticket or update an existing ticket */
967 enum { set,add,err } eCmd = err;
968 int i = 0;
969 int rid;
970 const char *zTktUuid = 0;
971 Blob tktchng, cksum;
972
973 /* get command type (set/add) and get uuid, if needed for set */
974 if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ){
975 eCmd = set;
976 if( g.argc==3 ){
977 usage("set TICKETUUID");
978 }
979 zTktUuid = db_text(0,
980 "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
981 );
982 if( !zTktUuid ){
983 fossil_fatal("unknown ticket: '%s'!",g.argv[3]);
984 }
985 i=4;
986 }else if( strncmp(g.argv[2],"add",n)==0 ){
987 eCmd = add;
988 i = 3;
989 zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))");
990 }
991 /* none of set/add, so show the usage! */
992 if( eCmd==err ){
993 usage("add|fieldlist|set|show");
994 }
995
996 /* read all given ticket field/value pairs from command line */
997 if( i==g.argc ){
998 fossil_fatal("empty %s command aborted!",g.argv[2]);
999 }
1000 getAllTicketFields();
1001 /* read commandline and assign fields in the azValue array */
1002 while( i<g.argc ){
1003 char *zFName;
1004 char *zFValue;
1005 int j;
1006
1007 zFName = g.argv[i++];
1008 if( i==g.argc ){
1009 fossil_fatal("missing value for '%s'!",zFName);
1010 }
1011 zFValue = g.argv[i++];
1012 j = fieldId(zFName);
1013 if( tktEncoding == tktFossilize ){
1014 zFValue=mprintf("%s",zFValue);
1015 defossilize(zFValue);
1016 }
1017 if( j == -1 ){
1018 fossil_fatal("unknown field name '%s'!",zFName);
1019 }else{
1020 azValue[j] = zFValue;
1021 }
1022 }
1023
1024 /* now add the needed artifacts to the repository */
1025 blob_zero(&tktchng);
1026 { /* add the time to the ticket manifest */
1027 char *zDate;
1028
1029 zDate = db_text(0, "SELECT datetime('now')");
1030 zDate[10] = 'T';
1031 blob_appendf(&tktchng, "D %s\n", zDate);
1032 free(zDate);
1033 }
1034 /* append defined elements */
1035 for(i=0; i<nField; i++){
1036 char *zValue;
1037
1038 zValue = azValue[i];
1039 if( azValue[i] && azValue[i][0] ){
1040 if( strncmp(azField[i], "private_", 8)==0 ){
1041 zValue = db_conceal(zValue, strlen(zValue));
1042 blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue);
1043 }else{
1044 blob_appendf(&tktchng, "J %s %#F\n",
1045 azField[i], strlen(zValue), zValue);
1046 }
1047 if( tktEncoding == tktFossilize ){
1048 free(azValue[i]);
1049 }
1050 }
1051 }
1052 blob_appendf(&tktchng, "K %s\n", zTktUuid);
1053 blob_appendf(&tktchng, "U %F\n", g.zLogin);
1054 md5sum_blob(&tktchng, &cksum);
1055 blob_appendf(&tktchng, "Z %b\n", &cksum);
1056 rid = content_put(&tktchng, 0, 0);
1057 if( rid==0 ){
1058 fossil_panic("trouble committing ticket: %s", g.zErrMsg);
1059 }
1060 manifest_crosslink_begin();
1061 manifest_crosslink(rid, &tktchng);
1062 manifest_crosslink_end();
1063 printf("ticket %s succeeded for UID %s\n",
1064 (eCmd==set?"set":"add"),zTktUuid);
1065 }
1066 }
1067 }
1068 }
1069
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -224,10 +224,16 @@
224224
$(OBJDIR)\doc$O : doc_.c doc.h
225225
$(TCC) -o$@ -c doc_.c
226226
227227
doc_.c : $(SRCDIR)\doc.c
228228
+translate$E $** > $@
229
+
230
+$(OBJDIR)\event$O : event_.c event.h
231
+ $(TCC) -o$@ -c event_.c
232
+
233
+event_.c : $(SRCDIR)\event.c
234
+ +translate$E $** > $@
229235
230236
$(OBJDIR)\encode$O : encode_.c encode.h
231237
$(TCC) -o$@ -c encode_.c
232238
233239
encode_.c : $(SRCDIR)\encode.c
234240
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -224,10 +224,16 @@
224 $(OBJDIR)\doc$O : doc_.c doc.h
225 $(TCC) -o$@ -c doc_.c
226
227 doc_.c : $(SRCDIR)\doc.c
228 +translate$E $** > $@
 
 
 
 
 
 
229
230 $(OBJDIR)\encode$O : encode_.c encode.h
231 $(TCC) -o$@ -c encode_.c
232
233 encode_.c : $(SRCDIR)\encode.c
234
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -224,10 +224,16 @@
224 $(OBJDIR)\doc$O : doc_.c doc.h
225 $(TCC) -o$@ -c doc_.c
226
227 doc_.c : $(SRCDIR)\doc.c
228 +translate$E $** > $@
229
230 $(OBJDIR)\event$O : event_.c event.h
231 $(TCC) -o$@ -c event_.c
232
233 event_.c : $(SRCDIR)\event.c
234 +translate$E $** > $@
235
236 $(OBJDIR)\encode$O : encode_.c encode.h
237 $(TCC) -o$@ -c encode_.c
238
239 encode_.c : $(SRCDIR)\encode.c
240

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button