diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 2716e0d4c5..e83581e17c 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -204,6 +204,8 @@ phutil_register_library_map(array( 'DifferentialCommitsFieldSpecification' => 'applications/differential/field/specification/commits', 'DifferentialController' => 'applications/differential/controller/base', 'DifferentialDAO' => 'applications/differential/storage/base', + 'DifferentialDateCreatedFieldSpecification' => 'applications/differential/field/specification/datecreated', + 'DifferentialDateModifiedFieldSpecification' => 'applications/differential/field/specification/datemodified', 'DifferentialDefaultFieldSelector' => 'applications/differential/field/selector/default', 'DifferentialDependenciesFieldSpecification' => 'applications/differential/field/specification/dependencies', 'DifferentialDiff' => 'applications/differential/storage/diff', @@ -1033,6 +1035,8 @@ phutil_register_library_map(array( 'DifferentialCommitsFieldSpecification' => 'DifferentialFieldSpecification', 'DifferentialController' => 'PhabricatorController', 'DifferentialDAO' => 'PhabricatorLiskDAO', + 'DifferentialDateCreatedFieldSpecification' => 'DifferentialFieldSpecification', + 'DifferentialDateModifiedFieldSpecification' => 'DifferentialFieldSpecification', 'DifferentialDefaultFieldSelector' => 'DifferentialFieldSelector', 'DifferentialDependenciesFieldSpecification' => 'DifferentialFieldSpecification', 'DifferentialDiff' => 'DifferentialDAO', diff --git a/src/applications/differential/controller/revisionlist/DifferentialRevisionListController.php b/src/applications/differential/controller/revisionlist/DifferentialRevisionListController.php index c00e2d1a67..57dbfba859 100644 --- a/src/applications/differential/controller/revisionlist/DifferentialRevisionListController.php +++ b/src/applications/differential/controller/revisionlist/DifferentialRevisionListController.php @@ -366,6 +366,10 @@ class DifferentialRevisionListController extends DifferentialController { private function buildViews($filter, $user_phid, array $revisions) { $user = $this->getRequest()->getUser(); + $template = id(new DifferentialRevisionListView()) + ->setUser($user) + ->setFields(DifferentialRevisionListView::getDefaultFields()); + $views = array(); switch ($filter) { case 'active': @@ -373,18 +377,16 @@ class DifferentialRevisionListController extends DifferentialController { $revisions, $user_phid); - $view = id(new DifferentialRevisionListView()) + $view = id(clone $template) ->setRevisions($active) - ->setUser($user) ->setNoDataString("You have no active revisions requiring action."); $views[] = array( 'title' => 'Action Required', 'view' => $view, ); - $view = id(new DifferentialRevisionListView()) + $view = id(clone $template) ->setRevisions($waiting) - ->setUser($user) ->setNoDataString("You have no active revisions waiting on others."); $views[] = array( 'title' => 'Waiting On Others', @@ -401,9 +403,8 @@ class DifferentialRevisionListController extends DifferentialController { 'subscribed' => 'Revisions by Subscriber', 'all' => 'Revisions', ); - $view = id(new DifferentialRevisionListView()) - ->setRevisions($revisions) - ->setUser($user); + $view = id(clone $template) + ->setRevisions($revisions); $views[] = array( 'title' => idx($titles, $filter), 'view' => $view, @@ -412,6 +413,7 @@ class DifferentialRevisionListController extends DifferentialController { default: throw new Exception("Unknown filter '{$filter}'!"); } + return $views; } diff --git a/src/applications/differential/field/selector/base/DifferentialFieldSelector.php b/src/applications/differential/field/selector/base/DifferentialFieldSelector.php index 9ae8cfa0ca..7b58e92414 100644 --- a/src/applications/differential/field/selector/base/DifferentialFieldSelector.php +++ b/src/applications/differential/field/selector/base/DifferentialFieldSelector.php @@ -1,7 +1,7 @@ getAuthorPHID(); } + public function shouldAppearOnRevisionList() { + return true; + } + + public function renderHeaderForRevisionList() { + return 'Author'; + } + + public function renderValueForRevisionList(DifferentialRevision $revision) { + return $this->getHandle($revision->getAuthorPHID())->renderLink(); + } + + public function getRequiredHandlePHIDsForRevisionList( + DifferentialRevision $revision) { + return array($revision->getAuthorPHID()); + } + } diff --git a/src/applications/differential/field/specification/base/DifferentialFieldSpecification.php b/src/applications/differential/field/specification/base/DifferentialFieldSpecification.php index 6e26e58244..30a912b45b 100644 --- a/src/applications/differential/field/specification/base/DifferentialFieldSpecification.php +++ b/src/applications/differential/field/specification/base/DifferentialFieldSpecification.php @@ -27,6 +27,7 @@ * @task storage Field Storage * @task edit Extending the Revision Edit Interface * @task view Extending the Revision View Interface + * @task list Extending the Revision List Interface * @task conduit Extending the Conduit View Interface * @task commit Extending Commit Messages * @task load Loading Additional Data @@ -264,6 +265,59 @@ abstract class DifferentialFieldSpecification { } +/* -( Extending the Revision List Interface )------------------------------ */ + + + /** + * Determine if this field should appear in the table on the revision list + * interface. + * + * @return bool True if this field should appear in the table. + * + * @task list + */ + public function shouldAppearOnRevisionList() { + return false; + } + + + /** + * Return a column header for revision list tables. + * + * @return string Column header. + * + * @task list + */ + public function renderHeaderForRevisionList() { + throw new DifferentialFieldSpecificationIncompleteException($this); + } + + + /** + * Optionally, return a column class for revision list tables. + * + * @return string CSS class for table cells. + * + * @task list + */ + public function getColumnClassForRevisionList() { + return null; + } + + + /** + * Return a table cell value for revision list tables. + * + * @param DifferentialRevision The revision to render a value for. + * @return string Table cell value. + * + * @task list + */ + public function renderValueForRevisionList(DifferentialRevision $revision) { + throw new DifferentialFieldSpecificationIncompleteException($this); + } + + /* -( Extending the Conduit Interface )------------------------------------ */ @@ -483,6 +537,7 @@ abstract class DifferentialFieldSpecification { return array(); } + /** * Specify which @{class:PhabricatorObjectHandles} need to be loaded for your * field to render correctly on the view interface. @@ -498,6 +553,25 @@ abstract class DifferentialFieldSpecification { return $this->getRequiredHandlePHIDs(); } + + /** + * Specify which @{class:PhabricatorObjectHandles} need to be loaded for your + * field to render correctly on the list interface. + * + * This is a more specific version of @{method:getRequiredHandlePHIDs} which + * can be overridden to improve field performance by loading only data you + * need. + * + * @param DifferentialRevision The revision to pull PHIDs for. + * @return list List of PHIDs to load handles for. + * @task load + */ + public function getRequiredHandlePHIDsForRevisionList( + DifferentialRevision $revision) { + return array(); + } + + /** * Specify which @{class:PhabricatorObjectHandles} need to be loaded for your * field to render correctly on the edit interface. diff --git a/src/applications/differential/field/specification/datecreated/DifferentialDateCreatedFieldSpecification.php b/src/applications/differential/field/specification/datecreated/DifferentialDateCreatedFieldSpecification.php new file mode 100644 index 0000000000..b0b21a5bf5 --- /dev/null +++ b/src/applications/differential/field/specification/datecreated/DifferentialDateCreatedFieldSpecification.php @@ -0,0 +1,38 @@ +getDateCreated(), $this->getUser()); + } + +} diff --git a/src/applications/differential/field/specification/datecreated/__init__.php b/src/applications/differential/field/specification/datecreated/__init__.php new file mode 100644 index 0000000000..79276ec487 --- /dev/null +++ b/src/applications/differential/field/specification/datecreated/__init__.php @@ -0,0 +1,13 @@ +getDateModified(), $this->getUser()); + } + +} diff --git a/src/applications/differential/field/specification/datemodified/__init__.php b/src/applications/differential/field/specification/datemodified/__init__.php new file mode 100644 index 0000000000..8cfaee8f62 --- /dev/null +++ b/src/applications/differential/field/specification/datemodified/__init__.php @@ -0,0 +1,13 @@ +getLineCount())); } + public function shouldAppearOnRevisionList() { + return true; + } + + public function renderHeaderForRevisionList() { + return 'Lines'; + } + + public function getColumnClassForRevisionList() { + return 'n'; + } + + public function renderValueForRevisionList(DifferentialRevision $revision) { + return number_format($revision->getLineCount()); + } + } diff --git a/src/applications/differential/field/specification/reviewers/DifferentialReviewersFieldSpecification.php b/src/applications/differential/field/specification/reviewers/DifferentialReviewersFieldSpecification.php index a40f961e72..1065edef14 100644 --- a/src/applications/differential/field/specification/reviewers/DifferentialReviewersFieldSpecification.php +++ b/src/applications/differential/field/specification/reviewers/DifferentialReviewersFieldSpecification.php @@ -1,7 +1,7 @@ parseCommitMessageUserList($value); } + public function shouldAppearOnRevisionList() { + return true; + } + + public function renderHeaderForRevisionList() { + return 'Reviewers'; + } + + public function renderValueForRevisionList(DifferentialRevision $revision) { + $reviewer_phids = $revision->getReviewers(); + if ($reviewer_phids) { + $first = reset($reviewer_phids); + if (count($reviewer_phids) > 1) { + $suffix = ' (+'.(count($reviewer_phids) - 1).')'; + } else { + $suffix = null; + } + return $this->getHandle($first)->renderLink().$suffix; + } else { + return 'None'; + } + } + + public function getRequiredHandlePHIDsForRevisionList( + DifferentialRevision $revision) { + $reviewer_phids = $revision->getReviewers(); + if ($reviewer_phids) { + return array(reset($reviewer_phids)); + } + return array(); + } + } diff --git a/src/applications/differential/field/specification/revisionid/DifferentialRevisionIDFieldSpecification.php b/src/applications/differential/field/specification/revisionid/DifferentialRevisionIDFieldSpecification.php index 769fe79334..9d451b1252 100644 --- a/src/applications/differential/field/specification/revisionid/DifferentialRevisionIDFieldSpecification.php +++ b/src/applications/differential/field/specification/revisionid/DifferentialRevisionIDFieldSpecification.php @@ -92,4 +92,16 @@ final class DifferentialRevisionIDFieldSpecification return null; } + public function shouldAppearOnRevisionList() { + return true; + } + + public function renderHeaderForRevisionList() { + return 'ID'; + } + + public function renderValueForRevisionList(DifferentialRevision $revision) { + return 'D'.$revision->getID(); + } + } diff --git a/src/applications/differential/field/specification/revisionstatus/DifferentialRevisionStatusFieldSpecification.php b/src/applications/differential/field/specification/revisionstatus/DifferentialRevisionStatusFieldSpecification.php index a64320d04d..4fb35648ea 100644 --- a/src/applications/differential/field/specification/revisionstatus/DifferentialRevisionStatusFieldSpecification.php +++ b/src/applications/differential/field/specification/revisionstatus/DifferentialRevisionStatusFieldSpecification.php @@ -54,4 +54,17 @@ final class DifferentialRevisionStatusFieldSpecification return ''.$status.''.$next_step; } + public function shouldAppearOnRevisionList() { + return true; + } + + public function renderHeaderForRevisionList() { + return 'Status'; + } + + public function renderValueForRevisionList(DifferentialRevision $revision) { + return ArcanistDifferentialRevisionStatus::getNameForRevisionStatus( + $revision->getStatus()); + } + } diff --git a/src/applications/differential/field/specification/title/DifferentialTitleFieldSpecification.php b/src/applications/differential/field/specification/title/DifferentialTitleFieldSpecification.php index 9a55539144..81883a45c5 100644 --- a/src/applications/differential/field/specification/title/DifferentialTitleFieldSpecification.php +++ b/src/applications/differential/field/specification/title/DifferentialTitleFieldSpecification.php @@ -1,7 +1,7 @@ '/D'.$revision->getID(), + ), + phutil_escape_html($revision->getTitle())); + } + } diff --git a/src/applications/differential/field/specification/title/__init__.php b/src/applications/differential/field/specification/title/__init__.php index ddc512831d..7f0db8769f 100644 --- a/src/applications/differential/field/specification/title/__init__.php +++ b/src/applications/differential/field/specification/title/__init__.php @@ -10,6 +10,7 @@ phutil_require_module('phabricator', 'applications/differential/field/exception/ phutil_require_module('phabricator', 'applications/differential/field/specification/base'); phutil_require_module('phabricator', 'view/form/control/textarea'); +phutil_require_module('phutil', 'markup'); phutil_require_module('phutil', 'utils'); diff --git a/src/applications/differential/view/revisionlist/DifferentialRevisionListView.php b/src/applications/differential/view/revisionlist/DifferentialRevisionListView.php index 472a0de152..5389a6b90f 100644 --- a/src/applications/differential/view/revisionlist/DifferentialRevisionListView.php +++ b/src/applications/differential/view/revisionlist/DifferentialRevisionListView.php @@ -25,6 +25,12 @@ final class DifferentialRevisionListView extends AphrontView { private $handles; private $user; private $noDataString; + private $fields; + + public function setFields(array $fields) { + $this->fields = $fields; + return $this; + } public function setRevisions(array $revisions) { $this->revisions = $revisions; @@ -33,14 +39,12 @@ final class DifferentialRevisionListView extends AphrontView { public function getRequiredHandlePHIDs() { $phids = array(); - foreach ($this->revisions as $revision) { - $phids[] = $revision->getAuthorPHID(); - $reviewers = $revision->getReviewers(); - if ($reviewers) { - $phids[] = head($reviewers); + foreach ($this->fields as $field) { + foreach ($this->revisions as $revision) { + $phids[] = $field->getRequiredHandlePHIDsForRevisionList($revision); } } - return $phids; + return array_mergev($phids); } public function setHandles(array $handles) { @@ -65,67 +69,30 @@ final class DifferentialRevisionListView extends AphrontView { throw new Exception("Call setUser() before render()!"); } + foreach ($this->fields as $field) { + $field->setUser($this->user); + $field->setHandles($this->handles); + } + $rows = array(); foreach ($this->revisions as $revision) { - $status = $revision->getStatus(); - $status = - ArcanistDifferentialRevisionStatus::getNameForRevisionStatus($status); - - $reviewer_phids = $revision->getReviewers(); - if ($reviewer_phids) { - $first = reset($reviewer_phids); - if (count($reviewer_phids) > 1) { - $suffix = ' (+'.(count($reviewer_phids) - 1).')'; - } else { - $suffix = null; - } - $reviewers = $this->handles[$first]->renderLink().$suffix; - } else { - $reviewers = 'None'; + $row = array(); + foreach ($this->fields as $field) { + $row[] = $field->renderValueForRevisionList($revision); } + $rows[] = $row; + } - $link = phutil_render_tag( - 'a', - array( - 'href' => '/D'.$revision->getID(), - ), - phutil_escape_html($revision->getTitle())); - - $rows[] = array( - 'D'.$revision->getID(), - $link, - phutil_escape_html($status), - number_format($revision->getLineCount()), - $this->handles[$revision->getAuthorPHID()]->renderLink(), - $reviewers, - phabricator_datetime($revision->getDateModified(), $user), - phabricator_date($revision->getDateCreated(), $user), - ); + $headers = array(); + $classes = array(); + foreach ($this->fields as $field) { + $headers[] = $field->renderHeaderForRevisionList(); + $classes[] = $field->getColumnClassForRevisionList(); } $table = new AphrontTableView($rows); - $table->setHeaders( - array( - 'ID', - 'Revision', - 'Status', - 'Lines', - 'Author', - 'Reviewers', - 'Modified', - 'Created', - )); - $table->setColumnClasses( - array( - null, - 'wide pri', - null, - 'n', - null, - null, - 'right', - 'right', - )); + $table->setHeaders($headers); + $table->setColumnClasses($classes); if ($this->noDataString) { $table->setNoDataString($this->noDataString); @@ -134,4 +101,22 @@ final class DifferentialRevisionListView extends AphrontView { return $table->render(); } + public static function getDefaultFields() { + $selector = DifferentialFieldSelector::newSelector(); + $fields = $selector->getFieldSpecifications(); + foreach ($fields as $key => $field) { + if (!$field->shouldAppearOnRevisionList()) { + unset($fields[$key]); + } + } + + if (!$fields) { + throw new Exception( + "Phabricator configuration has no fields that appear on the list ". + "interface!"); + } + + return $selector->sortFieldsForRevisionList($fields); + } + } diff --git a/src/applications/differential/view/revisionlist/__init__.php b/src/applications/differential/view/revisionlist/__init__.php index 090036fc33..e941167b4d 100644 --- a/src/applications/differential/view/revisionlist/__init__.php +++ b/src/applications/differential/view/revisionlist/__init__.php @@ -6,13 +6,10 @@ -phutil_require_module('arcanist', 'differential/constants/revisionstatus'); - +phutil_require_module('phabricator', 'applications/differential/field/selector/base'); phutil_require_module('phabricator', 'view/base'); phutil_require_module('phabricator', 'view/control/table'); -phutil_require_module('phabricator', 'view/utils'); -phutil_require_module('phutil', 'markup'); phutil_require_module('phutil', 'utils'); diff --git a/src/applications/directory/controller/main/PhabricatorDirectoryMainController.php b/src/applications/directory/controller/main/PhabricatorDirectoryMainController.php index 2c9a7096dc..819f2f7f2d 100644 --- a/src/applications/directory/controller/main/PhabricatorDirectoryMainController.php +++ b/src/applications/directory/controller/main/PhabricatorDirectoryMainController.php @@ -292,8 +292,11 @@ class PhabricatorDirectoryMainController "View Active Revisions \xC2\xBB")); if ($active) { + $fields = + $revision_view = id(new DifferentialRevisionListView()) ->setRevisions($active) + ->setFields(DifferentialRevisionListView::getDefaultFields()) ->setUser($user); $phids = array_merge( array($user_phid),