Fossil SCM
Code style cleanups. Wrap some over-long lines.
Commit
a1d272a2066ca80f74943509ad01f8569fc863064df8a627e0d382030a2efb3d
Parent
bab9e5ac6bc2db7…
1 file changed
+58
-40
+58
-40
| --- src/attach.c | ||
| +++ src/attach.c | ||
| @@ -40,11 +40,11 @@ | ||
| 40 | 40 | ** |
| 41 | 41 | ** Wiki page names always require an exact match. |
| 42 | 42 | ** |
| 43 | 43 | ** Forum posts are a special case: |
| 44 | 44 | ** |
| 45 | -** - The ignore the bFull flag. That is, they will do prefix matches | |
| 45 | +** - They ignore the bFull flag. That is, they will do prefix matches | |
| 46 | 46 | ** but will not match an ambiguous prefix. |
| 47 | 47 | ** |
| 48 | 48 | ** - It is up to the caller to, if needed, resolve zTarget using |
| 49 | 49 | ** forumpost_head_rid2() to resolve the RID of the earliest version |
| 50 | 50 | ** of the post, as that is the only one which attachments should |
| @@ -773,11 +773,11 @@ | ||
| 773 | 773 | ** subject to amendment. |
| 774 | 774 | */ |
| 775 | 775 | void attachadd_ajax_post(void){ |
| 776 | 776 | const char *zTarget; |
| 777 | 777 | char *zExtraFree = 0; |
| 778 | - int iTgtType = 0; | |
| 778 | + int eTgtType = 0; | |
| 779 | 779 | int bNeedsModeration = 0; |
| 780 | 780 | int i; |
| 781 | 781 | int goodCaptcha = 1; |
| 782 | 782 | int szLimit; /* attachment-max-size setting */ |
| 783 | 783 | int bRollback = 0; /* Roll back if true. */ |
| @@ -794,13 +794,13 @@ | ||
| 794 | 794 | ajax_route_error(403, "Invalid CSRF signature."); |
| 795 | 795 | return; |
| 796 | 796 | } |
| 797 | 797 | db_begin_transaction(); |
| 798 | 798 | zTarget = P("target"); |
| 799 | - iTgtType = attachment_target_type(zTarget, 1); | |
| 799 | + eTgtType = attachment_target_type(zTarget, 1); | |
| 800 | 800 | CX("{"); |
| 801 | - switch( iTgtType ){ | |
| 801 | + switch( eTgtType ){ | |
| 802 | 802 | default: |
| 803 | 803 | case 0: |
| 804 | 804 | ajax_route_error(400, "Invalid attachment target."); |
| 805 | 805 | db_rollback_transaction(); |
| 806 | 806 | return; |
| @@ -824,11 +824,12 @@ | ||
| 824 | 824 | } |
| 825 | 825 | case CFTYPE_EVENT:{ |
| 826 | 826 | if( g.perm.Write==0 || g.perm.ApndWiki==0 || g.perm.Attach==0 ){ |
| 827 | 827 | goto ajax_post_403; |
| 828 | 828 | } |
| 829 | - if( !db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'", zTarget) ){ | |
| 829 | + if( !db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'", | |
| 830 | + zTarget) ){ | |
| 830 | 831 | zTarget = zExtraFree = |
| 831 | 832 | db_text(0, "SELECT substr(tagname,7) FROM tag" |
| 832 | 833 | " WHERE tagname GLOB 'event-%q*'", zTarget); |
| 833 | 834 | if( zTarget==0){ |
| 834 | 835 | goto ajax_post_404; |
| @@ -839,11 +840,12 @@ | ||
| 839 | 840 | } |
| 840 | 841 | case CFTYPE_TICKET:{ |
| 841 | 842 | if( g.perm.ApndTkt==0 || g.perm.Attach==0 ){ |
| 842 | 843 | goto ajax_post_403; |
| 843 | 844 | } |
| 844 | - if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", zTarget) ){ | |
| 845 | + if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", | |
| 846 | + zTarget) ){ | |
| 845 | 847 | zTarget = db_text(0, "SELECT substr(tagname,5) FROM tag" |
| 846 | 848 | " WHERE tagname GLOB 'tkt-%q*'", zTarget); |
| 847 | 849 | if( zTarget==0 ){ |
| 848 | 850 | goto ajax_post_404; |
| 849 | 851 | } |
| @@ -853,11 +855,12 @@ | ||
| 853 | 855 | } |
| 854 | 856 | case CFTYPE_WIKI:{ |
| 855 | 857 | if( g.perm.ApndWiki==0 || g.perm.Attach==0 ){ |
| 856 | 858 | goto ajax_post_403; |
| 857 | 859 | } |
| 858 | - if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", zTarget) ){ | |
| 860 | + if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", | |
| 861 | + zTarget) ){ | |
| 859 | 862 | goto ajax_post_404; |
| 860 | 863 | } |
| 861 | 864 | bNeedsModeration = wiki_need_moderation(0); |
| 862 | 865 | break; |
| 863 | 866 | } |
| @@ -878,10 +881,11 @@ | ||
| 878 | 881 | aKeyPrefix); |
| 879 | 882 | szContent = atoi(PD(aKeySize,"-1")); |
| 880 | 883 | if( szContent<=0 ){ |
| 881 | 884 | bRollback = 1; |
| 882 | 885 | ajax_route_error(400,"Invalid file size: %d", szContent); |
| 886 | + break; | |
| 883 | 887 | }else if( szLimit>0 && szContent>szLimit ){ |
| 884 | 888 | bRollback = 1; |
| 885 | 889 | ajax_route_error(400, "File size limit is %d bytes.", szLimit); |
| 886 | 890 | break; |
| 887 | 891 | }else{ |
| @@ -919,18 +923,15 @@ | ||
| 919 | 923 | ** |
| 920 | 924 | ** target=TKT_HASH|WIKIPAGE_NAME|TECHNOTE_HASH|FORUMPOST_HASH |
| 921 | 925 | ** from=ORIGINATING_URL |
| 922 | 926 | ** |
| 923 | 927 | ** Works like /attachadd but uses a JS-based interactive attachment |
| 924 | -** selector. If from=X is set, this page arrganges for a redirect | |
| 925 | -** back to X after attaching. | |
| 926 | -** | |
| 927 | -** from=X and to=X tell it how to redirect when it's done. to=X | |
| 928 | -** overrides from=X. If neither is set, it will redirect back to this | |
| 929 | -** page to render the updated attachment list. | |
| 930 | -** | |
| 931 | -** This page requires a post-2020 JS-capable browser. | |
| 928 | +** selector. | |
| 929 | +** | |
| 930 | +** from=X tells it where to redirect to when it's done. | |
| 931 | +** | |
| 932 | +** This page requires a post-2018-ish JS-capable browser. | |
| 932 | 933 | */ |
| 933 | 934 | void attachaddV2_page(void){ |
| 934 | 935 | const char *zFrom = P("from"); |
| 935 | 936 | const char *zTarget = P("target"); |
| 936 | 937 | char *zTo = 0; |
| @@ -964,23 +965,27 @@ | ||
| 964 | 965 | webpage_error("Only admins can attach files to other users' " |
| 965 | 966 | "forum posts."); |
| 966 | 967 | } |
| 967 | 968 | zTarget = zExtraFree = rid_to_uuid(fpid); |
| 968 | 969 | noJsArgs[0] = zTarget; |
| 969 | - zTargetType = mprintf("Forum post <a href=\"%R/forumpost/%S\">%.16h</a>", | |
| 970 | - zTarget, zTarget); | |
| 970 | + zTargetType = mprintf( | |
| 971 | + "Forum post <a href=\"%R/forumpost/%S\">%.16h</a>", | |
| 972 | + zTarget, zTarget | |
| 973 | + ); | |
| 971 | 974 | zTo = mprintf("%R/forumpost/%S", zTarget); |
| 972 | 975 | break; |
| 973 | 976 | } |
| 974 | 977 | case CFTYPE_EVENT:{ |
| 975 | 978 | if( g.perm.Write==0 || g.perm.ApndWiki==0 || g.perm.Attach==0 ){ |
| 976 | 979 | login_needed(g.anon.Write && g.anon.ApndWiki && g.anon.Attach); |
| 977 | 980 | return; |
| 978 | 981 | } |
| 979 | - if( !db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'", zTarget) ){ | |
| 982 | + if( !db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'", | |
| 983 | + zTarget) ){ | |
| 980 | 984 | zTarget = db_text(0, "SELECT substr(tagname,7) FROM tag" |
| 981 | - " WHERE tagname GLOB 'event-%q*'", zTarget); | |
| 985 | + " WHERE tagname GLOB 'event-%q*'", | |
| 986 | + zTarget); | |
| 982 | 987 | if( zTarget==0) fossil_redirect_home(); |
| 983 | 988 | } |
| 984 | 989 | zTo = zFrom ? 0 : mprintf("%R/technote?name=%T", zTarget); |
| 985 | 990 | zTargetType = mprintf("Tech-note <a href=\"%R/technote/%s\">%S</a>", |
| 986 | 991 | zTarget, zTarget); |
| @@ -990,11 +995,12 @@ | ||
| 990 | 995 | case CFTYPE_TICKET:{ |
| 991 | 996 | if( g.perm.ApndTkt==0 || g.perm.Attach==0 ){ |
| 992 | 997 | login_needed(g.anon.ApndTkt && g.anon.Attach); |
| 993 | 998 | return; |
| 994 | 999 | } |
| 995 | - if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", zTarget) ){ | |
| 1000 | + if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", | |
| 1001 | + zTarget) ){ | |
| 996 | 1002 | zTarget = db_text(0, "SELECT substr(tagname,5) FROM tag" |
| 997 | 1003 | " WHERE tagname GLOB 'tkt-%q*'", zTarget); |
| 998 | 1004 | if( zTarget==0 ) fossil_redirect_home(); |
| 999 | 1005 | } |
| 1000 | 1006 | zTo = zFrom ? 0 : mprintf("%R/tktview/%t", zTarget); |
| @@ -1006,16 +1012,19 @@ | ||
| 1006 | 1012 | case CFTYPE_WIKI:{ |
| 1007 | 1013 | if( g.perm.ApndWiki==0 || g.perm.Attach==0 ){ |
| 1008 | 1014 | login_needed(g.anon.ApndWiki && g.anon.Attach); |
| 1009 | 1015 | return; |
| 1010 | 1016 | } |
| 1011 | - if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", zTarget) ){ | |
| 1017 | + if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", | |
| 1018 | + zTarget) ){ | |
| 1012 | 1019 | fossil_redirect_home(); |
| 1013 | 1020 | } |
| 1014 | 1021 | zTo = zFrom ? 0 : mprintf("%R/wiki?name=%T", zTarget); |
| 1015 | - zTargetType = mprintf("Wiki page <a href=\"%R/wiki?name=%h\">%h</a>", | |
| 1016 | - zTarget, zTarget); | |
| 1022 | + zTargetType = mprintf( | |
| 1023 | + "Wiki page <a href=\"%R/wiki?name=%h\">%h</a>", | |
| 1024 | + zTarget, zTarget | |
| 1025 | + ); | |
| 1017 | 1026 | noJsArgs[3] = zTarget; |
| 1018 | 1027 | break; |
| 1019 | 1028 | } |
| 1020 | 1029 | } |
| 1021 | 1030 | |
| @@ -1117,15 +1126,17 @@ | ||
| 1117 | 1126 | && db_exists("SELECT 1 FROM ticket WHERE tkt_uuid='%q'", zTarget) |
| 1118 | 1127 | ){ |
| 1119 | 1128 | if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; } |
| 1120 | 1129 | zTktUuid = zTarget; |
| 1121 | 1130 | showDelMenu = g.perm.WrTkt; |
| 1122 | - }else if( db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'",zTarget) ){ | |
| 1131 | + }else if( db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", | |
| 1132 | + zTarget) ){ | |
| 1123 | 1133 | if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } |
| 1124 | 1134 | zWikiName = zTarget; |
| 1125 | 1135 | showDelMenu = g.perm.WrWiki; |
| 1126 | - }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'",zTarget) ){ | |
| 1136 | + }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'", | |
| 1137 | + zTarget) ){ | |
| 1127 | 1138 | if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } |
| 1128 | 1139 | zTNUuid = zTarget; |
| 1129 | 1140 | showDelMenu = g.perm.Write && g.perm.WrWiki; |
| 1130 | 1141 | } |
| 1131 | 1142 | if( showDelMenu ){ |
| @@ -1149,11 +1160,13 @@ | ||
| 1149 | 1160 | Blob cksum; |
| 1150 | 1161 | const char *zFile = zName; |
| 1151 | 1162 | |
| 1152 | 1163 | if( !bUserIsOwner ){ |
| 1153 | 1164 | if( zForumPost ? !forumpost_may_close() : !g.perm.Admin ){ |
| 1154 | - webpage_error("Only admins can delete other users' attachments."); | |
| 1165 | + webpage_error( | |
| 1166 | + "Only admins can delete other users' attachments." | |
| 1167 | + ); | |
| 1155 | 1168 | } |
| 1156 | 1169 | } |
| 1157 | 1170 | db_begin_transaction(); |
| 1158 | 1171 | blob_zero(&manifest); |
| 1159 | 1172 | for(i=n=0; zFile[i]; i++){ |
| @@ -1173,14 +1186,14 @@ | ||
| 1173 | 1186 | @ <p>The attachment below has been deleted.</p> |
| 1174 | 1187 | fossil_free(zNewDate); |
| 1175 | 1188 | } |
| 1176 | 1189 | |
| 1177 | 1190 | if( P("del") |
| 1178 | - && ((zForumPost && (bUserIsOwner || forumpost_may_close())) || | |
| 1179 | - (zTktUuid && g.perm.WrTkt) || | |
| 1180 | - (zWikiName && g.perm.WrWiki) || | |
| 1181 | - (zTNUuid && g.perm.Write && g.perm.WrWiki)) | |
| 1191 | + && ((zForumPost && (bUserIsOwner || forumpost_may_close())) | |
| 1192 | + || (zTktUuid && g.perm.WrTkt) | |
| 1193 | + || (zWikiName && g.perm.WrWiki) | |
| 1194 | + || (zTNUuid && g.perm.Write && g.perm.WrWiki)) | |
| 1182 | 1195 | ){ |
| 1183 | 1196 | form_begin(0, "%R/ainfo/%!S", zUuid); |
| 1184 | 1197 | @ <p>Confirm you want to delete the attachment shown below. |
| 1185 | 1198 | @ <input type="submit" name="confirm" value="Confirm"> |
| 1186 | 1199 | login_insert_csrf_secret(); |
| @@ -1228,20 +1241,22 @@ | ||
| 1228 | 1241 | @ (%d(rid)) |
| 1229 | 1242 | } |
| 1230 | 1243 | modPending = moderation_pending_www(rid); |
| 1231 | 1244 | if( zForumPost ){ |
| 1232 | 1245 | @ <tr><th>Forum Post:</th> |
| 1233 | - @ <td>%z(href("%R/forumpost/%s",zForumPost))%h(zForumPost)</a></td></tr> | |
| 1246 | + @ <td>%z(href("%R/forumpost/%s",zForumPost))%h(zForumPost)</a>\ | |
| 1247 | + @ </td></tr> | |
| 1234 | 1248 | }else if( zTktUuid ){ |
| 1235 | 1249 | @ <tr><th>Ticket:</th> |
| 1236 | 1250 | @ <td>%z(href("%R/tktview/%s",zTktUuid))%s(zTktUuid)</a></td></tr> |
| 1237 | 1251 | }else if( zTNUuid ){ |
| 1238 | 1252 | @ <tr><th>Tech Note:</th> |
| 1239 | 1253 | @ <td>%z(href("%R/technote/%s",zTNUuid))%s(zTNUuid)</a></td></tr> |
| 1240 | 1254 | }else if( zWikiName ){ |
| 1241 | 1255 | @ <tr><th>Wiki Page:</th> |
| 1242 | - @ <td>%z(href("%R/wiki?name=%t",zWikiName))%h(zWikiName)</a></td></tr> | |
| 1256 | + @ <td>%z(href("%R/wiki?name=%t",zWikiName))%h(zWikiName)</a>\ | |
| 1257 | + @ </td></tr> | |
| 1243 | 1258 | } |
| 1244 | 1259 | @ <tr><th>Date:</th><td> |
| 1245 | 1260 | hyperlink_to_date(zDate, "</td></tr>"); |
| 1246 | 1261 | @ <tr><th>User:</th><td> |
| 1247 | 1262 | hyperlink_to_user(pAttach->zUser, zDate, "</td></tr>"); |
| @@ -1252,11 +1267,12 @@ | ||
| 1252 | 1267 | } |
| 1253 | 1268 | @ <tr><th>Filename:</th><td>%h(zName)</td></tr> |
| 1254 | 1269 | if( g.perm.Setup ){ |
| 1255 | 1270 | @ <tr><th>MIME-Type:</th><td>%h(zMime)</td></tr> |
| 1256 | 1271 | } |
| 1257 | - @ <tr><th valign="top">Description:</th><td valign="top">%h(zDesc)</td></tr> | |
| 1272 | + @ <tr><th valign="top">Description:</th>\ | |
| 1273 | + @ <td valign="top">%h(zDesc)</td></tr> | |
| 1258 | 1274 | @ </table> |
| 1259 | 1275 | |
| 1260 | 1276 | if( modPending && (isModerator || bUserIsOwner) ){ |
| 1261 | 1277 | @ <div class="section">Moderation</div> |
| 1262 | 1278 | @ <blockquote> |
| @@ -1263,18 +1279,19 @@ | ||
| 1263 | 1279 | form_begin(0, "%R/ainfo/%s", zUuid); |
| 1264 | 1280 | @ <label><input type="radio" name="modaction" value="delete"> |
| 1265 | 1281 | @ Delete this attachment</label><br> |
| 1266 | 1282 | if( isModerator ){ |
| 1267 | 1283 | #if 0 |
| 1268 | - /* TODO: only allow approval of an attachment if its target has | |
| 1269 | - ** been approved. Without this, we can end up with stale | |
| 1270 | - ** attachments which refer to rejected targets. We need a | |
| 1271 | - ** type-specific RID/UUID here, which requires refactoring | |
| 1272 | - ** above to get it. */ | |
| 1284 | + /* TODO/FIXME (2026-06-03): only allow approval of an attachment | |
| 1285 | + ** if its target has been approved. Without this, we can end up | |
| 1286 | + ** with stale attachments which refer to rejected targets. We | |
| 1287 | + ** need a type-specific RID/UUID here, which requires | |
| 1288 | + ** refactoring above to get it. */ | |
| 1273 | 1289 | const int tgtid = 0; |
| 1274 | 1290 | if( moderation_pending(tgtid) ){ |
| 1275 | - @ <label><input type="radio" name="modaction" disabled value="approve"> | |
| 1291 | + @ <label><input type="radio" name="modaction" \ | |
| 1292 | + @ disabled value="approve"> | |
| 1276 | 1293 | @ <span class='modpending'>Cannot approve: |
| 1277 | 1294 | @ target is pending moderation</span>\ |
| 1278 | 1295 | @ </label><br> |
| 1279 | 1296 | }else |
| 1280 | 1297 | #else |
| @@ -1301,11 +1318,12 @@ | ||
| 1301 | 1318 | const char *z; |
| 1302 | 1319 | content_get(ridSrc, &attach); |
| 1303 | 1320 | blob_to_utf8_no_bom(&attach, 0); |
| 1304 | 1321 | z = blob_str(&attach); |
| 1305 | 1322 | if( zLn ){ |
| 1306 | - output_text_with_line_numbers(z, blob_size(&attach), zName, zLn, 1); | |
| 1323 | + output_text_with_line_numbers(z, blob_size(&attach), | |
| 1324 | + zName, zLn, 1); | |
| 1307 | 1325 | }else{ |
| 1308 | 1326 | @ <pre> |
| 1309 | 1327 | @ %h(z) |
| 1310 | 1328 | @ </pre> |
| 1311 | 1329 | } |
| 1312 | 1330 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -40,11 +40,11 @@ | |
| 40 | ** |
| 41 | ** Wiki page names always require an exact match. |
| 42 | ** |
| 43 | ** Forum posts are a special case: |
| 44 | ** |
| 45 | ** - The ignore the bFull flag. That is, they will do prefix matches |
| 46 | ** but will not match an ambiguous prefix. |
| 47 | ** |
| 48 | ** - It is up to the caller to, if needed, resolve zTarget using |
| 49 | ** forumpost_head_rid2() to resolve the RID of the earliest version |
| 50 | ** of the post, as that is the only one which attachments should |
| @@ -773,11 +773,11 @@ | |
| 773 | ** subject to amendment. |
| 774 | */ |
| 775 | void attachadd_ajax_post(void){ |
| 776 | const char *zTarget; |
| 777 | char *zExtraFree = 0; |
| 778 | int iTgtType = 0; |
| 779 | int bNeedsModeration = 0; |
| 780 | int i; |
| 781 | int goodCaptcha = 1; |
| 782 | int szLimit; /* attachment-max-size setting */ |
| 783 | int bRollback = 0; /* Roll back if true. */ |
| @@ -794,13 +794,13 @@ | |
| 794 | ajax_route_error(403, "Invalid CSRF signature."); |
| 795 | return; |
| 796 | } |
| 797 | db_begin_transaction(); |
| 798 | zTarget = P("target"); |
| 799 | iTgtType = attachment_target_type(zTarget, 1); |
| 800 | CX("{"); |
| 801 | switch( iTgtType ){ |
| 802 | default: |
| 803 | case 0: |
| 804 | ajax_route_error(400, "Invalid attachment target."); |
| 805 | db_rollback_transaction(); |
| 806 | return; |
| @@ -824,11 +824,12 @@ | |
| 824 | } |
| 825 | case CFTYPE_EVENT:{ |
| 826 | if( g.perm.Write==0 || g.perm.ApndWiki==0 || g.perm.Attach==0 ){ |
| 827 | goto ajax_post_403; |
| 828 | } |
| 829 | if( !db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'", zTarget) ){ |
| 830 | zTarget = zExtraFree = |
| 831 | db_text(0, "SELECT substr(tagname,7) FROM tag" |
| 832 | " WHERE tagname GLOB 'event-%q*'", zTarget); |
| 833 | if( zTarget==0){ |
| 834 | goto ajax_post_404; |
| @@ -839,11 +840,12 @@ | |
| 839 | } |
| 840 | case CFTYPE_TICKET:{ |
| 841 | if( g.perm.ApndTkt==0 || g.perm.Attach==0 ){ |
| 842 | goto ajax_post_403; |
| 843 | } |
| 844 | if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", zTarget) ){ |
| 845 | zTarget = db_text(0, "SELECT substr(tagname,5) FROM tag" |
| 846 | " WHERE tagname GLOB 'tkt-%q*'", zTarget); |
| 847 | if( zTarget==0 ){ |
| 848 | goto ajax_post_404; |
| 849 | } |
| @@ -853,11 +855,12 @@ | |
| 853 | } |
| 854 | case CFTYPE_WIKI:{ |
| 855 | if( g.perm.ApndWiki==0 || g.perm.Attach==0 ){ |
| 856 | goto ajax_post_403; |
| 857 | } |
| 858 | if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", zTarget) ){ |
| 859 | goto ajax_post_404; |
| 860 | } |
| 861 | bNeedsModeration = wiki_need_moderation(0); |
| 862 | break; |
| 863 | } |
| @@ -878,10 +881,11 @@ | |
| 878 | aKeyPrefix); |
| 879 | szContent = atoi(PD(aKeySize,"-1")); |
| 880 | if( szContent<=0 ){ |
| 881 | bRollback = 1; |
| 882 | ajax_route_error(400,"Invalid file size: %d", szContent); |
| 883 | }else if( szLimit>0 && szContent>szLimit ){ |
| 884 | bRollback = 1; |
| 885 | ajax_route_error(400, "File size limit is %d bytes.", szLimit); |
| 886 | break; |
| 887 | }else{ |
| @@ -919,18 +923,15 @@ | |
| 919 | ** |
| 920 | ** target=TKT_HASH|WIKIPAGE_NAME|TECHNOTE_HASH|FORUMPOST_HASH |
| 921 | ** from=ORIGINATING_URL |
| 922 | ** |
| 923 | ** Works like /attachadd but uses a JS-based interactive attachment |
| 924 | ** selector. If from=X is set, this page arrganges for a redirect |
| 925 | ** back to X after attaching. |
| 926 | ** |
| 927 | ** from=X and to=X tell it how to redirect when it's done. to=X |
| 928 | ** overrides from=X. If neither is set, it will redirect back to this |
| 929 | ** page to render the updated attachment list. |
| 930 | ** |
| 931 | ** This page requires a post-2020 JS-capable browser. |
| 932 | */ |
| 933 | void attachaddV2_page(void){ |
| 934 | const char *zFrom = P("from"); |
| 935 | const char *zTarget = P("target"); |
| 936 | char *zTo = 0; |
| @@ -964,23 +965,27 @@ | |
| 964 | webpage_error("Only admins can attach files to other users' " |
| 965 | "forum posts."); |
| 966 | } |
| 967 | zTarget = zExtraFree = rid_to_uuid(fpid); |
| 968 | noJsArgs[0] = zTarget; |
| 969 | zTargetType = mprintf("Forum post <a href=\"%R/forumpost/%S\">%.16h</a>", |
| 970 | zTarget, zTarget); |
| 971 | zTo = mprintf("%R/forumpost/%S", zTarget); |
| 972 | break; |
| 973 | } |
| 974 | case CFTYPE_EVENT:{ |
| 975 | if( g.perm.Write==0 || g.perm.ApndWiki==0 || g.perm.Attach==0 ){ |
| 976 | login_needed(g.anon.Write && g.anon.ApndWiki && g.anon.Attach); |
| 977 | return; |
| 978 | } |
| 979 | if( !db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'", zTarget) ){ |
| 980 | zTarget = db_text(0, "SELECT substr(tagname,7) FROM tag" |
| 981 | " WHERE tagname GLOB 'event-%q*'", zTarget); |
| 982 | if( zTarget==0) fossil_redirect_home(); |
| 983 | } |
| 984 | zTo = zFrom ? 0 : mprintf("%R/technote?name=%T", zTarget); |
| 985 | zTargetType = mprintf("Tech-note <a href=\"%R/technote/%s\">%S</a>", |
| 986 | zTarget, zTarget); |
| @@ -990,11 +995,12 @@ | |
| 990 | case CFTYPE_TICKET:{ |
| 991 | if( g.perm.ApndTkt==0 || g.perm.Attach==0 ){ |
| 992 | login_needed(g.anon.ApndTkt && g.anon.Attach); |
| 993 | return; |
| 994 | } |
| 995 | if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", zTarget) ){ |
| 996 | zTarget = db_text(0, "SELECT substr(tagname,5) FROM tag" |
| 997 | " WHERE tagname GLOB 'tkt-%q*'", zTarget); |
| 998 | if( zTarget==0 ) fossil_redirect_home(); |
| 999 | } |
| 1000 | zTo = zFrom ? 0 : mprintf("%R/tktview/%t", zTarget); |
| @@ -1006,16 +1012,19 @@ | |
| 1006 | case CFTYPE_WIKI:{ |
| 1007 | if( g.perm.ApndWiki==0 || g.perm.Attach==0 ){ |
| 1008 | login_needed(g.anon.ApndWiki && g.anon.Attach); |
| 1009 | return; |
| 1010 | } |
| 1011 | if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", zTarget) ){ |
| 1012 | fossil_redirect_home(); |
| 1013 | } |
| 1014 | zTo = zFrom ? 0 : mprintf("%R/wiki?name=%T", zTarget); |
| 1015 | zTargetType = mprintf("Wiki page <a href=\"%R/wiki?name=%h\">%h</a>", |
| 1016 | zTarget, zTarget); |
| 1017 | noJsArgs[3] = zTarget; |
| 1018 | break; |
| 1019 | } |
| 1020 | } |
| 1021 | |
| @@ -1117,15 +1126,17 @@ | |
| 1117 | && db_exists("SELECT 1 FROM ticket WHERE tkt_uuid='%q'", zTarget) |
| 1118 | ){ |
| 1119 | if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; } |
| 1120 | zTktUuid = zTarget; |
| 1121 | showDelMenu = g.perm.WrTkt; |
| 1122 | }else if( db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'",zTarget) ){ |
| 1123 | if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } |
| 1124 | zWikiName = zTarget; |
| 1125 | showDelMenu = g.perm.WrWiki; |
| 1126 | }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'",zTarget) ){ |
| 1127 | if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } |
| 1128 | zTNUuid = zTarget; |
| 1129 | showDelMenu = g.perm.Write && g.perm.WrWiki; |
| 1130 | } |
| 1131 | if( showDelMenu ){ |
| @@ -1149,11 +1160,13 @@ | |
| 1149 | Blob cksum; |
| 1150 | const char *zFile = zName; |
| 1151 | |
| 1152 | if( !bUserIsOwner ){ |
| 1153 | if( zForumPost ? !forumpost_may_close() : !g.perm.Admin ){ |
| 1154 | webpage_error("Only admins can delete other users' attachments."); |
| 1155 | } |
| 1156 | } |
| 1157 | db_begin_transaction(); |
| 1158 | blob_zero(&manifest); |
| 1159 | for(i=n=0; zFile[i]; i++){ |
| @@ -1173,14 +1186,14 @@ | |
| 1173 | @ <p>The attachment below has been deleted.</p> |
| 1174 | fossil_free(zNewDate); |
| 1175 | } |
| 1176 | |
| 1177 | if( P("del") |
| 1178 | && ((zForumPost && (bUserIsOwner || forumpost_may_close())) || |
| 1179 | (zTktUuid && g.perm.WrTkt) || |
| 1180 | (zWikiName && g.perm.WrWiki) || |
| 1181 | (zTNUuid && g.perm.Write && g.perm.WrWiki)) |
| 1182 | ){ |
| 1183 | form_begin(0, "%R/ainfo/%!S", zUuid); |
| 1184 | @ <p>Confirm you want to delete the attachment shown below. |
| 1185 | @ <input type="submit" name="confirm" value="Confirm"> |
| 1186 | login_insert_csrf_secret(); |
| @@ -1228,20 +1241,22 @@ | |
| 1228 | @ (%d(rid)) |
| 1229 | } |
| 1230 | modPending = moderation_pending_www(rid); |
| 1231 | if( zForumPost ){ |
| 1232 | @ <tr><th>Forum Post:</th> |
| 1233 | @ <td>%z(href("%R/forumpost/%s",zForumPost))%h(zForumPost)</a></td></tr> |
| 1234 | }else if( zTktUuid ){ |
| 1235 | @ <tr><th>Ticket:</th> |
| 1236 | @ <td>%z(href("%R/tktview/%s",zTktUuid))%s(zTktUuid)</a></td></tr> |
| 1237 | }else if( zTNUuid ){ |
| 1238 | @ <tr><th>Tech Note:</th> |
| 1239 | @ <td>%z(href("%R/technote/%s",zTNUuid))%s(zTNUuid)</a></td></tr> |
| 1240 | }else if( zWikiName ){ |
| 1241 | @ <tr><th>Wiki Page:</th> |
| 1242 | @ <td>%z(href("%R/wiki?name=%t",zWikiName))%h(zWikiName)</a></td></tr> |
| 1243 | } |
| 1244 | @ <tr><th>Date:</th><td> |
| 1245 | hyperlink_to_date(zDate, "</td></tr>"); |
| 1246 | @ <tr><th>User:</th><td> |
| 1247 | hyperlink_to_user(pAttach->zUser, zDate, "</td></tr>"); |
| @@ -1252,11 +1267,12 @@ | |
| 1252 | } |
| 1253 | @ <tr><th>Filename:</th><td>%h(zName)</td></tr> |
| 1254 | if( g.perm.Setup ){ |
| 1255 | @ <tr><th>MIME-Type:</th><td>%h(zMime)</td></tr> |
| 1256 | } |
| 1257 | @ <tr><th valign="top">Description:</th><td valign="top">%h(zDesc)</td></tr> |
| 1258 | @ </table> |
| 1259 | |
| 1260 | if( modPending && (isModerator || bUserIsOwner) ){ |
| 1261 | @ <div class="section">Moderation</div> |
| 1262 | @ <blockquote> |
| @@ -1263,18 +1279,19 @@ | |
| 1263 | form_begin(0, "%R/ainfo/%s", zUuid); |
| 1264 | @ <label><input type="radio" name="modaction" value="delete"> |
| 1265 | @ Delete this attachment</label><br> |
| 1266 | if( isModerator ){ |
| 1267 | #if 0 |
| 1268 | /* TODO: only allow approval of an attachment if its target has |
| 1269 | ** been approved. Without this, we can end up with stale |
| 1270 | ** attachments which refer to rejected targets. We need a |
| 1271 | ** type-specific RID/UUID here, which requires refactoring |
| 1272 | ** above to get it. */ |
| 1273 | const int tgtid = 0; |
| 1274 | if( moderation_pending(tgtid) ){ |
| 1275 | @ <label><input type="radio" name="modaction" disabled value="approve"> |
| 1276 | @ <span class='modpending'>Cannot approve: |
| 1277 | @ target is pending moderation</span>\ |
| 1278 | @ </label><br> |
| 1279 | }else |
| 1280 | #else |
| @@ -1301,11 +1318,12 @@ | |
| 1301 | const char *z; |
| 1302 | content_get(ridSrc, &attach); |
| 1303 | blob_to_utf8_no_bom(&attach, 0); |
| 1304 | z = blob_str(&attach); |
| 1305 | if( zLn ){ |
| 1306 | output_text_with_line_numbers(z, blob_size(&attach), zName, zLn, 1); |
| 1307 | }else{ |
| 1308 | @ <pre> |
| 1309 | @ %h(z) |
| 1310 | @ </pre> |
| 1311 | } |
| 1312 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -40,11 +40,11 @@ | |
| 40 | ** |
| 41 | ** Wiki page names always require an exact match. |
| 42 | ** |
| 43 | ** Forum posts are a special case: |
| 44 | ** |
| 45 | ** - They ignore the bFull flag. That is, they will do prefix matches |
| 46 | ** but will not match an ambiguous prefix. |
| 47 | ** |
| 48 | ** - It is up to the caller to, if needed, resolve zTarget using |
| 49 | ** forumpost_head_rid2() to resolve the RID of the earliest version |
| 50 | ** of the post, as that is the only one which attachments should |
| @@ -773,11 +773,11 @@ | |
| 773 | ** subject to amendment. |
| 774 | */ |
| 775 | void attachadd_ajax_post(void){ |
| 776 | const char *zTarget; |
| 777 | char *zExtraFree = 0; |
| 778 | int eTgtType = 0; |
| 779 | int bNeedsModeration = 0; |
| 780 | int i; |
| 781 | int goodCaptcha = 1; |
| 782 | int szLimit; /* attachment-max-size setting */ |
| 783 | int bRollback = 0; /* Roll back if true. */ |
| @@ -794,13 +794,13 @@ | |
| 794 | ajax_route_error(403, "Invalid CSRF signature."); |
| 795 | return; |
| 796 | } |
| 797 | db_begin_transaction(); |
| 798 | zTarget = P("target"); |
| 799 | eTgtType = attachment_target_type(zTarget, 1); |
| 800 | CX("{"); |
| 801 | switch( eTgtType ){ |
| 802 | default: |
| 803 | case 0: |
| 804 | ajax_route_error(400, "Invalid attachment target."); |
| 805 | db_rollback_transaction(); |
| 806 | return; |
| @@ -824,11 +824,12 @@ | |
| 824 | } |
| 825 | case CFTYPE_EVENT:{ |
| 826 | if( g.perm.Write==0 || g.perm.ApndWiki==0 || g.perm.Attach==0 ){ |
| 827 | goto ajax_post_403; |
| 828 | } |
| 829 | if( !db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'", |
| 830 | zTarget) ){ |
| 831 | zTarget = zExtraFree = |
| 832 | db_text(0, "SELECT substr(tagname,7) FROM tag" |
| 833 | " WHERE tagname GLOB 'event-%q*'", zTarget); |
| 834 | if( zTarget==0){ |
| 835 | goto ajax_post_404; |
| @@ -839,11 +840,12 @@ | |
| 840 | } |
| 841 | case CFTYPE_TICKET:{ |
| 842 | if( g.perm.ApndTkt==0 || g.perm.Attach==0 ){ |
| 843 | goto ajax_post_403; |
| 844 | } |
| 845 | if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", |
| 846 | zTarget) ){ |
| 847 | zTarget = db_text(0, "SELECT substr(tagname,5) FROM tag" |
| 848 | " WHERE tagname GLOB 'tkt-%q*'", zTarget); |
| 849 | if( zTarget==0 ){ |
| 850 | goto ajax_post_404; |
| 851 | } |
| @@ -853,11 +855,12 @@ | |
| 855 | } |
| 856 | case CFTYPE_WIKI:{ |
| 857 | if( g.perm.ApndWiki==0 || g.perm.Attach==0 ){ |
| 858 | goto ajax_post_403; |
| 859 | } |
| 860 | if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", |
| 861 | zTarget) ){ |
| 862 | goto ajax_post_404; |
| 863 | } |
| 864 | bNeedsModeration = wiki_need_moderation(0); |
| 865 | break; |
| 866 | } |
| @@ -878,10 +881,11 @@ | |
| 881 | aKeyPrefix); |
| 882 | szContent = atoi(PD(aKeySize,"-1")); |
| 883 | if( szContent<=0 ){ |
| 884 | bRollback = 1; |
| 885 | ajax_route_error(400,"Invalid file size: %d", szContent); |
| 886 | break; |
| 887 | }else if( szLimit>0 && szContent>szLimit ){ |
| 888 | bRollback = 1; |
| 889 | ajax_route_error(400, "File size limit is %d bytes.", szLimit); |
| 890 | break; |
| 891 | }else{ |
| @@ -919,18 +923,15 @@ | |
| 923 | ** |
| 924 | ** target=TKT_HASH|WIKIPAGE_NAME|TECHNOTE_HASH|FORUMPOST_HASH |
| 925 | ** from=ORIGINATING_URL |
| 926 | ** |
| 927 | ** Works like /attachadd but uses a JS-based interactive attachment |
| 928 | ** selector. |
| 929 | ** |
| 930 | ** from=X tells it where to redirect to when it's done. |
| 931 | ** |
| 932 | ** This page requires a post-2018-ish JS-capable browser. |
| 933 | */ |
| 934 | void attachaddV2_page(void){ |
| 935 | const char *zFrom = P("from"); |
| 936 | const char *zTarget = P("target"); |
| 937 | char *zTo = 0; |
| @@ -964,23 +965,27 @@ | |
| 965 | webpage_error("Only admins can attach files to other users' " |
| 966 | "forum posts."); |
| 967 | } |
| 968 | zTarget = zExtraFree = rid_to_uuid(fpid); |
| 969 | noJsArgs[0] = zTarget; |
| 970 | zTargetType = mprintf( |
| 971 | "Forum post <a href=\"%R/forumpost/%S\">%.16h</a>", |
| 972 | zTarget, zTarget |
| 973 | ); |
| 974 | zTo = mprintf("%R/forumpost/%S", zTarget); |
| 975 | break; |
| 976 | } |
| 977 | case CFTYPE_EVENT:{ |
| 978 | if( g.perm.Write==0 || g.perm.ApndWiki==0 || g.perm.Attach==0 ){ |
| 979 | login_needed(g.anon.Write && g.anon.ApndWiki && g.anon.Attach); |
| 980 | return; |
| 981 | } |
| 982 | if( !db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'", |
| 983 | zTarget) ){ |
| 984 | zTarget = db_text(0, "SELECT substr(tagname,7) FROM tag" |
| 985 | " WHERE tagname GLOB 'event-%q*'", |
| 986 | zTarget); |
| 987 | if( zTarget==0) fossil_redirect_home(); |
| 988 | } |
| 989 | zTo = zFrom ? 0 : mprintf("%R/technote?name=%T", zTarget); |
| 990 | zTargetType = mprintf("Tech-note <a href=\"%R/technote/%s\">%S</a>", |
| 991 | zTarget, zTarget); |
| @@ -990,11 +995,12 @@ | |
| 995 | case CFTYPE_TICKET:{ |
| 996 | if( g.perm.ApndTkt==0 || g.perm.Attach==0 ){ |
| 997 | login_needed(g.anon.ApndTkt && g.anon.Attach); |
| 998 | return; |
| 999 | } |
| 1000 | if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", |
| 1001 | zTarget) ){ |
| 1002 | zTarget = db_text(0, "SELECT substr(tagname,5) FROM tag" |
| 1003 | " WHERE tagname GLOB 'tkt-%q*'", zTarget); |
| 1004 | if( zTarget==0 ) fossil_redirect_home(); |
| 1005 | } |
| 1006 | zTo = zFrom ? 0 : mprintf("%R/tktview/%t", zTarget); |
| @@ -1006,16 +1012,19 @@ | |
| 1012 | case CFTYPE_WIKI:{ |
| 1013 | if( g.perm.ApndWiki==0 || g.perm.Attach==0 ){ |
| 1014 | login_needed(g.anon.ApndWiki && g.anon.Attach); |
| 1015 | return; |
| 1016 | } |
| 1017 | if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", |
| 1018 | zTarget) ){ |
| 1019 | fossil_redirect_home(); |
| 1020 | } |
| 1021 | zTo = zFrom ? 0 : mprintf("%R/wiki?name=%T", zTarget); |
| 1022 | zTargetType = mprintf( |
| 1023 | "Wiki page <a href=\"%R/wiki?name=%h\">%h</a>", |
| 1024 | zTarget, zTarget |
| 1025 | ); |
| 1026 | noJsArgs[3] = zTarget; |
| 1027 | break; |
| 1028 | } |
| 1029 | } |
| 1030 | |
| @@ -1117,15 +1126,17 @@ | |
| 1126 | && db_exists("SELECT 1 FROM ticket WHERE tkt_uuid='%q'", zTarget) |
| 1127 | ){ |
| 1128 | if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; } |
| 1129 | zTktUuid = zTarget; |
| 1130 | showDelMenu = g.perm.WrTkt; |
| 1131 | }else if( db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", |
| 1132 | zTarget) ){ |
| 1133 | if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } |
| 1134 | zWikiName = zTarget; |
| 1135 | showDelMenu = g.perm.WrWiki; |
| 1136 | }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'", |
| 1137 | zTarget) ){ |
| 1138 | if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } |
| 1139 | zTNUuid = zTarget; |
| 1140 | showDelMenu = g.perm.Write && g.perm.WrWiki; |
| 1141 | } |
| 1142 | if( showDelMenu ){ |
| @@ -1149,11 +1160,13 @@ | |
| 1160 | Blob cksum; |
| 1161 | const char *zFile = zName; |
| 1162 | |
| 1163 | if( !bUserIsOwner ){ |
| 1164 | if( zForumPost ? !forumpost_may_close() : !g.perm.Admin ){ |
| 1165 | webpage_error( |
| 1166 | "Only admins can delete other users' attachments." |
| 1167 | ); |
| 1168 | } |
| 1169 | } |
| 1170 | db_begin_transaction(); |
| 1171 | blob_zero(&manifest); |
| 1172 | for(i=n=0; zFile[i]; i++){ |
| @@ -1173,14 +1186,14 @@ | |
| 1186 | @ <p>The attachment below has been deleted.</p> |
| 1187 | fossil_free(zNewDate); |
| 1188 | } |
| 1189 | |
| 1190 | if( P("del") |
| 1191 | && ((zForumPost && (bUserIsOwner || forumpost_may_close())) |
| 1192 | || (zTktUuid && g.perm.WrTkt) |
| 1193 | || (zWikiName && g.perm.WrWiki) |
| 1194 | || (zTNUuid && g.perm.Write && g.perm.WrWiki)) |
| 1195 | ){ |
| 1196 | form_begin(0, "%R/ainfo/%!S", zUuid); |
| 1197 | @ <p>Confirm you want to delete the attachment shown below. |
| 1198 | @ <input type="submit" name="confirm" value="Confirm"> |
| 1199 | login_insert_csrf_secret(); |
| @@ -1228,20 +1241,22 @@ | |
| 1241 | @ (%d(rid)) |
| 1242 | } |
| 1243 | modPending = moderation_pending_www(rid); |
| 1244 | if( zForumPost ){ |
| 1245 | @ <tr><th>Forum Post:</th> |
| 1246 | @ <td>%z(href("%R/forumpost/%s",zForumPost))%h(zForumPost)</a>\ |
| 1247 | @ </td></tr> |
| 1248 | }else if( zTktUuid ){ |
| 1249 | @ <tr><th>Ticket:</th> |
| 1250 | @ <td>%z(href("%R/tktview/%s",zTktUuid))%s(zTktUuid)</a></td></tr> |
| 1251 | }else if( zTNUuid ){ |
| 1252 | @ <tr><th>Tech Note:</th> |
| 1253 | @ <td>%z(href("%R/technote/%s",zTNUuid))%s(zTNUuid)</a></td></tr> |
| 1254 | }else if( zWikiName ){ |
| 1255 | @ <tr><th>Wiki Page:</th> |
| 1256 | @ <td>%z(href("%R/wiki?name=%t",zWikiName))%h(zWikiName)</a>\ |
| 1257 | @ </td></tr> |
| 1258 | } |
| 1259 | @ <tr><th>Date:</th><td> |
| 1260 | hyperlink_to_date(zDate, "</td></tr>"); |
| 1261 | @ <tr><th>User:</th><td> |
| 1262 | hyperlink_to_user(pAttach->zUser, zDate, "</td></tr>"); |
| @@ -1252,11 +1267,12 @@ | |
| 1267 | } |
| 1268 | @ <tr><th>Filename:</th><td>%h(zName)</td></tr> |
| 1269 | if( g.perm.Setup ){ |
| 1270 | @ <tr><th>MIME-Type:</th><td>%h(zMime)</td></tr> |
| 1271 | } |
| 1272 | @ <tr><th valign="top">Description:</th>\ |
| 1273 | @ <td valign="top">%h(zDesc)</td></tr> |
| 1274 | @ </table> |
| 1275 | |
| 1276 | if( modPending && (isModerator || bUserIsOwner) ){ |
| 1277 | @ <div class="section">Moderation</div> |
| 1278 | @ <blockquote> |
| @@ -1263,18 +1279,19 @@ | |
| 1279 | form_begin(0, "%R/ainfo/%s", zUuid); |
| 1280 | @ <label><input type="radio" name="modaction" value="delete"> |
| 1281 | @ Delete this attachment</label><br> |
| 1282 | if( isModerator ){ |
| 1283 | #if 0 |
| 1284 | /* TODO/FIXME (2026-06-03): only allow approval of an attachment |
| 1285 | ** if its target has been approved. Without this, we can end up |
| 1286 | ** with stale attachments which refer to rejected targets. We |
| 1287 | ** need a type-specific RID/UUID here, which requires |
| 1288 | ** refactoring above to get it. */ |
| 1289 | const int tgtid = 0; |
| 1290 | if( moderation_pending(tgtid) ){ |
| 1291 | @ <label><input type="radio" name="modaction" \ |
| 1292 | @ disabled value="approve"> |
| 1293 | @ <span class='modpending'>Cannot approve: |
| 1294 | @ target is pending moderation</span>\ |
| 1295 | @ </label><br> |
| 1296 | }else |
| 1297 | #else |
| @@ -1301,11 +1318,12 @@ | |
| 1318 | const char *z; |
| 1319 | content_get(ridSrc, &attach); |
| 1320 | blob_to_utf8_no_bom(&attach, 0); |
| 1321 | z = blob_str(&attach); |
| 1322 | if( zLn ){ |
| 1323 | output_text_with_line_numbers(z, blob_size(&attach), |
| 1324 | zName, zLn, 1); |
| 1325 | }else{ |
| 1326 | @ <pre> |
| 1327 | @ %h(z) |
| 1328 | @ </pre> |
| 1329 | } |
| 1330 |