mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 23:02:42 +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:
parent
631c36aee3
commit
5033ef6f88
7 changed files with 248 additions and 31 deletions
|
@ -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',
|
||||||
|
|
110
src/applications/files/query/PhabricatorFileAttachmentQuery.php
Normal file
110
src/applications/files/query/PhabricatorFileAttachmentQuery.php
Normal 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';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue