Fossil SCM

Work toward moving the file browsing closer to what typical users expect.

drh 2020-05-09 13:17 UTC trunk merge
Commit 2b07e4e935bfbc68f64bf5450eb771a2e8c662558fff7fb12561b8ec3964c16b
4 files changed +41 -12 +11 -8 +62 -26 +3 -3
+41 -12
--- src/browse.c
+++ src/browse.c
@@ -133,21 +133,18 @@
133133
Manifest *pM = 0;
134134
const char *zSubdirLink;
135135
int linkTrunk = 1;
136136
int linkTip = 1;
137137
HQuery sURI;
138
+ int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */
139
+ char *zHeader = 0;
138140
141
+ if( zCI && strlen(zCI)==0 ){ zCI = 0; }
139142
if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; }
140143
login_check_credentials();
141144
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
142145
while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
143
- style_header("File List");
144
- style_adunit_config(ADUNIT_RIGHT_OK);
145
- sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
146
- pathelementFunc, 0, 0);
147
- url_initialize(&sURI, "dir");
148
- cgi_query_parameters_to_url(&sURI);
149146
150147
/* If the name= parameter is an empty string, make it a NULL pointer */
151148
if( zD && strlen(zD)==0 ){ zD = 0; }
152149
153150
/* If a specific check-in is requested, fetch and parse it. If the
@@ -159,14 +156,31 @@
159156
if( pM ){
160157
int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
161158
linkTrunk = trunkRid && rid != trunkRid;
162159
linkTip = rid != symbolic_name_to_rid("tip", "ci");
163160
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
161
+ isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0);
164162
}else{
165163
zCI = 0;
166164
}
167165
}
166
+
167
+ assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
168
+ if( isSymbolicCI ) {
169
+ zHeader = mprintf("%s at %s", (zD ? zD : "Files"), zCI);
170
+ }else if( zUuid && strlen(zUuid) ){
171
+ zHeader = mprintf("%s at [%S]", (zD ? zD : "Files"), zUuid);
172
+ }else{
173
+ zHeader = mprintf("%s", (zD ? zD : "All Files"));
174
+ }
175
+ style_header("%s", zHeader);
176
+ fossil_free(zHeader);
177
+ style_adunit_config(ADUNIT_RIGHT_OK);
178
+ sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
179
+ pathelementFunc, 0, 0);
180
+ url_initialize(&sURI, "dir");
181
+ cgi_query_parameters_to_url(&sURI);
168182
169183
/* Compute the title of the page */
170184
blob_zero(&dirname);
171185
if( zD ){
172186
blob_append(&dirname, "in directory ", -1);
@@ -282,12 +296,11 @@
282296
zFN++;
283297
@ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li>
284298
}else{
285299
const char *zLink;
286300
if( zCI ){
287
- const char *zUuid = db_column_text(&q, 1);
288
- zLink = href("%R/artifact/%!S",zUuid);
301
+ zLink = href("%R/file/%T%T?ci=%!S",zPrefix,zFN,zCI);
289302
}else{
290303
zLink = href("%R/finfo?name=%T%T",zPrefix,zFN);
291304
}
292305
@ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li>
293306
}
@@ -612,11 +625,14 @@
612625
HQuery sURI; /* Hyperlink */
613626
int startExpanded; /* True to start out with the tree expanded */
614627
int showDirOnly; /* Show directories only. Omit files */
615628
int nDir = 0; /* Number of directories. Used for ID attributes */
616629
char *zProjectName = db_get("project-name", 0);
630
+ int isSymbolicCI = 0; /* ci= is a symbolic name, not a hash prefix */
631
+ char *zHeader = 0;
617632
633
+ if( zCI && strlen(zCI)==0 ){ zCI = 0; }
618634
if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; }
619635
memset(&sTree, 0, sizeof(sTree));
620636
login_check_credentials();
621637
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
622638
while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
@@ -624,14 +640,12 @@
624640
pathelementFunc, 0, 0);
625641
url_initialize(&sURI, "tree");
626642
cgi_query_parameters_to_url(&sURI);
627643
if( PB("nofiles") ){
628644
showDirOnly = 1;
629
- style_header("Folder Hierarchy");
630645
}else{
631646
showDirOnly = 0;
632
- style_header("File Tree");
633647
}
634648
style_adunit_config(ADUNIT_RIGHT_OK);
635649
if( PB("expand") ){
636650
startExpanded = 1;
637651
}else{
@@ -660,18 +674,33 @@
660674
linkTip = rid != symbolic_name_to_rid("tip", "ci");
661675
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
662676
rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
663677
zNow = db_text("", "SELECT datetime(mtime,toLocal())"
664678
" FROM event WHERE objid=%d", rid);
679
+ isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0);
665680
}else{
666681
zCI = 0;
667682
}
668683
}
669684
if( zCI==0 ){
670685
rNow = db_double(0.0, "SELECT max(mtime) FROM event");
671686
zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event");
672687
}
688
+
689
+ assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
690
+ if( isSymbolicCI ) {
691
+ zHeader = mprintf("%s at %s",
692
+ (zD ? zD : (showDirOnly ? "Folder Hierarchy" : "Tree-View")), zCI);
693
+ }else if( zUuid && strlen(zUuid) ){
694
+ zHeader = mprintf("%s at [%S]",
695
+ (zD ? zD : (showDirOnly ? "Folder Hierarchy" : "Tree-View")), zUuid);
696
+ }else{
697
+ zHeader = mprintf("%s",
698
+ (zD ? zD : (showDirOnly ?"All Folders Hierarchy":"All Files Tree-View")));
699
+ }
700
+ style_header("%s", zHeader);
701
+ fossil_free(zHeader);
673702
674703
/* Compute the title of the page */
675704
blob_zero(&dirname);
676705
if( zD ){
677706
blob_append(&dirname, "within directory ", -1);
@@ -758,11 +787,11 @@
758787
}
759788
760789
style_submenu_checkbox("nofiles", "Folders Only", 0, 0);
761790
762791
if( zCI ){
763
- @ <h2>%s(zObjType) from
792
+ @ <h2>%s(zObjType) of check-in
764793
if( sqlite3_strnicmp(zCI, zUuid, (int)strlen(zCI))!=0 ){
765794
@ "%h(zCI)"
766795
}
767796
@ [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] %s(blob_str(&dirname))
768797
}else{
@@ -824,11 +853,11 @@
824853
nDir++;
825854
}else if( !showDirOnly ){
826855
const char *zFileClass = fileext_class(p->zName);
827856
char *zLink;
828857
if( zCI ){
829
- zLink = href("%R/artifact/%!S",p->zUuid);
858
+ zLink = href("%R/file/%T?ci=%!S",p->zFullName,zCI);
830859
}else{
831860
zLink = href("%R/finfo?name=%T",p->zFullName);
832861
}
833862
@ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline">
834863
@ %z(zLink)%h(p->zName)</a>
835864
--- src/browse.c
+++ src/browse.c
@@ -133,21 +133,18 @@
133 Manifest *pM = 0;
134 const char *zSubdirLink;
135 int linkTrunk = 1;
136 int linkTip = 1;
137 HQuery sURI;
 
 
138
 
139 if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; }
140 login_check_credentials();
141 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
142 while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
143 style_header("File List");
144 style_adunit_config(ADUNIT_RIGHT_OK);
145 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
146 pathelementFunc, 0, 0);
147 url_initialize(&sURI, "dir");
148 cgi_query_parameters_to_url(&sURI);
149
150 /* If the name= parameter is an empty string, make it a NULL pointer */
151 if( zD && strlen(zD)==0 ){ zD = 0; }
152
153 /* If a specific check-in is requested, fetch and parse it. If the
@@ -159,14 +156,31 @@
159 if( pM ){
160 int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
161 linkTrunk = trunkRid && rid != trunkRid;
162 linkTip = rid != symbolic_name_to_rid("tip", "ci");
163 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
 
164 }else{
165 zCI = 0;
166 }
167 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
169 /* Compute the title of the page */
170 blob_zero(&dirname);
171 if( zD ){
172 blob_append(&dirname, "in directory ", -1);
@@ -282,12 +296,11 @@
282 zFN++;
283 @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li>
284 }else{
285 const char *zLink;
286 if( zCI ){
287 const char *zUuid = db_column_text(&q, 1);
288 zLink = href("%R/artifact/%!S",zUuid);
289 }else{
290 zLink = href("%R/finfo?name=%T%T",zPrefix,zFN);
291 }
292 @ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li>
293 }
@@ -612,11 +625,14 @@
612 HQuery sURI; /* Hyperlink */
613 int startExpanded; /* True to start out with the tree expanded */
614 int showDirOnly; /* Show directories only. Omit files */
615 int nDir = 0; /* Number of directories. Used for ID attributes */
616 char *zProjectName = db_get("project-name", 0);
 
 
617
 
