Index: src/backlink.c ================================================================== --- src/backlink.c +++ src/backlink.c @@ -179,11 +179,12 @@ rid = db_int(0, "SELECT rid FROM tagxref WHERE tagid=%d" " ORDER BY mtime DESC LIMIT 1", tagid); if( rid==0 ) return; pWiki = manifest_get(rid, CFTYPE_WIKI, 0); if( pWiki ){ - backlink_extract(pWiki->zWiki, pWiki->zMimetype, tagid, BKLNK_WIKI, + int mimetype = parse_mimetype( pWiki->zMimetype ); + backlink_extract(pWiki->zWiki, mimetype, tagid, BKLNK_WIKI, pWiki->rDate, 1); manifest_destroy(pWiki); } } @@ -295,17 +296,28 @@ markdown(&out, &in, &html_renderer); blob_reset(&out); blob_reset(&in); } +/* +** Transform mimetype string into an integer code. +** NOTE: In the sake of compatability empty string is parsed as MT_UNKNOWN; +** it is yet unclear whether it can safely be changed to MT_NONE. +*/ +int parse_mimetype(const char* zMimetype){ + if( zMimetype==0 ) return MT_NONE; + if( strstr(zMimetype,"wiki")!=0 ) return MT_WIKI; + if( strstr(zMimetype,"markdown")!=0 ) return MT_MARKDOWN; + return MT_UNKNOWN; +} /* ** Parse text looking for hyperlinks. Insert references into the ** BACKLINK table. */ void backlink_extract( char *zSrc, /* Input text from which links are extracted */ - const char *zMimetype, /* Mimetype of input. NULL means fossil-wiki */ + int mimetype, /* Mimetype of input. MT_NONE works as MT_WIKI */ int srcid, /* srcid for the source document */ int srctype, /* One of BKLNK_*. 0=comment 1=ticket 2=wiki */ double mtime, /* mtime field for new BACKLINK table entries */ int replaceFlag /* True to overwrite prior BACKLINK entries */ ){ @@ -314,15 +326,16 @@ db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d", srctype, srcid); } bklnk.srcid = srcid; assert( ValidBklnk(srctype) ); + assert( ValidMTC(mimetype) ); bklnk.srctype = srctype; bklnk.mtime = mtime; - if( zMimetype==0 || strstr(zMimetype,"wiki")!=0 ){ + if( mimetype==MT_NONE || mimetype==MT_WIKI ){ wiki_extract_links(zSrc, &bklnk, srctype==BKLNK_COMMENT ? WIKI_INLINE : 0); - }else if( strstr(zMimetype,"markdown")!=0 ){ + }else if( mimetype==MT_MARKDOWN ){ markdown_extract_links(zSrc, &bklnk); } } /* @@ -340,10 +353,11 @@ ** --mimetype TYPE Use an alternative mimetype. */ void test_backlinks_cmd(void){ const char *zMTime = find_option("mtime",0,1); const char *zMimetype = find_option("mimetype",0,1); + const int mimetype = parse_mimetype(zMimetype); Blob in; int srcid; int srctype; double mtime; @@ -371,11 +385,11 @@ " ' srcid='||quote(new.srcid)||" " ' mtime='||datetime(new.mtime));\n" " SELECT raise(ignore);\n" "END;" ); - backlink_extract(blob_str(&in),zMimetype,srcid,srctype,mtime,0); + backlink_extract(blob_str(&in),mimetype,srcid,srctype,mtime,0); blob_reset(&in); } /* Index: src/manifest.c ================================================================== --- src/manifest.c +++ src/manifest.c @@ -2395,11 +2395,11 @@ rid, p->zUser, p->zComment, TAG_BGCOLOR, rid, TAG_USER, rid, TAG_COMMENT, rid, p->rDate ); - backlink_extract(zCom, 0, rid, BKLNK_COMMENT, p->rDate, 1); + backlink_extract(zCom, MT_NONE, rid, BKLNK_COMMENT, p->rDate, 1); fossil_free(zCom); /* If this is a delta-manifest, record the fact that this repository ** contains delta manifests, to free the "commit" logic to generate ** new delta manifests. @@ -2853,11 +2853,12 @@ p->rDate, rid, p->zUser, zFType, zTitle ); fossil_free(zTitle); } if( p->zWiki[0] ){ - backlink_extract(p->zWiki, p->zMimetype, rid, BKLNK_FORUM, p->rDate, 1); + int mimetype = parse_mimetype(p->zMimetype); + backlink_extract(p->zWiki, mimetype, rid, BKLNK_FORUM, p->rDate, 1); } } db_end_transaction(0); if( permitHooks ){ Index: src/schema.c ================================================================== --- src/schema.c +++ src/schema.c @@ -489,10 +489,21 @@ # define BKLNK_EVENT 3 /* Technote */ # define BKLNK_FORUM 4 /* Forum post */ # define ValidBklnk(X) (X>=0 && X<=4) /* True if backlink.srctype is valid */ #endif +/* +** Allowed values for MIMEtype codes +*/ +#if INTERFACE +# define MT_NONE 0 /* unspecified */ +# define MT_WIKI 1 /* Wiki */ +# define MT_MARKDOWN 2 /* Markdonw */ +# define MT_UNKNOWN 3 /* unknown */ +# define ValidMTC(X) ((X)>=0 && (X)<=3) /* True if MIMEtype code is valid */ +#endif + /* ** Predefined tagid values */ #if INTERFACE # define TAG_BGCOLOR 1 /* Set the background color for display */ Index: src/tag.c ================================================================== --- src/tag.c +++ src/tag.c @@ -220,11 +220,11 @@ if( zCol ){ db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d", zCol, zValue, rid); if( tagid==TAG_COMMENT ){ char *zCopy = mprintf("%s", zValue); - backlink_extract(zCopy, 0, rid, BKLNK_COMMENT, mtime, 1); + backlink_extract(zCopy, MT_NONE, rid, BKLNK_COMMENT, mtime, 1); free(zCopy); } } if( tagid==TAG_DATE ){ db_multi_exec("UPDATE event " Index: src/tkt.c ================================================================== --- src/tkt.c +++ src/tkt.c @@ -40,10 +40,13 @@ 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){ @@ -70,11 +73,11 @@ ** 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; + int i, noRegularMimetype; static int once = 0; if( once ) return; once = 1; db_prepare(&q, "PRAGMA table_info(ticket)"); while( db_step(&q)==SQLITE_ROW ){ @@ -115,13 +118,25 @@ aField[nField].mUsed = USEDBY_TICKETCHNG; nField++; } db_finalize(&q); qsort(aField, nField, sizeof(aField[0]), nameCmpr); + noRegularMimetype = 1; for(i=0; izTicketUuid); tktid = db_last_insert_rowid(); @@ -236,27 +251,30 @@ 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 ){ - zMimetype = p->aField[i].zValue; - } - } - if( rid>0 ){ - for(i=0; inField; i++){ - const char *zName = p->aField[i].zName; - const char *zBaseName = zName[0]=='+' ? zName+1 : zName; - j = fieldId(zBaseName); - if( j<0 ) continue; - backlink_extract(p->aField[i].zValue, zMimetype, rid, BKLNK_TICKET, - p->rDate, i==0); + const char *zMimetype = p->aField[i].zValue; + /* "mimetype" is a regular column => these two flags must be 0 */ + assert(!useTicketGenMt); + assert(!useTicketChngGenMt); + mimetype_tkt = mimetype_tktchng = parse_mimetype( zMimetype ); } } blob_append_sql(&sql1, " WHERE tkt_id=%d", tktid); + if( useTicketGenMt ){ + blob_append_literal(&sql1, " RETURNING mimetype"); + } db_prepare(&q, "%s", blob_sql_text(&sql1)); db_bind_double(&q, ":mtime", p->rDate); db_step(&q); + if( useTicketGenMt ){ + mimetype_tkt = parse_mimetype( db_column_text(&q,0) ); + if( !useTicketChngGenMt ){ + mimetype_tktchng = mimetype_tkt; + } + } db_finalize(&q); blob_reset(&sql1); if( blob_size(&sql2)>0 || haveTicketChngRid || haveTicketChngUser ){ int fromTkt = 0; if( haveTicketChngRid ){ @@ -278,25 +296,55 @@ blob_append_sql(&sql3, ",\"%w\"", z); } } if( fromTkt ){ db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" - "SELECT %d,:mtime%s FROM ticket WHERE tkt_id=%d", + "SELECT %d,:mtime%s FROM ticket WHERE tkt_id=%d%s", blob_sql_text(&sql2), tktid, - blob_sql_text(&sql3), tktid); + blob_sql_text(&sql3), tktid, + useTicketChngGenMt ? " RETURNING mimetype" : ""); }else{ db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" - "VALUES(%d,:mtime%s)", - blob_sql_text(&sql2), tktid, blob_sql_text(&sql3)); + "VALUES(%d,:mtime%s)%s", + blob_sql_text(&sql2), tktid, blob_sql_text(&sql3), + useTicketChngGenMt ? " RETURNING mimetype" : ""); } db_bind_double(&q, ":mtime", p->rDate); db_step(&q); + if( useTicketChngGenMt ){ + mimetype_tktchng = parse_mimetype( db_column_text(&q, 0) ); + /* substitute NULL with a value generated within another table */ + if( !useTicketGenMt ){ + mimetype_tkt = mimetype_tktchng; + }else if( mimetype_tktchng==MT_NONE ){ + mimetype_tktchng = mimetype_tkt; + }else if( mimetype_tkt==MT_NONE ){ + mimetype_tkt = mimetype_tktchng; + } + } db_finalize(&q); } blob_reset(&sql2); blob_reset(&sql3); fossil_free(aUsed); + if( rid>0 ){ /* extract backlinks */ + int bReplace = 1, mimetype; + for(i=0; inField; i++){ + const char *zName = p->aField[i].zName; + const char *zBaseName = zName[0]=='+' ? zName+1 : zName; + j = fieldId(zBaseName); + if( j<0 ) continue; + if( aField[j].mUsed & USEDBY_TICKETCHNG ){ + mimetype = mimetype_tktchng; + }else{ + mimetype = mimetype_tkt; + } + backlink_extract(p->aField[i].zValue, mimetype, rid, BKLNK_TICKET, + p->rDate, bReplace); + bReplace = 0; + } + } return tktid; } /* ** Returns non-zero if moderation is required for ticket changes and ticket