Fossil SCM
Add the ci=LABEL parameter to the "dir" webpage in order to look at just files within a single check-in. Add a Download link on the artifact and hexdump viewers.
Commit
923d644b89211895883a8442883afed8ee02a6e9
Parent
5e4b1632bc0d803…
2 files changed
+57
-9
+28
-5
+57
-9
| --- src/browse.c | ||
| +++ src/browse.c | ||
| @@ -101,63 +101,108 @@ | ||
| 101 | 101 | ** WEBPAGE: dir |
| 102 | 102 | ** |
| 103 | 103 | ** Query parameters: |
| 104 | 104 | ** |
| 105 | 105 | ** name=PATH Directory to display. Required. |
| 106 | +** ci=LABEL Show only files in this check-in. Optional. | |
| 106 | 107 | */ |
| 107 | 108 | void page_dir(void){ |
| 108 | 109 | const char *zD = P("name"); |
| 109 | 110 | int mxLen; |
| 110 | 111 | int nCol, nRow; |
| 111 | 112 | int cnt, i; |
| 112 | 113 | char *zPrefix; |
| 113 | 114 | Stmt q; |
| 115 | + const char *zCI = P("ci"); | |
| 116 | + int rid = 0; | |
| 117 | + Blob content; | |
| 118 | + Manifest m; | |
| 119 | + const char *zSubdirLink; | |
| 114 | 120 | |
| 115 | 121 | login_check_credentials(); |
| 116 | 122 | if( !g.okHistory ){ login_needed(); return; } |
| 117 | 123 | style_header("File List"); |
| 118 | 124 | sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, |
| 119 | 125 | pathelementFunc, 0, 0); |
| 120 | 126 | |
| 121 | 127 | /* If the name= parameter is an empty string, make it a NULL pointer */ |
| 122 | 128 | if( zD && strlen(zD)==0 ){ zD = 0; } |
| 129 | + | |
| 130 | + /* If a specific check-in is requested, fetch and parse it. */ | |
| 131 | + if( zCI && (rid = name_to_rid(zCI))!=0 && content_get(rid, &content) ){ | |
| 132 | + if( !manifest_parse(&m, &content) || m.type!=CFTYPE_MANIFEST ){ | |
| 133 | + zCI = 0; | |
| 134 | + } | |
| 135 | + } | |
| 123 | 136 | |
| 124 | 137 | /* Compute the title of the page */ |
| 125 | 138 | if( zD ){ |
| 126 | 139 | Blob title; |
| 127 | 140 | |
| 128 | 141 | blob_zero(&title); |
| 129 | 142 | blob_appendf(&title, "Files in directory "); |
| 130 | 143 | hyperlinked_path(zD, &title); |
| 131 | - @ <h2>%s(blob_str(&title))</h2> | |
| 144 | + @ <h2>%s(blob_str(&title)) | |
| 132 | 145 | blob_reset(&title); |
| 133 | 146 | zPrefix = mprintf("%h/", zD); |
| 134 | 147 | }else{ |
| 135 | - @ <h2>Files in the top-level directory</h2> | |
| 148 | + @ <h2>Files in the top-level directory | |
| 136 | 149 | zPrefix = ""; |
| 137 | 150 | } |
| 151 | + if( zCI ){ | |
| 152 | + char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 153 | + char zShort[20]; | |
| 154 | + memcpy(zShort, zUuid, 10); | |
| 155 | + zShort[10] = 0; | |
| 156 | + @ of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]</h2> | |
| 157 | + zSubdirLink = mprintf("%s/dir?ci=%s&name=%T", g.zBaseURL, zUuid, zPrefix); | |
| 158 | + if( zD ){ | |
| 159 | + style_submenu_element("Top", "Top", "%s/dir?ci=%s", g.zBaseURL, zUuid); | |
| 160 | + } | |
| 161 | + }else{ | |
| 162 | + @ </h2> | |
| 163 | + zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix); | |
| 164 | + } | |
| 138 | 165 | |
| 139 | 166 | /* Compute the temporary table "localfiles" containing the names |
| 140 | 167 | ** of all files and subdirectories in the zD[] directory. |
| 141 | 168 | ** |
| 142 | 169 | ** Subdirectory names begin with "/". This causes them to sort |
| 143 | 170 | ** first and it also gives us an easy way to distinguish files |
| 144 | 171 | ** from directories in the loop that follows. |
| 145 | 172 | */ |
| 173 | + db_multi_exec( | |
| 174 | + "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);" | |
| 175 | + "CREATE TEMP TABLE allfiles(x UNIQUE NOT NULL, u);" | |
| 176 | + ); | |
| 177 | + if( zCI ){ | |
| 178 | + Stmt ins; | |
| 179 | + int i; | |
| 180 | + db_prepare(&ins, "INSERT INTO allfiles VALUES(:x, :u)"); | |
| 181 | + for(i=0; i<m.nFile; i++){ | |
| 182 | + db_bind_text(&ins, ":x", m.aFile[i].zName); | |
| 183 | + db_bind_text(&ins, ":u", m.aFile[i].zUuid); | |
| 184 | + db_step(&ins); | |
| 185 | + db_reset(&ins); | |
| 186 | + } | |
| 187 | + db_finalize(&ins); | |
| 188 | + }else{ | |
| 189 | + db_multi_exec( | |
| 190 | + "INSERT INTO allfiles SELECT name, NULL FROM filename" | |
| 191 | + ); | |
| 192 | + } | |
| 146 | 193 | if( zD ){ |
| 147 | 194 | db_multi_exec( |
| 148 | - "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL);" | |
| 149 | 195 | "INSERT OR IGNORE INTO localfiles " |
| 150 | - " SELECT pathelement(name,%d) FROM filename" | |
| 151 | - " WHERE +name GLOB '%q/*'", | |
| 196 | + " SELECT pathelement(x,%d), u FROM allfiles" | |
| 197 | + " WHERE x GLOB '%q/*'", | |
| 152 | 198 | strlen(zD)+1, zD |
| 153 | 199 | ); |
| 154 | 200 | }else{ |
| 155 | 201 | db_multi_exec( |
| 156 | - "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL);" | |
| 157 | 202 | "INSERT OR IGNORE INTO localfiles " |
| 158 | - " SELECT pathelement(name,0) FROM filename" | |
| 203 | + " SELECT pathelement(x,0), u FROM allfiles" | |
| 159 | 204 | ); |
| 160 | 205 | } |
| 161 | 206 | |
| 162 | 207 | /* Generate a multi-column table listing the contents of zD[] |
| 163 | 208 | ** directory. |
| @@ -164,11 +209,11 @@ | ||
| 164 | 209 | */ |
| 165 | 210 | mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles"); |
| 166 | 211 | cnt = db_int(0, "SELECT count(*) FROM localfiles"); |
| 167 | 212 | nCol = 4; |
| 168 | 213 | nRow = (cnt+nCol-1)/nCol; |
| 169 | - db_prepare(&q, "SELECT x FROM localfiles ORDER BY x"); | |
| 214 | + db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x"); | |
| 170 | 215 | @ <table border="0" width="100%%"><tr><td valign="top" width="25%%"> |
| 171 | 216 | i = 0; |
| 172 | 217 | while( db_step(&q)==SQLITE_ROW ){ |
| 173 | 218 | const char *zFName; |
| 174 | 219 | if( i==nRow ){ |
| @@ -177,16 +222,19 @@ | ||
| 177 | 222 | } |
| 178 | 223 | i++; |
| 179 | 224 | zFName = db_column_text(&q, 0); |
| 180 | 225 | if( zFName[0]=='/' ){ |
| 181 | 226 | zFName++; |
| 182 | - @ <li><a href="%s(g.zBaseURL)/dir?name=%T(zPrefix)%T(zFName)"> | |
| 227 | + @ <li><a href="%s(zSubdirLink)%T(zFName)"> | |
| 183 | 228 | @ %h(zFName)/</a></li> |
| 229 | + }else if( zCI ){ | |
| 230 | + const char *zUuid = db_column_text(&q, 1); | |
| 231 | + @ <li><a href="%s(g.zBaseURL)/artifact?name=%s(zUuid)">%h(zFName)</a> | |
| 184 | 232 | }else{ |
| 185 | 233 | @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFName)"> |
| 186 | 234 | @ %h(zFName)</a></li> |
| 187 | 235 | } |
| 188 | 236 | } |
| 189 | 237 | db_finalize(&q); |
| 190 | 238 | @ </td></tr></table> |
| 191 | 239 | style_footer(); |
| 192 | 240 | } |
| 193 | 241 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -101,63 +101,108 @@ | |
| 101 | ** WEBPAGE: dir |
| 102 | ** |
| 103 | ** Query parameters: |
| 104 | ** |
| 105 | ** name=PATH Directory to display. Required. |
| 106 | */ |
| 107 | void page_dir(void){ |
| 108 | const char *zD = P("name"); |
| 109 | int mxLen; |
| 110 | int nCol, nRow; |
| 111 | int cnt, i; |
| 112 | char *zPrefix; |
| 113 | Stmt q; |
| 114 | |
| 115 | login_check_credentials(); |
| 116 | if( !g.okHistory ){ login_needed(); return; } |
| 117 | style_header("File List"); |
| 118 | sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, |
| 119 | pathelementFunc, 0, 0); |
| 120 | |
| 121 | /* If the name= parameter is an empty string, make it a NULL pointer */ |
| 122 | if( zD && strlen(zD)==0 ){ zD = 0; } |
| 123 | |
| 124 | /* Compute the title of the page */ |
| 125 | if( zD ){ |
| 126 | Blob title; |
| 127 | |
| 128 | blob_zero(&title); |
| 129 | blob_appendf(&title, "Files in directory "); |
| 130 | hyperlinked_path(zD, &title); |
| 131 | @ <h2>%s(blob_str(&title))</h2> |
| 132 | blob_reset(&title); |
| 133 | zPrefix = mprintf("%h/", zD); |
| 134 | }else{ |
| 135 | @ <h2>Files in the top-level directory</h2> |
| 136 | zPrefix = ""; |
| 137 | } |
| 138 | |
| 139 | /* Compute the temporary table "localfiles" containing the names |
| 140 | ** of all files and subdirectories in the zD[] directory. |
| 141 | ** |
| 142 | ** Subdirectory names begin with "/". This causes them to sort |
| 143 | ** first and it also gives us an easy way to distinguish files |
| 144 | ** from directories in the loop that follows. |
| 145 | */ |
| 146 | if( zD ){ |
| 147 | db_multi_exec( |
| 148 | "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL);" |
| 149 | "INSERT OR IGNORE INTO localfiles " |
| 150 | " SELECT pathelement(name,%d) FROM filename" |
| 151 | " WHERE +name GLOB '%q/*'", |
| 152 | strlen(zD)+1, zD |
| 153 | ); |
| 154 | }else{ |
| 155 | db_multi_exec( |
| 156 | "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL);" |
| 157 | "INSERT OR IGNORE INTO localfiles " |
| 158 | " SELECT pathelement(name,0) FROM filename" |
| 159 | ); |
| 160 | } |
| 161 | |
| 162 | /* Generate a multi-column table listing the contents of zD[] |
| 163 | ** directory. |
| @@ -164,11 +209,11 @@ | |
| 164 | */ |
| 165 | mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles"); |
| 166 | cnt = db_int(0, "SELECT count(*) FROM localfiles"); |
| 167 | nCol = 4; |
| 168 | nRow = (cnt+nCol-1)/nCol; |
| 169 | db_prepare(&q, "SELECT x FROM localfiles ORDER BY x"); |
| 170 | @ <table border="0" width="100%%"><tr><td valign="top" width="25%%"> |
| 171 | i = 0; |
| 172 | while( db_step(&q)==SQLITE_ROW ){ |
| 173 | const char *zFName; |
| 174 | if( i==nRow ){ |
| @@ -177,16 +222,19 @@ | |
| 177 | } |
| 178 | i++; |
| 179 | zFName = db_column_text(&q, 0); |
| 180 | if( zFName[0]=='/' ){ |
| 181 | zFName++; |
| 182 | @ <li><a href="%s(g.zBaseURL)/dir?name=%T(zPrefix)%T(zFName)"> |
| 183 | @ %h(zFName)/</a></li> |
| 184 | }else{ |
| 185 | @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFName)"> |
| 186 | @ %h(zFName)</a></li> |
| 187 | } |
| 188 | } |
| 189 | db_finalize(&q); |
| 190 | @ </td></tr></table> |
| 191 | style_footer(); |
| 192 | } |
| 193 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -101,63 +101,108 @@ | |
| 101 | ** WEBPAGE: dir |
| 102 | ** |
| 103 | ** Query parameters: |
| 104 | ** |
| 105 | ** name=PATH Directory to display. Required. |
| 106 | ** ci=LABEL Show only files in this check-in. Optional. |
| 107 | */ |
| 108 | void page_dir(void){ |
| 109 | const char *zD = P("name"); |
| 110 | int mxLen; |
| 111 | int nCol, nRow; |
| 112 | int cnt, i; |
| 113 | char *zPrefix; |
| 114 | Stmt q; |
| 115 | const char *zCI = P("ci"); |
| 116 | int rid = 0; |
| 117 | Blob content; |
| 118 | Manifest m; |
| 119 | const char *zSubdirLink; |
| 120 | |
| 121 | login_check_credentials(); |
| 122 | if( !g.okHistory ){ login_needed(); return; } |
| 123 | style_header("File List"); |
| 124 | sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, |
| 125 | pathelementFunc, 0, 0); |
| 126 | |
| 127 | /* If the name= parameter is an empty string, make it a NULL pointer */ |
| 128 | if( zD && strlen(zD)==0 ){ zD = 0; } |
| 129 | |
| 130 | /* If a specific check-in is requested, fetch and parse it. */ |
| 131 | if( zCI && (rid = name_to_rid(zCI))!=0 && content_get(rid, &content) ){ |
| 132 | if( !manifest_parse(&m, &content) || m.type!=CFTYPE_MANIFEST ){ |
| 133 | zCI = 0; |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | /* Compute the title of the page */ |
| 138 | if( zD ){ |
| 139 | Blob title; |
| 140 | |
| 141 | blob_zero(&title); |
| 142 | blob_appendf(&title, "Files in directory "); |
| 143 | hyperlinked_path(zD, &title); |
| 144 | @ <h2>%s(blob_str(&title)) |
| 145 | blob_reset(&title); |
| 146 | zPrefix = mprintf("%h/", zD); |
| 147 | }else{ |
| 148 | @ <h2>Files in the top-level directory |
| 149 | zPrefix = ""; |
| 150 | } |
| 151 | if( zCI ){ |
| 152 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 153 | char zShort[20]; |
| 154 | memcpy(zShort, zUuid, 10); |
| 155 | zShort[10] = 0; |
| 156 | @ of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]</h2> |
| 157 | zSubdirLink = mprintf("%s/dir?ci=%s&name=%T", g.zBaseURL, zUuid, zPrefix); |
| 158 | if( zD ){ |
| 159 | style_submenu_element("Top", "Top", "%s/dir?ci=%s", g.zBaseURL, zUuid); |
| 160 | } |
| 161 | }else{ |
| 162 | @ </h2> |
| 163 | zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix); |
| 164 | } |
| 165 | |
| 166 | /* Compute the temporary table "localfiles" containing the names |
| 167 | ** of all files and subdirectories in the zD[] directory. |
| 168 | ** |
| 169 | ** Subdirectory names begin with "/". This causes them to sort |
| 170 | ** first and it also gives us an easy way to distinguish files |
| 171 | ** from directories in the loop that follows. |
| 172 | */ |
| 173 | db_multi_exec( |
| 174 | "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);" |
| 175 | "CREATE TEMP TABLE allfiles(x UNIQUE NOT NULL, u);" |
| 176 | ); |
| 177 | if( zCI ){ |
| 178 | Stmt ins; |
| 179 | int i; |
| 180 | db_prepare(&ins, "INSERT INTO allfiles VALUES(:x, :u)"); |
| 181 | for(i=0; i<m.nFile; i++){ |
| 182 | db_bind_text(&ins, ":x", m.aFile[i].zName); |
| 183 | db_bind_text(&ins, ":u", m.aFile[i].zUuid); |
| 184 | db_step(&ins); |
| 185 | db_reset(&ins); |
| 186 | } |
| 187 | db_finalize(&ins); |
| 188 | }else{ |
| 189 | db_multi_exec( |
| 190 | "INSERT INTO allfiles SELECT name, NULL FROM filename" |
| 191 | ); |
| 192 | } |
| 193 | if( zD ){ |
| 194 | db_multi_exec( |
| 195 | "INSERT OR IGNORE INTO localfiles " |
| 196 | " SELECT pathelement(x,%d), u FROM allfiles" |
| 197 | " WHERE x GLOB '%q/*'", |
| 198 | strlen(zD)+1, zD |
| 199 | ); |
| 200 | }else{ |
| 201 | db_multi_exec( |
| 202 | "INSERT OR IGNORE INTO localfiles " |
| 203 | " SELECT pathelement(x,0), u FROM allfiles" |
| 204 | ); |
| 205 | } |
| 206 | |
| 207 | /* Generate a multi-column table listing the contents of zD[] |
| 208 | ** directory. |
| @@ -164,11 +209,11 @@ | |
| 209 | */ |
| 210 | mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles"); |
| 211 | cnt = db_int(0, "SELECT count(*) FROM localfiles"); |
| 212 | nCol = 4; |
| 213 | nRow = (cnt+nCol-1)/nCol; |
| 214 | db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x"); |
| 215 | @ <table border="0" width="100%%"><tr><td valign="top" width="25%%"> |
| 216 | i = 0; |
| 217 | while( db_step(&q)==SQLITE_ROW ){ |
| 218 | const char *zFName; |
| 219 | if( i==nRow ){ |
| @@ -177,16 +222,19 @@ | |
| 222 | } |
| 223 | i++; |
| 224 | zFName = db_column_text(&q, 0); |
| 225 | if( zFName[0]=='/' ){ |
| 226 | zFName++; |
| 227 | @ <li><a href="%s(zSubdirLink)%T(zFName)"> |
| 228 | @ %h(zFName)/</a></li> |
| 229 | }else if( zCI ){ |
| 230 | const char *zUuid = db_column_text(&q, 1); |
| 231 | @ <li><a href="%s(g.zBaseURL)/artifact?name=%s(zUuid)">%h(zFName)</a> |
| 232 | }else{ |
| 233 | @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFName)"> |
| 234 | @ %h(zFName)</a></li> |
| 235 | } |
| 236 | } |
| 237 | db_finalize(&q); |
| 238 | @ </td></tr></table> |
| 239 | style_footer(); |
| 240 | } |
| 241 |
+28
-5
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -324,10 +324,11 @@ | ||
| 324 | 324 | } |
| 325 | 325 | } |
| 326 | 326 | |
| 327 | 327 | |
| 328 | 328 | /* |
| 329 | +** WEBPAGE: vinfo | |
| 329 | 330 | ** WEBPAGE: ci |
| 330 | 331 | ** URL: /ci?name=RID|ARTIFACTID |
| 331 | 332 | ** |
| 332 | 333 | ** Return information about a baseline |
| 333 | 334 | */ |
| @@ -425,10 +426,11 @@ | ||
| 425 | 426 | db_finalize(&q); |
| 426 | 427 | @ </td></tr> |
| 427 | 428 | @ <tr><th>Commands:</th> |
| 428 | 429 | @ <td> |
| 429 | 430 | @ <a href="%s(g.zBaseURL)/vdiff/%d(rid)">diff</a> |
| 431 | + @ | <a href="%s(g.zBaseURL)/dir?ci=%s(zShortUuid)">files</a> | |
| 430 | 432 | @ | <a href="%s(g.zBaseURL)/zip/%s(zProjName)-%s(zShortUuid).zip?uuid=%s(zUuid)"> |
| 431 | 433 | @ ZIP archive</a> |
| 432 | 434 | @ | <a href="%s(g.zBaseURL)/artifact/%d(rid)">manifest</a> |
| 433 | 435 | if( g.okWrite ){ |
| 434 | 436 | @ | <a href="%s(g.zBaseURL)/ci_edit?r=%d(rid)">edit</a> |
| @@ -736,11 +738,12 @@ | ||
| 736 | 738 | ** * date of check-in |
| 737 | 739 | ** * Comment & user |
| 738 | 740 | */ |
| 739 | 741 | static void object_description( |
| 740 | 742 | int rid, /* The artifact ID */ |
| 741 | - int linkToView /* Add viewer link if true */ | |
| 743 | + int linkToView, /* Add viewer link if true */ | |
| 744 | + Blob *pDownloadName /* Fill with an appropriate download name */ | |
| 742 | 745 | ){ |
| 743 | 746 | Stmt q; |
| 744 | 747 | int cnt = 0; |
| 745 | 748 | int nWiki = 0; |
| 746 | 749 | db_prepare(&q, |
| @@ -771,10 +774,13 @@ | ||
| 771 | 774 | @ <a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a> |
| 772 | 775 | @ uuid %s(zFuuid) part of check-in |
| 773 | 776 | hyperlink_to_uuid(zVers); |
| 774 | 777 | @ %w(zCom) by %h(zUser) on %s(zDate). |
| 775 | 778 | cnt++; |
| 779 | + if( pDownloadName && blob_size(pDownloadName)==0 ){ | |
| 780 | + blob_append(pDownloadName, zName, -1); | |
| 781 | + } | |
| 776 | 782 | } |
| 777 | 783 | db_finalize(&q); |
| 778 | 784 | db_prepare(&q, |
| 779 | 785 | "SELECT substr(tagname, 6, 10000), datetime(event.mtime)," |
| 780 | 786 | " coalesce(event.euser, event.user), uuid" |
| @@ -798,10 +804,13 @@ | ||
| 798 | 804 | } |
| 799 | 805 | @ [<a href="%s(g.zBaseURL)/wiki?name=%t(zPagename)">%h(zPagename)</a>] |
| 800 | 806 | @ uuid %s(zUuid) by %h(zUser) on %s(zDate). |
| 801 | 807 | nWiki++; |
| 802 | 808 | cnt++; |
| 809 | + if( pDownloadName && blob_size(pDownloadName)==0 ){ | |
| 810 | + blob_append(pDownloadName, zPagename, -1); | |
| 811 | + } | |
| 803 | 812 | } |
| 804 | 813 | db_finalize(&q); |
| 805 | 814 | if( nWiki==0 ){ |
| 806 | 815 | db_prepare(&q, |
| 807 | 816 | "SELECT datetime(mtime), user, comment, uuid, type" |
| @@ -828,17 +837,23 @@ | ||
| 828 | 837 | }else{ |
| 829 | 838 | @ Control file referencing |
| 830 | 839 | } |
| 831 | 840 | hyperlink_to_uuid(zUuid); |
| 832 | 841 | @ %w(zCom) by %h(zUser) on %s(zDate). |
| 842 | + if( pDownloadName && blob_size(pDownloadName)==0 ){ | |
| 843 | + blob_append(pDownloadName, zUuid, -1); | |
| 844 | + } | |
| 833 | 845 | cnt++; |
| 834 | 846 | } |
| 835 | 847 | db_finalize(&q); |
| 836 | 848 | } |
| 837 | 849 | if( cnt==0 ){ |
| 838 | 850 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 839 | 851 | @ Control file %s(zUuid). |
| 852 | + if( pDownloadName && blob_size(pDownloadName)==0 ){ | |
| 853 | + blob_append(pDownloadName, zUuid, -1); | |
| 854 | + } | |
| 840 | 855 | }else if( linkToView ){ |
| 841 | 856 | @ <a href="%s(g.zBaseURL)/artifact/%d(rid)">[view]</a> |
| 842 | 857 | } |
| 843 | 858 | } |
| 844 | 859 | |
| @@ -856,15 +871,15 @@ | ||
| 856 | 871 | login_check_credentials(); |
| 857 | 872 | if( !g.okRead ){ login_needed(); return; } |
| 858 | 873 | style_header("Diff"); |
| 859 | 874 | @ <h2>Differences From:</h2> |
| 860 | 875 | @ <blockquote> |
| 861 | - object_description(v1, 1); | |
| 876 | + object_description(v1, 1, 0); | |
| 862 | 877 | @ </blockquote> |
| 863 | 878 | @ <h2>To:</h2> |
| 864 | 879 | @ <blockquote> |
| 865 | - object_description(v2, 1); | |
| 880 | + object_description(v2, 1, 0); | |
| 866 | 881 | @ </blockquote> |
| 867 | 882 | @ <hr> |
| 868 | 883 | @ <blockquote><pre> |
| 869 | 884 | content_get(v1, &c1); |
| 870 | 885 | content_get(v2, &c2); |
| @@ -959,10 +974,11 @@ | ||
| 959 | 974 | ** as preformatted text. |
| 960 | 975 | */ |
| 961 | 976 | void hexdump_page(void){ |
| 962 | 977 | int rid; |
| 963 | 978 | Blob content; |
| 979 | + Blob downloadName; | |
| 964 | 980 | |
| 965 | 981 | rid = name_to_rid(PD("name","0")); |
| 966 | 982 | login_check_credentials(); |
| 967 | 983 | if( !g.okRead ){ login_needed(); return; } |
| 968 | 984 | if( rid==0 ){ cgi_redirect("/home"); } |
| @@ -977,11 +993,14 @@ | ||
| 977 | 993 | } |
| 978 | 994 | } |
| 979 | 995 | style_header("Hex Artifact Content"); |
| 980 | 996 | @ <h2>Hexadecimal Content Of:</h2> |
| 981 | 997 | @ <blockquote> |
| 982 | - object_description(rid, 0); | |
| 998 | + blob_zero(&downloadName); | |
| 999 | + object_description(rid, 0, &downloadName); | |
| 1000 | + style_submenu_element("Download", "Download", | |
| 1001 | + "%s/raw/%T?name=%d", g.zBaseURL, blob_str(&downloadName), rid); | |
| 983 | 1002 | @ </blockquote> |
| 984 | 1003 | @ <hr> |
| 985 | 1004 | content_get(rid, &content); |
| 986 | 1005 | @ <blockquote><pre> |
| 987 | 1006 | hexdump(&content); |
| @@ -998,10 +1017,11 @@ | ||
| 998 | 1017 | */ |
| 999 | 1018 | void artifact_page(void){ |
| 1000 | 1019 | int rid; |
| 1001 | 1020 | Blob content; |
| 1002 | 1021 | const char *zMime; |
| 1022 | + Blob downloadName; | |
| 1003 | 1023 | |
| 1004 | 1024 | rid = name_to_rid(PD("name","0")); |
| 1005 | 1025 | login_check_credentials(); |
| 1006 | 1026 | if( !g.okRead ){ login_needed(); return; } |
| 1007 | 1027 | if( rid==0 ){ cgi_redirect("/home"); } |
| @@ -1016,11 +1036,14 @@ | ||
| 1016 | 1036 | } |
| 1017 | 1037 | } |
| 1018 | 1038 | style_header("Artifact Content"); |
| 1019 | 1039 | @ <h2>Content Of:</h2> |
| 1020 | 1040 | @ <blockquote> |
| 1021 | - object_description(rid, 0); | |
| 1041 | + blob_zero(&downloadName); | |
| 1042 | + object_description(rid, 0, &downloadName); | |
| 1043 | + style_submenu_element("Download", "Download", | |
| 1044 | + "%s/raw/%T?name=%d", g.zTop, blob_str(&downloadName), rid); | |
| 1022 | 1045 | @ </blockquote> |
| 1023 | 1046 | @ <hr> |
| 1024 | 1047 | @ <blockquote> |
| 1025 | 1048 | content_get(rid, &content); |
| 1026 | 1049 | zMime = mimetype_from_content(&content); |
| 1027 | 1050 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -324,10 +324,11 @@ | |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | |
| 328 | /* |
| 329 | ** WEBPAGE: ci |
| 330 | ** URL: /ci?name=RID|ARTIFACTID |
| 331 | ** |
| 332 | ** Return information about a baseline |
| 333 | */ |
| @@ -425,10 +426,11 @@ | |
| 425 | db_finalize(&q); |
| 426 | @ </td></tr> |
| 427 | @ <tr><th>Commands:</th> |
| 428 | @ <td> |
| 429 | @ <a href="%s(g.zBaseURL)/vdiff/%d(rid)">diff</a> |
| 430 | @ | <a href="%s(g.zBaseURL)/zip/%s(zProjName)-%s(zShortUuid).zip?uuid=%s(zUuid)"> |
| 431 | @ ZIP archive</a> |
| 432 | @ | <a href="%s(g.zBaseURL)/artifact/%d(rid)">manifest</a> |
| 433 | if( g.okWrite ){ |
| 434 | @ | <a href="%s(g.zBaseURL)/ci_edit?r=%d(rid)">edit</a> |
| @@ -736,11 +738,12 @@ | |
| 736 | ** * date of check-in |
| 737 | ** * Comment & user |
| 738 | */ |
| 739 | static void object_description( |
| 740 | int rid, /* The artifact ID */ |
| 741 | int linkToView /* Add viewer link if true */ |
| 742 | ){ |
| 743 | Stmt q; |
| 744 | int cnt = 0; |
| 745 | int nWiki = 0; |
| 746 | db_prepare(&q, |
| @@ -771,10 +774,13 @@ | |
| 771 | @ <a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a> |
| 772 | @ uuid %s(zFuuid) part of check-in |
| 773 | hyperlink_to_uuid(zVers); |
| 774 | @ %w(zCom) by %h(zUser) on %s(zDate). |
| 775 | cnt++; |
| 776 | } |
| 777 | db_finalize(&q); |
| 778 | db_prepare(&q, |
| 779 | "SELECT substr(tagname, 6, 10000), datetime(event.mtime)," |
| 780 | " coalesce(event.euser, event.user), uuid" |
| @@ -798,10 +804,13 @@ | |
| 798 | } |
| 799 | @ [<a href="%s(g.zBaseURL)/wiki?name=%t(zPagename)">%h(zPagename)</a>] |
| 800 | @ uuid %s(zUuid) by %h(zUser) on %s(zDate). |
| 801 | nWiki++; |
| 802 | cnt++; |
| 803 | } |
| 804 | db_finalize(&q); |
| 805 | if( nWiki==0 ){ |
| 806 | db_prepare(&q, |
| 807 | "SELECT datetime(mtime), user, comment, uuid, type" |
| @@ -828,17 +837,23 @@ | |
| 828 | }else{ |
| 829 | @ Control file referencing |
| 830 | } |
| 831 | hyperlink_to_uuid(zUuid); |
| 832 | @ %w(zCom) by %h(zUser) on %s(zDate). |
| 833 | cnt++; |
| 834 | } |
| 835 | db_finalize(&q); |
| 836 | } |
| 837 | if( cnt==0 ){ |
| 838 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 839 | @ Control file %s(zUuid). |
| 840 | }else if( linkToView ){ |
| 841 | @ <a href="%s(g.zBaseURL)/artifact/%d(rid)">[view]</a> |
| 842 | } |
| 843 | } |
| 844 | |
| @@ -856,15 +871,15 @@ | |
| 856 | login_check_credentials(); |
| 857 | if( !g.okRead ){ login_needed(); return; } |
| 858 | style_header("Diff"); |
| 859 | @ <h2>Differences From:</h2> |
| 860 | @ <blockquote> |
| 861 | object_description(v1, 1); |
| 862 | @ </blockquote> |
| 863 | @ <h2>To:</h2> |
| 864 | @ <blockquote> |
| 865 | object_description(v2, 1); |
| 866 | @ </blockquote> |
| 867 | @ <hr> |
| 868 | @ <blockquote><pre> |
| 869 | content_get(v1, &c1); |
| 870 | content_get(v2, &c2); |
| @@ -959,10 +974,11 @@ | |
| 959 | ** as preformatted text. |
| 960 | */ |
| 961 | void hexdump_page(void){ |
| 962 | int rid; |
| 963 | Blob content; |
| 964 | |
| 965 | rid = name_to_rid(PD("name","0")); |
| 966 | login_check_credentials(); |
| 967 | if( !g.okRead ){ login_needed(); return; } |
| 968 | if( rid==0 ){ cgi_redirect("/home"); } |
| @@ -977,11 +993,14 @@ | |
| 977 | } |
| 978 | } |
| 979 | style_header("Hex Artifact Content"); |
| 980 | @ <h2>Hexadecimal Content Of:</h2> |
| 981 | @ <blockquote> |
| 982 | object_description(rid, 0); |
| 983 | @ </blockquote> |
| 984 | @ <hr> |
| 985 | content_get(rid, &content); |
| 986 | @ <blockquote><pre> |
| 987 | hexdump(&content); |
| @@ -998,10 +1017,11 @@ | |
| 998 | */ |
| 999 | void artifact_page(void){ |
| 1000 | int rid; |
| 1001 | Blob content; |
| 1002 | const char *zMime; |
| 1003 | |
| 1004 | rid = name_to_rid(PD("name","0")); |
| 1005 | login_check_credentials(); |
| 1006 | if( !g.okRead ){ login_needed(); return; } |
| 1007 | if( rid==0 ){ cgi_redirect("/home"); } |
| @@ -1016,11 +1036,14 @@ | |
| 1016 | } |
| 1017 | } |
| 1018 | style_header("Artifact Content"); |
| 1019 | @ <h2>Content Of:</h2> |
| 1020 | @ <blockquote> |
| 1021 | object_description(rid, 0); |
| 1022 | @ </blockquote> |
| 1023 | @ <hr> |
| 1024 | @ <blockquote> |
| 1025 | content_get(rid, &content); |
| 1026 | zMime = mimetype_from_content(&content); |
| 1027 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -324,10 +324,11 @@ | |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | |
| 328 | /* |
| 329 | ** WEBPAGE: vinfo |
| 330 | ** WEBPAGE: ci |
| 331 | ** URL: /ci?name=RID|ARTIFACTID |
| 332 | ** |
| 333 | ** Return information about a baseline |
| 334 | */ |
| @@ -425,10 +426,11 @@ | |
| 426 | db_finalize(&q); |
| 427 | @ </td></tr> |
| 428 | @ <tr><th>Commands:</th> |
| 429 | @ <td> |
| 430 | @ <a href="%s(g.zBaseURL)/vdiff/%d(rid)">diff</a> |
| 431 | @ | <a href="%s(g.zBaseURL)/dir?ci=%s(zShortUuid)">files</a> |
| 432 | @ | <a href="%s(g.zBaseURL)/zip/%s(zProjName)-%s(zShortUuid).zip?uuid=%s(zUuid)"> |
| 433 | @ ZIP archive</a> |
| 434 | @ | <a href="%s(g.zBaseURL)/artifact/%d(rid)">manifest</a> |
| 435 | if( g.okWrite ){ |
| 436 | @ | <a href="%s(g.zBaseURL)/ci_edit?r=%d(rid)">edit</a> |
| @@ -736,11 +738,12 @@ | |
| 738 | ** * date of check-in |
| 739 | ** * Comment & user |
| 740 | */ |
| 741 | static void object_description( |
| 742 | int rid, /* The artifact ID */ |
| 743 | int linkToView, /* Add viewer link if true */ |
| 744 | Blob *pDownloadName /* Fill with an appropriate download name */ |
| 745 | ){ |
| 746 | Stmt q; |
| 747 | int cnt = 0; |
| 748 | int nWiki = 0; |
| 749 | db_prepare(&q, |
| @@ -771,10 +774,13 @@ | |
| 774 | @ <a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a> |
| 775 | @ uuid %s(zFuuid) part of check-in |
| 776 | hyperlink_to_uuid(zVers); |
| 777 | @ %w(zCom) by %h(zUser) on %s(zDate). |
| 778 | cnt++; |
| 779 | if( pDownloadName && blob_size(pDownloadName)==0 ){ |
| 780 | blob_append(pDownloadName, zName, -1); |
| 781 | } |
| 782 | } |
| 783 | db_finalize(&q); |
| 784 | db_prepare(&q, |
| 785 | "SELECT substr(tagname, 6, 10000), datetime(event.mtime)," |
| 786 | " coalesce(event.euser, event.user), uuid" |
| @@ -798,10 +804,13 @@ | |
| 804 | } |
| 805 | @ [<a href="%s(g.zBaseURL)/wiki?name=%t(zPagename)">%h(zPagename)</a>] |
| 806 | @ uuid %s(zUuid) by %h(zUser) on %s(zDate). |
| 807 | nWiki++; |
| 808 | cnt++; |
| 809 | if( pDownloadName && blob_size(pDownloadName)==0 ){ |
| 810 | blob_append(pDownloadName, zPagename, -1); |
| 811 | } |
| 812 | } |
| 813 | db_finalize(&q); |
| 814 | if( nWiki==0 ){ |
| 815 | db_prepare(&q, |
| 816 | "SELECT datetime(mtime), user, comment, uuid, type" |
| @@ -828,17 +837,23 @@ | |
| 837 | }else{ |
| 838 | @ Control file referencing |
| 839 | } |
| 840 | hyperlink_to_uuid(zUuid); |
| 841 | @ %w(zCom) by %h(zUser) on %s(zDate). |
| 842 | if( pDownloadName && blob_size(pDownloadName)==0 ){ |
| 843 | blob_append(pDownloadName, zUuid, -1); |
| 844 | } |
| 845 | cnt++; |
| 846 | } |
| 847 | db_finalize(&q); |
| 848 | } |
| 849 | if( cnt==0 ){ |
| 850 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 851 | @ Control file %s(zUuid). |
| 852 | if( pDownloadName && blob_size(pDownloadName)==0 ){ |
| 853 | blob_append(pDownloadName, zUuid, -1); |
| 854 | } |
| 855 | }else if( linkToView ){ |
| 856 | @ <a href="%s(g.zBaseURL)/artifact/%d(rid)">[view]</a> |
| 857 | } |
| 858 | } |
| 859 | |
| @@ -856,15 +871,15 @@ | |
| 871 | login_check_credentials(); |
| 872 | if( !g.okRead ){ login_needed(); return; } |
| 873 | style_header("Diff"); |
| 874 | @ <h2>Differences From:</h2> |
| 875 | @ <blockquote> |
| 876 | object_description(v1, 1, 0); |
| 877 | @ </blockquote> |
| 878 | @ <h2>To:</h2> |
| 879 | @ <blockquote> |
| 880 | object_description(v2, 1, 0); |
| 881 | @ </blockquote> |
| 882 | @ <hr> |
| 883 | @ <blockquote><pre> |
| 884 | content_get(v1, &c1); |
| 885 | content_get(v2, &c2); |
| @@ -959,10 +974,11 @@ | |
| 974 | ** as preformatted text. |
| 975 | */ |
| 976 | void hexdump_page(void){ |
| 977 | int rid; |
| 978 | Blob content; |
| 979 | Blob downloadName; |
| 980 | |
| 981 | rid = name_to_rid(PD("name","0")); |
| 982 | login_check_credentials(); |
| 983 | if( !g.okRead ){ login_needed(); return; } |
| 984 | if( rid==0 ){ cgi_redirect("/home"); } |
| @@ -977,11 +993,14 @@ | |
| 993 | } |
| 994 | } |
| 995 | style_header("Hex Artifact Content"); |
| 996 | @ <h2>Hexadecimal Content Of:</h2> |
| 997 | @ <blockquote> |
| 998 | blob_zero(&downloadName); |
| 999 | object_description(rid, 0, &downloadName); |
| 1000 | style_submenu_element("Download", "Download", |
| 1001 | "%s/raw/%T?name=%d", g.zBaseURL, blob_str(&downloadName), rid); |
| 1002 | @ </blockquote> |
| 1003 | @ <hr> |
| 1004 | content_get(rid, &content); |
| 1005 | @ <blockquote><pre> |
| 1006 | hexdump(&content); |
| @@ -998,10 +1017,11 @@ | |
| 1017 | */ |
| 1018 | void artifact_page(void){ |
| 1019 | int rid; |
| 1020 | Blob content; |
| 1021 | const char *zMime; |
| 1022 | Blob downloadName; |
| 1023 | |
| 1024 | rid = name_to_rid(PD("name","0")); |
| 1025 | login_check_credentials(); |
| 1026 | if( !g.okRead ){ login_needed(); return; } |
| 1027 | if( rid==0 ){ cgi_redirect("/home"); } |
| @@ -1016,11 +1036,14 @@ | |
| 1036 | } |
| 1037 | } |
| 1038 | style_header("Artifact Content"); |
| 1039 | @ <h2>Content Of:</h2> |
| 1040 | @ <blockquote> |
| 1041 | blob_zero(&downloadName); |
| 1042 | object_description(rid, 0, &downloadName); |
| 1043 | style_submenu_element("Download", "Download", |
| 1044 | "%s/raw/%T?name=%d", g.zTop, blob_str(&downloadName), rid); |
| 1045 | @ </blockquote> |
| 1046 | @ <hr> |
| 1047 | @ <blockquote> |
| 1048 | content_get(rid, &content); |
| 1049 | zMime = mimetype_from_content(&content); |
| 1050 |