Fossil SCM

Further enhancements to the file browsing UX.

drh 2020-05-11 19:24 trunk
Commit cd05cb20dbfc54939ea6b29b8b0368265beac23a295ec77ad6bbcf3599227350
3 files changed +30 -15 +4 -3 +48 -40
+30 -15
--- src/browse.c
+++ src/browse.c
@@ -59,10 +59,18 @@
5959
zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]);
6060
sqlite3_result_text(context, zOut, i-n+1, sqlite3_free);
6161
}
6262
}
6363
64
+/*
65
+** Flag arguments for hyperlinked_path()
66
+*/
67
+#if INTERFACE
68
+# define LINKPATH_FINFO 0x0001 /* Link final term to /finfo */
69
+# define LINKPATH_FILE 0x0002 /* Link final term to /file */
70
+#endif
71
+
6472
/*
6573
** Given a pathname which is a relative path from the root of
6674
** the repository to a file or directory, compute a string which
6775
** is an HTML rendering of that path with hyperlinks on each
6876
** directory component of the path where the hyperlink redirects
@@ -76,29 +84,36 @@
7684
void hyperlinked_path(
7785
const char *zPath, /* Path to render */
7886
Blob *pOut, /* Write into this blob */
7987
const char *zCI, /* check-in name, or NULL */
8088
const char *zURI, /* "dir" or "tree" */
81
- const char *zREx /* Extra query parameters */
89
+ const char *zREx, /* Extra query parameters */
90
+ unsigned int mFlags /* Extra flags */
8291
){
8392
int i, j;
8493
char *zSep = "";
8594
8695
for(i=0; zPath[i]; i=j){
8796
for(j=i; zPath[j] && zPath[j]!='/'; j++){}
88
- if( zPath[j] && g.perm.Hyperlink ){
89
- if( zCI ){
90
- char *zLink = href("%R/%s?name=%#T%s&ci=%!S", zURI, j, zPath, zREx,zCI);
91
- blob_appendf(pOut, "%s%z%#h</a>",
92
- zSep, zLink, j-i, &zPath[i]);
93
- }else{
94
- char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx);
95
- blob_appendf(pOut, "%s%z%#h</a>",
96
- zSep, zLink, j-i, &zPath[i]);
97
- }
98
- }else{
99
- blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
97
+ if( zPath[j]==0 ){
98
+ if( mFlags & LINKPATH_FILE ){
99
+ zURI = "file";
100
+ }else if( mFlags & LINKPATH_FINFO ){
101
+ zURI = "finfo";
102
+ }else{
103
+ blob_appendf(pOut, "/%h", zPath+i);
104
+ break;
105
+ }
106
+ }
107
+ if( zCI ){
108
+ char *zLink = href("%R/%s?name=%#T%s&ci=%!S", zURI, j, zPath, zREx,zCI);
109
+ blob_appendf(pOut, "%s%z%#h</a>",
110
+ zSep, zLink, j-i, &zPath[i]);
111
+ }else{
112
+ char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx);
113
+ blob_appendf(pOut, "%s%z%#h</a>",
114
+ zSep, zLink, j-i, &zPath[i]);
100115
}
101116
zSep = "/";
102117
while( zPath[j]=='/' ){ j++; }
103118
}
104119
}
@@ -182,11 +197,11 @@
182197
183198
/* Compute the title of the page */
184199
blob_zero(&dirname);
185200
if( zD ){
186201
blob_append(&dirname, "in directory ", -1);
187
- hyperlinked_path(zD, &dirname, zCI, "dir", "");
202
+ hyperlinked_path(zD, &dirname, zCI, "dir", "", 0);
188203
zPrefix = mprintf("%s/", zD);
189204
style_submenu_element("Top-Level", "%s",
190205
url_render(&sURI, "name", 0, 0, 0));
191206
}else{
192207
blob_append(&dirname, "in the top-level directory", -1);
@@ -702,11 +717,11 @@
702717
703718
/* Compute the title of the page */
704719
blob_zero(&dirname);
705720
if( zD ){
706721
blob_append(&dirname, "within directory ", -1);
707
- hyperlinked_path(zD, &dirname, zCI, "tree", zREx);
722
+ hyperlinked_path(zD, &dirname, zCI, "tree", zREx, 0);
708723
if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE);
709724
style_submenu_element("Top-Level", "%s",
710725
url_render(&sURI, "name", 0, 0, 0));
711726
}else{
712727
if( zRE ){
713728
--- src/browse.c
+++ src/browse.c
@@ -59,10 +59,18 @@
59 zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]);
60 sqlite3_result_text(context, zOut, i-n+1, sqlite3_free);
61 }
62 }
63
 
 
 
 
 
 
 
 
64 /*
65 ** Given a pathname which is a relative path from the root of
66 ** the repository to a file or directory, compute a string which
67 ** is an HTML rendering of that path with hyperlinks on each
68 ** directory component of the path where the hyperlink redirects
@@ -76,29 +84,36 @@
76 void hyperlinked_path(
77 const char *zPath, /* Path to render */
78 Blob *pOut, /* Write into this blob */
79 const char *zCI, /* check-in name, or NULL */
80 const char *zURI, /* "dir" or "tree" */
81 const char *zREx /* Extra query parameters */
 
