Page MenuHomePhorge

No OneTemporary

diff --git a/src/applications/config/schema/PhabricatorConfigSchemaSpec.php b/src/applications/config/schema/PhabricatorConfigSchemaSpec.php
index d44705f45b..22fbea4117 100644
--- a/src/applications/config/schema/PhabricatorConfigSchemaSpec.php
+++ b/src/applications/config/schema/PhabricatorConfigSchemaSpec.php
@@ -1,343 +1,355 @@
<?php
abstract class PhabricatorConfigSchemaSpec extends Phobject {
private $server;
private $utf8Charset;
private $utf8Collation;
public function setUTF8Collation($utf8_collation) {
$this->utf8Collation = $utf8_collation;
return $this;
}
public function getUTF8Collation() {
return $this->utf8Collation;
}
public function setUTF8Charset($utf8_charset) {
$this->utf8Charset = $utf8_charset;
return $this;
}
public function getUTF8Charset() {
return $this->utf8Charset;
}
public function setServer(PhabricatorConfigServerSchema $server) {
$this->server = $server;
return $this;
}
public function getServer() {
return $this->server;
}
abstract public function buildSchemata();
protected function buildLiskSchemata($base) {
$objects = id(new PhutilSymbolLoader())
->setAncestorClass($base)
->loadObjects();
foreach ($objects as $object) {
$this->buildLiskObjectSchema($object);
}
}
protected function buildTransactionSchema(
PhabricatorApplicationTransaction $xaction,
PhabricatorApplicationTransactionComment $comment = null) {
$this->buildLiskObjectSchema($xaction);
if ($comment) {
$this->buildLiskObjectSchema($comment);
}
}
protected function buildCustomFieldSchemata(
PhabricatorLiskDAO $storage,
array $indexes) {
$this->buildLiskObjectSchema($storage);
foreach ($indexes as $index) {
$this->buildLiskObjectSchema($index);
}
}
private function buildLiskObjectSchema(PhabricatorLiskDAO $object) {
$this->buildRawSchema(
$object->getApplicationName(),
$object->getTableName(),
$object->getSchemaColumns(),
$object->getSchemaKeys());
}
protected function buildRawSchema(
$database_name,
$table_name,
array $columns,
array $keys) {
$database = $this->getDatabase($database_name);
$table = $this->newTable($table_name);
foreach ($columns as $name => $type) {
if ($type === null) {
continue;
}
$details = $this->getDetailsForDataType($type);
list($column_type, $charset, $collation, $nullable) = $details;
$column = $this->newColumn($name)
->setDataType($type)
->setColumnType($column_type)
->setCharacterSet($charset)
->setCollation($collation)
->setNullable($nullable);
$table->addColumn($column);
}
foreach ($keys as $key_name => $key_spec) {
if ($key_spec === null) {
// This is a subclass removing a key which Lisk expects.
continue;
}
$key = $this->newKey($key_name)
->setColumnNames(idx($key_spec, 'columns', array()));
$key->setUnique((bool)idx($key_spec, 'unique'));
$table->addKey($key);
}
$database->addTable($table);
}
protected function buildEdgeSchemata(PhabricatorLiskDAO $object) {
$this->buildRawSchema(
$object->getApplicationName(),
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
array(
'src' => 'phid',
'type' => 'uint32',
'dst' => 'phid',
'dateCreated' => 'epoch',
'seq' => 'uint32',
'dataID' => 'id?',
),
array(
'PRIMARY' => array(
'columns' => array('src', 'type', 'dst'),
'unique' => true,
),
'src' => array(
'columns' => array('src', 'type', 'dateCreated', 'seq'),
),
+ 'key_dst' => array(
+ 'columns' => array('dst', 'type', 'src'),
+ 'unique' => true,
+ ),
));
$this->buildRawSchema(
$object->getApplicationName(),
PhabricatorEdgeConfig::TABLE_NAME_EDGEDATA,
array(
'id' => 'id',
'data' => 'text',
),
array(
'PRIMARY' => array(
'columns' => array('id'),
'unique' => true,
),
));
}
public function buildCounterSchema(PhabricatorLiskDAO $object) {
$this->buildRawSchema(
$object->getApplicationName(),
PhabricatorLiskDAO::COUNTER_TABLE_NAME,
array(
'counterName' => 'text32',
'counterValue' => 'id64',
),
array(
'PRIMARY' => array(
'columns' => array('counterName'),
),
));
}
protected function getDatabase($name) {
$server = $this->getServer();
$database = $server->getDatabase($this->getNamespacedDatabase($name));
if (!$database) {
$database = $this->newDatabase($name);
$server->addDatabase($database);
}
return $database;
}
protected function newDatabase($name) {
return id(new PhabricatorConfigDatabaseSchema())
->setName($this->getNamespacedDatabase($name))
->setCharacterSet($this->getUTF8Charset())
->setCollation($this->getUTF8Collation());
}
protected function getNamespacedDatabase($name) {
$namespace = PhabricatorLiskDAO::getStorageNamespace();
return $namespace.'_'.$name;
}
protected function newTable($name) {
return id(new PhabricatorConfigTableSchema())
->setName($name)
->setCollation($this->getUTF8Collation());
}
protected function newColumn($name) {
return id(new PhabricatorConfigColumnSchema())
->setName($name);
}
protected function newKey($name) {
return id(new PhabricatorConfigKeySchema())
->setName($name);
}
private function getDetailsForDataType($data_type) {
$column_type = null;
$charset = null;
$collation = null;
// If the type ends with "?", make the column nullable.
$nullable = false;
if (preg_match('/\?$/', $data_type)) {
$nullable = true;
$data_type = substr($data_type, 0, -1);
}
switch ($data_type) {
case 'id':
case 'epoch':
case 'uint32':
$column_type = 'int(10) unsigned';
break;
case 'id64':
case 'uint64':
$column_type = 'bigint(20) unsigned';
break;
case 'sint64':
$column_type = 'bigint(20)';
break;
case 'phid':
case 'policy';
$column_type = 'varchar(64)';
$charset = 'binary';
$collation = 'binary';
break;
case 'bytes40':
$column_type = 'char(40)';
$charset = 'binary';
$collation = 'binary';
break;
case 'bytes32':
$column_type = 'char(32)';
$charset = 'binary';
$collation = 'binary';
break;
+ case 'bytes20':
+ $column_type = 'char(20)';
+ $charset = 'binary';
+ $collation = 'binary';
+ break;
case 'bytes12':
$column_type = 'char(12)';
$charset = 'binary';
$collation = 'binary';
break;
- case 'bytes':
- $column_type = 'longblob';
+ case 'bytes4':
+ $column_type = 'char(4)';
$charset = 'binary';
$collation = 'binary';
break;
+ case 'bytes':
+ $column_type = 'longblob';
+ break;
case 'text255':
$column_type = 'varchar(255)';
$charset = $this->getUTF8Charset();
$collation = $this->getUTF8Collation();
break;
case 'text128':
$column_type = 'varchar(128)';
$charset = $this->getUTF8Charset();
$collation = $this->getUTF8Collation();
break;
case 'text80':
$column_type = 'varchar(80)';
$charset = $this->getUTF8Charset();
$collation = $this->getUTF8Collation();
break;
case 'text64':
$column_type = 'varchar(64)';
$charset = $this->getUTF8Charset();
$collation = $this->getUTF8Collation();
break;
case 'text40':
$column_type = 'varchar(40)';
$charset = $this->getUTF8Charset();
$collation = $this->getUTF8Collation();
break;
case 'text32':
$column_type = 'varchar(32)';
$charset = $this->getUTF8Charset();
$collation = $this->getUTF8Collation();
break;
case 'text20':
$column_type = 'varchar(20)';
$charset = $this->getUTF8Charset();
$collation = $this->getUTF8Collation();
break;
case 'text16':
$column_type = 'varchar(16)';
$charset = $this->getUTF8Charset();
$collation = $this->getUTF8Collation();
break;
case 'text12':
$column_type = 'varchar(12)';
$charset = $this->getUTF8Charset();
$collation = $this->getUTF8Collation();
break;
case 'text8':
$column_type = 'varchar(8)';
$charset = $this->getUTF8Charset();
$collation = $this->getUTF8Collation();
break;
case 'text4':
$column_type = 'varchar(4)';
$charset = $this->getUTF8Charset();
$collation = $this->getUTF8Collation();
break;
case 'text':
$column_type = 'longtext';
$charset = $this->getUTF8Charset();
$collation = $this->getUTF8Collation();
break;
case 'bool':
$column_type = 'tinyint(1)';
break;
case 'double':
$column_type = 'double';
break;
case 'date':
$column_type = 'date';
break;
default:
$column_type = pht('<unknown>');
$charset = pht('<unknown>');
$collation = pht('<unknown>');
break;
}
return array($column_type, $charset, $collation, $nullable);
}
}
diff --git a/src/applications/differential/storage/DifferentialAffectedPath.php b/src/applications/differential/storage/DifferentialAffectedPath.php
index ac0e9c23ec..35e7ba5d70 100644
--- a/src/applications/differential/storage/DifferentialAffectedPath.php
+++ b/src/applications/differential/storage/DifferentialAffectedPath.php
@@ -1,20 +1,32 @@
<?php
/**
* Denormalized index table which stores relationships between revisions in
* Differential and paths in Diffusion.
*/
final class DifferentialAffectedPath extends DifferentialDAO {
protected $repositoryID;
protected $pathID;
protected $epoch;
protected $revisionID;
public function getConfiguration() {
return array(
self::CONFIG_TIMESTAMPS => false,
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'id' => null,
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'PRIMARY' => null,
+ 'repositoryID' => array(
+ 'columns' => array('repositoryID', 'pathID', 'epoch'),
+ ),
+ 'revisionID' => array(
+ 'columns' => array('revisionID'),
+ ),
+ ),
) + parent::getConfiguration();
}
}
diff --git a/src/applications/differential/storage/DifferentialChangeset.php b/src/applications/differential/storage/DifferentialChangeset.php
index 743bf172b1..5ae22d8a2c 100644
--- a/src/applications/differential/storage/DifferentialChangeset.php
+++ b/src/applications/differential/storage/DifferentialChangeset.php
@@ -1,211 +1,225 @@
<?php
final class DifferentialChangeset extends DifferentialDAO
implements PhabricatorPolicyInterface {
protected $diffID;
protected $oldFile;
protected $filename;
protected $awayPaths;
protected $changeType;
protected $fileType;
protected $metadata;
protected $oldProperties;
protected $newProperties;
protected $addLines;
protected $delLines;
private $unsavedHunks = array();
private $hunks = self::ATTACHABLE;
private $diff = self::ATTACHABLE;
const TABLE_CACHE = 'differential_changeset_parse_cache';
protected function getConfiguration() {
return array(
self::CONFIG_SERIALIZATION => array(
'metadata' => self::SERIALIZATION_JSON,
'oldProperties' => self::SERIALIZATION_JSON,
'newProperties' => self::SERIALIZATION_JSON,
'awayPaths' => self::SERIALIZATION_JSON,
- )) + parent::getConfiguration();
+ ),
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'oldFile' => 'text255?',
+ 'filename' => 'text255',
+ 'changeType' => 'uint32',
+ 'fileType' => 'uint32',
+ 'addLines' => 'uint32',
+ 'delLines' => 'uint32',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'diffID' => array(
+ 'columns' => array('diffID'),
+ ),
+ ),
+ ) + parent::getConfiguration();
}
public function getAffectedLineCount() {
return $this->getAddLines() + $this->getDelLines();
}
public function attachHunks(array $hunks) {
assert_instances_of($hunks, 'DifferentialHunk');
$this->hunks = $hunks;
return $this;
}
public function getHunks() {
return $this->assertAttached($this->hunks);
}
public function getDisplayFilename() {
$name = $this->getFilename();
if ($this->getFileType() == DifferentialChangeType::FILE_DIRECTORY) {
$name .= '/';
}
return $name;
}
public function addUnsavedHunk(DifferentialHunk $hunk) {
if ($this->hunks === self::ATTACHABLE) {
$this->hunks = array();
}
$this->hunks[] = $hunk;
$this->unsavedHunks[] = $hunk;
return $this;
}
public function save() {
$this->openTransaction();
$ret = parent::save();
foreach ($this->unsavedHunks as $hunk) {
$hunk->setChangesetID($this->getID());
$hunk->save();
}
$this->saveTransaction();
return $ret;
}
public function delete() {
$this->openTransaction();
$legacy_hunks = id(new DifferentialHunkLegacy())->loadAllWhere(
'changesetID = %d',
$this->getID());
foreach ($legacy_hunks as $legacy_hunk) {
$legacy_hunk->delete();
}
$modern_hunks = id(new DifferentialHunkModern())->loadAllWhere(
'changesetID = %d',
$this->getID());
foreach ($modern_hunks as $modern_hunk) {
$modern_hunk->delete();
}
$this->unsavedHunks = array();
queryfx(
$this->establishConnection('w'),
'DELETE FROM %T WHERE id = %d',
self::TABLE_CACHE,
$this->getID());
$ret = parent::delete();
$this->saveTransaction();
return $ret;
}
public function getSortKey() {
$sort_key = $this->getFilename();
// Sort files with ".h" in them first, so headers (.h, .hpp) come before
// implementations (.c, .cpp, .cs).
$sort_key = str_replace('.h', '.!h', $sort_key);
return $sort_key;
}
public function makeNewFile() {
$file = mpull($this->getHunks(), 'makeNewFile');
return implode('', $file);
}
public function makeOldFile() {
$file = mpull($this->getHunks(), 'makeOldFile');
return implode('', $file);
}
public function makeChangesWithContext($num_lines = 3) {
$with_context = array();
foreach ($this->getHunks() as $hunk) {
$context = array();
$changes = explode("\n", $hunk->getChanges());
foreach ($changes as $l => $line) {
$type = substr($line, 0, 1);
if ($type == '+' || $type == '-') {
$context += array_fill($l - $num_lines, 2 * $num_lines + 1, true);
}
}
$with_context[] = array_intersect_key($changes, $context);
}
return array_mergev($with_context);
}
public function getAnchorName() {
return substr(md5($this->getFilename()), 0, 8);
}
public function getAbsoluteRepositoryPath(
PhabricatorRepository $repository = null,
DifferentialDiff $diff = null) {
$base = '/';
if ($diff && $diff->getSourceControlPath()) {
$base = id(new PhutilURI($diff->getSourceControlPath()))->getPath();
}
$path = $this->getFilename();
$path = rtrim($base, '/').'/'.ltrim($path, '/');
$svn = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN;
if ($repository && $repository->getVersionControlSystem() == $svn) {
$prefix = $repository->getDetail('remote-uri');
$prefix = id(new PhutilURI($prefix))->getPath();
if (!strncmp($path, $prefix, strlen($prefix))) {
$path = substr($path, strlen($prefix));
}
$path = '/'.ltrim($path, '/');
}
return $path;
}
public function getWhitespaceMatters() {
$config = PhabricatorEnv::getEnvConfig('differential.whitespace-matters');
foreach ($config as $regexp) {
if (preg_match($regexp, $this->getFilename())) {
return true;
}
}
return false;
}
public function attachDiff(DifferentialDiff $diff) {
$this->diff = $diff;
return $this;
}
public function getDiff() {
return $this->assertAttached($this->diff);
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
return $this->getDiff()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getDiff()->hasAutomaticCapability($capability, $viewer);
}
public function describeAutomaticCapability($capability) {
return null;
}
}
diff --git a/src/applications/differential/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php
index 863312520d..7828510f80 100644
--- a/src/applications/differential/storage/DifferentialDiff.php
+++ b/src/applications/differential/storage/DifferentialDiff.php
@@ -1,418 +1,442 @@
<?php
final class DifferentialDiff
extends DifferentialDAO
implements
PhabricatorPolicyInterface,
HarbormasterBuildableInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorDestructibleInterface {
protected $revisionID;
protected $authorPHID;
protected $repositoryPHID;
protected $sourceMachine;
protected $sourcePath;
protected $sourceControlSystem;
protected $sourceControlBaseRevision;
protected $sourceControlPath;
protected $lintStatus;
protected $unitStatus;
protected $lineCount;
protected $branch;
protected $bookmark;
protected $arcanistProjectPHID;
protected $creationMethod;
protected $repositoryUUID;
protected $description;
private $unsavedChangesets = array();
private $changesets = self::ATTACHABLE;
private $arcanistProject = self::ATTACHABLE;
private $revision = self::ATTACHABLE;
private $properties = array();
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'revisionID' => 'id?',
+ 'authorPHID' => 'phid?',
+ 'repositoryPHID' => 'phid?',
+ 'sourceMachine' => 'text255?',
+ 'sourcePath' => 'text255?',
+ 'sourceControlSystem' => 'text64?',
+ 'sourceControlBaseRevision' => 'text255?',
+ 'sourceControlPath' => 'text255?',
+ 'lintStatus' => 'uint32',
+ 'unitStatus' => 'uint32',
+ 'lineCount' => 'uint32',
+ 'branch' => 'text255?',
+ 'bookmark' => 'text255?',
+ 'arcanistProjectPHID' => 'phid?',
+ 'creationMethod' => 'text255',
+ 'description' => 'text255',
+ 'repositoryUUID' => 'text64?',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'revisionID' => array(
+ 'columns' => array('revisionID'),
+ ),
+ ),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
DifferentialDiffPHIDType::TYPECONST);
}
public function addUnsavedChangeset(DifferentialChangeset $changeset) {
if ($this->changesets === null) {
$this->changesets = array();
}
$this->unsavedChangesets[] = $changeset;
$this->changesets[] = $changeset;
return $this;
}
public function attachChangesets(array $changesets) {
assert_instances_of($changesets, 'DifferentialChangeset');
$this->changesets = $changesets;
return $this;
}
public function getChangesets() {
return $this->assertAttached($this->changesets);
}
public function loadChangesets() {
if (!$this->getID()) {
return array();
}
return id(new DifferentialChangeset())->loadAllWhere(
'diffID = %d',
$this->getID());
}
public function attachArcanistProject(
PhabricatorRepositoryArcanistProject $project = null) {
$this->arcanistProject = $project;
return $this;
}
public function getArcanistProject() {
return $this->assertAttached($this->arcanistProject);
}
public function getArcanistProjectName() {
$name = '';
if ($this->arcanistProject) {
$project = $this->getArcanistProject();
$name = $project->getName();
}
return $name;
}
public function save() {
$this->openTransaction();
$ret = parent::save();
foreach ($this->unsavedChangesets as $changeset) {
$changeset->setDiffID($this->getID());
$changeset->save();
}
$this->saveTransaction();
return $ret;
}
public static function newFromRawChanges(array $changes) {
assert_instances_of($changes, 'ArcanistDiffChange');
$diff = new DifferentialDiff();
// There may not be any changes; initialize the changesets list so that
// we don't throw later when accessing it.
$diff->attachChangesets(array());
$lines = 0;
foreach ($changes as $change) {
if ($change->getType() == ArcanistDiffChangeType::TYPE_MESSAGE) {
// If a user pastes a diff into Differential which includes a commit
// message (e.g., they ran `git show` to generate it), discard that
// change when constructing a DifferentialDiff.
continue;
}
$changeset = new DifferentialChangeset();
$add_lines = 0;
$del_lines = 0;
$first_line = PHP_INT_MAX;
$hunks = $change->getHunks();
if ($hunks) {
foreach ($hunks as $hunk) {
$dhunk = new DifferentialHunkModern();
$dhunk->setOldOffset($hunk->getOldOffset());
$dhunk->setOldLen($hunk->getOldLength());
$dhunk->setNewOffset($hunk->getNewOffset());
$dhunk->setNewLen($hunk->getNewLength());
$dhunk->setChanges($hunk->getCorpus());
$changeset->addUnsavedHunk($dhunk);
$add_lines += $hunk->getAddLines();
$del_lines += $hunk->getDelLines();
$added_lines = $hunk->getChangedLines('new');
if ($added_lines) {
$first_line = min($first_line, head_key($added_lines));
}
}
$lines += $add_lines + $del_lines;
} else {
// This happens when you add empty files.
$changeset->attachHunks(array());
}
$metadata = $change->getAllMetadata();
if ($first_line != PHP_INT_MAX) {
$metadata['line:first'] = $first_line;
}
$changeset->setOldFile($change->getOldPath());
$changeset->setFilename($change->getCurrentPath());
$changeset->setChangeType($change->getType());
$changeset->setFileType($change->getFileType());
$changeset->setMetadata($metadata);
$changeset->setOldProperties($change->getOldProperties());
$changeset->setNewProperties($change->getNewProperties());
$changeset->setAwayPaths($change->getAwayPaths());
$changeset->setAddLines($add_lines);
$changeset->setDelLines($del_lines);
$diff->addUnsavedChangeset($changeset);
}
$diff->setLineCount($lines);
$parser = new DifferentialChangesetParser();
$changesets = $parser->detectCopiedCode(
$diff->getChangesets(),
$min_width = 30,
$min_lines = 3);
$diff->attachChangesets($changesets);
return $diff;
}
public function getDiffDict() {
$dict = array(
'id' => $this->getID(),
'revisionID' => $this->getRevisionID(),
'dateCreated' => $this->getDateCreated(),
'dateModified' => $this->getDateModified(),
'sourceControlBaseRevision' => $this->getSourceControlBaseRevision(),
'sourceControlPath' => $this->getSourceControlPath(),
'sourceControlSystem' => $this->getSourceControlSystem(),
'branch' => $this->getBranch(),
'bookmark' => $this->getBookmark(),
'creationMethod' => $this->getCreationMethod(),
'description' => $this->getDescription(),
'unitStatus' => $this->getUnitStatus(),
'lintStatus' => $this->getLintStatus(),
'changes' => array(),
'properties' => array(),
'projectName' => $this->getArcanistProjectName()
);
$dict['changes'] = $this->buildChangesList();
$properties = id(new DifferentialDiffProperty())->loadAllWhere(
'diffID = %d',
$this->getID());
foreach ($properties as $property) {
$dict['properties'][$property->getName()] = $property->getData();
if ($property->getName() == 'local:commits') {
foreach ($property->getData() as $commit) {
$dict['authorName'] = $commit['author'];
$dict['authorEmail'] = idx($commit, 'authorEmail');
break;
}
}
}
return $dict;
}
public function buildChangesList() {
$changes = array();
foreach ($this->getChangesets() as $changeset) {
$hunks = array();
foreach ($changeset->getHunks() as $hunk) {
$hunks[] = array(
'oldOffset' => $hunk->getOldOffset(),
'newOffset' => $hunk->getNewOffset(),
'oldLength' => $hunk->getOldLen(),
'newLength' => $hunk->getNewLen(),
'addLines' => null,
'delLines' => null,
'isMissingOldNewline' => null,
'isMissingNewNewline' => null,
'corpus' => $hunk->getChanges(),
);
}
$change = array(
'id' => $changeset->getID(),
'metadata' => $changeset->getMetadata(),
'oldPath' => $changeset->getOldFile(),
'currentPath' => $changeset->getFilename(),
'awayPaths' => $changeset->getAwayPaths(),
'oldProperties' => $changeset->getOldProperties(),
'newProperties' => $changeset->getNewProperties(),
'type' => $changeset->getChangeType(),
'fileType' => $changeset->getFileType(),
'commitHash' => null,
'addLines' => $changeset->getAddLines(),
'delLines' => $changeset->getDelLines(),
'hunks' => $hunks,
);
$changes[] = $change;
}
return $changes;
}
public function getRevision() {
return $this->assertAttached($this->revision);
}
public function attachRevision(DifferentialRevision $revision = null) {
$this->revision = $revision;
return $this;
}
public function attachProperty($key, $value) {
$this->properties[$key] = $value;
return $this;
}
public function getProperty($key) {
return $this->assertAttachedKey($this->properties, $key);
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
if ($this->getRevision()) {
return $this->getRevision()->getPolicy($capability);
}
return PhabricatorPolicies::POLICY_USER;
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
if ($this->getRevision()) {
return $this->getRevision()->hasAutomaticCapability($capability, $viewer);
}
return false;
}
public function describeAutomaticCapability($capability) {
if ($this->getRevision()) {
return pht(
'This diff is attached to a revision, and inherits its policies.');
}
return null;
}
/* -( HarbormasterBuildableInterface )------------------------------------- */
public function getHarbormasterBuildablePHID() {
return $this->getPHID();
}
public function getHarbormasterContainerPHID() {
if ($this->getRevisionID()) {
$revision = id(new DifferentialRevision())->load($this->getRevisionID());
if ($revision) {
return $revision->getPHID();
}
}
return null;
}
public function getBuildVariables() {
$results = array();
$results['buildable.diff'] = $this->getID();
$revision = $this->getRevision();
$results['buildable.revision'] = $revision->getID();
$repo = $revision->getRepository();
if ($repo) {
$results['repository.callsign'] = $repo->getCallsign();
$results['repository.vcs'] = $repo->getVersionControlSystem();
$results['repository.uri'] = $repo->getPublicCloneURI();
}
return $results;
}
public function getAvailableBuildVariables() {
return array(
'buildable.diff' =>
pht('The differential diff ID, if applicable.'),
'buildable.revision' =>
pht('The differential revision ID, if applicable.'),
'repository.callsign' =>
pht('The callsign of the repository in Phabricator.'),
'repository.vcs' =>
pht('The version control system, either "svn", "hg" or "git".'),
'repository.uri' =>
pht('The URI to clone or checkout the repository from.'),
);
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
if (!$this->getRevisionID()) {
return null;
}
return $this->getRevision()->getApplicationTransactionEditor();
}
public function getApplicationTransactionObject() {
if (!$this->getRevisionID()) {
return null;
}
return $this->getRevision();
}
public function getApplicationTransactionTemplate() {
if (!$this->getRevisionID()) {
return null;
}
return $this->getRevision()->getApplicationTransactionTemplate();
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->openTransaction();
$this->delete();
foreach ($this->loadChangesets() as $changeset) {
$changeset->delete();
}
$properties = id(new DifferentialDiffProperty())->loadAllWhere(
'diffID = %d',
$this->getID());
foreach ($properties as $prop) {
$prop->delete();
}
$this->saveTransaction();
}
}
diff --git a/src/applications/differential/storage/DifferentialDiffProperty.php b/src/applications/differential/storage/DifferentialDiffProperty.php
index 11be887eb4..a7286bfec1 100644
--- a/src/applications/differential/storage/DifferentialDiffProperty.php
+++ b/src/applications/differential/storage/DifferentialDiffProperty.php
@@ -1,16 +1,26 @@
<?php
final class DifferentialDiffProperty extends DifferentialDAO {
protected $diffID;
protected $name;
protected $data;
protected function getConfiguration() {
return array(
self::CONFIG_SERIALIZATION => array(
'data' => self::SERIALIZATION_JSON,
- )) + parent::getConfiguration();
+ ),
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'name' => 'text255',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'diffID' => array(
+ 'columns' => array('diffID', 'name'),
+ 'unique' => true,
+ ),
+ ),
+ ) + parent::getConfiguration();
}
}
diff --git a/src/applications/differential/storage/DifferentialDraft.php b/src/applications/differential/storage/DifferentialDraft.php
index 6ad406d3f0..8ea12f73e0 100644
--- a/src/applications/differential/storage/DifferentialDraft.php
+++ b/src/applications/differential/storage/DifferentialDraft.php
@@ -1,51 +1,65 @@
<?php
final class DifferentialDraft extends DifferentialDAO {
protected $objectPHID;
protected $authorPHID;
protected $draftKey;
+ public function getConfiguration() {
+ return array(
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'draftKey' => 'text64',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'key_unique' => array(
+ 'columns' => array('objectPHID', 'authorPHID', 'draftKey'),
+ 'unique' => true,
+ ),
+ ),
+ ) + parent::getConfiguration();
+ }
+
public static function markHasDraft(
$author_phid,
$object_phid,
$draft_key) {
try {
id(new DifferentialDraft())
->setObjectPHID($object_phid)
->setAuthorPHID($author_phid)
->setDraftKey($draft_key)
->save();
} catch (AphrontDuplicateKeyQueryException $ex) {
// no worries
}
}
public static function deleteHasDraft(
$author_phid,
$object_phid,
$draft_key) {
$draft = id(new DifferentialDraft())->loadOneWhere(
'objectPHID = %s AND authorPHID = %s AND draftKey = %s',
$object_phid,
$author_phid,
$draft_key);
if ($draft) {
$draft->delete();
}
}
public static function deleteAllDrafts(
$author_phid,
$object_phid) {
$drafts = id(new DifferentialDraft())->loadAllWhere(
'objectPHID = %s AND authorPHID = %s',
$object_phid,
$author_phid);
foreach ($drafts as $draft) {
$draft->delete();
}
}
}
diff --git a/src/applications/differential/storage/DifferentialHunkLegacy.php b/src/applications/differential/storage/DifferentialHunkLegacy.php
index 7eedd770c1..c64a4f1c9d 100644
--- a/src/applications/differential/storage/DifferentialHunkLegacy.php
+++ b/src/applications/differential/storage/DifferentialHunkLegacy.php
@@ -1,20 +1,37 @@
<?php
final class DifferentialHunkLegacy extends DifferentialHunk {
protected $changes;
+ public function getConfiguration() {
+ return array(
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'changes' => 'text?',
+ 'oldOffset' => 'uint32',
+ 'oldLen' => 'uint32',
+ 'newOffset' => 'uint32',
+ 'newLen' => 'uint32',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'changesetID' => array(
+ 'columns' => array('changesetID'),
+ ),
+ ),
+ ) + parent::getConfiguration();
+ }
+
public function getTableName() {
return 'differential_hunk';
}
public function getDataEncoding() {
return 'utf8';
}
public function forceEncoding($encoding) {
// Not supported, these are always utf8.
return $this;
}
}
diff --git a/src/applications/differential/storage/DifferentialHunkModern.php b/src/applications/differential/storage/DifferentialHunkModern.php
index eae36a6f7f..0279dd5f78 100644
--- a/src/applications/differential/storage/DifferentialHunkModern.php
+++ b/src/applications/differential/storage/DifferentialHunkModern.php
@@ -1,109 +1,126 @@
<?php
final class DifferentialHunkModern extends DifferentialHunk {
const DATATYPE_TEXT = 'text';
const DATATYPE_FILE = 'file';
const DATAFORMAT_RAW = 'byte';
const DATAFORMAT_DEFLATED = 'gzde';
protected $dataType;
protected $dataEncoding;
protected $dataFormat;
protected $data;
private $rawData;
private $forcedEncoding;
public function getTableName() {
return 'differential_hunk_modern';
}
public function getConfiguration() {
return array(
self::CONFIG_BINARY => array(
'data' => true,
),
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'dataType' => 'bytes4',
+ 'dataEncoding' => 'text16?',
+ 'dataFormat' => 'bytes4',
+ 'oldOffset' => 'uint32',
+ 'oldLen' => 'uint32',
+ 'newOffset' => 'uint32',
+ 'newLen' => 'uint32',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'key_changeset' => array(
+ 'columns' => array('changesetID'),
+ ),
+ 'key_created' => array(
+ 'columns' => array('dateCreated'),
+ ),
+ ),
) + parent::getConfiguration();
}
public function setChanges($text) {
$this->rawData = $text;
$this->dataEncoding = $this->detectEncodingForStorage($text);
$this->dataType = self::DATATYPE_TEXT;
$this->dataFormat = self::DATAFORMAT_RAW;
$this->data = $text;
return $this;
}
public function getChanges() {
return $this->getUTF8StringFromStorage(
$this->getRawData(),
nonempty($this->forcedEncoding, $this->getDataEncoding()));
}
public function forceEncoding($encoding) {
$this->forcedEncoding = $encoding;
return $this;
}
public function save() {
$type = $this->getDataType();
$format = $this->getDataFormat();
// Before saving the data, attempt to compress it.
if ($type == self::DATATYPE_TEXT) {
if ($format == self::DATAFORMAT_RAW) {
$data = $this->getData();
$deflated = PhabricatorCaches::maybeDeflateData($data);
if ($deflated !== null) {
$this->data = $deflated;
$this->dataFormat = self::DATAFORMAT_DEFLATED;
}
}
}
return parent::save();
}
private function getRawData() {
if ($this->rawData === null) {
$type = $this->getDataType();
$data = $this->getData();
switch ($type) {
case self::DATATYPE_TEXT:
// In this storage type, the changes are stored on the object.
$data = $data;
break;
case self::DATATYPE_FILE:
default:
throw new Exception(
pht('Hunk has unsupported data type "%s"!', $type));
}
$format = $this->getDataFormat();
switch ($format) {
case self::DATAFORMAT_RAW:
// In this format, the changes are stored as-is.
$data = $data;
break;
case self::DATAFORMAT_DEFLATED:
$data = PhabricatorCaches::inflateData($data);
break;
default:
throw new Exception(
pht('Hunk has unsupported data encoding "%s"!', $type));
}
$this->rawData = $data;
}
return $this->rawData;
}
}
diff --git a/src/applications/differential/storage/DifferentialRevision.php b/src/applications/differential/storage/DifferentialRevision.php
index 428536a0e1..c8b3fa17af 100644
--- a/src/applications/differential/storage/DifferentialRevision.php
+++ b/src/applications/differential/storage/DifferentialRevision.php
@@ -1,500 +1,527 @@
<?php
final class DifferentialRevision extends DifferentialDAO
implements
PhabricatorTokenReceiverInterface,
PhabricatorPolicyInterface,
PhabricatorFlaggableInterface,
PhrequentTrackableInterface,
HarbormasterBuildableInterface,
PhabricatorSubscribableInterface,
PhabricatorCustomFieldInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorMentionableInterface,
PhabricatorDestructibleInterface,
PhabricatorProjectInterface {
protected $title = '';
protected $originalTitle;
protected $status;
protected $summary = '';
protected $testPlan = '';
protected $authorPHID;
protected $lastReviewerPHID;
protected $lineCount = 0;
protected $attached = array();
protected $mailKey;
protected $branchName;
protected $arcanistProjectPHID;
protected $repositoryPHID;
protected $viewPolicy = PhabricatorPolicies::POLICY_USER;
protected $editPolicy = PhabricatorPolicies::POLICY_USER;
private $relationships = self::ATTACHABLE;
private $commits = self::ATTACHABLE;
private $activeDiff = self::ATTACHABLE;
private $diffIDs = self::ATTACHABLE;
private $hashes = self::ATTACHABLE;
private $repository = self::ATTACHABLE;
private $reviewerStatus = self::ATTACHABLE;
private $customFields = self::ATTACHABLE;
private $drafts = array();
private $flags = array();
const TABLE_COMMIT = 'differential_commit';
const RELATION_REVIEWER = 'revw';
const RELATION_SUBSCRIBED = 'subd';
public static function initializeNewRevision(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery())
->setViewer($actor)
->withClasses(array('PhabricatorDifferentialApplication'))
->executeOne();
$view_policy = $app->getPolicy(
DifferentialDefaultViewCapability::CAPABILITY);
return id(new DifferentialRevision())
->setViewPolicy($view_policy)
->setAuthorPHID($actor->getPHID())
->attachRelationships(array())
->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVIEW);
}
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'attached' => self::SERIALIZATION_JSON,
'unsubscribed' => self::SERIALIZATION_JSON,
),
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'title' => 'text255',
+ 'originalTitle' => 'text255',
+ 'status' => 'text32',
+ 'summary' => 'text',
+ 'testPlan' => 'text',
+ 'authorPHID' => 'phid?',
+ 'lastReviewerPHID' => 'phid?',
+ 'lineCount' => 'uint32?',
+ 'mailKey' => 'bytes40',
+ 'branchName' => 'text255',
+ 'arcanistProjectPHID' => 'phid?',
+ 'repositoryPHID' => 'phid?',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'key_phid' => null,
+ 'phid' => array(
+ 'columns' => array('phid'),
+ 'unique' => true,
+ ),
+ 'authorPHID' => array(
+ 'columns' => array('authorPHID', 'status'),
+ ),
+ 'repositoryPHID' => array(
+ 'columns' => array('repositoryPHID'),
+ ),
+ ),
) + parent::getConfiguration();
}
public function getMonogram() {
$id = $this->getID();
return "D{$id}";
}
public function setTitle($title) {
$this->title = $title;
if (!$this->getID()) {
$this->originalTitle = $title;
}
return $this;
}
public function loadIDsByCommitPHIDs($phids) {
if (!$phids) {
return array();
}
$revision_ids = queryfx_all(
$this->establishConnection('r'),
'SELECT * FROM %T WHERE commitPHID IN (%Ls)',
self::TABLE_COMMIT,
$phids);
return ipull($revision_ids, 'revisionID', 'commitPHID');
}
public function loadCommitPHIDs() {
if (!$this->getID()) {
return ($this->commits = array());
}
$commits = queryfx_all(
$this->establishConnection('r'),
'SELECT commitPHID FROM %T WHERE revisionID = %d',
self::TABLE_COMMIT,
$this->getID());
$commits = ipull($commits, 'commitPHID');
return ($this->commits = $commits);
}
public function getCommitPHIDs() {
return $this->assertAttached($this->commits);
}
public function getActiveDiff() {
// TODO: Because it's currently technically possible to create a revision
// without an associated diff, we allow an attached-but-null active diff.
// It would be good to get rid of this once we make diff-attaching
// transactional.
return $this->assertAttached($this->activeDiff);
}
public function attachActiveDiff($diff) {
$this->activeDiff = $diff;
return $this;
}
public function getDiffIDs() {
return $this->assertAttached($this->diffIDs);
}
public function attachDiffIDs(array $ids) {
rsort($ids);
$this->diffIDs = array_values($ids);
return $this;
}
public function attachCommitPHIDs(array $phids) {
$this->commits = array_values($phids);
return $this;
}
public function getAttachedPHIDs($type) {
return array_keys(idx($this->attached, $type, array()));
}
public function setAttachedPHIDs($type, array $phids) {
$this->attached[$type] = array_fill_keys($phids, array());
return $this;
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
DifferentialRevisionPHIDType::TYPECONST);
}
public function loadActiveDiff() {
return id(new DifferentialDiff())->loadOneWhere(
'revisionID = %d ORDER BY id DESC LIMIT 1',
$this->getID());
}
public function save() {
if (!$this->getMailKey()) {
$this->mailKey = Filesystem::readRandomCharacters(40);
}
return parent::save();
}
public function loadRelationships() {
if (!$this->getID()) {
$this->relationships = array();
return;
}
$data = array();
$subscriber_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$this->getPHID(),
PhabricatorEdgeConfig::TYPE_OBJECT_HAS_SUBSCRIBER);
$subscriber_phids = array_reverse($subscriber_phids);
foreach ($subscriber_phids as $phid) {
$data[] = array(
'relation' => self::RELATION_SUBSCRIBED,
'objectPHID' => $phid,
'reasonPHID' => null,
);
}
$reviewer_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$this->getPHID(),
PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER);
$reviewer_phids = array_reverse($reviewer_phids);
foreach ($reviewer_phids as $phid) {
$data[] = array(
'relation' => self::RELATION_REVIEWER,
'objectPHID' => $phid,
'reasonPHID' => null,
);
}
return $this->attachRelationships($data);
}
public function attachRelationships(array $relationships) {
$this->relationships = igroup($relationships, 'relation');
return $this;
}
public function getReviewers() {
return $this->getRelatedPHIDs(self::RELATION_REVIEWER);
}
public function getCCPHIDs() {
return $this->getRelatedPHIDs(self::RELATION_SUBSCRIBED);
}
private function getRelatedPHIDs($relation) {
$this->assertAttached($this->relationships);
return ipull($this->getRawRelations($relation), 'objectPHID');
}
public function getRawRelations($relation) {
return idx($this->relationships, $relation, array());
}
public function getPrimaryReviewer() {
$reviewers = $this->getReviewers();
$last = $this->lastReviewerPHID;
if (!$last || !in_array($last, $reviewers)) {
return head($this->getReviewers());
}
return $last;
}
public function getHashes() {
return $this->assertAttached($this->hashes);
}
public function attachHashes(array $hashes) {
$this->hashes = $hashes;
return $this;
}
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 $user) {
// A revision's author (which effectively means "owner" after we added
// commandeering) can always view and edit it.
$author_phid = $this->getAuthorPHID();
if ($author_phid) {
if ($user->getPHID() == $author_phid) {
return true;
}
}
return false;
}
public function describeAutomaticCapability($capability) {
$description = array(
pht('The owner of a revision can always view and edit it.'),
);
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
$description[] = pht(
"A revision's reviewers can always view it.");
$description[] = pht(
'If a revision belongs to a repository, other users must be able '.
'to view the repository in order to view the revision.');
break;
}
return $description;
}
public function getUsersToNotifyOfTokenGiven() {
return array(
$this->getAuthorPHID(),
);
}
public function getReviewerStatus() {
return $this->assertAttached($this->reviewerStatus);
}
public function attachReviewerStatus(array $reviewers) {
assert_instances_of($reviewers, 'DifferentialReviewer');
$this->reviewerStatus = $reviewers;
return $this;
}
public function getRepository() {
return $this->assertAttached($this->repository);
}
public function attachRepository(PhabricatorRepository $repository = null) {
$this->repository = $repository;
return $this;
}
public function isClosed() {
return DifferentialRevisionStatus::isClosedStatus($this->getStatus());
}
public function getFlag(PhabricatorUser $viewer) {
return $this->assertAttachedKey($this->flags, $viewer->getPHID());
}
public function attachFlag(
PhabricatorUser $viewer,
PhabricatorFlag $flag = null) {
$this->flags[$viewer->getPHID()] = $flag;
return $this;
}
public function getDrafts(PhabricatorUser $viewer) {
return $this->assertAttachedKey($this->drafts, $viewer->getPHID());
}
public function attachDrafts(PhabricatorUser $viewer, array $drafts) {
$this->drafts[$viewer->getPHID()] = $drafts;
return $this;
}
/* -( HarbormasterBuildableInterface )------------------------------------- */
public function getHarbormasterBuildablePHID() {
return $this->loadActiveDiff()->getPHID();
}
public function getHarbormasterContainerPHID() {
return $this->getPHID();
}
public function getBuildVariables() {
return array();
}
public function getAvailableBuildVariables() {
return array();
}
/* -( PhabricatorSubscribableInterface )----------------------------------- */
public function isAutomaticallySubscribed($phid) {
if ($phid == $this->getAuthorPHID()) {
return true;
}
// TODO: This only happens when adding or removing CCs, and is safe from a
// policy perspective, but the subscription pathway should have some
// opportunity to load this data properly. For now, this is the only case
// where implicit subscription is not an intrinsic property of the object.
if ($this->reviewerStatus == self::ATTACHABLE) {
$reviewers = id(new DifferentialRevisionQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($this->getPHID()))
->needReviewerStatus(true)
->executeOne()
->getReviewerStatus();
} else {
$reviewers = $this->getReviewerStatus();
}
foreach ($reviewers as $reviewer) {
if ($reviewer->getReviewerPHID() == $phid) {
return true;
}
}
return false;
}
public function shouldShowSubscribersProperty() {
return true;
}
public function shouldAllowSubscription($phid) {
return true;
}
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
public function getCustomFieldSpecificationForRole($role) {
return PhabricatorEnv::getEnvConfig('differential.fields');
}
public function getCustomFieldBaseClass() {
return 'DifferentialCustomField';
}
public function getCustomFields() {
return $this->assertAttached($this->customFields);
}
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
$this->customFields = $fields;
return $this;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new DifferentialTransactionEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new DifferentialTransaction();
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->openTransaction();
$diffs = id(new DifferentialDiffQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withRevisionIDs(array($this->getID()))
->execute();
foreach ($diffs as $diff) {
$engine->destroyObject($diff);
}
$conn_w = $this->establishConnection('w');
queryfx(
$conn_w,
'DELETE FROM %T WHERE revisionID = %d',
self::TABLE_COMMIT,
$this->getID());
try {
$inlines = id(new DifferentialInlineCommentQuery())
->withRevisionIDs(array($this->getID()))
->execute();
foreach ($inlines as $inline) {
$inline->delete();
}
} catch (PhabricatorEmptyQueryException $ex) {
// TODO: There's still some funky legacy wrapping going on here, and
// we might catch a raw query exception.
}
// we have to do paths a little differentally as they do not have
// an id or phid column for delete() to act on
$dummy_path = new DifferentialAffectedPath();
queryfx(
$conn_w,
'DELETE FROM %T WHERE revisionID = %d',
$dummy_path->getTableName(),
$this->getID());
$this->delete();
$this->saveTransaction();
}
}
diff --git a/src/applications/differential/storage/DifferentialSchemaSpec.php b/src/applications/differential/storage/DifferentialSchemaSpec.php
index e568da0a45..fc75096381 100644
--- a/src/applications/differential/storage/DifferentialSchemaSpec.php
+++ b/src/applications/differential/storage/DifferentialSchemaSpec.php
@@ -1,10 +1,77 @@
<?php
final class DifferentialSchemaSpec extends PhabricatorConfigSchemaSpec {
public function buildSchemata() {
$this->buildLiskSchemata('DifferentialDAO');
-// $this->addEdgeSchemata($server, new DifferentialRevision());
+
+ $this->buildEdgeSchemata(new DifferentialRevision());
+
+ $this->buildTransactionSchema(
+ new DifferentialTransaction(),
+ new DifferentialTransactionComment());
+
+ $this->buildCustomFieldSchemata(
+ new DifferentialCustomFieldStorage(),
+ array(
+ new DifferentialCustomFieldNumericIndex(),
+ new DifferentialCustomFieldStringIndex(),
+ ));
+
+ $this->buildRawSchema(
+ id(new DifferentialRevision())->getApplicationName(),
+ DifferentialChangeset::TABLE_CACHE,
+ array(
+ 'id' => 'id',
+ 'cache' => 'bytes',
+ 'dateCreated' => 'epoch',
+ ),
+ array(
+ 'PRIMARY' => array(
+ 'columns' => array('id'),
+ 'unique' => true,
+ ),
+ 'dateCreated' => array(
+ 'columns' => array('dateCreated'),
+ ),
+ ));
+
+ $this->buildRawSchema(
+ id(new DifferentialRevision())->getApplicationName(),
+ DifferentialRevision::TABLE_COMMIT,
+ array(
+ 'revisionID' => 'id',
+ 'commitPHID' => 'phid',
+ ),
+ array(
+ 'PRIMARY' => array(
+ 'columns' => array('revisionID', 'commitPHID'),
+ 'unique' => true,
+ ),
+ 'commitPHID' => array(
+ 'columns' => array('commitPHID'),
+ 'unique' => true,
+ ),
+ ));
+
+ $this->buildRawSchema(
+ id(new DifferentialRevision())->getApplicationName(),
+ ArcanistDifferentialRevisionHash::TABLE_NAME,
+ array(
+ 'revisionID' => 'id',
+ 'type' => 'bytes4',
+ 'hash' => 'bytes40',
+ ),
+ array(
+ 'type' => array(
+ 'columns' => array('type', 'hash'),
+ ),
+ 'revisionID' => array(
+ 'columns' => array('revisionID'),
+ ),
+ ));
+
+
}
}
diff --git a/src/applications/differential/storage/DifferentialTransactionComment.php b/src/applications/differential/storage/DifferentialTransactionComment.php
index 90086618ec..d8835e3b1a 100644
--- a/src/applications/differential/storage/DifferentialTransactionComment.php
+++ b/src/applications/differential/storage/DifferentialTransactionComment.php
@@ -1,64 +1,90 @@
<?php
final class DifferentialTransactionComment
extends PhabricatorApplicationTransactionComment {
protected $revisionPHID;
protected $changesetID;
protected $isNewFile = 0;
protected $lineNumber = 0;
protected $lineLength = 0;
protected $fixedState;
protected $hasReplies = 0;
protected $replyToCommentPHID;
public function getApplicationTransactionObject() {
return new DifferentialTransaction();
}
+ public function getConfiguration() {
+ $config = parent::getConfiguration();
+ $config[self::CONFIG_COLUMN_SCHEMA] = array(
+ 'revisionPHID' => 'phid?',
+ 'changesetID' => 'id?',
+ 'isNewFile' => 'bool',
+ 'lineNumber' => 'uint32',
+ 'lineLength' => 'uint32',
+ 'fixedState' => 'text12?',
+ 'hasReplies' => 'bool',
+ 'replyToCommentPHID' => 'phid?',
+ ) + $config[self::CONFIG_COLUMN_SCHEMA];
+ $config[self::CONFIG_KEY_SCHEMA] = array(
+ 'key_draft' => array(
+ 'columns' => array('authorPHID', 'transactionPHID'),
+ ),
+ 'key_changeset' => array(
+ 'columns' => array('changesetID'),
+ ),
+ 'key_revision' => array(
+ 'columns' => array('revisionPHID'),
+ ),
+ ) + $config[self::CONFIG_KEY_SCHEMA];
+ return $config;
+ }
+
public function shouldUseMarkupCache($field) {
// Only cache submitted comments.
return ($this->getTransactionPHID() != null);
}
public static function sortAndGroupInlines(
array $inlines,
array $changesets) {
assert_instances_of($inlines, 'DifferentialTransaction');
assert_instances_of($changesets, 'DifferentialChangeset');
$changesets = mpull($changesets, null, 'getID');
$changesets = msort($changesets, 'getFilename');
// Group the changesets by file and reorder them by display order.
$inline_groups = array();
foreach ($inlines as $inline) {
$changeset_id = $inline->getComment()->getChangesetID();
$inline_groups[$changeset_id][] = $inline;
}
$inline_groups = array_select_keys($inline_groups, array_keys($changesets));
foreach ($inline_groups as $changeset_id => $group) {
// Sort the group of inlines by line number.
$items = array();
foreach ($group as $inline) {
$comment = $inline->getComment();
$num = $comment->getLineNumber();
$len = $comment->getLineLength();
$id = $comment->getID();
$items[] = array(
'inline' => $inline,
'sort' => sprintf('~%010d%010d%010d', $num, $len, $id),
);
}
$items = isort($items, 'sort');
$items = ipull($items, 'inline');
$inline_groups[$changeset_id] = $items;
}
return $inline_groups;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 17:45 (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1127098
Default Alt Text
(58 KB)

Event Timeline