mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-31 17:08:22 +01:00
Implement viewing versions and downloading patches in Phragment
Summary: This adds support for viewing individual versions on a fragment as well as comparing versions and downloading diff_match_patch-based patches. It does not use the side-by-side diff format as while it works for small changes, it quickly becomes impossible to distingush what changes have been made due to the diff_match_patch format. Test Plan: Clicked on versions and downloaded patches. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley CC: Korvin, epriestley, aran Maniphest Tasks: T4205 Differential Revision: https://secure.phabricator.com/D7734
This commit is contained in:
parent
5728ef7836
commit
c48cfb31bc
8 changed files with 302 additions and 2 deletions
|
@ -2186,8 +2186,10 @@ phutil_register_library_map(array(
|
||||||
'PhragmentHistoryController' => 'applications/phragment/controller/PhragmentHistoryController.php',
|
'PhragmentHistoryController' => 'applications/phragment/controller/PhragmentHistoryController.php',
|
||||||
'PhragmentPHIDTypeFragment' => 'applications/phragment/phid/PhragmentPHIDTypeFragment.php',
|
'PhragmentPHIDTypeFragment' => 'applications/phragment/phid/PhragmentPHIDTypeFragment.php',
|
||||||
'PhragmentPHIDTypeFragmentVersion' => 'applications/phragment/phid/PhragmentPHIDTypeFragmentVersion.php',
|
'PhragmentPHIDTypeFragmentVersion' => 'applications/phragment/phid/PhragmentPHIDTypeFragmentVersion.php',
|
||||||
|
'PhragmentPatchController' => 'applications/phragment/controller/PhragmentPatchController.php',
|
||||||
'PhragmentPatchUtil' => 'applications/phragment/util/PhragmentPatchUtil.php',
|
'PhragmentPatchUtil' => 'applications/phragment/util/PhragmentPatchUtil.php',
|
||||||
'PhragmentUpdateController' => 'applications/phragment/controller/PhragmentUpdateController.php',
|
'PhragmentUpdateController' => 'applications/phragment/controller/PhragmentUpdateController.php',
|
||||||
|
'PhragmentVersionController' => 'applications/phragment/controller/PhragmentVersionController.php',
|
||||||
'PhragmentZIPController' => 'applications/phragment/controller/PhragmentZIPController.php',
|
'PhragmentZIPController' => 'applications/phragment/controller/PhragmentZIPController.php',
|
||||||
'PhrequentController' => 'applications/phrequent/controller/PhrequentController.php',
|
'PhrequentController' => 'applications/phrequent/controller/PhrequentController.php',
|
||||||
'PhrequentDAO' => 'applications/phrequent/storage/PhrequentDAO.php',
|
'PhrequentDAO' => 'applications/phrequent/storage/PhrequentDAO.php',
|
||||||
|
@ -4786,8 +4788,10 @@ phutil_register_library_map(array(
|
||||||
'PhragmentHistoryController' => 'PhragmentController',
|
'PhragmentHistoryController' => 'PhragmentController',
|
||||||
'PhragmentPHIDTypeFragment' => 'PhabricatorPHIDType',
|
'PhragmentPHIDTypeFragment' => 'PhabricatorPHIDType',
|
||||||
'PhragmentPHIDTypeFragmentVersion' => 'PhabricatorPHIDType',
|
'PhragmentPHIDTypeFragmentVersion' => 'PhabricatorPHIDType',
|
||||||
|
'PhragmentPatchController' => 'PhragmentController',
|
||||||
'PhragmentPatchUtil' => 'Phobject',
|
'PhragmentPatchUtil' => 'Phobject',
|
||||||
'PhragmentUpdateController' => 'PhragmentController',
|
'PhragmentUpdateController' => 'PhragmentController',
|
||||||
|
'PhragmentVersionController' => 'PhragmentController',
|
||||||
'PhragmentZIPController' => 'PhragmentController',
|
'PhragmentZIPController' => 'PhragmentController',
|
||||||
'PhrequentController' => 'PhabricatorController',
|
'PhrequentController' => 'PhabricatorController',
|
||||||
'PhrequentDAO' => 'PhabricatorLiskDAO',
|
'PhrequentDAO' => 'PhabricatorLiskDAO',
|
||||||
|
|
|
@ -39,6 +39,8 @@ final class PhabricatorApplicationPhragment extends PhabricatorApplication {
|
||||||
'update/(?P<dblob>.*)' => 'PhragmentUpdateController',
|
'update/(?P<dblob>.*)' => 'PhragmentUpdateController',
|
||||||
'history/(?P<dblob>.*)' => 'PhragmentHistoryController',
|
'history/(?P<dblob>.*)' => 'PhragmentHistoryController',
|
||||||
'zip/(?P<dblob>.*)' => 'PhragmentZIPController',
|
'zip/(?P<dblob>.*)' => 'PhragmentZIPController',
|
||||||
|
'version/(?P<id>[0-9]*)/' => 'PhragmentVersionController',
|
||||||
|
'patch/(?P<aid>[0-9x]*)/(?P<bid>[0-9]*)/' => 'PhragmentPatchController',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ final class PhragmentHistoryController extends PhragmentController {
|
||||||
foreach ($versions as $version) {
|
foreach ($versions as $version) {
|
||||||
$item = id(new PHUIObjectItemView());
|
$item = id(new PHUIObjectItemView());
|
||||||
$item->setHeader('Version '.$version->getSequence());
|
$item->setHeader('Version '.$version->getSequence());
|
||||||
|
$item->setHref($version->getURI());
|
||||||
$item->addAttribute(phabricator_datetime(
|
$item->addAttribute(phabricator_datetime(
|
||||||
$version->getDateCreated(),
|
$version->getDateCreated(),
|
||||||
$viewer));
|
$viewer));
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhragmentPatchController extends PhragmentController {
|
||||||
|
|
||||||
|
private $aid;
|
||||||
|
private $bid;
|
||||||
|
|
||||||
|
public function willProcessRequest(array $data) {
|
||||||
|
$this->aid = idx($data, "aid", 0);
|
||||||
|
$this->bid = idx($data, "bid", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$viewer = $request->getUser();
|
||||||
|
|
||||||
|
// If "aid" is "x", then it means the user wants to generate
|
||||||
|
// a patch of an empty file to the version specified by "bid".
|
||||||
|
|
||||||
|
$ids = array($this->aid, $this->bid);
|
||||||
|
if ($this->aid === "x") {
|
||||||
|
$ids = array($this->bid);
|
||||||
|
}
|
||||||
|
|
||||||
|
$versions = id(new PhragmentFragmentVersionQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs($ids)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$version_a = null;
|
||||||
|
if ($this->aid !== "x") {
|
||||||
|
$version_a = idx($versions, $this->aid, null);
|
||||||
|
if ($version_a === null) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$version_b = idx($versions, $this->bid, null);
|
||||||
|
if ($version_b === null) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$file_phids = array();
|
||||||
|
if ($version_a !== null) {
|
||||||
|
$file_phids[] = $version_a->getFilePHID();
|
||||||
|
}
|
||||||
|
$file_phids[] = $version_b->getFilePHID();
|
||||||
|
|
||||||
|
$files = id(new PhabricatorFileQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs($file_phids)
|
||||||
|
->execute();
|
||||||
|
$files = mpull($files, null, 'getPHID');
|
||||||
|
|
||||||
|
$file_a = null;
|
||||||
|
if ($version_a != null) {
|
||||||
|
$file_a = idx($files, $version_a->getFilePHID(), null);
|
||||||
|
}
|
||||||
|
$file_b = idx($files, $version_b->getFilePHID(), null);
|
||||||
|
|
||||||
|
$patch = PhragmentPatchUtil::calculatePatch($file_a, $file_b);
|
||||||
|
|
||||||
|
if ($patch === null) {
|
||||||
|
throw new Exception("Unable to compute patch!");
|
||||||
|
}
|
||||||
|
|
||||||
|
$a_sequence = 'x';
|
||||||
|
if ($version_a !== null) {
|
||||||
|
$a_sequence = $version_a->getSequence();
|
||||||
|
}
|
||||||
|
|
||||||
|
$name =
|
||||||
|
$version_b->getFragment()->getName().'.'.
|
||||||
|
$a_sequence.'.'.
|
||||||
|
$version_b->getSequence().'.patch';
|
||||||
|
|
||||||
|
$result = PhabricatorFile::buildFromFileDataOrHash(
|
||||||
|
$patch,
|
||||||
|
array(
|
||||||
|
'name' => $name,
|
||||||
|
'mime-type' => 'text/plain',
|
||||||
|
'ttl' => time() + 60 * 60 * 24,
|
||||||
|
));
|
||||||
|
return id(new AphrontRedirectResponse())
|
||||||
|
->setURI($result->getBestURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhragmentVersionController extends PhragmentController {
|
||||||
|
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
public function willProcessRequest(array $data) {
|
||||||
|
$this->id = idx($data, "id", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$viewer = $request->getUser();
|
||||||
|
|
||||||
|
$version = id(new PhragmentFragmentVersionQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($this->id))
|
||||||
|
->executeOne();
|
||||||
|
if ($version === null) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$parents = $this->loadParentFragments($version->getFragment()->getPath());
|
||||||
|
if ($parents === null) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
$current = idx($parents, count($parents) - 1, null);
|
||||||
|
|
||||||
|
$crumbs = $this->buildApplicationCrumbsWithPath($parents);
|
||||||
|
$crumbs->addCrumb(
|
||||||
|
id(new PhabricatorCrumbView())
|
||||||
|
->setName(pht('View Version %d', $version->getSequence())));
|
||||||
|
|
||||||
|
$phids = array();
|
||||||
|
$phids[] = $version->getFilePHID();
|
||||||
|
|
||||||
|
$this->loadHandles($phids);
|
||||||
|
|
||||||
|
$file = id(new PhabricatorFileQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs(array($version->getFilePHID()))
|
||||||
|
->executeOne();
|
||||||
|
if ($file !== null) {
|
||||||
|
$file_uri = $file->getBestURI();
|
||||||
|
}
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader(pht(
|
||||||
|
"%s at version %d",
|
||||||
|
$version->getFragment()->getName(),
|
||||||
|
$version->getSequence()))
|
||||||
|
->setPolicyObject($version)
|
||||||
|
->setUser($viewer);
|
||||||
|
|
||||||
|
$actions = id(new PhabricatorActionListView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setObject($version)
|
||||||
|
->setObjectURI($version->getURI());
|
||||||
|
$actions->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setName(pht('Download Version'))
|
||||||
|
->setHref($file_uri)
|
||||||
|
->setDisabled($file === null)
|
||||||
|
->setIcon('download'));
|
||||||
|
|
||||||
|
$properties = id(new PHUIPropertyListView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setObject($version)
|
||||||
|
->setActionList($actions);
|
||||||
|
$properties->addProperty(
|
||||||
|
pht('File'),
|
||||||
|
$this->renderHandlesForPHIDs(array($version->getFilePHID())));
|
||||||
|
|
||||||
|
$box = id(new PHUIObjectBoxView())
|
||||||
|
->setHeader($header)
|
||||||
|
->addPropertyList($properties);
|
||||||
|
|
||||||
|
return $this->buildApplicationPage(
|
||||||
|
array(
|
||||||
|
$crumbs,
|
||||||
|
$box,
|
||||||
|
$this->renderPatchFromPreviousVersion($version, $file),
|
||||||
|
$this->renderPreviousVersionList($version)),
|
||||||
|
array(
|
||||||
|
'title' => pht('View Version'),
|
||||||
|
'device' => true));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderPatchFromPreviousVersion(
|
||||||
|
PhragmentFragmentVersion $version,
|
||||||
|
PhabricatorFile $file) {
|
||||||
|
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$viewer = $request->getUser();
|
||||||
|
|
||||||
|
$previous_file = null;
|
||||||
|
$previous = id(new PhragmentFragmentVersionQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withFragmentPHIDs(array($version->getFragmentPHID()))
|
||||||
|
->withSequences(array($version->getSequence() - 1))
|
||||||
|
->executeOne();
|
||||||
|
if ($previous !== null) {
|
||||||
|
$previous_file = id(new PhabricatorFileQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs(array($previous->getFilePHID()))
|
||||||
|
->executeOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
$patch = PhragmentPatchUtil::calculatePatch($previous_file, $file);
|
||||||
|
|
||||||
|
if ($patch === null) {
|
||||||
|
return id(new AphrontErrorView())
|
||||||
|
->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
|
||||||
|
->setTitle(pht("Identical Version"))
|
||||||
|
->appendChild(phutil_tag(
|
||||||
|
'p',
|
||||||
|
array(),
|
||||||
|
pht("This version is identical to the previous version.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($patch) > 20480) {
|
||||||
|
// Patch is longer than 20480 characters. Trim it and let the user know.
|
||||||
|
$patch = substr($patch, 0, 20480)."\n...\n";
|
||||||
|
$patch .= pht(
|
||||||
|
"This patch is longer than 20480 characters. Use the link ".
|
||||||
|
"in the action list to download the full patch.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return id(new PHUIObjectBoxView())
|
||||||
|
->setHeader(id(new PHUIHeaderView())
|
||||||
|
->setHeader(pht('Differences since previous version')))
|
||||||
|
->appendChild(id(new PhabricatorSourceCodeView())
|
||||||
|
->setLines(phutil_split_lines($patch)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderPreviousVersionList(
|
||||||
|
PhragmentFragmentVersion $version) {
|
||||||
|
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$viewer = $request->getUser();
|
||||||
|
|
||||||
|
$previous_versions = id(new PhragmentFragmentVersionQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withFragmentPHIDs(array($version->getFragmentPHID()))
|
||||||
|
->withSequenceBefore($version->getSequence())
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$list = id(new PHUIObjectItemListView())
|
||||||
|
->setUser($viewer);
|
||||||
|
|
||||||
|
foreach ($previous_versions as $previous_version) {
|
||||||
|
$item = id(new PHUIObjectItemView());
|
||||||
|
$item->setHeader('Version '.$previous_version->getSequence());
|
||||||
|
$item->setHref($previous_version->getURI());
|
||||||
|
$item->addAttribute(phabricator_datetime(
|
||||||
|
$previous_version->getDateCreated(),
|
||||||
|
$viewer));
|
||||||
|
$item->addAction(id(new PHUIListItemView())
|
||||||
|
->setIcon('patch')
|
||||||
|
->setName(pht("Get Patch"))
|
||||||
|
->setHref($this->getApplicationURI(
|
||||||
|
'patch/'.$previous_version->getID().'/'.$version->getID())));
|
||||||
|
$list->addItem($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = id(new PHUIObjectItemView());
|
||||||
|
$item->setHeader('Prior to Version 0');
|
||||||
|
$item->addAttribute('Prior to any content (empty file)');
|
||||||
|
$item->addAction(id(new PHUIListItemView())
|
||||||
|
->setIcon('patch')
|
||||||
|
->setName(pht("Get Patch"))
|
||||||
|
->setHref($this->getApplicationURI(
|
||||||
|
'patch/x/'.$version->getID())));
|
||||||
|
$list->addItem($item);
|
||||||
|
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,6 +6,8 @@ final class PhragmentFragmentVersionQuery
|
||||||
private $ids;
|
private $ids;
|
||||||
private $phids;
|
private $phids;
|
||||||
private $fragmentPHIDs;
|
private $fragmentPHIDs;
|
||||||
|
private $sequences;
|
||||||
|
private $sequenceBefore;
|
||||||
|
|
||||||
public function withIDs(array $ids) {
|
public function withIDs(array $ids) {
|
||||||
$this->ids = $ids;
|
$this->ids = $ids;
|
||||||
|
@ -22,6 +24,16 @@ final class PhragmentFragmentVersionQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withSequences(array $sequences) {
|
||||||
|
$this->sequences = $sequences;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withSequenceBefore($current) {
|
||||||
|
$this->sequenceBefore = $current;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function loadPage() {
|
public function loadPage() {
|
||||||
$table = new PhragmentFragmentVersion();
|
$table = new PhragmentFragmentVersion();
|
||||||
$conn_r = $table->establishConnection('r');
|
$conn_r = $table->establishConnection('r');
|
||||||
|
@ -61,6 +73,20 @@ final class PhragmentFragmentVersionQuery
|
||||||
$this->fragmentPHIDs);
|
$this->fragmentPHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->sequences) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'sequence IN (%Ld)',
|
||||||
|
$this->sequences);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->sequenceBefore !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'sequence < %d',
|
||||||
|
$this->sequenceBefore);
|
||||||
|
}
|
||||||
|
|
||||||
$where[] = $this->buildPagingClause($conn_r);
|
$where[] = $this->buildPagingClause($conn_r);
|
||||||
|
|
||||||
return $this->formatWhereClause($where);
|
return $this->formatWhereClause($where);
|
||||||
|
|
|
@ -22,7 +22,7 @@ final class PhragmentFragmentVersion extends PhragmentDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getURI() {
|
public function getURI() {
|
||||||
return '/phragment/patch/'.$this->getID().'/';
|
return '/phragment/version/'.$this->getID().'/';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFragment() {
|
public function getFragment() {
|
||||||
|
|
|
@ -9,7 +9,7 @@ final class PhragmentPatchUtil extends Phobject {
|
||||||
*
|
*
|
||||||
* @phutil-external-symbol class diff_match_patch
|
* @phutil-external-symbol class diff_match_patch
|
||||||
*/
|
*/
|
||||||
public function calculatePatch(
|
public static function calculatePatch(
|
||||||
PhabricatorFile $old = null,
|
PhabricatorFile $old = null,
|
||||||
PhabricatorFile $new = null) {
|
PhabricatorFile $new = null) {
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue