Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From 6f70a236ce66fa2a To c3e7ed308900482e
2022-08-29
| ||
14:27 | Add a missing closing LI tag for the /tkthistory page. ... (check-in: 2d0b2bda user: george tags: deltify-tkt-blobs) | |
14:03 |
Minor optimization within getAllTicketFields() function.
...
(check-in: c3e7ed30 user: george tags: deltify-tkt-blobs)
| |
2022-08-23
| ||
11:14 | Correct a NULL being passed to strcmp() which caused any submit of JS script code in the skin editor to segfault. Reported in forum post 9d9f0580fd. ... (check-in: a8847839 user: stephan tags: trunk) | |
2022-08-22
| ||
18:27 |
Make it possible to store similar ticket change artifacts as deltas.
This might be useful when a certain column of the TICKET table
holds a lengthy text that may undergo frequent modifications.
This is an opt-in feature. It is activated only when TICKET table
contains a phony | |
2022-08-18
| ||
13:21 | Add the "Timeline" submenu link on the setup_edit page, for ordinary users. Change the "Access Log" link on that same page so that it is only present for ordinary users - not special users like "reader" or "developer". ... (check-in: 6f70a236 user: drh tags: trunk) | |
2022-08-17
| ||
05:30 | Removed a digression in the gitusers doc about Fossil's new clone-and-open mechanisms. That got moved to the ckout-workflows doc quite some time back, and we already point to it from that same section. There's no reason for the redundancy. Also cleaned up some grammar and typos while in there. ... (check-in: f43eaf01 user: wyoung tags: trunk) | |
Changes to src/tkt.c.
︙ | ︙ | |||
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | ** use. The internal-use fields begin with "tkt_". */ static int nField = 0; static struct tktFieldInfo { char *zName; /* Name of the database field */ char *zValue; /* Value to store */ char *zAppend; /* Value to append */ unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */ } *aField; #define USEDBY_TICKET 01 #define USEDBY_TICKETCHNG 02 #define USEDBY_BOTH 03 static u8 haveTicket = 0; /* True if the TICKET table exists */ static u8 haveTicketCTime = 0; /* True if TICKET.TKT_CTIME exists */ static u8 haveTicketChng = 0; /* True if the TICKETCHNG table exists */ static u8 haveTicketChngRid = 0; /* True if TICKETCHNG.TKT_RID exists */ static u8 haveTicketChngUser = 0;/* True if TICKETCHNG.TKT_USER exists */ static u8 useTicketGenMt = 0; /* use generated TICKET.MIMETYPE */ static u8 useTicketChngGenMt = 0;/* use generated TICKETCHNG.MIMETYPE */ /* ** Compare two entries in aField[] for sorting purposes */ static int nameCmpr(const void *a, const void *b){ return fossil_strcmp(((const struct tktFieldInfo*)a)->zName, | > > > > > | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | ** use. The internal-use fields begin with "tkt_". */ static int nField = 0; static struct tktFieldInfo { char *zName; /* Name of the database field */ char *zValue; /* Value to store */ char *zAppend; /* Value to append */ char *zBsln; /* "baseline for $zName" if that field exists*/ unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */ } *aField; #define USEDBY_TICKET 01 #define USEDBY_TICKETCHNG 02 #define USEDBY_BOTH 03 #define JCARD_ASSIGN ('=') #define JCARD_APPEND ('+') #define JCARD_PRIVATE ('p') static u8 haveTicket = 0; /* True if the TICKET table exists */ static u8 haveTicketCTime = 0; /* True if TICKET.TKT_CTIME exists */ static u8 haveTicketChng = 0; /* True if the TICKETCHNG table exists */ static u8 haveTicketChngRid = 0; /* True if TICKETCHNG.TKT_RID exists */ static u8 haveTicketChngUser = 0;/* True if TICKETCHNG.TKT_USER exists */ static u8 useTicketGenMt = 0; /* use generated TICKET.MIMETYPE */ static u8 useTicketChngGenMt = 0;/* use generated TICKETCHNG.MIMETYPE */ static int nTicketBslns = 0; /* number of valid "baseline for ..." */ /* ** Compare two entries in aField[] for sorting purposes */ static int nameCmpr(const void *a, const void *b){ return fossil_strcmp(((const struct tktFieldInfo*)a)->zName, |
︙ | ︙ | |||
71 72 73 74 75 76 77 | ** in sorted order in aField[]. ** ** The haveTicket and haveTicketChng variables are set to 1 if the TICKET and ** TICKETCHANGE tables exist, respectively. */ static void getAllTicketFields(void){ Stmt q; | | > > > > > > > > > > > > > > > > > > > > > > > > > > | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | ** in sorted order in aField[]. ** ** The haveTicket and haveTicketChng variables are set to 1 if the TICKET and ** TICKETCHANGE tables exist, respectively. */ static void getAllTicketFields(void){ Stmt q; int i, noRegularMimetype, nBaselines; static int once = 0; if( once ) return; once = 1; nBaselines = 0; db_prepare(&q, "PRAGMA table_info(ticket)"); while( db_step(&q)==SQLITE_ROW ){ const char *zFieldName = db_column_text(&q, 1); haveTicket = 1; if( memcmp(zFieldName,"tkt_",4)==0 ){ if( strcmp(zFieldName, "tkt_ctime")==0 ) haveTicketCTime = 1; continue; } if( memcmp(zFieldName,"baseline for ",13)==0 ){ if( strcmp(db_column_text(&q,2),"INTEGER")==0 ){ nBaselines++; } continue; } if( strchr(zFieldName,' ')!=0 ) continue; if( nField%10==0 ){ aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) ); } aField[nField].zBsln = 0; aField[nField].zName = mprintf("%s", zFieldName); aField[nField].mUsed = USEDBY_TICKET; nField++; } db_finalize(&q); if( nBaselines ){ db_prepare(&q, "SELECT 1 FROM pragma_table_info('ticket') " "WHERE type = 'INTEGER' AND name = :n"); for(i=0; i<nField && nBaselines!=0; i++){ char *zBsln = mprintf("baseline for %s",aField[i].zName); db_bind_text(&q, ":n", zBsln); if( db_step(&q)==SQLITE_ROW ){ aField[i].zBsln = zBsln; nTicketBslns++; nBaselines--; }else{ free(zBsln); } db_reset(&q); } db_finalize(&q); } db_prepare(&q, "PRAGMA table_info(ticketchng)"); while( db_step(&q)==SQLITE_ROW ){ const char *zFieldName = db_column_text(&q, 1); haveTicketChng = 1; if( memcmp(zFieldName,"tkt_",4)==0 ){ if( strcmp(zFieldName+4,"rid")==0 ){ haveTicketChngRid = 1; /* tkt_rid */ }else if( strcmp(zFieldName+4,"user")==0 ){ haveTicketChngUser = 1; /* tkt_user */ } continue; } if( strchr(zFieldName,' ')!=0 ) continue; if( (i = fieldId(zFieldName))>=0 ){ aField[i].mUsed |= USEDBY_TICKETCHNG; continue; } if( nField%10==0 ){ aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) ); } aField[nField].zBsln = 0; aField[nField].zName = mprintf("%s", zFieldName); aField[nField].mUsed = USEDBY_TICKETCHNG; nField++; } db_finalize(&q); qsort(aField, nField, sizeof(aField[0]), nameCmpr); noRegularMimetype = 1; |
︙ | ︙ | |||
230 231 232 233 234 235 236 | blob_zero(&sql1); blob_zero(&sql2); blob_zero(&sql3); blob_append_sql(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime"); if( haveTicketCTime ){ blob_append_sql(&sql1, ", tkt_ctime=coalesce(tkt_ctime,:mtime)"); } | | < > > > > | 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | blob_zero(&sql1); blob_zero(&sql2); blob_zero(&sql3); blob_append_sql(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime"); if( haveTicketCTime ){ blob_append_sql(&sql1, ", tkt_ctime=coalesce(tkt_ctime,:mtime)"); } aUsed = fossil_malloc_zero( nField ); for(i=0; i<p->nField; i++){ const char * const zName = p->aField[i].zName; const char * const zBaseName = zName[0]=='+' ? zName+1 : zName; j = fieldId(zBaseName); if( j<0 ) continue; aUsed[j] = 1; if( aField[j].mUsed & USEDBY_TICKET ){ if( zName[0]=='+' ){ blob_append_sql(&sql1,", \"%w\"=coalesce(\"%w\",'') || %Q", zBaseName, zBaseName, p->aField[i].zValue); /* when appending keep "baseline for ..." unchanged */ }else{ blob_append_sql(&sql1,", \"%w\"=%Q", zBaseName, p->aField[i].zValue); if( aField[j].zBsln ){ blob_append_sql(&sql1,", \"%w\"=%d", aField[j].zBsln, rid); } } } if( aField[j].mUsed & USEDBY_TICKETCHNG ){ blob_append_sql(&sql2, ",\"%w\"", zBaseName); blob_append_sql(&sql3, ",%Q", p->aField[i].zValue); } if( strcmp(zBaseName,"mimetype")==0 ){ |
︙ | ︙ | |||
740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 | /* ** Write a ticket into the repository. */ static int ticket_put( Blob *pTicket, /* The text of the ticket change record */ const char *zTktId, /* The ticket to which this change is applied */ int needMod /* True if moderation is needed */ ){ int result; int rid; manifest_crosslink_begin(); rid = content_put_ex(pTicket, 0, 0, 0, needMod); if( rid==0 ){ fossil_fatal("trouble committing ticket: %s", g.zErrMsg); } if( needMod ){ moderation_table_create(); db_multi_exec( "INSERT INTO modreq(objid, tktid) VALUES(%d,%Q)", rid, zTktId ); }else{ | > > > > > > > > > > > > > > > > | 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 | /* ** Write a ticket into the repository. */ static int ticket_put( Blob *pTicket, /* The text of the ticket change record */ const char *zTktId, /* The ticket to which this change is applied */ const char *aUsed, /* Indicators for fields' modifications */ int needMod /* True if moderation is needed */ ){ int result; int rid; manifest_crosslink_begin(); rid = content_put_ex(pTicket, 0, 0, 0, needMod); if( rid==0 ){ fossil_fatal("trouble committing ticket: %s", g.zErrMsg); } if( nTicketBslns ){ int i, s, buf[8], nSrc=0, *aSrc=&(buf[0]); if( nTicketBslns > count(buf) ){ aSrc = (int*)fossil_malloc(sizeof(int)*nTicketBslns); } for(i=0; i<nField; i++){ if( aField[i].zBsln && aUsed[i]==JCARD_ASSIGN ){ s = db_int(0,"SELECT \"%w\" FROM ticket WHERE tkt_uuid = '%q'", aField[i].zBsln, zTktId ); if( s > 0 ) aSrc[nSrc++] = s; } } if( nSrc ) content_deltify(rid, aSrc, nSrc, 0); if( aSrc!=&(buf[0]) ) fossil_free( aSrc ); } if( needMod ){ moderation_table_create(); db_multi_exec( "INSERT INTO modreq(objid, tktid) VALUES(%d,%Q)", rid, zTktId ); }else{ |
︙ | ︙ | |||
785 786 787 788 789 790 791 | static int submitTicketCmd( Th_Interp *interp, void *pUuid, int argc, const char **argv, int *argl ){ | | | > > > > | < | > > > | | 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 | static int submitTicketCmd( Th_Interp *interp, void *pUuid, int argc, const char **argv, int *argl ){ char *zDate, *aUsed; const char *zUuid; int i; int nJ = 0, rc = TH_OK; Blob tktchng, cksum; int needMod; login_verify_csrf_secret(); if( !captcha_is_correct(0) ){ @ <p class="generalError">Error: Incorrect security code.</p> return TH_OK; } zUuid = (const char *)pUuid; blob_zero(&tktchng); zDate = date_in_standard_format("now"); blob_appendf(&tktchng, "D %s\n", zDate); free(zDate); aUsed = fossil_malloc_zero( nField ); for(i=0; i<nField; i++){ if( aField[i].zAppend ){ blob_appendf(&tktchng, "J +%s %z\n", aField[i].zName, fossilize(aField[i].zAppend, -1)); ++nJ; aUsed[i] = JCARD_APPEND; } } for(i=0; i<nField; i++){ const char *zValue; int nValue; if( aField[i].zAppend ) continue; zValue = Th_Fetch(aField[i].zName, &nValue); if( zValue ){ while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; } if( ((aField[i].mUsed & USEDBY_TICKETCHNG)!=0 && nValue>0) || memcmp(zValue, aField[i].zValue, nValue)!=0 || strlen(aField[i].zValue)!=nValue ){ if( memcmp(aField[i].zName, "private_", 8)==0 ){ zValue = db_conceal(zValue, nValue); blob_appendf(&tktchng, "J %s %s\n", aField[i].zName, zValue); aUsed[i] = JCARD_PRIVATE; }else{ blob_appendf(&tktchng, "J %s %#F\n", aField[i].zName, nValue, zValue); aUsed[i] = JCARD_ASSIGN; } nJ++; } } } if( *(char**)pUuid ){ zUuid = db_text(0, "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%q*'", P("name") ); }else{ zUuid = db_text(0, "SELECT lower(hex(randomblob(20)))"); } *(const char**)pUuid = zUuid; blob_appendf(&tktchng, "K %s\n", zUuid); blob_appendf(&tktchng, "U %F\n", login_name()); md5sum_blob(&tktchng, &cksum); blob_appendf(&tktchng, "Z %b\n", &cksum); if( nJ==0 ){ blob_reset(&tktchng); goto finish; } needMod = ticket_need_moderation(0); if( g.zPath[0]=='d' ){ const char *zNeedMod = needMod ? "required" : "skipped"; /* If called from /debug_tktnew or /debug_tktedit... */ @ <div style="color:blue"> @ <p>Ticket artifact that would have been submitted:</p> @ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote> @ <blockquote><pre>Moderation would be %h(zNeedMod).</pre></blockquote> @ </div> @ <hr /> }else{ if( g.thTrace ){ Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n" "}<br />\n", blob_str(&tktchng)); } ticket_put(&tktchng, zUuid, aUsed, needMod); rc = ticket_change(zUuid); } finish: fossil_free( aUsed ); return rc; } /* ** WEBPAGE: tktnew ** WEBPAGE: debug_tktnew ** |
︙ | ︙ | |||
1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 | rptshow( zRep, zSep, zFilterUuid, tktEncoding ); } }else{ /* add a new ticket or update an existing ticket */ enum { set,add,history,err } eCmd = err; int i = 0; Blob tktchng, cksum; /* get command type (set/add) and get uuid, if needed for set */ if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 || strncmp(g.argv[2],"history",n)==0 ){ if( strncmp(g.argv[2],"history",n)==0 ){ eCmd = history; }else{ | > | 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 | rptshow( zRep, zSep, zFilterUuid, tktEncoding ); } }else{ /* add a new ticket or update an existing ticket */ enum { set,add,history,err } eCmd = err; int i = 0; Blob tktchng, cksum; char *aUsed; /* get command type (set/add) and get uuid, if needed for set */ if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 || strncmp(g.argv[2],"history",n)==0 ){ if( strncmp(g.argv[2],"history",n)==0 ){ eCmd = history; }else{ |
︙ | ︙ | |||
1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 | if( append ){ aField[j].zAppend = zFValue; }else{ aField[j].zValue = zFValue; } } } /* now add the needed artifacts to the repository */ blob_zero(&tktchng); /* add the time to the ticket manifest */ blob_appendf(&tktchng, "D %s\n", zDate); /* append defined elements */ for(i=0; i<nField; i++){ char *zValue = 0; char *zPfx; if( aField[i].zAppend && aField[i].zAppend[0] ){ zPfx = " +"; zValue = aField[i].zAppend; }else if( aField[i].zValue && aField[i].zValue[0] ){ zPfx = " "; zValue = aField[i].zValue; }else{ continue; } if( memcmp(aField[i].zName, "private_", 8)==0 ){ zValue = db_conceal(zValue, strlen(zValue)); blob_appendf(&tktchng, "J%s%s %s\n", zPfx, aField[i].zName, zValue); }else{ blob_appendf(&tktchng, "J%s%s %#F\n", zPfx, aField[i].zName, strlen(zValue), zValue); } } blob_appendf(&tktchng, "K %s\n", zTktUuid); blob_appendf(&tktchng, "U %F\n", zUser); md5sum_blob(&tktchng, &cksum); blob_appendf(&tktchng, "Z %b\n", &cksum); | > > > > | > > | 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 | if( append ){ aField[j].zAppend = zFValue; }else{ aField[j].zValue = zFValue; } } } aUsed = fossil_malloc_zero( nField ); /* now add the needed artifacts to the repository */ blob_zero(&tktchng); /* add the time to the ticket manifest */ blob_appendf(&tktchng, "D %s\n", zDate); /* append defined elements */ for(i=0; i<nField; i++){ char *zValue = 0; char *zPfx; if( aField[i].zAppend && aField[i].zAppend[0] ){ zPfx = " +"; zValue = aField[i].zAppend; aUsed[i] = JCARD_APPEND; }else if( aField[i].zValue && aField[i].zValue[0] ){ zPfx = " "; zValue = aField[i].zValue; aUsed[i] = JCARD_ASSIGN; }else{ continue; } if( memcmp(aField[i].zName, "private_", 8)==0 ){ zValue = db_conceal(zValue, strlen(zValue)); blob_appendf(&tktchng, "J%s%s %s\n", zPfx, aField[i].zName, zValue); aUsed[i] = JCARD_PRIVATE; }else{ blob_appendf(&tktchng, "J%s%s %#F\n", zPfx, aField[i].zName, strlen(zValue), zValue); } } blob_appendf(&tktchng, "K %s\n", zTktUuid); blob_appendf(&tktchng, "U %F\n", zUser); md5sum_blob(&tktchng, &cksum); blob_appendf(&tktchng, "Z %b\n", &cksum); if( ticket_put(&tktchng, zTktUuid, aUsed, ticket_need_moderation(1) )==0 ){ fossil_fatal("%s", g.zErrMsg); }else{ fossil_print("ticket %s succeeded for %s\n", (eCmd==set?"set":"add"),zTktUuid); } fossil_free( aUsed ); } } } #if INTERFACE /* Standard submenu items for wiki pages */ |
︙ | ︙ |