Skip to content

Commit 7e5f7b9

Browse files
author
epriestley
committedMay 25, 2022
Update "Files" attachment table to show more attachment details and support detachment
Summary: Ref T13682. Make the "Attached" list in Files a bit more detailed, and add a "Detach" button. Test Plan: Tried to detach unrelated, referenced, and attached files. Saw attached files detach. Maniphest Tasks: T13682 Differential Revision: https://secure.phabricator.com/D21840
1 parent 5aa159a commit 7e5f7b9

7 files changed

+232
-18
lines changed
 

‎src/__phutil_library_map__.php

+2
Original file line numberDiff line numberDiff line change
@@ -3464,6 +3464,7 @@
34643464
'PhabricatorFileDataController' => 'applications/files/controller/PhabricatorFileDataController.php',
34653465
'PhabricatorFileDeleteController' => 'applications/files/controller/PhabricatorFileDeleteController.php',
34663466
'PhabricatorFileDeleteTransaction' => 'applications/files/xaction/PhabricatorFileDeleteTransaction.php',
3467+
'PhabricatorFileDetachController' => 'applications/files/controller/PhabricatorFileDetachController.php',
34673468
'PhabricatorFileDocumentController' => 'applications/files/controller/PhabricatorFileDocumentController.php',
34683469
'PhabricatorFileDocumentRenderingEngine' => 'applications/files/document/render/PhabricatorFileDocumentRenderingEngine.php',
34693470
'PhabricatorFileDropUploadController' => 'applications/files/controller/PhabricatorFileDropUploadController.php',
@@ -9919,6 +9920,7 @@
99199920
'PhabricatorFileDataController' => 'PhabricatorFileController',
99209921
'PhabricatorFileDeleteController' => 'PhabricatorFileController',
99219922
'PhabricatorFileDeleteTransaction' => 'PhabricatorFileTransactionType',
9923+
'PhabricatorFileDetachController' => 'PhabricatorFileController',
99229924
'PhabricatorFileDocumentController' => 'PhabricatorFileController',
99239925
'PhabricatorFileDocumentRenderingEngine' => 'PhabricatorDocumentRenderingEngine',
99249926
'PhabricatorFileDropUploadController' => 'PhabricatorFileController',

‎src/applications/files/application/PhabricatorFilesApplication.php

