diff --git a/resources/celerity/map.php b/resources/celerity/map.php index d3a9a1a34f..720683c371 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ return array( 'names' => array( 'conpherence.pkg.css' => 'e68cf1fa', 'conpherence.pkg.js' => '15191c65', - 'core.pkg.css' => 'badf3f16', + 'core.pkg.css' => '2574c199', 'core.pkg.js' => 'b5a949ca', 'differential.pkg.css' => '06dc617c', 'differential.pkg.js' => 'c1cfa143', @@ -122,7 +122,7 @@ return array( 'rsrc/css/layout/phabricator-source-code-view.css' => '2ab25dfa', 'rsrc/css/phui/button/phui-button-bar.css' => 'f1ff5494', 'rsrc/css/phui/button/phui-button-simple.css' => '8e1baf68', - 'rsrc/css/phui/button/phui-button.css' => '1863cc6e', + 'rsrc/css/phui/button/phui-button.css' => '6ccb303c', 'rsrc/css/phui/calendar/phui-calendar-day.css' => '572b1893', 'rsrc/css/phui/calendar/phui-calendar-list.css' => '576be600', 'rsrc/css/phui/calendar/phui-calendar-month.css' => '21154caf', @@ -146,12 +146,12 @@ return array( 'rsrc/css/phui/phui-comment-panel.css' => 'f50152ad', 'rsrc/css/phui/phui-crumbs-view.css' => '10728aaa', 'rsrc/css/phui/phui-curtain-view.css' => '2bdaf026', - 'rsrc/css/phui/phui-document-pro.css' => '1a08ef4b', + 'rsrc/css/phui/phui-document-pro.css' => 'd033e8d5', 'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf', 'rsrc/css/phui/phui-document.css' => 'c4ac41f9', 'rsrc/css/phui/phui-feed-story.css' => '44a9c8e9', 'rsrc/css/phui/phui-fontkit.css' => '1320ed01', - 'rsrc/css/phui/phui-form-view.css' => 'f808e5be', + 'rsrc/css/phui/phui-form-view.css' => '2f43fae7', 'rsrc/css/phui/phui-form.css' => '7aaa04e3', 'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f', 'rsrc/css/phui/phui-header-view.css' => '1ba8b707', @@ -423,7 +423,7 @@ return array( 'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e2e0a072', 'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08', 'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f', - 'rsrc/js/application/transactions/behavior-comment-actions.js' => '54110499', + 'rsrc/js/application/transactions/behavior-comment-actions.js' => '038bf27f', 'rsrc/js/application/transactions/behavior-reorder-configs.js' => 'd7a74243', 'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96', 'rsrc/js/application/transactions/behavior-show-older-transactions.js' => '8f29b364', @@ -575,7 +575,7 @@ return array( 'javelin-behavior-bulk-job-reload' => 'edf8a145', 'javelin-behavior-calendar-month-view' => 'fe33e256', 'javelin-behavior-choose-control' => '327a00d1', - 'javelin-behavior-comment-actions' => '54110499', + 'javelin-behavior-comment-actions' => '038bf27f', 'javelin-behavior-config-reorder-fields' => 'b6993408', 'javelin-behavior-conpherence-menu' => '4047cd35', 'javelin-behavior-conpherence-participant-pane' => 'd057e45a', @@ -800,7 +800,7 @@ return array( 'phui-box-css' => '4bd6cdb9', 'phui-bulk-editor-css' => '9a81e5d5', 'phui-button-bar-css' => 'f1ff5494', - 'phui-button-css' => '1863cc6e', + 'phui-button-css' => '6ccb303c', 'phui-button-simple-css' => '8e1baf68', 'phui-calendar-css' => 'f1ddf11c', 'phui-calendar-day-css' => '572b1893', @@ -814,12 +814,12 @@ return array( 'phui-curtain-view-css' => '2bdaf026', 'phui-document-summary-view-css' => '9ca48bdf', 'phui-document-view-css' => 'c4ac41f9', - 'phui-document-view-pro-css' => '1a08ef4b', + 'phui-document-view-pro-css' => 'd033e8d5', 'phui-feed-story-css' => '44a9c8e9', 'phui-font-icon-base-css' => '870a7360', 'phui-fontkit-css' => '1320ed01', 'phui-form-css' => '7aaa04e3', - 'phui-form-view-css' => 'f808e5be', + 'phui-form-view-css' => '2f43fae7', 'phui-head-thing-view-css' => 'fd311e5f', 'phui-header-view-css' => '1ba8b707', 'phui-hovercard' => '1bd28176', @@ -905,6 +905,15 @@ return array( 'javelin-behavior', 'javelin-uri', ), + '038bf27f' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-workflow', + 'javelin-dom', + 'phuix-form-control-view', + 'phuix-icon-view', + 'javelin-behavior-phabricator-gesture', + ), '040fce04' => array( 'javelin-behavior', 'javelin-request', @@ -1251,15 +1260,6 @@ return array( 'javelin-vector', 'javelin-typeahead-static-source', ), - 54110499 => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-workflow', - 'javelin-dom', - 'phuix-form-control-view', - 'phuix-icon-view', - 'javelin-behavior-phabricator-gesture', - ), '549459b8' => array( 'javelin-behavior', ), diff --git a/resources/sql/autopatches/20180910.audit.01.searches.php b/resources/sql/autopatches/20180910.audit.01.searches.php new file mode 100644 index 0000000000..f68e76fe45 --- /dev/null +++ b/resources/sql/autopatches/20180910.audit.01.searches.php @@ -0,0 +1,54 @@ +establishConnection('w'); + +$status_map = array( + 0 => 'none', + 1 => 'needs-audit', + 2 => 'concern-raised', + 3 => 'partially-audited', + 4 => 'audited', + 5 => 'needs-verification', +); + +foreach (new LiskMigrationIterator($table) as $query) { + if ($query->getEngineClassName() !== 'PhabricatorCommitSearchEngine') { + continue; + } + + $parameters = $query->getParameters(); + $status = idx($parameters, 'statuses'); + + if (!$status) { + // No saved "status" constraint. + continue; + } + + if (!is_array($status)) { + // Saved constraint isn't a list. + continue; + } + + // Migrate old integer values to new string values. + $old_status = $status; + foreach ($status as $key => $value) { + if (is_numeric($value)) { + $status[$key] = $status_map[$value]; + } + } + + if ($status === $old_status) { + // Nothing changed. + continue; + } + + $parameters['statuses'] = $status; + + queryfx( + $conn, + 'UPDATE %T SET parameters = %s WHERE id = %d', + $table->getTableName(), + phutil_json_encode($parameters), + $query->getID()); +} diff --git a/resources/sql/autopatches/20180910.audit.02.string.sql b/resources/sql/autopatches/20180910.audit.02.string.sql new file mode 100644 index 0000000000..4caa4a1724 --- /dev/null +++ b/resources/sql/autopatches/20180910.audit.02.string.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_repository.repository_commit + CHANGE auditStatus auditStatus VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20180910.audit.03.status.php b/resources/sql/autopatches/20180910.audit.03.status.php new file mode 100644 index 0000000000..b7a0bf9f58 --- /dev/null +++ b/resources/sql/autopatches/20180910.audit.03.status.php @@ -0,0 +1,28 @@ +establishConnection('w'); + +$status_map = array( + 0 => 'none', + 1 => 'needs-audit', + 2 => 'concern-raised', + 3 => 'partially-audited', + 4 => 'audited', + 5 => 'needs-verification', +); + +foreach (new LiskMigrationIterator($table) as $commit) { + $status = $commit->getAuditStatus(); + + if (!isset($status_map[$status])) { + continue; + } + + queryfx( + $conn, + 'UPDATE %T SET auditStatus = %s WHERE id = %d', + $table->getTableName(), + $status_map[$status], + $commit->getID()); +} diff --git a/resources/sql/autopatches/20180910.audit.04.xactions.php b/resources/sql/autopatches/20180910.audit.04.xactions.php new file mode 100644 index 0000000000..1ecf9ef320 --- /dev/null +++ b/resources/sql/autopatches/20180910.audit.04.xactions.php @@ -0,0 +1,48 @@ +establishConnection('w'); + +$status_map = array( + 0 => 'none', + 1 => 'needs-audit', + 2 => 'concern-raised', + 3 => 'partially-audited', + 4 => 'audited', + 5 => 'needs-verification', +); + +$state_type = DiffusionCommitStateTransaction::TRANSACTIONTYPE; + +foreach (new LiskMigrationIterator($table) as $xaction) { + if ($xaction->getTransactionType() !== $state_type) { + continue; + } + + $old_value = $xaction->getOldValue(); + $new_value = $xaction->getNewValue(); + + $any_change = false; + + if (isset($status_map[$old_value])) { + $old_value = $status_map[$old_value]; + $any_change = true; + } + + if (isset($status_map[$new_value])) { + $new_value = $status_map[$new_value]; + $any_change = true; + } + + if (!$any_change) { + continue; + } + + queryfx( + $conn, + 'UPDATE %T SET oldValue = %s, newValue = %s WHERE id = %d', + $table->getTableName(), + phutil_json_encode($old_value), + phutil_json_encode($new_value), + $xaction->getID()); +} diff --git a/resources/sql/autopatches/20180914.audit.01.mailkey.php b/resources/sql/autopatches/20180914.audit.01.mailkey.php new file mode 100644 index 0000000000..de8419a3c4 --- /dev/null +++ b/resources/sql/autopatches/20180914.audit.01.mailkey.php @@ -0,0 +1,26 @@ +establishConnection('w'); +$commit_name = $commit_table->getTableName(); + +$properties_table = new PhabricatorMetaMTAMailProperties(); +$conn = $properties_table->establishConnection('w'); + +$iterator = new LiskRawMigrationIterator($commit_conn, $commit_name); +foreach ($iterator as $commit) { + queryfx( + $conn, + 'INSERT IGNORE INTO %T + (objectPHID, mailProperties, dateCreated, dateModified) + VALUES + (%s, %s, %d, %d)', + $properties_table->getTableName(), + $commit['phid'], + phutil_json_encode( + array( + 'mailKey' => $commit['mailKey'], + )), + PhabricatorTime::getNow(), + PhabricatorTime::getNow()); +} diff --git a/resources/sql/autopatches/20180914.audit.02.rmkey.sql b/resources/sql/autopatches/20180914.audit.02.rmkey.sql new file mode 100644 index 0000000000..3187da4258 --- /dev/null +++ b/resources/sql/autopatches/20180914.audit.02.rmkey.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_repository.repository_commit + DROP mailKey; diff --git a/resources/sql/autopatches/20180914.drydock.01.operationphid.sql b/resources/sql/autopatches/20180914.drydock.01.operationphid.sql new file mode 100644 index 0000000000..bdfe02b0df --- /dev/null +++ b/resources/sql/autopatches/20180914.drydock.01.operationphid.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_drydock.drydock_log + ADD operationPHID VARBINARY(64); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 9774741060..8bcf3d6120 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -704,6 +704,7 @@ phutil_register_library_map(array( 'DiffusionCommitAcceptTransaction' => 'applications/diffusion/xaction/DiffusionCommitAcceptTransaction.php', 'DiffusionCommitActionTransaction' => 'applications/diffusion/xaction/DiffusionCommitActionTransaction.php', 'DiffusionCommitAffectedFilesHeraldField' => 'applications/diffusion/herald/DiffusionCommitAffectedFilesHeraldField.php', + 'DiffusionCommitAuditStatus' => 'applications/diffusion/DiffusionCommitAuditStatus.php', 'DiffusionCommitAuditTransaction' => 'applications/diffusion/xaction/DiffusionCommitAuditTransaction.php', 'DiffusionCommitAuditorsHeraldField' => 'applications/diffusion/herald/DiffusionCommitAuditorsHeraldField.php', 'DiffusionCommitAuditorsTransaction' => 'applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php', @@ -1167,6 +1168,7 @@ phutil_register_library_map(array( 'DrydockManagementUpdateResourceWorkflow' => 'applications/drydock/management/DrydockManagementUpdateResourceWorkflow.php', 'DrydockManagementWorkflow' => 'applications/drydock/management/DrydockManagementWorkflow.php', 'DrydockObjectAuthorizationView' => 'applications/drydock/view/DrydockObjectAuthorizationView.php', + 'DrydockOperationWorkLogType' => 'applications/drydock/logtype/DrydockOperationWorkLogType.php', 'DrydockQuery' => 'applications/drydock/query/DrydockQuery.php', 'DrydockRepositoryOperation' => 'applications/drydock/storage/DrydockRepositoryOperation.php', 'DrydockRepositoryOperationController' => 'applications/drydock/controller/DrydockRepositoryOperationController.php', @@ -1204,6 +1206,7 @@ phutil_register_library_map(array( 'DrydockSlotLockException' => 'applications/drydock/exception/DrydockSlotLockException.php', 'DrydockSlotLockFailureLogType' => 'applications/drydock/logtype/DrydockSlotLockFailureLogType.php', 'DrydockTestRepositoryOperation' => 'applications/drydock/operation/DrydockTestRepositoryOperation.php', + 'DrydockTextLogType' => 'applications/drydock/logtype/DrydockTextLogType.php', 'DrydockWebrootInterface' => 'applications/drydock/interface/webroot/DrydockWebrootInterface.php', 'DrydockWorker' => 'applications/drydock/worker/DrydockWorker.php', 'DrydockWorkingCopyBlueprintImplementation' => 'applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php', @@ -2147,7 +2150,6 @@ phutil_register_library_map(array( 'PhabricatorAuditActionConstants' => 'applications/audit/constants/PhabricatorAuditActionConstants.php', 'PhabricatorAuditApplication' => 'applications/audit/application/PhabricatorAuditApplication.php', 'PhabricatorAuditCommentEditor' => 'applications/audit/editor/PhabricatorAuditCommentEditor.php', - 'PhabricatorAuditCommitStatusConstants' => 'applications/audit/constants/PhabricatorAuditCommitStatusConstants.php', 'PhabricatorAuditController' => 'applications/audit/controller/PhabricatorAuditController.php', 'PhabricatorAuditEditor' => 'applications/audit/editor/PhabricatorAuditEditor.php', 'PhabricatorAuditInlineComment' => 'applications/audit/storage/PhabricatorAuditInlineComment.php', @@ -5021,6 +5023,9 @@ phutil_register_library_map(array( 'PhrictionDocumentController' => 'applications/phriction/controller/PhrictionDocumentController.php', 'PhrictionDocumentDatasource' => 'applications/phriction/typeahead/PhrictionDocumentDatasource.php', 'PhrictionDocumentDeleteTransaction' => 'applications/phriction/xaction/PhrictionDocumentDeleteTransaction.php', + 'PhrictionDocumentDraftTransaction' => 'applications/phriction/xaction/PhrictionDocumentDraftTransaction.php', + 'PhrictionDocumentEditEngine' => 'applications/phriction/editor/PhrictionDocumentEditEngine.php', + 'PhrictionDocumentEditTransaction' => 'applications/phriction/xaction/PhrictionDocumentEditTransaction.php', 'PhrictionDocumentFerretEngine' => 'applications/phriction/search/PhrictionDocumentFerretEngine.php', 'PhrictionDocumentFulltextEngine' => 'applications/phriction/search/PhrictionDocumentFulltextEngine.php', 'PhrictionDocumentHeraldAdapter' => 'applications/phriction/herald/PhrictionDocumentHeraldAdapter.php', @@ -5042,6 +5047,7 @@ phutil_register_library_map(array( 'PhrictionDocumentVersionTransaction' => 'applications/phriction/xaction/PhrictionDocumentVersionTransaction.php', 'PhrictionEditConduitAPIMethod' => 'applications/phriction/conduit/PhrictionEditConduitAPIMethod.php', 'PhrictionEditController' => 'applications/phriction/controller/PhrictionEditController.php', + 'PhrictionEditEngineController' => 'applications/phriction/controller/PhrictionEditEngineController.php', 'PhrictionHistoryConduitAPIMethod' => 'applications/phriction/conduit/PhrictionHistoryConduitAPIMethod.php', 'PhrictionHistoryController' => 'applications/phriction/controller/PhrictionHistoryController.php', 'PhrictionInfoConduitAPIMethod' => 'applications/phriction/conduit/PhrictionInfoConduitAPIMethod.php', @@ -6066,6 +6072,7 @@ phutil_register_library_map(array( 'DiffusionCommitAcceptTransaction' => 'DiffusionCommitAuditTransaction', 'DiffusionCommitActionTransaction' => 'DiffusionCommitTransactionType', 'DiffusionCommitAffectedFilesHeraldField' => 'DiffusionCommitHeraldField', + 'DiffusionCommitAuditStatus' => 'Phobject', 'DiffusionCommitAuditTransaction' => 'DiffusionCommitActionTransaction', 'DiffusionCommitAuditorsHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitAuditorsTransaction' => 'DiffusionCommitTransactionType', @@ -6570,6 +6577,7 @@ phutil_register_library_map(array( 'DrydockManagementUpdateResourceWorkflow' => 'DrydockManagementWorkflow', 'DrydockManagementWorkflow' => 'PhabricatorManagementWorkflow', 'DrydockObjectAuthorizationView' => 'AphrontView', + 'DrydockOperationWorkLogType' => 'DrydockLogType', 'DrydockQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'DrydockRepositoryOperation' => array( 'DrydockDAO', @@ -6613,6 +6621,7 @@ phutil_register_library_map(array( 'DrydockSlotLockException' => 'Exception', 'DrydockSlotLockFailureLogType' => 'DrydockLogType', 'DrydockTestRepositoryOperation' => 'DrydockRepositoryOperationType', + 'DrydockTextLogType' => 'DrydockLogType', 'DrydockWebrootInterface' => 'DrydockInterface', 'DrydockWorker' => 'PhabricatorWorker', 'DrydockWorkingCopyBlueprintImplementation' => 'DrydockBlueprintImplementation', @@ -7715,7 +7724,6 @@ phutil_register_library_map(array( 'PhabricatorAuditActionConstants' => 'Phobject', 'PhabricatorAuditApplication' => 'PhabricatorApplication', 'PhabricatorAuditCommentEditor' => 'PhabricatorEditor', - 'PhabricatorAuditCommitStatusConstants' => 'Phobject', 'PhabricatorAuditController' => 'PhabricatorController', 'PhabricatorAuditEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorAuditInlineComment' => array( @@ -11136,10 +11144,13 @@ phutil_register_library_map(array( ), 'PhrictionDocumentAuthorHeraldField' => 'PhrictionDocumentHeraldField', 'PhrictionDocumentContentHeraldField' => 'PhrictionDocumentHeraldField', - 'PhrictionDocumentContentTransaction' => 'PhrictionDocumentVersionTransaction', + 'PhrictionDocumentContentTransaction' => 'PhrictionDocumentEditTransaction', 'PhrictionDocumentController' => 'PhrictionController', 'PhrictionDocumentDatasource' => 'PhabricatorTypeaheadDatasource', 'PhrictionDocumentDeleteTransaction' => 'PhrictionDocumentVersionTransaction', + 'PhrictionDocumentDraftTransaction' => 'PhrictionDocumentEditTransaction', + 'PhrictionDocumentEditEngine' => 'PhabricatorEditEngine', + 'PhrictionDocumentEditTransaction' => 'PhrictionDocumentVersionTransaction', 'PhrictionDocumentFerretEngine' => 'PhabricatorFerretEngine', 'PhrictionDocumentFulltextEngine' => 'PhabricatorFulltextEngine', 'PhrictionDocumentHeraldAdapter' => 'HeraldAdapter', @@ -11161,6 +11172,7 @@ phutil_register_library_map(array( 'PhrictionDocumentVersionTransaction' => 'PhrictionDocumentTransactionType', 'PhrictionEditConduitAPIMethod' => 'PhrictionConduitAPIMethod', 'PhrictionEditController' => 'PhrictionController', + 'PhrictionEditEngineController' => 'PhrictionController', 'PhrictionHistoryConduitAPIMethod' => 'PhrictionConduitAPIMethod', 'PhrictionHistoryController' => 'PhrictionController', 'PhrictionInfoConduitAPIMethod' => 'PhrictionConduitAPIMethod', diff --git a/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php b/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php index f3858afbf8..d0d3779c2a 100644 --- a/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php +++ b/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php @@ -68,17 +68,17 @@ final class AuditQueryConduitAPIMethod extends AuditConduitAPIMethod { $status_map = array( self::AUDIT_LEGACYSTATUS_OPEN => array( - PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT, - PhabricatorAuditCommitStatusConstants::CONCERN_RAISED, + DiffusionCommitAuditStatus::NEEDS_AUDIT, + DiffusionCommitAuditStatus::CONCERN_RAISED, ), self::AUDIT_LEGACYSTATUS_CONCERN => array( - PhabricatorAuditCommitStatusConstants::CONCERN_RAISED, + DiffusionCommitAuditStatus::CONCERN_RAISED, ), self::AUDIT_LEGACYSTATUS_ACCEPTED => array( - PhabricatorAuditCommitStatusConstants::FULLY_AUDITED, + DiffusionCommitAuditStatus::AUDITED, ), self::AUDIT_LEGACYSTATUS_PARTIAL => array( - PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED, + DiffusionCommitAuditStatus::PARTIALLY_AUDITED, ), ); diff --git a/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php b/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php deleted file mode 100644 index e559862c83..0000000000 --- a/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php +++ /dev/null @@ -1,140 +0,0 @@ - $spec) { - if (idx($spec, 'legacy') == $status) { - return self::newForStatus($key); - } - } - - return self::newForStatus($status); - } - - public static function newForStatus($status) { - $result = new self(); - - $result->key = $status; - - $map = self::getMap(); - if (isset($map[$status])) { - $result->spec = $map[$status]; - } - - return $result; - } - - public function getKey() { - return $this->key; - } - - public function getIcon() { - return idx($this->spec, 'icon'); - } - - public function getColor() { - return idx($this->spec, 'color'); - } - - public function getName() { - return idx($this->spec, 'name', pht('Unknown ("%s")', $this->key)); - } - - public static function getStatusNameMap() { - $map = self::getMap(); - return ipull($map, 'name', 'legacy'); - } - - public static function getStatusName($code) { - return idx(self::getStatusNameMap(), $code, pht('Unknown')); - } - - public static function getOpenStatusConstants() { - $constants = array(); - foreach (self::getMap() as $map) { - if (!$map['closed']) { - $constants[] = $map['legacy']; - } - } - return $constants; - } - - public static function getStatusColor($code) { - $map = self::getMap(); - $map = ipull($map, 'color', 'legacy'); - return idx($map, $code); - } - - public static function getStatusIcon($code) { - $map = self::getMap(); - $map = ipull($map, 'icon', 'legacy'); - return idx($map, $code); - } - - private static function getMap() { - return array( - self::MODERN_NONE => array( - 'name' => pht('No Audits'), - 'legacy' => self::NONE, - 'icon' => 'fa-check', - 'color' => 'bluegrey', - 'closed' => true, - ), - self::MODERN_NEEDS_AUDIT => array( - 'name' => pht('Audit Required'), - 'legacy' => self::NEEDS_AUDIT, - 'icon' => 'fa-exclamation-circle', - 'color' => 'orange', - 'closed' => false, - ), - self::MODERN_CONCERN_RAISED => array( - 'name' => pht('Concern Raised'), - 'legacy' => self::CONCERN_RAISED, - 'icon' => 'fa-times-circle', - 'color' => 'red', - 'closed' => false, - ), - self::MODERN_PARTIALLY_AUDITED => array( - 'name' => pht('Partially Audited'), - 'legacy' => self::PARTIALLY_AUDITED, - 'icon' => 'fa-check-circle-o', - 'color' => 'yellow', - 'closed' => false, - ), - self::MODERN_AUDITED => array( - 'name' => pht('Audited'), - 'legacy' => self::FULLY_AUDITED, - 'icon' => 'fa-check-circle', - 'color' => 'green', - 'closed' => true, - ), - self::MODERN_NEEDS_VERIFICATION => array( - 'name' => pht('Needs Verification'), - 'legacy' => self::NEEDS_VERIFICATION, - 'icon' => 'fa-refresh', - 'color' => 'indigo', - 'closed' => false, - ), - ); - } -} diff --git a/src/applications/audit/editor/PhabricatorAuditEditor.php b/src/applications/audit/editor/PhabricatorAuditEditor.php index 673abeb85f..cfa84b8bd7 100644 --- a/src/applications/audit/editor/PhabricatorAuditEditor.php +++ b/src/applications/audit/editor/PhabricatorAuditEditor.php @@ -206,12 +206,10 @@ final class PhabricatorAuditEditor $object->writeImportStatusFlag($import_status_flag); } - $partial_status = PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED; - // If the commit has changed state after this edit, add an informational // transaction about the state change. if ($old_status != $new_status) { - if ($new_status == $partial_status) { + if ($object->isAuditStatusPartiallyAudited()) { // This state isn't interesting enough to get a transaction. The // best way we could lead the user forward is something like "This // commit still requires additional audits." but that's redundant and diff --git a/src/applications/audit/query/PhabricatorCommitSearchEngine.php b/src/applications/audit/query/PhabricatorCommitSearchEngine.php index e1b294a9d6..ee5105f193 100644 --- a/src/applications/audit/query/PhabricatorCommitSearchEngine.php +++ b/src/applications/audit/query/PhabricatorCommitSearchEngine.php @@ -15,6 +15,7 @@ final class PhabricatorCommitSearchEngine return id(new DiffusionCommitQuery()) ->needAuditRequests(true) ->needCommitData(true) + ->needIdentities(true) ->needDrafts(true); } @@ -92,7 +93,9 @@ final class PhabricatorCommitSearchEngine ->setLabel(pht('Audit Status')) ->setKey('statuses') ->setAliases(array('status')) - ->setOptions(PhabricatorAuditCommitStatusConstants::getStatusNameMap()) + ->setOptions(DiffusionCommitAuditStatus::newOptions()) + ->setDeprecatedOptions( + DiffusionCommitAuditStatus::newDeprecatedOptions()) ->setDescription(pht('Find commits with given audit statuses.')), id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Repositories')) @@ -160,7 +163,7 @@ final class PhabricatorCommitSearchEngine case 'active': $bucket_key = DiffusionCommitRequiredActionResultBucket::BUCKETKEY; - $open = PhabricatorAuditCommitStatusConstants::getOpenStatusConstants(); + $open = DiffusionCommitAuditStatus::getOpenStatusConstants(); $query ->setParameter('responsiblePHIDs', array($viewer_phid)) diff --git a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php index 82303fff2b..6e40cdde98 100644 --- a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php +++ b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php @@ -490,8 +490,7 @@ final class PhabricatorAuthSessionEngine extends Phobject { PhabricatorAuthSession $session, $force = false) { - $until = $session->getHighSecurityUntil(); - if ($until > time() || $force) { + if ($session->isHighSecuritySession() || $force) { return new PhabricatorAuthHighSecurityToken(); } diff --git a/src/applications/auth/storage/PhabricatorAuthSession.php b/src/applications/auth/storage/PhabricatorAuthSession.php index b0b4996c93..cf707a053d 100644 --- a/src/applications/auth/storage/PhabricatorAuthSession.php +++ b/src/applications/auth/storage/PhabricatorAuthSession.php @@ -74,6 +74,22 @@ final class PhabricatorAuthSession extends PhabricatorAuthDAO } } + public function isHighSecuritySession() { + $until = $this->getHighSecurityUntil(); + + if (!$until) { + return false; + } + + $now = PhabricatorTime::getNow(); + if ($until < $now) { + return false; + } + + return true; + } + + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/conduit/data/ConduitConstantDescription.php b/src/applications/conduit/data/ConduitConstantDescription.php index fe2af2b716..c9b2d00f6b 100644 --- a/src/applications/conduit/data/ConduitConstantDescription.php +++ b/src/applications/conduit/data/ConduitConstantDescription.php @@ -4,6 +4,7 @@ final class ConduitConstantDescription extends Phobject { private $key; private $value; + private $isDeprecated; public function setKey($key) { $this->key = $key; @@ -23,4 +24,13 @@ final class ConduitConstantDescription extends Phobject { return $this->value; } + public function setIsDeprecated($is_deprecated) { + $this->isDeprecated = $is_deprecated; + return $this; + } + + public function getIsDeprecated() { + return $this->isDeprecated; + } + } diff --git a/src/applications/diffusion/DiffusionCommitAuditStatus.php b/src/applications/diffusion/DiffusionCommitAuditStatus.php new file mode 100644 index 0000000000..95d0e52213 --- /dev/null +++ b/src/applications/diffusion/DiffusionCommitAuditStatus.php @@ -0,0 +1,173 @@ + $spec) { + if (isset($spec['legacy'])) { + $modern[$spec['legacy']] = $key; + } + } + + foreach ($values as $key => $value) { + $values[$key] = idx($modern, $value, $value); + } + + return $values; + } + + public static function newForStatus($status) { + $result = new self(); + + $result->key = $status; + + $map = self::getMap(); + if (isset($map[$status])) { + $result->spec = $map[$status]; + } + + return $result; + } + + public function getKey() { + return $this->key; + } + + public function getIcon() { + return idx($this->spec, 'icon'); + } + + public function getColor() { + return idx($this->spec, 'color'); + } + + public function getAnsiColor() { + return idx($this->spec, 'color.ansi'); + } + + public function getName() { + return idx($this->spec, 'name', pht('Unknown ("%s")', $this->key)); + } + + public function isNoAudit() { + return ($this->key == self::NONE); + } + + public function isNeedsAudit() { + return ($this->key == self::NEEDS_AUDIT); + } + + public function isConcernRaised() { + return ($this->key == self::CONCERN_RAISED); + } + + public function isNeedsVerification() { + return ($this->key == self::NEEDS_VERIFICATION); + } + + public function isPartiallyAudited() { + return ($this->key == self::PARTIALLY_AUDITED); + } + + public function isAudited() { + return ($this->key == self::AUDITED); + } + + public function getIsClosed() { + return idx($this->spec, 'closed'); + } + + public static function getOpenStatusConstants() { + $constants = array(); + foreach (self::getMap() as $key => $map) { + if (!$map['closed']) { + $constants[] = $key; + } + } + return $constants; + } + + public static function newOptions() { + $map = self::getMap(); + return ipull($map, 'name'); + } + + public static function newDeprecatedOptions() { + $map = self::getMap(); + + $results = array(); + foreach ($map as $key => $spec) { + if (isset($spec['legacy'])) { + $results[$spec['legacy']] = $key; + } + } + + return $results; + } + + private static function getMap() { + return array( + self::NONE => array( + 'name' => pht('No Audits'), + 'legacy' => 0, + 'icon' => 'fa-check', + 'color' => 'bluegrey', + 'closed' => true, + 'color.ansi' => null, + ), + self::NEEDS_AUDIT => array( + 'name' => pht('Audit Required'), + 'legacy' => 1, + 'icon' => 'fa-exclamation-circle', + 'color' => 'orange', + 'closed' => false, + 'color.ansi' => 'magenta', + ), + self::CONCERN_RAISED => array( + 'name' => pht('Concern Raised'), + 'legacy' => 2, + 'icon' => 'fa-times-circle', + 'color' => 'red', + 'closed' => false, + 'color.ansi' => 'red', + ), + self::PARTIALLY_AUDITED => array( + 'name' => pht('Partially Audited'), + 'legacy' => 3, + 'icon' => 'fa-check-circle-o', + 'color' => 'yellow', + 'closed' => false, + 'color.ansi' => 'yellow', + ), + self::AUDITED => array( + 'name' => pht('Audited'), + 'legacy' => 4, + 'icon' => 'fa-check-circle', + 'color' => 'green', + 'closed' => true, + 'color.ansi' => 'green', + ), + self::NEEDS_VERIFICATION => array( + 'name' => pht('Needs Verification'), + 'legacy' => 5, + 'icon' => 'fa-refresh', + 'color' => 'indigo', + 'closed' => false, + 'color.ansi' => 'magenta', + ), + ); + } +} diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php index a6743b6557..283278fcf8 100644 --- a/src/applications/diffusion/controller/DiffusionCommitController.php +++ b/src/applications/diffusion/controller/DiffusionCommitController.php @@ -171,13 +171,12 @@ final class DiffusionCommitController extends DiffusionController { ->setHeaderIcon('fa-code-fork') ->addTag($commit_tag); - if ($commit->getAuditStatus()) { - $icon = PhabricatorAuditCommitStatusConstants::getStatusIcon( - $commit->getAuditStatus()); - $color = PhabricatorAuditCommitStatusConstants::getStatusColor( - $commit->getAuditStatus()); - $status = PhabricatorAuditCommitStatusConstants::getStatusName( - $commit->getAuditStatus()); + if (!$commit->isAuditStatusNoAudit()) { + $status = $commit->getAuditStatusObject(); + + $icon = $status->getIcon(); + $color = $status->getColor(); + $status = $status->getName(); $header->setStatus($icon, $color, $status); } diff --git a/src/applications/diffusion/doorkeeper/DiffusionDoorkeeperCommitFeedStoryPublisher.php b/src/applications/diffusion/doorkeeper/DiffusionDoorkeeperCommitFeedStoryPublisher.php index 088f4b752e..712b7ae150 100644 --- a/src/applications/diffusion/doorkeeper/DiffusionDoorkeeperCommitFeedStoryPublisher.php +++ b/src/applications/diffusion/doorkeeper/DiffusionDoorkeeperCommitFeedStoryPublisher.php @@ -31,8 +31,6 @@ final class DiffusionDoorkeeperCommitFeedStoryPublisher // After ApplicationTransactions, we could annotate feed stories more // explicitly. - $fully_audited = PhabricatorAuditCommitStatusConstants::FULLY_AUDITED; - $story = $this->getFeedStory(); $xaction = $story->getPrimaryTransaction(); switch ($xaction->getTransactionType()) { @@ -41,7 +39,7 @@ final class DiffusionDoorkeeperCommitFeedStoryPublisher case PhabricatorAuditActionConstants::CLOSE: return true; case PhabricatorAuditActionConstants::ACCEPT: - if ($object->getAuditStatus() == $fully_audited) { + if ($object->isAuditStatusAudited()) { return true; } break; @@ -165,14 +163,7 @@ final class DiffusionDoorkeeperCommitFeedStoryPublisher } public function isObjectClosed($object) { - switch ($object->getAuditStatus()) { - case PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT: - case PhabricatorAuditCommitStatusConstants::CONCERN_RAISED: - case PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED: - return false; - default: - return true; - } + return $object->getAuditStatusObject()->getIsClosed(); } public function getResponsibilityTitle($object) { diff --git a/src/applications/diffusion/engineextension/DiffusionHovercardEngineExtension.php b/src/applications/diffusion/engineextension/DiffusionHovercardEngineExtension.php index 0711028796..3ce95cb3c3 100644 --- a/src/applications/diffusion/engineextension/DiffusionHovercardEngineExtension.php +++ b/src/applications/diffusion/engineextension/DiffusionHovercardEngineExtension.php @@ -41,12 +41,12 @@ final class DiffusionHovercardEngineExtension $hovercard->addField(pht('Date'), phabricator_date($commit->getEpoch(), $viewer)); - if ($commit->getAuditStatus() != - PhabricatorAuditCommitStatusConstants::NONE) { + if (!$commit->isAuditStatusNoAudit()) { + $status = $commit->getAuditStatusObject(); - $hovercard->addField(pht('Audit Status'), - PhabricatorAuditCommitStatusConstants::getStatusName( - $commit->getAuditStatus())); + $hovercard->addField( + pht('Audit Status'), + $status->getName()); } } diff --git a/src/applications/diffusion/query/DiffusionCommitQuery.php b/src/applications/diffusion/query/DiffusionCommitQuery.php index 0ca9cd97d4..f2d0920b3a 100644 --- a/src/applications/diffusion/query/DiffusionCommitQuery.php +++ b/src/applications/diffusion/query/DiffusionCommitQuery.php @@ -714,10 +714,13 @@ final class DiffusionCommitQuery } if ($this->statuses !== null) { + $statuses = DiffusionCommitAuditStatus::newModernKeys( + $this->statuses); + $where[] = qsprintf( $conn, - 'commit.auditStatus IN (%Ld)', - $this->statuses); + 'commit.auditStatus IN (%Ls)', + $statuses); } if ($this->packagePHIDs !== null) { diff --git a/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php b/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php index 40bc63025d..25984c93e1 100644 --- a/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php +++ b/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php @@ -69,14 +69,12 @@ final class DiffusionCommitRequiredActionResultBucket $results = array(); $objects = $this->objects; - $status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED; - foreach ($objects as $key => $object) { if (empty($phids[$object->getAuthorPHID()])) { continue; } - if ($object->getAuditStatus() != $status_concern) { + if (!$object->isAuditStatusConcernRaised()) { continue; } @@ -91,7 +89,6 @@ final class DiffusionCommitRequiredActionResultBucket $results = array(); $objects = $this->objects; - $status_verify = PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION; $has_concern = array( PhabricatorAuditStatusConstants::CONCERNED, ); @@ -102,7 +99,7 @@ final class DiffusionCommitRequiredActionResultBucket continue; } - if ($object->getAuditStatus() != $status_verify) { + if (!$object->isAuditStatusNeedsVerification()) { continue; } @@ -147,10 +144,8 @@ final class DiffusionCommitRequiredActionResultBucket $results = array(); $objects = $this->objects; - $status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED; - foreach ($objects as $key => $object) { - if ($object->getAuditStatus() != $status_concern) { + if (!$object->isAuditStatusConcernRaised()) { continue; } @@ -169,15 +164,13 @@ final class DiffusionCommitRequiredActionResultBucket $results = array(); $objects = $this->objects; - $status_waiting = array( - PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT, - PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION, - PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED, - ); - $status_waiting = array_fuse($status_waiting); - foreach ($objects as $key => $object) { - if (empty($status_waiting[$object->getAuditStatus()])) { + $any_waiting = + $object->isAuditStatusNeedsAudit() || + $object->isAuditStatusNeedsVerification() || + $object->isAuditStatusPartiallyAudited(); + + if (!$any_waiting) { continue; } diff --git a/src/applications/diffusion/xaction/DiffusionCommitConcernTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitConcernTransaction.php index 30ec8d9f6d..1f6ae72621 100644 --- a/src/applications/diffusion/xaction/DiffusionCommitConcernTransaction.php +++ b/src/applications/diffusion/xaction/DiffusionCommitConcernTransaction.php @@ -33,8 +33,7 @@ final class DiffusionCommitConcernTransaction public function applyInternalEffects($object, $value) { // NOTE: We force the commit directly into "Concern Raised" so that we // override a possible "Needs Verification" state. - $object->setAuditStatus( - PhabricatorAuditCommitStatusConstants::CONCERN_RAISED); + $object->setAuditStatus(DiffusionCommitAuditStatus::CONCERN_RAISED); } public function applyExternalEffects($object, $value) { @@ -54,10 +53,8 @@ final class DiffusionCommitConcernTransaction // Even if you've already raised a concern, you can raise again as long // as the author requested you verify. - $state_verify = PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION; - if ($this->isViewerFullyRejected($object, $viewer)) { - if ($object->getAuditStatus() != $state_verify) { + if (!$object->isAuditStatusNeedsVerification()) { throw new Exception( pht( 'You can not raise a concern with this commit because you have '. diff --git a/src/applications/diffusion/xaction/DiffusionCommitStateTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitStateTransaction.php index a6288923f4..6fd2a6977d 100644 --- a/src/applications/diffusion/xaction/DiffusionCommitStateTransaction.php +++ b/src/applications/diffusion/xaction/DiffusionCommitStateTransaction.php @@ -11,29 +11,32 @@ final class DiffusionCommitStateTransaction throw new PhutilMethodNotImplementedException(); } - public function getIcon() { + private function getAuditStatusObject() { $new = $this->getNewValue(); - return PhabricatorAuditCommitStatusConstants::getStatusIcon($new); + return DiffusionCommitAuditStatus::newForStatus($new); + } + + public function getIcon() { + return $this->getAuditStatusObject()->getIcon(); } public function getColor() { - $new = $this->getNewValue(); - return PhabricatorAuditCommitStatusConstants::getStatusColor($new); + return $this->getAuditStatusObject()->getColor(); } public function getTitle() { - $new = $this->getNewValue(); + $status = $this->getAuditStatusObject(); - switch ($new) { - case PhabricatorAuditCommitStatusConstants::NONE: + switch ($status->getKey()) { + case DiffusionCommitAuditStatus::NONE: return pht('This commit no longer requires audit.'); - case PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT: + case DiffusionCommitAuditStatus::NEEDS_AUDIT: return pht('This commit now requires audit.'); - case PhabricatorAuditCommitStatusConstants::CONCERN_RAISED: + case DiffusionCommitAuditStatus::CONCERN_RAISED: return pht('This commit now has outstanding concerns.'); - case PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION: + case DiffusionCommitAuditStatus::NEEDS_VERIFICATION: return pht('This commit now requires verification by auditors.'); - case PhabricatorAuditCommitStatusConstants::FULLY_AUDITED: + case DiffusionCommitAuditStatus::AUDITED: return pht('All concerns with this commit have now been addressed.'); } @@ -41,26 +44,26 @@ final class DiffusionCommitStateTransaction } public function getTitleForFeed() { - $new = $this->getNewValue(); + $status = $this->getAuditStatusObject(); - switch ($new) { - case PhabricatorAuditCommitStatusConstants::NONE: + switch ($status->getKey()) { + case DiffusionCommitAuditStatus::NONE: return pht( '%s no longer requires audit.', $this->renderObject()); - case PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT: + case DiffusionCommitAuditStatus::NEEDS_AUDIT: return pht( '%s now requires audit.', $this->renderObject()); - case PhabricatorAuditCommitStatusConstants::CONCERN_RAISED: + case DiffusionCommitAuditStatus::CONCERN_RAISED: return pht( '%s now has outstanding concerns.', $this->renderObject()); - case PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION: + case DiffusionCommitAuditStatus::NEEDS_VERIFICATION: return pht( '%s now requires verification by auditors.', $this->renderObject()); - case PhabricatorAuditCommitStatusConstants::FULLY_AUDITED: + case DiffusionCommitAuditStatus::AUDITED: return pht( 'All concerns with %s have now been addressed.', $this->renderObject()); diff --git a/src/applications/diffusion/xaction/DiffusionCommitVerifyTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitVerifyTransaction.php index a44261f7a4..f1ec5834b1 100644 --- a/src/applications/diffusion/xaction/DiffusionCommitVerifyTransaction.php +++ b/src/applications/diffusion/xaction/DiffusionCommitVerifyTransaction.php @@ -36,8 +36,7 @@ final class DiffusionCommitVerifyTransaction } public function applyInternalEffects($object, $value) { - $object->setAuditStatus( - PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION); + $object->setAuditStatus(DiffusionCommitAuditStatus::NEEDS_VERIFICATION); } protected function validateAction($object, PhabricatorUser $viewer) { @@ -48,8 +47,7 @@ final class DiffusionCommitVerifyTransaction 'are not the author.')); } - $status = $object->getAuditStatus(); - if ($status != PhabricatorAuditCommitStatusConstants::CONCERN_RAISED) { + if (!$object->isAuditStatusConcernRaised()) { throw new Exception( pht( 'You can not request verification of this commit because no '. diff --git a/src/applications/drydock/application/PhabricatorDrydockApplication.php b/src/applications/drydock/application/PhabricatorDrydockApplication.php index fef2c843ff..79bc1cb0d7 100644 --- a/src/applications/drydock/application/PhabricatorDrydockApplication.php +++ b/src/applications/drydock/application/PhabricatorDrydockApplication.php @@ -93,6 +93,8 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication { '' => 'DrydockRepositoryOperationViewController', 'status/' => 'DrydockRepositoryOperationStatusController', 'dismiss/' => 'DrydockRepositoryOperationDismissController', + 'logs/(?:query/(?P[^/]+)/)?' => + 'DrydockLogListController', ), ), ), diff --git a/src/applications/drydock/controller/DrydockBlueprintViewController.php b/src/applications/drydock/controller/DrydockBlueprintViewController.php index d3f73bf78c..2c31e4ae8f 100644 --- a/src/applications/drydock/controller/DrydockBlueprintViewController.php +++ b/src/applications/drydock/controller/DrydockBlueprintViewController.php @@ -58,8 +58,11 @@ final class DrydockBlueprintViewController extends DrydockBlueprintController { $log_query = id(new DrydockLogQuery()) ->withBlueprintPHIDs(array($blueprint->getPHID())); + $log_table = $this->buildLogTable($log_query) + ->setHideBlueprints(true); + $logs = $this->buildLogBox( - $log_query, + $log_table, $this->getApplicationURI("blueprint/{$id}/logs/query/all/")); $view = id(new PHUITwoColumnView()) diff --git a/src/applications/drydock/controller/DrydockController.php b/src/applications/drydock/controller/DrydockController.php index 872a78f66f..f393f210d7 100644 --- a/src/applications/drydock/controller/DrydockController.php +++ b/src/applications/drydock/controller/DrydockController.php @@ -79,7 +79,7 @@ abstract class DrydockController extends PhabricatorController { ->addRawContent($table); } - protected function buildLogBox(DrydockLogQuery $query, $all_uri) { + protected function buildLogTable(DrydockLogQuery $query) { $viewer = $this->getViewer(); $logs = $query @@ -89,9 +89,12 @@ abstract class DrydockController extends PhabricatorController { $log_table = id(new DrydockLogListView()) ->setUser($viewer) - ->setLogs($logs) - ->render(); + ->setLogs($logs); + return $log_table; + } + + protected function buildLogBox(DrydockLogListView $log_table, $all_uri) { $log_header = id(new PHUIHeaderView()) ->setHeader(pht('Logs')) ->addActionLink( diff --git a/src/applications/drydock/controller/DrydockLeaseViewController.php b/src/applications/drydock/controller/DrydockLeaseViewController.php index 18e1d0088d..dfffcf8402 100644 --- a/src/applications/drydock/controller/DrydockLeaseViewController.php +++ b/src/applications/drydock/controller/DrydockLeaseViewController.php @@ -43,8 +43,11 @@ final class DrydockLeaseViewController extends DrydockLeaseController { $log_query = id(new DrydockLogQuery()) ->withLeasePHIDs(array($lease->getPHID())); + $log_table = $this->buildLogTable($log_query) + ->setHideLeases(true); + $logs = $this->buildLogBox( - $log_query, + $log_table, $this->getApplicationURI("lease/{$id}/logs/query/all/")); $crumbs = $this->buildApplicationCrumbs(); diff --git a/src/applications/drydock/controller/DrydockLogController.php b/src/applications/drydock/controller/DrydockLogController.php index 704cbb53b9..69e0c84804 100644 --- a/src/applications/drydock/controller/DrydockLogController.php +++ b/src/applications/drydock/controller/DrydockLogController.php @@ -6,6 +6,7 @@ abstract class DrydockLogController private $blueprint; private $resource; private $lease; + private $operation; public function setBlueprint(DrydockBlueprint $blueprint) { $this->blueprint = $blueprint; @@ -34,6 +35,15 @@ abstract class DrydockLogController return $this->lease; } + public function setOperation(DrydockRepositoryOperation $operation) { + $this->operation = $operation; + return $this; + } + + public function getOperation() { + return $this->operation; + } + public function buildSideNavView() { $nav = new AphrontSideNavFilterView(); $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); @@ -56,6 +66,11 @@ abstract class DrydockLogController $engine->setLease($lease); } + $operation = $this->getOperation(); + if ($operation) { + $engine->setOperation($operation); + } + $engine->addNavigationItems($nav->getMenu()); $nav->selectFilter(null); @@ -66,9 +81,12 @@ abstract class DrydockLogController protected function buildApplicationCrumbs() { $crumbs = parent::buildApplicationCrumbs(); + $viewer = $this->getViewer(); + $blueprint = $this->getBlueprint(); $resource = $this->getResource(); $lease = $this->getLease(); + $operation = $this->getOperation(); if ($blueprint) { $id = $blueprint->getID(); @@ -111,6 +129,20 @@ abstract class DrydockLogController $crumbs->addTextCrumb( pht('Logs'), $this->getApplicationURI("lease/{$id}/logs/")); + } else if ($operation) { + $id = $operation->getID(); + + $crumbs->addTextCrumb( + pht('Operations'), + $this->getApplicationURI('operation/')); + + $crumbs->addTextCrumb( + pht('Repository Operation %d', $id), + $this->getApplicationURI("operation/{$id}/")); + + $crumbs->addTextCrumb( + pht('Logs'), + $this->getApplicationURI("operation/{$id}/logs/")); } return $crumbs; diff --git a/src/applications/drydock/controller/DrydockLogListController.php b/src/applications/drydock/controller/DrydockLogListController.php index b5e4d4ff0c..26719db39e 100644 --- a/src/applications/drydock/controller/DrydockLogListController.php +++ b/src/applications/drydock/controller/DrydockLogListController.php @@ -46,6 +46,17 @@ final class DrydockLogListController extends DrydockLogController { $engine->setLease($lease); $this->setLease($lease); break; + case 'operation': + $operation = id(new DrydockRepositoryOperationQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->executeOne(); + if (!$operation) { + return new Aphront404Response(); + } + $engine->setOperation($operation); + $this->setOperation($operation); + break; default: return new Aphront404Response(); } diff --git a/src/applications/drydock/controller/DrydockRepositoryOperationViewController.php b/src/applications/drydock/controller/DrydockRepositoryOperationViewController.php index e7fbf07b35..e255c63ec3 100644 --- a/src/applications/drydock/controller/DrydockRepositoryOperationViewController.php +++ b/src/applications/drydock/controller/DrydockRepositoryOperationViewController.php @@ -47,13 +47,25 @@ final class DrydockRepositoryOperationViewController ->setUser($viewer) ->setOperation($operation); + $log_query = id(new DrydockLogQuery()) + ->withOperationPHIDs(array($operation->getPHID())); + + $log_table = $this->buildLogTable($log_query) + ->setHideOperations(true); + + $logs = $this->buildLogBox( + $log_table, + $this->getApplicationURI("operation/{$id}/logs/query/all/")); + $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setCurtain($curtain) ->addPropertySection(pht('Properties'), $properties) - ->setMainColumn(array( - $status_view, - )); + ->setMainColumn( + array( + $status_view, + $logs, + )); return $this->newPage() ->setTitle($title) diff --git a/src/applications/drydock/controller/DrydockResourceViewController.php b/src/applications/drydock/controller/DrydockResourceViewController.php index 8718caa9e2..ec0dce53e1 100644 --- a/src/applications/drydock/controller/DrydockResourceViewController.php +++ b/src/applications/drydock/controller/DrydockResourceViewController.php @@ -48,8 +48,11 @@ final class DrydockResourceViewController extends DrydockResourceController { $log_query = id(new DrydockLogQuery()) ->withResourcePHIDs(array($resource->getPHID())); - $log_box = $this->buildLogBox( - $log_query, + $log_table = $this->buildLogTable($log_query) + ->setHideResources(true); + + $logs = $this->buildLogBox( + $log_table, $this->getApplicationURI("resource/{$id}/logs/query/all/")); $crumbs = $this->buildApplicationCrumbs(); @@ -86,11 +89,12 @@ final class DrydockResourceViewController extends DrydockResourceController { $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setCurtain($curtain) - ->setMainColumn(array( - $object_box, - $lease_box, - $log_box, - )); + ->setMainColumn( + array( + $object_box, + $lease_box, + $logs, + )); return $this->newPage() ->setTitle($title) diff --git a/src/applications/drydock/logtype/DrydockOperationWorkLogType.php b/src/applications/drydock/logtype/DrydockOperationWorkLogType.php new file mode 100644 index 0000000000..deff90c16c --- /dev/null +++ b/src/applications/drydock/logtype/DrydockOperationWorkLogType.php @@ -0,0 +1,19 @@ +getID(); - $handle->setName(pht('Drydock Repository Operation %d', $id)); + $handle->setName(pht('Repository Operation %d', $id)); $handle->setURI("/drydock/operation/{$id}/"); } } diff --git a/src/applications/drydock/query/DrydockLogQuery.php b/src/applications/drydock/query/DrydockLogQuery.php index 00980edb4d..b73ad371ae 100644 --- a/src/applications/drydock/query/DrydockLogQuery.php +++ b/src/applications/drydock/query/DrydockLogQuery.php @@ -5,6 +5,7 @@ final class DrydockLogQuery extends DrydockQuery { private $blueprintPHIDs; private $resourcePHIDs; private $leasePHIDs; + private $operationPHIDs; public function withBlueprintPHIDs(array $phids) { $this->blueprintPHIDs = $phids; @@ -21,6 +22,11 @@ final class DrydockLogQuery extends DrydockQuery { return $this; } + public function withOperationPHIDs(array $phids) { + $this->operationPHIDs = $phids; + return $this; + } + public function newResultObject() { return new DrydockLog(); } @@ -93,6 +99,27 @@ final class DrydockLogQuery extends DrydockQuery { $log->attachLease($lease); } + $operation_phids = array_filter(mpull($logs, 'getOperationPHID')); + if ($operation_phids) { + $operations = id(new DrydockRepositoryOperationQuery()) + ->setParentQuery($this) + ->setViewer($this->getViewer()) + ->withPHIDs($operation_phids) + ->execute(); + $operations = mpull($operations, null, 'getPHID'); + } else { + $operations = array(); + } + + foreach ($logs as $key => $log) { + $operation = null; + $operation_phid = $log->getOperationPHID(); + if ($operation_phid) { + $operation = idx($operations, $operation_phid); + } + $log->attachOperation($operation); + } + return $logs; } @@ -120,6 +147,13 @@ final class DrydockLogQuery extends DrydockQuery { $this->leasePHIDs); } + if ($this->operationPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'operationPHID IN (%Ls)', + $this->operationPHIDs); + } + return $where; } diff --git a/src/applications/drydock/query/DrydockLogSearchEngine.php b/src/applications/drydock/query/DrydockLogSearchEngine.php index 43b1511c01..3602714a60 100644 --- a/src/applications/drydock/query/DrydockLogSearchEngine.php +++ b/src/applications/drydock/query/DrydockLogSearchEngine.php @@ -5,6 +5,7 @@ final class DrydockLogSearchEngine extends PhabricatorApplicationSearchEngine { private $blueprint; private $resource; private $lease; + private $operation; public function setBlueprint(DrydockBlueprint $blueprint) { $this->blueprint = $blueprint; @@ -33,6 +34,15 @@ final class DrydockLogSearchEngine extends PhabricatorApplicationSearchEngine { return $this->lease; } + public function setOperation(DrydockRepositoryOperation $operation) { + $this->operation = $operation; + return $this; + } + + public function getOperation() { + return $this->operation; + } + public function canUseInPanelContext() { // Prevent use on Dashboard panels since all log queries currently need a // parent object and these don't seem particularly useful in any case. @@ -65,6 +75,11 @@ final class DrydockLogSearchEngine extends PhabricatorApplicationSearchEngine { $query->withLeasePHIDs(array($lease->getPHID())); } + $operation = $this->getOperation(); + if ($operation) { + $query->withOperationPHIDs(array($operation->getPHID())); + } + return $query; } @@ -97,9 +112,15 @@ final class DrydockLogSearchEngine extends PhabricatorApplicationSearchEngine { return "/drydock/lease/{$id}/logs/{$path}"; } + $operation = $this->getOperation(); + if ($operation) { + $id = $operation->getID(); + return "/drydock/operation/{$id}/logs/{$path}"; + } + throw new Exception( pht( - 'Search engine has no blueprint, resource, or lease.')); + 'Search engine has no blueprint, resource, lease, or operation.')); } protected function getBuiltinQueryNames() { diff --git a/src/applications/drydock/storage/DrydockLog.php b/src/applications/drydock/storage/DrydockLog.php index 5d75d82c65..81879944a0 100644 --- a/src/applications/drydock/storage/DrydockLog.php +++ b/src/applications/drydock/storage/DrydockLog.php @@ -6,6 +6,7 @@ final class DrydockLog extends DrydockDAO protected $blueprintPHID; protected $resourcePHID; protected $leasePHID; + protected $operationPHID; protected $epoch; protected $type; protected $data = array(); @@ -13,6 +14,7 @@ final class DrydockLog extends DrydockDAO private $blueprint = self::ATTACHABLE; private $resource = self::ATTACHABLE; private $lease = self::ATTACHABLE; + private $operation = self::ATTACHABLE; protected function getConfiguration() { return array( @@ -24,6 +26,7 @@ final class DrydockLog extends DrydockDAO 'blueprintPHID' => 'phid?', 'resourcePHID' => 'phid?', 'leasePHID' => 'phid?', + 'operationPHID' => 'phid?', 'type' => 'text64', ), self::CONFIG_KEY_SCHEMA => array( @@ -36,6 +39,9 @@ final class DrydockLog extends DrydockDAO 'key_lease' => array( 'columns' => array('leasePHID', 'type'), ), + 'key_operation' => array( + 'columns' => array('operationPHID', 'type'), + ), 'epoch' => array( 'columns' => array('epoch'), ), @@ -70,6 +76,16 @@ final class DrydockLog extends DrydockDAO return $this->assertAttached($this->lease); } + public function attachOperation( + DrydockRepositoryOperation $operation = null) { + $this->operation = $operation; + return $this; + } + + public function getOperation() { + return $this->assertAttached($this->operation); + } + public function isComplete() { if ($this->getBlueprintPHID() && !$this->getBlueprint()) { return false; @@ -83,6 +99,10 @@ final class DrydockLog extends DrydockDAO return false; } + if ($this->getOperationPHID() && !$this->getOperation()) { + return false; + } + return true; } @@ -108,8 +128,8 @@ final class DrydockLog extends DrydockDAO public function describeAutomaticCapability($capability) { return pht( - 'To view log details, you must be able to view the associated '. - 'blueprint, resource and lease.'); + 'To view log details, you must be able to view all associated '. + 'blueprints, resources, leases, and repository operations.'); } } diff --git a/src/applications/drydock/storage/DrydockRepositoryOperation.php b/src/applications/drydock/storage/DrydockRepositoryOperation.php index 809a10a116..195c5de8ec 100644 --- a/src/applications/drydock/storage/DrydockRepositoryOperation.php +++ b/src/applications/drydock/storage/DrydockRepositoryOperation.php @@ -25,6 +25,7 @@ final class DrydockRepositoryOperation extends DrydockDAO private $repository = self::ATTACHABLE; private $object = self::ATTACHABLE; private $implementation = self::ATTACHABLE; + private $workingCopyLease = self::ATTACHABLE; public static function initializeNewOperation( DrydockRepositoryOperationType $op) { @@ -90,6 +91,19 @@ final class DrydockRepositoryOperation extends DrydockDAO return $this->implementation; } + public function getWorkingCopyLease() { + return $this->assertAttached($this->workingCopyLease); + } + + public function attachWorkingCopyLease(DrydockLease $lease) { + $this->workingCopyLease = $lease; + return $this; + } + + public function hasWorkingCopyLease() { + return ($this->workingCopyLease !== self::ATTACHABLE); + } + public function getProperty($key, $default = null) { return idx($this->properties, $key, $default); } @@ -189,6 +203,37 @@ final class DrydockRepositoryOperation extends DrydockDAO return $this->getProperty('exec.workingcopy.error'); } + public function logText($text) { + return $this->logEvent( + DrydockTextLogType::LOGCONST, + array( + 'text' => $text, + )); + } + + public function logEvent($type, array $data = array()) { + $log = id(new DrydockLog()) + ->setEpoch(PhabricatorTime::getNow()) + ->setType($type) + ->setData($data); + + $log->setOperationPHID($this->getPHID()); + + if ($this->hasWorkingCopyLease()) { + $lease = $this->getWorkingCopyLease(); + $log->setLeasePHID($lease->getPHID()); + + $resource_phid = $lease->getResourcePHID(); + if ($resource_phid) { + $resource = $lease->getResource(); + + $log->setResourcePHID($resource->getPHID()); + $log->setBlueprintPHID($resource->getBlueprintPHID()); + } + } + + return $log->save(); + } /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/drydock/view/DrydockLogListView.php b/src/applications/drydock/view/DrydockLogListView.php index 61fde7cee7..32f5d0d7ed 100644 --- a/src/applications/drydock/view/DrydockLogListView.php +++ b/src/applications/drydock/view/DrydockLogListView.php @@ -3,6 +3,46 @@ final class DrydockLogListView extends AphrontView { private $logs; + private $hideBlueprints; + private $hideResources; + private $hideLeases; + private $hideOperations; + + public function setHideBlueprints($hide_blueprints) { + $this->hideBlueprints = $hide_blueprints; + return $this; + } + + public function getHideBlueprints() { + return $this->hideBlueprints; + } + + public function setHideResources($hide_resources) { + $this->hideResources = $hide_resources; + return $this; + } + + public function getHideResources() { + return $this->hideResources; + } + + public function setHideLeases($hide_leases) { + $this->hideLeases = $hide_leases; + return $this; + } + + public function getHideLeases() { + return $this->hideLeases; + } + + public function setHideOperations($hide_operations) { + $this->hideOperations = $hide_operations; + return $this; + } + + public function getHideOperations() { + return $this->hideOperations; + } public function setLogs(array $logs) { assert_instances_of($logs, 'DrydockLog'); @@ -41,6 +81,13 @@ final class DrydockLogListView extends AphrontView { $lease = null; } + $operation_phid = $log->getOperationPHID(); + if ($operation_phid) { + $operation = $viewer->renderHandle($operation_phid); + } else { + $operation = null; + } + if ($log->isComplete()) { $type_key = $log->getType(); if (isset($types[$type_key])) { @@ -72,6 +119,7 @@ final class DrydockLogListView extends AphrontView { $blueprint, $resource, $lease, + $operation, id(new PHUIIconView())->setIcon($icon), $type, $data, @@ -86,16 +134,25 @@ final class DrydockLogListView extends AphrontView { pht('Blueprint'), pht('Resource'), pht('Lease'), + pht('Operation'), null, pht('Type'), pht('Data'), pht('Date'), )) + ->setColumnVisibility( + array( + !$this->getHideBlueprints(), + !$this->getHideResources(), + !$this->getHideLeases(), + !$this->getHideOperations(), + )) ->setColumnClasses( array( '', '', '', + '', 'icon', '', 'wide', diff --git a/src/applications/drydock/worker/DrydockRepositoryOperationUpdateWorker.php b/src/applications/drydock/worker/DrydockRepositoryOperationUpdateWorker.php index 2ee9324fb6..c6e6927f22 100644 --- a/src/applications/drydock/worker/DrydockRepositoryOperationUpdateWorker.php +++ b/src/applications/drydock/worker/DrydockRepositoryOperationUpdateWorker.php @@ -59,6 +59,10 @@ final class DrydockRepositoryOperationUpdateWorker // No matter what happens here, destroy the lease away once we're done. $lease->setReleaseOnDestruction(true); + $operation->attachWorkingCopyLease($lease); + + $operation->logEvent(DrydockOperationWorkLogType::LOGCONST); + $operation->applyOperation($interface); } catch (PhabricatorWorkerYieldException $ex) { diff --git a/src/applications/owners/controller/PhabricatorOwnersDetailController.php b/src/applications/owners/controller/PhabricatorOwnersDetailController.php index 1caf12a82d..f71009cf19 100644 --- a/src/applications/owners/controller/PhabricatorOwnersDetailController.php +++ b/src/applications/owners/controller/PhabricatorOwnersDetailController.php @@ -71,7 +71,7 @@ final class PhabricatorOwnersDetailController 'package' => $package->getPHID(), )); - $status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED; + $status_concern = DiffusionCommitAuditStatus::CONCERN_RAISED; $attention_commits = id(new DiffusionCommitQuery()) ->setViewer($request->getUser()) diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index 03da9dee04..cd85800ab1 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -306,6 +306,14 @@ final class PhabricatorUser return ($this->session !== self::ATTACHABLE); } + public function hasHighSecuritySession() { + if (!$this->hasSession()) { + return false; + } + + return $this->getSession()->isHighSecuritySession(); + } + private function generateConduitCertificate() { return Filesystem::readRandomCharacters(255); } diff --git a/src/applications/phriction/application/PhabricatorPhrictionApplication.php b/src/applications/phriction/application/PhabricatorPhrictionApplication.php index d44845c4b9..02f285aef3 100644 --- a/src/applications/phriction/application/PhabricatorPhrictionApplication.php +++ b/src/applications/phriction/application/PhabricatorPhrictionApplication.php @@ -63,6 +63,9 @@ final class PhabricatorPhrictionApplication extends PhabricatorApplication { 'preview/(?P.*/)' => 'PhrictionMarkupPreviewController', 'diff/(?P[1-9]\d*)/' => 'PhrictionDiffController', + + $this->getEditRoutePattern('document/edit/') + => 'PhrictionEditEngineController', ), ); } diff --git a/src/applications/phriction/controller/PhrictionDiffController.php b/src/applications/phriction/controller/PhrictionDiffController.php index c81a90a3df..537f743eba 100644 --- a/src/applications/phriction/controller/PhrictionDiffController.php +++ b/src/applications/phriction/controller/PhrictionDiffController.php @@ -95,7 +95,7 @@ final class PhrictionDiffController extends PhrictionController { $navigation_table = null; if ($l + 1 == $r) { $nav_l = ($l > 1); - $nav_r = ($r != $current->getVersion()); + $nav_r = ($r != $document->getMaxVersion()); $uri = $request->getRequestURI(); @@ -191,30 +191,28 @@ final class PhrictionDiffController extends PhrictionController { PhrictionChangeType::CHANGE_MOVE_AWAY => true, // Plain silly PhrictionChangeType::CHANGE_STUB => true, // Utterly silly ); + if (isset($hidden_statuses[$content->getChangeType()])) { // Don't show an edit/revert button for changes which deleted, moved or // stubbed the content since it's silly. return null; } - if ($content->getID() == $current->getID()) { - return phutil_tag( - 'a', - array( - 'href' => '/phriction/edit/'.$document_id.'/', - 'class' => 'button button-grey', - ), - pht('Edit Current Version')); + if ($version == $current->getVersion()) { + $label = pht('Edit Current Version %s...', new PhutilNumber($version)); + } else if ($version < $current->getVersion()) { + $label = pht('Edit Older Version %s...', new PhutilNumber($version)); + } else { + $label = pht('Edit Draft Version %s...', new PhutilNumber($version)); } - return phutil_tag( 'a', array( 'href' => '/phriction/edit/'.$document_id.'/?revert='.$version, 'class' => 'button button-grey', ), - pht('Revert to Version %s...', $version)); + $label); } private function renderComparisonTable(array $content) { diff --git a/src/applications/phriction/controller/PhrictionDocumentController.php b/src/applications/phriction/controller/PhrictionDocumentController.php index f2cf974504..77f6a3529c 100644 --- a/src/applications/phriction/controller/PhrictionDocumentController.php +++ b/src/applications/phriction/controller/PhrictionDocumentController.php @@ -186,6 +186,35 @@ final class PhrictionDocumentController ); } else { $content = $document->getContent(); + + if ($content->getVersion() < $document->getMaxVersion()) { + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $document, + PhabricatorPolicyCapability::CAN_EDIT); + if ($can_edit) { + $document_uri = new PhutilURI($document->getURI()); + $draft_uri = $document_uri->alter('v', $document->getMaxVersion()); + + $draft_link = phutil_tag( + 'a', + array( + 'href' => $draft_uri, + ), + pht('View Draft Version')); + + $draft_link = phutil_tag('strong', array(), $draft_link); + + $version_note = id(new PHUIInfoView()) + ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) + ->appendChild( + array( + pht('This document has unpublished draft changes.'), + ' ', + $draft_link, + )); + } + } } $page_title = $content->getTitle(); @@ -348,6 +377,17 @@ final class PhrictionDocumentController $page_content->setCurtain($curtain); } + $timeline = $this->buildTransactionTimeline( + $document, + new PhrictionTransactionQuery()); + + $edit_engine = id(new PhrictionDocumentEditEngine()) + ->setViewer($viewer) + ->setTargetObject($document); + + $comment_view = $edit_engine + ->buildEditEngineCommentView($document); + return $this->newPage() ->setTitle($page_title) ->setCrumbs($crumbs) @@ -356,7 +396,16 @@ final class PhrictionDocumentController array( $page_content, $prop_list, - $children, + phutil_tag( + 'div', + array( + 'class' => 'phui-document-view-pro-box', + ), + array( + $children, + $timeline, + $comment_view, + )), )); } @@ -427,22 +476,31 @@ final class PhrictionDocumentController if ($is_draft) { $publish_name = pht('Publish Draft'); } else { - $publish_name = pht('Publish Revert'); + $publish_name = pht('Publish Older Version'); + } + + // If you're looking at the current version; and it's an unpublished + // draft; and you can publish it, add a UI hint that this might be an + // interesting action to take. + $hint_publish = false; + if ($is_draft) { + if ($can_publish) { + if ($document->getMaxVersion() == $content->getVersion()) { + $hint_publish = true; + } + } } $publish_uri = "/phriction/publish/{$id}/{$content_id}/"; - if (PhabricatorEnv::getEnvConfig('phabricator.show-prototypes')) { - $publish_name = pht('Publish (Prototype!)'); - - $curtain->addAction( - id(new PhabricatorActionView()) - ->setName($publish_name) - ->setIcon('fa-upload') - ->setDisabled(!$can_publish) - ->setWorkflow(true) - ->setHref($publish_uri)); - } + $curtain->addAction( + id(new PhabricatorActionView()) + ->setName($publish_name) + ->setIcon('fa-upload') + ->setSelected($hint_publish) + ->setDisabled(!$can_publish) + ->setWorkflow(true) + ->setHref($publish_uri)); if ($document->getStatus() == PhrictionDocumentStatus::STATUS_EXISTS) { $curtain->addAction( @@ -600,7 +658,7 @@ final class PhrictionDocumentController ), $list))); - return phutil_tag_div('phui-document-view-pro-box', $box); + return $box; } private function renderChildDocumentLink(array $info) { diff --git a/src/applications/phriction/controller/PhrictionEditController.php b/src/applications/phriction/controller/PhrictionEditController.php index 2c47187007..9f59b63121 100644 --- a/src/applications/phriction/controller/PhrictionEditController.php +++ b/src/applications/phriction/controller/PhrictionEditController.php @@ -72,43 +72,6 @@ final class PhrictionEditController } } - if ($request->getBool('nodraft')) { - $draft = null; - $draft_key = null; - } else { - if ($document->getPHID()) { - $draft_key = $document->getPHID().':'.$content->getVersion(); - } else { - $draft_key = 'phriction:'.$content->getSlug(); - } - $draft = id(new PhabricatorDraft())->loadOneWhere( - 'authorPHID = %s AND draftKey = %s', - $viewer->getPHID(), - $draft_key); - } - - if ($draft && - strlen($draft->getDraft()) && - ($draft->getDraft() != $content->getContent())) { - $content_text = $draft->getDraft(); - - $discard = phutil_tag( - 'a', - array( - 'href' => $request->getRequestURI()->alter('nodraft', true), - ), - pht('discard this draft')); - - $draft_note = new PHUIInfoView(); - $draft_note->setSeverity(PHUIInfoView::SEVERITY_NOTICE); - $draft_note->setTitle(pht('Recovered Draft')); - $draft_note->appendChild( - pht('Showing a saved draft of your edits, you can %s.', $discard)); - } else { - $content_text = $content->getContent(); - $draft_note = null; - } - require_celerity_resource('phriction-document-css'); $e_title = true; @@ -131,7 +94,15 @@ final class PhrictionEditController $v_space = $document->getSpacePHID(); + $content_text = $content->getContent(); + $is_draft_mode = ($document->getContent()->getVersion() != $max_version); + if ($request->isFormPost()) { + if ($is_new) { + $save_as_draft = false; + } else { + $save_as_draft = ($is_draft_mode || $request->getExists('draft')); + } $title = $request->getStr('title'); $content_text = $request->getStr('content'); @@ -143,13 +114,19 @@ final class PhrictionEditController $v_projects = $request->getArr('projects'); $v_space = $request->getStr('spacePHID'); + if ($save_as_draft) { + $edit_type = PhrictionDocumentDraftTransaction::TRANSACTIONTYPE; + } else { + $edit_type = PhrictionDocumentContentTransaction::TRANSACTIONTYPE; + } + $xactions = array(); + $xactions[] = id(new PhrictionTransaction()) ->setTransactionType(PhrictionDocumentTitleTransaction::TRANSACTIONTYPE) ->setNewValue($title); $xactions[] = id(new PhrictionTransaction()) - ->setTransactionType( - PhrictionDocumentContentTransaction::TRANSACTIONTYPE) + ->setTransactionType($edit_type) ->setNewValue($content_text); $xactions[] = id(new PhrictionTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) @@ -181,11 +158,15 @@ final class PhrictionEditController try { $editor->applyTransactions($document, $xactions); - if ($draft) { - $draft->delete(); + $uri = PhrictionDocument::getSlugURI($document->getSlug()); + $uri = new PhutilURI($uri); + + // If the user clicked "Save as Draft", take them to the draft, not + // to the current published page. + if ($save_as_draft) { + $uri = $uri->alter('v', $document->getMaxVersion()); } - $uri = PhrictionDocument::getSlugURI($document->getSlug()); return id(new AphrontRedirectResponse())->setURI($uri); } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; @@ -215,7 +196,7 @@ final class PhrictionEditController if ($overwrite) { $submit_button = pht('Overwrite Changes'); } else { - $submit_button = pht('Save Changes'); + $submit_button = pht('Save and Publish'); } } else { $submit_button = pht('Create Document'); @@ -235,11 +216,9 @@ final class PhrictionEditController $view_capability = PhabricatorPolicyCapability::CAN_VIEW; $edit_capability = PhabricatorPolicyCapability::CAN_EDIT; - $form = id(new AphrontFormView()) ->setUser($viewer) ->addHiddenInput('slug', $document->getSlug()) - ->addHiddenInput('nodraft', $request->getBool('nodraft')) ->addHiddenInput('contentVersion', $max_version) ->addHiddenInput('overwrite', $overwrite) ->appendChild( @@ -293,11 +272,31 @@ final class PhrictionEditController ->setLabel(pht('Edit Notes')) ->setValue($notes) ->setError(null) - ->setName('description')) - ->appendChild( + ->setName('description')); + + if ($is_draft_mode) { + $form->appendControl( id(new AphrontFormSubmitControl()) ->addCancelButton($cancel_uri) - ->setValue($submit_button)); + ->setValue(pht('Save Draft'))); + } else { + $submit = id(new AphrontFormSubmitControl()); + + if (!$is_new) { + $draft_button = id(new PHUIButtonView()) + ->setTag('input') + ->setName('draft') + ->setText(pht('Save as Draft')) + ->setColor(PHUIButtonView::GREEN); + $submit->addButton($draft_button); + } + + $submit + ->addCancelButton($cancel_uri) + ->setValue($submit_button); + + $form->appendControl($submit); + } $form_box = id(new PHUIObjectBoxView()) ->setHeaderText($page_title) @@ -325,7 +324,6 @@ final class PhrictionEditController $view = id(new PHUITwoColumnView()) ->setFooter( array( - $draft_note, $form_box, $preview, )); diff --git a/src/applications/phriction/controller/PhrictionEditEngineController.php b/src/applications/phriction/controller/PhrictionEditEngineController.php new file mode 100644 index 0000000000..f2b7722eab --- /dev/null +++ b/src/applications/phriction/controller/PhrictionEditEngineController.php @@ -0,0 +1,14 @@ +setController($this) + ->buildResponse(); + } + +} diff --git a/src/applications/phriction/controller/PhrictionPublishController.php b/src/applications/phriction/controller/PhrictionPublishController.php index 43c90cb1fa..e24b5f9be0 100644 --- a/src/applications/phriction/controller/PhrictionPublishController.php +++ b/src/applications/phriction/controller/PhrictionPublishController.php @@ -63,7 +63,7 @@ final class PhrictionPublishController } if ($content->getVersion() < $document->getContent()->getVersion()) { - $title = pht('Revert Document?'); + $title = pht('Publish Older Version?'); $body = pht( 'Revert the published version of this document to an older '. 'version?'); diff --git a/src/applications/phriction/editor/PhrictionDocumentEditEngine.php b/src/applications/phriction/editor/PhrictionDocumentEditEngine.php new file mode 100644 index 0000000000..22f56f2f57 --- /dev/null +++ b/src/applications/phriction/editor/PhrictionDocumentEditEngine.php @@ -0,0 +1,85 @@ +getViewer(); + return PhrictionDocument::initializeNewDocument( + $viewer, + '/'); + } + + protected function newObjectQuery() { + return id(new PhrictionDocumentQuery()) + ->needContent(true); + } + + protected function getObjectCreateTitleText($object) { + return pht('Create Document'); + } + + protected function getObjectCreateButtonText($object) { + return pht('Create Document'); + } + + protected function getObjectEditTitleText($object) { + return pht('Edit Document: %s', $object->getContent()->getTitle()); + } + + protected function getObjectEditShortText($object) { + return pht('Edit Document'); + } + + protected function getObjectCreateShortText() { + return pht('Create Document'); + } + + protected function getObjectName() { + return pht('Document'); + } + + protected function getEditorURI() { + return '/phriction/document/edit/'; + } + + protected function getObjectCreateCancelURI($object) { + return '/phriction/document/'; + } + + protected function getObjectViewURI($object) { + return $object->getURI(); + } + + protected function getCreateNewObjectPolicy() { + // NOTE: For now, this engine is only to support commenting. + return PhabricatorPolicies::POLICY_NOONE; + } + + protected function buildCustomEditFields($object) { + return array(); + } + +} diff --git a/src/applications/phriction/editor/PhrictionTransactionEditor.php b/src/applications/phriction/editor/PhrictionTransactionEditor.php index 3c045e16ba..6d72fc5b8e 100644 --- a/src/applications/phriction/editor/PhrictionTransactionEditor.php +++ b/src/applications/phriction/editor/PhrictionTransactionEditor.php @@ -74,6 +74,21 @@ final class PhrictionTransactionEditor return $this; } + public function setShouldPublishContent( + PhrictionDocument $object, + $publish) { + + if ($publish) { + $content_phid = $this->getNewContent()->getPHID(); + } else { + $content_phid = $this->getOldContent()->getPHID(); + } + + $object->setContentPHID($content_phid); + + return $this; + } + public function getEditorApplicationClass() { return 'PhabricatorPhrictionApplication'; } diff --git a/src/applications/phriction/xaction/PhrictionDocumentContentTransaction.php b/src/applications/phriction/xaction/PhrictionDocumentContentTransaction.php index 759121dd65..f5f1305279 100644 --- a/src/applications/phriction/xaction/PhrictionDocumentContentTransaction.php +++ b/src/applications/phriction/xaction/PhrictionDocumentContentTransaction.php @@ -1,87 +1,25 @@ getEditor()->getIsNewObject()) { - return null; - } - return $object->getContent()->getContent(); - } - - public function generateNewValue($object, $value) { - return $value; - } - public function applyInternalEffects($object, $value) { + parent::applyInternalEffects($object, $value); + $object->setStatus(PhrictionDocumentStatus::STATUS_EXISTS); - $content = $this->getNewDocumentContent($object); - $content->setContent($value); - } - - public function shouldHide() { - if ($this->getOldValue() === null) { - return true; - } else { - return false; - } - } - - public function getActionStrength() { - return 1.3; - } - - public function getActionName() { - return pht('Edited'); - } - - public function getTitle() { - return pht( - '%s edited the content of this document.', - $this->renderAuthor()); - } - - public function getTitleForFeed() { - return pht( - '%s edited the content of %s.', - $this->renderAuthor(), - $this->renderObject()); - } - - public function hasChangeDetailView() { - return true; - } - - public function getMailDiffSectionHeader() { - return pht('CHANGES TO DOCUMENT CONTENT'); - } - - public function newChangeDetailView() { - $viewer = $this->getViewer(); - - return id(new PhabricatorApplicationTransactionTextDiffDetailView()) - ->setViewer($viewer) - ->setOldText($this->getOldValue()) - ->setNewText($this->getNewValue()); - } - - public function newRemarkupChanges() { - $changes = array(); - - $changes[] = $this->newRemarkupChange() - ->setOldValue($this->getOldValue()) - ->setNewValue($this->getNewValue()); - - return $changes; + $this->getEditor()->setShouldPublishContent($object, true); } public function validateTransactions($object, array $xactions) { $errors = array(); + // NOTE: This is slightly different from the draft validation. Here, + // we're validating that: you can't edit away a document; and you can't + // create an empty document. + $content = $object->getContent()->getContent(); if ($this->isEmptyTextTransaction($content, $xactions)) { $errors[] = $this->newRequiredError( diff --git a/src/applications/phriction/xaction/PhrictionDocumentDraftTransaction.php b/src/applications/phriction/xaction/PhrictionDocumentDraftTransaction.php new file mode 100644 index 0000000000..3fc9d63ca8 --- /dev/null +++ b/src/applications/phriction/xaction/PhrictionDocumentDraftTransaction.php @@ -0,0 +1,39 @@ +getEditor()->setShouldPublishContent($object, false); + } + + public function shouldHideForFeed() { + return true; + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + // NOTE: We're only validating that you can't edit a document down to + // nothing in a draft transaction. Alone, this doesn't prevent you from + // creating a document with no content. The content transaction has + // validation for that. + + if (!$xactions) { + return $errors; + } + + $content = $object->getContent()->getContent(); + if ($this->isEmptyTextTransaction($content, $xactions)) { + $errors[] = $this->newRequiredError( + pht('Documents must have content.')); + } + + return $errors; + } + +} diff --git a/src/applications/phriction/xaction/PhrictionDocumentEditTransaction.php b/src/applications/phriction/xaction/PhrictionDocumentEditTransaction.php new file mode 100644 index 0000000000..3130658311 --- /dev/null +++ b/src/applications/phriction/xaction/PhrictionDocumentEditTransaction.php @@ -0,0 +1,82 @@ +getEditor()->getIsNewObject()) { + return null; + } + + // NOTE: We want to get the newest version of the content here, regardless + // of whether it's published or not. + + $actor = $this->getActor(); + + return id(new PhrictionContentQuery()) + ->setViewer($actor) + ->withDocumentPHIDs(array($object->getPHID())) + ->setOrder('newest') + ->setLimit(1) + ->executeOne() + ->getContent(); + } + + public function generateNewValue($object, $value) { + return $value; + } + + public function applyInternalEffects($object, $value) { + $content = $this->getNewDocumentContent($object); + $content->setContent($value); + } + + public function getActionStrength() { + return 1.3; + } + + public function getActionName() { + return pht('Edited'); + } + + public function getTitle() { + return pht( + '%s edited the content of this document.', + $this->renderAuthor()); + } + + public function getTitleForFeed() { + return pht( + '%s edited the content of %s.', + $this->renderAuthor(), + $this->renderObject()); + } + + public function getMailDiffSectionHeader() { + return pht('CHANGES TO DOCUMENT CONTENT'); + } + + public function hasChangeDetailView() { + return true; + } + + public function newChangeDetailView() { + $viewer = $this->getViewer(); + + return id(new PhabricatorApplicationTransactionTextDiffDetailView()) + ->setViewer($viewer) + ->setOldText($this->getOldValue()) + ->setNewText($this->getNewValue()); + } + + public function newRemarkupChanges() { + $changes = array(); + + $changes[] = $this->newRemarkupChange() + ->setOldValue($this->getOldValue()) + ->setNewValue($this->getNewValue()); + + return $changes; + } + +} diff --git a/src/applications/repository/storage/PhabricatorRepositoryCommit.php b/src/applications/repository/storage/PhabricatorRepositoryCommit.php index 447db08e6c..5615b5231f 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryCommit.php +++ b/src/applications/repository/storage/PhabricatorRepositoryCommit.php @@ -25,9 +25,8 @@ final class PhabricatorRepositoryCommit protected $committerIdentityPHID; protected $commitIdentifier; protected $epoch; - protected $mailKey; protected $authorPHID; - protected $auditStatus = PhabricatorAuditCommitStatusConstants::NONE; + protected $auditStatus = DiffusionCommitAuditStatus::NONE; protected $summary = ''; protected $importStatus = 0; @@ -116,11 +115,10 @@ final class PhabricatorRepositoryCommit self::CONFIG_TIMESTAMPS => false, self::CONFIG_COLUMN_SCHEMA => array( 'commitIdentifier' => 'text40', - 'mailKey' => 'bytes20', 'authorPHID' => 'phid?', 'authorIdentityPHID' => 'phid?', 'committerIdentityPHID' => 'phid?', - 'auditStatus' => 'uint32', + 'auditStatus' => 'text32', 'summary' => 'text255', 'importStatus' => 'uint32', ), @@ -321,13 +319,6 @@ final class PhabricatorRepositoryCommit return mpull($audits, 'getAuditorPHID'); } - public function save() { - if (!$this->mailKey) { - $this->mailKey = Filesystem::readRandomCharacters(20); - } - return parent::save(); - } - public function delete() { $data = $this->loadCommitData(); $audits = id(new PhabricatorRepositoryAuditRequest()) @@ -381,27 +372,24 @@ final class PhabricatorRepositoryCommit } } - $current_status = $this->getAuditStatus(); - $status_verify = PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION; - if ($any_concern) { - if ($current_status == $status_verify) { + if ($this->isAuditStatusNeedsVerification()) { // If the change is in "Needs Verification", we keep it there as // long as any auditors still have concerns. - $status = $status_verify; + $status = DiffusionCommitAuditStatus::NEEDS_VERIFICATION; } else { - $status = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED; + $status = DiffusionCommitAuditStatus::CONCERN_RAISED; } } else if ($any_accept) { if ($any_need) { - $status = PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED; + $status = DiffusionCommitAuditStatus::PARTIALLY_AUDITED; } else { - $status = PhabricatorAuditCommitStatusConstants::FULLY_AUDITED; + $status = DiffusionCommitAuditStatus::AUDITED; } } else if ($any_need) { - $status = PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT; + $status = DiffusionCommitAuditStatus::NEEDS_AUDIT; } else { - $status = PhabricatorAuditCommitStatusConstants::NONE; + $status = DiffusionCommitAuditStatus::NONE; } return $this->setAuditStatus($status); @@ -532,7 +520,31 @@ final class PhabricatorRepositoryCommit public function getAuditStatusObject() { $status = $this->getAuditStatus(); - return PhabricatorAuditCommitStatusConstants::newForLegacyStatus($status); + return DiffusionCommitAuditStatus::newForStatus($status); + } + + public function isAuditStatusNoAudit() { + return $this->getAuditStatusObject()->isNoAudit(); + } + + public function isAuditStatusNeedsAudit() { + return $this->getAuditStatusObject()->isNeedsAudit(); + } + + public function isAuditStatusConcernRaised() { + return $this->getAuditStatusObject()->isConcernRaised(); + } + + public function isAuditStatusNeedsVerification() { + return $this->getAuditStatusObject()->isNeedsVerification(); + } + + public function isAuditStatusPartiallyAudited() { + return $this->getAuditStatusObject()->isPartiallyAudited(); + } + + public function isAuditStatusAudited() { + return $this->getAuditStatusObject()->isAudited(); } /* -( PhabricatorPolicyInterface )----------------------------------------- */ @@ -584,7 +596,6 @@ final class PhabricatorRepositoryCommit 'phid' => $this->getPHID(), 'commitIdentifier' => $this->getCommitIdentifier(), 'epoch' => $this->getEpoch(), - 'mailKey' => $this->getMailKey(), 'authorPHID' => $this->getAuthorPHID(), 'auditStatus' => $this->getAuditStatus(), 'summary' => $this->getSummary(), @@ -830,16 +841,110 @@ final class PhabricatorRepositoryCommit ->setKey('identifier') ->setType('string') ->setDescription(pht('The commit identifier.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('repositoryPHID') + ->setType('phid') + ->setDescription(pht('The repository this commit belongs to.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('author') + ->setType('map') + ->setDescription(pht('Information about the commit author.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('committer') + ->setType('map') + ->setDescription(pht('Information about the committer.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('isImported') + ->setType('bool') + ->setDescription(pht('True if the commit is fully imported.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('isUnreachable') + ->setType('bool') + ->setDescription( + pht( + 'True if the commit is not the ancestor of any tag, branch, or '. + 'ref.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('auditStatus') + ->setType('map') + ->setDescription(pht('Information about the current audit status.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('message') + ->setType('string') + ->setDescription(pht('The commit message.')), ); } public function getFieldValuesForConduit() { + $data = $this->getCommitData(); - // NOTE: This data should be similar to the information returned about - // commmits by "differential.diff.search" with the "commits" attachment. + $author_identity = $this->getAuthorIdentity(); + if ($author_identity) { + $author_name = $author_identity->getIdentityDisplayName(); + $author_email = $author_identity->getIdentityEmailAddress(); + $author_raw = $author_identity->getIdentityName(); + $author_identity_phid = $author_identity->getPHID(); + $author_user_phid = $author_identity->getCurrentEffectiveUserPHID(); + } else { + $author_name = null; + $author_email = null; + $author_raw = null; + $author_identity_phid = null; + $author_user_phid = null; + } + + $committer_identity = $this->getCommitterIdentity(); + if ($committer_identity) { + $committer_name = $committer_identity->getIdentityDisplayName(); + $committer_email = $committer_identity->getIdentityEmailAddress(); + $committer_raw = $committer_identity->getIdentityName(); + $committer_identity_phid = $committer_identity->getPHID(); + $committer_user_phid = $committer_identity->getCurrentEffectiveUserPHID(); + } else { + $committer_name = null; + $committer_email = null; + $committer_raw = null; + $committer_identity_phid = null; + $committer_user_phid = null; + } + + $author_epoch = $data->getCommitDetail('authorEpoch'); + if ($author_epoch) { + $author_epoch = (int)$author_epoch; + } else { + $author_epoch = null; + } + + $audit_status = $this->getAuditStatusObject(); return array( 'identifier' => $this->getCommitIdentifier(), + 'repositoryPHID' => $this->getRepository()->getPHID(), + 'author' => array( + 'name' => $author_name, + 'email' => $author_email, + 'raw' => $author_raw, + 'epoch' => $author_epoch, + 'identityPHID' => $author_identity_phid, + 'userPHID' => $author_user_phid, + ), + 'committer' => array( + 'name' => $committer_name, + 'email' => $committer_email, + 'raw' => $committer_raw, + 'epoch' => (int)$this->getEpoch(), + 'identityPHID' => $committer_identity_phid, + 'userPHID' => $committer_user_phid, + ), + 'isUnreachable' => (bool)$this->isUnreachable(), + 'isImported' => (bool)$this->isImported(), + 'auditStatus' => array( + 'value' => $audit_status->getKey(), + 'name' => $audit_status->getName(), + 'closed' => (bool)$audit_status->getIsClosed(), + 'color.ansi' => $audit_status->getAnsiColor(), + ), + 'message' => $data->getCommitMessage(), ); } diff --git a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php index 335ba58852..235d74d6f3 100644 --- a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php +++ b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php @@ -230,9 +230,20 @@ EOTEXT $constants_rows = array(); foreach ($constants as $constant) { + if ($constant->getIsDeprecated()) { + $icon = id(new PHUIIconView()) + ->setIcon('fa-exclamation-triangle', 'red'); + } else { + $icon = null; + } + $constants_rows[] = array( $constant->getKey(), - $constant->getValue(), + array( + $icon, + ' ', + $constant->getValue(), + ), ); } @@ -244,7 +255,7 @@ EOTEXT )) ->setColumnClasses( array( - 'pre', + 'mono', 'wide', )); diff --git a/src/applications/search/field/PhabricatorSearchCheckboxesField.php b/src/applications/search/field/PhabricatorSearchCheckboxesField.php index 0985c976ce..ce41f85db3 100644 --- a/src/applications/search/field/PhabricatorSearchCheckboxesField.php +++ b/src/applications/search/field/PhabricatorSearchCheckboxesField.php @@ -4,6 +4,7 @@ final class PhabricatorSearchCheckboxesField extends PhabricatorSearchField { private $options; + private $deprecatedOptions = array(); public function setOptions(array $options) { $this->options = $options; @@ -14,6 +15,15 @@ final class PhabricatorSearchCheckboxesField return $this->options; } + public function setDeprecatedOptions(array $deprecated_options) { + $this->deprecatedOptions = $deprecated_options; + return $this; + } + + public function getDeprecatedOptions() { + return $this->deprecatedOptions; + } + protected function getDefaultValue() { return array(); } @@ -23,11 +33,12 @@ final class PhabricatorSearchCheckboxesField return array(); } - return $value; + return $this->getCanonicalValue($value); } protected function getValueFromRequest(AphrontRequest $request, $key) { - return $this->getListFromRequest($request, $key); + $value = $this->getListFromRequest($request, $key); + return $this->getCanonicalValue($value); } protected function newControl() { @@ -58,7 +69,29 @@ final class PhabricatorSearchCheckboxesField ->setValue($option); } + foreach ($this->getDeprecatedOptions() as $key => $value) { + $list[] = id(new ConduitConstantDescription()) + ->setKey($key) + ->setIsDeprecated(true) + ->setValue(pht('Deprecated alias for "%s".', $value)); + } + return $list; } + private function getCanonicalValue(array $values) { + // Always map the current normal options to themselves. + $normal_options = array_fuse(array_keys($this->getOptions())); + + // Map deprecated values to their new values. + $deprecated_options = $this->getDeprecatedOptions(); + + $map = $normal_options + $deprecated_options; + foreach ($values as $key => $value) { + $values[$key] = idx($map, $value, $value); + } + + return $values; + } + } diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index 1873fcb6e4..b4dd09ed10 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -850,6 +850,10 @@ abstract class PhabricatorApplicationTransactionEditor $xaction->setIsSilentTransaction(true); } + if ($actor->hasHighSecuritySession()) { + $xaction->setIsMFATransaction(true); + } + return $xaction; } diff --git a/src/applications/transactions/response/PhabricatorApplicationTransactionResponse.php b/src/applications/transactions/response/PhabricatorApplicationTransactionResponse.php index 08e6ec464b..43a2fbc1b5 100644 --- a/src/applications/transactions/response/PhabricatorApplicationTransactionResponse.php +++ b/src/applications/transactions/response/PhabricatorApplicationTransactionResponse.php @@ -83,7 +83,15 @@ final class PhabricatorApplicationTransactionResponse $xactions[$key] = hsprintf('%s', $xaction); } + $aural = phutil_tag( + 'h3', + array( + 'class' => 'aural-only', + ), + pht('Comment Preview')); + $content = array( + 'header' => hsprintf('%s', $aural), 'xactions' => $xactions, 'spacer' => PHUITimelineView::renderSpacer(), 'previewContent' => hsprintf('%s', $this->getPreviewContent()), diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index ed88ac98db..c65adcac6b 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -165,6 +165,14 @@ abstract class PhabricatorApplicationTransaction return (bool)$this->getMetadataValue('core.silent', false); } + public function setIsMFATransaction($mfa) { + return $this->setMetadataValue('core.mfa', $mfa); + } + + public function getIsMFATransaction() { + return (bool)$this->getMetadataValue('core.mfa', false); + } + public function attachComment( PhabricatorApplicationTransactionComment $comment) { $this->comment = $comment; @@ -1461,6 +1469,12 @@ abstract class PhabricatorApplicationTransaction if ($is_silent != $xaction->getIsSilentTransaction()) { return false; } + + // Don't group MFA and non-MFA transactions together. + $is_mfa = $this->getIsMFATransaction(); + if ($is_mfa != $xaction->getIsMFATransaction()) { + return false; + } } return true; diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php index 7469004e1b..8be45f8cde 100644 --- a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php +++ b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php @@ -243,8 +243,16 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView { $comment_box = id(new PHUIObjectBoxView()) ->setFlush(true) + ->setNoBorder(true) ->addClass('phui-comment-form-view') ->addSigil('phui-comment-form') + ->appendChild( + phutil_tag( + 'h3', + array( + 'class' => 'aural-only', + ), + pht('Add Comment'))) ->appendChild($image) ->appendChild($badge_view) ->appendChild($wedge) diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionView.php index e10f5c008e..9916628edf 100644 --- a/src/applications/transactions/view/PhabricatorApplicationTransactionView.php +++ b/src/applications/transactions/view/PhabricatorApplicationTransactionView.php @@ -424,7 +424,8 @@ class PhabricatorApplicationTransactionView extends AphrontView { ->setIcon($xaction->getIcon()) ->setColor($xaction->getColor()) ->setHideCommentOptions($this->getHideCommentOptions()) - ->setIsSilent($xaction->getIsSilentTransaction()); + ->setIsSilent($xaction->getIsSilentTransaction()) + ->setIsMFA($xaction->getIsMFATransaction()); list($token, $token_removed) = $xaction->getToken(); if ($token) { diff --git a/src/docs/user/userguide/phriction.diviner b/src/docs/user/userguide/phriction.diviner index 60ad6357fe..1a56dce46b 100644 --- a/src/docs/user/userguide/phriction.diviner +++ b/src/docs/user/userguide/phriction.diviner @@ -6,5 +6,78 @@ Construct a detailed written history of your civilization. Overview ======== -Phriction is a simple wiki. You can edit pages, and the text you write will stay +Phriction is a wiki. You can edit pages, and the text you write will stay there. Other people can see it later. + +Phriction documents are arranged in a hierarchy, like a filesystem. This can +make it easier to keep things organized and to apply policy controls to +groups of documents. + + +Policies +======== + +Documents and policies in Phriction are hierarchical, similar to a filesystem. +For example, a document called "Zebra Information" may be located +at `/zoo/animals/zebra/`. + +To view a document in Phrction, you must first be able to view all of its +ancestors. So a user can only see {nav Zoo > Animals > Zebra Information} if +they can see the pages {nav Zoo} and {nav Zoo > Animals}. + +This allows sections of the wiki to be restricted by applying a restrictive +policy to the parent (or grandparent) document. For example, if you apply a +restrictive view policy to the {nav Zoo} page, it will implicitly apply to +all sub-pages, including {nav Zoo > Animals > Zebra Information}. + + +Versions and Drafts +=================== + +Document content is tracked with linear version numbers: version 1, version 2, +version 3, and so on. Each time a page is edited, a new version of the page is +created. + +You can {nav View History} to review older versions of a page and see how it +has changed over time (and who has changed it). + +When you visit a particular document, you are normally shown the most recent +version of that document. For example, if there are 17 versions, you'll see +version 17. + +Likewise, when you edit a document using {nav Edit Document > Save and Publish}, +your changes are published immediately. If there were previously 17 versions, +your new changes will become version 18 and visitors to the document will begin +seeing version 18. + +If you want to edit a document without publishing the changes right away, you +can use {nav Edit Document > Save as Draft} instead. This will still create a +new version 18, but it won't change which version users see when they visit the +document: they'll still see version 17 (the last published version). + +You (and other users) can continue editing the draft by using +{nav Edit Document}. (Once a document has an unpublished draft, editing will +stay in draft mode.) + +Once you're satisfied with your changes, use {nav Publish Draft} to make your +changes the current visible version of the document that users see by default +when they visit it. + +If you made a mistake and published something you didn't intend to, you can +navigate back to an older version of the document and use +{nav Publish Older Version} to change the current visible version of the +document to some older version. + +Note that draft versions are still normal versions of the document: they are +not private, they can not be deleted, other users can see them if they can see +the document, and they will eventually become a standard part of the document +history. The only private parts of drafts are: editing a draft does not +generate a feed story; and users won't see draft content by default when +viewing a document. + +Drafts may be a good fit if you are: + + - working on changes over time; or + - starting with a rough change and refining it in several iterations; or + - collaborating with others on a change; or + - sharing changes before they're published to get feedback. diff --git a/src/view/phui/PHUIButtonView.php b/src/view/phui/PHUIButtonView.php index bad24122bc..2231649f12 100644 --- a/src/view/phui/PHUIButtonView.php +++ b/src/view/phui/PHUIButtonView.php @@ -233,14 +233,15 @@ final class PHUIButtonView extends AphrontTagView { $classes = array(); } - // See PHI823. If we aren't rendering a "