diff --git a/src/applications/differential/storage/DifferentialViewState.php b/src/applications/differential/storage/DifferentialViewState.php index 101061f379..44c1f308d8 100644 --- a/src/applications/differential/storage/DifferentialViewState.php +++ b/src/applications/differential/storage/DifferentialViewState.php @@ -1,145 +1,150 @@ array( 'viewState' => self::SERIALIZATION_JSON, ), self::CONFIG_KEY_SCHEMA => array( 'key_viewer' => array( 'columns' => array('viewerPHID', 'objectPHID'), 'unique' => true, ), 'key_object' => array( 'columns' => array('objectPHID'), ), 'key_modified' => array( 'columns' => array('dateModified'), ), ), ) + parent::getConfiguration(); } public function setChangesetProperty( DifferentialChangeset $changeset, $key, $value) { if ($this->getChangesetProperty($changeset, $key) === $value) { return; } $properties = array( 'value' => $value, 'epoch' => PhabricatorTime::getNow(), ); $diff_id = $changeset->getDiffID(); if ($diff_id !== null) { $properties['diffID'] = (int)$diff_id; } + $changeset_id = $changeset->getID(); + if ($changeset_id !== null) { + $properties['changesetID'] = (int)$changeset_id; + } + $path_hash = $this->getChangesetPathHash($changeset); $changeset_phid = $this->getChangesetKey($changeset); $this->hasModifications = true; $this->viewState['changesets'][$path_hash][$key][$changeset_phid] = $properties; } public function getChangesetProperty( DifferentialChangeset $changeset, $key, $default = null) { $entries = $this->getChangesetPropertyEntries( $changeset, $key); $entries = isort($entries, 'epoch'); $entry = last($entries); if (!is_array($entry)) { $entry = array(); } return idx($entry, 'value', $default); } public function getChangesetPropertyEntries( DifferentialChangeset $changeset, $key) { $path_hash = $this->getChangesetPathHash($changeset); $entries = idxv($this->viewState, array('changesets', $path_hash, $key)); if (!is_array($entries)) { $entries = array(); } return $entries; } public function getHasModifications() { return $this->hasModifications; } private function getChangesetPathHash(DifferentialChangeset $changeset) { $path = $changeset->getFilename(); return PhabricatorHash::digestForIndex($path); } private function getChangesetKey(DifferentialChangeset $changeset) { $key = $changeset->getID(); if ($key === null) { return '*'; } return (string)$key; } public static function copyViewStatesToObject($src_phid, $dst_phid) { $table = new self(); $conn = $table->establishConnection('w'); queryfx( $conn, 'INSERT IGNORE INTO %R (viewerPHID, objectPHID, viewState, dateCreated, dateModified) SELECT viewerPHID, %s, viewState, dateCreated, dateModified FROM %R WHERE objectPHID = %s', $table, $dst_phid, $table, $src_phid); } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, ); } public function getPolicy($capability) { return PhabricatorPolicies::POLICY_NOONE; } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { return ($viewer->getPHID() === $this->getViewerPHID()); } } diff --git a/src/infrastructure/diff/viewstate/PhabricatorChangesetViewStateEngine.php b/src/infrastructure/diff/viewstate/PhabricatorChangesetViewStateEngine.php index 2363f7f0ff..5979ca2a7b 100644 --- a/src/infrastructure/diff/viewstate/PhabricatorChangesetViewStateEngine.php +++ b/src/infrastructure/diff/viewstate/PhabricatorChangesetViewStateEngine.php @@ -1,235 +1,239 @@ viewer = $viewer; return $this; } public function getViewer() { return $this->viewer; } public function setObjectPHID($object_phid) { $this->objectPHID = $object_phid; return $this; } public function getObjectPHID() { return $this->objectPHID; } public function setChangeset(DifferentialChangeset $changeset) { $this->changeset = $changeset; return $this; } public function getChangeset() { return $this->changeset; } public function newViewStateFromRequest(AphrontRequest $request) { $storage = $this->loadViewStateStorage(); $this->setStorage($storage); $highlight = $request->getStr('highlight'); if ($highlight !== null) { $this->setChangesetProperty('highlight', $highlight); } $encoding = $request->getStr('encoding'); if ($encoding !== null) { $this->setChangesetProperty('encoding', $encoding); } $engine = $request->getStr('engine'); if ($engine !== null) { $this->setChangesetProperty('engine', $engine); } $renderer = $request->getStr('renderer'); if ($renderer !== null) { $this->setChangesetProperty('renderer', $renderer); } $hidden = $request->getStr('hidden'); if ($hidden !== null) { $this->setChangesetProperty('hidden', (int)$hidden); } $this->saveViewStateStorage(); $state = new PhabricatorChangesetViewState(); $highlight_language = $this->getChangesetProperty('highlight'); $state->setHighlightLanguage($highlight_language); $encoding = $this->getChangesetProperty('encoding'); $state->setCharacterEncoding($encoding); $document_engine = $this->getChangesetProperty('engine'); $state->setDocumentEngineKey($document_engine); $renderer = $this->getChangesetProperty('renderer'); $state->setRendererKey($renderer); $this->updateHiddenState($state); // This is the client-selected default renderer based on viewport // dimensions. $device_key = $request->getStr('device'); if ($device_key !== null) { $state->setDefaultDeviceRendererKey($device_key); } $discard_response = $request->getStr('discard'); if ($discard_response !== null) { $state->setDiscardResponse(true); } return $state; } private function setStorage(DifferentialViewState $storage) { $this->storage = $storage; return $this; } private function getStorage() { return $this->storage; } private function setChangesetProperty( $key, $value) { $storage = $this->getStorage(); $changeset = $this->getChangeset(); $storage->setChangesetProperty($changeset, $key, $value); } private function getChangesetProperty( $key, $default = null) { $storage = $this->getStorage(); $changeset = $this->getChangeset(); return $storage->getChangesetProperty($changeset, $key, $default); } private function loadViewStateStorage() { $viewer = $this->getViewer(); $object_phid = $this->getObjectPHID(); $viewer_phid = $viewer->getPHID(); $storage = null; if ($viewer_phid !== null) { $storage = id(new DifferentialViewStateQuery()) ->setViewer($viewer) ->withViewerPHIDs(array($viewer_phid)) ->withObjectPHIDs(array($object_phid)) ->executeOne(); } if ($storage === null) { $storage = id(new DifferentialViewState()) ->setObjectPHID($object_phid); if ($viewer_phid !== null) { $storage->setViewerPHID($viewer_phid); } else { $storage->makeEphemeral(); } } return $storage; } private function saveViewStateStorage() { if (PhabricatorEnv::isReadOnly()) { return; } $storage = $this->getStorage(); $viewer_phid = $storage->getViewerPHID(); if ($viewer_phid === null) { return; } if (!$storage->getHasModifications()) { return; } $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); try { $storage->save(); } catch (AphrontDuplicateKeyQueryException $ex) { // We may race another process to save view state. For now, just discard // our state if we do. } unset($unguarded); } private function updateHiddenState(PhabricatorChangesetViewState $state) { $is_hidden = false; $was_modified = false; $storage = $this->getStorage(); $changeset = $this->getChangeset(); $entries = $storage->getChangesetPropertyEntries($changeset, 'hidden'); $entries = isort($entries, 'epoch'); if ($entries) { - $other_key = last_key($entries); $other_spec = last($entries); $this_version = (int)$changeset->getDiffID(); $other_version = (int)idx($other_spec, 'diffID'); $other_value = (bool)idx($other_spec, 'value', false); + $other_id = (int)idx($other_spec, 'changesetID'); if ($other_value === false) { $is_hidden = false; } else if ($other_version >= $this_version) { $is_hidden = $other_value; } else { $viewer = $this->getViewer(); - $other_changeset = id(new DifferentialChangesetQuery()) - ->setViewer($viewer) - ->withIDs(array($other_key)) - ->executeOne(); + if ($other_id) { + $other_changeset = id(new DifferentialChangesetQuery()) + ->setViewer($viewer) + ->withIDs(array($other_id)) + ->executeOne(); + } else { + $other_changeset = null; + } $is_modified = false; if ($other_changeset) { if (!$changeset->hasSameEffectAs($other_changeset)) { $is_modified = true; } } $is_hidden = false; $was_modified = true; } } $state->setHidden($is_hidden); $state->setModifiedSinceHide($was_modified); } }