mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-28 08:20:57 +01:00
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
This commit is contained in:
parent
5aa159a830
commit
7e5f7b9640
7 changed files with 231 additions and 17 deletions
|
@ -3464,6 +3464,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFileDataController' => 'applications/files/controller/PhabricatorFileDataController.php',
|
||||
'PhabricatorFileDeleteController' => 'applications/files/controller/PhabricatorFileDeleteController.php',
|
||||
'PhabricatorFileDeleteTransaction' => 'applications/files/xaction/PhabricatorFileDeleteTransaction.php',
|
||||
'PhabricatorFileDetachController' => 'applications/files/controller/PhabricatorFileDetachController.php',
|
||||
'PhabricatorFileDocumentController' => 'applications/files/controller/PhabricatorFileDocumentController.php',
|
||||
'PhabricatorFileDocumentRenderingEngine' => 'applications/files/document/render/PhabricatorFileDocumentRenderingEngine.php',
|
||||
'PhabricatorFileDropUploadController' => 'applications/files/controller/PhabricatorFileDropUploadController.php',
|
||||
|
@ -9919,6 +9920,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFileDataController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileDeleteController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileDeleteTransaction' => 'PhabricatorFileTransactionType',
|
||||
'PhabricatorFileDetachController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileDocumentController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileDocumentRenderingEngine' => 'PhabricatorDocumentRenderingEngine',
|
||||
'PhabricatorFileDropUploadController' => 'PhabricatorFileController',
|
||||
|
|
|
@ -96,6 +96,8 @@ final class PhabricatorFilesApplication extends PhabricatorApplication {
|
|||
'document/(?P<engineKey>[^/]+)/(?P<phid>[^/]+)/'
|
||||
=> 'PhabricatorFileDocumentController',
|
||||
'ui/' => array(
|
||||
'detach/(?P<objectPHID>[^/]+)/(?P<filePHID>[^/]+)/'
|
||||
=> 'PhabricatorFileDetachController',
|
||||
'curtain/' => array(
|
||||
'list/(?P<phid>[^/]+)/'
|
||||
=> 'PhabricatorFileUICurtainListController',
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorFileDetachController
|
||||
extends PhabricatorFileController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
$object_phid = $request->getURIData('objectPHID');
|
||||
$file_phid = $request->getURIData('filePHID');
|
||||
|
||||
$object = id(new PhabricatorObjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($object_phid))
|
||||
->executeOne();
|
||||
if (!$object) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$handles = $viewer->loadHandles(
|
||||
array(
|
||||
$object_phid,
|
||||
$file_phid,
|
||||
));
|
||||
|
||||
$object_handle = $handles[$object_phid];
|
||||
$file_handle = $handles[$file_phid];
|
||||
$cancel_uri = $file_handle->getURI();
|
||||
|
||||
$dialog = $this->newDialog()
|
||||
->setViewer($viewer)
|
||||
->setTitle(pht('Detach File'))
|
||||
->addCancelButton($cancel_uri, pht('Close'));
|
||||
|
||||
$file_link = phutil_tag('strong', array(), $file_handle->renderLink());
|
||||
$object_link = phutil_tag('strong', array(), $object_handle->renderLink());
|
||||
|
||||
$attachment = id(new PhabricatorFileAttachmentQuery())
|
||||
->setViewer($viewer)
|
||||
->withObjectPHIDs(array($object->getPHID()))
|
||||
->withFilePHIDs(array($file_phid))
|
||||
->needFiles(true)
|
||||
->withVisibleFiles(true)
|
||||
->executeOne();
|
||||
if (!$attachment) {
|
||||
$body = pht(
|
||||
'The file %s is not attached to the object %s.',
|
||||
$file_link,
|
||||
$object_link);
|
||||
|
||||
return $dialog->appendParagraph($body);
|
||||
}
|
||||
|
||||
$mode_reference = PhabricatorFileAttachment::MODE_REFERENCE;
|
||||
if ($attachment->getAttachmentMode() === $mode_reference) {
|
||||
$body = pht(
|
||||
'The file %s is referenced by the object %s, but not attached to '.
|
||||
'it, so it can not be detached.',
|
||||
$file_link,
|
||||
$object_link);
|
||||
|
||||
return $dialog->appendParagraph($body);
|
||||
}
|
||||
|
||||
if (!$attachment->canDetach()) {
|
||||
$body = pht(
|
||||
'The file %s can not be detached from the object %s.',
|
||||
$file_link,
|
||||
$object_link);
|
||||
|
||||
return $dialog->appendParagraph($body);
|
||||
}
|
||||
|
||||
if (!$request->isDialogFormPost()) {
|
||||
$dialog->appendParagraph(
|
||||
pht(
|
||||
'Detach the file %s from the object %s?',
|
||||
$file_link,
|
||||
$object_link));
|
||||
|
||||
$dialog->addSubmitButton(pht('Detach File'));
|
||||
|
||||
return $dialog;
|
||||
}
|
||||
|
||||
if (!($object instanceof PhabricatorApplicationTransactionInterface)) {
|
||||
$dialog->appendParagraph(
|
||||
pht(
|
||||
'This object (of class "%s") does not implement the required '.
|
||||
'interface ("%s"), so files can not be manually detached from it.',
|
||||
get_class($object),
|
||||
'PhabricatorApplicationTransactionInterface'));
|
||||
|
||||
return $dialog;
|
||||
}
|
||||
|
||||
$editor = $object->getApplicationTransactionEditor()
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContinueOnMissingFields(true);
|
||||
|
||||
$template = $object->getApplicationTransactionTemplate();
|
||||
|
||||
$xactions = array();
|
||||
|
||||
$xactions[] = id(clone $template)
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_FILE)
|
||||
->setNewValue(
|
||||
array(
|
||||
$file_phid => PhabricatorFileAttachment::MODE_DETACH,
|
||||
));
|
||||
|
||||
$editor->applyTransactions($object, $xactions);
|
||||
|
||||
return $this->newRedirect()
|
||||
->setURI($cancel_uri);
|
||||
}
|
||||
|
||||
}
|
|
@ -28,9 +28,6 @@ final class PhabricatorFileUICurtainAttachController
|
|||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$file = $attachment->getFile();
|
||||
$file_phid = $file->getPHID();
|
||||
|
||||
$handles = $viewer->loadHandles(
|
||||
array(
|
||||
$object_phid,
|
||||
|
@ -44,7 +41,7 @@ final class PhabricatorFileUICurtainAttachController
|
|||
$dialog = $this->newDialog()
|
||||
->setViewer($viewer)
|
||||
->setTitle(pht('Attach File'))
|
||||
->addCancelButton($object_handle->getURI(), pht('Close'));
|
||||
->addCancelButton($cancel_uri, pht('Close'));
|
||||
|
||||
$file_link = phutil_tag('strong', array(), $file_handle->renderLink());
|
||||
$object_link = phutil_tag('strong', array(), $object_handle->renderLink());
|
||||
|
|
|
@ -320,20 +320,13 @@ final class PhabricatorFileViewController extends PhabricatorFileController {
|
|||
$finfo->addProperty(pht('Default Alt Text'), $default_alt);
|
||||
}
|
||||
|
||||
$phids = $file->getObjectPHIDs();
|
||||
if ($phids) {
|
||||
$attached = new PHUIPropertyListView();
|
||||
$attachments_table = $this->newAttachmentsView($file);
|
||||
|
||||
$tab_group->addTab(
|
||||
id(new PHUITabView())
|
||||
->setName(pht('Attached'))
|
||||
->setKey('attached')
|
||||
->appendChild($attached));
|
||||
|
||||
$attached->addProperty(
|
||||
pht('Attached To'),
|
||||
$viewer->renderHandleList($phids));
|
||||
}
|
||||
$tab_group->addTab(
|
||||
id(new PHUITabView())
|
||||
->setName(pht('Attached'))
|
||||
->setKey('attached')
|
||||
->appendChild($attachments_table));
|
||||
|
||||
$engine = $this->loadStorageEngine($file);
|
||||
if ($engine) {
|
||||
|
@ -420,4 +413,81 @@ final class PhabricatorFileViewController extends PhabricatorFileController {
|
|||
return $engine->newDocumentView($ref);
|
||||
}
|
||||
|
||||
private function newAttachmentsView(PhabricatorFile $file) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$attachments = id(new PhabricatorFileAttachmentQuery())
|
||||
->setViewer($viewer)
|
||||
->withFilePHIDs(array($file->getPHID()))
|
||||
->execute();
|
||||
|
||||
$handles = $viewer->loadHandles(mpull($attachments, 'getObjectPHID'));
|
||||
|
||||
$rows = array();
|
||||
|
||||
$mode_map = PhabricatorFileAttachment::getModeNameMap();
|
||||
$mode_attach = PhabricatorFileAttachment::MODE_ATTACH;
|
||||
|
||||
foreach ($attachments as $attachment) {
|
||||
$object_phid = $attachment->getObjectPHID();
|
||||
$handle = $handles[$object_phid];
|
||||
|
||||
$attachment_mode = $attachment->getAttachmentMode();
|
||||
|
||||
$mode_name = idx($mode_map, $attachment_mode);
|
||||
if ($mode_name === null) {
|
||||
$mode_name = pht('Unknown ("%s")', $attachment_mode);
|
||||
}
|
||||
|
||||
$detach_uri = urisprintf(
|
||||
'/file/ui/detach/%s/%s/',
|
||||
$object_phid,
|
||||
$file->getPHID());
|
||||
|
||||
$is_disabled = !$attachment->canDetach();
|
||||
|
||||
$detach_button = id(new PHUIButtonView())
|
||||
->setHref($detach_uri)
|
||||
->setTag('a')
|
||||
->setWorkflow(true)
|
||||
->setDisabled($is_disabled)
|
||||
->setColor(PHUIButtonView::GREY)
|
||||
->setSize(PHUIButtonView::SMALL)
|
||||
->setText(pht('Detach File'));
|
||||
|
||||
javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $detach_uri,
|
||||
'sigil' => 'workflow',
|
||||
'disabled' => true,
|
||||
'class' => 'small button button-grey disabled',
|
||||
),
|
||||
pht('Detach File'));
|
||||
|
||||
$rows[] = array(
|
||||
$handle->renderLink(),
|
||||
$mode_name,
|
||||
$detach_button,
|
||||
);
|
||||
}
|
||||
|
||||
$table = id(new AphrontTableView($rows))
|
||||
->setHeaders(
|
||||
array(
|
||||
pht('Attached To'),
|
||||
pht('Mode'),
|
||||
null,
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
'pri wide',
|
||||
null,
|
||||
null,
|
||||
));
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -46,6 +46,13 @@ final class PhabricatorFileAttachment
|
|||
);
|
||||
}
|
||||
|
||||
public static function getModeNameMap() {
|
||||
return array(
|
||||
self::MODE_ATTACH => pht('Attached'),
|
||||
self::MODE_REFERENCE => pht('Referenced'),
|
||||
);
|
||||
}
|
||||
|
||||
public function isPolicyAttachment() {
|
||||
switch ($this->getAttachmentMode()) {
|
||||
case self::MODE_ATTACH:
|
||||
|
@ -73,6 +80,15 @@ final class PhabricatorFileAttachment
|
|||
return $this->assertAttached($this->file);
|
||||
}
|
||||
|
||||
public function canDetach() {
|
||||
switch ($this->getAttachmentMode()) {
|
||||
case self::MODE_ATTACH:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
|
|
@ -1803,6 +1803,13 @@ final class PhabricatorUSEnglishTranslation
|
|||
),
|
||||
),
|
||||
|
||||
'%s removed %s attached file(s): %s.' => array(
|
||||
array(
|
||||
'%s removed an attached file: %3$s.',
|
||||
'%s removed attached files: %3$s.',
|
||||
),
|
||||
),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue