Fossil SCM

Implement history display for tickets.

drh 2008-07-15 19:03 trunk
Commit b3ee50c9463214942d0af7055f4a06e2353f631c
+89
--- src/info.c
+++ src/info.c
@@ -791,10 +791,95 @@
791791
@ %h(blob_str(&content))
792792
@ </pre></blockquote>
793793
blob_reset(&content);
794794
style_footer();
795795
}
796
+
797
+/*
798
+** Return TRUE if the given BLOB contains a newline character.
799
+*/
800
+static int contains_newline(Blob *p){
801
+ const char *z = blob_str(p);
802
+ while( *z ){
803
+ if( *z=='\n' ) return 1;
804
+ z++;
805
+ }
806
+ return 0;
807
+}
808
+
809
+/*
810
+** WEBPAGE: tinfo
811
+** URL: /tinfo?name=UUID
812
+**
813
+** Show the details of a ticket change control artifact.
814
+*/
815
+void tinfo_page(void){
816
+ int rid;
817
+ Blob content;
818
+ char *zDate;
819
+ int i;
820
+ const char *zUuid;
821
+ char zTktName[20];
822
+ const char *z;
823
+ Manifest m;
824
+
825
+ login_check_credentials();
826
+ if( !g.okRdTkt ){ login_needed(); return; }
827
+ rid = name_to_rid(PD("name","0"));
828
+ if( rid==0 ){ fossil_redirect_home(); }
829
+ zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
830
+ if( g.okAdmin ){
831
+ if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
832
+ style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
833
+ g.zTop, zUuid);
834
+ }else{
835
+ style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
836
+ g.zTop, zUuid);
837
+ }
838
+ }
839
+ content_get(rid, &content);
840
+ if( manifest_parse(&m, &content)==0 ){
841
+ fossil_redirect_home();
842
+ }
843
+ if( m.type!=CFTYPE_TICKET ){
844
+ fossil_redirect_home();
845
+ }
846
+ style_header("Ticket Change Details");
847
+ zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate);
848
+ memcpy(zTktName, m.zTicketUuid, 10);
849
+ zTktName[10] = 0;
850
+ @ <h2>Changes to ticket <a href="%s(m.zTicketUuid)">%s(zTktName)</a></h2>
851
+ @
852
+ @ <p>By %h(m.zUser) on %s(zDate). See also:
853
+ @ <a href="%s(g.zTop)/artifact/%T(zUuid)">artifact content</a>, and
854
+ @ <a href="%s(g.zTop)/tkthistory/%s(m.zTicketUuid)">ticket history</a>
855
+ @ </p>
856
+ @
857
+ @ <ol>
858
+ free(zDate);
859
+ for(i=0; i<m.nField; i++){
860
+ Blob val;
861
+ z = m.aField[i].zName;
862
+ blob_set(&val, m.aField[i].zValue);
863
+ if( z[0]=='+' ){
864
+ @ <li><p>Appended to %h(&z[1]):</p><blockquote>
865
+ wiki_convert(&val, 0, 0);
866
+ @ </blockquote></li>
867
+ }else if( blob_size(&val)<=50 && contains_newline(&val) ){
868
+ @ <li><p>Change %h(z) to:</p><blockquote>
869
+ wiki_convert(&val, 0, 0);
870
+ @ </blockquote></li>
871
+ }else{
872
+ @ <li><p>Change %h(z) to "%h(blob_str(&val))"</p></li>
873
+ }
874
+ blob_reset(&val);
875
+ }
876
+ manifest_clear(&m);
877
+ @ </ol>
878
+ style_footer();
879
+}
880
+
796881
797882
/*
798883
** WEBPAGE: info
799884
** URL: info/UUID
800885
**
@@ -831,10 +916,14 @@
831916
finfo_page();
832917
}else
833918
if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)"
834919
" WHERE rid=%d AND tagname LIKE 'wiki-%%'", rid) ){
835920
winfo_page();
921
+ }else
922
+ if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)"
923
+ " WHERE rid=%d AND tagname LIKE 'tkt-%%'", rid) ){
924
+ tinfo_page();
836925
}else
837926
{
838927
artifact_page();
839928
}
840929
}
841930
--- src/info.c
+++ src/info.c
@@ -791,10 +791,95 @@
791 @ %h(blob_str(&content))
792 @ </pre></blockquote>
793 blob_reset(&content);
794 style_footer();
795 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
796
797 /*
798 ** WEBPAGE: info
799 ** URL: info/UUID
800 **
@@ -831,10 +916,14 @@
831 finfo_page();
832 }else
833 if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)"
834 " WHERE rid=%d AND tagname LIKE 'wiki-%%'", rid) ){
835 winfo_page();
 
 
 
 
836 }else
837 {
838 artifact_page();
839 }
840 }
841
--- src/info.c
+++ src/info.c
@@ -791,10 +791,95 @@
791 @ %h(blob_str(&content))
792 @ </pre></blockquote>
793 blob_reset(&content);
794 style_footer();
795 }
796
797 /*
798 ** Return TRUE if the given BLOB contains a newline character.
799 */
800 static int contains_newline(Blob *p){
801 const char *z = blob_str(p);
802 while( *z ){
803 if( *z=='\n' ) return 1;
804 z++;
805 }
806 return 0;
807 }
808
809 /*
810 ** WEBPAGE: tinfo
811 ** URL: /tinfo?name=UUID
812 **
813 ** Show the details of a ticket change control artifact.
814 */
815 void tinfo_page(void){
816 int rid;
817 Blob content;
818 char *zDate;
819 int i;
820 const char *zUuid;
821 char zTktName[20];
822 const char *z;
823 Manifest m;
824
825 login_check_credentials();
826 if( !g.okRdTkt ){ login_needed(); return; }
827 rid = name_to_rid(PD("name","0"));
828 if( rid==0 ){ fossil_redirect_home(); }
829 zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
830 if( g.okAdmin ){
831 if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
832 style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
833 g.zTop, zUuid);
834 }else{
835 style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
836 g.zTop, zUuid);
837 }
838 }
839 content_get(rid, &content);
840 if( manifest_parse(&m, &content)==0 ){
841 fossil_redirect_home();
842 }
843 if( m.type!=CFTYPE_TICKET ){
844 fossil_redirect_home();
845 }
846 style_header("Ticket Change Details");
847 zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate);
848 memcpy(zTktName, m.zTicketUuid, 10);
849 zTktName[10] = 0;
850 @ <h2>Changes to ticket <a href="%s(m.zTicketUuid)">%s(zTktName)</a></h2>
851 @
852 @ <p>By %h(m.zUser) on %s(zDate). See also:
853 @ <a href="%s(g.zTop)/artifact/%T(zUuid)">artifact content</a>, and
854 @ <a href="%s(g.zTop)/tkthistory/%s(m.zTicketUuid)">ticket history</a>
855 @ </p>
856 @
857 @ <ol>
858 free(zDate);
859 for(i=0; i<m.nField; i++){
860 Blob val;
861 z = m.aField[i].zName;
862 blob_set(&val, m.aField[i].zValue);
863 if( z[0]=='+' ){
864 @ <li><p>Appended to %h(&z[1]):</p><blockquote>
865 wiki_convert(&val, 0, 0);
866 @ </blockquote></li>
867 }else if( blob_size(&val)<=50 && contains_newline(&val) ){
868 @ <li><p>Change %h(z) to:</p><blockquote>
869 wiki_convert(&val, 0, 0);
870 @ </blockquote></li>
871 }else{
872 @ <li><p>Change %h(z) to "%h(blob_str(&val))"</p></li>
873 }
874 blob_reset(&val);
875 }
876 manifest_clear(&m);
877 @ </ol>
878 style_footer();
879 }
880
881
882 /*
883 ** WEBPAGE: info
884 ** URL: info/UUID
885 **
@@ -831,10 +916,14 @@
916 finfo_page();
917 }else
918 if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)"
919 " WHERE rid=%d AND tagname LIKE 'wiki-%%'", rid) ){
920 winfo_page();
921 }else
922 if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)"
923 " WHERE rid=%d AND tagname LIKE 'tkt-%%'", rid) ){
924 tinfo_page();
925 }else
926 {
927 artifact_page();
928 }
929 }
930
+34 -4
--- src/manifest.c
+++ src/manifest.c
@@ -893,23 +893,53 @@
893893
);
894894
free(zComment);
895895
}
896896
if( m.type==CFTYPE_TICKET ){
897897
char *zTag;
898
- char *zComment;
898
+ Blob comment;
899
+ int i;
899900
900901
ticket_insert(&m, 1, 1);
901902
zTag = mprintf("tkt-%s", m.zTicketUuid);
902903
tag_insert(zTag, 1, 0, rid, m.rDate, rid);
903904
free(zTag);
904
- zComment = mprintf("Changes to ticket [%.10s]", m.zTicketUuid);
905
+ blob_zero(&comment);
906
+ if( m.nField==1 ){
907
+ if( m.aField[0].zName[0]=='+' ){
908
+ blob_appendf(&comment,
909
+ "Appended to %h in ticket [%.10s]",
910
+ &m.aField[0].zName[1], m.zTicketUuid
911
+ );
912
+ }else if( strlen(m.aField[0].zValue)<40 ){
913
+ blob_appendf(&comment,
914
+ "Changed %h to \"%h\" in ticket [%.10s]",
915
+ m.aField[0].zName, m.aField[0].zValue, m.zTicketUuid
916
+ );
917
+ }else{
918
+ blob_appendf(&comment,
919
+ "Changed %h in ticket [%.10s]",
920
+ m.aField[0].zName, m.zTicketUuid
921
+ );
922
+ }
923
+ }else{
924
+ const char *z;
925
+ const char *zSep = " ";
926
+ blob_appendf(&comment, "%d changes to ticket [%.10s]:",
927
+ m.nField, m.zTicketUuid);
928
+ for(i=0; i<m.nField; i++){
929
+ z = m.aField[i].zName;
930
+ if( z[0]=='+' ) z++;
931
+ blob_appendf(&comment, "%s%h", zSep, z);
932
+ zSep = ", ";
933
+ }
934
+ }
905935
db_multi_exec(
906936
"REPLACE INTO event(type,mtime,objid,user,comment)"
907937
"VALUES('t',%.17g,%d,%Q,%Q)",
908
- m.rDate, rid, m.zUser, zComment
938
+ m.rDate, rid, m.zUser, blob_str(&comment)
909939
);
910
- free(zComment);
940
+ blob_reset(&comment);
911941
}
912942
db_end_transaction(0);
913943
manifest_clear(&m);
914944
return 1;
915945
}
916946
--- src/manifest.c
+++ src/manifest.c
@@ -893,23 +893,53 @@
893 );
894 free(zComment);
895 }
896 if( m.type==CFTYPE_TICKET ){
897 char *zTag;
898 char *zComment;
 
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 zComment = mprintf("Changes to ticket [%.10s]", m.zTicketUuid);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
905 db_multi_exec(
906 "REPLACE INTO event(type,mtime,objid,user,comment)"
907 "VALUES('t',%.17g,%d,%Q,%Q)",
908 m.rDate, rid, m.zUser, zComment
909 );
910 free(zComment);
911 }
912 db_end_transaction(0);
913 manifest_clear(&m);
914 return 1;
915 }
916
--- src/manifest.c
+++ src/manifest.c
@@ -893,23 +893,53 @@
893 );
894 free(zComment);
895 }
896 if( m.type==CFTYPE_TICKET ){
897 char *zTag;
898 Blob comment;
899 int i;
900
901 ticket_insert(&m, 1, 1);
902 zTag = mprintf("tkt-%s", m.zTicketUuid);
903 tag_insert(zTag, 1, 0, rid, m.rDate, rid);
904 free(zTag);
905 blob_zero(&comment);
906 if( m.nField==1 ){
907 if( m.aField[0].zName[0]=='+' ){
908 blob_appendf(&comment,
909 "Appended to %h in ticket [%.10s]",
910 &m.aField[0].zName[1], m.zTicketUuid
911 );
912 }else if( strlen(m.aField[0].zValue)<40 ){
913 blob_appendf(&comment,
914 "Changed %h to \"%h\" in ticket [%.10s]",
915 m.aField[0].zName, m.aField[0].zValue, m.zTicketUuid
916 );
917 }else{
918 blob_appendf(&comment,
919 "Changed %h in ticket [%.10s]",
920 m.aField[0].zName, m.zTicketUuid
921 );
922 }
923 }else{
924 const char *z;
925 const char *zSep = " ";
926 blob_appendf(&comment, "%d changes to ticket [%.10s]:",
927 m.nField, m.zTicketUuid);
928 for(i=0; i<m.nField; i++){
929 z = m.aField[i].zName;
930 if( z[0]=='+' ) z++;
931 blob_appendf(&comment, "%s%h", zSep, z);
932 zSep = ", ";
933 }
934 }
935 db_multi_exec(
936 "REPLACE INTO event(type,mtime,objid,user,comment)"
937 "VALUES('t',%.17g,%d,%Q,%Q)",
938 m.rDate, rid, m.zUser, blob_str(&comment)
939 );
940 blob_reset(&comment);
941 }
942 db_end_transaction(0);
943 manifest_clear(&m);
944 return 1;
945 }
946
+41
--- src/tkt.c
+++ src/tkt.c
@@ -304,10 +304,14 @@
304304
if( !g.okRdTkt ){ login_needed(); return; }
305305
if( g.okWrTkt ){
306306
style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
307307
g.zTop, PD("name",""));
308308
}
309
+ if( g.okHistory ){
310
+ style_submenu_element("History", "History Of This Ticket",
311
+ "%s/tkthistory/%T", g.zTop, PD("name",""));
312
+ }
309313
style_header("View Ticket");
310314
ticket_init();
311315
initializeVariablesFromDb();
312316
zScript = ticket_viewpage_code();
313317
Th_Render(zScript);
@@ -547,5 +551,42 @@
547551
return zErr;
548552
}
549553
}
550554
return 0;
551555
}
556
+
557
+/*
558
+** WEBPAGE: tkthistory
559
+** URL: /tkthistory?name=TICKETUUID
560
+**
561
+** Show the complete change history for a single ticket
562
+*/
563
+void tkthistory_page(void){
564
+ Stmt q;
565
+ char *zTitle;
566
+ char *zSQL;
567
+ const char *zUuid;
568
+ int tagid;
569
+
570
+ login_check_credentials();
571
+ if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
572
+ zUuid = PD("name","");
573
+ zTitle = mprintf("History Of Ticket %h", zUuid);
574
+ style_header(zTitle);
575
+ free(zTitle);
576
+
577
+ tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
578
+ if( tagid==0 ){
579
+ @ No such ticket: %h(zUuid)
580
+ style_footer();
581
+ return;
582
+ }
583
+ zSQL = mprintf("%s AND event.objid IN "
584
+ " (SELECT rid FROM tagxref WHERE tagid=%d) "
585
+ "ORDER BY mtime DESC",
586
+ timeline_query_for_www(), tagid);
587
+ db_prepare(&q, zSQL);
588
+ free(zSQL);
589
+ www_print_timeline(&q);
590
+ db_finalize(&q);
591
+ style_footer();
592
+}
552593
--- src/tkt.c
+++ src/tkt.c
@@ -304,10 +304,14 @@
304 if( !g.okRdTkt ){ login_needed(); return; }
305 if( g.okWrTkt ){
306 style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
307 g.zTop, PD("name",""));
308 }
 
 
 
 
309 style_header("View Ticket");
310 ticket_init();
311 initializeVariablesFromDb();
312 zScript = ticket_viewpage_code();
313 Th_Render(zScript);
@@ -547,5 +551,42 @@
547 return zErr;
548 }
549 }
550 return 0;
551 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
552
--- src/tkt.c
+++ src/tkt.c
@@ -304,10 +304,14 @@
304 if( !g.okRdTkt ){ login_needed(); return; }
305 if( g.okWrTkt ){
306 style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
307 g.zTop, PD("name",""));
308 }
309 if( g.okHistory ){
310 style_submenu_element("History", "History Of This Ticket",
311 "%s/tkthistory/%T", g.zTop, PD("name",""));
312 }
313 style_header("View Ticket");
314 ticket_init();
315 initializeVariablesFromDb();
316 zScript = ticket_viewpage_code();
317 Th_Render(zScript);
@@ -547,5 +551,42 @@
551 return zErr;
552 }
553 }
554 return 0;
555 }
556
557 /*
558 ** WEBPAGE: tkthistory
559 ** URL: /tkthistory?name=TICKETUUID
560 **
561 ** Show the complete change history for a single ticket
562 */
563 void tkthistory_page(void){
564 Stmt q;
565 char *zTitle;
566 char *zSQL;
567 const char *zUuid;
568 int tagid;
569
570 login_check_credentials();
571 if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
572 zUuid = PD("name","");
573 zTitle = mprintf("History Of Ticket %h", zUuid);
574 style_header(zTitle);
575 free(zTitle);
576
577 tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
578 if( tagid==0 ){
579 @ No such ticket: %h(zUuid)
580 style_footer();
581 return;
582 }
583 zSQL = mprintf("%s AND event.objid IN "
584 " (SELECT rid FROM tagxref WHERE tagid=%d) "
585 "ORDER BY mtime DESC",
586 timeline_query_for_www(), tagid);
587 db_prepare(&q, zSQL);
588 free(zSQL);
589 www_print_timeline(&q);
590 db_finalize(&q);
591 style_footer();
592 }
593

Keyboard Shortcuts

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