618 if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; }
619 memset(&sTree, 0, sizeof(sTree));
620 login_check_credentials();
621 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
622 while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
@@ -624,14 +640,12 @@
624 pathelementFunc, 0, 0);
625 url_initialize(&sURI, "tree");
626 cgi_query_parameters_to_url(&sURI);
627 if( PB("nofiles") ){
628 showDirOnly = 1;
629 style_header("Folder Hierarchy");
630 }else{
631 showDirOnly = 0;
632 style_header("File Tree");
633 }
634 style_adunit_config(ADUNIT_RIGHT_OK);
635 if( PB("expand") ){
636 startExpanded = 1;
637 }else{
@@ -660,18 +674,33 @@
660 linkTip = rid != symbolic_name_to_rid("tip", "ci");
661 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
662 rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
663 zNow = db_text("", "SELECT datetime(mtime,toLocal())"
664 " FROM event WHERE objid=%d", rid);
 
665 }else{
666 zCI = 0;
667 }
668 }
669 if( zCI==0 ){
670 rNow = db_double(0.0, "SELECT max(mtime) FROM event");
671 zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event");
672 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
673
674 /* Compute the title of the page */
675 blob_zero(&dirname);
676 if( zD ){
677 blob_append(&dirname, "within directory ", -1);
@@ -758,11 +787,11 @@
758 }
759
760 style_submenu_checkbox("nofiles", "Folders Only", 0, 0);
761
762 if( zCI ){
763 @ <h2>%s(zObjType) from
764 if( sqlite3_strnicmp(zCI, zUuid, (int)strlen(zCI))!=0 ){
765 @ "%h(zCI)"
766 }
767 @ [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] %s(blob_str(&dirname))
768 }else{
@@ -824,11 +853,11 @@
824 nDir++;
825 }else if( !showDirOnly ){
826 const char *zFileClass = fileext_class(p->zName);
827 char *zLink;
828 if( zCI ){
829 zLink = href("%R/artifact/%!S",p->zUuid);
830 }else{
831 zLink = href("%R/finfo?name=%T",p->zFullName);
832 }
833 @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline">
834 @ %z(zLink)%h(p->zName)</a>
835
--- src/browse.c
+++ src/browse.c
@@ -133,21 +133,18 @@
133 Manifest *pM = 0;
134 const char *zSubdirLink;
135 int linkTrunk = 1;
136 int linkTip = 1;
137 HQuery sURI;
138 int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */
139 char *zHeader = 0;
140
141 if( zCI && strlen(zCI)==0 ){ zCI = 0; }
142 if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; }
143 login_check_credentials();
144 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
145 while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
 
 
 
 
 
 
146
147 /* If the name= parameter is an empty string, make it a NULL pointer */
148 if( zD && strlen(zD)==0 ){ zD = 0; }
149
150 /* If a specific check-in is requested, fetch and parse it. If the
@@ -159,14 +156,31 @@
156 if( pM ){
157 int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
158 linkTrunk = trunkRid && rid != trunkRid;
159 linkTip = rid != symbolic_name_to_rid("tip", "ci");
160 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
161 isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0);
162 }else{
163 zCI = 0;
164 }
165 }
166
167 assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
168 if( isSymbolicCI ) {
169 zHeader = mprintf("%s at %s", (zD ? zD : "Files"), zCI);
170 }else if( zUuid && strlen(zUuid) ){
171 zHeader = mprintf("%s at [%S]", (zD ? zD : "Files"), zUuid);
172 }else{
173 zHeader = mprintf("%s", (zD ? zD : "All Files"));
174 }
175 style_header("%s", zHeader);
176 fossil_free(zHeader);
177 style_adunit_config(ADUNIT_RIGHT_OK);
178 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
179 pathelementFunc, 0, 0);
180 url_initialize(&sURI, "dir");
181 cgi_query_parameters_to_url(&sURI);
182
183 /* Compute the title of the page */
184 blob_zero(&dirname);
185 if( zD ){
186 blob_append(&dirname, "in directory ", -1);
@@ -282,12 +296,11 @@
296 zFN++;
297 @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li>
298 }else{
299 const char *zLink;
300 if( zCI ){
301 zLink = href("%R/file/%T%T?ci=%!S",zPrefix,zFN,zCI);
 
302 }else{
303 zLink = href("%R/finfo?name=%T%T",zPrefix,zFN);
304 }
305 @ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li>
306 }
@@ -612,11 +625,14 @@
625 HQuery sURI; /* Hyperlink */
626 int startExpanded; /* True to start out with the tree expanded */
627 int showDirOnly; /* Show directories only. Omit files */
628 int nDir = 0; /* Number of directories. Used for ID attributes */
629 char *zProjectName = db_get("project-name", 0);
630 int isSymbolicCI = 0; /* ci= is a symbolic name, not a hash prefix */
631 char *zHeader = 0;
632
633 if( zCI && strlen(zCI)==0 ){ zCI = 0; }
634 if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; }
635 memset(&sTree, 0, sizeof(sTree));
636 login_check_credentials();
637 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
638 while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
@@ -624,14 +640,12 @@
640 pathelementFunc, 0, 0);
641 url_initialize(&sURI, "tree");
642 cgi_query_parameters_to_url(&sURI);
643 if( PB("nofiles") ){
644 showDirOnly = 1;
 
645 }else{
646 showDirOnly = 0;
 
647 }
648 style_adunit_config(ADUNIT_RIGHT_OK);
649 if( PB("expand") ){
650 startExpanded = 1;
651 }else{
@@ -660,18 +674,33 @@
674 linkTip = rid != symbolic_name_to_rid("tip", "ci");
675 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
676 rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
677 zNow = db_text("", "SELECT datetime(mtime,toLocal())"
678 " FROM event WHERE objid=%d", rid);
679 isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0);
680 }else{
681 zCI = 0;
682 }
683 }
684 if( zCI==0 ){
685 rNow = db_double(0.0, "SELECT max(mtime) FROM event");
686 zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event");
687 }
688
689 assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
690 if( isSymbolicCI ) {
691 zHeader = mprintf("%s at %s",
692 (zD ? zD : (showDirOnly ? "Folder Hierarchy" : "Tree-View")), zCI);
693 }else if( zUuid && strlen(zUuid) ){
694 zHeader = mprintf("%s at [%S]",
695 (zD ? zD : (showDirOnly ? "Folder Hierarchy" : "Tree-View")), zUuid);
696 }else{
697 zHeader = mprintf("%s",
698 (zD ? zD : (showDirOnly ?"All Folders Hierarchy":"All Files Tree-View")));
699 }
700 style_header("%s", zHeader);
701 fossil_free(zHeader);
702
703 /* Compute the title of the page */
704 blob_zero(&dirname);
705 if( zD ){
706 blob_append(&dirname, "within directory ", -1);
@@ -758,11 +787,11 @@
787 }
788
789 style_submenu_checkbox("nofiles", "Folders Only", 0, 0);
790
791 if( zCI ){
792 @ <h2>%s(zObjType) of check-in
793 if( sqlite3_strnicmp(zCI, zUuid, (int)strlen(zCI))!=0 ){
794 @ "%h(zCI)"
795 }
796 @ [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] %s(blob_str(&dirname))
797 }else{
@@ -824,11 +853,11 @@
853 nDir++;
854 }else if( !showDirOnly ){
855 const char *zFileClass = fileext_class(p->zName);
856 char *zLink;
857 if( zCI ){
858 zLink = href("%R/file/%T?ci=%!S",p->zFullName,zCI);
859 }else{
860 zLink = href("%R/finfo?name=%T",p->zFullName);
861 }
862 @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline">
863 @ %z(zLink)%h(p->zName)</a>
864
+11 -8
--- src/finfo.c
+++ src/finfo.c
@@ -199,11 +199,11 @@
199199
TAG_BRANCH, zFilename, filename_collation(),
200200
iLimit, iOffset
201201
);
202202
blob_zero(&line);
203203
if( iBrief ){
204
- fossil_print("History of %s\n", blob_str(&fname));
204
+ fossil_print("History for %s\n", blob_str(&fname));
205205
}
206206
while( db_step(&q)==SQLITE_ROW ){
207207
const char *zFileUuid = db_column_text(&q, 0);
208208
const char *zCiUuid = db_column_text(&q,1);
209209
const char *zDate = db_column_text(&q, 2);
@@ -296,11 +296,11 @@
296296
** timezone offset from UTC as "-HH:MM" (westward) or "+HH:MM"
297297
** (eastward). Either no timezone suffix or "Z" means UTC.
298298
*/
299299
void finfo_page(void){
300300
Stmt q;
301
- const char *zFilename;
301
+ const char *zFilename = PD("name","");
302302
char zPrevDate[20];
303303
const char *zA;
304304
const char *zB;
305305
int n;
306306
int baseCheckin;
@@ -321,11 +321,16 @@
321321
const char *zMark; /* Mark this version of the file */
322322
int selRid = 0; /* RID of the marked file version */
323323
324324
login_check_credentials();
325325
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
326
- style_header("File History");
326
+ fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
327
+ if( fnid==0 ){
328
+ style_header("History unavailable");
329
+ }else{
330
+ style_header("History for %s", zFilename);
331
+ }
327332
login_anonymous_available();
328333
tmFlags = timeline_ss_submenu();
329334
if( tmFlags & TIMELINE_COLUMNAR ){
330335
zStyle = "Columnar";
331336
}else if( tmFlags & TIMELINE_COMPACT ){
@@ -340,13 +345,11 @@
340345
url_initialize(&url, "finfo");
341346
if( brBg ) url_add_parameter(&url, "brbg", 0);
342347
if( uBg ) url_add_parameter(&url, "ubg", 0);
343348
baseCheckin = name_to_rid_www("ci");
344349
zPrevDate[0] = 0;
345
- zFilename = PD("name","");
346350
cookie_render();
347
- fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
348351
if( fnid==0 ){
349352
@ No such file: %h(zFilename)
350353
style_footer();
351354
return;
352355
}
@@ -449,11 +452,11 @@
449452
zLink = href("%R/info/%!S", zUuid);
450453
blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
451454
fossil_free(zUuid);
452455
}
453456
}else{
454
- blob_appendf(&title, "History of ");
457
+ blob_appendf(&title, "History for ");
455458
hyperlinked_path(zFilename, &title, 0, "tree", "");
456459
if( fShowId ) blob_appendf(&title, " (%d)", fnid);
457460
}
458461
if( uBg ){
459462
blob_append(&title, " (color-coded by user)", -1);
@@ -524,11 +527,11 @@
524527
@ <tr class='timelineSelected'>
525528
}else{
526529
@ <tr>
527530
}
528531
@ <td class="timelineTime">\
529
- @ %z(href("%R/artifact/%!S",zUuid))%s(zTime)</a></td>
532
+ @ %z(href("%R/file/%T?ci=%!S",zFilename,zCkin))%s(zTime)</a></td>
530533
@ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div>
531534
@ </td>
532535
if( zBgClr && zBgClr[0] ){
533536
@ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'>
534537
}else{
@@ -561,11 +564,11 @@
561564
cgi_printf("<span class='clutter' id='detail-%d'>",frid);
562565
}
563566
cgi_printf("<span class='timeline%sDetail'>", zStyle);
564567
if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("(");
565568
if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
566
- @ file:&nbsp;%z(href("%R/artifact/%!S",zUuid))[%S(zUuid)]</a>
569
+ @ file:&nbsp;%z(href("%R/file/%T?ci=%!S",zFilename,zCkin))[%S(zUuid)]</a>
567570
if( fShowId ){
568571
int srcId = delta_source_rid(frid);
569572
if( srcId>0 ){
570573
@ id:&nbsp;%d(frid)&larr;%d(srcId)
571574
}else{
572575
--- src/finfo.c
+++ src/finfo.c
@@ -199,11 +199,11 @@
199 TAG_BRANCH, zFilename, filename_collation(),
200 iLimit, iOffset
201 );
202 blob_zero(&line);
203 if( iBrief ){
204 fossil_print("History of %s\n", blob_str(&fname));
205 }
206 while( db_step(&q)==SQLITE_ROW ){
207 const char *zFileUuid = db_column_text(&q, 0);
208 const char *zCiUuid = db_column_text(&q,1);
209 const char *zDate = db_column_text(&q, 2);
@@ -296,11 +296,11 @@
296 ** timezone offset from UTC as "-HH:MM" (westward) or "+HH:MM"
297 ** (eastward). Either no timezone suffix or "Z" means UTC.
298 */
299 void finfo_page(void){
300 Stmt q;
301 const char *zFilename;
302 char zPrevDate[20];
303 const char *zA;
304 const char *zB;
305 int n;
306 int baseCheckin;
@@ -321,11 +321,16 @@
321 const char *zMark; /* Mark this version of the file */
322 int selRid = 0; /* RID of the marked file version */
323
324 login_check_credentials();
325 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
326 style_header("File History");
 
 
 
 
 
327 login_anonymous_available();
328 tmFlags = timeline_ss_submenu();
329 if( tmFlags & TIMELINE_COLUMNAR ){
330 zStyle = "Columnar";
331 }else if( tmFlags & TIMELINE_COMPACT ){
@@ -340,13 +345,11 @@
340 url_initialize(&url, "finfo");
341 if( brBg ) url_add_parameter(&url, "brbg", 0);
342 if( uBg ) url_add_parameter(&url, "ubg", 0);
343 baseCheckin = name_to_rid_www("ci");
344 zPrevDate[0] = 0;
345 zFilename = PD("name","");
346 cookie_render();
347 fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
348 if( fnid==0 ){
349 @ No such file: %h(zFilename)
350 style_footer();
351 return;
352 }
@@ -449,11 +452,11 @@
449 zLink = href("%R/info/%!S", zUuid);
450 blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
451 fossil_free(zUuid);
452 }
453 }else{
454 blob_appendf(&title, "History of ");
455 hyperlinked_path(zFilename, &title, 0, "tree", "");
456 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
457 }
458 if( uBg ){
459 blob_append(&title, " (color-coded by user)", -1);
@@ -524,11 +527,11 @@
524 @ <tr class='timelineSelected'>
525 }else{
526 @ <tr>
527 }
528 @ <td class="timelineTime">\
529 @ %z(href("%R/artifact/%!S",zUuid))%s(zTime)</a></td>
530 @ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div>
531 @ </td>
532 if( zBgClr && zBgClr[0] ){
533 @ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'>
534 }else{
@@ -561,11 +564,11 @@
561 cgi_printf("<span class='clutter' id='detail-%d'>",frid);
562 }
563 cgi_printf("<span class='timeline%sDetail'>", zStyle);
564 if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("(");
565 if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
566 @ file:&nbsp;%z(href("%R/artifact/%!S",zUuid))[%S(zUuid)]</a>
567 if( fShowId ){
568 int srcId = delta_source_rid(frid);
569 if( srcId>0 ){
570 @ id:&nbsp;%d(frid)&larr;%d(srcId)
571 }else{
572
--- src/finfo.c
+++ src/finfo.c
@@ -199,11 +199,11 @@
199 TAG_BRANCH, zFilename, filename_collation(),
200 iLimit, iOffset
201 );
202 blob_zero(&line);
203 if( iBrief ){
204 fossil_print("History for %s\n", blob_str(&fname));
205 }
206 while( db_step(&q)==SQLITE_ROW ){
207 const char *zFileUuid = db_column_text(&q, 0);
208 const char *zCiUuid = db_column_text(&q,1);
209 const char *zDate = db_column_text(&q, 2);
@@ -296,11 +296,11 @@
296 ** timezone offset from UTC as "-HH:MM" (westward) or "+HH:MM"
297 ** (eastward). Either no timezone suffix or "Z" means UTC.
298 */
299 void finfo_page(void){
300 Stmt q;
301 const char *zFilename = PD("name","");
302 char zPrevDate[20];
303 const char *zA;
304 const char *zB;
305 int n;
306 int baseCheckin;
@@ -321,11 +321,16 @@
321 const char *zMark; /* Mark this version of the file */
322 int selRid = 0; /* RID of the marked file version */
323
324 login_check_credentials();
325 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
326 fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
327 if( fnid==0 ){
328 style_header("History unavailable");
329 }else{
330 style_header("History for %s", zFilename);
331 }
332 login_anonymous_available();
333 tmFlags = timeline_ss_submenu();
334 if( tmFlags & TIMELINE_COLUMNAR ){
335 zStyle = "Columnar";
336 }else if( tmFlags & TIMELINE_COMPACT ){
@@ -340,13 +345,11 @@
345 url_initialize(&url, "finfo");
346 if( brBg ) url_add_parameter(&url, "brbg", 0);
347 if( uBg ) url_add_parameter(&url, "ubg", 0);
348 baseCheckin = name_to_rid_www("ci");
349 zPrevDate[0] = 0;
 
350 cookie_render();
 
351 if( fnid==0 ){
352 @ No such file: %h(zFilename)
353 style_footer();
354 return;
355 }
@@ -449,11 +452,11 @@
452 zLink = href("%R/info/%!S", zUuid);
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);
@@ -524,11 +527,11 @@
527 @ <tr class='timelineSelected'>
528 }else{
529 @ <tr>
530 }
531 @ <td class="timelineTime">\
532 @ %z(href("%R/file/%T?ci=%!S",zFilename,zCkin))%s(zTime)</a></td>
533 @ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div>
534 @ </td>
535 if( zBgClr && zBgClr[0] ){
536 @ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'>
537 }else{
@@ -561,11 +564,11 @@
564 cgi_printf("<span class='clutter' id='detail-%d'>",frid);
565 }
566 cgi_printf("<span class='timeline%sDetail'>", zStyle);
567 if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("(");
568 if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
569 @ file:&nbsp;%z(href("%R/file/%T?ci=%!S",zFilename,zCkin))[%S(zUuid)]</a>
570 if( fShowId ){
571 int srcId = delta_source_rid(frid);
572 if( srcId>0 ){
573 @ id:&nbsp;%d(frid)&larr;%d(srcId)
574 }else{
575
+62 -26
--- src/info.c
+++ src/info.c
@@ -1340,33 +1340,22 @@
13401340
13411341
/*
13421342
** Possible flags for the second parameter to
13431343
** object_description()
13441344
*/
1345
-#define OBJDESC_DETAIL 0x0001 /* more detail */
1345
+#define OBJDESC_DETAIL 0x0001 /* Show more detail */
13461346
#define OBJDESC_BASE 0x0002 /* Set <base> using this object */
13471347
#endif
13481348
13491349
/*
13501350
** Write a description of an object to the www reply.
1351
-**
1352
-** If the object is a file then mention:
1353
-**
1354
-** * It's artifact ID
1355
-** * All its filenames
1356
-** * The check-in it was part of, with times and users
1357
-**
1358
-** If the object is a manifest, then mention:
1359
-**
1360
-** * It's artifact ID
1361
-** * date of check-in
1362
-** * Comment & user
13631351
*/
13641352
int object_description(
1365
- int rid, /* The artifact ID */
1353
+ int rid, /* The artifact ID for the object to describe */
13661354
u32 objdescFlags, /* Flags to control display */
1367
- Blob *pDownloadName /* Fill with an appropriate download name */
1355
+ const char *zFileName, /* For file objects, use this name. Can be NULL */
1356
+ Blob *pDownloadName /* Fill with a good download name. Can be NULL */
13681357
){
13691358
Stmt q;
13701359
int cnt = 0;
13711360
int nWiki = 0;
13721361
int objType = 0;
@@ -1401,10 +1390,11 @@
14011390
const char *zVers = db_column_text(&q, 4);
14021391
int mPerm = db_column_int(&q, 5);
14031392
const char *zBr = db_column_text(&q, 6);
14041393
int szFile = db_column_int(&q,7);
14051394
int sameFilename = prevName!=0 && fossil_strcmp(zName,prevName)==0;
1395
+ if( zFileName && fossil_strcmp(zName,zFileName)!=0 ) continue;
14061396
if( sameFilename && !showDetail ){
14071397
if( cnt==1 ){
14081398
@ %z(href("%R/whatis/%!S",zUuid))[more...]</a>
14091399
}
14101400
cnt++;
@@ -1747,13 +1737,13 @@
17471737
@ %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a> To
17481738
@ %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>.</h2>
17491739
}else{
17501740
@ <h2>Differences From
17511741
@ Artifact %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a>:</h2>
1752
- object_description(v1, objdescFlags, 0);
1742
+ object_description(v1, objdescFlags,0, 0);
17531743
@ <h2>To Artifact %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>:</h2>
1754
- object_description(v2, objdescFlags, 0);
1744
+ object_description(v2, objdescFlags,0, 0);
17551745
}
17561746
if( pRe ){
17571747
@ <b>Only differences that match regular expression "%h(zRe)"
17581748
@ are shown.</b>
17591749
}
@@ -1937,11 +1927,11 @@
19371927
}else{
19381928
@ :</h2>
19391929
}
19401930
blob_zero(&downloadName);
19411931
if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
1942
- object_description(rid, objdescFlags, &downloadName);
1932
+ object_description(rid, objdescFlags, 0, &downloadName);
19431933
style_submenu_element("Download", "%s/raw/%T?name=%s",
19441934
g.zTop, blob_str(&downloadName), zUuid);
19451935
@ <hr />
19461936
content_get(rid, &content);
19471937
@ <blockquote><pre>
@@ -2127,20 +2117,41 @@
21272117
Blob downloadName;
21282118
int renderAsWiki = 0;
21292119
int renderAsHtml = 0;
21302120
int objType;
21312121
int asText;
2132
- const char *zUuid;
2122
+ const char *zUuid = 0;
21332123
u32 objdescFlags = OBJDESC_BASE;
21342124
int descOnly = fossil_strcmp(g.zPath,"whatis")==0;
21352125
int isFile = fossil_strcmp(g.zPath,"file")==0;
21362126
const char *zLn = P("ln");
21372127
const char *zName = P("name");
2128
+ const char *zCI = P("ci");
21382129
HQuery url;
2130
+ Blob dirname;
2131
+ char *zCIUuid = 0;
2132
+ int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */
2133
+ char *zHeader = 0;
21392134
2135
+ login_check_credentials();
2136
+ if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
21402137
url_initialize(&url, g.zPath);
2141
- rid = artifact_from_ci_and_filename(&url, 0);
2138
+ if( zCI && strlen(zCI)==0 ){ zCI = 0; }
2139
+ if( zCI ){
2140
+ blob_zero(&dirname);
2141
+ hyperlinked_path(zName, &dirname, zCI, "dir", "");
2142
+ blob_reset(&dirname);
2143
+
2144
+ if( name_to_uuid2(zCI, "ci", &zCIUuid) ){
2145
+ isSymbolicCI = (strncmp(zCIUuid, zCI, strlen(zCI)) != 0);
2146
+ }
2147
+ }
2148
+ if( isFile && zName ) {
2149
+ rid = artifact_from_ci_and_filename(0, "name");
2150
+ }else{
2151
+ rid = artifact_from_ci_and_filename(&url, 0);
2152
+ }
21422153
if( rid==0 ){
21432154
url_add_parameter(&url, "name", zName);
21442155
if( isFile ){
21452156
/* Do a top-level directory listing in /file mode if no argument
21462157
** specified */
@@ -2183,12 +2194,10 @@
21832194
}else{
21842195
rid = name_to_rid_www("name");
21852196
}
21862197
}
21872198
2188
- login_check_credentials();
2189
- if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
21902199
if( rid==0 ){
21912200
style_header("No such artifact");
21922201
@ Artifact '%h(zName)' does not exist in this repository.
21932202
style_footer();
21942203
return;
@@ -2196,12 +2205,24 @@
21962205
if( descOnly || P("verbose")!=0 ){
21972206
url_add_parameter(&url, "verbose", "1");
21982207
objdescFlags |= OBJDESC_DETAIL;
21992208
}
22002209
zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
2210
+
22012211
if( isFile ){
2202
- @ <h2>Latest version of file '%h(zName)':</h2>
2212
+ if( zCI ){
2213
+ const char *zPath;
2214
+ Blob path;
2215
+ blob_zero(&path);
2216
+ hyperlinked_path(zName, &path, zCI, "dir", "");
2217
+ zPath = blob_str(&path);
2218
+ @ <h2>File %s(zPath) of check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>]
2219
+ @ </h2>
2220
+ blob_reset(&path);
2221
+ }else{
2222
+ @ <h2>Latest version of file '%h(zName)':</h2>
2223
+ }
22032224
style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
22042225
}else{
22052226
@ <h2>Artifact
22062227
style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
22072228
if( g.perm.Setup ){
@@ -2211,11 +2232,11 @@
22112232
}
22122233
}
22132234
blob_zero(&downloadName);
22142235
asText = P("txt")!=0;
22152236
if( asText ) objdescFlags &= ~OBJDESC_BASE;
2216
- objType = object_description(rid, objdescFlags, &downloadName);
2237
+ objType = object_description(rid, objdescFlags, (isFile ? zName : 0),&downloadName);
22172238
if( !descOnly && P("download")!=0 ){
22182239
cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
22192240
db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
22202241
/*NOTREACHED*/
22212242
}
@@ -2226,12 +2247,27 @@
22262247
g.zTop, zUuid);
22272248
}else{
22282249
style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid);
22292250
}
22302251
}
2231
- style_header("%s", isFile ? "File Content" :
2232
- descOnly ? "Artifact Description" : "Artifact Content");
2252
+
2253
+ if( isFile ){
2254
+ if( isSymbolicCI ){
2255
+ zHeader = mprintf("%s at %s", file_tail(zName), zCI);
2256
+ }else if( zCI ){
2257
+ zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid);
2258
+ }else{
2259
+ zHeader = mprintf("%s", file_tail(zName));
2260
+ }
2261
+ }else if( descOnly ){
2262
+ zHeader = mprintf("Artifact Description [%S]", zUuid);
2263
+ }else{
2264
+ zHeader = mprintf("Artifact [%S]", zUuid);
2265
+ }
2266
+ style_header("%s", zHeader);
2267
+ fossil_free(zCIUuid);
2268
+ fossil_free(zHeader);
22332269
if( g.perm.Admin ){
22342270
Stmt q;
22352271
db_prepare(&q,
22362272
"SELECT coalesce(user.login,rcvfrom.uid),"
22372273
" datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr"
22382274
--- src/info.c
+++ src/info.c
@@ -1340,33 +1340,22 @@
1340
1341 /*
1342 ** Possible flags for the second parameter to
1343 ** object_description()
1344 */
1345 #define OBJDESC_DETAIL 0x0001 /* more detail */
1346 #define OBJDESC_BASE 0x0002 /* Set <base> using this object */
1347 #endif
1348
1349 /*
1350 ** Write a description of an object to the www reply.
1351 **
1352 ** If the object is a file then mention:
1353 **
1354 ** * It's artifact ID
1355 ** * All its filenames
1356 ** * The check-in it was part of, with times and users
1357 **
1358 ** If the object is a manifest, then mention:
1359 **
1360 ** * It's artifact ID
1361 ** * date of check-in
1362 ** * Comment & user
1363 */
1364 int object_description(
1365 int rid, /* The artifact ID */
1366 u32 objdescFlags, /* Flags to control display */
1367 Blob *pDownloadName /* Fill with an appropriate download name */
 
1368 ){
1369 Stmt q;
1370 int cnt = 0;
1371 int nWiki = 0;
1372 int objType = 0;
@@ -1401,10 +1390,11 @@
1401 const char *zVers = db_column_text(&q, 4);
1402 int mPerm = db_column_int(&q, 5);
1403 const char *zBr = db_column_text(&q, 6);
1404 int szFile = db_column_int(&q,7);
1405 int sameFilename = prevName!=0 && fossil_strcmp(zName,prevName)==0;
 
1406 if( sameFilename && !showDetail ){
1407 if( cnt==1 ){
1408 @ %z(href("%R/whatis/%!S",zUuid))[more...]</a>
1409 }
1410 cnt++;
@@ -1747,13 +1737,13 @@
1747 @ %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a> To
1748 @ %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>.</h2>
1749 }else{
1750 @ <h2>Differences From
1751 @ Artifact %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a>:</h2>
1752 object_description(v1, objdescFlags, 0);
1753 @ <h2>To Artifact %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>:</h2>
1754 object_description(v2, objdescFlags, 0);
1755 }
1756 if( pRe ){
1757 @ <b>Only differences that match regular expression "%h(zRe)"
1758 @ are shown.</b>
1759 }
@@ -1937,11 +1927,11 @@
1937 }else{
1938 @ :</h2>
1939 }
1940 blob_zero(&downloadName);
1941 if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
1942 object_description(rid, objdescFlags, &downloadName);
1943 style_submenu_element("Download", "%s/raw/%T?name=%s",
1944 g.zTop, blob_str(&downloadName), zUuid);
1945 @ <hr />
1946 content_get(rid, &content);
1947 @ <blockquote><pre>
@@ -2127,20 +2117,41 @@
2127 Blob downloadName;
2128 int renderAsWiki = 0;
2129 int renderAsHtml = 0;
2130 int objType;
2131 int asText;
2132 const char *zUuid;
2133 u32 objdescFlags = OBJDESC_BASE;
2134 int descOnly = fossil_strcmp(g.zPath,"whatis")==0;
2135 int isFile = fossil_strcmp(g.zPath,"file")==0;
2136 const char *zLn = P("ln");
2137 const char *zName = P("name");
 
2138 HQuery url;
 
 
 
 
2139
 
 
2140 url_initialize(&url, g.zPath);
2141 rid = artifact_from_ci_and_filename(&url, 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2142 if( rid==0 ){
2143 url_add_parameter(&url, "name", zName);
2144 if( isFile ){
2145 /* Do a top-level directory listing in /file mode if no argument
2146 ** specified */
@@ -2183,12 +2194,10 @@
2183 }else{
2184 rid = name_to_rid_www("name");
2185 }
2186 }
2187
2188 login_check_credentials();
2189 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2190 if( rid==0 ){
2191 style_header("No such artifact");
2192 @ Artifact '%h(zName)' does not exist in this repository.
2193 style_footer();
2194 return;
@@ -2196,12 +2205,24 @@
2196 if( descOnly || P("verbose")!=0 ){
2197 url_add_parameter(&url, "verbose", "1");
2198 objdescFlags |= OBJDESC_DETAIL;
2199 }
2200 zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
 
2201 if( isFile ){
2202 @ <h2>Latest version of file '%h(zName)':</h2>
 
 
 
 
 
 
 
 
 
 
 
2203 style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
2204 }else{
2205 @ <h2>Artifact
2206 style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
2207 if( g.perm.Setup ){
@@ -2211,11 +2232,11 @@
2211 }
2212 }
2213 blob_zero(&downloadName);
2214 asText = P("txt")!=0;
2215 if( asText ) objdescFlags &= ~OBJDESC_BASE;
2216 objType = object_description(rid, objdescFlags, &downloadName);
2217 if( !descOnly && P("download")!=0 ){
2218 cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
2219 db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
2220 /*NOTREACHED*/
2221 }
@@ -2226,12 +2247,27 @@
2226 g.zTop, zUuid);
2227 }else{
2228 style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid);
2229 }
2230 }
2231 style_header("%s", isFile ? "File Content" :
2232 descOnly ? "Artifact Description" : "Artifact Content");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2233 if( g.perm.Admin ){
2234 Stmt q;
2235 db_prepare(&q,
2236 "SELECT coalesce(user.login,rcvfrom.uid),"
2237 " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr"
2238
--- src/info.c
+++ src/info.c
@@ -1340,33 +1340,22 @@
1340
1341 /*
1342 ** Possible flags for the second parameter to
1343 ** object_description()
1344 */
1345 #define OBJDESC_DETAIL 0x0001 /* Show more detail */
1346 #define OBJDESC_BASE 0x0002 /* Set <base> using this object */
1347 #endif
1348
1349 /*
1350 ** Write a description of an object to the www reply.
 
 
 
 
 
 
 
 
 
 
 
 
1351 */
1352 int object_description(
1353 int rid, /* The artifact ID for the object to describe */
1354 u32 objdescFlags, /* Flags to control display */
1355 const char *zFileName, /* For file objects, use this name. Can be NULL */
1356 Blob *pDownloadName /* Fill with a good download name. Can be NULL */
1357 ){
1358 Stmt q;
1359 int cnt = 0;
1360 int nWiki = 0;
1361 int objType = 0;
@@ -1401,10 +1390,11 @@
1390 const char *zVers = db_column_text(&q, 4);
1391 int mPerm = db_column_int(&q, 5);
1392 const char *zBr = db_column_text(&q, 6);
1393 int szFile = db_column_int(&q,7);
1394 int sameFilename = prevName!=0 && fossil_strcmp(zName,prevName)==0;
1395 if( zFileName && fossil_strcmp(zName,zFileName)!=0 ) continue;
1396 if( sameFilename && !showDetail ){
1397 if( cnt==1 ){
1398 @ %z(href("%R/whatis/%!S",zUuid))[more...]</a>
1399 }
1400 cnt++;
@@ -1747,13 +1737,13 @@
1737 @ %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a> To
1738 @ %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>.</h2>
1739 }else{
1740 @ <h2>Differences From
1741 @ Artifact %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a>:</h2>
1742 object_description(v1, objdescFlags,0, 0);
1743 @ <h2>To Artifact %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>:</h2>
1744 object_description(v2, objdescFlags,0, 0);
1745 }
1746 if( pRe ){
1747 @ <b>Only differences that match regular expression "%h(zRe)"
1748 @ are shown.</b>
1749 }
@@ -1937,11 +1927,11 @@
1927 }else{
1928 @ :</h2>
1929 }
1930 blob_zero(&downloadName);
1931 if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
1932 object_description(rid, objdescFlags, 0, &downloadName);
1933 style_submenu_element("Download", "%s/raw/%T?name=%s",
1934 g.zTop, blob_str(&downloadName), zUuid);
1935 @ <hr />
1936 content_get(rid, &content);
1937 @ <blockquote><pre>
@@ -2127,20 +2117,41 @@
2117 Blob downloadName;
2118 int renderAsWiki = 0;
2119 int renderAsHtml = 0;
2120 int objType;
2121 int asText;
2122 const char *zUuid = 0;
2123 u32 objdescFlags = OBJDESC_BASE;
2124 int descOnly = fossil_strcmp(g.zPath,"whatis")==0;
2125 int isFile = fossil_strcmp(g.zPath,"file")==0;
2126 const char *zLn = P("ln");
2127 const char *zName = P("name");
2128 const char *zCI = P("ci");
2129 HQuery url;
2130 Blob dirname;
2131 char *zCIUuid = 0;
2132 int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */
2133 char *zHeader = 0;
2134
2135 login_check_credentials();
2136 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2137 url_initialize(&url, g.zPath);
2138 if( zCI && strlen(zCI)==0 ){ zCI = 0; }
2139 if( zCI ){
2140 blob_zero(&dirname);
2141 hyperlinked_path(zName, &dirname, zCI, "dir", "");
2142 blob_reset(&dirname);
2143
2144 if( name_to_uuid2(zCI, "ci", &zCIUuid) ){
2145 isSymbolicCI = (strncmp(zCIUuid, zCI, strlen(zCI)) != 0);
2146 }
2147 }
2148 if( isFile && zName ) {
2149 rid = artifact_from_ci_and_filename(0, "name");
2150 }else{
2151 rid = artifact_from_ci_and_filename(&url, 0);
2152 }
2153 if( rid==0 ){
2154 url_add_parameter(&url, "name", zName);
2155 if( isFile ){
2156 /* Do a top-level directory listing in /file mode if no argument
2157 ** specified */
@@ -2183,12 +2194,10 @@
2194 }else{
2195 rid = name_to_rid_www("name");
2196 }
2197 }
2198
 
 
2199 if( rid==0 ){
2200 style_header("No such artifact");
2201 @ Artifact '%h(zName)' does not exist in this repository.
2202 style_footer();
2203 return;
@@ -2196,12 +2205,24 @@
2205 if( descOnly || P("verbose")!=0 ){
2206 url_add_parameter(&url, "verbose", "1");
2207 objdescFlags |= OBJDESC_DETAIL;
2208 }
2209 zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
2210
2211 if( isFile ){
2212 if( zCI ){
2213 const char *zPath;
2214 Blob path;
2215 blob_zero(&path);
2216 hyperlinked_path(zName, &path, zCI, "dir", "");
2217 zPath = blob_str(&path);
2218 @ <h2>File %s(zPath) of check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>]
2219 @ </h2>
2220 blob_reset(&path);
2221 }else{
2222 @ <h2>Latest version of file '%h(zName)':</h2>
2223 }
2224 style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
2225 }else{
2226 @ <h2>Artifact
2227 style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
2228 if( g.perm.Setup ){
@@ -2211,11 +2232,11 @@
2232 }
2233 }
2234 blob_zero(&downloadName);
2235 asText = P("txt")!=0;
2236 if( asText ) objdescFlags &= ~OBJDESC_BASE;
2237 objType = object_description(rid, objdescFlags, (isFile ? zName : 0),&downloadName);
2238 if( !descOnly && P("download")!=0 ){
2239 cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
2240 db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
2241 /*NOTREACHED*/
2242 }
@@ -2226,12 +2247,27 @@
2247 g.zTop, zUuid);
2248 }else{
2249 style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid);
2250 }
2251 }
2252
2253 if( isFile ){
2254 if( isSymbolicCI ){
2255 zHeader = mprintf("%s at %s", file_tail(zName), zCI);
2256 }else if( zCI ){
2257 zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid);
2258 }else{
2259 zHeader = mprintf("%s", file_tail(zName));
2260 }
2261 }else if( descOnly ){
2262 zHeader = mprintf("Artifact Description [%S]", zUuid);
2263 }else{
2264 zHeader = mprintf("Artifact [%S]", zUuid);
2265 }
2266 style_header("%s", zHeader);
2267 fossil_free(zCIUuid);
2268 fossil_free(zHeader);
2269 if( g.perm.Admin ){
2270 Stmt q;
2271 db_prepare(&q,
2272 "SELECT coalesce(user.login,rcvfrom.uid),"
2273 " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr"
2274
+3 -3
--- src/name.c
+++ src/name.c
@@ -614,11 +614,11 @@
614614
while( db_step(&q)==SQLITE_ROW ){
615615
const char *zUuid = db_column_text(&q, 0);
616616
int rid = db_column_int(&q, 1);
617617
@ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
618618
@ %s(zUuid)</a> -
619
- object_description(rid, 0, 0);
619
+ object_description(rid, 0, 0, 0);
620620
@ </p></li>
621621
}
622622
db_finalize(&q);
623623
db_prepare(&q,
624624
" SELECT tkt_rid, tkt_uuid, title"
@@ -636,11 +636,11 @@
636636
@ <ul></ul>
637637
@ Ticket
638638
hyperlink_to_uuid(zUuid);
639639
@ - %h(zTitle).
640640
@ <ul><li>
641
- object_description(rid, 0, 0);
641
+ object_description(rid, 0, 0, 0);
642642
@ </li></ul>
643643
@ </p></li>
644644
}
645645
db_finalize(&q);
646646
db_prepare(&q,
@@ -652,11 +652,11 @@
652652
int rid = db_column_int(&q, 0);
653653
const char* zUuid = db_column_text(&q, 1);
654654
@ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
655655
@ %s(zUuid)</a> -
656656
@ <ul><li>
657
- object_description(rid, 0, 0);
657
+ object_description(rid, 0, 0, 0);
658658
@ </li></ul>
659659
@ </p></li>
660660
}
661661
@ </ol>
662662
db_finalize(&q);
663663
--- src/name.c
+++ src/name.c
@@ -614,11 +614,11 @@
614 while( db_step(&q)==SQLITE_ROW ){
615 const char *zUuid = db_column_text(&q, 0);
616 int rid = db_column_int(&q, 1);
617 @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
618 @ %s(zUuid)</a> -
619 object_description(rid, 0, 0);
620 @ </p></li>
621 }
622 db_finalize(&q);
623 db_prepare(&q,
624 " SELECT tkt_rid, tkt_uuid, title"
@@ -636,11 +636,11 @@
636 @ <ul></ul>
637 @ Ticket
638 hyperlink_to_uuid(zUuid);
639 @ - %h(zTitle).
640 @ <ul><li>
641 object_description(rid, 0, 0);
642 @ </li></ul>
643 @ </p></li>
644 }
645 db_finalize(&q);
646 db_prepare(&q,
@@ -652,11 +652,11 @@
652 int rid = db_column_int(&q, 0);
653 const char* zUuid = db_column_text(&q, 1);
654 @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
655 @ %s(zUuid)</a> -
656 @ <ul><li>
657 object_description(rid, 0, 0);
658 @ </li></ul>
659 @ </p></li>
660 }
661 @ </ol>
662 db_finalize(&q);
663
--- src/name.c
+++ src/name.c
@@ -614,11 +614,11 @@
614 while( db_step(&q)==SQLITE_ROW ){
615 const char *zUuid = db_column_text(&q, 0);
616 int rid = db_column_int(&q, 1);
617 @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
618 @ %s(zUuid)</a> -
619 object_description(rid, 0, 0, 0);
620 @ </p></li>
621 }
622 db_finalize(&q);
623 db_prepare(&q,
624 " SELECT tkt_rid, tkt_uuid, title"
@@ -636,11 +636,11 @@
636 @ <ul></ul>
637 @ Ticket
638 hyperlink_to_uuid(zUuid);
639 @ - %h(zTitle).
640 @ <ul><li>
641 object_description(rid, 0, 0, 0);
642 @ </li></ul>
643 @ </p></li>
644 }
645 db_finalize(&q);
646 db_prepare(&q,
@@ -652,11 +652,11 @@
652 int rid = db_column_int(&q, 0);
653 const char* zUuid = db_column_text(&q, 1);
654 @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
655 @ %s(zUuid)</a> -
656 @ <ul><li>
657 object_description(rid, 0, 0, 0);
658 @ </li></ul>
659 @ </p></li>
660 }
661 @ </ol>
662 db_finalize(&q);
663

Keyboard Shortcuts

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