1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 08:42:41 +01:00

Give "FileAttachment" policy support and a query object

Summary: Ref T13682. This supports an "Attached Files" curtain UI element.

Test Plan: See next change.

Maniphest Tasks: T13682

Differential Revision: https://secure.phabricator.com/D21835
This commit is contained in:
epriestley 2022-05-23 10:12:04 -07:00
parent 631c36aee3
commit 5033ef6f88
7 changed files with 248 additions and 31 deletions

View file

@ -3453,6 +3453,7 @@ phutil_register_library_map(array(
'PhabricatorFileAES256StorageFormat' => 'applications/files/format/PhabricatorFileAES256StorageFormat.php', 'PhabricatorFileAES256StorageFormat' => 'applications/files/format/PhabricatorFileAES256StorageFormat.php',
'PhabricatorFileAltTextTransaction' => 'applications/files/xaction/PhabricatorFileAltTextTransaction.php', 'PhabricatorFileAltTextTransaction' => 'applications/files/xaction/PhabricatorFileAltTextTransaction.php',
'PhabricatorFileAttachment' => 'applications/files/storage/PhabricatorFileAttachment.php', 'PhabricatorFileAttachment' => 'applications/files/storage/PhabricatorFileAttachment.php',
'PhabricatorFileAttachmentQuery' => 'applications/files/query/PhabricatorFileAttachmentQuery.php',
'PhabricatorFileBundleLoader' => 'applications/files/query/PhabricatorFileBundleLoader.php', 'PhabricatorFileBundleLoader' => 'applications/files/query/PhabricatorFileBundleLoader.php',
'PhabricatorFileChunk' => 'applications/files/storage/PhabricatorFileChunk.php', 'PhabricatorFileChunk' => 'applications/files/storage/PhabricatorFileChunk.php',
'PhabricatorFileChunkIterator' => 'applications/files/engine/PhabricatorFileChunkIterator.php', 'PhabricatorFileChunkIterator' => 'applications/files/engine/PhabricatorFileChunkIterator.php',
@ -9892,7 +9893,12 @@ phutil_register_library_map(array(
), ),
'PhabricatorFileAES256StorageFormat' => 'PhabricatorFileStorageFormat', 'PhabricatorFileAES256StorageFormat' => 'PhabricatorFileStorageFormat',
'PhabricatorFileAltTextTransaction' => 'PhabricatorFileTransactionType', 'PhabricatorFileAltTextTransaction' => 'PhabricatorFileTransactionType',
'PhabricatorFileAttachment' => 'PhabricatorFileDAO', 'PhabricatorFileAttachment' => array(
'PhabricatorFileDAO',
'PhabricatorPolicyInterface',
'PhabricatorExtendedPolicyInterface',
),
'PhabricatorFileAttachmentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorFileBundleLoader' => 'Phobject', 'PhabricatorFileBundleLoader' => 'Phobject',
'PhabricatorFileChunk' => array( 'PhabricatorFileChunk' => array(
'PhabricatorFileDAO', 'PhabricatorFileDAO',

View file

@ -0,0 +1,110 @@
<?php
final class PhabricatorFileAttachmentQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $objectPHIDs;
private $needFiles;
public function withObjectPHIDs(array $object_phids) {
$this->objectPHIDs = $object_phids;
return $this;
}
public function needFiles($need) {
$this->needFiles = $need;
return $this;
}
public function newResultObject() {
return new PhabricatorFileAttachment();
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->objectPHIDs !== null) {
$where[] = qsprintf(
$conn,
'attachments.objectPHID IN (%Ls)',
$this->objectPHIDs);
}
return $where;
}
protected function willFilterPage(array $attachments) {
$viewer = $this->getViewer();
$object_phids = array();
foreach ($attachments as $attachment) {
$object_phid = $attachment->getObjectPHID();
$object_phids[$object_phid] = $object_phid;
}
if ($object_phids) {
$objects = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->setParentQuery($this)
->withPHIDs($object_phids)
->execute();
$objects = mpull($objects, null, 'getPHID');
} else {
$objects = array();
}
foreach ($attachments as $key => $attachment) {
$object_phid = $attachment->getObjectPHID();
$object = idx($objects, $object_phid);
if (!$object) {
$this->didRejectResult($attachment);
unset($attachments[$key]);
continue;
}
$attachment->attachObject($object);
}
if ($this->needFiles) {
$file_phids = array();
foreach ($attachments as $attachment) {
$file_phid = $attachment->getFilePHID();
$file_phids[$file_phid] = $file_phid;
}
if ($file_phids) {
$files = id(new PhabricatorFileQuery())
->setViewer($viewer)
->setParentQuery($this)
->withPHIDs($file_phids)
->execute();
$files = mpull($files, null, 'getPHID');
} else {
$files = array();
}
foreach ($attachments as $key => $attachment) {
$file_phid = $attachment->getFilePHID();
$file = idx($files, $file_phid);
$attachment->attachFile($file);
}
}
return $attachments;
}
protected function getPrimaryTableAlias() {
return 'attachments';
}
public function getQueryApplicationClass() {
return 'PhabricatorFilesApplication';
}
}

View file

@ -309,9 +309,13 @@ final class PhabricatorFileQuery
$attachments = queryfx_all( $attachments = queryfx_all(
$attachments_conn, $attachments_conn,
'SELECT filePHID, objectPHID FROM %R WHERE filePHID IN (%Ls)', 'SELECT filePHID, objectPHID FROM %R WHERE filePHID IN (%Ls)
AND attachmentMode IN (%Ls)',
$attachments_table, $attachments_table,
$file_phids); $file_phids,
array(
PhabricatorFileAttachment::MODE_ATTACH,
));
$attachments_map = array_fill_keys($file_phids, array()); $attachments_map = array_fill_keys($file_phids, array());
foreach ($attachments as $row) { foreach ($attachments as $row) {
@ -374,8 +378,12 @@ final class PhabricatorFileQuery
if ($this->shouldJoinAttachmentsTable()) { if ($this->shouldJoinAttachmentsTable()) {
$joins[] = qsprintf( $joins[] = qsprintf(
$conn, $conn,
'JOIN %R attachments ON attachments.filePHID = f.phid', 'JOIN %R attachments ON attachments.filePHID = f.phid
new PhabricatorFileAttachment()); AND attachmentMode IN (%Ls)',
new PhabricatorFileAttachment(),
array(
PhabricatorFileAttachment::MODE_ATTACH,
));
} }
return $joins; return $joins;

View file

@ -1,13 +1,19 @@
<?php <?php
final class PhabricatorFileAttachment final class PhabricatorFileAttachment
extends PhabricatorFileDAO { extends PhabricatorFileDAO
implements
PhabricatorPolicyInterface,
PhabricatorExtendedPolicyInterface {
protected $objectPHID; protected $objectPHID;
protected $filePHID; protected $filePHID;
protected $attacherPHID; protected $attacherPHID;
protected $attachmentMode; protected $attachmentMode;
private $object = self::ATTACHABLE;
private $file = self::ATTACHABLE;
const MODE_ATTACH = 'attach'; const MODE_ATTACH = 'attach';
const MODE_REFERENCE = 'reference'; const MODE_REFERENCE = 'reference';
const MODE_DETACH = 'detach'; const MODE_DETACH = 'detach';
@ -40,4 +46,53 @@ final class PhabricatorFileAttachment
); );
} }
public function attachObject($object) {
$this->object = $object;
return $this;
}
public function getObject() {
return $this->assertAttached($this->object);
}
public function attachFile(PhabricatorFile $file = null) {
$this->file = $file;
return $this;
}
public function getFile() {
return $this->assertAttached($this->file);
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return PhabricatorPolicies::getMostOpenPolicy();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
return array(
array($this->getObject(), $capability),
);
}
} }

