Fossil SCM

Enhance the repository list page so that it shows the name of project for each repository, and so that the "Last Modified" time is based on the most recent event in the repository, not the repository file mtime.

drh 2018-11-02 13:48 trunk
Commit cdea59dc5c832837e9012c6a29aef8b36f0b2dff4f4bc44701f448e4f2ec4515
1 file changed +73 -13
+73 -13
--- src/repolist.c
+++ src/repolist.c
@@ -18,16 +18,61 @@
1818
** This module contains code to implement the repository list page when
1919
** "fossil server" or "fossil ui" is serving a directory full of repositories.
2020
*/
2121
#include "config.h"
2222
#include "repolist.h"
23
+
24
+#if INTERFACE
25
+/*
26
+** Return value from the remote_repo_info() command. zRepoName is the
27
+** input. All other fields are outputs.
28
+*/
29
+struct RepoInfo {
30
+ char *zRepoName; /* Name of the repository file */
31
+ int isValid; /* True if zRepoName is a valid Fossil repository */
32
+ char *zProjName; /* Project Name. Memory from fossil_malloc() */
33
+ double rMTime; /* Last update. Julian day number */
34
+};
35
+#endif
36
+
37
+/*
38
+** Discover information about the repository given by
39
+** pRepo->zRepoName.
40
+*/
41
+void remote_repo_info(RepoInfo *pRepo){
42
+ sqlite3 *db;
43
+ sqlite3_stmt *pStmt;
44
+ int rc;
45
+
46
+ pRepo->isValid = 0;
47
+ pRepo->zProjName = 0;
48
+ pRepo->rMTime = 0.0;
49
+
50
+ rc = sqlite3_open(pRepo->zRepoName, &db);
51
+ if( rc ) return;
52
+ rc = sqlite3_prepare_v2(db, "SELECT value FROM config"
53
+ " WHERE name='project-name'",
54
+ -1, &pStmt, 0);
55
+ if( rc ) return;
56
+ if( sqlite3_step(pStmt)==SQLITE_ROW ){
57
+ pRepo->zProjName = fossil_strdup((char*)sqlite3_column_text(pStmt,0));
58
+ }
59
+ sqlite3_finalize(pStmt);
60
+ rc = sqlite3_prepare_v2(db, "SELECT max(mtime) FROM event", -1, &pStmt, 0);
61
+ if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
62
+ pRepo->rMTime = sqlite3_column_double(pStmt,0);
63
+ }
64
+ sqlite3_finalize(pStmt);
65
+ sqlite3_close(db);
66
+ pRepo->isValid = 1;
67
+}
2368
2469
/*
2570
** Generate a web-page that lists all repositories located under the
2671
** g.zRepositoryName directory and return non-zero.
2772
**
28
-** For the special case when g.zRepositoryName a non-chroot-jail "/",
73
+** For the special case when g.zRepositoryName is a non-chroot-jail "/",
2974
** compose the list using the "repo:" entries in the global_config
3075
** table of the configuration database. These entries comprise all
3176
** of the repositories known to the "all" command. The special case
3277
** processing is disallowed for chroot jails because g.zRepositoryName
3378
** is always "/" inside a chroot jail and so it cannot be used as a flag
@@ -80,25 +125,29 @@
80125
@ </head>
81126
@ <body>
82127
n = db_int(0, "SELECT count(*) FROM sfile");
83128
if( n>0 ){
84129
Stmt q;
85
- sqlite3_int64 iNow, iMTime;
130
+ double rNow;
86131
@ <h1 align="center">Fossil Repositories</h1>
87132
@ <table border="0" class="sortable" data-init-sort="1" \
88
- @ data-column-types="tnk"><thead>
89
- @ <tr><th>Filename<th width="20"><th>Last Modified</tr>
133
+ @ data-column-types="tntnk"><thead>
134
+ @ <tr><th>Filename<th width="20">\
135
+ @ <th>Project Name<th width="20">\
136
+ @ <th>Last Modified</tr>
90137
@ </thead><tbody>
91138
db_prepare(&q, "SELECT pathname"
92139
" FROM sfile ORDER BY pathname COLLATE nocase;");
93
- iNow = db_int64(0, "SELECT strftime('%%s','now')");
140
+ rNow = db_double(0, "SELECT julianday('now')");
94141
while( db_step(&q)==SQLITE_ROW ){
95142
const char *zName = db_column_text(&q, 0);
96143
int nName = (int)strlen(zName);
97144
char *zUrl;
98145
char *zAge;
99146
char *zFull;
147
+ RepoInfo x;
148
+ int iAge;
100149
if( nName<7 ) continue;
101150
zUrl = sqlite3_mprintf("%.*s", nName-7, zName);
102151
if( zName[0]=='/'
103152
#ifdef _WIN32
104153
|| sqlite3_strglob("[a-zA-Z]:/*", zName)==0
@@ -108,31 +157,42 @@
108157
}else if ( allRepo ){
109158
zFull = mprintf("/%s", zName);
110159
}else{
111160
zFull = mprintf("%s/%s", g.zRepositoryName, zName);
112161
}
113
- iMTime = file_mtime(zFull, ExtFILE);
162
+ x.zRepoName = zFull;
163
+ remote_repo_info(&x);
114164
fossil_free(zFull);
115
- if( iMTime<=0 ){
165
+ if( !x.isValid ){
116166
zAge = mprintf("...");
167
+ iAge = 0;
117168
}else{
118
- zAge = human_readable_age((iNow - iMTime)/86400.0);
169
+ iAge = (rNow - x.rMTime)*86400;
170
+ if( iAge<0 ) x.rMTime = rNow;
171
+ zAge = human_readable_age(rNow - x.rMTime);
119172
}
173
+ @ <tr><td valign="top">\
120174
if( sqlite3_strglob("*.fossil", zName)!=0 ){
121175
/* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands
122176
** do not work for repositories whose names do not end in ".fossil".
123177
** So do not hyperlink those cases. */
124
- @ <tr><td>%h(zName)
178
+ @ %h(zName)
125179
} else if( sqlite3_strglob("*/.*", zName)==0 ){
126180
/* Do not show hidden repos */
127
- @ <tr><td>%h(zName) (hidden)
181
+ @ %h(zName) (hidden)
128182
} else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){
129
- @ <tr><td><a href="%R/%T(zUrl)/home" target="_blank">/%h(zName)</a>
183
+ @ <a href="%R/%T(zUrl)/home" target="_blank">/%h(zName)</a>
184
+ }else{
185
+ @ <a href="%R/%T(zUrl)/home" target="_blank">%h(zName)</a>
186
+ }
187
+ if( x.zProjName ){
188
+ @ <td></td><td>%h(x.zProjName)</td>
189
+ fossil_free(x.zProjName);
130190
}else{
131
- @ <tr><td><a href="%R/%T(zUrl)/home" target="_blank">%h(zName)</a>
191
+ @ <td></td><td></td>
132192
}
133
- @ <td></td><td data-sortkey='%010llx(iNow - iMTime)'>%h(zAge)</tr>
193
+ @ <td></td><td data-sortkey='%08x(iAge)'>%h(zAge)</tr>
134194
fossil_free(zAge);
135195
sqlite3_free(zUrl);
136196
}
137197
@ </tbody></table>
138198
}else{
139199
--- src/repolist.c
+++ src/repolist.c
@@ -18,16 +18,61 @@
18 ** This module contains code to implement the repository list page when
19 ** "fossil server" or "fossil ui" is serving a directory full of repositories.
20 */
21 #include "config.h"
22 #include "repolist.h"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
24 /*
25 ** Generate a web-page that lists all repositories located under the
26 ** g.zRepositoryName directory and return non-zero.
27 **
28 ** For the special case when g.zRepositoryName a non-chroot-jail "/",
29 ** compose the list using the "repo:" entries in the global_config
30 ** table of the configuration database. These entries comprise all
31 ** of the repositories known to the "all" command. The special case
32 ** processing is disallowed for chroot jails because g.zRepositoryName
33 ** is always "/" inside a chroot jail and so it cannot be used as a flag
@@ -80,25 +125,29 @@
80 @ </head>
81 @ <body>
82 n = db_int(0, "SELECT count(*) FROM sfile");
83 if( n>0 ){
84 Stmt q;
85 sqlite3_int64 iNow, iMTime;
86 @ <h1 align="center">Fossil Repositories</h1>
87 @ <table border="0" class="sortable" data-init-sort="1" \
88 @ data-column-types="tnk"><thead>
89 @ <tr><th>Filename<th width="20"><th>Last Modified</tr>
 
 
90 @ </thead><tbody>
91 db_prepare(&q, "SELECT pathname"
92 " FROM sfile ORDER BY pathname COLLATE nocase;");
93 iNow = db_int64(0, "SELECT strftime('%%s','now')");
94 while( db_step(&q)==SQLITE_ROW ){
95 const char *zName = db_column_text(&q, 0);
96 int nName = (int)strlen(zName);
97 char *zUrl;
98 char *zAge;
99 char *zFull;
 
 
100 if( nName<7 ) continue;
101 zUrl = sqlite3_mprintf("%.*s", nName-7, zName);
102 if( zName[0]=='/'
103 #ifdef _WIN32
104 || sqlite3_strglob("[a-zA-Z]:/*", zName)==0
@@ -108,31 +157,42 @@
108 }else if ( allRepo ){
109 zFull = mprintf("/%s", zName);
110 }else{
111 zFull = mprintf("%s/%s", g.zRepositoryName, zName);
112 }
113 iMTime = file_mtime(zFull, ExtFILE);
 
114 fossil_free(zFull);
115 if( iMTime<=0 ){
116 zAge = mprintf("...");
 
117 }else{
118 zAge = human_readable_age((iNow - iMTime)/86400.0);
 
 
119 }
 
120 if( sqlite3_strglob("*.fossil", zName)!=0 ){
121 /* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands
122 ** do not work for repositories whose names do not end in ".fossil".
123 ** So do not hyperlink those cases. */
124 @ <tr><td>%h(zName)
125 } else if( sqlite3_strglob("*/.*", zName)==0 ){
126 /* Do not show hidden repos */
127 @ <tr><td>%h(zName) (hidden)
128 } else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){
129 @ <tr><td><a href="%R/%T(zUrl)/home" target="_blank">/%h(zName)</a>
 
 
 
 
 
 
130 }else{
131 @ <tr><td><a href="%R/%T(zUrl)/home" target="_blank">%h(zName)</a>
132 }
133 @ <td></td><td data-sortkey='%010llx(iNow - iMTime)'>%h(zAge)</tr>
134 fossil_free(zAge);
135 sqlite3_free(zUrl);
136 }
137 @ </tbody></table>
138 }else{
139
--- src/repolist.c
+++ src/repolist.c
@@ -18,16 +18,61 @@
18 ** This module contains code to implement the repository list page when
19 ** "fossil server" or "fossil ui" is serving a directory full of repositories.
20 */
21 #include "config.h"
22 #include "repolist.h"
23
24 #if INTERFACE
25 /*
26 ** Return value from the remote_repo_info() command. zRepoName is the
27 ** input. All other fields are outputs.
28 */
29 struct RepoInfo {
30 char *zRepoName; /* Name of the repository file */
31 int isValid; /* True if zRepoName is a valid Fossil repository */
32 char *zProjName; /* Project Name. Memory from fossil_malloc() */
33 double rMTime; /* Last update. Julian day number */
34 };
35 #endif
36
37 /*
38 ** Discover information about the repository given by
39 ** pRepo->zRepoName.
40 */
41 void remote_repo_info(RepoInfo *pRepo){
42 sqlite3 *db;
43 sqlite3_stmt *pStmt;
44 int rc;
45
46 pRepo->isValid = 0;
47 pRepo->zProjName = 0;
48 pRepo->rMTime = 0.0;
49
50 rc = sqlite3_open(pRepo->zRepoName, &db);
51 if( rc ) return;
52 rc = sqlite3_prepare_v2(db, "SELECT value FROM config"
53 " WHERE name='project-name'",
54 -1, &pStmt, 0);
55 if( rc ) return;
56 if( sqlite3_step(pStmt)==SQLITE_ROW ){
57 pRepo->zProjName = fossil_strdup((char*)sqlite3_column_text(pStmt,0));
58 }
59 sqlite3_finalize(pStmt);
60 rc = sqlite3_prepare_v2(db, "SELECT max(mtime) FROM event", -1, &pStmt, 0);
61 if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
62 pRepo->rMTime = sqlite3_column_double(pStmt,0);
63 }
64 sqlite3_finalize(pStmt);
65 sqlite3_close(db);
66 pRepo->isValid = 1;
67 }
68
69 /*
70 ** Generate a web-page that lists all repositories located under the
71 ** g.zRepositoryName directory and return non-zero.
72 **
73 ** For the special case when g.zRepositoryName is a non-chroot-jail "/",
74 ** compose the list using the "repo:" entries in the global_config
75 ** table of the configuration database. These entries comprise all
76 ** of the repositories known to the "all" command. The special case
77 ** processing is disallowed for chroot jails because g.zRepositoryName
78 ** is always "/" inside a chroot jail and so it cannot be used as a flag
@@ -80,25 +125,29 @@
125 @ </head>
126 @ <body>
127 n = db_int(0, "SELECT count(*) FROM sfile");
128 if( n>0 ){
129 Stmt q;
130 double rNow;
131 @ <h1 align="center">Fossil Repositories</h1>
132 @ <table border="0" class="sortable" data-init-sort="1" \
133 @ data-column-types="tntnk"><thead>
134 @ <tr><th>Filename<th width="20">\
135 @ <th>Project Name<th width="20">\
136 @ <th>Last Modified</tr>
137 @ </thead><tbody>
138 db_prepare(&q, "SELECT pathname"
139 " FROM sfile ORDER BY pathname COLLATE nocase;");
140 rNow = db_double(0, "SELECT julianday('now')");
141 while( db_step(&q)==SQLITE_ROW ){
142 const char *zName = db_column_text(&q, 0);
143 int nName = (int)strlen(zName);
144 char *zUrl;
145 char *zAge;
146 char *zFull;
147 RepoInfo x;
148 int iAge;
149 if( nName<7 ) continue;
150 zUrl = sqlite3_mprintf("%.*s", nName-7, zName);
151 if( zName[0]=='/'
152 #ifdef _WIN32
153 || sqlite3_strglob("[a-zA-Z]:/*", zName)==0
@@ -108,31 +157,42 @@
157 }else if ( allRepo ){
158 zFull = mprintf("/%s", zName);
159 }else{
160 zFull = mprintf("%s/%s", g.zRepositoryName, zName);
161 }
162 x.zRepoName = zFull;
163 remote_repo_info(&x);
164 fossil_free(zFull);
165 if( !x.isValid ){
166 zAge = mprintf("...");
167 iAge = 0;
168 }else{
169 iAge = (rNow - x.rMTime)*86400;
170 if( iAge<0 ) x.rMTime = rNow;
171 zAge = human_readable_age(rNow - x.rMTime);
172 }
173 @ <tr><td valign="top">\
174 if( sqlite3_strglob("*.fossil", zName)!=0 ){
175 /* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands
176 ** do not work for repositories whose names do not end in ".fossil".
177 ** So do not hyperlink those cases. */
178 @ %h(zName)
179 } else if( sqlite3_strglob("*/.*", zName)==0 ){
180 /* Do not show hidden repos */
181 @ %h(zName) (hidden)
182 } else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){
183 @ <a href="%R/%T(zUrl)/home" target="_blank">/%h(zName)</a>
184 }else{
185 @ <a href="%R/%T(zUrl)/home" target="_blank">%h(zName)</a>
186 }
187 if( x.zProjName ){
188 @ <td></td><td>%h(x.zProjName)</td>
189 fossil_free(x.zProjName);
190 }else{
191 @ <td></td><td></td>
192 }
193 @ <td></td><td data-sortkey='%08x(iAge)'>%h(zAge)</tr>
194 fossil_free(zAge);
195 sqlite3_free(zUrl);
196 }
197 @ </tbody></table>
198 }else{
199

Keyboard Shortcuts

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