82 ){
83 int i, j;
84 char *zSep = "";
85
86 for(i=0; zPath[i]; i=j){
87 for(j=i; zPath[j] && zPath[j]!='/'; j++){}
88 if( zPath[j] && g.perm.Hyperlink ){
89 if( zCI ){
90 char *zLink = href("%R/%s?name=%#T%s&ci=%!S", zURI, j, zPath, zREx,zCI);
91 blob_appendf(pOut, "%s%z%#h</a>",
92 zSep, zLink, j-i, &zPath[i]);
93 }else{
94 char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx);
95 blob_appendf(pOut, "%s%z%#h</a>",
96 zSep, zLink, j-i, &zPath[i]);
97 }
98 }else{
99 blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
 
 
 
 
 
 
100 }
101 zSep = "/";
102 while( zPath[j]=='/' ){ j++; }
103 }
104 }
@@ -182,11 +197,11 @@
182
183 /* Compute the title of the page */
184 blob_zero(&dirname);
185 if( zD ){
186 blob_append(&dirname, "in directory ", -1);
187 hyperlinked_path(zD, &dirname, zCI, "dir", "");
188 zPrefix = mprintf("%s/", zD);
189 style_submenu_element("Top-Level", "%s",
190 url_render(&sURI, "name", 0, 0, 0));
191 }else{
192 blob_append(&dirname, "in the top-level directory", -1);
@@ -702,11 +717,11 @@
702
703 /* Compute the title of the page */
704 blob_zero(&dirname);
705 if( zD ){
706 blob_append(&dirname, "within directory ", -1);
707 hyperlinked_path(zD, &dirname, zCI, "tree", zREx);
708 if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE);
709 style_submenu_element("Top-Level", "%s",
710 url_render(&sURI, "name", 0, 0, 0));
711 }else{
712 if( zRE ){
713
--- src/browse.c
+++ src/browse.c
@@ -59,10 +59,18 @@
59 zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]);
60 sqlite3_result_text(context, zOut, i-n+1, sqlite3_free);
61 }
62 }
63
64 /*
65 ** Flag arguments for hyperlinked_path()
66 */
67 #if INTERFACE
68 # define LINKPATH_FINFO 0x0001 /* Link final term to /finfo */
69 # define LINKPATH_FILE 0x0002 /* Link final term to /file */
70 #endif
71
72 /*
73 ** Given a pathname which is a relative path from the root of
74 ** the repository to a file or directory, compute a string which
75 ** is an HTML rendering of that path with hyperlinks on each
76 ** directory component of the path where the hyperlink redirects
@@ -76,29 +84,36 @@
84 void hyperlinked_path(
85 const char *zPath, /* Path to render */
86 Blob *pOut, /* Write into this blob */
87 const char *zCI, /* check-in name, or NULL */
88 const char *zURI, /* "dir" or "tree" */
89 const char *zREx, /* Extra query parameters */
90 unsigned int mFlags /* Extra flags */
91 ){
92 int i, j;
93 char *zSep = "";
94
95 for(i=0; zPath[i]; i=j){
96 for(j=i; zPath[j] && zPath[j]!='/'; j++){}
97 if( zPath[j]==0 ){
98 if( mFlags & LINKPATH_FILE ){
99 zURI = "file";
100 }else if( mFlags & LINKPATH_FINFO ){
101 zURI = "finfo";
102 }else{
103 blob_appendf(pOut, "/%h", zPath+i);
104 break;
105 }
106 }
107 if( zCI ){
108 char *zLink = href("%R/%s?name=%#T%s&ci=%!S", zURI, j, zPath, zREx,zCI);
109 blob_appendf(pOut, "%s%z%#h</a>",
110 zSep, zLink, j-i, &zPath[i]);
111 }else{
112 char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx);
113 blob_appendf(pOut, "%s%z%#h</a>",
114 zSep, zLink, j-i, &zPath[i]);
115 }
116 zSep = "/";
117 while( zPath[j]=='/' ){ j++; }
118 }
119 }
@@ -182,11 +197,11 @@
197
198 /* Compute the title of the page */
199 blob_zero(&dirname);
200 if( zD ){
201 blob_append(&dirname, "in directory ", -1);
202 hyperlinked_path(zD, &dirname, zCI, "dir", "", 0);
203 zPrefix = mprintf("%s/", zD);
204 style_submenu_element("Top-Level", "%s",
205 url_render(&sURI, "name", 0, 0, 0));
206 }else{
207 blob_append(&dirname, "in the top-level directory", -1);
@@ -702,11 +717,11 @@
717
718 /* Compute the title of the page */
719 blob_zero(&dirname);
720 if( zD ){
721 blob_append(&dirname, "within directory ", -1);
722 hyperlinked_path(zD, &dirname, zCI, "tree", zREx, 0);
723 if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE);
724 style_submenu_element("Top-Level", "%s",
725 url_render(&sURI, "name", 0, 0, 0));
726 }else{
727 if( zRE ){
728
+4 -3
--- src/finfo.c
+++ src/finfo.c
@@ -438,12 +438,13 @@
438438
}else if( n>0 ){
439439
blob_appendf(&title, "First %d ancestors of file ", n);
440440
}else{
441441
blob_appendf(&title, "Ancestors of file ");
442442
}
443
- blob_appendf(&title,"<a href='%R/finfo?name=%T'>%h</a>",
444
- zFilename, zFilename);
443
+ blob_appendf(&title,"%z%h</a>",
444
+ href("%R/file?name=%T&ci=%!S", zFilename, zUuid),
445
+ zFilename);
445446
if( fShowId ) blob_appendf(&title, " (%d)", fnid);
446447
blob_append(&title, origCheckin ? " between " : " from ", -1);
447448
blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid);
448449
if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin);
449450
fossil_free(zUuid);
@@ -453,11 +454,11 @@
453454
blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
454455
fossil_free(zUuid);
455456
}
456457
}else{
457458
blob_appendf(&title, "History for ");
458
- hyperlinked_path(zFilename, &title, 0, "tree", "");
459
+ hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE);
459460
if( fShowId ) blob_appendf(&title, " (%d)", fnid);
460461
}
461462
if( uBg ){
462463
blob_append(&title, " (color-coded by user)", -1);
463464
}
464465
--- src/finfo.c
+++ src/finfo.c
@@ -438,12 +438,13 @@
438 }else if( n>0 ){
439 blob_appendf(&title, "First %d ancestors of file ", n);
440 }else{
441 blob_appendf(&title, "Ancestors of file ");
442 }
443 blob_appendf(&title,"<a href='%R/finfo?name=%T'>%h</a>",
444 zFilename, zFilename);
 