View file

@ -100,7 +100,14 @@ final class PhabricatorFileTestCase extends PhabricatorTestCase {
$xactions[] = id(new ManiphestTransaction()) $xactions[] = id(new ManiphestTransaction())
->setTransactionType( ->setTransactionType(
ManiphestTaskDescriptionTransaction::TRANSACTIONTYPE) ManiphestTaskDescriptionTransaction::TRANSACTIONTYPE)
->setNewValue('{'.$file->getMonogram().'}'); ->setNewValue('{'.$file->getMonogram().'}')
->setMetadataValue(
'remarkup.control',
array(
'attachedFilePHIDs' => array(
$file->getPHID(),
),
));
id(new ManiphestTransactionEditor()) id(new ManiphestTransactionEditor())
->setActor($author) ->setActor($author)
@ -167,9 +174,10 @@ final class PhabricatorFileTestCase extends PhabricatorTestCase {
// Create an object and test object policies. // Create an object and test object policies.
$object = ManiphestTask::initializeNewTask($author); $object = ManiphestTask::initializeNewTask($author)
$object->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()); ->setTitle(pht('File Visibility Test Task'))
$object->save(); ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy())
->save();
$this->assertTrue( $this->assertTrue(
$filter->hasCapability( $filter->hasCapability(
@ -185,10 +193,53 @@ final class PhabricatorFileTestCase extends PhabricatorTestCase {
PhabricatorPolicyCapability::CAN_VIEW), PhabricatorPolicyCapability::CAN_VIEW),
pht('Object Visible to Others')); pht('Object Visible to Others'));
// Reference the file in a comment. This should not affect the file
// policy.
$file_ref = '{F'.$file->getID().'}';
$xactions = array();
$xactions[] = id(new ManiphestTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
->attachComment(
id(new ManiphestTransactionComment())
->setContent($file_ref));
id(new ManiphestTransactionEditor())
->setActor($author)
->setContentSource($this->newContentSource())
->applyTransactions($object, $xactions);
// Test the referenced file's visibility.
$this->assertEqual(
array(
true,
false,
),
$this->canViewFile($users, $file),
pht('Referenced File Visibility'));
// Attach the file to the object and test that the association opens a // Attach the file to the object and test that the association opens a
// policy exception for the non-author viewer. // policy exception for the non-author viewer.
$file->attachToObject($object->getPHID()); $xactions = array();
$xactions[] = id(new ManiphestTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
->setMetadataValue(
'remarkup.control',
array(
'attachedFilePHIDs' => array(
$file->getPHID(),
),
))
->attachComment(
id(new ManiphestTransactionComment())
->setContent($file_ref));
id(new ManiphestTransactionEditor())
->setActor($author)
->setContentSource($this->newContentSource())
->applyTransactions($object, $xactions);
// Test the attached file's visibility. // Test the attached file's visibility.
$this->assertEqual( $this->assertEqual(

View file

@ -2080,26 +2080,6 @@ abstract class PhabricatorEditEngine
} }
} }
public static function newTransactionsFromRemarkupMetadata(
PhabricatorApplicationTransaction $template,
array $metadata) {
$xactions = array();
$attached_phids = idx($metadata, 'attachedFilePHIDs');
if (is_array($attached_phids) && $attached_phids) {
$attachment_map = array_fill_keys(
$attached_phids,
PhabricatorFileAttachment::MODE_ATTACH);
$xactions[] = id(clone $template)
->setTransactionType(PhabricatorTransactions::TYPE_FILE)
->setNewValue($attachment_map);
}
return $xactions;
}
protected function newDraftEngine($object) { protected function newDraftEngine($object) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();

View file

@ -2266,7 +2266,14 @@ abstract class PhabricatorApplicationTransactionEditor
$viewer = $this->getActor(); $viewer = $this->getActor();
$old_blocks = mpull($remarkup_changes, 'getOldValue'); $old_blocks = mpull($remarkup_changes, 'getOldValue');
foreach ($old_blocks as $key => $old_block) {
$old_blocks[$key] = phutil_string_cast($old_block);
}
$new_blocks = mpull($remarkup_changes, 'getNewValue'); $new_blocks = mpull($remarkup_changes, 'getNewValue');
foreach ($new_blocks as $key => $new_block) {
$new_blocks[$key] = phutil_string_cast($new_block);
}
$old_refs = PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles( $old_refs = PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles(
$viewer, $viewer,