+2
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ public function getRoutes() {
9696
'document/(?P<engineKey>[^/]+)/(?P<phid>[^/]+)/'
9797
=> 'PhabricatorFileDocumentController',
9898
'ui/' => array(
99+
'detach/(?P<objectPHID>[^/]+)/(?P<filePHID>[^/]+)/'
100+
=> 'PhabricatorFileDetachController',
99101
'curtain/' => array(
100102
'list/(?P<phid>[^/]+)/'
101103
=> 'PhabricatorFileUICurtainListController',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
3+
final class PhabricatorFileDetachController
4+
extends PhabricatorFileController {
5+
6+
public function handleRequest(AphrontRequest $request) {
7+
$viewer = $request->getViewer();
8+
9+
$object_phid = $request->getURIData('objectPHID');
10+
$file_phid = $request->getURIData('filePHID');
11+
12+
$object = id(new PhabricatorObjectQuery())
13+
->setViewer($viewer)
14+
->withPHIDs(array($object_phid))
15+
->executeOne();
16+
if (!$object) {
17+
return new Aphront404Response();
18+
}
19+
20+
$handles = $viewer->loadHandles(
21+
array(
22+
$object_phid,
23+
$file_phid,
24+
));
25+
26+
$object_handle = $handles[$object_phid];
27+
$file_handle = $handles[$file_phid];
28+
$cancel_uri = $file_handle->getURI();
29+
30+
$dialog = $this->newDialog()
31+
->setViewer($viewer)
32+
->setTitle(pht('Detach File'))
33+
->addCancelButton($cancel_uri, pht('Close'));
34+
35+
$file_link = phutil_tag('strong', array(), $file_handle->renderLink());
36+
$object_link = phutil_tag('strong', array(), $object_handle->renderLink());
37+
38+
$attachment = id(new PhabricatorFileAttachmentQuery())
39+
->setViewer($viewer)
40+
->withObjectPHIDs(array($object->getPHID()))
41+
->withFilePHIDs(array($file_phid))
42+
->needFiles(true)
43+
->withVisibleFiles(true)
44+
->executeOne();
45+
if (!$attachment) {
46+
$body = pht(
47+
'The file %s is not attached to the object %s.',
48+
$file_link,
49+
$object_link);
50+
51+
return $dialog->appendParagraph($body);
52+
}
53+
54+
$mode_reference = PhabricatorFileAttachment::MODE_REFERENCE;
55+
if ($attachment->getAttachmentMode() === $mode_reference) {
56+
$body = pht(
57+
'The file %s is referenced by the object %s, but not attached to '.
58+
'it, so it can not be detached.',
59+
$file_link,
60+
$object_link);
61+
62+
return $dialog->appendParagraph($body);
63+
}
64+
65+
if (!$attachment->canDetach()) {
66+
$body = pht(
67+
'The file %s can not be detached from the object %s.',
68+
$file_link,
69+
$object_link);
70+
71+
return $dialog->appendParagraph($body);
72+
}
73+
74+
if (!$request->isDialogFormPost()) {
75+
$dialog->appendParagraph(
76+
pht(
77+
'Detach the file %s from the object %s?',
78+
$file_link,
79+
$object_link));
80+
81+
$dialog->addSubmitButton(pht('Detach File'));
82+
83+
return $dialog;
84+
}
85+
86+
if (!($object instanceof PhabricatorApplicationTransactionInterface)) {
87+
$dialog->appendParagraph(
88+
pht(
89+
'This object (of class "%s") does not implement the required '.
90+
'interface ("%s"), so files can not be manually detached from it.',
91+
get_class($object),
92+
'PhabricatorApplicationTransactionInterface'));
93+
94+
return $dialog;
95+
}
96+
97+
$editor = $object->getApplicationTransactionEditor()
98+
->setActor($viewer)
99+
->setContentSourceFromRequest($request)
100+
->setContinueOnNoEffect(true)
101+
->setContinueOnMissingFields(true);
102+
103+
$template = $object->getApplicationTransactionTemplate();
104+
105+
$xactions = array();
106+
107+
$xactions[] = id(clone $template)
108+
->setTransactionType(PhabricatorTransactions::TYPE_FILE)
109+
->setNewValue(
110+
array(
111+
$file_phid => PhabricatorFileAttachment::MODE_DETACH,
112+
));
113+
114+
$editor->applyTransactions($object, $xactions);
115+
116+
return $this->newRedirect()
117+
->setURI($cancel_uri);
118+
}
119+
120+
}

‎src/applications/files/controller/PhabricatorFileUICurtainAttachController.php

+1-4
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ public function handleRequest(AphrontRequest $request) {
2828
return new Aphront404Response();
2929
}
3030

31-
$file = $attachment->getFile();
32-
$file_phid = $file->getPHID();
33-
3431
$handles = $viewer->loadHandles(
3532
array(
3633
$object_phid,
@@ -44,7 +41,7 @@ public function handleRequest(AphrontRequest $request) {
4441
$dialog = $this->newDialog()
4542
->setViewer($viewer)
4643
->setTitle(pht('Attach File'))
47-
->addCancelButton($object_handle->getURI(), pht('Close'));
44+
->addCancelButton($cancel_uri, pht('Close'));
4845

4946
$file_link = phutil_tag('strong', array(), $file_handle->renderLink());
5047
$object_link = phutil_tag('strong', array(), $object_handle->renderLink());

‎src/applications/files/controller/PhabricatorFileViewController.php

+84-14
Original file line numberDiff line numberDiff line change
@@ -320,20 +320,13 @@ private function buildPropertyViews(
320320
$finfo->addProperty(pht('Default Alt Text'), $default_alt);
321321
}
322322

323-
$phids = $file->getObjectPHIDs();
324-
if ($phids) {
325-
$attached = new PHUIPropertyListView();
326-
327-
$tab_group->addTab(
328-
id(new PHUITabView())
329-
->setName(pht('Attached'))
330-
->setKey('attached')
331-
->appendChild($attached));
332-
333-
$attached->addProperty(
334-
pht('Attached To'),
335-
$viewer->renderHandleList($phids));
336-
}
323+
$attachments_table = $this->newAttachmentsView($file);
324+
325+
$tab_group->addTab(
326+
id(new PHUITabView())
327+
->setName(pht('Attached'))
328+
->setKey('attached')
329+
->appendChild($attachments_table));
337330

338331
$engine = $this->loadStorageEngine($file);
339332
if ($engine) {
@@ -420,4 +413,81 @@ private function newFileContent(PhabricatorFile $file) {
420413
return $engine->newDocumentView($ref);
421414
}
422415

416+
private function newAttachmentsView(PhabricatorFile $file) {
417+
$viewer = $this->getViewer();
418+
419+
$attachments = id(new PhabricatorFileAttachmentQuery())
420+
->setViewer($viewer)
421+
->withFilePHIDs(array($file->getPHID()))
422+
->execute();
423+
424+
$handles = $viewer->loadHandles(mpull($attachments, 'getObjectPHID'));
425+
426+
$rows = array();
427+
428+
$mode_map = PhabricatorFileAttachment::getModeNameMap();
429+
$mode_attach = PhabricatorFileAttachment::MODE_ATTACH;
430+
431+
foreach ($attachments as $attachment) {
432+
$object_phid = $attachment->getObjectPHID();
433+
$handle = $handles[$object_phid];
434+
435+
$attachment_mode = $attachment->getAttachmentMode();
436+
437+
$mode_name = idx($mode_map, $attachment_mode);
438+
if ($mode_name === null) {
439+
$mode_name = pht('Unknown ("%s")', $attachment_mode);
440+
}
441+
442+
$detach_uri = urisprintf(
443+
'/file/ui/detach/%s/%s/',
444+
$object_phid,
445+
$file->getPHID());
446+
447+
$is_disabled = !$attachment->canDetach();
448+
449+
$detach_button = id(new PHUIButtonView())
450+
->setHref($detach_uri)
451+
->setTag('a')
452+
->setWorkflow(true)
453+
->setDisabled($is_disabled)
454+
->setColor(PHUIButtonView::GREY)
455+
->setSize(PHUIButtonView::SMALL)
456+
->setText(pht('Detach File'));
457+
458+
javelin_tag(
459+
'a',
460+
array(
461+
'href' => $detach_uri,
462+
'sigil' => 'workflow',
463+
'disabled' => true,
464+
'class' => 'small button button-grey disabled',
465+
),
466+
pht('Detach File'));
467+
468+
$rows[] = array(
469+
$handle->renderLink(),
470+
$mode_name,
471+
$detach_button,
472+
);
473+
}
474+
475+
$table = id(new AphrontTableView($rows))
476+
->setHeaders(
477+
array(
478+
pht('Attached To'),
479+
pht('Mode'),
480+
null,
481+
))
482+
->setColumnClasses(
483+
array(
484+
'pri wide',
485+
null,
486+
null,
487+
));
488+
489+
return $table;
490+
}
491+
492+
423493
}

‎src/applications/files/storage/PhabricatorFileAttachment.php

+16
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ public static function getModeList() {
4646
);
4747
}
4848

49+
public static function getModeNameMap() {
50+
return array(
51+
self::MODE_ATTACH => pht('Attached'),
52+
self::MODE_REFERENCE => pht('Referenced'),
53+
);
54+
}
55+
4956
public function isPolicyAttachment() {
5057
switch ($this->getAttachmentMode()) {
5158
case self::MODE_ATTACH:
@@ -73,6 +80,15 @@ public function getFile() {
7380
return $this->assertAttached($this->file);
7481
}
7582

83+
public function canDetach() {
84+
switch ($this->getAttachmentMode()) {
85+
case self::MODE_ATTACH:
86+
return true;
87+
}
88+
89+
return false;
90+
}
91+
7692

7793
/* -( PhabricatorPolicyInterface )----------------------------------------- */
7894

‎src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php

+7
Original file line numberDiff line numberDiff line change
@@ -1803,6 +1803,13 @@ protected function getTranslations() {
18031803
),
18041804
),
18051805

1806+
'%s removed %s attached file(s): %s.' => array(
1807+
array(
1808+
'%s removed an attached file: %3$s.',
1809+
'%s removed attached files: %3$s.',
1810+
),
1811+
),
1812+
18061813
);
18071814
}
18081815

0 commit comments

Comments
 (0)
Failed to load comments.