Fossil SCM
Add a timeline view at the top of the /tktview page if the "tl" query parameter is present. If a ticket is viewed from /info, then the timeline is always on. Perhaps the timeline should be on regardless?
Commit
3d131528c8c0167d6d1d06cd161e752d6c2ae53d03e56909635f4ab6259a6162
Parent
4cf8dbe39820f10…
2 files changed
+1
+57
-32
+1
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -2511,10 +2511,11 @@ | ||
| 2511 | 2511 | } |
| 2512 | 2512 | rc = name_to_uuid(&uuid, -1, "*"); |
| 2513 | 2513 | if( rc==1 ){ |
| 2514 | 2514 | if( validate16(zName, nLen) ){ |
| 2515 | 2515 | if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){ |
| 2516 | + cgi_set_parameter_nocopy("tl","1",0); | |
| 2516 | 2517 | tktview_page(); |
| 2517 | 2518 | return; |
| 2518 | 2519 | } |
| 2519 | 2520 | if( db_exists("SELECT 1 FROM tag" |
| 2520 | 2521 | " WHERE tagname GLOB 'event-%q*'", zName) ){ |
| 2521 | 2522 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -2511,10 +2511,11 @@ | |
| 2511 | } |
| 2512 | rc = name_to_uuid(&uuid, -1, "*"); |
| 2513 | if( rc==1 ){ |
| 2514 | if( validate16(zName, nLen) ){ |
| 2515 | if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){ |
| 2516 | tktview_page(); |
| 2517 | return; |
| 2518 | } |
| 2519 | if( db_exists("SELECT 1 FROM tag" |
| 2520 | " WHERE tagname GLOB 'event-%q*'", zName) ){ |
| 2521 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -2511,10 +2511,11 @@ | |
| 2511 | } |
| 2512 | rc = name_to_uuid(&uuid, -1, "*"); |
| 2513 | if( rc==1 ){ |
| 2514 | if( validate16(zName, nLen) ){ |
| 2515 | if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){ |
| 2516 | cgi_set_parameter_nocopy("tl","1",0); |
| 2517 | tktview_page(); |
| 2518 | return; |
| 2519 | } |
| 2520 | if( db_exists("SELECT 1 FROM tag" |
| 2521 | " WHERE tagname GLOB 'event-%q*'", zName) ){ |
| 2522 |
+57
-32
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -445,27 +445,30 @@ | ||
| 445 | 445 | @ </ul></div> |
| 446 | 446 | } |
| 447 | 447 | |
| 448 | 448 | /* |
| 449 | 449 | ** WEBPAGE: tktview |
| 450 | -** URL: tktview?name=UUID | |
| 450 | +** URL: tktview?name=HASH | |
| 451 | 451 | ** |
| 452 | 452 | ** View a ticket identified by the name= query parameter. |
| 453 | +** Other query parameters: | |
| 454 | +** | |
| 455 | +** tl Show a timeline of the ticket above the status | |
| 453 | 456 | */ |
| 454 | 457 | void tktview_page(void){ |
| 455 | 458 | const char *zScript; |
| 456 | 459 | char *zFullName; |
| 457 | 460 | const char *zUuid = PD("name",""); |
| 461 | + int showTimeline = P("tl")!=0; | |
| 458 | 462 | |
| 459 | 463 | login_check_credentials(); |
| 460 | 464 | if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; } |
| 461 | 465 | if( g.anon.WrTkt || g.anon.ApndTkt ){ |
| 462 | 466 | style_submenu_element("Edit", "%s/tktedit?name=%T", g.zTop, PD("name","")); |
| 463 | 467 | } |
| 464 | 468 | if( g.perm.Hyperlink ){ |
| 465 | 469 | style_submenu_element("History", "%s/tkthistory/%T", g.zTop, zUuid); |
| 466 | - style_submenu_element("Timeline", "%s/tkttimeline/%T", g.zTop, zUuid); | |
| 467 | 470 | style_submenu_element("Check-ins", "%s/tkttimeline/%T?y=ci", g.zTop, zUuid); |
| 468 | 471 | } |
| 469 | 472 | if( g.anon.NewTkt ){ |
| 470 | 473 | style_submenu_element("New Ticket", "%s/tktnew", g.zTop); |
| 471 | 474 | } |
| @@ -477,10 +480,23 @@ | ||
| 477 | 480 | style_submenu_element("Formatted", "%R/tktview/%s", zUuid); |
| 478 | 481 | }else{ |
| 479 | 482 | style_submenu_element("Plaintext", "%R/tktview/%s?plaintext", zUuid); |
| 480 | 483 | } |
| 481 | 484 | style_header("View Ticket"); |
| 485 | + if( showTimeline ){ | |
| 486 | + int tagid = db_int(0,"SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'", | |
| 487 | + zUuid); | |
| 488 | + if( tagid ){ | |
| 489 | + tkt_draw_timeline(tagid, "a"); | |
| 490 | + @ <hr> | |
| 491 | + }else{ | |
| 492 | + showTimeline = 0; | |
| 493 | + } | |
| 494 | + } | |
| 495 | + if( !showTimeline && g.perm.Hyperlink ){ | |
| 496 | + style_submenu_element("Timeline", "%s/info/%T", g.zTop, zUuid); | |
| 497 | + } | |
| 482 | 498 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1); |
| 483 | 499 | ticket_init(); |
| 484 | 500 | initializeVariablesFromCGI(); |
| 485 | 501 | getAllTicketFields(); |
| 486 | 502 | initializeVariablesFromDb(); |
| @@ -825,23 +841,58 @@ | ||
| 825 | 841 | } |
| 826 | 842 | sqlite3_close(db); |
| 827 | 843 | } |
| 828 | 844 | return zErr; |
| 829 | 845 | } |
| 846 | + | |
| 847 | +/* | |
| 848 | +** Draw a timeline for a ticket with tag.tagid given by the tagid | |
| 849 | +** parameter. | |
| 850 | +*/ | |
| 851 | +void tkt_draw_timeline(int tagid, const char *zType){ | |
| 852 | + Stmt q; | |
| 853 | + char *zFullUuid; | |
| 854 | + char *zSQL; | |
| 855 | + zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d", | |
| 856 | + tagid); | |
| 857 | + if( zType[0]=='c' ){ | |
| 858 | + zSQL = mprintf( | |
| 859 | + "%s AND event.objid IN " | |
| 860 | + " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' " | |
| 861 | + "AND '%s' GLOB (target||'*')) " | |
| 862 | + "ORDER BY mtime DESC", | |
| 863 | + timeline_query_for_www(), zFullUuid, zFullUuid | |
| 864 | + ); | |
| 865 | + }else{ | |
| 866 | + zSQL = mprintf( | |
| 867 | + "%s AND event.objid IN " | |
| 868 | + " (SELECT rid FROM tagxref WHERE tagid=%d" | |
| 869 | + " UNION SELECT srcid FROM backlink" | |
| 870 | + " WHERE target GLOB '%.4s*'" | |
| 871 | + " AND '%s' GLOB (target||'*')" | |
| 872 | + " UNION SELECT attachid FROM attachment" | |
| 873 | + " WHERE target=%Q) " | |
| 874 | + "ORDER BY mtime DESC", | |
| 875 | + timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid | |
| 876 | + ); | |
| 877 | + } | |
| 878 | + db_prepare(&q, "%z", zSQL/*safe-for-%s*/); | |
| 879 | + www_print_timeline(&q, TIMELINE_ARTID|TIMELINE_DISJOINT|TIMELINE_GRAPH, | |
| 880 | + 0, 0, 0, 0, 0, 0); | |
| 881 | + db_finalize(&q); | |
| 882 | + fossil_free(zFullUuid); | |
| 883 | +} | |
| 830 | 884 | |
| 831 | 885 | /* |
| 832 | 886 | ** WEBPAGE: tkttimeline |
| 833 | 887 | ** URL: /tkttimeline?name=TICKETUUID&y=TYPE |
| 834 | 888 | ** |
| 835 | 889 | ** Show the change history for a single ticket in timeline format. |
| 836 | 890 | */ |
| 837 | 891 | void tkttimeline_page(void){ |
| 838 | - Stmt q; | |
| 839 | 892 | char *zTitle; |
| 840 | - char *zSQL; | |
| 841 | 893 | const char *zUuid; |
| 842 | - char *zFullUuid; | |
| 843 | 894 | int tagid; |
| 844 | 895 | char zGlobPattern[50]; |
| 845 | 896 | const char *zType; |
| 846 | 897 | |
| 847 | 898 | login_check_credentials(); |
| @@ -872,37 +923,11 @@ | ||
| 872 | 923 | if( tagid==0 ){ |
| 873 | 924 | @ No such ticket: %h(zUuid) |
| 874 | 925 | style_footer(); |
| 875 | 926 | return; |
| 876 | 927 | } |
| 877 | - zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d", | |
| 878 | - tagid); | |
| 879 | - if( zType[0]=='c' ){ | |
| 880 | - zSQL = mprintf( | |
| 881 | - "%s AND event.objid IN " | |
| 882 | - " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' " | |
| 883 | - "AND '%s' GLOB (target||'*')) " | |
| 884 | - "ORDER BY mtime DESC", | |
| 885 | - timeline_query_for_www(), zFullUuid, zFullUuid | |
| 886 | - ); | |
| 887 | - }else{ | |
| 888 | - zSQL = mprintf( | |
| 889 | - "%s AND event.objid IN " | |
| 890 | - " (SELECT rid FROM tagxref WHERE tagid=%d" | |
| 891 | - " UNION SELECT srcid FROM backlink" | |
| 892 | - " WHERE target GLOB '%.4s*'" | |
| 893 | - " AND '%s' GLOB (target||'*')" | |
| 894 | - " UNION SELECT attachid FROM attachment" | |
| 895 | - " WHERE target=%Q) " | |
| 896 | - "ORDER BY mtime DESC", | |
| 897 | - timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid | |
| 898 | - ); | |
| 899 | - } | |
| 900 | - db_prepare(&q, "%z", zSQL/*safe-for-%s*/); | |
| 901 | - www_print_timeline(&q, TIMELINE_ARTID|TIMELINE_DISJOINT|TIMELINE_GRAPH, | |
| 902 | - 0, 0, 0, 0, 0, 0); | |
| 903 | - db_finalize(&q); | |
| 928 | + tkt_draw_timeline(tagid, zType); | |
| 904 | 929 | style_footer(); |
| 905 | 930 | } |
| 906 | 931 | |
| 907 | 932 | /* |
| 908 | 933 | ** WEBPAGE: tkthistory |
| 909 | 934 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -445,27 +445,30 @@ | |
| 445 | @ </ul></div> |
| 446 | } |
| 447 | |
| 448 | /* |
| 449 | ** WEBPAGE: tktview |
| 450 | ** URL: tktview?name=UUID |
| 451 | ** |
| 452 | ** View a ticket identified by the name= query parameter. |
| 453 | */ |
| 454 | void tktview_page(void){ |
| 455 | const char *zScript; |
| 456 | char *zFullName; |
| 457 | const char *zUuid = PD("name",""); |
| 458 | |
| 459 | login_check_credentials(); |
| 460 | if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; } |
| 461 | if( g.anon.WrTkt || g.anon.ApndTkt ){ |
| 462 | style_submenu_element("Edit", "%s/tktedit?name=%T", g.zTop, PD("name","")); |
| 463 | } |
| 464 | if( g.perm.Hyperlink ){ |
| 465 | style_submenu_element("History", "%s/tkthistory/%T", g.zTop, zUuid); |
| 466 | style_submenu_element("Timeline", "%s/tkttimeline/%T", g.zTop, zUuid); |
| 467 | style_submenu_element("Check-ins", "%s/tkttimeline/%T?y=ci", g.zTop, zUuid); |
| 468 | } |
| 469 | if( g.anon.NewTkt ){ |
| 470 | style_submenu_element("New Ticket", "%s/tktnew", g.zTop); |
| 471 | } |
| @@ -477,10 +480,23 @@ | |
| 477 | style_submenu_element("Formatted", "%R/tktview/%s", zUuid); |
| 478 | }else{ |
| 479 | style_submenu_element("Plaintext", "%R/tktview/%s?plaintext", zUuid); |
| 480 | } |
| 481 | style_header("View Ticket"); |
| 482 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1); |
| 483 | ticket_init(); |
| 484 | initializeVariablesFromCGI(); |
| 485 | getAllTicketFields(); |
| 486 | initializeVariablesFromDb(); |
| @@ -825,23 +841,58 @@ | |
| 825 | } |
| 826 | sqlite3_close(db); |
| 827 | } |
| 828 | return zErr; |
| 829 | } |
| 830 | |
| 831 | /* |
| 832 | ** WEBPAGE: tkttimeline |
| 833 | ** URL: /tkttimeline?name=TICKETUUID&y=TYPE |
| 834 | ** |
| 835 | ** Show the change history for a single ticket in timeline format. |
| 836 | */ |
| 837 | void tkttimeline_page(void){ |
| 838 | Stmt q; |
| 839 | char *zTitle; |
| 840 | char *zSQL; |
| 841 | const char *zUuid; |
| 842 | char *zFullUuid; |
| 843 | int tagid; |
| 844 | char zGlobPattern[50]; |
| 845 | const char *zType; |
| 846 | |
| 847 | login_check_credentials(); |
| @@ -872,37 +923,11 @@ | |
| 872 | if( tagid==0 ){ |
| 873 | @ No such ticket: %h(zUuid) |
| 874 | style_footer(); |
| 875 | return; |
| 876 | } |
| 877 | zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d", |
| 878 | tagid); |
| 879 | if( zType[0]=='c' ){ |
| 880 | zSQL = mprintf( |
| 881 | "%s AND event.objid IN " |
| 882 | " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' " |
| 883 | "AND '%s' GLOB (target||'*')) " |
| 884 | "ORDER BY mtime DESC", |
| 885 | timeline_query_for_www(), zFullUuid, zFullUuid |
| 886 | ); |
| 887 | }else{ |
| 888 | zSQL = mprintf( |
| 889 | "%s AND event.objid IN " |
| 890 | " (SELECT rid FROM tagxref WHERE tagid=%d" |
| 891 | " UNION SELECT srcid FROM backlink" |
| 892 | " WHERE target GLOB '%.4s*'" |
| 893 | " AND '%s' GLOB (target||'*')" |
| 894 | " UNION SELECT attachid FROM attachment" |
| 895 | " WHERE target=%Q) " |
| 896 | "ORDER BY mtime DESC", |
| 897 | timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid |
| 898 | ); |
| 899 | } |
| 900 | db_prepare(&q, "%z", zSQL/*safe-for-%s*/); |
| 901 | www_print_timeline(&q, TIMELINE_ARTID|TIMELINE_DISJOINT|TIMELINE_GRAPH, |
| 902 | 0, 0, 0, 0, 0, 0); |
| 903 | db_finalize(&q); |
| 904 | style_footer(); |
| 905 | } |
| 906 | |
| 907 | /* |
| 908 | ** WEBPAGE: tkthistory |
| 909 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -445,27 +445,30 @@ | |
| 445 | @ </ul></div> |
| 446 | } |
| 447 | |
| 448 | /* |
| 449 | ** WEBPAGE: tktview |
| 450 | ** URL: tktview?name=HASH |
| 451 | ** |
| 452 | ** View a ticket identified by the name= query parameter. |
| 453 | ** Other query parameters: |
| 454 | ** |
| 455 | ** tl Show a timeline of the ticket above the status |
| 456 | */ |
| 457 | void tktview_page(void){ |
| 458 | const char *zScript; |
| 459 | char *zFullName; |
| 460 | const char *zUuid = PD("name",""); |
| 461 | int showTimeline = P("tl")!=0; |
| 462 | |
| 463 | login_check_credentials(); |
| 464 | if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; } |
| 465 | if( g.anon.WrTkt || g.anon.ApndTkt ){ |
| 466 | style_submenu_element("Edit", "%s/tktedit?name=%T", g.zTop, PD("name","")); |
| 467 | } |
| 468 | if( g.perm.Hyperlink ){ |
| 469 | style_submenu_element("History", "%s/tkthistory/%T", g.zTop, zUuid); |
| 470 | style_submenu_element("Check-ins", "%s/tkttimeline/%T?y=ci", g.zTop, zUuid); |
| 471 | } |
| 472 | if( g.anon.NewTkt ){ |
| 473 | style_submenu_element("New Ticket", "%s/tktnew", g.zTop); |
| 474 | } |
| @@ -477,10 +480,23 @@ | |
| 480 | style_submenu_element("Formatted", "%R/tktview/%s", zUuid); |
| 481 | }else{ |
| 482 | style_submenu_element("Plaintext", "%R/tktview/%s?plaintext", zUuid); |
| 483 | } |
| 484 | style_header("View Ticket"); |
| 485 | if( showTimeline ){ |
| 486 | int tagid = db_int(0,"SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'", |
| 487 | zUuid); |
| 488 | if( tagid ){ |
| 489 | tkt_draw_timeline(tagid, "a"); |
| 490 | @ <hr> |
| 491 | }else{ |
| 492 | showTimeline = 0; |
| 493 | } |
| 494 | } |
| 495 | if( !showTimeline && g.perm.Hyperlink ){ |
| 496 | style_submenu_element("Timeline", "%s/info/%T", g.zTop, zUuid); |
| 497 | } |
| 498 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1); |
| 499 | ticket_init(); |
| 500 | initializeVariablesFromCGI(); |
| 501 | getAllTicketFields(); |
| 502 | initializeVariablesFromDb(); |
| @@ -825,23 +841,58 @@ | |
| 841 | } |
| 842 | sqlite3_close(db); |
| 843 | } |
| 844 | return zErr; |
| 845 | } |
| 846 | |
| 847 | /* |
| 848 | ** Draw a timeline for a ticket with tag.tagid given by the tagid |
| 849 | ** parameter. |
| 850 | */ |
| 851 | void tkt_draw_timeline(int tagid, const char *zType){ |
| 852 | Stmt q; |
| 853 | char *zFullUuid; |
| 854 | char *zSQL; |
| 855 | zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d", |
| 856 | tagid); |
| 857 | if( zType[0]=='c' ){ |
| 858 | zSQL = mprintf( |
| 859 | "%s AND event.objid IN " |
| 860 | " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' " |
| 861 | "AND '%s' GLOB (target||'*')) " |
| 862 | "ORDER BY mtime DESC", |
| 863 | timeline_query_for_www(), zFullUuid, zFullUuid |
| 864 | ); |
| 865 | }else{ |
| 866 | zSQL = mprintf( |
| 867 | "%s AND event.objid IN " |
| 868 | " (SELECT rid FROM tagxref WHERE tagid=%d" |
| 869 | " UNION SELECT srcid FROM backlink" |
| 870 | " WHERE target GLOB '%.4s*'" |
| 871 | " AND '%s' GLOB (target||'*')" |
| 872 | " UNION SELECT attachid FROM attachment" |
| 873 | " WHERE target=%Q) " |
| 874 | "ORDER BY mtime DESC", |
| 875 | timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid |
| 876 | ); |
| 877 | } |
| 878 | db_prepare(&q, "%z", zSQL/*safe-for-%s*/); |
| 879 | www_print_timeline(&q, TIMELINE_ARTID|TIMELINE_DISJOINT|TIMELINE_GRAPH, |
| 880 | 0, 0, 0, 0, 0, 0); |
| 881 | db_finalize(&q); |
| 882 | fossil_free(zFullUuid); |
| 883 | } |
| 884 | |
| 885 | /* |
| 886 | ** WEBPAGE: tkttimeline |
| 887 | ** URL: /tkttimeline?name=TICKETUUID&y=TYPE |
| 888 | ** |
| 889 | ** Show the change history for a single ticket in timeline format. |
| 890 | */ |
| 891 | void tkttimeline_page(void){ |
| 892 | char *zTitle; |
| 893 | const char *zUuid; |
| 894 | int tagid; |
| 895 | char zGlobPattern[50]; |
| 896 | const char *zType; |
| 897 | |
| 898 | login_check_credentials(); |
| @@ -872,37 +923,11 @@ | |
| 923 | if( tagid==0 ){ |
| 924 | @ No such ticket: %h(zUuid) |
| 925 | style_footer(); |
| 926 | return; |
| 927 | } |
| 928 | tkt_draw_timeline(tagid, zType); |
| 929 | style_footer(); |
| 930 | } |
| 931 | |
| 932 | /* |
| 933 | ** WEBPAGE: tkthistory |
| 934 |