Skip to content

Commit 08bcb8c

Browse files
author
epriestley
committed
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
1 parent fc4cb57 commit 08bcb8c

File tree

7 files changed

+70
-46
lines changed

7 files changed

+70
-46
lines changed

src/applications/files/controller/PhabricatorFileInfoController.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ private function buildActionView(PhabricatorFile $file) {
7272
id(new PhabricatorActionView())
7373
->setUser($user)
7474
->setRenderAsForm(true)
75+
->setDownload(true)
7576
->setName(pht('Download File'))
7677
->setIcon('download')
7778
->setHref($file->getViewURI()));

src/view/AphrontTagView.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ final public function addSigil($sigil) {
5656
return $this;
5757
}
5858

59-
final public function getSigil() {
60-
return $this->sigil;
59+
final public function getSigils() {
60+
return $this->sigils;
6161
}
6262

6363
public function addClass($class) {

src/view/form/AphrontFormView.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,6 @@ public function render() {
5858
}
5959
require_celerity_resource('aphront-form-view-css');
6060

61-
Javelin::initBehavior('aphront-form-disable-on-submit');
62-
6361
$layout = new AphrontFormLayoutView();
6462

6563
if (!$this->flexible) {

src/view/layout/PhabricatorActionView.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ final class PhabricatorActionView extends AphrontView {
88
private $disabled;
99
private $workflow;
1010
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+
}
1121

1222
public function setHref($href) {
1323
$this->href = $href;
@@ -73,12 +83,20 @@ public function render() {
7383
),
7484
$this->name);
7585

86+
$sigils = array();
87+
if ($this->workflow) {
88+
$sigils[] = 'workflow';
89+
}
90+
if ($this->download) {
91+
$sigils[] = 'download';
92+
}
93+
7694
$item = phabricator_render_form(
7795
$this->user,
7896
array(
7997
'action' => $this->href,
8098
'method' => 'POST',
81-
'sigil' => $this->workflow ? 'workflow' : null,
99+
'sigil' => implode(' ', $sigils),
82100
),
83101
$item);
84102
} else {

src/view/page/PhabricatorStandardPageView.php

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -108,36 +108,55 @@ protected function willRenderPage() {
108108

109109
Javelin::initBehavior('workflow', array());
110110

111-
$current_token = null;
112111
$request = $this->getRequest();
112+
$user = null;
113113
if ($request) {
114114
$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-
);
122-
123-
Javelin::initBehavior(
124-
'lightbox-attachments',
125-
array(
126-
'defaultImageUri' => $default_img_uri,
127-
'downloadForm' => $download_form,
128-
));
129-
}
130115
}
131116

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')));
134+
135+
Javelin::initBehavior(
136+
'lightbox-attachments',
137+
array(
138+
'defaultImageUri' => $default_img_uri,
139+
'downloadForm' => $download_form,
140+
));
141+
}
142+
143+
Javelin::initBehavior('aphront-form-disable-on-submit');
132144
Javelin::initBehavior('toggle-class', array());
133145
Javelin::initBehavior('konami', array());
146+
147+
$current_token = null;
148+
if ($user) {
149+
$current_token = $user->getCSRFToken();
150+
}
151+
134152
Javelin::initBehavior(
135153
'refresh-csrf',
136154
array(
137155
'tokenName' => AphrontRequest::getCSRFTokenName(),
138156
'header' => AphrontRequest::getCSRFHeaderName(),
139157
'current' => $current_token,
140158
));
159+
141160
Javelin::initBehavior('device');
142161

143162
if ($console) {

webroot/rsrc/js/application/core/behavior-form.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ JX.behavior('aphront-form-disable-on-submit', function(config) {
2323

2424
root = e.getNode('tag:form');
2525

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+
2632
// Open the form to a new tab in Firefox explicitly (automatic in Chrome).
2733
// We render some buttons as links so users may be confused that the links
2834
// don't open to new tabs with Ctrl+click as normal links.

webroot/rsrc/js/application/core/behavior-lightbox-attachments.js

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ JX.behavior('lightbox-attachments', function (config) {
1515
var next = null;
1616
var x_margin = 40;
1717
var y_margin = 100;
18-
var downloadForm = JX.$H(config.downloadForm);
18+
var downloadForm = JX.$H(config.downloadForm).getFragment().firstChild;
1919

2020
function loadLightBox(e) {
2121
if (!e.isNormalClick()) {
@@ -133,33 +133,11 @@ JX.behavior('lightbox-attachments', function (config) {
133133
},
134134
'Image '+current+' of '+total+'.'+extra_status
135135
);
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+
157137
var downloadSpan = JX.$N('span',
158138
{
159139
className : 'lightbox-download'
160-
},
161-
form
162-
);
140+
});
163141
var statusHTML = JX.$N('div',
164142
{
165143
className : 'lightbox-status'
@@ -169,6 +147,10 @@ JX.behavior('lightbox-attachments', function (config) {
169147
JX.DOM.appendContent(lightbox, statusHTML);
170148
JX.DOM.alterClass(document.body, 'lightbox-attached', true);
171149
JX.Mask.show('jx-dark-mask');
150+
151+
downloadForm.action = target_data.dUri;
152+
downloadSpan.appendChild(downloadForm);
153+
172154
document.body.appendChild(lightbox);
173155

174156
JX.Busy.start();

0 commit comments

Comments
 (0)