diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1360,9 +1360,11 @@ 'PhabricatorConfigProxySource' => 'infrastructure/env/PhabricatorConfigProxySource.php', 'PhabricatorConfigResponse' => 'applications/config/response/PhabricatorConfigResponse.php', 'PhabricatorConfigSchemaQuery' => 'applications/config/schema/PhabricatorConfigSchemaQuery.php', + 'PhabricatorConfigSchemaSpec' => 'applications/config/schema/PhabricatorConfigSchemaSpec.php', 'PhabricatorConfigServerSchema' => 'applications/config/schema/PhabricatorConfigServerSchema.php', 'PhabricatorConfigSource' => 'infrastructure/env/PhabricatorConfigSource.php', 'PhabricatorConfigStackSource' => 'infrastructure/env/PhabricatorConfigStackSource.php', + 'PhabricatorConfigStorageSchema' => 'applications/config/schema/PhabricatorConfigStorageSchema.php', 'PhabricatorConfigTableSchema' => 'applications/config/schema/PhabricatorConfigTableSchema.php', 'PhabricatorConfigTransaction' => 'applications/config/storage/PhabricatorConfigTransaction.php', 'PhabricatorConfigTransactionQuery' => 'applications/config/query/PhabricatorConfigTransactionQuery.php', @@ -4216,11 +4218,11 @@ 'PhabricatorConduitTokenController' => 'PhabricatorConduitController', 'PhabricatorConfigAllController' => 'PhabricatorConfigController', 'PhabricatorConfigApplication' => 'PhabricatorApplication', - 'PhabricatorConfigColumnSchema' => 'Phobject', + 'PhabricatorConfigColumnSchema' => 'PhabricatorConfigStorageSchema', 'PhabricatorConfigConfigPHIDType' => 'PhabricatorPHIDType', 'PhabricatorConfigController' => 'PhabricatorController', 'PhabricatorConfigDatabaseController' => 'PhabricatorConfigController', - 'PhabricatorConfigDatabaseSchema' => 'Phobject', + 'PhabricatorConfigDatabaseSchema' => 'PhabricatorConfigStorageSchema', 'PhabricatorConfigDatabaseSource' => 'PhabricatorConfigProxySource', 'PhabricatorConfigDefaultSource' => 'PhabricatorConfigProxySource', 'PhabricatorConfigDictionarySource' => 'PhabricatorConfigSource', @@ -4252,9 +4254,11 @@ 'PhabricatorConfigProxySource' => 'PhabricatorConfigSource', 'PhabricatorConfigResponse' => 'AphrontHTMLResponse', 'PhabricatorConfigSchemaQuery' => 'Phobject', - 'PhabricatorConfigServerSchema' => 'Phobject', + 'PhabricatorConfigSchemaSpec' => 'Phobject', + 'PhabricatorConfigServerSchema' => 'PhabricatorConfigStorageSchema', 'PhabricatorConfigStackSource' => 'PhabricatorConfigSource', - 'PhabricatorConfigTableSchema' => 'Phobject', + 'PhabricatorConfigStorageSchema' => 'Phobject', + 'PhabricatorConfigTableSchema' => 'PhabricatorConfigStorageSchema', 'PhabricatorConfigTransaction' => 'PhabricatorApplicationTransaction', 'PhabricatorConfigTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorConfigValidationException' => 'Exception', diff --git a/src/applications/config/application/PhabricatorConfigApplication.php b/src/applications/config/application/PhabricatorConfigApplication.php --- a/src/applications/config/application/PhabricatorConfigApplication.php +++ b/src/applications/config/application/PhabricatorConfigApplication.php @@ -42,7 +42,10 @@ 'edit/(?P[\w\.\-]+)/' => 'PhabricatorConfigEditController', 'group/(?P[^/]+)/' => 'PhabricatorConfigGroupController', 'welcome/' => 'PhabricatorConfigWelcomeController', - 'database/(?:(?P[^/]+)/(?:(?P[^/]+)/)?)?' + 'database/'. + '(?:(?P[^/]+)/'. + '(?:(?P
[^/]+)/'. + '(?:(?P[^/]+)/)?)?)?' => 'PhabricatorConfigDatabaseController', '(?Pignore|unignore)/(?P[^/]+)/' => 'PhabricatorConfigIgnoreController', diff --git a/src/applications/config/controller/PhabricatorConfigDatabaseController.php b/src/applications/config/controller/PhabricatorConfigDatabaseController.php --- a/src/applications/config/controller/PhabricatorConfigDatabaseController.php +++ b/src/applications/config/controller/PhabricatorConfigDatabaseController.php @@ -5,10 +5,12 @@ private $database; private $table; + private $column; public function willProcessRequest(array $data) { $this->database = idx($data, 'database'); $this->table = idx($data, 'table'); + $this->column = idx($data, 'column'); } public function processRequest() { @@ -31,22 +33,34 @@ $actual = $query->loadActualSchema(); $expect = $query->loadExpectedSchema(); + $comp = $query->buildComparisonSchema($expect, $actual); - if ($this->table) { - return $this->renderTable( + if ($this->column) { + return $this->renderColumn( + $comp, + $expect, $actual, + $this->database, + $this->table, + $this->column); + } else if ($this->table) { + return $this->renderTable( + $comp, $expect, + $actual, $this->database, $this->table); } else if ($this->database) { return $this->renderDatabase( - $actual, + $comp, $expect, + $actual, $this->database); } else { return $this->renderServer( - $actual, - $expect); + $comp, + $expect, + $actual); } } @@ -83,41 +97,29 @@ private function renderServer( - PhabricatorConfigServerSchema $schema, - PhabricatorConfigServerSchema $expect) { - - $icon_ok = id(new PHUIIconView()) - ->setIconFont('fa-check-circle green'); + PhabricatorConfigServerSchema $comp, + PhabricatorConfigServerSchema $expect, + PhabricatorConfigServerSchema $actual) { - $icon_warn = id(new PHUIIconView()) - ->setIconFont('fa-exclamation-circle yellow'); + $charset_issue = PhabricatorConfigStorageSchema::ISSUE_CHARSET; + $collation_issue = PhabricatorConfigStorageSchema::ISSUE_COLLATION; $rows = array(); - foreach ($schema->getDatabases() as $database_name => $database) { - - $expect_database = $expect->getDatabase($database_name); - if ($expect_database) { - $expect_set = $expect_database->getCharacterSet(); - $expect_collation = $expect_database->getCollation(); - - if ($database->isSameSchema($expect_database)) { - $icon = $icon_ok; - } else { - $icon = $icon_warn; - } + foreach ($comp->getDatabases() as $database_name => $database) { + $actual_database = $actual->getDatabase($database_name); + if ($actual_database) { + $charset = $actual_database->getCharacterSet(); + $collation = $actual_database->getCollation(); } else { - $expect_set = null; - $expect_collation = null; - $icon = $icon_warn; + $charset = null; + $collation = null; } - $actual_set = $database->getCharacterSet(); - $actual_collation = $database->getCollation(); - - + $status = $database->getStatus(); + $issues = $database->getIssues(); $rows[] = array( - $icon, + $this->renderIcon($status), phutil_tag( 'a', array( @@ -125,10 +127,8 @@ '/database/'.$database_name.'/'), ), $database_name), - $actual_set, - $expect_set, - $actual_collation, - $expect_collation, + $this->renderAttr($charset, $database->hasIssue($charset_issue)), + $this->renderAttr($collation, $database->hasIssue($collation_issue)), ); } @@ -138,13 +138,11 @@ null, pht('Database'), pht('Charset'), - pht('Expected Charset'), pht('Collation'), - pht('Expected Collation'), )) ->setColumnClasses( array( - '', + null, 'wide pri', null, null, @@ -152,26 +150,38 @@ $title = pht('Database Status'); + $properties = $this->buildProperties( + array( + ), + $comp->getIssues()); + $box = id(new PHUIObjectBoxView()) ->setHeaderText($title) + ->addPropertyList($properties) ->appendChild($table); return $this->buildResponse($title, $box); } private function renderDatabase( - PhabricatorConfigServerSchema $schema, + PhabricatorConfigServerSchema $comp, PhabricatorConfigServerSchema $expect, + PhabricatorConfigServerSchema $actual, $database_name) { - $database = $schema->getDatabase($database_name); + $database = $comp->getDatabase($database_name); if (!$database) { return new Aphront404Response(); } $rows = array(); foreach ($database->getTables() as $table_name => $table) { + + $status = $table->getStatus(); + $issues = $table->getIssues(); + $rows[] = array( + $this->renderIcon($status), phutil_tag( 'a', array( @@ -186,31 +196,74 @@ $table = id(new AphrontTableView($rows)) ->setHeaders( array( + null, pht('Table'), pht('Collation'), )) ->setColumnClasses( array( + null, 'wide pri', null, )); $title = pht('Database Status: %s', $database_name); + $actual_database = $actual->getDatabase($database_name); + if ($actual_database) { + $actual_charset = $actual_database->getCharacterSet(); + $actual_collation = $actual_database->getCollation(); + } else { + $actual_charset = null; + $actual_collation = null; + } + + $expect_database = $expect->getDatabase($database_name); + if ($expect_database) { + $expect_charset = $expect_database->getCharacterSet(); + $expect_collation = $expect_database->getCollation(); + } else { + $expect_charset = null; + $expect_collation = null; + } + + $properties = $this->buildProperties( + array( + array( + pht('Character Set'), + $actual_charset, + ), + array( + pht('Expected Character Set'), + $expect_charset, + ), + array( + pht('Collation'), + $actual_collation, + ), + array( + pht('Expected Collation'), + $expect_collation, + ), + ), + $database->getIssues()); + $box = id(new PHUIObjectBoxView()) ->setHeaderText($title) + ->addPropertyList($properties) ->appendChild($table); return $this->buildResponse($title, $box); } private function renderTable( - PhabricatorConfigServerSchema $schema, + PhabricatorConfigServerSchema $comp, PhabricatorConfigServerSchema $expect, + PhabricatorConfigServerSchema $actual, $database_name, $table_name) { - $database = $schema->getDatabase($database_name); + $database = $comp->getDatabase($database_name); if (!$database) { return new Aphront404Response(); } @@ -222,17 +275,30 @@ $rows = array(); foreach ($table->getColumns() as $column_name => $column) { + $status = $column->getStatus(); + $rows[] = array( - $column_name, + $this->renderIcon($status), + phutil_tag( + 'a', + array( + 'href' => $this->getApplicationURI( + 'database/'. + $database_name.'/'. + $table_name.'/'. + $column_name.'/'), + ), + $column_name), $column->getColumnType(), $column->getCharacterSet(), $column->getCollation(), ); } - $table = id(new AphrontTableView($rows)) + $table_view = id(new AphrontTableView($rows)) ->setHeaders( array( + null, pht('Table'), pht('Column Type'), pht('Character Set'), @@ -240,6 +306,7 @@ )) ->setColumnClasses( array( + null, 'wide pri', null, null, @@ -248,11 +315,133 @@ $title = pht('Database Status: %s.%s', $database_name, $table_name); + $properties = $this->buildProperties( + array( + ), + $table->getIssues()); + $box = id(new PHUIObjectBoxView()) ->setHeaderText($title) - ->appendChild($table); + ->addPropertyList($properties) + ->appendChild($table_view); + + return $this->buildResponse($title, $box); + } + + private function renderColumn( + PhabricatorConfigServerSchema $comp, + PhabricatorConfigServerSchema $expect, + PhabricatorConfigServerSchema $actual, + $database_name, + $table_name, + $column_name) { + + $database = $comp->getDatabase($database_name); + if (!$database) { + return new Aphront404Response(); + } + + $table = $database->getTable($table_name); + if (!$table) { + return new Aphront404Response(); + } + + $column = $table->getColumn($column_name); + if (!$table) { + return new Aphront404Response(); + } + + $title = pht( + 'Database Status: %s.%s.%s', + $database_name, + $table_name, + $column_name); + + $properties = $this->buildProperties( + array( + ), + $column->getIssues()); + + $box = id(new PHUIObjectBoxView()) + ->setHeaderText($title) + ->addPropertyList($properties); return $this->buildResponse($title, $box); } + private function renderIcon($status) { + switch ($status) { + case PhabricatorConfigStorageSchema::STATUS_OKAY: + $icon = 'fa-check-circle green'; + break; + case PhabricatorConfigStorageSchema::STATUS_WARN: + $icon = 'fa-exclamation-circle yellow'; + break; + case PhabricatorConfigStorageSchema::STATUS_FAIL: + default: + $icon = 'fa-times-circle red'; + break; + } + + return id(new PHUIIconView()) + ->setIconFont($icon); + } + + private function renderAttr($attr, $issue) { + if ($issue) { + return phutil_tag( + 'span', + array( + 'style' => 'color: #aa0000;', + ), + $attr); + } else { + return $attr; + } + } + + private function buildProperties(array $properties, array $issues) { + $view = id(new PHUIPropertyListView()) + ->setUser($this->getRequest()->getUser()); + + foreach ($properties as $property) { + list($key, $value) = $property; + $view->addProperty($key, $value); + } + + $status_view = new PHUIStatusListView(); + if (!$issues) { + $status_view->addItem( + id(new PHUIStatusItemView()) + ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green') + ->setTarget(pht('No Schema Issues'))); + } else { + foreach ($issues as $issue) { + $note = PhabricatorConfigStorageSchema::getIssueDescription($issue); + + $status = PhabricatorConfigStorageSchema::getIssueStatus($issue); + switch ($status) { + case PhabricatorConfigStorageSchema::STATUS_WARN: + $icon = PHUIStatusItemView::ICON_WARNING; + $color = 'yellow'; + break; + case PhabricatorConfigStorageSchema::STATUS_FAIL: + default: + $icon = PHUIStatusItemView::ICON_REJECT; + $color = 'red'; + break; + } + + $item = id(new PHUIStatusItemView()) + ->setTarget(PhabricatorConfigStorageSchema::getIssueName($issue)) + ->setIcon($icon, $color) + ->setNote($note); + + $status_view->addItem($item); + } + } + $view->addProperty(pht('Schema Status'), $status_view); + + return $view; + } } diff --git a/src/applications/config/schema/PhabricatorConfigColumnSchema.php b/src/applications/config/schema/PhabricatorConfigColumnSchema.php --- a/src/applications/config/schema/PhabricatorConfigColumnSchema.php +++ b/src/applications/config/schema/PhabricatorConfigColumnSchema.php @@ -1,8 +1,8 @@ columnType; } + protected function getSubschemata() { + return array(); + } + public function setCollation($collation) { $this->collation = $collation; return $this; @@ -34,13 +38,28 @@ return $this->characterSet; } - public function setName($name) { - $this->name = $name; - return $this; + public function compareToSimilarSchema( + PhabricatorConfigStorageSchema $expect) { + + $issues = array(); + if ($this->getCharacterSet() != $expect->getCharacterSet()) { + $issues[] = self::ISSUE_CHARSET; + } + + if ($this->getCollation() != $expect->getCollation()) { + $issues[] = self::ISSUE_COLLATION; + } + + if ($this->getColumnType() != $expect->getColumnType()) { + $issues[] = self::ISSUE_COLUMNTYPE; + } + + return $issues; } - public function getName() { - return $this->name; + public function newEmptyClone() { + $clone = clone $this; + return $clone; } } diff --git a/src/applications/config/schema/PhabricatorConfigDatabaseSchema.php b/src/applications/config/schema/PhabricatorConfigDatabaseSchema.php --- a/src/applications/config/schema/PhabricatorConfigDatabaseSchema.php +++ b/src/applications/config/schema/PhabricatorConfigDatabaseSchema.php @@ -1,8 +1,8 @@ tables, $key); } - public function isSameSchema(PhabricatorConfigDatabaseSchema $expect) { - return ($this->toDictionary() === $expect->toDictionary()); + protected function getSubschemata() { + return $this->getTables(); } - public function toDictionary() { - return array( - 'name' => $this->getName(), - 'characterSet' => $this->getCharacterSet(), - 'collation' => $this->getCollation(), - ); + public function compareToSimilarSchema( + PhabricatorConfigStorageSchema $expect) { + + $issues = array(); + if ($this->getCharacterSet() != $expect->getCharacterSet()) { + $issues[] = self::ISSUE_CHARSET; + } + + if ($this->getCollation() != $expect->getCollation()) { + $issues[] = self::ISSUE_COLLATION; + } + + return $issues; + } + + public function newEmptyClone() { + $clone = clone $this; + $clone->tables = array(); + return $clone; } public function setCollation($collation) { @@ -55,13 +68,4 @@ return $this->characterSet; } - public function setName($name) { - $this->name = $name; - return $this; - } - - public function getName() { - return $this->name; - } - } diff --git a/src/applications/config/schema/PhabricatorConfigSchemaQuery.php b/src/applications/config/schema/PhabricatorConfigSchemaQuery.php --- a/src/applications/config/schema/PhabricatorConfigSchemaQuery.php +++ b/src/applications/config/schema/PhabricatorConfigSchemaQuery.php @@ -92,7 +92,6 @@ return $server_schema; } - public function loadExpectedSchema() { $databases = $this->getDatabaseNames(); @@ -119,17 +118,101 @@ $utf8_collate = 'binary'; } + $specs = id(new PhutilSymbolLoader()) + ->setAncestorClass('PhabricatorConfigSchemaSpec') + ->loadObjects(); + $server_schema = new PhabricatorConfigServerSchema(); - foreach ($databases as $database_name) { - $database_schema = id(new PhabricatorConfigDatabaseSchema()) - ->setName($database_name) - ->setCharacterSet($utf8_charset) - ->setCollation($utf8_collate); + foreach ($specs as $spec) { + $spec->setUTF8Charset($utf8_charset); + $spec->setUTF8Collate($utf8_collate); - $server_schema->addDatabase($database_schema); + $spec->buildSchemata($server_schema); } return $server_schema; } + public function buildComparisonSchema( + PhabricatorConfigServerSchema $expect, + PhabricatorConfigServerSchema $actual) { + + $comp_server = $actual->newEmptyClone(); + + $all_databases = $actual->getDatabases() + $expect->getDatabases(); + foreach ($all_databases as $database_name => $database_template) { + $actual_database = $actual->getDatabase($database_name); + $expect_database = $expect->getDatabase($database_name); + + $issues = $this->compareSchemata($expect_database, $actual_database); + + $comp_database = $database_template->newEmptyClone() + ->setIssues($issues); + + if (!$actual_database) { + $actual_database = $expect_database->newEmptyClone(); + } + if (!$expect_database) { + $expect_database = $actual_database->newEmptyClone(); + } + + $all_tables = + $actual_database->getTables() + + $expect_database->getTables(); + foreach ($all_tables as $table_name => $table_template) { + $actual_table = $actual_database->getTable($table_name); + $expect_table = $expect_database->getTable($table_name); + + $issues = $this->compareSchemata($expect_table, $actual_table); + + $comp_table = $table_template->newEmptyClone() + ->setIssues($issues); + + if (!$actual_table) { + $actual_table = $expect_table->newEmptyClone(); + } + if (!$expect_table) { + $expect_table = $actual_table->newEmptyClone(); + } + + $all_columns = + $actual_table->getColumns() + + $expect_table->getColumns(); + foreach ($all_columns as $column_name => $column_template) { + $actual_column = $actual_table->getColumn($column_name); + $expect_column = $expect_table->getColumn($column_name); + + $issues = $this->compareSchemata($expect_column, $actual_column); + + $comp_column = $column_template->newEmptyClone() + ->setIssues($issues); + + $comp_table->addColumn($comp_column); + } + $comp_database->addTable($comp_table); + } + $comp_server->addDatabase($comp_database); + } + + return $comp_server; + } + + private function compareSchemata( + PhabricatorConfigStorageSchema $expect = null, + PhabricatorConfigStorageSchema $actual = null) { + + if (!$expect && !$actual) { + throw new Exception(pht('Can not compare two missing schemata!')); + } else if ($expect && !$actual) { + $issues = array(PhabricatorConfigStorageSchema::ISSUE_MISSING); + } else if ($actual && !$expect) { + $issues = array(PhabricatorConfigStorageSchema::ISSUE_SURPLUS); + } else { + $issues = $actual->compareTo($expect); + } + + return $issues; + } + + } diff --git a/src/applications/config/schema/PhabricatorConfigSchemaSpec.php b/src/applications/config/schema/PhabricatorConfigSchemaSpec.php new file mode 100644 --- /dev/null +++ b/src/applications/config/schema/PhabricatorConfigSchemaSpec.php @@ -0,0 +1,7 @@ +getDatabases(), $key); } + protected function getSubschemata() { + return $this->getDatabases(); + } + + public function compareToSimilarSchema( + PhabricatorConfigStorageSchema $expect) { + return array(); + } + + public function newEmptyClone() { + $clone = clone $this; + $clone->databases = array(); + return $clone; + } + } diff --git a/src/applications/config/schema/PhabricatorConfigStorageSchema.php b/src/applications/config/schema/PhabricatorConfigStorageSchema.php new file mode 100644 --- /dev/null +++ b/src/applications/config/schema/PhabricatorConfigStorageSchema.php @@ -0,0 +1,172 @@ +getName() != $expect->getName()) { + throw new Exception(pht('Names must match to compare schemata!')); + } + + return $this->compareToSimilarSchema($expect); + } + + public function setName($name) { + $this->name = $name; + return $this; + } + + public function getName() { + return $this->name; + } + + public function setIssues(array $issues) { + $this->issues = array_fuse($issues); + return $this; + } + + public function getIssues() { + $issues = $this->issues; + + foreach ($this->getSubschemata() as $sub) { + switch ($sub->getStatus()) { + case self::STATUS_WARN: + $issues[self::ISSUE_SUBWARN] = self::ISSUE_SUBWARN; + break; + case self::STATUS_FAIL: + $issues[self::ISSUE_SUBFAIL] = self::ISSUE_SUBFAIL; + break; + } + } + + return $issues; + } + + public function hasIssue($issue) { + return (bool)idx($this->getIssues(), $issue); + } + + public function getAllIssues() { + $issues = $this->getIssues(); + foreach ($this->getSubschemata() as $sub) { + $issues += $sub->getAllIssues(); + } + return $issues; + } + + public function getStatus() { + $status = self::STATUS_OKAY; + foreach ($this->getAllIssues() as $issue) { + $issue_status = self::getIssueStatus($issue); + $status = self::getStrongestStatus($status, $issue_status); + } + return $status; + } + + public static function getIssueName($issue) { + switch ($issue) { + case self::ISSUE_MISSING: + return pht('Missing'); + case self::ISSUE_SURPLUS: + return pht('Surplus'); + case self::ISSUE_CHARSET: + return pht('Wrong Character Set'); + case self::ISSUE_COLLATION: + return pht('Wrong Collation'); + case self::ISSUE_COLUMNTYPE: + return pht('Wrong Column Type'); + case self::ISSUE_SUBWARN: + return pht('Subschemata Have Warnings'); + case self::ISSUE_SUBFAIL: + return pht('Subschemata Have Failures'); + default: + throw new Exception(pht('Unknown schema issue "%s"!', $issue)); + } + } + + public static function getIssueDescription($issue) { + switch ($issue) { + case self::ISSUE_MISSING: + return pht('This schema is expected to exist, but does not.'); + case self::ISSUE_SURPLUS: + return pht('This schema is not expected to exist.'); + case self::ISSUE_CHARSET: + return pht('This schema can use a better character set.'); + case self::ISSUE_COLLATION: + return pht('This schema can use a better collation.'); + case self::ISSUE_COLUMNTYPE: + return pht('This schema can use a better column type.'); + case self::ISSUE_SUBWARN: + return pht('Subschemata have setup warnings.'); + case self::ISSUE_SUBFAIL: + return pht('Subschemata have setup failures.'); + default: + throw new Exception(pht('Unknown schema issue "%s"!', $issue)); + } + } + + public static function getIssueStatus($issue) { + switch ($issue) { + case self::ISSUE_MISSING: + case self::ISSUE_SUBFAIL: + return self::STATUS_FAIL; + case self::ISSUE_SURPLUS: + case self::ISSUE_CHARSET: + case self::ISSUE_COLLATION: + case self::ISSUE_COLUMNTYPE: + case self::ISSUE_SUBWARN: + return self::STATUS_WARN; + default: + throw new Exception(pht('Unknown schema issue "%s"!', $issue)); + } + } + + public static function getStatusSeverity($status) { + switch ($status) { + case self::STATUS_FAIL: + return 2; + case self::STATUS_WARN: + return 1; + case self::STATUS_OKAY: + return 0; + default: + throw new Exception(pht('Unknown schema status "%s"!', $status)); + } + } + + public static function getStrongestStatus($u, $v) { + $u_sev = self::getStatusSeverity($u); + $v_sev = self::getStatusSeverity($v); + + if ($u_sev >= $v_sev) { + return $u; + } else { + return $v; + } + } + + +} diff --git a/src/applications/config/schema/PhabricatorConfigTableSchema.php b/src/applications/config/schema/PhabricatorConfigTableSchema.php --- a/src/applications/config/schema/PhabricatorConfigTableSchema.php +++ b/src/applications/config/schema/PhabricatorConfigTableSchema.php @@ -1,8 +1,8 @@ columns; } + public function getColumn($key) { + return idx($this->getColumns(), $key); + } + + protected function getSubschemata() { + return $this->getColumns(); + } + public function setCollation($collation) { $this->collation = $collation; return $this; @@ -29,13 +37,21 @@ return $this->collation; } - public function setName($name) { - $this->name = $name; - return $this; + public function compareToSimilarSchema( + PhabricatorConfigStorageSchema $expect) { + + $issues = array(); + if ($this->getCollation() != $expect->getCollation()) { + $issues[] = self::ISSUE_COLLATION; + } + + return $issues; } - public function getName() { - return $this->name; + public function newEmptyClone() { + $clone = clone $this; + $clone->columns = array(); + return $clone; } }