Fossil SCM

If a ticket definition has a field named "mimetype" then use the specified mimetype when parsing ticket content to extract backlinks. Add the ability to extract backlinks from markdown-formatted text. Add the /test-backlinks webpage and the test-backlink command for debugging.

drh 2020-04-16 16:52 backlink-updates
Commit 7c13a57358ae16a764900ac11d30aebf6bff8dbdfa8cdb4acdccf093e20b3574
+240 -1
--- src/backlink.c
+++ src/backlink.c
@@ -56,11 +56,11 @@
5656
0, 0, 0, 0, 0, 0);
5757
db_finalize(&q);
5858
}
5959
6060
/*
61
-** WEBPAGE: test-backlinks
61
+** WEBPAGE: test-backlink-timeline
6262
**
6363
** Show a timeline of all check-ins and other events that have entries
6464
** in the backlink table. This is used for testing the rendering
6565
** of the "References" section of the /info page.
6666
*/
@@ -88,5 +88,244 @@
8888
www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
8989
0, 0, 0, 0, 0, 0);
9090
db_finalize(&q);
9191
style_footer();
9292
}
93
+
94
+/*
95
+** WEBPAGE: test-backlinks
96
+**
97
+** Show a table of all backlinks. Admin access only.
98
+*/
99
+void backlink_table_page(void){
100
+ Stmt q;
101
+ int n;
102
+ login_check_credentials();
103
+ if( !g.perm.Admin ){
104
+ login_needed(g.anon.Admin);
105
+ return;
106
+ }
107
+ style_header("Backlink Table (Internal Testing Use)");
108
+ n = db_int(0, "SELECT count(*) FROM backlink");
109
+ @ <p>%d(n) backlink table entries:</p>
110
+ db_prepare(&q,
111
+ "SELECT target, srctype, srcid, datetime(mtime) FROM backlink"
112
+ );
113
+ style_table_sorter();
114
+ @ <table border="1" cellpadding="2" cellspacing="0" \
115
+ @ class='sortable' data-column-types='ttt' data-init-sort='0'>
116
+ @ <thead><tr><th> Source <th> Target <th> mtime </tr></thead>
117
+ @ <tbody>
118
+ while( db_step(&q)==SQLITE_ROW ){
119
+ const char *zTarget = db_column_text(&q, 0);
120
+ int srctype = db_column_int(&q, 1);
121
+ int srcid = db_column_int(&q, 2);
122
+ const char *zMtime = db_column_text(&q, 3);
123
+ static const char *azSrcType[] = { "comment", "ticket", "wiki", "unknown" };
124
+ @ <tr><td><a href="%R/info/%h(zTarget)">%h(zTarget)</a>
125
+ switch( srctype ){
126
+ case BKLNK_COMMENT: {
127
+ @ <td><a href="%R/info?name=rid:%d(srcid)">comment-%d(srcid)</a>
128
+ break;
129
+ }
130
+ case BKLNK_TICKET: {
131
+ @ <td><a href="%R/info?name=rid:%d(srcid)">ticket-%d(srcid)</a>
132
+ break;
133
+ }
134
+ case BKLNK_WIKI: {
135
+ @ <td><a href="%R/info?name=rid:%d(srcid)">wiki-%d(srcid)</a>
136
+ break;
137
+ }
138
+ default: {
139
+ @ <td>unknown(%d(srctype)) - %d(srcid)
140
+ break;
141
+ }
142
+ }
143
+ @ <td>%h(zMtime)</tr>
144
+ }
145
+ @ </tbody>
146
+ @ </table>
147
+ db_finalize(&q);
148
+ style_footer();
149
+}
150
+
151
+
152
+
153
+/*
154
+** Structure used to pass down state information through the
155
+** markup formatters into the BACKLINK generator.
156
+*/
157
+#if INTERFACE
158
+struct Backlink {
159
+ int srcid; /* srcid for the source document */
160
+ int srctype; /* One of BKLNK_*. 0=comment 1=ticket 2=wiki */
161
+ double mtime; /* mtime field for new BACKLINK table entries */
162
+};
163
+#endif
164
+
165
+
166
+/*
167
+** zTarget is a hyperlink target in some markup format. If this
168
+** target is a self-reference to some other object in the repository,
169
+** then create an appropriate backlink.
170
+*/
171
+void backlink_create(Backlink *p, const char *zTarget, int nTarget){
172
+ char zLink[HNAME_MAX+4];
173
+ if( zTarget==0 ) return;
174
+ if( nTarget<4 ) return;
175
+ if( nTarget>=10 && strncmp(zTarget,"/info/",6)==0 ){
176
+ zTarget += 6;
177
+ nTarget -= 6;
178
+ }
179
+ if( nTarget>HNAME_MAX ) return;
180
+ if( !validate16(zTarget, nTarget) ) return;
181
+ memcpy(zLink, zTarget, nTarget);
182
+ zLink[nTarget] = 0;
183
+ canonical16(zLink, nTarget);
184
+ db_multi_exec(
185
+ "REPLACE INTO backlink(target,srctype,srcid,mtime)"
186
+ "VALUES(%Q,%d,%d,%.17g)", zLink, p->srctype, p->srcid, p->mtime
187
+ );
188
+}
189
+
190
+/*
191
+** This routine is called by the markdown formatter for each hyperlink.
192
+** If the hyperlink is a backlink, add it to the BACKLINK table.
193
+*/
194
+static int backlink_md_link(
195
+ Blob *ob, /* Write output text here (not used in this case) */
196
+ Blob *target, /* The hyperlink target */
197
+ Blob *title, /* Hyperlink title */
198
+ Blob *content, /* Content of the link */
199
+ void *opaque
200
+){
201
+ Backlink *p = (Backlink*)opaque;
202
+ char *zTarget = blob_buffer(target);
203
+ int nTarget = blob_size(target);
204
+
205
+ backlink_create(p, zTarget, nTarget);
206
+ return 1;
207
+}
208
+
209
+/*
210
+** Scan markdown text and add self-hyperlinks to the BACKLINK table.
211
+*/
212
+void markdown_extract_links(
213
+ char *zInputText,
214
+ Backlink *p
215
+){
216
+ struct mkd_renderer html_renderer = {
217
+ 0, /* prolog */
218
+ 0, /* epilog */
219
+ 0, /* blockcode */
220
+ 0, /* blockquote */
221
+ 0, /* raw_block */
222
+ 0, /* header */
223
+ 0, /* hrule */
224
+ 0, /* list */
225
+ 0, /* list_item */
226
+ 0, /* paragraph */
227
+ 0, /* table */
228
+ 0, /* table_cell */
229
+ 0, /* table_row */
230
+ 0, /* autolink */
231
+ 0, /* code_span */
232
+ 0, /* double-emphasis */
233
+ 0, /* emphasis */
234
+ 0, /* image */
235
+ 0, /* line_break */
236
+ backlink_md_link, /* link */
237
+ 0, /* raw_span */
238
+ 0, /* triple_emphasis */
239
+ 0, /* entity */
240
+ 0, /* normal_text */
241
+ "*_", /* emphasis characters */
242
+ 0 /* client data */
243
+ };
244
+ Blob out, in;
245
+ html_renderer.opaque = (void*)p;
246
+ blob_init(&out, 0, 0);
247
+ blob_init(&in, zInputText, -1);
248
+ markdown(&out, &in, &html_renderer);
249
+ blob_reset(&out);
250
+ blob_reset(&in);
251
+}
252
+
253
+/*
254
+** Parse text looking for hyperlinks. Insert references into the
255
+** BACKLINK table.
256
+*/
257
+void backlink_extract(
258
+ char *zSrc, /* Input text from which links are extracted */
259
+ const char *zMimetype, /* Mimetype of input. NULL means fossil-wiki */
260
+ int srcid, /* srcid for the source document */
261
+ int srctype, /* One of BKLNK_*. 0=comment 1=ticket 2=wiki */
262
+ double mtime, /* mtime field for new BACKLINK table entries */
263
+ int replaceFlag /* True to overwrite prior BACKLINK entries */
264
+){
265
+ Backlink bklnk;
266
+ if( replaceFlag ){
267
+ db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
268
+ srctype, srcid);
269
+ }
270
+ bklnk.srcid = srcid;
271
+ assert( srctype>=BKLNK_COMMENT && srctype<=BKLNK_WIKI );
272
+ bklnk.srctype = srctype;
273
+ bklnk.mtime = mtime;
274
+ if( zMimetype==0 || strstr(zMimetype,"wiki")!=0 ){
275
+ wiki_extract_links(zSrc, &bklnk, srctype==BKLNK_COMMENT ? WIKI_INLINE : 0);
276
+ }else if( strstr(zMimetype,"markdown")!=0 ){
277
+ markdown_extract_links(zSrc, &bklnk);
278
+ }
279
+}
280
+
281
+/*
282
+** COMMAND: test-backlinks
283
+**
284
+** Usage: %fossil test-backlinks SRCID SRCTYPE ?OPTIONS? INPUT-FILE
285
+**
286
+** Read the content of INPUT-FILE and pass it into the backlink_extract()
287
+** routine. But instead of adding backlinks to the backlink table,
288
+** just print them on stdout. SRCID and SRCTYPE are integers.
289
+**
290
+** Options:
291
+** --mtime DATETIME Use an alternative date/time. Defaults to the
292
+** current date/time.
293
+** --mimetype TYPE Use an alternative mimetype.
294
+*/
295
+void test_backlinks_cmd(void){
296
+ const char *zMTime = find_option("mtime",0,1);
297
+ const char *zMimetype = find_option("mimetype",0,1);
298
+ Blob in;
299
+ int srcid;
300
+ int srctype;
301
+ double mtime;
302
+
303
+ verify_all_options();
304
+ if( g.argc!=5 ){
305
+ usage("SRCTYPE SRCID INPUTFILE");
306
+ }
307
+ srctype = atoi(g.argv[2]);
308
+ if( srctype<0 || srctype>2 ){
309
+ fossil_fatal("SRCTYPE should be a integer 0, 1, or 2");
310
+ }
311
+ srcid = atoi(g.argv[3]);
312
+ blob_read_from_file(&in, g.argv[4], ExtFILE);
313
+ sqlite3_open(":memory:",&g.db);
314
+ if( zMTime==0 ) zMTime = "now";
315
+ mtime = db_double(1721059.5,"SELECT julianday(%Q)",zMTime);
316
+ g.fSqlPrint = 1;
317
+ sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
318
+ db_multi_exec(
319
+ "CREATE TEMP TABLE backlink(target,srctype,srcid,mtime);\n"
320
+ "CREATE TRIGGER backlink_insert BEFORE INSERT ON backlink BEGIN\n"
321
+ " SELECT print("
322
+ " 'target='||quote(new.target)||"
323
+ " ' srctype='||quote(new.srctype)||"
324
+ " ' srcid='||quote(new.srcid)||"
325
+ " ' mtime='||datetime(new.mtime));\n"
326
+ " SELECT raise(ignore);\n"
327
+ "END;"
328
+ );
329
+ backlink_extract(blob_str(&in),zMimetype,srcid,srctype,mtime,0);
330
+ blob_reset(&in);
331
+}
93332
--- src/backlink.c
+++ src/backlink.c
@@ -56,11 +56,11 @@
56 0, 0, 0, 0, 0, 0);
57 db_finalize(&q);
58 }
59
60 /*
61 ** WEBPAGE: test-backlinks
62 **
63 ** Show a timeline of all check-ins and other events that have entries
64 ** in the backlink table. This is used for testing the rendering
65 ** of the "References" section of the /info page.
66 */
@@ -88,5 +88,244 @@
88 www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
89 0, 0, 0, 0, 0, 0);
90 db_finalize(&q);
91 style_footer();
92 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
--- src/backlink.c
+++ src/backlink.c
@@ -56,11 +56,11 @@
56 0, 0, 0, 0, 0, 0);
57 db_finalize(&q);
58 }
59
60 /*
61 ** WEBPAGE: test-backlink-timeline
62 **
63 ** Show a timeline of all check-ins and other events that have entries
64 ** in the backlink table. This is used for testing the rendering
65 ** of the "References" section of the /info page.
66 */
@@ -88,5 +88,244 @@
88 www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
89 0, 0, 0, 0, 0, 0);
90 db_finalize(&q);
91 style_footer();
92 }
93
94 /*
95 ** WEBPAGE: test-backlinks
96 **
97 ** Show a table of all backlinks. Admin access only.
98 */
99 void backlink_table_page(void){
100 Stmt q;
101 int n;
102 login_check_credentials();
103 if( !g.perm.Admin ){
104 login_needed(g.anon.Admin);
105 return;
106 }
107 style_header("Backlink Table (Internal Testing Use)");
108 n = db_int(0, "SELECT count(*) FROM backlink");
109 @ <p>%d(n) backlink table entries:</p>
110 db_prepare(&q,
111 "SELECT target, srctype, srcid, datetime(mtime) FROM backlink"
112 );
113 style_table_sorter();
114 @ <table border="1" cellpadding="2" cellspacing="0" \
115 @ class='sortable' data-column-types='ttt' data-init-sort='0'>
116 @ <thead><tr><th> Source <th> Target <th> mtime </tr></thead>
117 @ <tbody>
118 while( db_step(&q)==SQLITE_ROW ){
119 const char *zTarget = db_column_text(&q, 0);
120 int srctype = db_column_int(&q, 1);
121 int srcid = db_column_int(&q, 2);
122 const char *zMtime = db_column_text(&q, 3);
123 static const char *azSrcType[] = { "comment", "ticket", "wiki", "unknown" };
124 @ <tr><td><a href="%R/info/%h(zTarget)">%h(zTarget)</a>
125 switch( srctype ){
126 case BKLNK_COMMENT: {
127 @ <td><a href="%R/info?name=rid:%d(srcid)">comment-%d(srcid)</a>
128 break;
129 }
130 case BKLNK_TICKET: {
131 @ <td><a href="%R/info?name=rid:%d(srcid)">ticket-%d(srcid)</a>
132 break;
133 }
134 case BKLNK_WIKI: {
135 @ <td><a href="%R/info?name=rid:%d(srcid)">wiki-%d(srcid)</a>
136 break;
137 }
138 default: {
139 @ <td>unknown(%d(srctype)) - %d(srcid)
140 break;
141 }
142 }
143 @ <td>%h(zMtime)</tr>
144 }
145 @ </tbody>
146 @ </table>
147 db_finalize(&q);
148 style_footer();
149 }
150
151
152
153 /*
154 ** Structure used to pass down state information through the
155 ** markup formatters into the BACKLINK generator.
156 */
157 #if INTERFACE
158 struct Backlink {
159 int srcid; /* srcid for the source document */
160 int srctype; /* One of BKLNK_*. 0=comment 1=ticket 2=wiki */
161 double mtime; /* mtime field for new BACKLINK table entries */
162 };
163 #endif
164
165
166 /*
167 ** zTarget is a hyperlink target in some markup format. If this
168 ** target is a self-reference to some other object in the repository,
169 ** then create an appropriate backlink.
170 */
171 void backlink_create(Backlink *p, const char *zTarget, int nTarget){
172 char zLink[HNAME_MAX+4];
173 if( zTarget==0 ) return;
174 if( nTarget<4 ) return;
175 if( nTarget>=10 && strncmp(zTarget,"/info/",6)==0 ){
176 zTarget += 6;
177 nTarget -= 6;
178 }
179 if( nTarget>HNAME_MAX ) return;
180 if( !validate16(zTarget, nTarget) ) return;
181 memcpy(zLink, zTarget, nTarget);
182 zLink[nTarget] = 0;
183 canonical16(zLink, nTarget);
184 db_multi_exec(
185 "REPLACE INTO backlink(target,srctype,srcid,mtime)"
186 "VALUES(%Q,%d,%d,%.17g)", zLink, p->srctype, p->srcid, p->mtime
187 );
188 }
189
190 /*
191 ** This routine is called by the markdown formatter for each hyperlink.
192 ** If the hyperlink is a backlink, add it to the BACKLINK table.
193 */
194 static int backlink_md_link(
195 Blob *ob, /* Write output text here (not used in this case) */
196 Blob *target, /* The hyperlink target */
197 Blob *title, /* Hyperlink title */
198 Blob *content, /* Content of the link */
199 void *opaque
200 ){
201 Backlink *p = (Backlink*)opaque;
202 char *zTarget = blob_buffer(target);
203 int nTarget = blob_size(target);
204
205 backlink_create(p, zTarget, nTarget);
206 return 1;
207 }
208
209 /*
210 ** Scan markdown text and add self-hyperlinks to the BACKLINK table.
211 */
212 void markdown_extract_links(
213 char *zInputText,
214 Backlink *p
215 ){
216 struct mkd_renderer html_renderer = {
217 0, /* prolog */
218 0, /* epilog */
219 0, /* blockcode */
220 0, /* blockquote */
221 0, /* raw_block */
222 0, /* header */
223 0, /* hrule */
224 0, /* list */
225 0, /* list_item */
226 0, /* paragraph */
227 0, /* table */
228 0, /* table_cell */
229 0, /* table_row */
230 0, /* autolink */
231 0, /* code_span */
232 0, /* double-emphasis */
233 0, /* emphasis */
234 0, /* image */
235 0, /* line_break */
236 backlink_md_link, /* link */
237 0, /* raw_span */
238 0, /* triple_emphasis */
239 0, /* entity */
240 0, /* normal_text */
241 "*_", /* emphasis characters */
242 0 /* client data */
243 };
244 Blob out, in;
245 html_renderer.opaque = (void*)p;
246 blob_init(&out, 0, 0);
247 blob_init(&in, zInputText, -1);
248 markdown(&out, &in, &html_renderer);
249 blob_reset(&out);
250 blob_reset(&in);
251 }
252
253 /*
254 ** Parse text looking for hyperlinks. Insert references into the
255 ** BACKLINK table.
256 */
257 void backlink_extract(
258 char *zSrc, /* Input text from which links are extracted */
259 const char *zMimetype, /* Mimetype of input. NULL means fossil-wiki */
260 int srcid, /* srcid for the source document */
261 int srctype, /* One of BKLNK_*. 0=comment 1=ticket 2=wiki */
262 double mtime, /* mtime field for new BACKLINK table entries */
263 int replaceFlag /* True to overwrite prior BACKLINK entries */
264 ){
265 Backlink bklnk;
266 if( replaceFlag ){
267 db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
268 srctype, srcid);
269 }
270 bklnk.srcid = srcid;
271 assert( srctype>=BKLNK_COMMENT && srctype<=BKLNK_WIKI );
272 bklnk.srctype = srctype;
273 bklnk.mtime = mtime;
274 if( zMimetype==0 || strstr(zMimetype,"wiki")!=0 ){
275 wiki_extract_links(zSrc, &bklnk, srctype==BKLNK_COMMENT ? WIKI_INLINE : 0);
276 }else if( strstr(zMimetype,"markdown")!=0 ){
277 markdown_extract_links(zSrc, &bklnk);
278 }
279 }
280
281 /*
282 ** COMMAND: test-backlinks
283 **
284 ** Usage: %fossil test-backlinks SRCID SRCTYPE ?OPTIONS? INPUT-FILE
285 **
286 ** Read the content of INPUT-FILE and pass it into the backlink_extract()
287 ** routine. But instead of adding backlinks to the backlink table,
288 ** just print them on stdout. SRCID and SRCTYPE are integers.
289 **
290 ** Options:
291 ** --mtime DATETIME Use an alternative date/time. Defaults to the
292 ** current date/time.
293 ** --mimetype TYPE Use an alternative mimetype.
294 */
295 void test_backlinks_cmd(void){
296 const char *zMTime = find_option("mtime",0,1);
297 const char *zMimetype = find_option("mimetype",0,1);
298 Blob in;
299 int srcid;
300 int srctype;
301 double mtime;
302
303 verify_all_options();
304 if( g.argc!=5 ){
305 usage("SRCTYPE SRCID INPUTFILE");
306 }
307 srctype = atoi(g.argv[2]);
308 if( srctype<0 || srctype>2 ){
309 fossil_fatal("SRCTYPE should be a integer 0, 1, or 2");
310 }
311 srcid = atoi(g.argv[3]);
312 blob_read_from_file(&in, g.argv[4], ExtFILE);
313 sqlite3_open(":memory:",&g.db);
314 if( zMTime==0 ) zMTime = "now";
315 mtime = db_double(1721059.5,"SELECT julianday(%Q)",zMTime);
316 g.fSqlPrint = 1;
317 sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
318 db_multi_exec(
319 "CREATE TEMP TABLE backlink(target,srctype,srcid,mtime);\n"
320 "CREATE TRIGGER backlink_insert BEFORE INSERT ON backlink BEGIN\n"
321 " SELECT print("
322 " 'target='||quote(new.target)||"
323 " ' srctype='||quote(new.srctype)||"
324 " ' srcid='||quote(new.srcid)||"
325 " ' mtime='||datetime(new.mtime));\n"
326 " SELECT raise(ignore);\n"
327 "END;"
328 );
329 backlink_extract(blob_str(&in),zMimetype,srcid,srctype,mtime,0);
330 blob_reset(&in);
331 }
332
+1 -1
--- src/db.c
+++ src/db.c
@@ -2310,11 +2310,11 @@
23102310
** SQL functions for debugging.
23112311
**
23122312
** The print() function writes its arguments on stdout, but only
23132313
** if the -sqlprint command-line option is turned on.
23142314
*/
2315
-LOCAL void db_sql_print(
2315
+void db_sql_print(
23162316
sqlite3_context *context,
23172317
int argc,
23182318
sqlite3_value **argv
23192319
){
23202320
int i;
23212321
--- src/db.c
+++ src/db.c
@@ -2310,11 +2310,11 @@
2310 ** SQL functions for debugging.
2311 **
2312 ** The print() function writes its arguments on stdout, but only
2313 ** if the -sqlprint command-line option is turned on.
2314 */
2315 LOCAL void db_sql_print(
2316 sqlite3_context *context,
2317 int argc,
2318 sqlite3_value **argv
2319 ){
2320 int i;
2321
--- src/db.c
+++ src/db.c
@@ -2310,11 +2310,11 @@
2310 ** SQL functions for debugging.
2311 **
2312 ** The print() function writes its arguments on stdout, but only
2313 ** if the -sqlprint command-line option is turned on.
2314 */
2315 void db_sql_print(
2316 sqlite3_context *context,
2317 int argc,
2318 sqlite3_value **argv
2319 ){
2320 int i;
2321
+1 -1
--- src/manifest.c
+++ src/manifest.c
@@ -2162,11 +2162,11 @@
21622162
TAG_USER, rid,
21632163
TAG_COMMENT, rid, p->rDate
21642164
);
21652165
zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event"
21662166
" WHERE rowid=last_insert_rowid()");
2167
- wiki_extract_links(zCom, rid, BKLNK_COMMENT, p->rDate, 1, WIKI_INLINE);
2167
+ backlink_extract(zCom, 0, rid, BKLNK_COMMENT, p->rDate, 1);
21682168
fossil_free(zCom);
21692169
21702170
/* If this is a delta-manifest, record the fact that this repository
21712171
** contains delta manifests, to free the "commit" logic to generate
21722172
** new delta manifests.
21732173
--- src/manifest.c
+++ src/manifest.c
@@ -2162,11 +2162,11 @@
2162 TAG_USER, rid,
2163 TAG_COMMENT, rid, p->rDate
2164 );
2165 zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event"
2166 " WHERE rowid=last_insert_rowid()");
2167 wiki_extract_links(zCom, rid, BKLNK_COMMENT, p->rDate, 1, WIKI_INLINE);
2168 fossil_free(zCom);
2169
2170 /* If this is a delta-manifest, record the fact that this repository
2171 ** contains delta manifests, to free the "commit" logic to generate
2172 ** new delta manifests.
2173
--- src/manifest.c
+++ src/manifest.c
@@ -2162,11 +2162,11 @@
2162 TAG_USER, rid,
2163 TAG_COMMENT, rid, p->rDate
2164 );
2165 zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event"
2166 " WHERE rowid=last_insert_rowid()");
2167 backlink_extract(zCom, 0, rid, BKLNK_COMMENT, p->rDate, 1);
2168 fossil_free(zCom);
2169
2170 /* If this is a delta-manifest, record the fact that this repository
2171 ** contains delta manifests, to free the "commit" logic to generate
2172 ** new delta manifests.
2173
+1 -1
--- src/tag.c
+++ src/tag.c
@@ -220,11 +220,11 @@
220220
if( zCol ){
221221
db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d",
222222
zCol, zValue, rid);
223223
if( tagid==TAG_COMMENT ){
224224
char *zCopy = mprintf("%s", zValue);
225
- wiki_extract_links(zCopy, rid, BKLNK_COMMENT, mtime, 1, WIKI_INLINE);
225
+ backlink_extract(zCopy, 0, rid, BKLNK_COMMENT, mtime, 1);
226226
free(zCopy);
227227
}
228228
}
229229
if( tagid==TAG_DATE ){
230230
db_multi_exec("UPDATE event "
231231
--- src/tag.c
+++ src/tag.c
@@ -220,11 +220,11 @@
220 if( zCol ){
221 db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d",
222 zCol, zValue, rid);
223 if( tagid==TAG_COMMENT ){
224 char *zCopy = mprintf("%s", zValue);
225 wiki_extract_links(zCopy, rid, BKLNK_COMMENT, mtime, 1, WIKI_INLINE);
226 free(zCopy);
227 }
228 }
229 if( tagid==TAG_DATE ){
230 db_multi_exec("UPDATE event "
231
--- src/tag.c
+++ src/tag.c
@@ -220,11 +220,11 @@
220 if( zCol ){
221 db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d",
222 zCol, zValue, rid);
223 if( tagid==TAG_COMMENT ){
224 char *zCopy = mprintf("%s", zValue);
225 backlink_extract(zCopy, 0, rid, BKLNK_COMMENT, mtime, 1);
226 free(zCopy);
227 }
228 }
229 if( tagid==TAG_DATE ){
230 db_multi_exec("UPDATE event "
231
+13 -3
--- src/tkt.c
+++ src/tkt.c
@@ -194,10 +194,11 @@
194194
static int ticket_insert(const Manifest *p, int rid, int tktid){
195195
Blob sql1, sql2, sql3;
196196
Stmt q;
197197
int i, j;
198198
char *aUsed;
199
+ const char *zMimetype = 0;
199200
200201
if( tktid==0 ){
201202
db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) "
202203
"VALUES(%Q, 0)", p->zTicketUuid);
203204
tktid = db_last_insert_rowid();
@@ -233,13 +234,22 @@
233234
zUsedByName++;
234235
}
235236
blob_append_sql(&sql2, ",\"%w\"", zUsedByName);
236237
blob_append_sql(&sql3, ",%Q", p->aField[i].zValue);
237238
}
238
- if( rid>0 ){
239
- wiki_extract_links(p->aField[i].zValue, rid, BKLNK_TICKET,
240
- p->rDate, i==0, 0);
239
+ if( strcmp(zBaseName,"mimetype")==0 ){
240
+ zMimetype = p->aField[i].zValue;
241
+ }
242
+ }
243
+ if( rid>0 ){
244
+ for(i=0; i<p->nField; i++){
245
+ const char *zName = p->aField[i].zName;
246
+ const char *zBaseName = zName[0]=='+' ? zName+1 : zName;
247
+ j = fieldId(zBaseName);
248
+ if( j<0 ) continue;
249
+ backlink_extract(p->aField[i].zValue, zMimetype, rid, BKLNK_TICKET,
250
+ p->rDate, i==0);
241251
}
242252
}
243253
blob_append_sql(&sql1, " WHERE tkt_id=%d", tktid);
244254
db_prepare(&q, "%s", blob_sql_text(&sql1));
245255
db_bind_double(&q, ":mtime", p->rDate);
246256
--- src/tkt.c
+++ src/tkt.c
@@ -194,10 +194,11 @@
194 static int ticket_insert(const Manifest *p, int rid, int tktid){
195 Blob sql1, sql2, sql3;
196 Stmt q;
197 int i, j;
198 char *aUsed;
 
199
200 if( tktid==0 ){
201 db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) "
202 "VALUES(%Q, 0)", p->zTicketUuid);
203 tktid = db_last_insert_rowid();
@@ -233,13 +234,22 @@
233 zUsedByName++;
234 }
235 blob_append_sql(&sql2, ",\"%w\"", zUsedByName);
236 blob_append_sql(&sql3, ",%Q", p->aField[i].zValue);
237 }
238 if( rid>0 ){
239 wiki_extract_links(p->aField[i].zValue, rid, BKLNK_TICKET,
240 p->rDate, i==0, 0);
 
 
 
 
 
 
 
 
 
241 }
242 }
243 blob_append_sql(&sql1, " WHERE tkt_id=%d", tktid);
244 db_prepare(&q, "%s", blob_sql_text(&sql1));
245 db_bind_double(&q, ":mtime", p->rDate);
246
--- src/tkt.c
+++ src/tkt.c
@@ -194,10 +194,11 @@
194 static int ticket_insert(const Manifest *p, int rid, int tktid){
195 Blob sql1, sql2, sql3;
196 Stmt q;
197 int i, j;
198 char *aUsed;
199 const char *zMimetype = 0;
200
201 if( tktid==0 ){
202 db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) "
203 "VALUES(%Q, 0)", p->zTicketUuid);
204 tktid = db_last_insert_rowid();
@@ -233,13 +234,22 @@
234 zUsedByName++;
235 }
236 blob_append_sql(&sql2, ",\"%w\"", zUsedByName);
237 blob_append_sql(&sql3, ",%Q", p->aField[i].zValue);
238 }
239 if( strcmp(zBaseName,"mimetype")==0 ){
240 zMimetype = p->aField[i].zValue;
241 }
242 }
243 if( rid>0 ){
244 for(i=0; i<p->nField; i++){
245 const char *zName = p->aField[i].zName;
246 const char *zBaseName = zName[0]=='+' ? zName+1 : zName;
247 j = fieldId(zBaseName);
248 if( j<0 ) continue;
249 backlink_extract(p->aField[i].zValue, zMimetype, rid, BKLNK_TICKET,
250 p->rDate, i==0);
251 }
252 }
253 blob_append_sql(&sql1, " WHERE tkt_id=%d", tktid);
254 db_prepare(&q, "%s", blob_sql_text(&sql1));
255 db_bind_double(&q, ":mtime", p->rDate);
256
+3 -21
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -1869,14 +1869,11 @@
18691869
** name. For each such hyperlink found, add an entry to the
18701870
** backlink table.
18711871
*/
18721872
void wiki_extract_links(
18731873
char *z, /* The wiki text from which to extract links */
1874
- int srcid, /* srcid field for new BACKLINK table entries */
1875
- int srctype, /* srctype field for new BACKLINK table entries */
1876
- double mtime, /* mtime field for new BACKLINK table entries */
1877
- int replaceFlag, /* True first delete prior BACKLINK entries */
1874
+ Backlink *pBklnk, /* Backlink extraction context */
18781875
int flags /* wiki parsing flags */
18791876
){
18801877
Renderer renderer;
18811878
int tokenType;
18821879
ParsedMarkup markup;
@@ -1892,14 +1889,10 @@
18921889
if( wikiUsesHtml() ){
18931890
renderer.state |= WIKI_HTMLONLY;
18941891
wikiHtmlOnly = 1;
18951892
}
18961893
inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
1897
- if( replaceFlag ){
1898
- db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
1899
- srctype, srcid);
1900
- }
19011894
19021895
while( z[0] ){
19031896
if( wikiHtmlOnly ){
19041897
n = nextRawToken(z, &renderer, &tokenType);
19051898
}else{
@@ -1906,27 +1899,16 @@
19061899
n = nextWikiToken(z, &renderer, &tokenType);
19071900
}
19081901
switch( tokenType ){
19091902
case TOKEN_LINK: {
19101903
char *zTarget;
1911
- int i, c;
1912
- char zLink[HNAME_MAX+4];
1904
+ int i;
19131905
19141906
zTarget = &z[1];
19151907
for(i=0; zTarget[i] && zTarget[i]!='|' && zTarget[i]!=']'; i++){}
19161908
while(i>1 && zTarget[i-1]==' '){ i--; }
1917
- c = zTarget[i];
1918
- zTarget[i] = 0;
1919
- if( is_valid_hname(zTarget) ){
1920
- memcpy(zLink, zTarget, i+1);
1921
- canonical16(zLink, i);
1922
- db_multi_exec(
1923
- "REPLACE INTO backlink(target,srctype,srcid,mtime)"
1924
- "VALUES(%Q,%d,%d,%g)", zLink, srctype, srcid, mtime
1925
- );
1926
- }
1927
- zTarget[i] = c;
1909
+ backlink_create(pBklnk, zTarget, i);
19281910
break;
19291911
}
19301912
case TOKEN_MARKUP: {
19311913
const char *zId;
19321914
int iDiv;
19331915
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -1869,14 +1869,11 @@
1869 ** name. For each such hyperlink found, add an entry to the
1870 ** backlink table.
1871 */
1872 void wiki_extract_links(
1873 char *z, /* The wiki text from which to extract links */
1874 int srcid, /* srcid field for new BACKLINK table entries */
1875 int srctype, /* srctype field for new BACKLINK table entries */
1876 double mtime, /* mtime field for new BACKLINK table entries */
1877 int replaceFlag, /* True first delete prior BACKLINK entries */
1878 int flags /* wiki parsing flags */
1879 ){
1880 Renderer renderer;
1881 int tokenType;
1882 ParsedMarkup markup;
@@ -1892,14 +1889,10 @@
1892 if( wikiUsesHtml() ){
1893 renderer.state |= WIKI_HTMLONLY;
1894 wikiHtmlOnly = 1;
1895 }
1896 inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
1897 if( replaceFlag ){
1898 db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
1899 srctype, srcid);
1900 }
1901
1902 while( z[0] ){
1903 if( wikiHtmlOnly ){
1904 n = nextRawToken(z, &renderer, &tokenType);
1905 }else{
@@ -1906,27 +1899,16 @@
1906 n = nextWikiToken(z, &renderer, &tokenType);
1907 }
1908 switch( tokenType ){
1909 case TOKEN_LINK: {
1910 char *zTarget;
1911 int i, c;
1912 char zLink[HNAME_MAX+4];
1913
1914 zTarget = &z[1];
1915 for(i=0; zTarget[i] && zTarget[i]!='|' && zTarget[i]!=']'; i++){}
1916 while(i>1 && zTarget[i-1]==' '){ i--; }
1917 c = zTarget[i];
1918 zTarget[i] = 0;
1919 if( is_valid_hname(zTarget) ){
1920 memcpy(zLink, zTarget, i+1);
1921 canonical16(zLink, i);
1922 db_multi_exec(
1923 "REPLACE INTO backlink(target,srctype,srcid,mtime)"
1924 "VALUES(%Q,%d,%d,%g)", zLink, srctype, srcid, mtime
1925 );
1926 }
1927 zTarget[i] = c;
1928 break;
1929 }
1930 case TOKEN_MARKUP: {
1931 const char *zId;
1932 int iDiv;
1933
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -1869,14 +1869,11 @@
1869 ** name. For each such hyperlink found, add an entry to the
1870 ** backlink table.
1871 */
1872 void wiki_extract_links(
1873 char *z, /* The wiki text from which to extract links */
1874 Backlink *pBklnk, /* Backlink extraction context */
 
 
 
1875 int flags /* wiki parsing flags */
1876 ){
1877 Renderer renderer;
1878 int tokenType;
1879 ParsedMarkup markup;
@@ -1892,14 +1889,10 @@
1889 if( wikiUsesHtml() ){
1890 renderer.state |= WIKI_HTMLONLY;
1891 wikiHtmlOnly = 1;
1892 }
1893 inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
 
 
 
 
1894
1895 while( z[0] ){
1896 if( wikiHtmlOnly ){
1897 n = nextRawToken(z, &renderer, &tokenType);
1898 }else{
@@ -1906,27 +1899,16 @@
1899 n = nextWikiToken(z, &renderer, &tokenType);
1900 }
1901 switch( tokenType ){
1902 case TOKEN_LINK: {
1903 char *zTarget;
1904 int i;
 
1905
1906 zTarget = &z[1];
1907 for(i=0; zTarget[i] && zTarget[i]!='|' && zTarget[i]!=']'; i++){}
1908 while(i>1 && zTarget[i-1]==' '){ i--; }
1909 backlink_create(pBklnk, zTarget, i);
 
 
 
 
 
 
 
 
 
 
1910 break;
1911 }
1912 case TOKEN_MARKUP: {
1913 const char *zId;
1914 int iDiv;
1915

Keyboard Shortcuts

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