Fossil SCM
Two new settings "show-repolist-desc" and "show-repolist-lg" control whether or not the description and the login-group are shown on the repolist, respectively. These settings must be global to be effective. They default to "off".
Commit
1760fa2bf4ab1285ed606c52e77f6f1946a1e5b4560bfb1507d97d4dd84945bd
Parent
af78e282bb0a068…
1 file changed
+87
-29
+87
-29
| --- src/repolist.c | ||
| +++ src/repolist.c | ||
| @@ -101,10 +101,25 @@ | ||
| 101 | 101 | finish_repo_list: |
| 102 | 102 | g.dbIgnoreErrors--; |
| 103 | 103 | sqlite3_close(db); |
| 104 | 104 | } |
| 105 | 105 | |
| 106 | +/* | |
| 107 | +** SETTING: show-repolist-desc boolean default=off | |
| 108 | +** | |
| 109 | +** If the value of this setting is "1" globally, then the repository-list | |
| 110 | +** page will show the description of each repository. This setting only | |
| 111 | +** has effect when it is in the global setting database. | |
| 112 | +*/ | |
| 113 | +/* | |
| 114 | +** SETTING: show-repolist-lg boolean default=off | |
| 115 | +** | |
| 116 | +** If the value of this setting is "1" globally, then the repository-list | |
| 117 | +** page will show the login-group for each repository. This setting only | |
| 118 | +** has effect when it is in the global setting database. | |
| 119 | +*/ | |
| 120 | + | |
| 106 | 121 | /* |
| 107 | 122 | ** Generate a web-page that lists all repositories located under the |
| 108 | 123 | ** g.zRepositoryName directory and return non-zero. |
| 109 | 124 | ** |
| 110 | 125 | ** For the special case when g.zRepositoryName is a non-chroot-jail "/", |
| @@ -126,12 +141,19 @@ | ||
| 126 | 141 | int allRepo; /* True if running "fossil ui all". |
| 127 | 142 | ** False if a directory scan of base for repos */ |
| 128 | 143 | Blob html; /* Html for the body of the repository list */ |
| 129 | 144 | char *zSkinRepo = 0; /* Name of the repository database used for skins */ |
| 130 | 145 | char *zSkinUrl = 0; /* URL for the skin database */ |
| 146 | + int bShowDesc = 0; /* True to show the description column */ | |
| 147 | + int bShowLg = 0; /* True to show the login-group column */ | |
| 131 | 148 | |
| 132 | 149 | assert( g.db==0 ); |
| 150 | + db_open_config(1, 0); | |
| 151 | + bShowDesc = (0!=db_int(0, "SELECT value FROM global_config" | |
| 152 | + " WHERE name='show-repolist-desc'")); | |
| 153 | + bShowLg = (0!=db_int(0, "SELECT value FROM global_config" | |
| 154 | + " WHERE name='show-repolist-lg'")); | |
| 133 | 155 | blob_init(&html, 0, 0); |
| 134 | 156 | if( fossil_strcmp(g.zRepositoryName,"/")==0 && !g.fJail ){ |
| 135 | 157 | /* For the special case of the "repository directory" being "/", |
| 136 | 158 | ** show all of the repositories named in the ~/.fossil database. |
| 137 | 159 | ** |
| @@ -138,11 +160,10 @@ | ||
| 138 | 160 | ** On unix systems, then entries are of the form "repo:/home/..." |
| 139 | 161 | ** and on Windows systems they are like on unix, starting with a "/" |
| 140 | 162 | ** or they can begin with a drive letter: "repo:C:/Users/...". In either |
| 141 | 163 | ** case, we want returned path to omit any initial "/". |
| 142 | 164 | */ |
| 143 | - db_open_config(1, 0); | |
| 144 | 165 | db_multi_exec( |
| 145 | 166 | "CREATE TEMP VIEW sfile AS" |
| 146 | 167 | " SELECT ltrim(substr(name,6),'/') AS 'pathname' FROM global_config" |
| 147 | 168 | " WHERE name GLOB 'repo:*'" |
| 148 | 169 | ); |
| @@ -150,10 +171,12 @@ | ||
| 150 | 171 | }else{ |
| 151 | 172 | /* The default case: All repositories under the g.zRepositoryName |
| 152 | 173 | ** directory. |
| 153 | 174 | */ |
| 154 | 175 | blob_init(&base, g.zRepositoryName, -1); |
| 176 | + db_close(0); | |
| 177 | + assert( g.db==0 ); | |
| 155 | 178 | sqlite3_open(":memory:", &g.db); |
| 156 | 179 | db_multi_exec("CREATE TABLE sfile(pathname TEXT);"); |
| 157 | 180 | db_multi_exec("CREATE TABLE vfile(pathname);"); |
| 158 | 181 | vfile_scan(&base, blob_size(&base), 0, 0, 0, ExtFILE); |
| 159 | 182 | db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'" |
| @@ -171,19 +194,50 @@ | ||
| 171 | 194 | g.localOpen = 0; |
| 172 | 195 | return 0; |
| 173 | 196 | }else{ |
| 174 | 197 | Stmt q; |
| 175 | 198 | double rNow; |
| 176 | - blob_append_sql(&html, | |
| 199 | + char zType[16]; /* Column type letters for class "sortable" */ | |
| 200 | + int nType; | |
| 201 | + zType[0] = 't'; /* Repo name */ | |
| 202 | + zType[1] = 'x'; /* Space between repo-name and project-name */ | |
| 203 | + zType[2] = 't'; /* Project name */ | |
| 204 | + nType = 3; | |
| 205 | + if( bShowDesc ){ | |
| 206 | + zType[nType++] = 'x'; /* Space between name and description */ | |
| 207 | + zType[nType++] = 't'; /* Project description */ | |
| 208 | + } | |
| 209 | + zType[nType++] = 'x'; /* space before age */ | |
| 210 | + zType[nType++] = 'k'; /* Project age */ | |
| 211 | + if( bShowLg ){ | |
| 212 | + zType[nType++] = 'x'; /* space before login-group */ | |
| 213 | + zType[nType++] = 't'; /* Login Group */ | |
| 214 | + } | |
| 215 | + zType[nType] = 0; | |
| 216 | + blob_appendf(&html, | |
| 177 | 217 | "<table border='0' class='sortable' data-init-sort='1'" |
| 178 | - " data-column-types='txtxtxkxt' cellspacing='0' cellpadding='0'><thead>\n" | |
| 179 | - "<tr><th>Filename<th width='7'>" | |
| 180 | - "<th width='25%%'>Project Name<th width='10'>" | |
| 181 | - "<th width='25%%'>Project Description<th width='5'>" | |
| 182 | - "<th><nobr>Last Modified</nobr><th width='1'>" | |
| 183 | - "<th><nobr>Login Group</nobr></tr>\n" | |
| 184 | - "</thead><tbody>\n"); | |
| 218 | + " data-column-types='%s' cellspacing='0' cellpadding='0'><thead>\n" | |
| 219 | + "<tr><th>Filename</th><th> </th>\n" | |
| 220 | + "<th%s><nobr>Project Name</nobr></th>\n", | |
| 221 | + zType, (bShowDesc ? " width='25%'" : "")); | |
| 222 | + if( bShowDesc ){ | |
| 223 | + blob_appendf(&html, | |
| 224 | + "<th> </th>\n" | |
| 225 | + "<th width='25%%'><nobr>Project Description</nobr></th>\n" | |
| 226 | + ); | |
| 227 | + } | |
| 228 | + blob_appendf(&html, | |
| 229 | + "<th> </th>" | |
| 230 | + "<th><nobr>Last Modified</nobr></th>\n" | |
| 231 | + ); | |
| 232 | + if( bShowLg ){ | |
| 233 | + blob_appendf(&html, | |
| 234 | + "<th> </th>" | |
| 235 | + "<th><nobr>Login Group</nobr></th></tr>\n" | |
| 236 | + ); | |
| 237 | + } | |
| 238 | + blob_appendf(&html,"</thead><tbody>\n"); | |
| 185 | 239 | db_prepare(&q, "SELECT pathname" |
| 186 | 240 | " FROM sfile ORDER BY pathname COLLATE nocase;"); |
| 187 | 241 | rNow = db_double(0, "SELECT julianday('now')"); |
| 188 | 242 | while( db_step(&q)==SQLITE_ROW ){ |
| 189 | 243 | const char *zName = db_column_text(&q, 0); |
| @@ -242,21 +296,21 @@ | ||
| 242 | 296 | if( x.rMTime==0.0 ){ |
| 243 | 297 | /* This repository has no entry in the "event" table. |
| 244 | 298 | ** Its age will still be maximum, so data-sortkey will work. */ |
| 245 | 299 | zAge = mprintf("unknown"); |
| 246 | 300 | } |
| 247 | - blob_append_sql(&html, "<tr><td valign='top'><nobr>"); | |
| 301 | + blob_appendf(&html, "<tr><td valign='top'><nobr>"); | |
| 248 | 302 | if( !file_ends_with_repository_extension(zName,0) ){ |
| 249 | 303 | /* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands |
| 250 | 304 | ** do not work for repositories whose names do not end in ".fossil". |
| 251 | 305 | ** So do not hyperlink those cases. */ |
| 252 | - blob_append_sql(&html,"%h",zName); | |
| 306 | + blob_appendf(&html,"%h",zName); | |
| 253 | 307 | } else if( sqlite3_strglob("*/.*", zName)==0 ){ |
| 254 | 308 | /* Do not show hyperlinks for hidden repos */ |
| 255 | - blob_append_sql(&html, "%h (hidden)", zName); | |
| 309 | + blob_appendf(&html, "%h (hidden)", zName); | |
| 256 | 310 | } else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){ |
| 257 | - blob_append_sql(&html, | |
| 311 | + blob_appendf(&html, | |
| 258 | 312 | "<a href='%R/%T/home' target='_blank'>/%h</a>\n", |
| 259 | 313 | zUrl, zName); |
| 260 | 314 | }else if( file_ends_with_repository_extension(zName,1) ){ |
| 261 | 315 | /* As described in |
| 262 | 316 | ** https://fossil-scm.org/forum/info/f50f647c97c72fc1: if |
| @@ -273,56 +327,60 @@ | ||
| 273 | 327 | , zDirPart |
| 274 | 328 | #if USE_SEE |
| 275 | 329 | , zDirPart |
| 276 | 330 | #endif |
| 277 | 331 | ) ){ |
| 278 | - blob_append_sql(&html, | |
| 332 | + blob_appendf(&html, | |
| 279 | 333 | "<s>%h</s> (directory/repo name collision)\n", |
| 280 | 334 | zName); |
| 281 | 335 | }else{ |
| 282 | - blob_append_sql(&html, | |
| 336 | + blob_appendf(&html, | |
| 283 | 337 | "<a href='%R/%T/home' target='_blank'>%h</a>\n", |
| 284 | 338 | zUrl, zName); |
| 285 | 339 | } |
| 286 | 340 | fossil_free(zDirPart); |
| 287 | 341 | }else{ |
| 288 | - blob_append_sql(&html, | |
| 342 | + blob_appendf(&html, | |
| 289 | 343 | "<a href='%R/%T/home' target='_blank'>%h</a>\n", |
| 290 | 344 | zUrl, zName); |
| 291 | 345 | } |
| 292 | - blob_append_sql(&html,"</nobr>"); | |
| 346 | + blob_appendf(&html,"</nobr></td>\n"); | |
| 293 | 347 | if( x.zProjName ){ |
| 294 | - blob_append_sql(&html, "<td></td><td valign='top'>%h</td>\n", | |
| 295 | - x.zProjName); | |
| 348 | + blob_appendf(&html, "<td> </td><td valign='top'>%h</td>\n", | |
| 349 | + x.zProjName); | |
| 296 | 350 | fossil_free(x.zProjName); |
| 297 | 351 | }else{ |
| 298 | - blob_append_sql(&html, "<td></td><td></td>\n"); | |
| 352 | + blob_appendf(&html, "<td> </td><td></td>\n"); | |
| 299 | 353 | } |
| 300 | - if( x.zProjDesc ){ | |
| 301 | - blob_append_sql(&html, "<td></td><td valign='top'>%h</td>\n", | |
| 354 | + if( !bShowDesc ){ | |
| 355 | + /* Do nothing */ | |
| 356 | + }else if( x.zProjDesc ){ | |
| 357 | + blob_appendf(&html, "<td> </td><td valign='top'>%h</td>\n", | |
| 302 | 358 | x.zProjDesc); |
| 303 | 359 | fossil_free(x.zProjDesc); |
| 304 | 360 | }else{ |
| 305 | - blob_append_sql(&html, "<td></td><td></td>\n"); | |
| 361 | + blob_appendf(&html, "<td> </td><td></td>\n"); | |
| 306 | 362 | } |
| 307 | - blob_append_sql(&html, | |
| 308 | - "<td></td><td data-sortkey='%08x' align='center' valign='top'>" | |
| 363 | + blob_appendf(&html, | |
| 364 | + "<td> </td><td data-sortkey='%08x' align='center' valign='top'>" | |
| 309 | 365 | "<nobr>%h</nobr></td>\n", |
| 310 | 366 | (int)iAge, zAge); |
| 311 | 367 | fossil_free(zAge); |
| 312 | - if( x.zLoginGroup ){ | |
| 313 | - blob_append_sql(&html, "<td></td><td valign='top'>" | |
| 368 | + if( !bShowLg ){ | |
| 369 | + blob_appendf(&html, "</tr>\n"); | |
| 370 | + }else if( x.zLoginGroup ){ | |
| 371 | + blob_appendf(&html, "<td> </td><td valign='top'>" | |
| 314 | 372 | "<nobr>%h</nobr></td></tr>\n", |
| 315 | 373 | x.zLoginGroup); |
| 316 | 374 | fossil_free(x.zLoginGroup); |
| 317 | 375 | }else{ |
| 318 | - blob_append_sql(&html, "<td></td><td></td></tr>\n"); | |
| 376 | + blob_appendf(&html, "<td> </td><td></td></tr>\n"); | |
| 319 | 377 | } |
| 320 | 378 | sqlite3_free(zUrl); |
| 321 | 379 | } |
| 322 | 380 | db_finalize(&q); |
| 323 | - blob_append_sql(&html,"</tbody></table>\n"); | |
| 381 | + blob_appendf(&html,"</tbody></table>\n"); | |
| 324 | 382 | } |
| 325 | 383 | if( zSkinRepo ){ |
| 326 | 384 | char *zNewBase = mprintf("%s/%s", g.zBaseURL, zSkinUrl); |
| 327 | 385 | g.zBaseURL = 0; |
| 328 | 386 | set_base_url(zNewBase); |
| 329 | 387 |
| --- src/repolist.c | |
| +++ src/repolist.c | |
| @@ -101,10 +101,25 @@ | |
| 101 | finish_repo_list: |
| 102 | g.dbIgnoreErrors--; |
| 103 | sqlite3_close(db); |
| 104 | } |
| 105 | |
| 106 | /* |
| 107 | ** Generate a web-page that lists all repositories located under the |
| 108 | ** g.zRepositoryName directory and return non-zero. |
| 109 | ** |
| 110 | ** For the special case when g.zRepositoryName is a non-chroot-jail "/", |
| @@ -126,12 +141,19 @@ | |
| 126 | int allRepo; /* True if running "fossil ui all". |
| 127 | ** False if a directory scan of base for repos */ |
| 128 | Blob html; /* Html for the body of the repository list */ |
| 129 | char *zSkinRepo = 0; /* Name of the repository database used for skins */ |
| 130 | char *zSkinUrl = 0; /* URL for the skin database */ |
| 131 | |
| 132 | assert( g.db==0 ); |
| 133 | blob_init(&html, 0, 0); |
| 134 | if( fossil_strcmp(g.zRepositoryName,"/")==0 && !g.fJail ){ |
| 135 | /* For the special case of the "repository directory" being "/", |
| 136 | ** show all of the repositories named in the ~/.fossil database. |
| 137 | ** |
| @@ -138,11 +160,10 @@ | |
| 138 | ** On unix systems, then entries are of the form "repo:/home/..." |
| 139 | ** and on Windows systems they are like on unix, starting with a "/" |
| 140 | ** or they can begin with a drive letter: "repo:C:/Users/...". In either |
| 141 | ** case, we want returned path to omit any initial "/". |
| 142 | */ |
| 143 | db_open_config(1, 0); |
| 144 | db_multi_exec( |
| 145 | "CREATE TEMP VIEW sfile AS" |
| 146 | " SELECT ltrim(substr(name,6),'/') AS 'pathname' FROM global_config" |
| 147 | " WHERE name GLOB 'repo:*'" |
| 148 | ); |
| @@ -150,10 +171,12 @@ | |
| 150 | }else{ |
| 151 | /* The default case: All repositories under the g.zRepositoryName |
| 152 | ** directory. |
| 153 | */ |
| 154 | blob_init(&base, g.zRepositoryName, -1); |
| 155 | sqlite3_open(":memory:", &g.db); |
| 156 | db_multi_exec("CREATE TABLE sfile(pathname TEXT);"); |
| 157 | db_multi_exec("CREATE TABLE vfile(pathname);"); |
| 158 | vfile_scan(&base, blob_size(&base), 0, 0, 0, ExtFILE); |
| 159 | db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'" |
| @@ -171,19 +194,50 @@ | |
| 171 | g.localOpen = 0; |
| 172 | return 0; |
| 173 | }else{ |
| 174 | Stmt q; |
| 175 | double rNow; |
| 176 | blob_append_sql(&html, |
| 177 | "<table border='0' class='sortable' data-init-sort='1'" |
| 178 | " data-column-types='txtxtxkxt' cellspacing='0' cellpadding='0'><thead>\n" |
| 179 | "<tr><th>Filename<th width='7'>" |
| 180 | "<th width='25%%'>Project Name<th width='10'>" |
| 181 | "<th width='25%%'>Project Description<th width='5'>" |
| 182 | "<th><nobr>Last Modified</nobr><th width='1'>" |
| 183 | "<th><nobr>Login Group</nobr></tr>\n" |
| 184 | "</thead><tbody>\n"); |
| 185 | db_prepare(&q, "SELECT pathname" |
| 186 | " FROM sfile ORDER BY pathname COLLATE nocase;"); |
| 187 | rNow = db_double(0, "SELECT julianday('now')"); |
| 188 | while( db_step(&q)==SQLITE_ROW ){ |
| 189 | const char *zName = db_column_text(&q, 0); |
| @@ -242,21 +296,21 @@ | |
| 242 | if( x.rMTime==0.0 ){ |
| 243 | /* This repository has no entry in the "event" table. |
| 244 | ** Its age will still be maximum, so data-sortkey will work. */ |
| 245 | zAge = mprintf("unknown"); |
| 246 | } |
| 247 | blob_append_sql(&html, "<tr><td valign='top'><nobr>"); |
| 248 | if( !file_ends_with_repository_extension(zName,0) ){ |
| 249 | /* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands |
| 250 | ** do not work for repositories whose names do not end in ".fossil". |
| 251 | ** So do not hyperlink those cases. */ |
| 252 | blob_append_sql(&html,"%h",zName); |
| 253 | } else if( sqlite3_strglob("*/.*", zName)==0 ){ |
| 254 | /* Do not show hyperlinks for hidden repos */ |
| 255 | blob_append_sql(&html, "%h (hidden)", zName); |
| 256 | } else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){ |
| 257 | blob_append_sql(&html, |
| 258 | "<a href='%R/%T/home' target='_blank'>/%h</a>\n", |
| 259 | zUrl, zName); |
| 260 | }else if( file_ends_with_repository_extension(zName,1) ){ |
| 261 | /* As described in |
| 262 | ** https://fossil-scm.org/forum/info/f50f647c97c72fc1: if |
| @@ -273,56 +327,60 @@ | |
| 273 | , zDirPart |
| 274 | #if USE_SEE |
| 275 | , zDirPart |
| 276 | #endif |
| 277 | ) ){ |
| 278 | blob_append_sql(&html, |
| 279 | "<s>%h</s> (directory/repo name collision)\n", |
| 280 | zName); |
| 281 | }else{ |
| 282 | blob_append_sql(&html, |
| 283 | "<a href='%R/%T/home' target='_blank'>%h</a>\n", |
| 284 | zUrl, zName); |
| 285 | } |
| 286 | fossil_free(zDirPart); |
| 287 | }else{ |
| 288 | blob_append_sql(&html, |
| 289 | "<a href='%R/%T/home' target='_blank'>%h</a>\n", |
| 290 | zUrl, zName); |
| 291 | } |
| 292 | blob_append_sql(&html,"</nobr>"); |
| 293 | if( x.zProjName ){ |
| 294 | blob_append_sql(&html, "<td></td><td valign='top'>%h</td>\n", |
| 295 | x.zProjName); |
| 296 | fossil_free(x.zProjName); |
| 297 | }else{ |
| 298 | blob_append_sql(&html, "<td></td><td></td>\n"); |
| 299 | } |
| 300 | if( x.zProjDesc ){ |
| 301 | blob_append_sql(&html, "<td></td><td valign='top'>%h</td>\n", |
| 302 | x.zProjDesc); |
| 303 | fossil_free(x.zProjDesc); |
| 304 | }else{ |
| 305 | blob_append_sql(&html, "<td></td><td></td>\n"); |
| 306 | } |
| 307 | blob_append_sql(&html, |
| 308 | "<td></td><td data-sortkey='%08x' align='center' valign='top'>" |
| 309 | "<nobr>%h</nobr></td>\n", |
| 310 | (int)iAge, zAge); |
| 311 | fossil_free(zAge); |
| 312 | if( x.zLoginGroup ){ |
| 313 | blob_append_sql(&html, "<td></td><td valign='top'>" |
| 314 | "<nobr>%h</nobr></td></tr>\n", |
| 315 | x.zLoginGroup); |
| 316 | fossil_free(x.zLoginGroup); |
| 317 | }else{ |
| 318 | blob_append_sql(&html, "<td></td><td></td></tr>\n"); |
| 319 | } |
| 320 | sqlite3_free(zUrl); |
| 321 | } |
| 322 | db_finalize(&q); |
| 323 | blob_append_sql(&html,"</tbody></table>\n"); |
| 324 | } |
| 325 | if( zSkinRepo ){ |
| 326 | char *zNewBase = mprintf("%s/%s", g.zBaseURL, zSkinUrl); |
| 327 | g.zBaseURL = 0; |
| 328 | set_base_url(zNewBase); |
| 329 |
| --- src/repolist.c | |
| +++ src/repolist.c | |
| @@ -101,10 +101,25 @@ | |
| 101 | finish_repo_list: |
| 102 | g.dbIgnoreErrors--; |
| 103 | sqlite3_close(db); |
| 104 | } |
| 105 | |
| 106 | /* |
| 107 | ** SETTING: show-repolist-desc boolean default=off |
| 108 | ** |
| 109 | ** If the value of this setting is "1" globally, then the repository-list |
| 110 | ** page will show the description of each repository. This setting only |
| 111 | ** has effect when it is in the global setting database. |
| 112 | */ |
| 113 | /* |
| 114 | ** SETTING: show-repolist-lg boolean default=off |
| 115 | ** |
| 116 | ** If the value of this setting is "1" globally, then the repository-list |
| 117 | ** page will show the login-group for each repository. This setting only |
| 118 | ** has effect when it is in the global setting database. |
| 119 | */ |
| 120 | |
| 121 | /* |
| 122 | ** Generate a web-page that lists all repositories located under the |
| 123 | ** g.zRepositoryName directory and return non-zero. |
| 124 | ** |
| 125 | ** For the special case when g.zRepositoryName is a non-chroot-jail "/", |
| @@ -126,12 +141,19 @@ | |
| 141 | int allRepo; /* True if running "fossil ui all". |
| 142 | ** False if a directory scan of base for repos */ |
| 143 | Blob html; /* Html for the body of the repository list */ |
| 144 | char *zSkinRepo = 0; /* Name of the repository database used for skins */ |
| 145 | char *zSkinUrl = 0; /* URL for the skin database */ |
| 146 | int bShowDesc = 0; /* True to show the description column */ |
| 147 | int bShowLg = 0; /* True to show the login-group column */ |
| 148 | |
| 149 | assert( g.db==0 ); |
| 150 | db_open_config(1, 0); |
| 151 | bShowDesc = (0!=db_int(0, "SELECT value FROM global_config" |
| 152 | " WHERE name='show-repolist-desc'")); |
| 153 | bShowLg = (0!=db_int(0, "SELECT value FROM global_config" |
| 154 | " WHERE name='show-repolist-lg'")); |
| 155 | blob_init(&html, 0, 0); |
| 156 | if( fossil_strcmp(g.zRepositoryName,"/")==0 && !g.fJail ){ |
| 157 | /* For the special case of the "repository directory" being "/", |
| 158 | ** show all of the repositories named in the ~/.fossil database. |
| 159 | ** |
| @@ -138,11 +160,10 @@ | |
| 160 | ** On unix systems, then entries are of the form "repo:/home/..." |
| 161 | ** and on Windows systems they are like on unix, starting with a "/" |
| 162 | ** or they can begin with a drive letter: "repo:C:/Users/...". In either |
| 163 | ** case, we want returned path to omit any initial "/". |
| 164 | */ |
| 165 | db_multi_exec( |
| 166 | "CREATE TEMP VIEW sfile AS" |
| 167 | " SELECT ltrim(substr(name,6),'/') AS 'pathname' FROM global_config" |
| 168 | " WHERE name GLOB 'repo:*'" |
| 169 | ); |
| @@ -150,10 +171,12 @@ | |
| 171 | }else{ |
| 172 | /* The default case: All repositories under the g.zRepositoryName |
| 173 | ** directory. |
| 174 | */ |
| 175 | blob_init(&base, g.zRepositoryName, -1); |
| 176 | db_close(0); |
| 177 | assert( g.db==0 ); |
| 178 | sqlite3_open(":memory:", &g.db); |
| 179 | db_multi_exec("CREATE TABLE sfile(pathname TEXT);"); |
| 180 | db_multi_exec("CREATE TABLE vfile(pathname);"); |
| 181 | vfile_scan(&base, blob_size(&base), 0, 0, 0, ExtFILE); |
| 182 | db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'" |
| @@ -171,19 +194,50 @@ | |
| 194 | g.localOpen = 0; |
| 195 | return 0; |
| 196 | }else{ |
| 197 | Stmt q; |
| 198 | double rNow; |
| 199 | char zType[16]; /* Column type letters for class "sortable" */ |
| 200 | int nType; |
| 201 | zType[0] = 't'; /* Repo name */ |
| 202 | zType[1] = 'x'; /* Space between repo-name and project-name */ |
| 203 | zType[2] = 't'; /* Project name */ |
| 204 | nType = 3; |
| 205 | if( bShowDesc ){ |
| 206 | zType[nType++] = 'x'; /* Space between name and description */ |
| 207 | zType[nType++] = 't'; /* Project description */ |
| 208 | } |
| 209 | zType[nType++] = 'x'; /* space before age */ |
| 210 | zType[nType++] = 'k'; /* Project age */ |
| 211 | if( bShowLg ){ |
| 212 | zType[nType++] = 'x'; /* space before login-group */ |
| 213 | zType[nType++] = 't'; /* Login Group */ |
| 214 | } |
| 215 | zType[nType] = 0; |
| 216 | blob_appendf(&html, |
| 217 | "<table border='0' class='sortable' data-init-sort='1'" |
| 218 | " data-column-types='%s' cellspacing='0' cellpadding='0'><thead>\n" |
| 219 | "<tr><th>Filename</th><th> </th>\n" |
| 220 | "<th%s><nobr>Project Name</nobr></th>\n", |
| 221 | zType, (bShowDesc ? " width='25%'" : "")); |
| 222 | if( bShowDesc ){ |
| 223 | blob_appendf(&html, |
| 224 | "<th> </th>\n" |
| 225 | "<th width='25%%'><nobr>Project Description</nobr></th>\n" |
| 226 | ); |
| 227 | } |
| 228 | blob_appendf(&html, |
| 229 | "<th> </th>" |
| 230 | "<th><nobr>Last Modified</nobr></th>\n" |
| 231 | ); |
| 232 | if( bShowLg ){ |
| 233 | blob_appendf(&html, |
| 234 | "<th> </th>" |
| 235 | "<th><nobr>Login Group</nobr></th></tr>\n" |
| 236 | ); |
| 237 | } |
| 238 | blob_appendf(&html,"</thead><tbody>\n"); |
| 239 | db_prepare(&q, "SELECT pathname" |
| 240 | " FROM sfile ORDER BY pathname COLLATE nocase;"); |
| 241 | rNow = db_double(0, "SELECT julianday('now')"); |
| 242 | while( db_step(&q)==SQLITE_ROW ){ |
| 243 | const char *zName = db_column_text(&q, 0); |
| @@ -242,21 +296,21 @@ | |
| 296 | if( x.rMTime==0.0 ){ |
| 297 | /* This repository has no entry in the "event" table. |
| 298 | ** Its age will still be maximum, so data-sortkey will work. */ |
| 299 | zAge = mprintf("unknown"); |
| 300 | } |
| 301 | blob_appendf(&html, "<tr><td valign='top'><nobr>"); |
| 302 | if( !file_ends_with_repository_extension(zName,0) ){ |
| 303 | /* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands |
| 304 | ** do not work for repositories whose names do not end in ".fossil". |
| 305 | ** So do not hyperlink those cases. */ |
| 306 | blob_appendf(&html,"%h",zName); |
| 307 | } else if( sqlite3_strglob("*/.*", zName)==0 ){ |
| 308 | /* Do not show hyperlinks for hidden repos */ |
| 309 | blob_appendf(&html, "%h (hidden)", zName); |
| 310 | } else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){ |
| 311 | blob_appendf(&html, |
| 312 | "<a href='%R/%T/home' target='_blank'>/%h</a>\n", |
| 313 | zUrl, zName); |
| 314 | }else if( file_ends_with_repository_extension(zName,1) ){ |
| 315 | /* As described in |
| 316 | ** https://fossil-scm.org/forum/info/f50f647c97c72fc1: if |
| @@ -273,56 +327,60 @@ | |
| 327 | , zDirPart |
| 328 | #if USE_SEE |
| 329 | , zDirPart |
| 330 | #endif |
| 331 | ) ){ |
| 332 | blob_appendf(&html, |
| 333 | "<s>%h</s> (directory/repo name collision)\n", |
| 334 | zName); |
| 335 | }else{ |
| 336 | blob_appendf(&html, |
| 337 | "<a href='%R/%T/home' target='_blank'>%h</a>\n", |
| 338 | zUrl, zName); |
| 339 | } |
| 340 | fossil_free(zDirPart); |
| 341 | }else{ |
| 342 | blob_appendf(&html, |
| 343 | "<a href='%R/%T/home' target='_blank'>%h</a>\n", |
| 344 | zUrl, zName); |
| 345 | } |
| 346 | blob_appendf(&html,"</nobr></td>\n"); |
| 347 | if( x.zProjName ){ |
| 348 | blob_appendf(&html, "<td> </td><td valign='top'>%h</td>\n", |
| 349 | x.zProjName); |
| 350 | fossil_free(x.zProjName); |
| 351 | }else{ |
| 352 | blob_appendf(&html, "<td> </td><td></td>\n"); |
| 353 | } |
| 354 | if( !bShowDesc ){ |
| 355 | /* Do nothing */ |
| 356 | }else if( x.zProjDesc ){ |
| 357 | blob_appendf(&html, "<td> </td><td valign='top'>%h</td>\n", |
| 358 | x.zProjDesc); |
| 359 | fossil_free(x.zProjDesc); |
| 360 | }else{ |
| 361 | blob_appendf(&html, "<td> </td><td></td>\n"); |
| 362 | } |
| 363 | blob_appendf(&html, |
| 364 | "<td> </td><td data-sortkey='%08x' align='center' valign='top'>" |
| 365 | "<nobr>%h</nobr></td>\n", |
| 366 | (int)iAge, zAge); |
| 367 | fossil_free(zAge); |
| 368 | if( !bShowLg ){ |
| 369 | blob_appendf(&html, "</tr>\n"); |
| 370 | }else if( x.zLoginGroup ){ |
| 371 | blob_appendf(&html, "<td> </td><td valign='top'>" |
| 372 | "<nobr>%h</nobr></td></tr>\n", |
| 373 | x.zLoginGroup); |
| 374 | fossil_free(x.zLoginGroup); |
| 375 | }else{ |
| 376 | blob_appendf(&html, "<td> </td><td></td></tr>\n"); |
| 377 | } |
| 378 | sqlite3_free(zUrl); |
| 379 | } |
| 380 | db_finalize(&q); |
| 381 | blob_appendf(&html,"</tbody></table>\n"); |
| 382 | } |
| 383 | if( zSkinRepo ){ |
| 384 | char *zNewBase = mprintf("%s/%s", g.zBaseURL, zSkinUrl); |
| 385 | g.zBaseURL = 0; |
| 386 | set_base_url(zNewBase); |
| 387 |