@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+212 -6
+6
src/__phutil_library_map__.php
··· 239 239 'AphrontIsolatedDatabaseConnection' => 'infrastructure/storage/connection/AphrontIsolatedDatabaseConnection.php', 240 240 'AphrontIsolatedDatabaseConnectionTestCase' => 'infrastructure/storage/__tests__/AphrontIsolatedDatabaseConnectionTestCase.php', 241 241 'AphrontIsolatedHTTPSink' => 'aphront/sink/AphrontIsolatedHTTPSink.php', 242 + 'AphrontJSONHTTPParameterType' => 'aphront/httpparametertype/AphrontJSONHTTPParameterType.php', 242 243 'AphrontJSONResponse' => 'aphront/response/AphrontJSONResponse.php', 243 244 'AphrontJavelinView' => 'view/AphrontJavelinView.php', 244 245 'AphrontKeyboardShortcutsAvailableView' => 'view/widget/AphrontKeyboardShortcutsAvailableView.php', ··· 272 273 'AphrontRedirectResponse' => 'aphront/response/AphrontRedirectResponse.php', 273 274 'AphrontRedirectResponseTestCase' => 'aphront/response/__tests__/AphrontRedirectResponseTestCase.php', 274 275 'AphrontReloadResponse' => 'aphront/response/AphrontReloadResponse.php', 276 + 'AphrontRemarkupHTTPParameterType' => 'aphront/httpparametertype/AphrontRemarkupHTTPParameterType.php', 275 277 'AphrontRequest' => 'aphront/AphrontRequest.php', 276 278 'AphrontRequestExceptionHandler' => 'aphront/handler/AphrontRequestExceptionHandler.php', 277 279 'AphrontRequestStream' => 'aphront/requeststream/AphrontRequestStream.php', ··· 5858 5860 'QueryFormattingTestCase' => 'infrastructure/storage/__tests__/QueryFormattingTestCase.php', 5859 5861 'QueryFuture' => 'infrastructure/storage/future/QueryFuture.php', 5860 5862 'RemarkupProcessConduitAPIMethod' => 'applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php', 5863 + 'RemarkupValue' => 'applications/remarkup/RemarkupValue.php', 5861 5864 'RepositoryConduitAPIMethod' => 'applications/repository/conduit/RepositoryConduitAPIMethod.php', 5862 5865 'RepositoryQueryConduitAPIMethod' => 'applications/repository/conduit/RepositoryQueryConduitAPIMethod.php', 5863 5866 'ShellLogView' => 'applications/harbormaster/view/ShellLogView.php', ··· 6207 6210 'AphrontIsolatedDatabaseConnection' => 'AphrontDatabaseConnection', 6208 6211 'AphrontIsolatedDatabaseConnectionTestCase' => 'PhabricatorTestCase', 6209 6212 'AphrontIsolatedHTTPSink' => 'AphrontHTTPSink', 6213 + 'AphrontJSONHTTPParameterType' => 'AphrontHTTPParameterType', 6210 6214 'AphrontJSONResponse' => 'AphrontResponse', 6211 6215 'AphrontJavelinView' => 'AphrontView', 6212 6216 'AphrontKeyboardShortcutsAvailableView' => 'AphrontView', ··· 6243 6247 'AphrontRedirectResponse' => 'AphrontResponse', 6244 6248 'AphrontRedirectResponseTestCase' => 'PhabricatorTestCase', 6245 6249 'AphrontReloadResponse' => 'AphrontRedirectResponse', 6250 + 'AphrontRemarkupHTTPParameterType' => 'AphrontHTTPParameterType', 6246 6251 'AphrontRequest' => 'Phobject', 6247 6252 'AphrontRequestExceptionHandler' => 'Phobject', 6248 6253 'AphrontRequestStream' => 'Phobject', ··· 12747 12752 'QueryFormattingTestCase' => 'PhabricatorTestCase', 12748 12753 'QueryFuture' => 'Future', 12749 12754 'RemarkupProcessConduitAPIMethod' => 'ConduitAPIMethod', 12755 + 'RemarkupValue' => 'Phobject', 12750 12756 'RepositoryConduitAPIMethod' => 'ConduitAPIMethod', 12751 12757 'RepositoryQueryConduitAPIMethod' => 'RepositoryConduitAPIMethod', 12752 12758 'ShellLogView' => 'AphrontView',
+31
src/aphront/httpparametertype/AphrontJSONHTTPParameterType.php
··· 1 + <?php 2 + 3 + final class AphrontJSONHTTPParameterType 4 + extends AphrontHTTPParameterType { 5 + 6 + protected function getParameterDefault() { 7 + return array(); 8 + } 9 + 10 + protected function getParameterValue(AphrontRequest $request, $key) { 11 + $str = $request->getStr($key); 12 + return phutil_json_decode($str); 13 + } 14 + 15 + protected function getParameterTypeName() { 16 + return 'string (json object)'; 17 + } 18 + 19 + protected function getParameterFormatDescriptions() { 20 + return array( 21 + pht('A JSON-encoded object.'), 22 + ); 23 + } 24 + 25 + protected function getParameterExamples() { 26 + return array( 27 + 'v={...}', 28 + ); 29 + } 30 + 31 + }
+50
src/aphront/httpparametertype/AphrontRemarkupHTTPParameterType.php
··· 1 + <?php 2 + 3 + final class AphrontRemarkupHTTPParameterType 4 + extends AphrontHTTPParameterType { 5 + 6 + protected function getParameterDefault() { 7 + return $this->newRemarkupValue(); 8 + } 9 + 10 + protected function getParameterValue(AphrontRequest $request, $key) { 11 + $corpus_key = $key; 12 + $corpus_type = new AphrontStringHTTPParameterType(); 13 + $corpus_value = $this->getValueWithType( 14 + $corpus_type, 15 + $request, 16 + $corpus_key); 17 + 18 + $metadata_key = $key.'_metadata'; 19 + $metadata_type = new AphrontJSONHTTPParameterType(); 20 + $metadata_value = $this->getValueWithType( 21 + $metadata_type, 22 + $request, 23 + $metadata_key); 24 + 25 + return $this->newRemarkupValue() 26 + ->setCorpus($corpus_value) 27 + ->setMetadata($metadata_value); 28 + } 29 + 30 + protected function getParameterTypeName() { 31 + return 'string (remarkup)'; 32 + } 33 + 34 + protected function getParameterFormatDescriptions() { 35 + return array( 36 + pht('Remarkup text.'), 37 + ); 38 + } 39 + 40 + protected function getParameterExamples() { 41 + return array( 42 + 'v=Lorem...', 43 + ); 44 + } 45 + 46 + private function newRemarkupValue() { 47 + return new RemarkupValue(); 48 + } 49 + 50 + }
+27
src/applications/remarkup/RemarkupValue.php
··· 1 + <?php 2 + 3 + final class RemarkupValue 4 + extends Phobject { 5 + 6 + private $corpus; 7 + private $metadata; 8 + 9 + public function setCorpus($corpus) { 10 + $this->corpus = $corpus; 11 + return $this; 12 + } 13 + 14 + public function getCorpus() { 15 + return $this->corpus; 16 + } 17 + 18 + public function setMetadata(array $metadata) { 19 + $this->metadata = $metadata; 20 + return $this; 21 + } 22 + 23 + public function getMetadata() { 24 + return $this->metadata; 25 + } 26 + 27 + }
+10
src/applications/transactions/data/PhabricatorTransactionChange.php
··· 3 3 abstract class PhabricatorTransactionChange extends Phobject { 4 4 5 5 private $transaction; 6 + private $metadata = array(); 6 7 private $oldValue; 7 8 private $newValue; 8 9 ··· 32 33 33 34 final public function getNewValue() { 34 35 return $this->newValue; 36 + } 37 + 38 + final public function setMetadata(array $metadata) { 39 + $this->metadata = $metadata; 40 + return $this; 41 + } 42 + 43 + final public function getMetadata() { 44 + return $this->metadata; 35 45 } 36 46 37 47 }
+21
src/applications/transactions/editengine/PhabricatorEditEngine.php
··· 2012 2012 if (strlen($comment_text) || !$xactions) { 2013 2013 $xactions[] = id(clone $template) 2014 2014 ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) 2015 + ->setMetadataValue('remarkup.control', $comment_metadata) 2015 2016 ->attachComment( 2016 2017 id(clone $comment_template) 2017 2018 ->setContent($comment_text)); ··· 2077 2078 return id(new AphrontRedirectResponse()) 2078 2079 ->setURI($view_uri); 2079 2080 } 2081 + } 2082 + 2083 + public static function newTransactionsFromRemarkupMetadata( 2084 + PhabricatorApplicationTransaction $template, 2085 + array $metadata) { 2086 + 2087 + $xactions = array(); 2088 + 2089 + $attached_phids = idx($metadata, 'attachedFilePHIDs'); 2090 + if (is_array($attached_phids) && $attached_phids) { 2091 + $attachment_map = array_fill_keys( 2092 + $attached_phids, 2093 + PhabricatorFileAttachment::MODE_ATTACH); 2094 + 2095 + $xactions[] = id(clone $template) 2096 + ->setTransactionType(PhabricatorTransactions::TYPE_FILE) 2097 + ->setNewValue($attachment_map); 2098 + } 2099 + 2100 + return $xactions; 2080 2101 } 2081 2102 2082 2103 protected function newDraftEngine($object) {
+29
src/applications/transactions/editfield/PhabricatorRemarkupEditField.php
··· 7 7 return new PhabricatorRemarkupControl(); 8 8 } 9 9 10 + protected function newHTTPParameterType() { 11 + return new AphrontRemarkupHTTPParameterType(); 12 + } 13 + 10 14 protected function newConduitParameterType() { 11 15 return new ConduitStringParameterType(); 12 16 } ··· 14 18 protected function newBulkParameterType() { 15 19 return new BulkRemarkupParameterType(); 16 20 } 21 + 22 + public function getValueForTransaction() { 23 + $value = $this->getValue(); 24 + 25 + if ($value instanceof RemarkupValue) { 26 + $value = $value->getCorpus(); 27 + } 28 + 29 + return $value; 30 + } 31 + 32 + public function getMetadata() { 33 + $defaults = array(); 34 + 35 + $value = $this->getValue(); 36 + if ($value instanceof RemarkupValue) { 37 + $defaults['remarkup.control'] = $value->getMetadata(); 38 + } 39 + 40 + $metadata = parent::getMetadata(); 41 + $metadata = $metadata + $defaults; 42 + 43 + return $metadata; 44 + } 45 + 17 46 18 47 }
+20 -5
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 2225 2225 2226 2226 $file_xaction = $this->newFileTransaction( 2227 2227 $object, 2228 - $xactions); 2228 + $xactions, 2229 + $changes); 2229 2230 if ($file_xaction) { 2230 2231 $xactions[] = $file_xaction; 2231 2232 } ··· 2236 2237 2237 2238 private function newFileTransaction( 2238 2239 PhabricatorLiskDAO $object, 2239 - array $xactions) { 2240 + array $xactions, 2241 + array $remarkup_changes) { 2242 + 2243 + assert_instances_of( 2244 + $remarkup_changes, 2245 + 'PhabricatorTransactionRemarkupChange'); 2240 2246 2241 2247 $new_map = array(); 2242 2248 2243 - $file_phids = $this->extractFilePHIDs($object, $xactions); 2244 - if (!$file_phids) { 2245 - return null; 2249 + foreach ($remarkup_changes as $remarkup_change) { 2250 + $metadata = $remarkup_change->getMetadata(); 2251 + 2252 + $attached_phids = idx($metadata, 'attachedFilePHIDs'); 2253 + foreach ($attached_phids as $file_phid) { 2254 + $new_map[$file_phid] = PhabricatorFileAttachment::MODE_ATTACH; 2255 + } 2246 2256 } 2247 2257 2258 + $file_phids = $this->extractFilePHIDs($object, $xactions); 2248 2259 foreach ($file_phids as $file_phid) { 2249 2260 $new_map[$file_phid] = PhabricatorFileAttachment::MODE_ATTACH; 2261 + } 2262 + 2263 + if (!$new_map) { 2264 + return null; 2250 2265 } 2251 2266 2252 2267 $xaction = $object->getApplicationTransactionTemplate()
+7
src/applications/transactions/storage/PhabricatorApplicationTransaction.php
··· 244 244 ->setNewValue($new_value); 245 245 } 246 246 247 + $metadata = $this->getMetadataValue('remarkup.control', array()); 248 + foreach ($changes as $change) { 249 + if (!$change->getMetadata()) { 250 + $change->setMetadata($metadata); 251 + } 252 + } 253 + 247 254 return $changes; 248 255 } 249 256
+11 -1
src/view/form/control/PhabricatorRemarkupControl.php
··· 1 1 <?php 2 2 3 - final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl { 3 + final class PhabricatorRemarkupControl 4 + extends AphrontFormTextAreaControl { 4 5 5 6 private $disableMacro = false; 6 7 private $disableFullScreen = false; ··· 43 44 44 45 public function getRemarkupMetadata() { 45 46 return $this->remarkupMetadata; 47 + } 48 + 49 + public function setValue($value) { 50 + if ($value instanceof RemarkupValue) { 51 + $this->setRemarkupMetadata($value->getMetadata()); 52 + $value = $value->getCorpus(); 53 + } 54 + 55 + return parent::setValue($value); 46 56 } 47 57 48 58 protected function renderInput() {