1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-23 14:00:56 +01:00

Generate file attachment transactions for explicit Remarkup attachments on common edit pathways

Summary:
Ref T13603. On common edit pathways, extract explicit file attachments from Remarkup. These pathways are affected:

  - Objects that use EditEngine and expose a remarkup area via "RemarkupEditField".
  - Objects that use EditEngine to generate a comment area.

This is the vast majority of pathways, but not entirely exhaustive.

Test Plan: Created and commented on a task, explicitly attaching images. Saw images attach properly.

Maniphest Tasks: T13603

Differential Revision: https://secure.phabricator.com/D21830
This commit is contained in:
epriestley 2022-05-19 12:19:48 -07:00
parent fee8297121
commit 42876de60d
10 changed files with 212 additions and 6 deletions

View file

@ -239,6 +239,7 @@ phutil_register_library_map(array(
'AphrontIsolatedDatabaseConnection' => 'infrastructure/storage/connection/AphrontIsolatedDatabaseConnection.php', 'AphrontIsolatedDatabaseConnection' => 'infrastructure/storage/connection/AphrontIsolatedDatabaseConnection.php',
'AphrontIsolatedDatabaseConnectionTestCase' => 'infrastructure/storage/__tests__/AphrontIsolatedDatabaseConnectionTestCase.php', 'AphrontIsolatedDatabaseConnectionTestCase' => 'infrastructure/storage/__tests__/AphrontIsolatedDatabaseConnectionTestCase.php',
'AphrontIsolatedHTTPSink' => 'aphront/sink/AphrontIsolatedHTTPSink.php', 'AphrontIsolatedHTTPSink' => 'aphront/sink/AphrontIsolatedHTTPSink.php',
'AphrontJSONHTTPParameterType' => 'aphront/httpparametertype/AphrontJSONHTTPParameterType.php',
'AphrontJSONResponse' => 'aphront/response/AphrontJSONResponse.php', 'AphrontJSONResponse' => 'aphront/response/AphrontJSONResponse.php',
'AphrontJavelinView' => 'view/AphrontJavelinView.php', 'AphrontJavelinView' => 'view/AphrontJavelinView.php',
'AphrontKeyboardShortcutsAvailableView' => 'view/widget/AphrontKeyboardShortcutsAvailableView.php', 'AphrontKeyboardShortcutsAvailableView' => 'view/widget/AphrontKeyboardShortcutsAvailableView.php',
@ -272,6 +273,7 @@ phutil_register_library_map(array(
'AphrontRedirectResponse' => 'aphront/response/AphrontRedirectResponse.php', 'AphrontRedirectResponse' => 'aphront/response/AphrontRedirectResponse.php',
'AphrontRedirectResponseTestCase' => 'aphront/response/__tests__/AphrontRedirectResponseTestCase.php', 'AphrontRedirectResponseTestCase' => 'aphront/response/__tests__/AphrontRedirectResponseTestCase.php',
'AphrontReloadResponse' => 'aphront/response/AphrontReloadResponse.php', 'AphrontReloadResponse' => 'aphront/response/AphrontReloadResponse.php',
'AphrontRemarkupHTTPParameterType' => 'aphront/httpparametertype/AphrontRemarkupHTTPParameterType.php',
'AphrontRequest' => 'aphront/AphrontRequest.php', 'AphrontRequest' => 'aphront/AphrontRequest.php',
'AphrontRequestExceptionHandler' => 'aphront/handler/AphrontRequestExceptionHandler.php', 'AphrontRequestExceptionHandler' => 'aphront/handler/AphrontRequestExceptionHandler.php',
'AphrontRequestStream' => 'aphront/requeststream/AphrontRequestStream.php', 'AphrontRequestStream' => 'aphront/requeststream/AphrontRequestStream.php',
@ -5858,6 +5860,7 @@ phutil_register_library_map(array(
'QueryFormattingTestCase' => 'infrastructure/storage/__tests__/QueryFormattingTestCase.php', 'QueryFormattingTestCase' => 'infrastructure/storage/__tests__/QueryFormattingTestCase.php',
'QueryFuture' => 'infrastructure/storage/future/QueryFuture.php', 'QueryFuture' => 'infrastructure/storage/future/QueryFuture.php',
'RemarkupProcessConduitAPIMethod' => 'applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php', 'RemarkupProcessConduitAPIMethod' => 'applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php',
'RemarkupValue' => 'applications/remarkup/RemarkupValue.php',
'RepositoryConduitAPIMethod' => 'applications/repository/conduit/RepositoryConduitAPIMethod.php', 'RepositoryConduitAPIMethod' => 'applications/repository/conduit/RepositoryConduitAPIMethod.php',
'RepositoryQueryConduitAPIMethod' => 'applications/repository/conduit/RepositoryQueryConduitAPIMethod.php', 'RepositoryQueryConduitAPIMethod' => 'applications/repository/conduit/RepositoryQueryConduitAPIMethod.php',
'ShellLogView' => 'applications/harbormaster/view/ShellLogView.php', 'ShellLogView' => 'applications/harbormaster/view/ShellLogView.php',
@ -6207,6 +6210,7 @@ phutil_register_library_map(array(
'AphrontIsolatedDatabaseConnection' => 'AphrontDatabaseConnection', 'AphrontIsolatedDatabaseConnection' => 'AphrontDatabaseConnection',
'AphrontIsolatedDatabaseConnectionTestCase' => 'PhabricatorTestCase', 'AphrontIsolatedDatabaseConnectionTestCase' => 'PhabricatorTestCase',
'AphrontIsolatedHTTPSink' => 'AphrontHTTPSink', 'AphrontIsolatedHTTPSink' => 'AphrontHTTPSink',
'AphrontJSONHTTPParameterType' => 'AphrontHTTPParameterType',
'AphrontJSONResponse' => 'AphrontResponse', 'AphrontJSONResponse' => 'AphrontResponse',
'AphrontJavelinView' => 'AphrontView', 'AphrontJavelinView' => 'AphrontView',
'AphrontKeyboardShortcutsAvailableView' => 'AphrontView', 'AphrontKeyboardShortcutsAvailableView' => 'AphrontView',
@ -6243,6 +6247,7 @@ phutil_register_library_map(array(
'AphrontRedirectResponse' => 'AphrontResponse', 'AphrontRedirectResponse' => 'AphrontResponse',
'AphrontRedirectResponseTestCase' => 'PhabricatorTestCase', 'AphrontRedirectResponseTestCase' => 'PhabricatorTestCase',
'AphrontReloadResponse' => 'AphrontRedirectResponse', 'AphrontReloadResponse' => 'AphrontRedirectResponse',
'AphrontRemarkupHTTPParameterType' => 'AphrontHTTPParameterType',
'AphrontRequest' => 'Phobject', 'AphrontRequest' => 'Phobject',
'AphrontRequestExceptionHandler' => 'Phobject', 'AphrontRequestExceptionHandler' => 'Phobject',
'AphrontRequestStream' => 'Phobject', 'AphrontRequestStream' => 'Phobject',
@ -12747,6 +12752,7 @@ phutil_register_library_map(array(
'QueryFormattingTestCase' => 'PhabricatorTestCase', 'QueryFormattingTestCase' => 'PhabricatorTestCase',
'QueryFuture' => 'Future', 'QueryFuture' => 'Future',
'RemarkupProcessConduitAPIMethod' => 'ConduitAPIMethod', 'RemarkupProcessConduitAPIMethod' => 'ConduitAPIMethod',
'RemarkupValue' => 'Phobject',
'RepositoryConduitAPIMethod' => 'ConduitAPIMethod', 'RepositoryConduitAPIMethod' => 'ConduitAPIMethod',
'RepositoryQueryConduitAPIMethod' => 'RepositoryConduitAPIMethod', 'RepositoryQueryConduitAPIMethod' => 'RepositoryConduitAPIMethod',
'ShellLogView' => 'AphrontView', 'ShellLogView' => 'AphrontView',

View file

@ -0,0 +1,31 @@
<?php
final class AphrontJSONHTTPParameterType
extends AphrontHTTPParameterType {
protected function getParameterDefault() {
return array();
}
protected function getParameterValue(AphrontRequest $request, $key) {
$str = $request->getStr($key);
return phutil_json_decode($str);
}
protected function getParameterTypeName() {
return 'string (json object)';
}
protected function getParameterFormatDescriptions() {
return array(
pht('A JSON-encoded object.'),
);
}
protected function getParameterExamples() {
return array(
'v={...}',
);
}
}

View file

@ -0,0 +1,50 @@
<?php
final class AphrontRemarkupHTTPParameterType
extends AphrontHTTPParameterType {
protected function getParameterDefault() {
return $this->newRemarkupValue();
}
protected function getParameterValue(AphrontRequest $request, $key) {
$corpus_key = $key;
$corpus_type = new AphrontStringHTTPParameterType();
$corpus_value = $this->getValueWithType(
$corpus_type,
$request,
$corpus_key);
$metadata_key = $key.'_metadata';
$metadata_type = new AphrontJSONHTTPParameterType();
$metadata_value = $this->getValueWithType(
$metadata_type,
$request,
$metadata_key);
return $this->newRemarkupValue()
->setCorpus($corpus_value)
->setMetadata($metadata_value);
}
protected function getParameterTypeName() {
return 'string (remarkup)';
}
protected function getParameterFormatDescriptions() {
return array(
pht('Remarkup text.'),
);
}
protected function getParameterExamples() {
return array(
'v=Lorem...',
);
}
private function newRemarkupValue() {
return new RemarkupValue();
}
}

View file

@ -0,0 +1,27 @@
<?php
final class RemarkupValue
extends Phobject {
private $corpus;
private $metadata;
public function setCorpus($corpus) {
$this->corpus = $corpus;
return $this;
}
public function getCorpus() {
return $this->corpus;
}
public function setMetadata(array $metadata) {
$this->metadata = $metadata;
return $this;
}
public function getMetadata() {
return $this->metadata;
}
}

View file

@ -3,6 +3,7 @@
abstract class PhabricatorTransactionChange extends Phobject { abstract class PhabricatorTransactionChange extends Phobject {
private $transaction; private $transaction;
private $metadata = array();
private $oldValue; private $oldValue;
private $newValue; private $newValue;
@ -34,4 +35,13 @@ abstract class PhabricatorTransactionChange extends Phobject {
return $this->newValue; return $this->newValue;
} }
final public function setMetadata(array $metadata) {
$this->metadata = $metadata;
return $this;
}
final public function getMetadata() {
return $this->metadata;
}
} }

View file

@ -2012,6 +2012,7 @@ abstract class PhabricatorEditEngine
if (strlen($comment_text) || !$xactions) { if (strlen($comment_text) || !$xactions) {
$xactions[] = id(clone $template) $xactions[] = id(clone $template)
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
->setMetadataValue('remarkup.control', $comment_metadata)
->attachComment( ->attachComment(
id(clone $comment_template) id(clone $comment_template)
->setContent($comment_text)); ->setContent($comment_text));
@ -2079,6 +2080,26 @@ 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

@ -7,6 +7,10 @@ final class PhabricatorRemarkupEditField
return new PhabricatorRemarkupControl(); return new PhabricatorRemarkupControl();
} }
protected function newHTTPParameterType() {
return new AphrontRemarkupHTTPParameterType();
}
protected function newConduitParameterType() { protected function newConduitParameterType() {
return new ConduitStringParameterType(); return new ConduitStringParameterType();
} }
@ -15,4 +19,29 @@ final class PhabricatorRemarkupEditField
return new BulkRemarkupParameterType(); return new BulkRemarkupParameterType();
} }
public function getValueForTransaction() {
$value = $this->getValue();
if ($value instanceof RemarkupValue) {
$value = $value->getCorpus();
}
return $value;
}
public function getMetadata() {
$defaults = array();
$value = $this->getValue();
if ($value instanceof RemarkupValue) {
$defaults['remarkup.control'] = $value->getMetadata();
}
$metadata = parent::getMetadata();
$metadata = $metadata + $defaults;
return $metadata;
}
} }

View file

@ -2225,7 +2225,8 @@ abstract class PhabricatorApplicationTransactionEditor
$file_xaction = $this->newFileTransaction( $file_xaction = $this->newFileTransaction(
$object, $object,
$xactions); $xactions,
$changes);
if ($file_xaction) { if ($file_xaction) {
$xactions[] = $file_xaction; $xactions[] = $file_xaction;
} }
@ -2236,19 +2237,33 @@ abstract class PhabricatorApplicationTransactionEditor
private function newFileTransaction( private function newFileTransaction(
PhabricatorLiskDAO $object, PhabricatorLiskDAO $object,
array $xactions) { array $xactions,
array $remarkup_changes) {
assert_instances_of(
$remarkup_changes,
'PhabricatorTransactionRemarkupChange');
$new_map = array(); $new_map = array();
$file_phids = $this->extractFilePHIDs($object, $xactions); foreach ($remarkup_changes as $remarkup_change) {
if (!$file_phids) { $metadata = $remarkup_change->getMetadata();
return null;
$attached_phids = idx($metadata, 'attachedFilePHIDs');
foreach ($attached_phids as $file_phid) {
$new_map[$file_phid] = PhabricatorFileAttachment::MODE_ATTACH;
}
} }
$file_phids = $this->extractFilePHIDs($object, $xactions);
foreach ($file_phids as $file_phid) { foreach ($file_phids as $file_phid) {
$new_map[$file_phid] = PhabricatorFileAttachment::MODE_ATTACH; $new_map[$file_phid] = PhabricatorFileAttachment::MODE_ATTACH;
} }
if (!$new_map) {
return null;
}
$xaction = $object->getApplicationTransactionTemplate() $xaction = $object->getApplicationTransactionTemplate()
->setTransactionType(PhabricatorTransactions::TYPE_FILE) ->setTransactionType(PhabricatorTransactions::TYPE_FILE)
->setNewValue($new_map); ->setNewValue($new_map);

View file

@ -244,6 +244,13 @@ abstract class PhabricatorApplicationTransaction
->setNewValue($new_value); ->setNewValue($new_value);
} }
$metadata = $this->getMetadataValue('remarkup.control', array());
foreach ($changes as $change) {
if (!$change->getMetadata()) {
$change->setMetadata($metadata);
}
}
return $changes; return $changes;
} }

View file

@ -1,6 +1,7 @@
<?php <?php
final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl { final class PhabricatorRemarkupControl
extends AphrontFormTextAreaControl {
private $disableMacro = false; private $disableMacro = false;
private $disableFullScreen = false; private $disableFullScreen = false;
@ -45,6 +46,15 @@ final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl {
return $this->remarkupMetadata; return $this->remarkupMetadata;
} }
public function setValue($value) {
if ($value instanceof RemarkupValue) {
$this->setRemarkupMetadata($value->getMetadata());
$value = $value->getCorpus();
}
return parent::setValue($value);
}
protected function renderInput() { protected function renderInput() {
$id = $this->getID(); $id = $this->getID();
if (!$id) { if (!$id) {