@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
at recaptime-dev/main 239 lines 5.9 kB view raw
1<?php 2 3final class PhabricatorChangesetViewStateEngine 4 extends Phobject { 5 6 private $viewer; 7 private $objectPHID; 8 private $changeset; 9 private $storage; 10 11 public function setViewer(PhabricatorUser $viewer) { 12 $this->viewer = $viewer; 13 return $this; 14 } 15 16 public function getViewer() { 17 return $this->viewer; 18 } 19 20 public function setObjectPHID($object_phid) { 21 $this->objectPHID = $object_phid; 22 return $this; 23 } 24 25 public function getObjectPHID() { 26 return $this->objectPHID; 27 } 28 29 public function setChangeset(DifferentialChangeset $changeset) { 30 $this->changeset = $changeset; 31 return $this; 32 } 33 34 public function getChangeset() { 35 return $this->changeset; 36 } 37 38 public function newViewStateFromRequest(AphrontRequest $request) { 39 $storage = $this->loadViewStateStorage(); 40 41 $this->setStorage($storage); 42 43 $highlight = $request->getStr('highlight'); 44 if ($highlight !== null) { 45 $this->setChangesetProperty('highlight', $highlight); 46 } 47 48 $encoding = $request->getStr('encoding'); 49 if ($encoding !== null) { 50 $this->setChangesetProperty('encoding', $encoding); 51 } 52 53 $engine = $request->getStr('engine'); 54 if ($engine !== null) { 55 $this->setChangesetProperty('engine', $engine); 56 } 57 58 $renderer = $request->getStr('renderer'); 59 if ($renderer !== null) { 60 $this->setChangesetProperty('renderer', $renderer); 61 } 62 63 $hidden = $request->getStr('hidden'); 64 if ($hidden !== null) { 65 $this->setChangesetProperty('hidden', (int)$hidden); 66 } 67 68 $this->saveViewStateStorage(); 69 70 $state = new PhabricatorChangesetViewState(); 71 72 $highlight_language = $this->getChangesetProperty('highlight'); 73 $state->setHighlightLanguage($highlight_language); 74 75 $encoding = $this->getChangesetProperty('encoding'); 76 $state->setCharacterEncoding($encoding); 77 78 $document_engine = $this->getChangesetProperty('engine'); 79 $state->setDocumentEngineKey($document_engine); 80 81 $renderer = $this->getChangesetProperty('renderer'); 82 $state->setRendererKey($renderer); 83 84 $this->updateHiddenState($state); 85 86 // This is the client-selected default renderer based on viewport 87 // dimensions. 88 89 $device_key = $request->getStr('device'); 90 if ($device_key !== null) { 91 $state->setDefaultDeviceRendererKey($device_key); 92 } 93 94 $discard_response = $request->getStr('discard'); 95 if ($discard_response !== null) { 96 $state->setDiscardResponse(true); 97 } 98 99 return $state; 100 } 101 102 private function setStorage(DifferentialViewState $storage) { 103 $this->storage = $storage; 104 return $this; 105 } 106 107 private function getStorage() { 108 return $this->storage; 109 } 110 111 private function setChangesetProperty( 112 $key, 113 $value) { 114 115 $storage = $this->getStorage(); 116 $changeset = $this->getChangeset(); 117 118 $storage->setChangesetProperty($changeset, $key, $value); 119 } 120 121 private function getChangesetProperty( 122 $key, 123 $default = null) { 124 125 $storage = $this->getStorage(); 126 $changeset = $this->getChangeset(); 127 128 return $storage->getChangesetProperty($changeset, $key, $default); 129 } 130 131 private function loadViewStateStorage() { 132 $viewer = $this->getViewer(); 133 134 $object_phid = $this->getObjectPHID(); 135 $viewer_phid = $viewer->getPHID(); 136 137 $storage = null; 138 139 if ($viewer_phid !== null) { 140 $storage = id(new DifferentialViewStateQuery()) 141 ->setViewer($viewer) 142 ->withViewerPHIDs(array($viewer_phid)) 143 ->withObjectPHIDs(array($object_phid)) 144 ->executeOne(); 145 } 146 147 if ($storage === null) { 148 $storage = id(new DifferentialViewState()) 149 ->setObjectPHID($object_phid); 150 151 if ($viewer_phid !== null) { 152 $storage->setViewerPHID($viewer_phid); 153 } else { 154 $storage->makeEphemeral(); 155 } 156 } 157 158 return $storage; 159 } 160 161 private function saveViewStateStorage() { 162 if (PhabricatorEnv::isReadOnly()) { 163 return; 164 } 165 166 $storage = $this->getStorage(); 167 168 $viewer_phid = $storage->getViewerPHID(); 169 if ($viewer_phid === null) { 170 return; 171 } 172 173 if (!$storage->getHasModifications()) { 174 return; 175 } 176 177 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 178 179 try { 180 $storage->save(); 181 } catch (AphrontDuplicateKeyQueryException $ex) { 182 // We may race another process to save view state. For now, just discard 183 // our state if we do. 184 } 185 186 unset($unguarded); 187 } 188 189 private function updateHiddenState(PhabricatorChangesetViewState $state) { 190 $is_hidden = false; 191 $was_modified = false; 192 193 $storage = $this->getStorage(); 194 $changeset = $this->getChangeset(); 195 196 $entries = $storage->getChangesetPropertyEntries($changeset, 'hidden'); 197 $entries = isort($entries, 'epoch'); 198 199 if ($entries) { 200 $other_spec = last($entries); 201 202 $this_version = (int)$changeset->getDiffID(); 203 $other_version = (int)idx($other_spec, 'diffID'); 204 $other_value = (bool)idx($other_spec, 'value', false); 205 $other_id = (int)idx($other_spec, 'changesetID'); 206 207 if ($other_value === false) { 208 $is_hidden = false; 209 } else if ($other_version >= $this_version) { 210 $is_hidden = $other_value; 211 } else { 212 $viewer = $this->getViewer(); 213 214 if ($other_id) { 215 $other_changeset = id(new DifferentialChangesetQuery()) 216 ->setViewer($viewer) 217 ->withIDs(array($other_id)) 218 ->executeOne(); 219 } else { 220 $other_changeset = null; 221 } 222 223 $is_modified = false; 224 if ($other_changeset) { 225 if (!$changeset->hasSameEffectAs($other_changeset)) { 226 $is_modified = true; 227 } 228 } 229 230 $is_hidden = false; 231 $was_modified = true; 232 } 233 } 234 235 $state->setHidden($is_hidden); 236 $state->setModifiedSinceHide($was_modified); 237 } 238 239}