| | @@ -416,12 +416,13 @@ |
| 416 | 416 | ** (not accounting for an inherited closed tag), this is a no-op. |
| 417 | 417 | ** |
| 418 | 418 | ** If bCheckIrt is true then the forum post IRT hierarchy is searched |
| 419 | 419 | ** for the tag, otherwise only the given RID is checked. |
| 420 | 420 | ** |
| 421 | | -** Returns true if it actually creates a new tag, else false. Fails |
| 422 | | -** fatally on error. |
| 421 | +** Returns a positive value (a new tag.tagid value) if it actually |
| 422 | +** creates a new tag, else 0. On error it returns a negative alue |
| 423 | +** and g.zErrMsg "should" contain details. |
| 423 | 424 | ** |
| 424 | 425 | ** If it returns true then state from previously-loaded posts may be |
| 425 | 426 | ** invalidated if they refer to the amended post or a response to it. |
| 426 | 427 | ** e.g. if zTagName is "closed" then ForumPost::iClosed values may be |
| 427 | 428 | ** stale. |
| | @@ -484,22 +485,22 @@ |
| 484 | 485 | md5sum_blob(&artifact, &cksum); |
| 485 | 486 | blob_appendf(&artifact, "Z %b\n", &cksum); |
| 486 | 487 | blob_reset(&cksum); |
| 487 | 488 | trid = content_put_ex(&artifact, 0, 0, 0, 0); |
| 488 | 489 | if( trid==0 ){ |
| 489 | | - fossil_fatal("Error saving tag artifact: %s", g.zErrMsg); |
| 490 | + return -1; |
| 490 | 491 | } |
| 491 | 492 | if( manifest_crosslink(trid, &artifact, MC_NONE)==0 ){ |
| 492 | | - fossil_fatal("%s", g.zErrMsg); |
| 493 | + return -2; |
| 493 | 494 | } |
| 494 | 495 | assert( blob_is_reset(&artifact) ); |
| 495 | 496 | db_add_unsent(trid); |
| 496 | 497 | admin_log("Tag forum post %S with %c%s", |
| 497 | 498 | zUuid, addTag ? '*' : '-', zTagName); |
| 498 | 499 | fossil_free(zUuid); |
| 499 | 500 | db_end_transaction(0); |
| 500 | | - return 1; |
| 501 | + return trid; |
| 501 | 502 | } |
| 502 | 503 | |
| 503 | 504 | /* |
| 504 | 505 | ** Returns true if the forum-close-policy setting is true, else false, |
| 505 | 506 | ** caching the result for subsequent calls. |
| | @@ -1658,11 +1659,11 @@ |
| 1658 | 1659 | return z[0]==0; |
| 1659 | 1660 | } |
| 1660 | 1661 | |
| 1661 | 1662 | /* Flags for use with forum_post() */ |
| 1662 | 1663 | #define FPOST_NO_ALERT 1 /* do not send any alerts */ |
| 1663 | | -#define FPOST_DRY_RUN 2 /* do not save the artifact */ |
| 1664 | +#define FPOST_DRYRUN 2 /* do not save the artifact */ |
| 1664 | 1665 | |
| 1665 | 1666 | /* |
| 1666 | 1667 | ** Return a flags value for use with the final argument to |
| 1667 | 1668 | ** forum_post(), extracted from the CGI environment. |
| 1668 | 1669 | */ |
| | @@ -1670,11 +1671,11 @@ |
| 1670 | 1671 | int iPostFlags = 0; |
| 1671 | 1672 | if( g.perm.Debug && P("fpsilent")!=0 ){ |
| 1672 | 1673 | iPostFlags |= FPOST_NO_ALERT; |
| 1673 | 1674 | } |
| 1674 | 1675 | if( P("dryrun")!=0 ){ |
| 1675 | | - iPostFlags |= FPOST_DRY_RUN; |
| 1676 | + iPostFlags |= FPOST_DRYRUN; |
| 1676 | 1677 | } |
| 1677 | 1678 | return iPostFlags; |
| 1678 | 1679 | } |
| 1679 | 1680 | |
| 1680 | 1681 | /* |
| | @@ -1768,11 +1769,11 @@ |
| 1768 | 1769 | webpage_error("malformed forum post artifact - %s", blob_str(&errMsg)); |
| 1769 | 1770 | } |
| 1770 | 1771 | webpage_assert( pPost->type==CFTYPE_FORUM ); |
| 1771 | 1772 | manifest_destroy(pPost); |
| 1772 | 1773 | |
| 1773 | | - if( (iFlags & FPOST_DRY_RUN)!=0 ){ |
| 1774 | + if( (iFlags & FPOST_DRYRUN)!=0 ){ |
| 1774 | 1775 | @ <div class='debug'> |
| 1775 | 1776 | @ This is the artifact that would have been generated: |
| 1776 | 1777 | @ <pre>%h(blob_str(&x))</pre> |
| 1777 | 1778 | @ </div> |
| 1778 | 1779 | blob_reset(&x); |
| | @@ -1831,12 +1832,17 @@ |
| 1831 | 1832 | int addTag, int validFpid){ |
| 1832 | 1833 | if( !cgi_csrf_safe(2) ){ |
| 1833 | 1834 | webpage_error("CSRF validation failed"); |
| 1834 | 1835 | }else{ |
| 1835 | 1836 | const int fpid = validFpid>0 ? validFpid : forum_validate_fpid_param(); |
| 1836 | | - forumpost_tag(fpid, addTag, zTag, zVal); |
| 1837 | | - cgi_redirectf("%R/forumpost/%S",P("fpid")); |
| 1837 | + if( fpid>0 ){ |
| 1838 | + if( forumpost_tag(fpid, addTag, zTag, zVal) < 0 ){ |
| 1839 | + webpage_error("Tagging artifact failed: %s", g.zErrMsg); |
| 1840 | + }else{ |
| 1841 | + cgi_redirectf("%R/forumpost/%S",P("fpid")); |
| 1842 | + } |
| 1843 | + } |
| 1838 | 1844 | } |
| 1839 | 1845 | } |
| 1840 | 1846 | |
| 1841 | 1847 | /* |
| 1842 | 1848 | ** WEBPAGE: forumpost_close hidden |
| | @@ -2918,11 +2924,11 @@ |
| 2918 | 2924 | rc = -500; |
| 2919 | 2925 | goto post_ajax_end; |
| 2920 | 2926 | } |
| 2921 | 2927 | webpage_assert( pPost->type==CFTYPE_FORUM ); |
| 2922 | 2928 | |
| 2923 | | - if( (iFlags & FPOST_DRY_RUN)!=0 ){ |
| 2929 | + if( (iFlags & FPOST_DRYRUN)!=0 ){ |
| 2924 | 2930 | rc = 0; |
| 2925 | 2931 | }else{ |
| 2926 | 2932 | int nrid; |
| 2927 | 2933 | db_begin_transaction(); |
| 2928 | 2934 | nrid = wiki_put(&x, iEdit>0 ? iEdit : 0, forum_need_moderation()); |
| | @@ -2948,25 +2954,25 @@ |
| 2948 | 2954 | ** Response JSON: |
| 2949 | 2955 | ** |
| 2950 | 2956 | ** { uuid: hash, ...tbd } |
| 2951 | 2957 | */ |
| 2952 | 2958 | void forum_ajax_save(void){ |
| 2953 | | - const char *zUuid; |
| 2959 | + const char *zFpid; |
| 2954 | 2960 | const char *zTitle; |
| 2955 | 2961 | const char *zIrt; |
| 2956 | 2962 | const char *zMimetype; |
| 2957 | 2963 | const char *zContent; |
| 2958 | 2964 | const char *zStatus; |
| 2959 | 2965 | const int bHasAttachment = P("file1")!=0; |
| 2960 | 2966 | char *zNewUuid = 0; |
| 2961 | | - int bNeedsModeration = 0; |
| 2962 | 2967 | int goodCaptcha = 1; |
| 2963 | | - int bRollback = PB("rollback"); /* True if we should roll back. */ |
| 2964 | 2968 | int iIrt = 0; /* In-reply-to rid or 0 */ |
| 2965 | 2969 | int iEditRid = 0; /* Post rid being edited or 0 */ |
| 2966 | 2970 | int rc = 0; |
| 2967 | 2971 | int nrid = 0; |
| 2972 | + const int iPostFlags = forum_post_flags(); |
| 2973 | + int bRollback = (FPOST_DRYRUN & iPostFlags); /* True = roll back. */ |
| 2968 | 2974 | |
| 2969 | 2975 | if( !ajax_route_bootstrap(0, 1) ){ |
| 2970 | 2976 | return; |
| 2971 | 2977 | }else if( !g.perm.WrForum |
| 2972 | 2978 | || (bHasAttachment && !g.perm.AttachForum) ){ |
| | @@ -2992,60 +2998,89 @@ |
| 2992 | 2998 | ** - Permissions and sanity checks, of course. |
| 2993 | 2999 | ** |
| 2994 | 3000 | ** - Fork forum_post() into an AJAX-friendly form. It currently |
| 2995 | 3001 | ** assumes HTML output. |
| 2996 | 3002 | ** |
| 2997 | | - ** - If zUuid then this is an edit. Else... |
| 3003 | + ** - If zFpid then this is an edit. Else... |
| 2998 | 3004 | ** |
| 2999 | 3005 | ** - If zIrt then this is a new response. |
| 3000 | 3006 | ** |
| 3001 | | - ** - zTitle is only honored if !zIrt, i.e. zUuid is the root post. |
| 3007 | + ** - zTitle is only honored if !zIrt, i.e. zFpid is the root post. |
| 3002 | 3008 | ** |
| 3003 | 3009 | ** - attachments_ajax_from_POST() |
| 3004 | 3010 | ** |
| 3005 | 3011 | ** - Allow status change only if permissions allow. |
| 3006 | 3012 | */ |
| 3007 | 3013 | |
| 3008 | | - (void)bNeedsModeration; |
| 3009 | | - (void)zUuid; |
| 3010 | | - (void)zIrt; |
| 3011 | | - (void)zMimetype; |
| 3012 | | - (void)zContent; |
| 3013 | | - (void)zStatus; |
| 3014 | | - |
| 3015 | | - if( 1 ){ |
| 3016 | | - bRollback = 1; |
| 3017 | | - ajax_route_error(400, "Save is TODO"); |
| 3014 | + if( zFpid ){ |
| 3015 | + iEditRid = symbolic_name_to_rid(zFpid, "f"); |
| 3016 | + if( iEditRid<0 ){ |
| 3017 | + rc = -ajax_route_error(400, "Ambiguous forum ID."); |
| 3018 | + goto ajax_save_end; |
| 3019 | + }else if( 0==iEditRid ){ |
| 3020 | + rc = -ajax_route_error(404, "Cannot resolve forum post ID."); |
| 3021 | + goto ajax_save_end; |
| 3022 | + } |
| 3023 | + } |
| 3024 | + if( zIrt ){ |
| 3025 | + iIrt = symbolic_name_to_rid(zIrt, "f"); |
| 3026 | + if( iIrt<0 ){ |
| 3027 | + rc = -ajax_route_error(400, "Ambiguous in-reply-do ID."); |
| 3028 | + goto ajax_save_end; |
| 3029 | + }else if( 0==iIrt ){ |
| 3030 | + rc = -ajax_route_error(404, "Cannot resolve in-reply-do ID."); |
| 3031 | + goto ajax_save_end; |
| 3032 | + } |
| 3033 | + } |
| 3034 | + |
| 3035 | + if( 0 ){ |
| 3036 | + rc = -ajax_route_error(400, "Save is TODO"); |
| 3018 | 3037 | goto ajax_save_end; |
| 3019 | 3038 | } |
| 3020 | 3039 | |
| 3021 | 3040 | nrid = forum_post_ajax(zTitle, iIrt, iEditRid, 0, zMimetype, |
| 3022 | | - zContent, forum_post_flags()); |
| 3023 | | - if( nrid==0 ){ |
| 3024 | | - CX("{\"message\": \"No changes needed saving.\"}\n"); |
| 3025 | | - goto ajax_save_end; |
| 3026 | | - } |
| 3027 | | - if( zStatus!=0 && zStatus[0]!=0 ){ |
| 3028 | | - forumpost_tag(nrid, 1, "status", zStatus) |
| 3029 | | - /* FIXME: ^^^ fails fatally on error */; |
| 3030 | | - } |
| 3031 | | - zNewUuid = rid_to_uuid(nrid); |
| 3032 | | - if( 0!=P("file1") ){ |
| 3033 | | - /* Attachments */ |
| 3034 | | - const int atRc = |
| 3035 | | - attachments_ajax_from_POST(zNewUuid, bNeedsModeration); |
| 3036 | | - if( atRc<0 ){ |
| 3037 | | - rc = atRc; |
| 3038 | | - goto ajax_save_end; |
| 3039 | | - } |
| 3040 | | - } |
| 3041 | | - |
| 3042 | | - |
| 3043 | | - if( 0==rc ){ |
| 3044 | | - CX("{\"uuid\": %!j}\n", zNewUuid); |
| 3045 | | - } |
| 3046 | | - |
| 3047 | | -ajax_save_end: |
| 3048 | | - fossil_free(zNewUuid); |
| 3049 | | - if( 0!=rc ) bRollback = 1; |
| 3050 | | - db_end_transaction(bRollback); |
| 3041 | + zContent, iPostFlags); |
| 3042 | + if( nrid<0 ){ |
| 3043 | + rc = nrid; |
| 3044 | + goto ajax_save_end; |
| 3045 | + }else if( nrid==0 ){ |
| 3046 | + if( 0==(FPOST_DRYRUN & iPostFlags) ){ |
| 3047 | + bRollback = 1; |
| 3048 | + CX("{\"message\": \"No saving needed.\"}\n"); |
| 3049 | + }else{ |
| 3050 | + CX("{\"message\": \"Rolled back for dry-run.\"}\n"); |
| 3051 | + } |
| 3052 | + goto ajax_save_end; |
| 3053 | + } |
| 3054 | + if( nrid>0 ){ |
| 3055 | + zNewUuid = rid_to_uuid(nrid); |
| 3056 | + if( 0!=P("file1") ){ |
| 3057 | + /* Attachments */ |
| 3058 | + if( !g.perm.Admin && !g.perm.AttachForum ){ |
| 3059 | + rc = -ajax_route_error(403, "No permission no attach files."); |
| 3060 | + goto ajax_save_end; |
| 3061 | + }else{ |
| 3062 | + const int atRc = |
| 3063 | + attachments_ajax_from_POST(zNewUuid, forum_need_moderation()); |
| 3064 | + if( atRc<0 ){ |
| 3065 | + rc = atRc; |
| 3066 | + goto ajax_save_end; |
| 3067 | + } |
| 3068 | + } |
| 3069 | + } |
| 3070 | + if( zStatus!=0 && zStatus[0]!=0 |
| 3071 | + && forum_may_set_status(nrid) |
| 3072 | + && forumpost_tag(nrid, 1, "status", zStatus)<0 ){ |
| 3073 | + rc = -ajax_route_error(500, "Tagging failed: %s", g.zErrMsg); |
| 3074 | + goto ajax_save_end; |
| 3075 | + } |
| 3076 | + } |
| 3077 | + |
| 3078 | + assert( 0==rc ); |
| 3079 | + assert( zNewUuid ); |
| 3080 | + CX("{\"uuid\": %!j, \"dryrun\": %s}\n", |
| 3081 | + zNewUuid, bRollback ? "true" : "false"); |
| 3082 | + |
| 3083 | +ajax_save_end: |
| 3084 | + fossil_free(zNewUuid); |
| 3085 | + db_end_transaction(rc || bRollback); |
| 3051 | 3086 | } |
| 3052 | 3087 | |