Fossil SCM

More improvements to the timeline display of ticket changes.

drh 2008-10-20 06:41 trunk
Commit f0c8693845d31d6592eafd35e4243f2e800cfba5
+59 -3
--- src/manifest.c
+++ src/manifest.c
@@ -892,18 +892,26 @@
892892
TAG_COMMENT, rid
893893
);
894894
free(zComment);
895895
}
896896
if( m.type==CFTYPE_TICKET ){
897
+ int i;
898
+ char *zTitle;
897899
char *zTag;
898900
Blob comment;
901
+ char *zNewStatus = 0;
902
+ static char *zTitleExpr = 0;
903
+ static char *zStatusColumn = 0;
904
+ static int once = 1;
905
+ int isNew;
899906
900
- ticket_insert(&m, 1, 1);
907
+ isNew = ticket_insert(&m, 1, 1);
901908
zTag = mprintf("tkt-%s", m.zTicketUuid);
902909
tag_insert(zTag, 1, 0, rid, m.rDate, rid);
903910
free(zTag);
904911
blob_zero(&comment);
912
+#if 0
905913
if( m.nField==1 ){
906914
if( m.aField[0].zName[0]=='+' ){
907915
blob_appendf(&comment,
908916
"Appended to %h in ticket [%.10s]",
909917
&m.aField[0].zName[1], m.zTicketUuid
@@ -918,11 +926,10 @@
918926
"Changed %h in ticket [%.10s]",
919927
m.aField[0].zName, m.zTicketUuid
920928
);
921929
}
922930
}else{
923
-#if 0
924931
int i;
925932
const char *z;
926933
const char *zSep = " ";
927934
blob_appendf(&comment, "%d changes to ticket [%.10s]:",
928935
m.nField, m.zTicketUuid);
@@ -930,13 +937,62 @@
930937
z = m.aField[i].zName;
931938
if( z[0]=='+' ) z++;
932939
blob_appendf(&comment, "%s%h", zSep, z);
933940
zSep = ", ";
934941
}
935
-#endif
942
+ int i;
943
+ const char *zStatus = 0;
944
+ const char *zTitle;
945
+ for(i=0; i<m.nField; i++){
946
+ z = m.aField[i].zName;
947
+ if( strcmp(z, "status") ) zStatus = m.aField[i].zValue;
948
+ }
949
+ if( zField
936950
blob_appendf(&comment, "Edits to ticket [%.10s]", m.zTicketUuid);
937951
}
952
+#endif
953
+ if( once ){
954
+ once = 0;
955
+ zTitleExpr = db_get("ticket-title-expr", "title");
956
+ zStatusColumn = db_get("ticket-status-column", "status");
957
+ }
958
+ zTitle = db_text("unknown",
959
+ "SELECT %s FROM ticket WHERE tkt_uuid='%s'",
960
+ zTitleExpr, m.zTicketUuid
961
+ );
962
+ if( !isNew ){
963
+ for(i=0; i<m.nField; i++){
964
+ if( strcmp(m.aField[i].zName, zStatusColumn)==0 ){
965
+ zNewStatus = m.aField[i].zValue;
966
+ }
967
+ }
968
+ if( zNewStatus ){
969
+ blob_appendf(&comment, "%h ticket [%.10s]: <i>%h</i>",
970
+ zNewStatus, m.zTicketUuid, zTitle
971
+ );
972
+ if( m.nField>1 ){
973
+ blob_appendf(&comment, " plus %d other change%s",
974
+ m.nField-1, m.nField==2 ? "" : "s");
975
+ }
976
+ }else{
977
+ zNewStatus = db_text("unknown",
978
+ "SELECT %s FROM ticket WHERE tkt_uuid='%s'",
979
+ zStatusColumn, m.zTicketUuid
980
+ );
981
+ blob_appendf(&comment, "Ticket [%.10s] <i>%h</i> status still %h with "
982
+ "%d other change%s",
983
+ m.zTicketUuid, zTitle, zNewStatus, m.nField,
984
+ m.nField==1 ? "" : "s"
985
+ );
986
+ free(zNewStatus);
987
+ }
988
+ }else{
989
+ blob_appendf(&comment, "New ticket [%.10s] <i>%h</i>.",
990
+ m.zTicketUuid, zTitle
991
+ );
992
+ }
993
+ free(zTitle);
938994
db_multi_exec(
939995
"REPLACE INTO event(type,mtime,objid,user,comment)"
940996
"VALUES('t',%.17g,%d,%Q,%Q)",
941997
m.rDate, rid, m.zUser, blob_str(&comment)
942998
);
943999
--- src/manifest.c
+++ src/manifest.c
@@ -892,18 +892,26 @@
892 TAG_COMMENT, rid
893 );
894 free(zComment);
895 }
896 if( m.type==CFTYPE_TICKET ){
 
 
897 char *zTag;
898 Blob comment;
 
 
 
 
 
899
900 ticket_insert(&m, 1, 1);
901 zTag = mprintf("tkt-%s", m.zTicketUuid);
902 tag_insert(zTag, 1, 0, rid, m.rDate, rid);
903 free(zTag);
904 blob_zero(&comment);
 
905 if( m.nField==1 ){
906 if( m.aField[0].zName[0]=='+' ){
907 blob_appendf(&comment,
908 "Appended to %h in ticket [%.10s]",
909 &m.aField[0].zName[1], m.zTicketUuid
@@ -918,11 +926,10 @@
918 "Changed %h in ticket [%.10s]",
919 m.aField[0].zName, m.zTicketUuid
920 );
921 }
922 }else{
923 #if 0
924 int i;
925 const char *z;
926 const char *zSep = " ";
927 blob_appendf(&comment, "%d changes to ticket [%.10s]:",
928 m.nField, m.zTicketUuid);
@@ -930,13 +937,62 @@
930 z = m.aField[i].zName;
931 if( z[0]=='+' ) z++;
932 blob_appendf(&comment, "%s%h", zSep, z);
933 zSep = ", ";
934 }
935 #endif
 
 
 
 
 
 
 
936 blob_appendf(&comment, "Edits to ticket [%.10s]", m.zTicketUuid);
937 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
938 db_multi_exec(
939 "REPLACE INTO event(type,mtime,objid,user,comment)"
940 "VALUES('t',%.17g,%d,%Q,%Q)",
941 m.rDate, rid, m.zUser, blob_str(&comment)
942 );
943
--- src/manifest.c
+++ src/manifest.c
@@ -892,18 +892,26 @@
892 TAG_COMMENT, rid
893 );
894 free(zComment);
895 }
896 if( m.type==CFTYPE_TICKET ){
897 int i;
898 char *zTitle;
899 char *zTag;
900 Blob comment;
901 char *zNewStatus = 0;
902 static char *zTitleExpr = 0;
903 static char *zStatusColumn = 0;
904 static int once = 1;
905 int isNew;
906
907 isNew = ticket_insert(&m, 1, 1);
908 zTag = mprintf("tkt-%s", m.zTicketUuid);
909 tag_insert(zTag, 1, 0, rid, m.rDate, rid);
910 free(zTag);
911 blob_zero(&comment);
912 #if 0
913 if( m.nField==1 ){
914 if( m.aField[0].zName[0]=='+' ){
915 blob_appendf(&comment,
916 "Appended to %h in ticket [%.10s]",
917 &m.aField[0].zName[1], m.zTicketUuid
@@ -918,11 +926,10 @@
926 "Changed %h in ticket [%.10s]",
927 m.aField[0].zName, m.zTicketUuid
928 );
929 }
930 }else{
 
931 int i;
932 const char *z;
933 const char *zSep = " ";
934 blob_appendf(&comment, "%d changes to ticket [%.10s]:",
935 m.nField, m.zTicketUuid);
@@ -930,13 +937,62 @@
937 z = m.aField[i].zName;
938 if( z[0]=='+' ) z++;
939 blob_appendf(&comment, "%s%h", zSep, z);
940 zSep = ", ";
941 }
942 int i;
943 const char *zStatus = 0;
944 const char *zTitle;
945 for(i=0; i<m.nField; i++){
946 z = m.aField[i].zName;
947 if( strcmp(z, "status") ) zStatus = m.aField[i].zValue;
948 }
949 if( zField
950 blob_appendf(&comment, "Edits to ticket [%.10s]", m.zTicketUuid);
951 }
952 #endif
953 if( once ){
954 once = 0;
955 zTitleExpr = db_get("ticket-title-expr", "title");
956 zStatusColumn = db_get("ticket-status-column", "status");
957 }
958 zTitle = db_text("unknown",
959 "SELECT %s FROM ticket WHERE tkt_uuid='%s'",
960 zTitleExpr, m.zTicketUuid
961 );
962 if( !isNew ){
963 for(i=0; i<m.nField; i++){
964 if( strcmp(m.aField[i].zName, zStatusColumn)==0 ){
965 zNewStatus = m.aField[i].zValue;
966 }
967 }
968 if( zNewStatus ){
969 blob_appendf(&comment, "%h ticket [%.10s]: <i>%h</i>",
970 zNewStatus, m.zTicketUuid, zTitle
971 );
972 if( m.nField>1 ){
973 blob_appendf(&comment, " plus %d other change%s",
974 m.nField-1, m.nField==2 ? "" : "s");
975 }
976 }else{
977 zNewStatus = db_text("unknown",
978 "SELECT %s FROM ticket WHERE tkt_uuid='%s'",
979 zStatusColumn, m.zTicketUuid
980 );
981 blob_appendf(&comment, "Ticket [%.10s] <i>%h</i> status still %h with "
982 "%d other change%s",
983 m.zTicketUuid, zTitle, zNewStatus, m.nField,
984 m.nField==1 ? "" : "s"
985 );
986 free(zNewStatus);
987 }
988 }else{
989 blob_appendf(&comment, "New ticket [%.10s] <i>%h</i>.",
990 m.zTicketUuid, zTitle
991 );
992 }
993 free(zTitle);
994 db_multi_exec(
995 "REPLACE INTO event(type,mtime,objid,user,comment)"
996 "VALUES('t',%.17g,%d,%Q,%Q)",
997 m.rDate, rid, m.zUser, blob_str(&comment)
998 );
999
+7 -1
--- src/tkt.c
+++ src/tkt.c
@@ -186,21 +186,26 @@
186186
** Update an entry of the TICKET table according to the information
187187
** in the control file given in p. Attempt to create the appropriate
188188
** TICKET table entry if createFlag is true. If createFlag is false,
189189
** that means we already know the entry exists and so we can save the
190190
** work of trying to create it.
191
+**
192
+** Return TRUE if a new TICKET entry was created and FALSE if an
193
+** existing entry was revised.
191194
*/
192
-void ticket_insert(Manifest *p, int createFlag, int checkTime){
195
+int ticket_insert(Manifest *p, int createFlag, int checkTime){
193196
Blob sql;
194197
Stmt q;
195198
int i;
196199
const char *zSep;
200
+ int rc = 0;
197201
198202
getAllTicketFields();
199203
if( createFlag ){
200204
db_multi_exec("INSERT OR IGNORE INTO ticket(tkt_uuid, tkt_mtime) "
201205
"VALUES(%Q, 0)", p->zTicketUuid);
206
+ rc = db_changes();
202207
}
203208
blob_zero(&sql);
204209
blob_appendf(&sql, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime");
205210
zSep = "SET";
206211
for(i=0; i<p->nField; i++){
@@ -229,10 +234,11 @@
229234
}
230235
db_multi_exec("INSERT OR IGNORE INTO _pending_ticket "
231236
"VALUES(%Q)", p->zTicketUuid);
232237
}
233238
blob_reset(&sql);
239
+ return rc;
234240
}
235241
236242
/*
237243
** Rebuild an entire entry in the TICKET table
238244
*/
239245
--- src/tkt.c
+++ src/tkt.c
@@ -186,21 +186,26 @@
186 ** Update an entry of the TICKET table according to the information
187 ** in the control file given in p. Attempt to create the appropriate
188 ** TICKET table entry if createFlag is true. If createFlag is false,
189 ** that means we already know the entry exists and so we can save the
190 ** work of trying to create it.
 
 
 
191 */
192 void ticket_insert(Manifest *p, int createFlag, int checkTime){
193 Blob sql;
194 Stmt q;
195 int i;
196 const char *zSep;
 
197
198 getAllTicketFields();
199 if( createFlag ){
200 db_multi_exec("INSERT OR IGNORE INTO ticket(tkt_uuid, tkt_mtime) "
201 "VALUES(%Q, 0)", p->zTicketUuid);
 
202 }
203 blob_zero(&sql);
204 blob_appendf(&sql, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime");
205 zSep = "SET";
206 for(i=0; i<p->nField; i++){
@@ -229,10 +234,11 @@
229 }
230 db_multi_exec("INSERT OR IGNORE INTO _pending_ticket "
231 "VALUES(%Q)", p->zTicketUuid);
232 }
233 blob_reset(&sql);
 
234 }
235
236 /*
237 ** Rebuild an entire entry in the TICKET table
238 */
239
--- src/tkt.c
+++ src/tkt.c
@@ -186,21 +186,26 @@
186 ** Update an entry of the TICKET table according to the information
187 ** in the control file given in p. Attempt to create the appropriate
188 ** TICKET table entry if createFlag is true. If createFlag is false,
189 ** that means we already know the entry exists and so we can save the
190 ** work of trying to create it.
191 **
192 ** Return TRUE if a new TICKET entry was created and FALSE if an
193 ** existing entry was revised.
194 */
195 int ticket_insert(Manifest *p, int createFlag, int checkTime){
196 Blob sql;
197 Stmt q;
198 int i;
199 const char *zSep;
200 int rc = 0;
201
202 getAllTicketFields();
203 if( createFlag ){
204 db_multi_exec("INSERT OR IGNORE INTO ticket(tkt_uuid, tkt_mtime) "
205 "VALUES(%Q, 0)", p->zTicketUuid);
206 rc = db_changes();
207 }
208 blob_zero(&sql);
209 blob_appendf(&sql, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime");
210 zSep = "SET";
211 for(i=0; i<p->nField; i++){
@@ -229,10 +234,11 @@
234 }
235 db_multi_exec("INSERT OR IGNORE INTO _pending_ticket "
236 "VALUES(%Q)", p->zTicketUuid);
237 }
238 blob_reset(&sql);
239 return rc;
240 }
241
242 /*
243 ** Rebuild an entire entry in the TICKET table
244 */
245
+6 -2
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -642,12 +642,16 @@
642642
login_insert_csrf_secret();
643643
644644
@ <hr>
645645
entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", "title");
646646
@ <p>An SQL expression in a query against the TICKET table that will
647
- @ return the title of the ticket for display purposes after hyperlinks to
648
- @ that ticket</p>
647
+ @ return the title of the ticket for display purposes.</p>
648
+
649
+ @ <hr>
650
+ entry_attribute("Ticket Status", 40, "ticket-status-column", "s", "status");
651
+ @ <p>The name of the column in the TICKET table that contains the ticket
652
+ @ status in human-readable form. Case sensitive.</p>
649653
650654
@ <hr>
651655
entry_attribute("Ticket Closed", 40, "ticket-closed-expr", "c",
652656
"status='Closed'");
653657
@ <p>An SQL expression that evaluates to true in a TICKET table query if
654658
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -642,12 +642,16 @@
642 login_insert_csrf_secret();
643
644 @ <hr>
645 entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", "title");
646 @ <p>An SQL expression in a query against the TICKET table that will
647 @ return the title of the ticket for display purposes after hyperlinks to
648 @ that ticket</p>
 
 
 
 
649
650 @ <hr>
651 entry_attribute("Ticket Closed", 40, "ticket-closed-expr", "c",
652 "status='Closed'");
653 @ <p>An SQL expression that evaluates to true in a TICKET table query if
654
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -642,12 +642,16 @@
642 login_insert_csrf_secret();
643
644 @ <hr>
645 entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", "title");
646 @ <p>An SQL expression in a query against the TICKET table that will
647 @ return the title of the ticket for display purposes.</p>
648
649 @ <hr>
650 entry_attribute("Ticket Status", 40, "ticket-status-column", "s", "status");
651 @ <p>The name of the column in the TICKET table that contains the ticket
652 @ status in human-readable form. Case sensitive.</p>
653
654 @ <hr>
655 entry_attribute("Ticket Closed", 40, "ticket-closed-expr", "c",
656 "status='Closed'");
657 @ <p>An SQL expression that evaluates to true in a TICKET table query if
658
+62 -83
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -868,18 +868,16 @@
868868
return 1;
869869
}
870870
871871
/*
872872
** zTarget is guaranteed to be a UUID. It might be the UUID of a ticket.
873
-** If it is, fill zDisplay[0..nDisplay-1] with the title of the ticket
874
-** (or a prefix if the title is too long) and return true. If zTarget
873
+** If it is, store in *pClosed a true or false depending on whether or not
874
+** the ticket is closed and return true. If zTarget
875875
** is not the UUID of a ticket, return false.
876876
*/
877877
static int is_ticket(
878878
const char *zTarget, /* Ticket UUID */
879
- char *zDisplay, /* Space in which to write ticket title */
880
- int nDisplay, /* Bytes available in zDisplay[] */
881879
int *pClosed /* True if the ticket is closed */
882880
){
883881
static Stmt q;
884882
static int once = 1;
885883
int n;
@@ -890,107 +888,97 @@
890888
memcpy(zLower, zTarget, n+1);
891889
canonical16(zLower, n+1);
892890
memcpy(zUpper, zLower, n+1);
893891
zUpper[n-1]++;
894892
if( once ){
895
- const char *zTitleExpr = db_get("ticket-title-expr", "title");
896893
const char *zClosedExpr = db_get("ticket-closed-expr", "status='Closed'");
897894
db_static_prepare(&q,
898
- "SELECT %s, %s FROM ticket "
895
+ "SELECT %s FROM ticket "
899896
" WHERE tkt_uuid>=:lwr AND tkt_uuid<:upr",
900
- zTitleExpr, zClosedExpr
897
+ zClosedExpr
901898
);
899
+ once = 0;
902900
}
903901
db_bind_text(&q, ":lwr", zLower);
904902
db_bind_text(&q, ":upr", zUpper);
905903
if( db_step(&q)==SQLITE_ROW ){
906
- n = db_column_bytes(&q,0);
907
- if( n>nDisplay-1 ) n = nDisplay - 1;
908
- memcpy(zDisplay, db_column_text(&q, 0), n);
909
- zDisplay[n] = 0;
910904
rc = 1;
911
- *pClosed = db_column_int(&q, 1);
905
+ *pClosed = db_column_int(&q, 0);
912906
}else{
913907
rc = 0;
914908
}
915909
db_reset(&q);
916910
return rc;
917911
}
918912
919913
/*
920914
** Resolve a hyperlink. The zTarget argument is the content of the [...]
921
-** in the wiki. Append an <a> markup to the output of the Renderer.
915
+** in the wiki. Append to the output string whatever text is approprate
916
+** for opening the hyperlink. Write into zClose[0...nClose-1] text that will
917
+** close the markup.
922918
**
923919
** Actually, this routine might or might not append the hyperlink, depending
924920
** on current rendering rules: specifically does the current user have
925
-** "History" permission. If this routine does append the <a> and thus needs
926
-** a </a> to follow, it returns true. If the <a> is suppressed, then return
927
-** false.
928
-**
929
-** If nDisplay>0 then optionally write up to nDisplay bytes of
930
-** alternative display text into zDisplay. The text must be zero
931
-** terminated. The final zero is included in the nDisplay byte count
932
-** limit.
921
+** "History" permission.
933922
*/
934
-static int resolveHyperlink(
923
+static void openHyperlink(
935924
Renderer *p, /* Rendering context */
936925
const char *zTarget, /* Hyperlink traget; text within [...] */
937
- char *zDisplay, /* Space in which to write alternative display */
938
- int nDisplay /* Bytes available in zDisplay[] */
926
+ char *zClose, /* Write hyperlink closing text here */
927
+ int nClose /* Bytes available in zClose[] */
939928
){
940
- int rc = 0;
929
+ const char *zTerm = "</a>";
930
+ assert( nClose>10 );
931
+
941932
if( strncmp(zTarget, "http:", 5)==0
942933
|| strncmp(zTarget, "https:", 6)==0
943934
|| strncmp(zTarget, "ftp:", 4)==0
944935
|| strncmp(zTarget, "mailto:", 7)==0
945936
){
946937
blob_appendf(p->pOut, "<a href=\"%s\">", zTarget);
947
- rc = 1;
948
- }else if( zTarget[0]=='/' ){
949
- if( g.okHistory ){
950
- blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zBaseURL, zTarget);
951
- rc = 1;
952
- }
953
- }else if( is_valid_uuid(zTarget) ){
954
- int isClosed;
955
- if( nDisplay && is_ticket(zTarget, zDisplay, nDisplay, &isClosed) ){
956
- /* Special display processing for tickets. Display the hyperlink
957
- ** as crossed out if the ticket is closed. Add the title after the
958
- ** hyperlink.
959
- */
960
- if( isClosed ){
961
- if( g.okHistory ){
962
- blob_appendf(p->pOut,"<a href=\"%s/info/%s\">[<s>%s</s>]</a>: %s",
963
- g.zBaseURL, zTarget, zTarget, zDisplay
964
- );
965
- }else{
966
- blob_appendf(p->pOut,"[<s>%s</s>]: %s", zTarget, zDisplay);
967
- }
968
- }else{
969
- if( g.okHistory ){
970
- blob_appendf(p->pOut,"<a href=\"%s/info/%s\">[%s]</a>: %s",
971
- g.zBaseURL, zTarget, zTarget, zDisplay
972
- );
973
- }else{
974
- blob_appendf(p->pOut,"[%s]: %s", zTarget, zDisplay);
975
- }
976
- }
977
- zDisplay[0] = ' ';
978
- zDisplay[1] = 0;
979
- rc = 0;
980
- }else if( g.okHistory ){
981
- blob_appendf(p->pOut, "<a href=\"%s/info/%s\">", g.zBaseURL, zTarget);
982
- rc = 1;
983
- }
984
- }else if( wiki_name_is_wellformed(zTarget) ){
985
- blob_appendf(p->pOut, "<a href=\"%s/wiki?name=%T\">", g.zBaseURL, zTarget);
986
- rc = 1;
987
- }else{
988
- blob_appendf(p->pOut, "[bad-link: %h]", zTarget);
989
- rc = 0;
990
- }
991
- return rc;
938
+ }else if( zTarget[0]=='/' ){
939
+ if( g.okHistory ){
940
+ blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zBaseURL, zTarget);
941
+ }else{
942
+ zTerm = "";
943
+ }
944
+ }else if( is_valid_uuid(zTarget) ){
945
+ int isClosed;
946
+ if( is_ticket(zTarget, &isClosed) ){
947
+ /* Special display processing for tickets. Display the hyperlink
948
+ ** as crossed out if the ticket is closed.
949
+ */
950
+ if( isClosed ){
951
+ if( g.okHistory ){
952
+ blob_appendf(p->pOut,"<a href=\"%s/info/%s\"><s>",
953
+ g.zBaseURL, zTarget
954
+ );
955
+ zTerm = "</s></a>";
956
+ }else{
957
+ blob_appendf(p->pOut,"<s>");
958
+ zTerm = "</s>";
959
+ }
960
+ }else{
961
+ if( g.okHistory ){
962
+ blob_appendf(p->pOut,"<a href=\"%s/info/%s\">",
963
+ g.zBaseURL, zTarget
964
+ );
965
+ }else{
966
+ zTerm = "";
967
+ }
968
+ }
969
+ }else if( g.okHistory ){
970
+ blob_appendf(p->pOut, "<a href=\"%s/info/%s\">", g.zBaseURL, zTarget);
971
+ }
972
+ }else if( wiki_name_is_wellformed(zTarget) ){
973
+ blob_appendf(p->pOut, "<a href=\"%s/wiki?name=%T\">", g.zBaseURL, zTarget);
974
+ }else{
975
+ blob_appendf(p->pOut, "[bad-link: %h]", zTarget);
976
+ zTerm = "";
977
+ }
978
+ assert( strlen(zTerm)<nClose );
979
+ strcpy(zClose, zTerm);
992980
}
993981
994982
/*
995983
** Check to see if the given parsed markup is the correct
996984
** </verbatim> tag.
@@ -1111,13 +1099,11 @@
11111099
case TOKEN_LINK: {
11121100
char *zTarget;
11131101
char *zDisplay = 0;
11141102
int i, j;
11151103
int savedState;
1116
- int needCloseA;
1117
- int altSize;
1118
- char zAltDisplay[100];
1104
+ char zClose[20];
11191105
11201106
startAutoParagraph(p);
11211107
zTarget = &z[1];
11221108
for(i=1; z[i] && z[i]!=']'; i++){
11231109
if( z[i]=='|' && zDisplay==0 ){
@@ -1127,27 +1113,20 @@
11271113
}
11281114
}
11291115
z[i] = 0;
11301116
if( zDisplay==0 ){
11311117
zDisplay = zTarget;
1132
- altSize = sizeof(zAltDisplay);
11331118
}else{
11341119
while( isspace(*zDisplay) ) zDisplay++;
1135
- altSize = 0;
11361120
}
1137
- zAltDisplay[0] = 0;
1138
- needCloseA = resolveHyperlink(p, zTarget, zAltDisplay, altSize);
1121
+ openHyperlink(p, zTarget, zClose, sizeof(zClose));
11391122
savedState = p->state;
11401123
p->state &= ~ALLOW_WIKI;
11411124
p->state |= FONT_MARKUP_ONLY;
1142
- if( zAltDisplay[0] ){
1143
- wiki_render(p, zAltDisplay);
1144
- }else{
1145
- wiki_render(p, zDisplay);
1146
- }
1125
+ wiki_render(p, zDisplay);
11471126
p->state = savedState;
1148
- if( needCloseA ) blob_append(p->pOut, "</a>", 4);
1127
+ blob_append(p->pOut, zClose, -1);
11491128
break;
11501129
}
11511130
case TOKEN_TEXT: {
11521131
startAutoParagraph(p);
11531132
blob_append(p->pOut, z, n);
11541133
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -868,18 +868,16 @@
868 return 1;
869 }
870
871 /*
872 ** zTarget is guaranteed to be a UUID. It might be the UUID of a ticket.
873 ** If it is, fill zDisplay[0..nDisplay-1] with the title of the ticket
874 ** (or a prefix if the title is too long) and return true. If zTarget
875 ** is not the UUID of a ticket, return false.
876 */
877 static int is_ticket(
878 const char *zTarget, /* Ticket UUID */
879 char *zDisplay, /* Space in which to write ticket title */
880 int nDisplay, /* Bytes available in zDisplay[] */
881 int *pClosed /* True if the ticket is closed */
882 ){
883 static Stmt q;
884 static int once = 1;
885 int n;
@@ -890,107 +888,97 @@
890 memcpy(zLower, zTarget, n+1);
891 canonical16(zLower, n+1);
892 memcpy(zUpper, zLower, n+1);
893 zUpper[n-1]++;
894 if( once ){
895 const char *zTitleExpr = db_get("ticket-title-expr", "title");
896 const char *zClosedExpr = db_get("ticket-closed-expr", "status='Closed'");
897 db_static_prepare(&q,
898 "SELECT %s, %s FROM ticket "
899 " WHERE tkt_uuid>=:lwr AND tkt_uuid<:upr",
900 zTitleExpr, zClosedExpr
901 );
 
902 }
903 db_bind_text(&q, ":lwr", zLower);
904 db_bind_text(&q, ":upr", zUpper);
905 if( db_step(&q)==SQLITE_ROW ){
906 n = db_column_bytes(&q,0);
907 if( n>nDisplay-1 ) n = nDisplay - 1;
908 memcpy(zDisplay, db_column_text(&q, 0), n);
909 zDisplay[n] = 0;
910 rc = 1;
911 *pClosed = db_column_int(&q, 1);
912 }else{
913 rc = 0;
914 }
915 db_reset(&q);
916 return rc;
917 }
918
919 /*
920 ** Resolve a hyperlink. The zTarget argument is the content of the [...]
921 ** in the wiki. Append an <a> markup to the output of the Renderer.
 
 
922 **
923 ** Actually, this routine might or might not append the hyperlink, depending
924 ** on current rendering rules: specifically does the current user have
925 ** "History" permission. If this routine does append the <a> and thus needs
926 ** a </a> to follow, it returns true. If the <a> is suppressed, then return
927 ** false.
928 **
929 ** If nDisplay>0 then optionally write up to nDisplay bytes of
930 ** alternative display text into zDisplay. The text must be zero
931 ** terminated. The final zero is included in the nDisplay byte count
932 ** limit.
933 */
934 static int resolveHyperlink(
935 Renderer *p, /* Rendering context */
936 const char *zTarget, /* Hyperlink traget; text within [...] */
937 char *zDisplay, /* Space in which to write alternative display */
938 int nDisplay /* Bytes available in zDisplay[] */
939 ){
940 int rc = 0;
 
 
941 if( strncmp(zTarget, "http:", 5)==0
942 || strncmp(zTarget, "https:", 6)==0
943 || strncmp(zTarget, "ftp:", 4)==0
944 || strncmp(zTarget, "mailto:", 7)==0
945 ){
946 blob_appendf(p->pOut, "<a href=\"%s\">", zTarget);
947 rc = 1;
948 }else if( zTarget[0]=='/' ){
949 if( g.okHistory ){
950 blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zBaseURL, zTarget);
951 rc = 1;
952 }
953 }else if( is_valid_uuid(zTarget) ){
954 int isClosed;
955 if( nDisplay && is_ticket(zTarget, zDisplay, nDisplay, &isClosed) ){
956 /* Special display processing for tickets. Display the hyperlink
957 ** as crossed out if the ticket is closed. Add the title after the
958 ** hyperlink.
959 */
960 if( isClosed ){
961 if( g.okHistory ){
962 blob_appendf(p->pOut,"<a href=\"%s/info/%s\">[<s>%s</s>]</a>: %s",
963 g.zBaseURL, zTarget, zTarget, zDisplay
964 );
965 }else{
966 blob_appendf(p->pOut,"[<s>%s</s>]: %s", zTarget, zDisplay);
967 }
968 }else{
969 if( g.okHistory ){
970 blob_appendf(p->pOut,"<a href=\"%s/info/%s\">[%s]</a>: %s",
971 g.zBaseURL, zTarget, zTarget, zDisplay
972 );
973 }else{
974 blob_appendf(p->pOut,"[%s]: %s", zTarget, zDisplay);
975 }
976 }
977 zDisplay[0] = ' ';
978 zDisplay[1] = 0;
979 rc = 0;
980 }else if( g.okHistory ){
981 blob_appendf(p->pOut, "<a href=\"%s/info/%s\">", g.zBaseURL, zTarget);
982 rc = 1;
983 }
984 }else if( wiki_name_is_wellformed(zTarget) ){
985 blob_appendf(p->pOut, "<a href=\"%s/wiki?name=%T\">", g.zBaseURL, zTarget);
986 rc = 1;
987 }else{
988 blob_appendf(p->pOut, "[bad-link: %h]", zTarget);
989 rc = 0;
990 }
991 return rc;
992 }
993
994 /*
995 ** Check to see if the given parsed markup is the correct
996 ** </verbatim> tag.
@@ -1111,13 +1099,11 @@
1111 case TOKEN_LINK: {
1112 char *zTarget;
1113 char *zDisplay = 0;
1114 int i, j;
1115 int savedState;
1116 int needCloseA;
1117 int altSize;
1118 char zAltDisplay[100];
1119
1120 startAutoParagraph(p);
1121 zTarget = &z[1];
1122 for(i=1; z[i] && z[i]!=']'; i++){
1123 if( z[i]=='|' && zDisplay==0 ){
@@ -1127,27 +1113,20 @@
1127 }
1128 }
1129 z[i] = 0;
1130 if( zDisplay==0 ){
1131 zDisplay = zTarget;
1132 altSize = sizeof(zAltDisplay);
1133 }else{
1134 while( isspace(*zDisplay) ) zDisplay++;
1135 altSize = 0;
1136 }
1137 zAltDisplay[0] = 0;
1138 needCloseA = resolveHyperlink(p, zTarget, zAltDisplay, altSize);
1139 savedState = p->state;
1140 p->state &= ~ALLOW_WIKI;
1141 p->state |= FONT_MARKUP_ONLY;
1142 if( zAltDisplay[0] ){
1143 wiki_render(p, zAltDisplay);
1144 }else{
1145 wiki_render(p, zDisplay);
1146 }
1147 p->state = savedState;
1148 if( needCloseA ) blob_append(p->pOut, "</a>", 4);
1149 break;
1150 }
1151 case TOKEN_TEXT: {
1152 startAutoParagraph(p);
1153 blob_append(p->pOut, z, n);
1154
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -868,18 +868,16 @@
868 return 1;
869 }
870
871 /*
872 ** zTarget is guaranteed to be a UUID. It might be the UUID of a ticket.
873 ** If it is, store in *pClosed a true or false depending on whether or not
874 ** the ticket is closed and return true. If zTarget
875 ** is not the UUID of a ticket, return false.
876 */
877 static int is_ticket(
878 const char *zTarget, /* Ticket UUID */
 
 
879 int *pClosed /* True if the ticket is closed */
880 ){
881 static Stmt q;
882 static int once = 1;
883 int n;
@@ -890,107 +888,97 @@
888 memcpy(zLower, zTarget, n+1);
889 canonical16(zLower, n+1);
890 memcpy(zUpper, zLower, n+1);
891 zUpper[n-1]++;
892 if( once ){
 
893 const char *zClosedExpr = db_get("ticket-closed-expr", "status='Closed'");
894 db_static_prepare(&q,
895 "SELECT %s FROM ticket "
896 " WHERE tkt_uuid>=:lwr AND tkt_uuid<:upr",
897 zClosedExpr
898 );
899 once = 0;
900 }
901 db_bind_text(&q, ":lwr", zLower);
902 db_bind_text(&q, ":upr", zUpper);
903 if( db_step(&q)==SQLITE_ROW ){
 
 
 
 
904 rc = 1;
905 *pClosed = db_column_int(&q, 0);
906 }else{
907 rc = 0;
908 }
909 db_reset(&q);
910 return rc;
911 }
912
913 /*
914 ** Resolve a hyperlink. The zTarget argument is the content of the [...]
915 ** in the wiki. Append to the output string whatever text is approprate
916 ** for opening the hyperlink. Write into zClose[0...nClose-1] text that will
917 ** close the markup.
918 **
919 ** Actually, this routine might or might not append the hyperlink, depending
920 ** on current rendering rules: specifically does the current user have
921 ** "History" permission.
 
 
 
 
 
 
 
922 */
923 static void openHyperlink(
924 Renderer *p, /* Rendering context */
925 const char *zTarget, /* Hyperlink traget; text within [...] */
926 char *zClose, /* Write hyperlink closing text here */
927 int nClose /* Bytes available in zClose[] */
928 ){
929 const char *zTerm = "</a>";
930 assert( nClose>10 );
931
932 if( strncmp(zTarget, "http:", 5)==0
933 || strncmp(zTarget, "https:", 6)==0
934 || strncmp(zTarget, "ftp:", 4)==0
935 || strncmp(zTarget, "mailto:", 7)==0
936 ){
937 blob_appendf(p->pOut, "<a href=\"%s\">", zTarget);
938 }else if( zTarget[0]=='/' ){
939 if( g.okHistory ){
940 blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zBaseURL, zTarget);
941 }else{
942 zTerm = "";
943 }
944 }else if( is_valid_uuid(zTarget) ){
945 int isClosed;
946 if( is_ticket(zTarget, &isClosed) ){
947 /* Special display processing for tickets. Display the hyperlink
948 ** as crossed out if the ticket is closed.
949 */
950 if( isClosed ){
951 if( g.okHistory ){
952 blob_appendf(p->pOut,"<a href=\"%s/info/%s\"><s>",
953 g.zBaseURL, zTarget
954 );
955 zTerm = "</s></a>";
956 }else{
957 blob_appendf(p->pOut,"<s>");
958 zTerm = "</s>";
959 }
960 }else{
961 if( g.okHistory ){
962 blob_appendf(p->pOut,"<a href=\"%s/info/%s\">",
963 g.zBaseURL, zTarget
964 );
965 }else{
966 zTerm = "";
967 }
968 }
969 }else if( g.okHistory ){
970 blob_appendf(p->pOut, "<a href=\"%s/info/%s\">", g.zBaseURL, zTarget);
971 }
972 }else if( wiki_name_is_wellformed(zTarget) ){
973 blob_appendf(p->pOut, "<a href=\"%s/wiki?name=%T\">", g.zBaseURL, zTarget);
974 }else{
975 blob_appendf(p->pOut, "[bad-link: %h]", zTarget);
976 zTerm = "";
977 }
978 assert( strlen(zTerm)<nClose );
979 strcpy(zClose, zTerm);
 
 
 
