diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3453,6 +3453,7 @@ 'PhabricatorFileAES256StorageFormat' => 'applications/files/format/PhabricatorFileAES256StorageFormat.php', 'PhabricatorFileAltTextTransaction' => 'applications/files/xaction/PhabricatorFileAltTextTransaction.php', 'PhabricatorFileAttachment' => 'applications/files/storage/PhabricatorFileAttachment.php', + 'PhabricatorFileAttachmentQuery' => 'applications/files/query/PhabricatorFileAttachmentQuery.php', 'PhabricatorFileBundleLoader' => 'applications/files/query/PhabricatorFileBundleLoader.php', 'PhabricatorFileChunk' => 'applications/files/storage/PhabricatorFileChunk.php', 'PhabricatorFileChunkIterator' => 'applications/files/engine/PhabricatorFileChunkIterator.php', @@ -9892,7 +9893,12 @@ ), 'PhabricatorFileAES256StorageFormat' => 'PhabricatorFileStorageFormat', 'PhabricatorFileAltTextTransaction' => 'PhabricatorFileTransactionType', - 'PhabricatorFileAttachment' => 'PhabricatorFileDAO', + 'PhabricatorFileAttachment' => array( + 'PhabricatorFileDAO', + 'PhabricatorPolicyInterface', + 'PhabricatorExtendedPolicyInterface', + ), + 'PhabricatorFileAttachmentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorFileBundleLoader' => 'Phobject', 'PhabricatorFileChunk' => array( 'PhabricatorFileDAO', diff --git a/src/applications/files/query/PhabricatorFileAttachmentQuery.php b/src/applications/files/query/PhabricatorFileAttachmentQuery.php new file mode 100644 --- /dev/null +++ b/src/applications/files/query/PhabricatorFileAttachmentQuery.php @@ -0,0 +1,110 @@ +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'; + } + +} diff --git a/src/applications/files/query/PhabricatorFileQuery.php b/src/applications/files/query/PhabricatorFileQuery.php --- a/src/applications/files/query/PhabricatorFileQuery.php +++ b/src/applications/files/query/PhabricatorFileQuery.php @@ -374,8 +374,12 @@ if ($this->shouldJoinAttachmentsTable()) { $joins[] = qsprintf( $conn, - 'JOIN %R attachments ON attachments.filePHID = f.phid', - new PhabricatorFileAttachment()); + 'JOIN %R attachments ON attachments.filePHID = f.phid + AND attachmentMode IN (%Ls)', + new PhabricatorFileAttachment(), + array( + PhabricatorFileAttachment::MODE_ATTACH, + )); } return $joins; diff --git a/src/applications/files/storage/PhabricatorFileAttachment.php b/src/applications/files/storage/PhabricatorFileAttachment.php --- a/src/applications/files/storage/PhabricatorFileAttachment.php +++ b/src/applications/files/storage/PhabricatorFileAttachment.php @@ -1,13 +1,19 @@ 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), + ); + } + } diff --git a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php --- a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php +++ b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php @@ -100,7 +100,12 @@ $xactions[] = id(new ManiphestTransaction()) ->setTransactionType( ManiphestTaskDescriptionTransaction::TRANSACTIONTYPE) - ->setNewValue('{'.$file->getMonogram().'}'); + ->setNewValue('{'.$file->getMonogram().'}') + ->setMetadataValue( + 'attachedFilePHIDs', + array( + $file->getPHID(), + )); id(new ManiphestTransactionEditor()) ->setActor($author) diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -2266,7 +2266,14 @@ $viewer = $this->getActor(); $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'); + foreach ($new_blocks as $key => $new_block) { + $new_blocks[$key] = phutil_string_cast($new_block); + } $old_refs = PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles( $viewer,