Fossil SCM
Make the draft's mimetype and status persistent.
Commit
033d265fd057c6026d2bbbcf5f1925ac3990bf04d0b254bb348b50af05b6fc89
Parent
2ee0f61f8c26d51…
2 files changed
+2
-4
+39
-37
+2
-4
| --- src/attach.c | ||
| +++ src/attach.c | ||
| @@ -782,21 +782,19 @@ | ||
| 782 | 782 | char *zExtraFree = 0; |
| 783 | 783 | int eTgtType = 0; |
| 784 | 784 | int bNeedsModeration = 0; |
| 785 | 785 | int goodCaptcha = 1; |
| 786 | 786 | int bRollback = 0; /* Roll back if true. */ |
| 787 | - int bInTransaction = 0; | |
| 788 | 787 | |
| 789 | 788 | if( ! ajax_route_bootstrap(0, 1) ){ |
| 790 | 789 | return; |
| 791 | 790 | }else if( !(goodCaptcha = captcha_is_correct(0)) ){ |
| 792 | 791 | goto ajax_err_403; |
| 793 | 792 | }else if( !ajax_check_csrf(2) ){ |
| 794 | 793 | return; |
| 795 | 794 | } |
| 796 | 795 | db_begin_transaction(); |
| 797 | - bInTransaction = 1; | |
| 798 | 796 | zTarget = P("target"); |
| 799 | 797 | eTgtType = attachment_target_type(zTarget, 1); |
| 800 | 798 | CX("{"); |
| 801 | 799 | switch( eTgtType ){ |
| 802 | 800 | default: |
| @@ -874,17 +872,17 @@ | ||
| 874 | 872 | }/*else error response was set up*/ |
| 875 | 873 | fossil_free(zExtraFree); |
| 876 | 874 | db_end_transaction(bRollback); |
| 877 | 875 | return; |
| 878 | 876 | ajax_err_403: |
| 879 | - if( bInTransaction ){ | |
| 877 | + if( db_transaction_nesting_depth()>0 ){ | |
| 880 | 878 | db_rollback_transaction(); |
| 881 | 879 | } |
| 882 | 880 | ajax_route_error_forbidden(); |
| 883 | 881 | return; |
| 884 | 882 | ajax_err_404: |
| 885 | - assert( bInTransaction ); | |
| 883 | + assert( db_transaction_nesting_depth()>0 ); | |
| 886 | 884 | db_rollback_transaction(); |
| 887 | 885 | ajax_route_error(404, "Target not found."); |
| 888 | 886 | return; |
| 889 | 887 | } |
| 890 | 888 | |
| 891 | 889 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -782,21 +782,19 @@ | |
| 782 | char *zExtraFree = 0; |
| 783 | int eTgtType = 0; |
| 784 | int bNeedsModeration = 0; |
| 785 | int goodCaptcha = 1; |
| 786 | int bRollback = 0; /* Roll back if true. */ |
| 787 | int bInTransaction = 0; |
| 788 | |
| 789 | if( ! ajax_route_bootstrap(0, 1) ){ |
| 790 | return; |
| 791 | }else if( !(goodCaptcha = captcha_is_correct(0)) ){ |
| 792 | goto ajax_err_403; |
| 793 | }else if( !ajax_check_csrf(2) ){ |
| 794 | return; |
| 795 | } |
| 796 | db_begin_transaction(); |
| 797 | bInTransaction = 1; |
| 798 | zTarget = P("target"); |
| 799 | eTgtType = attachment_target_type(zTarget, 1); |
| 800 | CX("{"); |
| 801 | switch( eTgtType ){ |
| 802 | default: |
| @@ -874,17 +872,17 @@ | |
| 874 | }/*else error response was set up*/ |
| 875 | fossil_free(zExtraFree); |
| 876 | db_end_transaction(bRollback); |
| 877 | return; |
| 878 | ajax_err_403: |
| 879 | if( bInTransaction ){ |
| 880 | db_rollback_transaction(); |
| 881 | } |
| 882 | ajax_route_error_forbidden(); |
| 883 | return; |
| 884 | ajax_err_404: |
| 885 | assert( bInTransaction ); |
| 886 | db_rollback_transaction(); |
| 887 | ajax_route_error(404, "Target not found."); |
| 888 | return; |
| 889 | } |
| 890 | |
| 891 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -782,21 +782,19 @@ | |
| 782 | char *zExtraFree = 0; |
| 783 | int eTgtType = 0; |
| 784 | int bNeedsModeration = 0; |
| 785 | int goodCaptcha = 1; |
| 786 | int bRollback = 0; /* Roll back if true. */ |
| 787 | |
| 788 | if( ! ajax_route_bootstrap(0, 1) ){ |
| 789 | return; |
| 790 | }else if( !(goodCaptcha = captcha_is_correct(0)) ){ |
| 791 | goto ajax_err_403; |
| 792 | }else if( !ajax_check_csrf(2) ){ |
| 793 | return; |
| 794 | } |
| 795 | db_begin_transaction(); |
| 796 | zTarget = P("target"); |
| 797 | eTgtType = attachment_target_type(zTarget, 1); |
| 798 | CX("{"); |
| 799 | switch( eTgtType ){ |
| 800 | default: |
| @@ -874,17 +872,17 @@ | |
| 872 | }/*else error response was set up*/ |
| 873 | fossil_free(zExtraFree); |
| 874 | db_end_transaction(bRollback); |
| 875 | return; |
| 876 | ajax_err_403: |
| 877 | if( db_transaction_nesting_depth()>0 ){ |
| 878 | db_rollback_transaction(); |
| 879 | } |
| 880 | ajax_route_error_forbidden(); |
| 881 | return; |
| 882 | ajax_err_404: |
| 883 | assert( db_transaction_nesting_depth()>0 ); |
| 884 | db_rollback_transaction(); |
| 885 | ajax_route_error(404, "Target not found."); |
| 886 | return; |
| 887 | } |
| 888 | |
| 889 |
+39
-37
| --- src/fossil.page.forumpost.js | ||
| +++ src/fossil.page.forumpost.js | ||
| @@ -48,16 +48,11 @@ | ||
| 48 | 48 | // edit: hash |
| 49 | 49 | draftKey: undefined |
| 50 | 50 | }, opt); |
| 51 | 51 | opt.isNewThread = !opt.replyTo && !opt.edit; |
| 52 | 52 | if( opt.draftKey ){ |
| 53 | - this.#draft = F.storage.getJSON(opt.draftKey, F.nu({ | |
| 54 | - title: undefined, | |
| 55 | - content: undefined, | |
| 56 | - mimetype: undefined, | |
| 57 | - status: undefined | |
| 58 | - })); | |
| 53 | + this.#draft = F.nu(F.storage.getJSON(opt.draftKey, {})); | |
| 59 | 54 | } |
| 60 | 55 | const e = this.#e = F.nu({ |
| 61 | 56 | mimetype: F.nu(), |
| 62 | 57 | button: F.nu() |
| 63 | 58 | }); |
| @@ -88,27 +83,26 @@ | ||
| 88 | 83 | { /* Mimetype... */ |
| 89 | 84 | e.mimetype.wrapper = D.addClass(D.div(), 'mimetype-wrapper'); |
| 90 | 85 | e.mimetype.select = D.addClass(D.select(), 'mimetype-select'); |
| 91 | 86 | this.#toDisable.push(e.mimetype.select); |
| 92 | 87 | let i = 0; |
| 93 | - D.option(e.mimetype.select, '', 'Markdown format').disabled = true; | |
| 88 | + D.option(e.mimetype.select, '', 'Markup format').disabled = true; | |
| 94 | 89 | for(const [k,v] of Object.entries({ |
| 95 | 90 | 'text/x-markdown': 'Markdown', |
| 96 | 91 | 'text/x-fossil-wiki': 'Fossil Wiki', |
| 97 | 92 | 'text/plain': 'Plain text' |
| 98 | 93 | })) { |
| 99 | - const o = D.option(e.mimetype.select, k, v); | |
| 100 | - if( (opt.isNewThread && !i++) | |
| 101 | - || opt.mimetype===k ) o.setAttribute('selected', ''); | |
| 102 | - } | |
| 103 | - if( 0 ){ | |
| 104 | - e.mimetype.label = D.span(); | |
| 105 | - e.mimetype.label.append( | |
| 106 | - D.a(F.repoUrl('markup_help'), 'Markup style'), | |
| 107 | - ':' | |
| 108 | - ); | |
| 109 | - e.mimetype.wrapper.append(e.mimetype.label); | |
| 94 | + D.option(e.mimetype.select, k, v); | |
| 95 | + } | |
| 96 | + if( this.#draft ){ | |
| 97 | + e.mimetype.select.value = opt.mimetype || this.#draft.mimetype; | |
| 98 | + e.mimetype.select.addEventListener('change',ev=>{ | |
| 99 | + if( this.#draft.mimetype!==ev.target.value ){ | |
| 100 | + this.#draft.mimetype = ev.target.value; | |
| 101 | + this.#storeDraft(); | |
| 102 | + } | |
| 103 | + }); | |
| 110 | 104 | } |
| 111 | 105 | e.mimetype.wrapper.append(e.mimetype.select); |
| 112 | 106 | } |
| 113 | 107 | |
| 114 | 108 | e.buttons = D.addClass(D.div(), 'buttons'); |
| @@ -213,29 +207,37 @@ | ||
| 213 | 207 | } |
| 214 | 208 | this.#tabs.addTab(e.debug); |
| 215 | 209 | } |
| 216 | 210 | e.buttons.append(e.mimetype.wrapper); |
| 217 | 211 | |
| 218 | - if( 0 ){ | |
| 219 | - /* | |
| 220 | - Status selection. We probably don't _really_ want this in | |
| 221 | - the editor because people will open the editor, change the | |
| 222 | - status, and tap submit, resulting in a whole new, unedited | |
| 223 | - copy of the post, differing only in the new 'status' tag | |
| 224 | - added to it. | |
| 225 | - */ | |
| 226 | - if( F.config.forumStatuses?.length>0 ){ | |
| 227 | - const sel = e.status = D.select(); | |
| 228 | - D.option(sel, "", "- Status -").disabled = true; | |
| 229 | - sel.dataset.originalValue = opt.status; | |
| 230 | - for( const status of F.config.forumStatuses ){ | |
| 231 | - D.option(sel, status.value, status.label); | |
| 232 | - } | |
| 233 | - e.buttons.append(sel); | |
| 234 | - if( opt.status ){ | |
| 235 | - sel.value = opt.status; | |
| 236 | - } | |
| 212 | + if( 0 && F.config.forumStatuses?.length>0 ){ | |
| 213 | + /* Status selection. We probably don't _really_ want this in | |
| 214 | + the editor because people will open the editor, change the | |
| 215 | + status, and tap submit, resulting in a whole new, unedited | |
| 216 | + copy of the post, differing only in the new 'status' tag | |
| 217 | + added to it. */ | |
| 218 | + const sel = e.status = D.select(); | |
| 219 | + D.option(sel, "", "- Status -").disabled = true; | |
| 220 | + for( const status of F.config.forumStatuses ){ | |
| 221 | + D.option(sel, status.value, status.label); | |
| 222 | + } | |
| 223 | + e.buttons.append(sel); | |
| 224 | + if( opt.status ){ | |
| 225 | + sel.value = opt.status; | |
| 226 | + }else if( this.#draft ){ | |
| 227 | + if( this.#draft.status ){ | |
| 228 | + sel.value = this.#draft.status; | |
| 229 | + }else{ | |
| 230 | + this.#draft.status = sel.value = F.config.forumStatuses[0].value; | |
| 231 | + } | |
| 232 | + sel.addEventListener('change',ev=>{ | |
| 233 | + const v = sel.value; | |
| 234 | + if( this.#draft.status !== v ){ | |
| 235 | + this.#draft.status = v; | |
| 236 | + this.#storeDraft(); | |
| 237 | + } | |
| 238 | + }); | |
| 237 | 239 | } |
| 238 | 240 | } |
| 239 | 241 | |
| 240 | 242 | if( F.user.mayAttachForum ){ |
| 241 | 243 | //e.buttons.append( e.button.addAttach = this.#att.takeAddButton() ); |
| 242 | 244 |
| --- src/fossil.page.forumpost.js | |
| +++ src/fossil.page.forumpost.js | |
| @@ -48,16 +48,11 @@ | |
| 48 | // edit: hash |
| 49 | draftKey: undefined |
| 50 | }, opt); |
| 51 | opt.isNewThread = !opt.replyTo && !opt.edit; |
| 52 | if( opt.draftKey ){ |
| 53 | this.#draft = F.storage.getJSON(opt.draftKey, F.nu({ |
| 54 | title: undefined, |
| 55 | content: undefined, |
| 56 | mimetype: undefined, |
| 57 | status: undefined |
| 58 | })); |
| 59 | } |
| 60 | const e = this.#e = F.nu({ |
| 61 | mimetype: F.nu(), |
| 62 | button: F.nu() |
| 63 | }); |
| @@ -88,27 +83,26 @@ | |
| 88 | { /* Mimetype... */ |
| 89 | e.mimetype.wrapper = D.addClass(D.div(), 'mimetype-wrapper'); |
| 90 | e.mimetype.select = D.addClass(D.select(), 'mimetype-select'); |
| 91 | this.#toDisable.push(e.mimetype.select); |
| 92 | let i = 0; |
| 93 | D.option(e.mimetype.select, '', 'Markdown format').disabled = true; |
| 94 | for(const [k,v] of Object.entries({ |
| 95 | 'text/x-markdown': 'Markdown', |
| 96 | 'text/x-fossil-wiki': 'Fossil Wiki', |
| 97 | 'text/plain': 'Plain text' |
| 98 | })) { |
| 99 | const o = D.option(e.mimetype.select, k, v); |
| 100 | if( (opt.isNewThread && !i++) |
| 101 | || opt.mimetype===k ) o.setAttribute('selected', ''); |
| 102 | } |
| 103 | if( 0 ){ |
| 104 | e.mimetype.label = D.span(); |
| 105 | e.mimetype.label.append( |
| 106 | D.a(F.repoUrl('markup_help'), 'Markup style'), |
| 107 | ':' |
| 108 | ); |
| 109 | e.mimetype.wrapper.append(e.mimetype.label); |
| 110 | } |
| 111 | e.mimetype.wrapper.append(e.mimetype.select); |
| 112 | } |
| 113 | |
| 114 | e.buttons = D.addClass(D.div(), 'buttons'); |
| @@ -213,29 +207,37 @@ | |
| 213 | } |
| 214 | this.#tabs.addTab(e.debug); |
| 215 | } |
| 216 | e.buttons.append(e.mimetype.wrapper); |
| 217 | |
| 218 | if( 0 ){ |
| 219 | /* |
| 220 | Status selection. We probably don't _really_ want this in |
| 221 | the editor because people will open the editor, change the |
| 222 | status, and tap submit, resulting in a whole new, unedited |
| 223 | copy of the post, differing only in the new 'status' tag |
| 224 | added to it. |
| 225 | */ |
| 226 | if( F.config.forumStatuses?.length>0 ){ |
| 227 | const sel = e.status = D.select(); |
| 228 | D.option(sel, "", "- Status -").disabled = true; |
| 229 | sel.dataset.originalValue = opt.status; |
| 230 | for( const status of F.config.forumStatuses ){ |
| 231 | D.option(sel, status.value, status.label); |
| 232 | } |
| 233 | e.buttons.append(sel); |
| 234 | if( opt.status ){ |
| 235 | sel.value = opt.status; |
| 236 | } |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | if( F.user.mayAttachForum ){ |
| 241 | //e.buttons.append( e.button.addAttach = this.#att.takeAddButton() ); |
| 242 |
| --- src/fossil.page.forumpost.js | |
| +++ src/fossil.page.forumpost.js | |
| @@ -48,16 +48,11 @@ | |
| 48 | // edit: hash |
| 49 | draftKey: undefined |
| 50 | }, opt); |
| 51 | opt.isNewThread = !opt.replyTo && !opt.edit; |
| 52 | if( opt.draftKey ){ |
| 53 | this.#draft = F.nu(F.storage.getJSON(opt.draftKey, {})); |
| 54 | } |
| 55 | const e = this.#e = F.nu({ |
| 56 | mimetype: F.nu(), |
| 57 | button: F.nu() |
| 58 | }); |
| @@ -88,27 +83,26 @@ | |
| 83 | { /* Mimetype... */ |
| 84 | e.mimetype.wrapper = D.addClass(D.div(), 'mimetype-wrapper'); |
| 85 | e.mimetype.select = D.addClass(D.select(), 'mimetype-select'); |
| 86 | this.#toDisable.push(e.mimetype.select); |
| 87 | let i = 0; |
| 88 | D.option(e.mimetype.select, '', 'Markup format').disabled = true; |
| 89 | for(const [k,v] of Object.entries({ |
| 90 | 'text/x-markdown': 'Markdown', |
| 91 | 'text/x-fossil-wiki': 'Fossil Wiki', |
| 92 | 'text/plain': 'Plain text' |
| 93 | })) { |
| 94 | D.option(e.mimetype.select, k, v); |
| 95 | } |
| 96 | if( this.#draft ){ |
| 97 | e.mimetype.select.value = opt.mimetype || this.#draft.mimetype; |
| 98 | e.mimetype.select.addEventListener('change',ev=>{ |
| 99 | if( this.#draft.mimetype!==ev.target.value ){ |
| 100 | this.#draft.mimetype = ev.target.value; |
| 101 | this.#storeDraft(); |
| 102 | } |
| 103 | }); |
| 104 | } |
| 105 | e.mimetype.wrapper.append(e.mimetype.select); |
| 106 | } |
| 107 | |
| 108 | e.buttons = D.addClass(D.div(), 'buttons'); |
| @@ -213,29 +207,37 @@ | |
| 207 | } |
| 208 | this.#tabs.addTab(e.debug); |
| 209 | } |
| 210 | e.buttons.append(e.mimetype.wrapper); |
| 211 | |
| 212 | if( 0 && F.config.forumStatuses?.length>0 ){ |
| 213 | /* Status selection. We probably don't _really_ want this in |
| 214 | the editor because people will open the editor, change the |
| 215 | status, and tap submit, resulting in a whole new, unedited |
| 216 | copy of the post, differing only in the new 'status' tag |
| 217 | added to it. */ |
| 218 | const sel = e.status = D.select(); |
| 219 | D.option(sel, "", "- Status -").disabled = true; |
| 220 | for( const status of F.config.forumStatuses ){ |
| 221 | D.option(sel, status.value, status.label); |
| 222 | } |
| 223 | e.buttons.append(sel); |
| 224 | if( opt.status ){ |
| 225 | sel.value = opt.status; |
| 226 | }else if( this.#draft ){ |
| 227 | if( this.#draft.status ){ |
| 228 | sel.value = this.#draft.status; |
| 229 | }else{ |
| 230 | this.#draft.status = sel.value = F.config.forumStatuses[0].value; |
| 231 | } |
| 232 | sel.addEventListener('change',ev=>{ |
| 233 | const v = sel.value; |
| 234 | if( this.#draft.status !== v ){ |
| 235 | this.#draft.status = v; |
| 236 | this.#storeDraft(); |
| 237 | } |
| 238 | }); |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | if( F.user.mayAttachForum ){ |
| 243 | //e.buttons.append( e.button.addAttach = this.#att.takeAddButton() ); |
| 244 |