Fossil SCM
In /forum, when selecting a status filter clear out the x= hidden form field so that we return to the start of the list for the newly-selected status. This requires a JS-side solution. Just now discovered that it's not filtering quite properly when the explicitly-selected status is the default status and a post has a status=Z value for which Z is not in the configured status list. Per the agreed-upon rules, such posts should be treated as having the first/default status but they're currently being filtered out in that case.
Commit
d1fb4a49bb3418d2fc411a8b5259bdf44de517c443efc2495aa3f3f6c40756ed
Parent
45a8de423b9af62…
2 files changed
+9
+74
-58
+9
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -2499,10 +2499,14 @@ | ||
| 2499 | 2499 | iStatusTagId, !!zStatusFilter, zStatusFilter |
| 2500 | 2500 | ); |
| 2501 | 2501 | if( zStatusFilter ){ |
| 2502 | 2502 | const int bIsDflt = |
| 2503 | 2503 | 0==fossil_strcmp(pFstat->aStatus[0].zValue, zStatusFilter); |
| 2504 | + const int bIsKnown =bIsDflt | |
| 2505 | + ? 1 | |
| 2506 | + : db_int(0, "SELECT 1 FROM forumstatus WHERE value=%Q", | |
| 2507 | + zStatusFilter); | |
| 2504 | 2508 | db_multi_exec( |
| 2505 | 2509 | "INSERT INTO trootid\n" |
| 2506 | 2510 | /* Rules: |
| 2507 | 2511 | |
| 2508 | 2512 | (1) Filter on status=$zStatusFilter |
| @@ -2677,7 +2681,12 @@ | ||
| 2677 | 2681 | if( iCnt>0 ){ |
| 2678 | 2682 | @ </table></div> |
| 2679 | 2683 | }else{ |
| 2680 | 2684 | @ <h1>No forum posts found</h1> |
| 2681 | 2685 | } |
| 2686 | + if( bHasStatus ){ | |
| 2687 | + /* We need a JS-side kludge to avoid passing on the x=N | |
| 2688 | + ** URL arg when the status selection list is activated. */ | |
| 2689 | + forum_emit_js(); | |
| 2690 | + } | |
| 2682 | 2691 | style_finish_page(); |
| 2683 | 2692 | } |
| 2684 | 2693 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -2499,10 +2499,14 @@ | |
| 2499 | iStatusTagId, !!zStatusFilter, zStatusFilter |
| 2500 | ); |
| 2501 | if( zStatusFilter ){ |
| 2502 | const int bIsDflt = |
| 2503 | 0==fossil_strcmp(pFstat->aStatus[0].zValue, zStatusFilter); |
| 2504 | db_multi_exec( |
| 2505 | "INSERT INTO trootid\n" |
| 2506 | /* Rules: |
| 2507 | |
| 2508 | (1) Filter on status=$zStatusFilter |
| @@ -2677,7 +2681,12 @@ | |
| 2677 | if( iCnt>0 ){ |
| 2678 | @ </table></div> |
| 2679 | }else{ |
| 2680 | @ <h1>No forum posts found</h1> |
| 2681 | } |
| 2682 | style_finish_page(); |
| 2683 | } |
| 2684 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -2499,10 +2499,14 @@ | |
| 2499 | iStatusTagId, !!zStatusFilter, zStatusFilter |
| 2500 | ); |
| 2501 | if( zStatusFilter ){ |
| 2502 | const int bIsDflt = |
| 2503 | 0==fossil_strcmp(pFstat->aStatus[0].zValue, zStatusFilter); |
| 2504 | const int bIsKnown =bIsDflt |
| 2505 | ? 1 |
| 2506 | : db_int(0, "SELECT 1 FROM forumstatus WHERE value=%Q", |
| 2507 | zStatusFilter); |
| 2508 | db_multi_exec( |
| 2509 | "INSERT INTO trootid\n" |
| 2510 | /* Rules: |
| 2511 | |
| 2512 | (1) Filter on status=$zStatusFilter |
| @@ -2677,7 +2681,12 @@ | |
| 2681 | if( iCnt>0 ){ |
| 2682 | @ </table></div> |
| 2683 | }else{ |
| 2684 | @ <h1>No forum posts found</h1> |
| 2685 | } |
| 2686 | if( bHasStatus ){ |
| 2687 | /* We need a JS-side kludge to avoid passing on the x=N |
| 2688 | ** URL arg when the status selection list is activated. */ |
| 2689 | forum_emit_js(); |
| 2690 | } |
| 2691 | style_finish_page(); |
| 2692 | } |
| 2693 |
+74
-58
| --- src/fossil.page.forumpost.js | ||
| +++ src/fossil.page.forumpost.js | ||
| @@ -94,71 +94,87 @@ | ||
| 94 | 94 | forumPostWrapper.appendChild(widget); |
| 95 | 95 | } |
| 96 | 96 | content.appendChild(rightTapZone); |
| 97 | 97 | rightTapZone.addEventListener('click', widgetEventHandler, false); |
| 98 | 98 | refillTapZone(); |
| 99 | - })/*F.onPageLoad()*/; | |
| 99 | + })/*for-each div.forumTime|div.forumEdit*/; | |
| 100 | 100 | |
| 101 | 101 | if(F.pikchr){ |
| 102 | 102 | F.pikchr.addSrcView(); |
| 103 | 103 | } |
| 104 | 104 | |
| 105 | - /* Attempt to keep stray double-clicks from double-posting. | |
| 106 | - https://fossil-scm.org/forum/info/6bd02466533aa131 */ | |
| 107 | - const formSubmitted = function(event){ | |
| 108 | - const form = event.target; | |
| 109 | - if( form.dataset.submitted ){ | |
| 110 | - event.preventDefault(); | |
| 111 | - return; | |
| 112 | - } | |
| 113 | - form.dataset.submitted = '1'; | |
| 114 | - /** If the user is left waiting "a long time," disable the | |
| 115 | - resubmit protection. If we don't do this and they tap the | |
| 116 | - browser's cancel button while waiting, they'll be stuck with | |
| 117 | - an unsubmittable form. */ | |
| 118 | - setTimeout(()=>{delete form.dataset.submitted}, 7000); | |
| 119 | - return; | |
| 120 | - }; | |
| 121 | - document.querySelectorAll("form").forEach(function(form){ | |
| 122 | - form.addEventListener('submit',formSubmitted); | |
| 123 | - form | |
| 124 | - .querySelectorAll("input.action-close, input.action-reopen") | |
| 125 | - .forEach(function(e){ | |
| 126 | - e.classList.remove('hidden'); | |
| 127 | - F.confirmer(e, { | |
| 128 | - confirmText: (e.classList.contains('action-reopen') | |
| 129 | - ? "Confirm re-open" | |
| 130 | - : "Confirm close"), | |
| 131 | - onconfirm: ()=>form.submit() | |
| 132 | - }); | |
| 133 | - }); | |
| 134 | - form | |
| 135 | - .querySelectorAll("input[type='button'].action-status") | |
| 136 | - .forEach(function(btn){ | |
| 137 | - btn.classList.remove('hidden'); | |
| 138 | - const sel = btn.previousElementSibling; | |
| 139 | - const updateAble = ()=>{ | |
| 140 | - if( sel.dataset.initialValue ){ | |
| 141 | - if( sel.dataset.initialValue===sel.value ){ | |
| 142 | - btn.setAttribute('disabled',''); | |
| 143 | - }else{ | |
| 144 | - btn.removeAttribute('disabled'); | |
| 145 | - } | |
| 146 | - }else{ | |
| 147 | - if(sel.selectedIndex===0){ | |
| 148 | - btn.setAttribute('disabled',''); | |
| 149 | - }else{ | |
| 150 | - btn.removeAttribute('disabled'); | |
| 151 | - } | |
| 152 | - } | |
| 153 | - }; | |
| 154 | - sel.addEventListener('change', updateAble, true); | |
| 155 | - updateAble(); | |
| 156 | - F.confirmer(btn, { | |
| 157 | - confirmText: "Confirm status change", | |
| 158 | - onconfirm: ()=>form.submit() | |
| 159 | - }); | |
| 160 | - }); | |
| 161 | - }); | |
| 105 | + const eStatus = document.querySelector( | |
| 106 | + 'form div.submenu select.submenuctrl[name="status"]' | |
| 107 | + ); | |
| 108 | + if( eStatus ){ | |
| 109 | + /* Main /forum list. Remove the 'x' form element when eStatus | |
| 110 | + ** changes, to avoid propagating x when changing the filter. */ | |
| 111 | + const pForm = eStatus.parentElement?.parentElement; | |
| 112 | + if( pForm ){ | |
| 113 | + eStatus.addEventListener('change', ()=>{ | |
| 114 | + pForm.querySelector('input[type="hidden"][name="x"]')?.remove(); | |
| 115 | + }, true); | |
| 116 | + } | |
| 117 | + }else{ | |
| 118 | + /* One of the single-post edit/view pages. Handle various UI | |
| 119 | + controls and attempt to keep stray double-clicks from | |
| 120 | + double-posting. | |
| 121 | + https://fossil-scm.org/forum/info/6bd02466533aa131 */ | |
| 122 | + const formSubmitted = function(event){ | |
| 123 | + const form = event.target; | |
| 124 | + if( form.dataset.submitted ){ | |
| 125 | + event.preventDefault(); | |
| 126 | + return; | |
| 127 | + } | |
| 128 | + form.dataset.submitted = '1'; | |
| 129 | + /** If the user is left waiting "a long time," disable the | |
| 130 | + resubmit protection. If we don't do this and they tap the | |
| 131 | + browser's cancel button while waiting, they'll be stuck with | |
| 132 | + an unsubmittable form. */ | |
| 133 | + setTimeout(()=>{delete form.dataset.submitted}, 7000); | |
| 134 | + return; | |
| 135 | + }; | |
| 136 | + document.querySelectorAll("form").forEach(function(form){ | |
| 137 | + form.addEventListener('submit', formSubmitted); | |
| 138 | + form | |
| 139 | + .querySelectorAll("input.action-close, input.action-reopen") | |
| 140 | + .forEach(function(e){ | |
| 141 | + e.classList.remove('hidden'); | |
| 142 | + F.confirmer(e, { | |
| 143 | + confirmText: (e.classList.contains('action-reopen') | |
| 144 | + ? "Confirm re-open" | |
| 145 | + : "Confirm close"), | |
| 146 | + onconfirm: ()=>form.submit() | |
| 147 | + }); | |
| 148 | + }); | |
| 149 | + form | |
| 150 | + .querySelectorAll("input[type='button'].action-status") | |
| 151 | + .forEach(function(btn){ | |
| 152 | + btn.classList.remove('hidden'); | |
| 153 | + const sel = btn.previousElementSibling; | |
| 154 | + const updateAble = ()=>{ | |
| 155 | + if( sel.dataset.initialValue ){ | |
| 156 | + if( sel.dataset.initialValue===sel.value ){ | |
| 157 | + btn.setAttribute('disabled',''); | |
| 158 | + }else{ | |
| 159 | + btn.removeAttribute('disabled'); | |
| 160 | + } | |
| 161 | + }else{ | |
| 162 | + if(sel.selectedIndex===0){ | |
| 163 | + btn.setAttribute('disabled',''); | |
| 164 | + }else{ | |
| 165 | + btn.removeAttribute('disabled'); | |
| 166 | + } | |
| 167 | + } | |
| 168 | + }; | |
| 169 | + sel.addEventListener('change', updateAble, true); | |
| 170 | + updateAble(); | |
| 171 | + F.confirmer(btn, { | |
| 172 | + confirmText: "Confirm status change", | |
| 173 | + onconfirm: ()=>form.submit() | |
| 174 | + }); | |
| 175 | + }); | |
| 176 | + }); | |
| 177 | + } | |
| 162 | 178 | |
| 163 | 179 | })/*F.onPageLoad callback*/; |
| 164 | 180 | })(window.fossil); |
| 165 | 181 |
| --- src/fossil.page.forumpost.js | |
| +++ src/fossil.page.forumpost.js | |
| @@ -94,71 +94,87 @@ | |
| 94 | forumPostWrapper.appendChild(widget); |
| 95 | } |
| 96 | content.appendChild(rightTapZone); |
| 97 | rightTapZone.addEventListener('click', widgetEventHandler, false); |
| 98 | refillTapZone(); |
| 99 | })/*F.onPageLoad()*/; |
| 100 | |
| 101 | if(F.pikchr){ |
| 102 | F.pikchr.addSrcView(); |
| 103 | } |
| 104 | |
| 105 | /* Attempt to keep stray double-clicks from double-posting. |
| 106 | https://fossil-scm.org/forum/info/6bd02466533aa131 */ |
| 107 | const formSubmitted = function(event){ |
| 108 | const form = event.target; |
| 109 | if( form.dataset.submitted ){ |
| 110 | event.preventDefault(); |
| 111 | return; |
| 112 | } |
| 113 | form.dataset.submitted = '1'; |
| 114 | /** If the user is left waiting "a long time," disable the |
| 115 | resubmit protection. If we don't do this and they tap the |
| 116 | browser's cancel button while waiting, they'll be stuck with |
| 117 | an unsubmittable form. */ |
| 118 | setTimeout(()=>{delete form.dataset.submitted}, 7000); |
| 119 | return; |
| 120 | }; |
| 121 | document.querySelectorAll("form").forEach(function(form){ |
| 122 | form.addEventListener('submit',formSubmitted); |
| 123 | form |
| 124 | .querySelectorAll("input.action-close, input.action-reopen") |
| 125 | .forEach(function(e){ |
| 126 | e.classList.remove('hidden'); |
| 127 | F.confirmer(e, { |
| 128 | confirmText: (e.classList.contains('action-reopen') |
| 129 | ? "Confirm re-open" |
| 130 | : "Confirm close"), |
| 131 | onconfirm: ()=>form.submit() |
| 132 | }); |
| 133 | }); |
| 134 | form |
| 135 | .querySelectorAll("input[type='button'].action-status") |
| 136 | .forEach(function(btn){ |
| 137 | btn.classList.remove('hidden'); |
| 138 | const sel = btn.previousElementSibling; |
| 139 | const updateAble = ()=>{ |
| 140 | if( sel.dataset.initialValue ){ |
| 141 | if( sel.dataset.initialValue===sel.value ){ |
| 142 | btn.setAttribute('disabled',''); |
| 143 | }else{ |
| 144 | btn.removeAttribute('disabled'); |
| 145 | } |
| 146 | }else{ |
| 147 | if(sel.selectedIndex===0){ |
| 148 | btn.setAttribute('disabled',''); |
| 149 | }else{ |
| 150 | btn.removeAttribute('disabled'); |
| 151 | } |
| 152 | } |
| 153 | }; |
| 154 | sel.addEventListener('change', updateAble, true); |
| 155 | updateAble(); |
| 156 | F.confirmer(btn, { |
| 157 | confirmText: "Confirm status change", |
| 158 | onconfirm: ()=>form.submit() |
| 159 | }); |
| 160 | }); |
| 161 | }); |
| 162 | |
| 163 | })/*F.onPageLoad callback*/; |
| 164 | })(window.fossil); |
| 165 |
| --- src/fossil.page.forumpost.js | |
| +++ src/fossil.page.forumpost.js | |
| @@ -94,71 +94,87 @@ | |
| 94 | forumPostWrapper.appendChild(widget); |
| 95 | } |
| 96 | content.appendChild(rightTapZone); |
| 97 | rightTapZone.addEventListener('click', widgetEventHandler, false); |
| 98 | refillTapZone(); |
| 99 | })/*for-each div.forumTime|div.forumEdit*/; |
| 100 | |
| 101 | if(F.pikchr){ |
| 102 | F.pikchr.addSrcView(); |
| 103 | } |
| 104 | |
| 105 | const eStatus = document.querySelector( |
| 106 | 'form div.submenu select.submenuctrl[name="status"]' |
| 107 | ); |
| 108 | if( eStatus ){ |
| 109 | /* Main /forum list. Remove the 'x' form element when eStatus |
| 110 | ** changes, to avoid propagating x when changing the filter. */ |
| 111 | const pForm = eStatus.parentElement?.parentElement; |
| 112 | if( pForm ){ |
| 113 | eStatus.addEventListener('change', ()=>{ |
| 114 | pForm.querySelector('input[type="hidden"][name="x"]')?.remove(); |
| 115 | }, true); |
| 116 | } |
| 117 | }else{ |
| 118 | /* One of the single-post edit/view pages. Handle various UI |
| 119 | controls and attempt to keep stray double-clicks from |
| 120 | double-posting. |
| 121 | https://fossil-scm.org/forum/info/6bd02466533aa131 */ |
| 122 | const formSubmitted = function(event){ |
| 123 | const form = event.target; |
| 124 | if( form.dataset.submitted ){ |
| 125 | event.preventDefault(); |
| 126 | return; |
| 127 | } |
| 128 | form.dataset.submitted = '1'; |
| 129 | /** If the user is left waiting "a long time," disable the |
| 130 | resubmit protection. If we don't do this and they tap the |
| 131 | browser's cancel button while waiting, they'll be stuck with |
| 132 | an unsubmittable form. */ |
| 133 | setTimeout(()=>{delete form.dataset.submitted}, 7000); |
| 134 | return; |
| 135 | }; |
| 136 | document.querySelectorAll("form").forEach(function(form){ |
| 137 | form.addEventListener('submit', formSubmitted); |
| 138 | form |
| 139 | .querySelectorAll("input.action-close, input.action-reopen") |
| 140 | .forEach(function(e){ |
| 141 | e.classList.remove('hidden'); |
| 142 | F.confirmer(e, { |
| 143 | confirmText: (e.classList.contains('action-reopen') |
| 144 | ? "Confirm re-open" |
| 145 | : "Confirm close"), |
| 146 | onconfirm: ()=>form.submit() |
| 147 | }); |
| 148 | }); |
| 149 | form |
| 150 | .querySelectorAll("input[type='button'].action-status") |
| 151 | .forEach(function(btn){ |
| 152 | btn.classList.remove('hidden'); |
| 153 | const sel = btn.previousElementSibling; |
| 154 | const updateAble = ()=>{ |
| 155 | if( sel.dataset.initialValue ){ |
| 156 | if( sel.dataset.initialValue===sel.value ){ |
| 157 | btn.setAttribute('disabled',''); |
| 158 | }else{ |
| 159 | btn.removeAttribute('disabled'); |
| 160 | } |
| 161 | }else{ |
| 162 | if(sel.selectedIndex===0){ |
| 163 | btn.setAttribute('disabled',''); |
| 164 | }else{ |
| 165 | btn.removeAttribute('disabled'); |
| 166 | } |
| 167 | } |
| 168 | }; |
| 169 | sel.addEventListener('change', updateAble, true); |
| 170 | updateAble(); |
| 171 | F.confirmer(btn, { |
| 172 | confirmText: "Confirm status change", |
| 173 | onconfirm: ()=>form.submit() |
| 174 | }); |
| 175 | }); |
| 176 | }); |
| 177 | } |
| 178 | |
| 179 | })/*F.onPageLoad callback*/; |
| 180 | })(window.fossil); |
| 181 |