@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Simplify form/lightbox stuff

Summary:
I broke this a bit a few revisions ago by making `phabricator_render_csrf_magic()` return array instead of string without actually grepping for callsites.

Instead of building a form in JS, build it in PHP and just change its action in JS. This is simpler and doesn't require us to expose CSRF magic in more places.

This change triggered a rather subtle bug: if we rendered a normal form on the page (and thus installed the "disable forms when submitted" behavior), the download button would incorrectly disable after being clicked. This doesn't currently happen in Files because we don't put a form on the same page as the download button.

Instead, make the "disable" behavior check if the form is downloading stuff, and not disable stuff if it is. Then mark the lightbox and Files form as both download forms.

Test Plan: Used lightbox; used Files. Verified download buttons download the right stuff and behave correctly. Grepped for other download forms, but all the other places where we write "download" don't appear to actually be download links.

Reviewers: vrana, btrahan

Reviewed By: vrana

CC: aran

Maniphest Tasks: T2432

Differential Revision: https://secure.phabricator.com/D4685

+69 -45
+1
src/applications/files/controller/PhabricatorFileInfoController.php
··· 72 72 id(new PhabricatorActionView()) 73 73 ->setUser($user) 74 74 ->setRenderAsForm(true) 75 + ->setDownload(true) 75 76 ->setName(pht('Download File')) 76 77 ->setIcon('download') 77 78 ->setHref($file->getViewURI()));
+2 -2
src/view/AphrontTagView.php
··· 56 56 return $this; 57 57 } 58 58 59 - final public function getSigil() { 60 - return $this->sigil; 59 + final public function getSigils() { 60 + return $this->sigils; 61 61 } 62 62 63 63 public function addClass($class) {
-2
src/view/form/AphrontFormView.php
··· 58 58 } 59 59 require_celerity_resource('aphront-form-view-css'); 60 60 61 - Javelin::initBehavior('aphront-form-disable-on-submit'); 62 - 63 61 $layout = new AphrontFormLayoutView(); 64 62 65 63 if (!$this->flexible) {
+19 -1
src/view/layout/PhabricatorActionView.php
··· 8 8 private $disabled; 9 9 private $workflow; 10 10 private $renderAsForm; 11 + private $download; 12 + 13 + public function setDownload($download) { 14 + $this->download = $download; 15 + return $this; 16 + } 17 + 18 + public function getDownload() { 19 + return $this->download; 20 + } 11 21 12 22 public function setHref($href) { 13 23 $this->href = $href; ··· 73 83 ), 74 84 $this->name); 75 85 86 + $sigils = array(); 87 + if ($this->workflow) { 88 + $sigils[] = 'workflow'; 89 + } 90 + if ($this->download) { 91 + $sigils[] = 'download'; 92 + } 93 + 76 94 $item = phabricator_render_form( 77 95 $this->user, 78 96 array( 79 97 'action' => $this->href, 80 98 'method' => 'POST', 81 - 'sigil' => $this->workflow ? 'workflow' : null, 99 + 'sigil' => implode(' ', $sigils), 82 100 ), 83 101 $item); 84 102 } else {
+34 -15
src/view/page/PhabricatorStandardPageView.php
··· 108 108 109 109 Javelin::initBehavior('workflow', array()); 110 110 111 - $current_token = null; 112 111 $request = $this->getRequest(); 112 + $user = null; 113 113 if ($request) { 114 114 $user = $request->getUser(); 115 - if ($user) { 116 - $current_token = $user->getCSRFToken(); 117 - $download_form = phabricator_render_form_magic($user); 118 - $default_img_uri = 119 - PhabricatorEnv::getCDNURI( 120 - '/rsrc/image/icon/fatcow/document_black.png' 121 - ); 115 + } 116 + 117 + if ($user) { 118 + $default_img_uri = 119 + PhabricatorEnv::getCDNURI( 120 + '/rsrc/image/icon/fatcow/document_black.png' 121 + ); 122 + $download_form = phabricator_render_form( 123 + $user, 124 + array( 125 + 'action' => '#', 126 + 'method' => 'POST', 127 + 'class' => 'lightbox-download-form', 128 + 'sigil' => 'download', 129 + ), 130 + phutil_tag( 131 + 'button', 132 + array(), 133 + pht('Download'))); 122 134 123 - Javelin::initBehavior( 124 - 'lightbox-attachments', 125 - array( 126 - 'defaultImageUri' => $default_img_uri, 127 - 'downloadForm' => $download_form, 128 - )); 129 - } 135 + Javelin::initBehavior( 136 + 'lightbox-attachments', 137 + array( 138 + 'defaultImageUri' => $default_img_uri, 139 + 'downloadForm' => $download_form, 140 + )); 130 141 } 131 142 143 + Javelin::initBehavior('aphront-form-disable-on-submit'); 132 144 Javelin::initBehavior('toggle-class', array()); 133 145 Javelin::initBehavior('konami', array()); 146 + 147 + $current_token = null; 148 + if ($user) { 149 + $current_token = $user->getCSRFToken(); 150 + } 151 + 134 152 Javelin::initBehavior( 135 153 'refresh-csrf', 136 154 array( ··· 138 156 'header' => AphrontRequest::getCSRFHeaderName(), 139 157 'current' => $current_token, 140 158 )); 159 + 141 160 Javelin::initBehavior('device'); 142 161 143 162 if ($console) {
+6
webroot/rsrc/js/application/core/behavior-form.js
··· 23 23 24 24 root = e.getNode('tag:form'); 25 25 26 + // If the form is a "download" form, don't disable it on submit because 27 + // we won't transition off the page. 28 + if (JX.Stratcom.hasSigil(root, 'download')) { 29 + return; 30 + } 31 + 26 32 // Open the form to a new tab in Firefox explicitly (automatic in Chrome). 27 33 // We render some buttons as links so users may be confused that the links 28 34 // don't open to new tabs with Ctrl+click as normal links.
+7 -25
webroot/rsrc/js/application/core/behavior-lightbox-attachments.js
··· 15 15 var next = null; 16 16 var x_margin = 40; 17 17 var y_margin = 100; 18 - var downloadForm = JX.$H(config.downloadForm); 18 + var downloadForm = JX.$H(config.downloadForm).getFragment().firstChild; 19 19 20 20 function loadLightBox(e) { 21 21 if (!e.isNormalClick()) { ··· 133 133 }, 134 134 'Image '+current+' of '+total+'.'+extra_status 135 135 ); 136 - var form = JX.$N('form', 137 - { 138 - action : target_data.dUri, 139 - method : 'POST', 140 - className : 'lightbox-download-form' 141 - }, 142 - downloadForm 143 - ); 144 - JX.DOM.appendContent(form, JX.$N('button', {}, 'Download')); 145 - JX.DOM.listen(form, 146 - 'click', 147 - null, 148 - function (e) { 149 - e.prevent(); 150 - form.submit(); 151 - // Firefox and probably IE need this trick to work. 152 - // Removing a form from the DOM while its submitting is 153 - // tricky business. 154 - setTimeout(JX.bind(null, closeLightBox, e), 0); 155 - } 156 - ); 136 + 157 137 var downloadSpan = JX.$N('span', 158 138 { 159 139 className : 'lightbox-download' 160 - }, 161 - form 162 - ); 140 + }); 163 141 var statusHTML = JX.$N('div', 164 142 { 165 143 className : 'lightbox-status' ··· 169 147 JX.DOM.appendContent(lightbox, statusHTML); 170 148 JX.DOM.alterClass(document.body, 'lightbox-attached', true); 171 149 JX.Mask.show('jx-dark-mask'); 150 + 151 + downloadForm.action = target_data.dUri; 152 + downloadSpan.appendChild(downloadForm); 153 + 172 154 document.body.appendChild(lightbox); 173 155 174 156 JX.Busy.start();