445 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
446 blob_append(&title, origCheckin ? " between " : " from ", -1);
447 blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid);
448 if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin);
449 fossil_free(zUuid);
@@ -453,11 +454,11 @@
453 blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
454 fossil_free(zUuid);
455 }
456 }else{
457 blob_appendf(&title, "History for ");
458 hyperlinked_path(zFilename, &title, 0, "tree", "");
459 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
460 }
461 if( uBg ){
462 blob_append(&title, " (color-coded by user)", -1);
463 }
464
--- src/finfo.c
+++ src/finfo.c
@@ -438,12 +438,13 @@
438 }else if( n>0 ){
439 blob_appendf(&title, "First %d ancestors of file ", n);
440 }else{
441 blob_appendf(&title, "Ancestors of file ");
442 }
443 blob_appendf(&title,"%z%h</a>",
444 href("%R/file?name=%T&ci=%!S", zFilename, zUuid),
445 zFilename);
446 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
447 blob_append(&title, origCheckin ? " between " : " from ", -1);
448 blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid);
449 if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin);
450 fossil_free(zUuid);
@@ -453,11 +454,11 @@
454 blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
455 fossil_free(zUuid);
456 }
457 }else{
458 blob_appendf(&title, "History for ");
459 hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE);
460 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
461 }
462 if( uBg ){
463 blob_append(&title, " (color-coded by user)", -1);
464 }
465
+48 -40
--- src/info.c
+++ src/info.c
@@ -2102,19 +2102,19 @@
21022102
** identified by HASH. The /whatis page shows only a description
21032103
** of how the artifact is used. The /file page shows the most recent
21042104
** version of the file or directory called NAME, or a list of the
21052105
** top-level directory if NAME is omitted.
21062106
**
2107
-** The name= query parameter can refer to either the name of a file,
2108
-** or an artifact hash. If the ci= query parameter is also present,
2109
-** then name= must refer to a file name. If ci= is omitted, either
2110
-** interpretation may be used. When name= is a filename and ci=
2111
-** is omitted, a default value of "tip" is used for ci=.
2107
+** For /artifact and /whatis, the name= query parameter can refer to
2108
+** either the name of a file, or an artifact hash. If the ci= query
2109
+** parameter is also present, then name= must refer to a file name.
2110
+** If ci= is omitted, then the hash interpretation is preferred but
2111
+** if name= cannot be understood as a hash, a default "tip" value is
2112
+** used for ci=.
21122113
**
2113
-** If name= is ambiguous in that it might be either a filename or
2114
-** a hash, then the hash interpretation is preferred for /artifact
2115
-** and /whatis and the filename interpretation is preferred for /file.
2114
+** For /file, name= can only be interpreted as a filename. As before,
2115
+** a default value of "tip" is used for ci= if ci= is omitted.
21162116
*/
21172117
void artifact_page(void){
21182118
int rid = 0;
21192119
Blob content;
21202120
const char *zMime;
@@ -2189,51 +2189,52 @@
21892189
rid = name_to_rid(zName);
21902190
}
21912191
if( rid==0 ){
21922192
rid = artifact_from_ci_and_filename(0);
21932193
}
2194
- if( rid==0 && zCI==0 && isFile ){
2195
- /* For /file, only try to interpret name= as a hash if it did not
2196
- ** match any known filename. */
2197
- rid = name_to_rid(zName);
2198
- }
2199
- if( rid==0 && isFile ){
2200
- /* If no file called NAME exists, instead look for a directory
2201
- ** with that name, and do a directory listing */
2202
- int nName = (int)strlen(zName);
2203
- if( nName && zName[nName-1]=='/' ) nName--;
2204
- if( db_exists(
2205
- "SELECT 1 FROM filename"
2206
- " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
2207
- nName, zName, nName+1, nName, zName
2208
- ) ){
2209
- if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2210
- page_tree();
2211
- return;
2212
- }
2213
- }
2214
-
2215
- if( rid==0 ){
2216
- style_header("No such artifact");
2217
- @ Artifact '%h(zName)' does not exist in this repository.
2194
+
2195
+ if( rid==0 ){ /* Artifact not found */
2196
+ if( isFile ){
2197
+ /* For /file, also check to see if name= refers to a directory,
2198
+ ** and if so, do a listing for that directory */
2199
+ int nName = (int)strlen(zName);
2200
+ if( nName && zName[nName-1]=='/' ) nName--;
2201
+ if( db_exists(
2202
+ "SELECT 1 FROM filename"
2203
+ " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
2204
+ nName, zName, nName+1, nName, zName
2205
+ ) ){
2206
+ if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2207
+ page_tree();
2208
+ return;
2209
+ }
2210
+ style_header("No such file");
2211
+ @ File '%h(zName)' does not exist in this repository.
2212
+ }else{
2213
+ style_header("No such artifact");
2214
+ @ Artifact '%h(zName)' does not exist in this repository.
2215
+ }
22182216
style_footer();
22192217
return;
22202218
}
2219
+
22212220
if( descOnly || P("verbose")!=0 ){
22222221
url_add_parameter(&url, "verbose", "1");
22232222
objdescFlags |= OBJDESC_DETAIL;
22242223
}
22252224
zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
22262225
22272226
if( isFile ){
2228
- if( zCI==0 ){
2229
- @ <h2>Latest version of file '%h(zName)':</h2>
2227
+ if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
2228
+ zCI = "tip";
2229
+ @ <h2>Latest version of file \
2230
+ @ '%z(href("/finfo?name=%T&m=%T",zName,zCI))%h(zName)</a>':</h2>
22302231
}else{
22312232
const char *zPath;
22322233
Blob path;
22332234
blob_zero(&path);
2234
- hyperlinked_path(zName, &path, zCI, "dir", "");
2235
+ hyperlinked_path(zName, &path, zCI, "dir", "", LINKPATH_FINFO);
22352236
zPath = blob_str(&path);
22362237
@ <h2>File %s(zPath) \
22372238
if( isBranchCI ){
22382239
@ on branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2>
22392240
}else if( isSymbolicCI ){
@@ -2242,23 +2243,30 @@
22422243
@ as of check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>]</h2>
22432244
}
22442245
blob_reset(&path);
22452246
}
22462247
style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
2248
+ style_submenu_element("Annotate", "%R/annotate?filename=%T&ci=%T",
2249
+ zName, zCI);
2250
+ style_submenu_element("Blame", "%R/blame?filename=%T&ci=%T",
2251
+ zName, zCI);
2252
+ blob_init(&downloadName, zName, -1);
2253
+ objType = OBJTYPE_CONTENT;
22472254
}else{
22482255
@ <h2>Artifact
22492256
style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
22502257
if( g.perm.Setup ){
22512258
@ (%d(rid)):</h2>
22522259
}else{
22532260
@ :</h2>
22542261
}
2262
+ blob_zero(&downloadName);
2263
+ asText = P("txt")!=0;
2264
+ if( asText ) objdescFlags &= ~OBJDESC_BASE;
2265
+ objType = object_description(rid, objdescFlags,
2266
+ (isFile?zName:0), &downloadName);
22552267
}
2256
- blob_zero(&downloadName);
2257
- asText = P("txt")!=0;
2258
- if( asText ) objdescFlags &= ~OBJDESC_BASE;
2259
- objType = object_description(rid, objdescFlags, (isFile ? zName : 0),&downloadName);
22602268
if( !descOnly && P("download")!=0 ){
22612269
cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
22622270
db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
22632271
/*NOTREACHED*/
22642272
}
@@ -2286,11 +2294,11 @@
22862294
zHeader = mprintf("Artifact [%S]", zUuid);
22872295
}
22882296
style_header("%s", zHeader);
22892297
fossil_free(zCIUuid);
22902298
fossil_free(zHeader);
2291
- if( g.perm.Admin ){
2299
+ if( !isFile && g.perm.Admin ){
22922300
Stmt q;
22932301
db_prepare(&q,
22942302
"SELECT coalesce(user.login,rcvfrom.uid),"
22952303
" datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr"
22962304
" FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid"
22972305
--- src/info.c
+++ src/info.c
@@ -2102,19 +2102,19 @@
2102 ** identified by HASH. The /whatis page shows only a description
2103 ** of how the artifact is used. The /file page shows the most recent
2104 ** version of the file or directory called NAME, or a list of the
2105 ** top-level directory if NAME is omitted.
2106 **
2107 ** The name= query parameter can refer to either the name of a file,
2108 ** or an artifact hash. If the ci= query parameter is also present,
2109 ** then name= must refer to a file name. If ci= is omitted, either
2110 ** interpretation may be used. When name= is a filename and ci=
2111 ** is omitted, a default value of "tip" is used for ci=.
 
2112 **
2113 ** If name= is ambiguous in that it might be either a filename or
2114 ** a hash, then the hash interpretation is preferred for /artifact
2115 ** and /whatis and the filename interpretation is preferred for /file.
2116 */
2117 void artifact_page(void){
2118 int rid = 0;
2119 Blob content;
2120 const char *zMime;
@@ -2189,51 +2189,52 @@
2189 rid = name_to_rid(zName);
2190 }
2191 if( rid==0 ){
2192 rid = artifact_from_ci_and_filename(0);
2193 }
2194 if( rid==0 && zCI==0 && isFile ){
2195 /* For /file, only try to interpret name= as a hash if it did not
2196 ** match any known filename. */
2197 rid = name_to_rid(zName);
2198 }
2199 if( rid==0 && isFile ){
2200 /* If no file called NAME exists, instead look for a directory
2201 ** with that name, and do a directory listing */
2202 int nName = (int)strlen(zName);
2203 if( nName && zName[nName-1]=='/' ) nName--;
2204 if( db_exists(
2205 "SELECT 1 FROM filename"
2206 " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
2207 nName, zName, nName+1, nName, zName
2208 ) ){
2209 if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2210 page_tree();
2211 return;
2212 }
2213 }
2214
2215 if( rid==0 ){
2216 style_header("No such artifact");
2217 @ Artifact '%h(zName)' does not exist in this repository.
2218 style_footer();
2219 return;
2220 }
 
2221 if( descOnly || P("verbose")!=0 ){
2222 url_add_parameter(&url, "verbose", "1");
2223 objdescFlags |= OBJDESC_DETAIL;
2224 }
2225 zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
2226
2227 if( isFile ){
2228 if( zCI==0 ){
2229 @ <h2>Latest version of file '%h(zName)':</h2>
 
 
2230 }else{
2231 const char *zPath;
2232 Blob path;
2233 blob_zero(&path);
2234 hyperlinked_path(zName, &path, zCI, "dir", "");
2235 zPath = blob_str(&path);
2236 @ <h2>File %s(zPath) \
2237 if( isBranchCI ){
2238 @ on branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2>
2239 }else if( isSymbolicCI ){
@@ -2242,23 +2243,30 @@
2242 @ as of check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>]</h2>
2243 }
2244 blob_reset(&path);
2245 }
2246 style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
 
 
 
 
 
 
2247 }else{
2248 @ <h2>Artifact
2249 style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
2250 if( g.perm.Setup ){
2251 @ (%d(rid)):</h2>
2252 }else{
2253 @ :</h2>
2254 }
 
 
 
 
 
2255 }
2256 blob_zero(&downloadName);
2257 asText = P("txt")!=0;
2258 if( asText ) objdescFlags &= ~OBJDESC_BASE;
2259 objType = object_description(rid, objdescFlags, (isFile ? zName : 0),&downloadName);
2260 if( !descOnly && P("download")!=0 ){
2261 cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
2262 db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
2263 /*NOTREACHED*/
2264 }
@@ -2286,11 +2294,11 @@
2286 zHeader = mprintf("Artifact [%S]", zUuid);
2287 }
2288 style_header("%s", zHeader);
2289 fossil_free(zCIUuid);
2290 fossil_free(zHeader);
2291 if( g.perm.Admin ){
2292 Stmt q;
2293 db_prepare(&q,
2294 "SELECT coalesce(user.login,rcvfrom.uid),"
2295 " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr"
2296 " FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid"
2297
--- src/info.c
+++ src/info.c
@@ -2102,19 +2102,19 @@
2102 ** identified by HASH. The /whatis page shows only a description
2103 ** of how the artifact is used. The /file page shows the most recent
2104 ** version of the file or directory called NAME, or a list of the
2105 ** top-level directory if NAME is omitted.
2106 **
2107 ** For /artifact and /whatis, the name= query parameter can refer to
2108 ** either the name of a file, or an artifact hash. If the ci= query
2109 ** parameter is also present, then name= must refer to a file name.
2110 ** If ci= is omitted, then the hash interpretation is preferred but
2111 ** if name= cannot be understood as a hash, a default "tip" value is
2112 ** used for ci=.
2113 **
2114 ** For /file, name= can only be interpreted as a filename. As before,
2115 ** a default value of "tip" is used for ci= if ci= is omitted.
 
2116 */
2117 void artifact_page(void){
2118 int rid = 0;
2119 Blob content;
2120 const char *zMime;
@@ -2189,51 +2189,52 @@
2189 rid = name_to_rid(zName);
2190 }
2191 if( rid==0 ){
2192 rid = artifact_from_ci_and_filename(0);
2193 }
2194
2195 if( rid==0 ){ /* Artifact not found */
2196 if( isFile ){
2197 /* For /file, also check to see if name= refers to a directory,
2198 ** and if so, do a listing for that directory */
2199 int nName = (int)strlen(zName);
2200 if( nName && zName[nName-1]=='/' ) nName--;
2201 if( db_exists(
2202 "SELECT 1 FROM filename"
2203 " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
2204 nName, zName, nName+1, nName, zName
2205 ) ){
2206 if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2207 page_tree();
2208 return;
2209 }
2210 style_header("No such file");
2211 @ File '%h(zName)' does not exist in this repository.
2212 }else{
2213 style_header("No such artifact");
2214 @ Artifact '%h(zName)' does not exist in this repository.
2215 }
 
 
2216 style_footer();
2217 return;
2218 }
2219
2220 if( descOnly || P("verbose")!=0 ){
2221 url_add_parameter(&url, "verbose", "1");
2222 objdescFlags |= OBJDESC_DETAIL;
2223 }
2224 zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
2225
2226 if( isFile ){
2227 if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
2228 zCI = "tip";
2229 @ <h2>Latest version of file \
2230 @ '%z(href("/finfo?name=%T&m=%T",zName,zCI))%h(zName)</a>':</h2>
2231 }else{
2232 const char *zPath;
2233 Blob path;
2234 blob_zero(&path);
2235 hyperlinked_path(zName, &path, zCI, "dir", "", LINKPATH_FINFO);
2236 zPath = blob_str(&path);
2237 @ <h2>File %s(zPath) \
2238 if( isBranchCI ){
2239 @ on branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2>
2240 }else if( isSymbolicCI ){
@@ -2242,23 +2243,30 @@
2243 @ as of check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>]</h2>
2244 }
2245 blob_reset(&path);
2246 }
2247 style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
2248 style_submenu_element("Annotate", "%R/annotate?filename=%T&ci=%T",
2249 zName, zCI);
2250 style_submenu_element("Blame", "%R/blame?filename=%T&ci=%T",
2251 zName, zCI);
2252 blob_init(&downloadName, zName, -1);
2253 objType = OBJTYPE_CONTENT;
2254 }else{
2255 @ <h2>Artifact
2256 style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
2257 if( g.perm.Setup ){
2258 @ (%d(rid)):</h2>
2259 }else{
2260 @ :</h2>
2261 }
2262 blob_zero(&downloadName);
2263 asText = P("txt")!=0;
2264 if( asText ) objdescFlags &= ~OBJDESC_BASE;
2265 objType = object_description(rid, objdescFlags,
2266 (isFile?zName:0), &downloadName);
2267 }
 
 
 
 
2268 if( !descOnly && P("download")!=0 ){
2269 cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
2270 db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
2271 /*NOTREACHED*/
2272 }
@@ -2286,11 +2294,11 @@
2294 zHeader = mprintf("Artifact [%S]", zUuid);
2295 }
2296 style_header("%s", zHeader);
2297 fossil_free(zCIUuid);
2298 fossil_free(zHeader);
2299 if( !isFile && g.perm.Admin ){
2300 Stmt q;
2301 db_prepare(&q,
2302 "SELECT coalesce(user.login,rcvfrom.uid),"
2303 " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr"
2304 " FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid"
2305

Keyboard Shortcuts

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