1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-09 16:32:39 +01:00

Phragment v0

Summary: Ref T4205.  This is an initial implementation of Phragment.  You can create and browse fragments in the system (but you can't yet view a fragment's patches / history).

Test Plan: Clicked around and created fragments.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley

CC: Korvin, epriestley, aran

Maniphest Tasks: T4205

Differential Revision: https://secure.phabricator.com/D7726
This commit is contained in:
James Rhodes 2013-12-07 12:43:49 +11:00
parent 9d474452f9
commit 4c143ad3b2
16 changed files with 3047 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,24 @@
CREATE TABLE {$NAMESPACE}_phragment.phragment_fragment (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
path VARCHAR(254) NOT NULL COLLATE utf8_bin,
depth INT UNSIGNED NOT NULL,
latestVersionPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (phid),
UNIQUE KEY `key_path` (path)
) ENGINE=InnoDB, COLLATE utf8_general_ci;
CREATE TABLE {$NAMESPACE}_phragment.phragment_fragmentversion (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
sequence INT UNSIGNED NOT NULL,
fragmentPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
filePHID VARCHAR(64) NULL COLLATE utf8_bin,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_version` (fragmentPHID, sequence)
) ENGINE=InnoDB, COLLATE utf8_general_ci;

View file

@ -1053,6 +1053,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationPhlux' => 'applications/phlux/application/PhabricatorApplicationPhlux.php',
'PhabricatorApplicationPholio' => 'applications/pholio/application/PhabricatorApplicationPholio.php',
'PhabricatorApplicationPhortune' => 'applications/phortune/application/PhabricatorApplicationPhortune.php',
'PhabricatorApplicationPhragment' => 'applications/phragment/application/PhabricatorApplicationPhragment.php',
'PhabricatorApplicationPhrequent' => 'applications/phrequent/application/PhabricatorApplicationPhrequent.php',
'PhabricatorApplicationPhriction' => 'applications/phriction/application/PhabricatorApplicationPhriction.php',
'PhabricatorApplicationPolicy' => 'applications/policy/application/PhabricatorApplicationPolicy.php',
@ -2173,6 +2174,17 @@ phutil_register_library_map(array(
'PhortuneTestExtraPaymentProvider' => 'applications/phortune/provider/__tests__/PhortuneTestExtraPaymentProvider.php',
'PhortuneTestPaymentProvider' => 'applications/phortune/provider/PhortuneTestPaymentProvider.php',
'PhortuneWePayPaymentProvider' => 'applications/phortune/provider/PhortuneWePayPaymentProvider.php',
'PhragmentBrowseController' => 'applications/phragment/controller/PhragmentBrowseController.php',
'PhragmentController' => 'applications/phragment/controller/PhragmentController.php',
'PhragmentCreateController' => 'applications/phragment/controller/PhragmentCreateController.php',
'PhragmentDAO' => 'applications/phragment/storage/PhragmentDAO.php',
'PhragmentFragment' => 'applications/phragment/storage/PhragmentFragment.php',
'PhragmentFragmentQuery' => 'applications/phragment/query/PhragmentFragmentQuery.php',
'PhragmentFragmentVersion' => 'applications/phragment/storage/PhragmentFragmentVersion.php',
'PhragmentFragmentVersionQuery' => 'applications/phragment/query/PhragmentFragmentVersionQuery.php',
'PhragmentPHIDTypeFragment' => 'applications/phragment/phid/PhragmentPHIDTypeFragment.php',
'PhragmentPHIDTypeFragmentVersion' => 'applications/phragment/phid/PhragmentPHIDTypeFragmentVersion.php',
'PhragmentPatchUtil' => 'applications/phragment/util/PhragmentPatchUtil.php',
'PhrequentController' => 'applications/phrequent/controller/PhrequentController.php',
'PhrequentDAO' => 'applications/phrequent/storage/PhrequentDAO.php',
'PhrequentListController' => 'applications/phrequent/controller/PhrequentListController.php',
@ -3488,6 +3500,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationPhlux' => 'PhabricatorApplication',
'PhabricatorApplicationPholio' => 'PhabricatorApplication',
'PhabricatorApplicationPhortune' => 'PhabricatorApplication',
'PhabricatorApplicationPhragment' => 'PhabricatorApplication',
'PhabricatorApplicationPhrequent' => 'PhabricatorApplication',
'PhabricatorApplicationPhriction' => 'PhabricatorApplication',
'PhabricatorApplicationPolicy' => 'PhabricatorApplication',
@ -4749,6 +4762,25 @@ phutil_register_library_map(array(
'PhortuneTestExtraPaymentProvider' => 'PhortunePaymentProvider',
'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
'PhortuneWePayPaymentProvider' => 'PhortunePaymentProvider',
'PhragmentBrowseController' => 'PhragmentController',
'PhragmentController' => 'PhabricatorController',
'PhragmentCreateController' => 'PhragmentController',
'PhragmentDAO' => 'PhabricatorLiskDAO',
'PhragmentFragment' =>
array(
0 => 'PhragmentDAO',
1 => 'PhabricatorPolicyInterface',
),
'PhragmentFragmentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhragmentFragmentVersion' =>
array(
0 => 'PhragmentDAO',
1 => 'PhabricatorPolicyInterface',
),
'PhragmentFragmentVersionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhragmentPHIDTypeFragment' => 'PhabricatorPHIDType',
'PhragmentPHIDTypeFragmentVersion' => 'PhabricatorPHIDType',
'PhragmentPatchUtil' => 'Phobject',
'PhrequentController' => 'PhabricatorController',
'PhrequentDAO' => 'PhabricatorLiskDAO',
'PhrequentListController' =>

View file

@ -0,0 +1,44 @@
<?php
final class PhabricatorApplicationPhragment extends PhabricatorApplication {
public function getBaseURI() {
return '/phragment/';
}
public function getShortDescription() {
return pht('Versioned Artifact Storage');
}
public function getIconName() {
return 'phragment';
}
public function getTitleGlyph() {
return "\xE2\x26\xB6";
}
public function getApplicationGroup() {
return self::GROUP_UTILITIES;
}
public function isBeta() {
return true;
}
public function canUninstall() {
return true;
}
public function getRoutes() {
return array(
'/phragment/' => array(
'' => 'PhragmentBrowseController',
'browse/(?P<dblob>.*)' => 'PhragmentBrowseController',
'create/(?P<dblob>.*)' => 'PhragmentCreateController',
),
);
}
}

View file

@ -0,0 +1,108 @@
<?php
final class PhragmentBrowseController extends PhragmentController {
private $dblob;
public function willProcessRequest(array $data) {
$this->dblob = idx($data, "dblob", "");
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$parents = $this->loadParentFragments($this->dblob);
if ($parents === null) {
return new Aphront404Response();
}
$current = nonempty(last($parents), null);
$path = '';
if ($current !== null) {
$path = $current->getPath();
}
$crumbs = $this->buildApplicationCrumbsWithPath($parents);
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('Create Fragment'))
->setHref($this->getApplicationURI('/create/'.$path))
->setIcon('create'));
$current_box = $this->createCurrentFragmentView($current);
$list = id(new PHUIObjectItemListView())
->setUser($viewer);
$fragments = null;
if ($current === null) {
// Find all root fragments.
$fragments = id(new PhragmentFragmentQuery())
->setViewer($this->getRequest()->getUser())
->needLatestVersion(true)
->withDepths(array(1))
->execute();
} else {
// Find all child fragments.
$fragments = id(new PhragmentFragmentQuery())
->setViewer($this->getRequest()->getUser())
->needLatestVersion(true)
->withLeadingPath($current->getPath().'/')
->withDepths(array($current->getDepth() + 1))
->execute();
}
foreach ($fragments as $fragment) {
$item = id(new PHUIObjectItemView());
$item->setHeader($fragment->getName());
$item->setHref($this->getApplicationURI('/browse/'.$fragment->getPath()));
$item->addAttribute(pht(
'Last Updated %s',
phabricator_datetime(
$fragment->getLatestVersion()->getDateCreated(),
$viewer)));
$item->addAttribute(pht(
'Latest Version %s',
$fragment->getLatestVersion()->getSequence()));
$list->addItem($item);
}
return $this->buildApplicationPage(
array(
$crumbs,
$current_box,
$list),
array(
'title' => pht('Browse Phragments'),
'device' => true));
}
private function createCurrentFragmentView($fragment) {
if ($fragment === null) {
return null;
}
$viewer = $this->getRequest()->getUser();
$header = id(new PHUIHeaderView())
->setHeader($fragment->getName())
->setPolicyObject($fragment)
->setUser($viewer);
$properties = new PHUIPropertyListView();
$phids = array();
$phids[] = $fragment->getLatestVersionPHID();
$this->loadHandles($phids);
$properties->addProperty(
pht('Latest Version'),
$this->renderHandlesForPHIDs(array($fragment->getLatestVersionPHID())));
return id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
}
}

View file

@ -0,0 +1,56 @@
<?php
abstract class PhragmentController extends PhabricatorController {
protected function loadParentFragments($path) {
$components = explode('/', $path);
$combinations = array();
$current = '';
foreach ($components as $component) {
$current .= '/'.$component;
$current = trim($current, '/');
if (trim($current) === '') {
continue;
}
$combinations[] = $current;
}
$fragments = array();
$results = id(new PhragmentFragmentQuery())
->setViewer($this->getRequest()->getUser())
->withPaths($combinations)
->execute();
foreach ($combinations as $combination) {
$found = false;
foreach ($results as $fragment) {
if ($fragment->getPath() === $combination) {
$fragments[] = $fragment;
$found = true;
break;
}
}
if (!$found) {
return null;
}
}
return $fragments;
}
protected function buildApplicationCrumbsWithPath(array $fragments) {
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName('/')
->setHref('/phragment/'));
foreach ($fragments as $parent) {
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName($parent->getName())
->setHref('/phragment/browse/'.$parent->getPath()));
}
return $crumbs;
}
}

View file

@ -0,0 +1,141 @@
<?php
final class PhragmentCreateController extends PhragmentController {
private $dblob;
public function willProcessRequest(array $data) {
$this->dblob = idx($data, "dblob", "");
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$parent = null;
$parents = $this->loadParentFragments($this->dblob);
if ($parents === null) {
return new Aphront404Response();
}
if (count($parents) !== 0) {
$parent = idx($parents, count($parents) - 1, null);
}
$parent_path = '';
if ($parent !== null) {
$parent_path = $parent->getPath();
}
$parent_path = trim($parent_path, '/');
$fragment = id(new PhragmentFragment());
$error_view = null;
if ($request->isFormPost()) {
$errors = array();
$v_name = $request->getStr('name');
$v_fileid = $request->getInt('fileID');
$v_viewpolicy = $request->getStr('viewPolicy');
$v_editpolicy = $request->getStr('editPolicy');
if (strpos($v_name, '/') !== false) {
$errors[] = pht('The fragment name can not contain \'/\'.');
}
$file = id(new PhabricatorFile())->load($v_fileid);
if ($file === null) {
$errors[] = pht('The specified file doesn\'t exist.');
}
if (!count($errors)) {
$depth = 1;
if ($parent !== null) {
$depth = $parent->getDepth() + 1;
}
$version = id(new PhragmentFragmentVersion());
$version->setSequence(0);
$version->setFragmentPHID(''); // Can't set this yet...
$version->setFilePHID($file->getPHID());
$version->save();
$fragment->setPath(trim($parent_path.'/'.$v_name, '/'));
$fragment->setDepth($depth);
$fragment->setLatestVersionPHID($version->getPHID());
$fragment->setViewPolicy($v_viewpolicy);
$fragment->setEditPolicy($v_editpolicy);
$fragment->save();
$version->setFragmentPHID($fragment->getPHID());
$version->save();
return id(new AphrontRedirectResponse())
->setURI('/phragment/browse/'.trim($parent_path.'/'.$v_name, '/'));
} else {
$error_view = id(new AphrontErrorView())
->setErrors($errors)
->setTitle(pht('Errors while creating fragment'));
}
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($fragment)
->execute();
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Parent Path'))
->setDisabled(true)
->setValue('/'.trim($parent_path.'/', '/')))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name'))
->setName('name'))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('File ID'))
->setName('fileID'))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($viewer)
->setName('viewPolicy')
->setPolicyObject($fragment)
->setPolicies($policies)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($viewer)
->setName('editPolicy')
->setPolicyObject($fragment)
->setPolicies($policies)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Create Fragment'))
->addCancelButton(
$this->getApplicationURI('browse/'.$parent_path)));
$crumbs = $this->buildApplicationCrumbsWithPath($parents);
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht('Create Fragment')));
$box = id(new PHUIObjectBoxView())
->setHeaderText('Create Fragment')
->setValidationException(null)
->setForm($form);
return $this->buildApplicationPage(
array(
$crumbs,
$box),
array(
'title' => pht('Create Phragment'),
'device' => true));
}
}

View file

@ -0,0 +1,46 @@
<?php
final class PhragmentPHIDTypeFragment
extends PhabricatorPHIDType {
const TYPECONST = 'PHRF';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Fragment');
}
public function newObject() {
return new PhragmentFragment();
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhragmentFragmentQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
$viewer = $query->getViewer();
foreach ($handles as $phid => $handle) {
$fragment = $objects[$phid];
$handle->setName($fragment->getID());
$handle->setURI($fragment->getURI());
}
}
public function canLoadNamedObject($name) {
return false;
}
}

View file

@ -0,0 +1,46 @@
<?php
final class PhragmentPHIDTypeFragmentVersion
extends PhabricatorPHIDType {
const TYPECONST = 'PHRV';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Fragment Version');
}
public function newObject() {
return new PhragmentFragmentVersion();
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhragmentFragmentVersionQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
$viewer = $query->getViewer();
foreach ($handles as $phid => $handle) {
$version = $objects[$phid];
$handle->setName($version->getSequence());
$handle->setURI($version->getURI());
}
}
public function canLoadNamedObject($name) {
return false;
}
}

View file

@ -0,0 +1,131 @@
<?php
final class PhragmentFragmentQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $paths;
private $leadingPath;
private $depths;
private $needsLatestVersion;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withPaths(array $paths) {
$this->paths = $paths;
return $this;
}
public function withLeadingPath($path) {
$this->leadingPath = $path;
return $this;
}
public function withDepths($depths) {
$this->depths = $depths;
return $this;
}
public function needLatestVersion($need_latest_version) {
$this->needsLatestVersion = $need_latest_version;
return $this;
}
public function loadPage() {
$table = new PhragmentFragment();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
protected function buildWhereClause($conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->paths) {
$where[] = qsprintf(
$conn_r,
'path IN (%Ls)',
$this->paths);
}
if ($this->leadingPath) {
$where[] = qsprintf(
$conn_r,
'path LIKE %>',
$this->leadingPath);
}
if ($this->depths) {
$where[] = qsprintf(
$conn_r,
'depth IN (%Ld)',
$this->depths);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
protected function didFilterPage(array $page) {
if ($this->needsLatestVersion) {
$versions = array();
$version_phids = array_filter(mpull($page, 'getLatestVersionPHID'));
if ($version_phids) {
$versions = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->withPHIDs($version_phids)
->setParentQuery($this)
->execute();
$versions = mpull($versions, null, 'getPHID');
}
foreach ($page as $key => $fragment) {
$version_phid = $fragment->getLatestVersionPHID();
if (empty($versions[$version_phid])) {
unset($page[$key]);
continue;
}
$fragment->attachLatestVersion($versions[$version_phid]);
}
}
return $page;
}
public function getQueryApplicationClass() {
return 'PhabricatorApplicationPhragment';
}
}

View file

@ -0,0 +1,97 @@
<?php
final class PhragmentFragmentVersionQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $fragmentPHIDs;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withFragmentPHIDs(array $fragment_phids) {
$this->fragmentPHIDs = $fragment_phids;
return $this;
}
public function loadPage() {
$table = new PhragmentFragmentVersion();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
protected function buildWhereClause($conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->fragmentPHIDs) {
$where[] = qsprintf(
$conn_r,
'fragmentPHID IN (%Ls)',
$this->fragmentPHIDs);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
protected function willFilterPage(array $page) {
$fragments = array();
$fragment_phids = array_filter(mpull($page, 'getFragmentPHID'));
if ($fragment_phids) {
$fragments = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->withPHIDs($fragment_phids)
->setParentQuery($this)
->execute();
$fragments = mpull($fragments, null, 'getPHID');
}
foreach ($page as $key => $version) {
$fragment_phid = $version->getFragmentPHID();
if (empty($fragments[$fragment_phid])) {
unset($page[$key]);
continue;
}
$version->attachFragment($fragments[$fragment_phid]);
}
return $page;
}
public function getQueryApplicationClass() {
return 'PhabricatorApplicationPhragment';
}
}

View file

@ -0,0 +1,9 @@
<?php
abstract class PhragmentDAO extends PhabricatorLiskDAO {
public function getApplicationName() {
return 'phragment';
}
}

View file

@ -0,0 +1,73 @@
<?php
final class PhragmentFragment extends PhragmentDAO
implements PhabricatorPolicyInterface {
protected $path;
protected $depth;
protected $latestVersionPHID;
protected $viewPolicy;
protected $editPolicy;
private $latestVersion = self::ATTACHABLE;
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhragmentPHIDTypeFragment::TYPECONST);
}
public function getURI() {
return '/phragment/fragment/'.$this->getID().'/';
}
public function getName() {
return basename($this->path);
}
public function getFile() {
return $this->assertAttached($this->file);
}
public function attachFile(PhabricatorFile $file) {
return $this->file = $file;
}
public function getLatestVersion() {
return $this->assertAttached($this->latestVersion);
}
public function attachLatestVersion(PhragmentFragmentVersion $version) {
return $this->latestVersion = $version;
}
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
public function describeAutomaticCapability($capability) {
return null;
}
}

View file

@ -0,0 +1,62 @@
<?php
final class PhragmentFragmentVersion extends PhragmentDAO
implements PhabricatorPolicyInterface {
protected $sequence;
protected $fragmentPHID;
protected $filePHID;
private $fragment = self::ATTACHABLE;
private $file = self::ATTACHABLE;
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhragmentPHIDTypeFragmentVersion::TYPECONST);
}
public function getURI() {
return '/phragment/patch/'.$this->getID().'/';
}
public function getFragment() {
return $this->assertAttached($this->fragment);
}
public function attachFragment(PhragmentFragment $fragment) {
return $this->fragment = $fragment;
}
public function getFile() {
return $this->assertAttached($this->file);
}
public function attachFile(PhabricatorFile $file) {
return $this->file = $file;
}
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW
);
}
public function getPolicy($capability) {
return $this->getFragment()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getFragment()->hasAutomaticCapability($capability, $viewer);
}
public function describeAutomaticCapability($capability) {
return $this->getFragment()->describeAutomaticCapability($capability);
}
}

View file

@ -0,0 +1,53 @@
<?php
final class PhragmentPatchUtil extends Phobject {
const EMPTY_HASH = "0000000000000000000000000000000000000000";
/**
* Calculate the DiffMatchPatch patch between two Phabricator files.
*
* @phutil-external-symbol class diff_match_patch
*/
public function calculatePatch(
PhabricatorFile $old = null,
PhabricatorFile $new = null) {
$root = dirname(phutil_get_library_root('phabricator'));
require_once $root.'/externals/diff_match_patch/diff_match_patch.php';
$old_hash = self::EMPTY_HASH;
$new_hash = self::EMPTY_HASH;
if ($old !== null) {
$old_hash = $old->getContentHash();
}
if ($new !== null) {
$new_hash = $new->getContentHash();
}
$old_content = "";
$new_content = "";
if ($old_hash === $new_hash) {
return null;
}
if ($old_hash !== self::EMPTY_HASH) {
$old_content = $old->loadFileData();
} else {
$old_content = "";
}
if ($new_hash !== self::EMPTY_HASH) {
$new_content = $new->loadFileData();
} else {
$new_content = "";
}
$dmp = new diff_match_patch();
$dmp_patches = $dmp->patch_make($old_content, $new_content);
return $dmp->patch_toText($dmp_patches);
}
}

View file

@ -216,6 +216,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
'type' => 'db',
'name' => 'passphrase',
),
'db.phragment' => array(
'type' => 'db',
'name' => 'phragment',
),
'0000.legacy.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('0000.legacy.sql'),
@ -1812,6 +1816,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
'type' => 'php',
'name' => $this->getPatchPath('20131205.buildstepordermig.php'),
),
'20131206.phragment.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20131206.phragment.sql'),
),
);
}
}