980 }
981
982 /*
983 ** Check to see if the given parsed markup is the correct
984 ** </verbatim> tag.
@@ -1111,13 +1099,11 @@
1099 case TOKEN_LINK: {
1100 char *zTarget;
1101 char *zDisplay = 0;
1102 int i, j;
1103 int savedState;
1104 char zClose[20];
 
 
1105
1106 startAutoParagraph(p);
1107 zTarget = &z[1];
1108 for(i=1; z[i] && z[i]!=']'; i++){
1109 if( z[i]=='|' && zDisplay==0 ){
@@ -1127,27 +1113,20 @@
1113 }
1114 }
1115 z[i] = 0;
1116 if( zDisplay==0 ){
1117 zDisplay = zTarget;
 
1118 }else{
1119 while( isspace(*zDisplay) ) zDisplay++;
 
1120 }
1121 openHyperlink(p, zTarget, zClose, sizeof(zClose));
 
1122 savedState = p->state;
1123 p->state &= ~ALLOW_WIKI;
1124 p->state |= FONT_MARKUP_ONLY;
1125 wiki_render(p, zDisplay);
 
 
 
 
1126 p->state = savedState;
1127 blob_append(p->pOut, zClose, -1);
1128 break;
1129 }
1130 case TOKEN_TEXT: {
1131 startAutoParagraph(p);
1132 blob_append(p->pOut, z, n);
1133

Keyboard Shortcuts

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