From 9bab3c25b83d8473e336790eb683da34ecb0d1f9 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 13 Feb 2016 08:27:29 -0800 Subject: [PATCH 01/42] Sort milestones by milestone number, not ID Summary: Ref T10350. Normally, milestone numbers and IDs have the same order, but they may not if you used the script in T10350 to artificially move a bunch of stuff around. Test Plan: Milestones now go "1, 2, " on local install. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10350 Differential Revision: https://secure.phabricator.com/D15266 --- .../controller/PhabricatorProjectProfileController.php | 2 +- .../controller/PhabricatorProjectSubprojectsController.php | 2 +- src/applications/project/query/PhabricatorProjectQuery.php | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php index 27cf1bf344..9ca30c22ed 100644 --- a/src/applications/project/controller/PhabricatorProjectProfileController.php +++ b/src/applications/project/controller/PhabricatorProjectProfileController.php @@ -205,7 +205,7 @@ final class PhabricatorProjectProfileController array( PhabricatorProjectStatus::STATUS_ACTIVE, )) - ->setOrder('newest') + ->setOrderVector(array('milestoneNumber', 'id')) ->execute(); if (!$milestones) { return null; diff --git a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php index afafed77b1..36f9d641a8 100644 --- a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php +++ b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php @@ -43,7 +43,7 @@ final class PhabricatorProjectSubprojectsController ->withParentProjectPHIDs(array($project->getPHID())) ->needImages(true) ->withIsMilestone(true) - ->setOrder('newest') + ->setOrderVector(array('milestoneNumber', 'id')) ->execute(); } else { $milestones = array(); diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php index d12e66e392..b72fa1cec2 100644 --- a/src/applications/project/query/PhabricatorProjectQuery.php +++ b/src/applications/project/query/PhabricatorProjectQuery.php @@ -170,6 +170,11 @@ final class PhabricatorProjectQuery 'type' => 'string', 'unique' => true, ), + 'milestoneNumber' => array( + 'table' => $this->getPrimaryTableAlias(), + 'column' => 'milestoneNumber', + 'type' => 'int', + ), ); } From 3f50ba90f1d0d30008ea13457dd90f8ea83f25b7 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 13 Feb 2016 08:31:28 -0800 Subject: [PATCH 02/42] On workboards, put older milestone columns on the right Summary: Ref T10349. Instead of showing columns in "Backlog, Custom, Sprint 1, Sprint 2, Sprint 3" order, show the sprints in reverse order: 3, 2, 1. This makes it easier to get to the new stuff, and you don't have to drag over older stuff or archive it immediately. Trello's own meta-board for Trello development is a good example of this in the wild: older stuff goes out to the right, so you can get to the newer stuff easily: https://trello.com/b/nC8QJJoZ/trello-development Test Plan: Saw board in 3, 2, 1 order instead of 1, 2, 3. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10349 Differential Revision: https://secure.phabricator.com/D15267 --- .../project/storage/PhabricatorProjectColumn.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/applications/project/storage/PhabricatorProjectColumn.php b/src/applications/project/storage/PhabricatorProjectColumn.php index 4092ca7fa8..683039fe99 100644 --- a/src/applications/project/storage/PhabricatorProjectColumn.php +++ b/src/applications/project/storage/PhabricatorProjectColumn.php @@ -169,14 +169,16 @@ final class PhabricatorProjectColumn // Normal columns and subproject columns go first, in a user-controlled // order. - // All the milestone columns go last, in their sequential order. + // All the milestone columns go last, in reverse order (newest on the + // left) so that you don't have to scroll across older milestones to get + // to the newest ones. if (!$proxy || !$proxy->isMilestone()) { $group = 'A'; $sequence = $this->getSequence(); } else { $group = 'B'; - $sequence = $proxy->getMilestoneNumber(); + $sequence = (10000000 - $proxy->getMilestoneNumber()); } return sprintf('%s%012d', $group, $sequence); From 329d03661f3b4e3b651b3b2ba5b12a2aa0d497e6 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 13 Feb 2016 10:02:42 -0800 Subject: [PATCH 03/42] Use an extended policy to bind column and board policies together Summary: Ref T10349. Columns have the same policies as the projects they belong to. However, the current implementation just returns the policy directly. This usually works, but if the project has a policy like "Members of (This) Project", the policy filter tries to check if the viewer is a member of //the column itself//. That doesn't work, since columns don't have members. This leads to a situation where columns on "Editable By: Project Members" projects can not be edited. Instead, return a permissive base policy and then use an extended policy to bind the column policy to the project policy. Test Plan: - Edited a column on an "Editable By: Members of Project" board. - Added and ran a unit test covering this case. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10349 Differential Revision: https://secure.phabricator.com/D15268 --- src/__phutil_library_map__.php | 1 + .../PhabricatorProjectCoreTestCase.php | 61 +++++++++++++++++++ ...abricatorProjectColumnDetailController.php | 3 +- .../storage/PhabricatorProjectColumn.php | 22 ++++++- 4 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index aacf85ac45..45cfd6fe6a 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -7305,6 +7305,7 @@ phutil_register_library_map(array( 'PhabricatorApplicationTransactionInterface', 'PhabricatorPolicyInterface', 'PhabricatorDestructibleInterface', + 'PhabricatorExtendedPolicyInterface', ), 'PhabricatorProjectColumnDetailController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectColumnEditController' => 'PhabricatorProjectBoardController', diff --git a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php index ee90afcb3b..39efcadb55 100644 --- a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php +++ b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php @@ -1013,6 +1013,51 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $this->assertColumns($expect, $user, $board, $task); } + public function testColumnExtendedPolicies() { + $user = $this->createUser(); + $user->save(); + + $board = $this->createProject($user); + $column = $this->addColumn($user, $board, 0); + + // At first, the user should be able to view and edit the column. + $column = $this->refreshColumn($user, $column); + $this->assertTrue((bool)$column); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $user, + $column, + PhabricatorPolicyCapability::CAN_EDIT); + $this->assertTrue($can_edit); + + // Now, set the project edit policy to "Members of Project". This should + // disable editing. + $members_policy = id(new PhabricatorProjectMembersPolicyRule()) + ->getObjectPolicyFullKey(); + $board->setEditPolicy($members_policy)->save(); + + $column = $this->refreshColumn($user, $column); + $this->assertTrue((bool)$column); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $user, + $column, + PhabricatorPolicyCapability::CAN_EDIT); + $this->assertFalse($can_edit); + + // Now, join the project. This should make the column editable again. + $this->joinProject($board, $user); + + $column = $this->refreshColumn($user, $column); + $this->assertTrue((bool)$column); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $user, + $column, + PhabricatorPolicyCapability::CAN_EDIT); + $this->assertTrue($can_edit); + } + private function moveToColumn( PhabricatorUser $viewer, PhabricatorProject $board, @@ -1251,6 +1296,22 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { } } + private function refreshColumn( + PhabricatorUser $viewer, + PhabricatorProjectColumn $column) { + + $results = id(new PhabricatorProjectColumnQuery()) + ->setViewer($viewer) + ->withIDs(array($column->getID())) + ->execute(); + + if ($results) { + return head($results); + } else { + return null; + } + } + private function createProject( PhabricatorUser $user, PhabricatorProject $parent = null, diff --git a/src/applications/project/controller/PhabricatorProjectColumnDetailController.php b/src/applications/project/controller/PhabricatorProjectColumnDetailController.php index b0cd4ac0b4..84bdc2b89c 100644 --- a/src/applications/project/controller/PhabricatorProjectColumnDetailController.php +++ b/src/applications/project/controller/PhabricatorProjectColumnDetailController.php @@ -73,8 +73,7 @@ final class PhabricatorProjectColumnDetailController $header = id(new PHUIHeaderView()) ->setUser($viewer) - ->setHeader($column->getDisplayName()) - ->setPolicyObject($column); + ->setHeader($column->getDisplayName()); if ($column->isHidden()) { $header->setStatus('fa-ban', 'dark', pht('Hidden')); diff --git a/src/applications/project/storage/PhabricatorProjectColumn.php b/src/applications/project/storage/PhabricatorProjectColumn.php index 683039fe99..28aed0f5a8 100644 --- a/src/applications/project/storage/PhabricatorProjectColumn.php +++ b/src/applications/project/storage/PhabricatorProjectColumn.php @@ -5,7 +5,8 @@ final class PhabricatorProjectColumn implements PhabricatorApplicationTransactionInterface, PhabricatorPolicyInterface, - PhabricatorDestructibleInterface { + PhabricatorDestructibleInterface, + PhabricatorExtendedPolicyInterface { const STATUS_ACTIVE = 0; const STATUS_HIDDEN = 1; @@ -219,7 +220,14 @@ final class PhabricatorProjectColumn } public function getPolicy($capability) { - return $this->getProject()->getPolicy($capability); + // NOTE: Column policies are enforced as an extended policy which makes + // them the same as the project's policies. + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return PhabricatorPolicies::getMostOpenPolicy(); + case PhabricatorPolicyCapability::CAN_EDIT: + return PhabricatorPolicies::POLICY_USER; + } } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { @@ -233,6 +241,16 @@ final class PhabricatorProjectColumn } +/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */ + + + public function getExtendedPolicy($capability, PhabricatorUser $viewer) { + return array( + array($this->getProject(), $capability), + ); + } + + /* -( PhabricatorDestructibleInterface )----------------------------------- */ public function destroyObjectPermanently( From c1f57605ab341b04417a0b28a2db9c92385a8a73 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 13 Feb 2016 11:55:14 -0800 Subject: [PATCH 04/42] Normalize colors a bit better on Profiles/Projects, add Workboard backgrounds Summary: Cleans up the backgrounds a little on Projects/Profiles and adds a number of colour choices for Workboards. Test Plan: Manually add each color for testing. Test new project / profile layouts with and without properties. {F1109325} {F1109326} {F1109327} {F1109328} Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15269 --- resources/celerity/map.php | 34 +++---- ...PhabricatorPeopleProfileViewController.php | 2 +- .../PhabricatorProjectBoardViewController.php | 5 ++ .../PhabricatorProjectProfileController.php | 2 +- .../css/application/project/project-view.css | 29 ++++-- webroot/rsrc/css/phui/phui-box.css | 20 +++-- webroot/rsrc/css/phui/phui-button.css | 8 +- .../css/phui/phui-object-item-list-view.css | 1 - .../phui/workboards/phui-workboard-color.css | 88 +++++++++++++++++++ .../css/phui/workboards/phui-workboard.css | 2 +- .../css/phui/workboards/phui-workpanel.css | 3 +- 11 files changed, 154 insertions(+), 40 deletions(-) create mode 100644 webroot/rsrc/css/phui/workboards/phui-workboard-color.css diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 644ec2fb2f..bd466223c7 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ */ return array( 'names' => array( - 'core.pkg.css' => 'b59766ad', + 'core.pkg.css' => '394a6788', 'core.pkg.js' => 'd7daa6d8', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '2de124c9', @@ -94,7 +94,7 @@ return array( 'rsrc/css/application/policy/policy.css' => '957ea14c', 'rsrc/css/application/ponder/ponder-view.css' => '7b0df4da', 'rsrc/css/application/project/project-card-view.css' => '9418c97d', - 'rsrc/css/application/project/project-view.css' => '4693497c', + 'rsrc/css/application/project/project-view.css' => '4c832c27', 'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733', 'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5', 'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd', @@ -123,8 +123,8 @@ return array( 'rsrc/css/phui/phui-action-panel.css' => '91c7b835', 'rsrc/css/phui/phui-badge.css' => 'f25c3476', 'rsrc/css/phui/phui-big-info-view.css' => 'bd903741', - 'rsrc/css/phui/phui-box.css' => '6e8ac7fd', - 'rsrc/css/phui/phui-button.css' => 'd6ac72db', + 'rsrc/css/phui/phui-box.css' => 'dd1294d3', + 'rsrc/css/phui/phui-button.css' => 'd04a0eb7', 'rsrc/css/phui/phui-chart.css' => '6bf6f78e', 'rsrc/css/phui/phui-crumbs-view.css' => '79d536e5', 'rsrc/css/phui/phui-document-pro.css' => '8799acf7', @@ -143,7 +143,7 @@ return array( 'rsrc/css/phui/phui-info-view.css' => '6d7c3509', 'rsrc/css/phui/phui-list.css' => '9da2aa00', 'rsrc/css/phui/phui-object-box.css' => '407eaf5a', - 'rsrc/css/phui/phui-object-item-list-view.css' => 'be31c3a7', + 'rsrc/css/phui/phui-object-item-list-view.css' => '18b2ce8e', 'rsrc/css/phui/phui-pager.css' => 'bea33d23', 'rsrc/css/phui/phui-pinboard-view.css' => '2495140e', 'rsrc/css/phui/phui-profile-menu.css' => 'f709256c', @@ -155,9 +155,10 @@ return array( 'rsrc/css/phui/phui-tag-view.css' => '9d5d4400', 'rsrc/css/phui/phui-timeline-view.css' => '2efceff8', 'rsrc/css/phui/phui-two-column-view.css' => 'c75bfc5b', - 'rsrc/css/phui/workboards/phui-workboard.css' => 'e9e56029', + 'rsrc/css/phui/workboards/phui-workboard-color.css' => 'd30ae971', + 'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647', 'rsrc/css/phui/workboards/phui-workcard.css' => '3646fb96', - 'rsrc/css/phui/workboards/phui-workpanel.css' => 'a78c0661', + 'rsrc/css/phui/workboards/phui-workpanel.css' => '92197373', 'rsrc/css/sprite-login.css' => '60e8560e', 'rsrc/css/sprite-menu.css' => '9dd65b92', 'rsrc/css/sprite-tokens.css' => '4f399012', @@ -801,8 +802,8 @@ return array( 'phui-action-panel-css' => '91c7b835', 'phui-badge-view-css' => 'f25c3476', 'phui-big-info-view-css' => 'bd903741', - 'phui-box-css' => '6e8ac7fd', - 'phui-button-css' => 'd6ac72db', + 'phui-box-css' => 'dd1294d3', + 'phui-button-css' => 'd04a0eb7', 'phui-calendar-css' => 'ccabe893', 'phui-calendar-day-css' => 'd1cf6f93', 'phui-calendar-list-css' => 'c1c7f338', @@ -828,7 +829,7 @@ return array( 'phui-inline-comment-view-css' => '0fdb3667', 'phui-list-view-css' => '9da2aa00', 'phui-object-box-css' => '407eaf5a', - 'phui-object-item-list-view-css' => 'be31c3a7', + 'phui-object-item-list-view-css' => '18b2ce8e', 'phui-pager-css' => 'bea33d23', 'phui-pinboard-view-css' => '2495140e', 'phui-profile-menu-css' => 'f709256c', @@ -841,9 +842,10 @@ return array( 'phui-theme-css' => 'ab7b848c', 'phui-timeline-view-css' => '2efceff8', 'phui-two-column-view-css' => 'c75bfc5b', - 'phui-workboard-view-css' => 'e9e56029', + 'phui-workboard-color-css' => 'd30ae971', + 'phui-workboard-view-css' => 'e6d89647', 'phui-workcard-view-css' => '3646fb96', - 'phui-workpanel-view-css' => 'a78c0661', + 'phui-workpanel-view-css' => '92197373', 'phuix-action-list-view' => 'b5c256b8', 'phuix-action-view' => '8cf6d262', 'phuix-autocomplete' => '9196fb06', @@ -855,7 +857,7 @@ return array( 'policy-transaction-detail-css' => '82100a43', 'ponder-view-css' => '7b0df4da', 'project-card-view-css' => '9418c97d', - 'project-view-css' => '4693497c', + 'project-view-css' => '4c832c27', 'releeph-core' => '9b3c5733', 'releeph-preview-branch' => 'b7a6f4a5', 'releeph-request-differential-create-dialog' => '8d8b92cd', @@ -1573,6 +1575,9 @@ return array( 'phuix-icon-view', 'phabricator-prefab', ), + 92197373 => array( + 'phui-workcard-view-css', + ), '93d0c9e3' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1659,9 +1664,6 @@ return array( 'javelin-install', 'javelin-dom', ), - 'a78c0661' => array( - 'phui-workcard-view-css', - ), 'a80d0378' => array( 'javelin-behavior', 'javelin-stratcom', diff --git a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php index ed2a2189a6..9978c6198e 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php @@ -115,7 +115,7 @@ final class PhabricatorPeopleProfileViewController } $view = id(new PHUIBoxView()) - ->setColor(PHUIBoxView::GREY) + ->setBorder(true) ->appendChild($view) ->addClass('project-view-properties'); diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php index e9a5159295..0685391cbb 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php @@ -427,6 +427,11 @@ final class PhabricatorProjectBoardViewController $crumbs->addAction($manage_menu); $crumbs->addAction($fullscreen); + // TODO: Wire to Workboard Preferences + // require_celerity_resource('phui-workboard-color-css'); + // ->addClass('phui-workboard-color') + // ->addClass('phui-workboard-bluegrey') + return $this->newPage() ->setTitle( array( diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php index 9ca30c22ed..0a9bc602d2 100644 --- a/src/applications/project/controller/PhabricatorProjectProfileController.php +++ b/src/applications/project/controller/PhabricatorProjectProfileController.php @@ -142,7 +142,7 @@ final class PhabricatorProjectProfileController } $view = id(new PHUIBoxView()) - ->setColor(PHUIBoxView::GREY) + ->setBorder(true) ->appendChild($view) ->addClass('project-view-properties'); diff --git a/webroot/rsrc/css/application/project/project-view.css b/webroot/rsrc/css/application/project/project-view.css index afbeff3950..5573780672 100644 --- a/webroot/rsrc/css/application/project/project-view.css +++ b/webroot/rsrc/css/application/project/project-view.css @@ -3,8 +3,8 @@ */ .project-view-home { - background: #fff; padding-bottom: 64px; + background: #fff; } .project-view-header-tag { @@ -24,18 +24,25 @@ color: {$bluetext}; } -.phui-box.phui-box-grey.project-view-properties { - margin: 0 16px 0 16px; +.project-view-home .phui-box.project-view-properties { + margin: 0 16px 16px 16px; padding: 4px 12px; + border: 2px solid rgba(55,55,55,.1); + background-color: #F7F7F9; } -.device-phone .phui-box.phui-box-grey.project-view-properties { +.device-phone .phui-box.project-view-properties { margin: 0 12px 0 12px; } +.project-view-properties .phui-property-list-container + + .phui-property-list-text-content { + border-color: rgba(55,55,55,.15); +} + .project-view-properties .phui-property-list-key { width: auto; - margin-left: 4px; + margin-left: 4px2 } .project-view-properties .phui-property-list-section-header { @@ -44,8 +51,8 @@ } .project-view-feed .phui-object-box.phui-box-border { + padding: 0 16px 8px 16px; border: none; - padding: 8px; } .project-view-feed .phui-object-box .phui-header-shell { @@ -67,7 +74,7 @@ } .project-view-home .phui-box-grey .phui-header-shell { - padding: 8px 8px 8px 12px; + padding: 6px 6px 6px 12px; } .project-view-home .phui-box-grey .phui-header-header { @@ -75,11 +82,15 @@ } .project-view-home .phui-box-grey .phui-object-item-list-view { - padding: 8px; + padding: 4px 8px 0 8px; } .device-desktop .phui-two-column-view.project-view-badges .phui-side-column { - width: 364px; + width: 366px; +} + +.project-view-badges .phui-badge-flex-view { + background-color: #fff; } .project-view-home .phui-box-grey .phui-object-item-attribute .phui-icon-view { diff --git a/webroot/rsrc/css/phui/phui-box.css b/webroot/rsrc/css/phui/phui-box.css index 16be92ad48..ce95924c72 100644 --- a/webroot/rsrc/css/phui/phui-box.css +++ b/webroot/rsrc/css/phui/phui-box.css @@ -13,15 +13,15 @@ } .phui-box-grey { - background-color: {$page.background}; + background-color: #F7F7F9; border-radius: 3px; - border: none; + border-color: rgba(55,55,55,.1); } .phui-box-blue { background-color: {$bluebackground}; border-radius: 3px; - border: none; + border-color: {$thinblueborder}; } .phui-box-blue .phui-object-item, @@ -34,15 +34,23 @@ color: {$darkbluetext}; } -.phui-box-blue .phui-header-shell, +.phui-box-blue .phui-object-item-list-view, +.phui-box-grey .phui-object-item-list-view { + background-color: #fff; +} + +.phui-box-blue .phui-header-shell { + border-color: {$thinblueborder}; +} + .phui-box-grey .phui-header-shell { - border-bottom: 2px solid #fff; + border-color: rgba(55,55,55,.1); } .phui-object-box.phui-box-blue div.phui-info-severity-nodata, .phui-object-box.phui-box-grey div.phui-info-severity-nodata { background: transparent; - padding: 12px 4px; + padding: 20px 4px 24px; text-align: center; border: none; color: {$greytext}; diff --git a/webroot/rsrc/css/phui/phui-button.css b/webroot/rsrc/css/phui/phui-button.css index 958deb7f67..f4cf9ac6aa 100644 --- a/webroot/rsrc/css/phui/phui-button.css +++ b/webroot/rsrc/css/phui/phui-button.css @@ -59,8 +59,8 @@ button.grey, input[type="submit"].grey, a.grey, a.grey:visited { - background-color: rgba(71, 87, 120, 0.06); - border-color: rgba(71, 87, 120, 0.12); + background-color: #F7F7F9; + border: 1px solid rgba(55,55,55,.1); color: {$darkgreytext}; } @@ -106,8 +106,8 @@ button:hover { a.button.grey:hover, button.grey:hover { - background-color: rgba(71, 87, 120, 0.12); - border-color: rgba(71, 87, 120, 0.2); + background-color: rgba(71, 87, 120, 0.1); + border-color: rgba(71, 87, 120, 0.15); transition: 0.1s; } diff --git a/webroot/rsrc/css/phui/phui-object-item-list-view.css b/webroot/rsrc/css/phui/phui-object-item-list-view.css index 913975c6eb..17894f6f9d 100644 --- a/webroot/rsrc/css/phui/phui-object-item-list-view.css +++ b/webroot/rsrc/css/phui/phui-object-item-list-view.css @@ -29,7 +29,6 @@ ul.phui-object-item-list-view { } .phui-object-box .phui-object-item-list-view .phui-info-view { - margin: 4px 0; color: {$greytext}; border: none; } diff --git a/webroot/rsrc/css/phui/workboards/phui-workboard-color.css b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css new file mode 100644 index 0000000000..903080e68f --- /dev/null +++ b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css @@ -0,0 +1,88 @@ +/** + * @provides phui-workboard-color-css + */ + +.phui-workboard-color .phabricator-nav-content .phui-workboard-view-shadow { + background-color: transparent; +} + +.phui-workboard-color .phui-crumbs-view { + background-color: rgba(55,55,55,.15); + border: none; + color: rgba(255,255,255,.8); +} + +.phui-workboard-color .phui-crumbs-view a, +.phui-workboard-color .phui-crumbs-view .phui-icon-view { + color: rgba(255,255,255,.85); +} + +.phui-workboard-color .phui-crumbs-view a:hover, +.phui-workboard-color .phui-crumbs-view a:hover .phui-icon-view { + color: #fff; +} + +.phui-workboard-color .phuix-dropdown-menu { + background-color: rgba(255,255,255,.9); +} + +.phui-workboard-color .phui-workpanel-view .phui-box-grey { + background-color: rgba(255,255,255,.6); +} + +/* Colors */ + +.phui-workboard-red { + background-color: #e53935; + background-image: linear-gradient(to left, #e53935, #e35d5b); +} + +.phui-workboard-orange { + background-color: #f46b45; + background-image: linear-gradient(to left, #f46b45, #eea849); +} + +.phui-workboard-yellow { + background-color: #FF8008; + background-image: linear-gradient(to left, #FF8008, #FFC837); +} + +.phui-workboard-blue { + background-color: #73a5c3; + background-image: linear-gradient(90deg, #73a5c3 0, #6875be); +} + +.phui-workboard-bluegrey { + background-color: #517fa4; + background-image: linear-gradient(to left, #517fa4, #243949); +} + +.phui-workboard-green { + background-color: #2fa0ac; + background-image: linear-gradient(90deg, #2fa0ac 0, #58cca6); +} + +.phui-workboard-indigo { + background-color: #4776E6; + background-image: linear-gradient(to left, #4776E6, #8E54E9); +} + +.phui-workboard-violet { + background-color: #9f73c3; + background-image: linear-gradient(90deg, #9f73c3 0, #6875be); +} + +.phui-workboard-sky { + background-color: #7474BF; + background-image: linear-gradient(to left, #7474BF, #348AC7); +} + +.phui-workboard-pink { + background-color: #EA2A90; + background-image: linear-gradient(to left, #EA2A90, #79164B); +} + +.phui-workboard-grey { + background-color: #283048; + background-image: linear-gradient(to left, #283048, #859398); +} diff --git a/webroot/rsrc/css/phui/workboards/phui-workboard.css b/webroot/rsrc/css/phui/workboards/phui-workboard.css index 36913d1663..9ba75661d1 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workboard.css +++ b/webroot/rsrc/css/phui/workboards/phui-workboard.css @@ -18,8 +18,8 @@ bottom: 0; left: 0; right: 0; - background: #fff; padding: 16px; + background-color: #fff; } .device-desktop .page-has-warning .phui-workboard-view-shadow { diff --git a/webroot/rsrc/css/phui/workboards/phui-workpanel.css b/webroot/rsrc/css/phui/workboards/phui-workpanel.css index 77bddf9f0f..86c48bb7dd 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workpanel.css +++ b/webroot/rsrc/css/phui/workboards/phui-workpanel.css @@ -85,6 +85,7 @@ .phui-workpanel-body .phui-object-item-list-view { min-height: 54px; + background-color: transparent; } .device .aphront-multi-column-outer @@ -115,7 +116,7 @@ } .project-panel-empty .phui-object-item-list-view { - background: {$sh-indigobackground}; + background: rgba(234, 230, 247, 0.85); border-radius: 3px; margin-bottom: 4px; border: 1px dashed {$sh-indigoborder}; From 3fdaf229a70ffdd343dc264ad5f483544da2cf56 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 14 Feb 2016 12:29:28 -0800 Subject: [PATCH 05/42] Convert Create/Edit Column pages to dialogs Summary: Makes these pages a dialog endpoint, keeping you on the Workboard when possible. Test Plan: Create a Column, Edit a Column, visit hard page. Test letters in the points field. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15270 --- .../PhabricatorProjectBoardViewController.php | 2 +- ...PhabricatorProjectColumnEditController.php | 22 ++++++------------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php index 0685391cbb..e8de61e3cc 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php @@ -692,7 +692,7 @@ final class PhabricatorProjectBoardViewController ->setName(pht('Add Column')) ->setHref($add_uri) ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit); + ->setWorkflow(true); $manage_items[] = id(new PhabricatorActionView()) ->setIcon('fa-exchange') diff --git a/src/applications/project/controller/PhabricatorProjectColumnEditController.php b/src/applications/project/controller/PhabricatorProjectColumnEditController.php index 5ebc721c69..9f880b9515 100644 --- a/src/applications/project/controller/PhabricatorProjectColumnEditController.php +++ b/src/applications/project/controller/PhabricatorProjectColumnEditController.php @@ -136,21 +136,13 @@ final class PhabricatorProjectColumnEditController $submit = pht('Save Column'); } - $form->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue($submit) - ->addCancelButton($view_uri)); - - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) - ->setValidationException($validation_exception) - ->setForm($form); - - $nav = $this->getProfileMenu(); - - return $this->newPage() + return $this->newDialog() + ->setWidth(AphrontDialogView::WIDTH_FORM) ->setTitle($title) - ->setNavigation($nav) - ->appendChild($form_box); + ->appendForm($form) + ->setValidationException($validation_exception) + ->addCancelButton($view_uri) + ->addSubmitButton($submit); + } } From 76ea67819f215c313ccb8b8711322313836c1814 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 14 Feb 2016 16:12:24 -0800 Subject: [PATCH 06/42] Add base colors as workboard color choices Summary: Our base color pallette is good here as well. Updating CSS classes accordingly. We have 22 choices total now. Test Plan: Fake via dom inspector. {F1111310} Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15271 --- resources/celerity/map.php | 4 +- .../phui/workboards/phui-workboard-color.css | 86 ++++++++++++++----- 2 files changed, 68 insertions(+), 22 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index bd466223c7..3dd8a542b8 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -155,7 +155,7 @@ return array( 'rsrc/css/phui/phui-tag-view.css' => '9d5d4400', 'rsrc/css/phui/phui-timeline-view.css' => '2efceff8', 'rsrc/css/phui/phui-two-column-view.css' => 'c75bfc5b', - 'rsrc/css/phui/workboards/phui-workboard-color.css' => 'd30ae971', + 'rsrc/css/phui/workboards/phui-workboard-color.css' => '543432b9', 'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647', 'rsrc/css/phui/workboards/phui-workcard.css' => '3646fb96', 'rsrc/css/phui/workboards/phui-workpanel.css' => '92197373', @@ -842,7 +842,7 @@ return array( 'phui-theme-css' => 'ab7b848c', 'phui-timeline-view-css' => '2efceff8', 'phui-two-column-view-css' => 'c75bfc5b', - 'phui-workboard-color-css' => 'd30ae971', + 'phui-workboard-color-css' => '543432b9', 'phui-workboard-view-css' => 'e6d89647', 'phui-workcard-view-css' => '3646fb96', 'phui-workpanel-view-css' => '92197373', diff --git a/webroot/rsrc/css/phui/workboards/phui-workboard-color.css b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css index 903080e68f..280f79782c 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workboard-color.css +++ b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css @@ -30,59 +30,105 @@ background-color: rgba(255,255,255,.6); } -/* Colors */ +/* Gradients */ -.phui-workboard-red { +.phui-workboard-gradient-red { background-color: #e53935; background-image: linear-gradient(to left, #e53935, #e35d5b); } -.phui-workboard-orange { +.phui-workboard-gradient-orange { background-color: #f46b45; background-image: linear-gradient(to left, #f46b45, #eea849); } -.phui-workboard-yellow { +.phui-workboard-gradient-yellow { background-color: #FF8008; background-image: linear-gradient(to left, #FF8008, #FFC837); } -.phui-workboard-blue { - background-color: #73a5c3; - background-image: linear-gradient(90deg, #73a5c3 0, #6875be); -} - -.phui-workboard-bluegrey { - background-color: #517fa4; - background-image: linear-gradient(to left, #517fa4, #243949); -} - -.phui-workboard-green { +.phui-workboard-gradient-green { background-color: #2fa0ac; background-image: linear-gradient(90deg, #2fa0ac 0, #58cca6); } -.phui-workboard-indigo { +.phui-workboard-gradient-blue { + background-color: #73a5c3; + background-image: linear-gradient(90deg, #73a5c3 0, #6875be); +} + +.phui-workboard-gradient-bluegrey { + background-color: #517fa4; + background-image: linear-gradient(to left, #517fa4, #243949); +} + +.phui-workboard-gradient-indigo { background-color: #4776E6; background-image: linear-gradient(to left, #4776E6, #8E54E9); } -.phui-workboard-violet { +.phui-workboard-gradient-violet { background-color: #9f73c3; background-image: linear-gradient(90deg, #9f73c3 0, #6875be); } -.phui-workboard-sky { +.phui-workboard-gradient-sky { background-color: #7474BF; background-image: linear-gradient(to left, #7474BF, #348AC7); } -.phui-workboard-pink { +.phui-workboard-gradient-pink { background-color: #EA2A90; background-image: linear-gradient(to left, #EA2A90, #79164B); } -.phui-workboard-grey { +.phui-workboard-gradient-grey { background-color: #283048; background-image: linear-gradient(to left, #283048, #859398); } + +/* Colors */ + +.phui-workboard-red { + background-color: {$red}; +} + +.phui-workboard-orange { + background-color: {$orange}; +} + +.phui-workboard-yellow { + background-color: {$yellow}; +} + +.phui-workboard-green { + background-color: {$green}; +} + +.phui-workboard-blue { + background-color: {$blue}; +} + +.phui-workboard-indigo { + background-color: {$indigo}; +} + +.phui-workboard-violet { + background-color: {$violet}; +} + +.phui-workboard-sky { + background-color: {$sky}; +} + +.phui-workboard-pink { + background-color: {$pink}; +} + +.phui-workboard-fire { + background-color: {$fire}; +} + +.phui-workboard-grey { + background-color: {$greytext}; +} From f1f8ee8e6ad5f094fd1a4f65e0e6cc194ca21c09 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 15 Feb 2016 07:20:29 -0800 Subject: [PATCH 07/42] Improve subproject/milestone error handling for users who can't create projects Summary: Fixes T10357. - Show a better (more descriptive) error message when a user who can't create projects tries to create a subproject or milestone. - Disable the subproject actions if you don't have create permission. All this stuff was already enforced properly: this diff doesn't make any actual policy changes, just improves the UI for users who lack permission. Test Plan: - As an unprivileged user (no "Can Create Projects"), tried to create a subproject or milestone. - After patch, got a disabled action, with more specific and helpful error than before. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10357 Differential Revision: https://secure.phabricator.com/D15274 --- .../controller/PhabricatorProjectEditController.php | 5 +++++ .../controller/PhabricatorProjectSubprojectsController.php | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/applications/project/controller/PhabricatorProjectEditController.php b/src/applications/project/controller/PhabricatorProjectEditController.php index 5091135bec..7c041af93a 100644 --- a/src/applications/project/controller/PhabricatorProjectEditController.php +++ b/src/applications/project/controller/PhabricatorProjectEditController.php @@ -24,6 +24,11 @@ final class PhabricatorProjectEditController $id = $request->getURIData('id'); if (!$id) { + // This capability is checked again later, but checking it here + // explicitly gives us a better error message. + $this->requireApplicationCapability( + ProjectCreateProjectsCapability::CAPABILITY); + $parent_id = head($request->getArr('parent')); if (!$parent_id) { $parent_id = $request->getStr('parent'); diff --git a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php index 36f9d641a8..eb32d00b92 100644 --- a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php +++ b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php @@ -181,6 +181,9 @@ final class PhabricatorProjectSubprojectsController $viewer = $this->getViewer(); $id = $project->getID(); + $can_create = $this->hasApplicationCapability( + ProjectCreateProjectsCapability::CAPABILITY); + $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $project, @@ -198,7 +201,7 @@ final class PhabricatorProjectSubprojectsController $milestone_text = pht('Create Milestone'); } - $can_milestone = ($can_edit && $allows_milestones); + $can_milestone = ($can_create && $can_edit && $allows_milestones); $milestone_href = "/project/edit/?milestone={$id}"; $view->addAction( @@ -209,7 +212,7 @@ final class PhabricatorProjectSubprojectsController ->setDisabled(!$can_milestone) ->setWorkflow(!$can_milestone)); - $can_subproject = ($can_edit && $allows_subprojects); + $can_subproject = ($can_create && $can_edit && $allows_subprojects); // If we're offering to create the first subproject, we're going to warn // the user about the effects before moving forward. From 71ee97d74f735eb0769acf4f5726fbe9d72c9d39 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 15 Feb 2016 11:37:13 -0800 Subject: [PATCH 08/42] Give Owners real view and edit policies Summary: Fixes T10360. In modern code, most of the meat is automatic. Test Plan: - Edited view policy and edit policy from web UI. - Viewed package, saw policy badge in header. - Tried to edit a package as a user without permission, got appropriate disabled states and errors. - Changed policies via Conduit. - Tried to view a package as a user without permission. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10360 Differential Revision: https://secure.phabricator.com/D15275 --- .../autopatches/20160215.owners.policy.1.sql | 2 ++ .../autopatches/20160215.owners.policy.2.sql | 2 ++ .../autopatches/20160215.owners.policy.3.sql | 2 ++ .../autopatches/20160215.owners.policy.4.sql | 2 ++ src/__phutil_library_map__.php | 4 ++++ .../PhabricatorOwnersApplication.php | 15 +++++++++++++ ...PhabricatorOwnersDefaultEditCapability.php | 12 ++++++++++ ...PhabricatorOwnersDefaultViewCapability.php | 16 ++++++++++++++ .../PhabricatorOwnersDetailController.php | 6 +++-- .../PhabricatorOwnersPathsController.php | 3 +-- ...bricatorOwnersPackageTransactionEditor.php | 3 +++ .../storage/PhabricatorOwnersPackage.php | 22 +++++++++++++++++-- 12 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 resources/sql/autopatches/20160215.owners.policy.1.sql create mode 100644 resources/sql/autopatches/20160215.owners.policy.2.sql create mode 100644 resources/sql/autopatches/20160215.owners.policy.3.sql create mode 100644 resources/sql/autopatches/20160215.owners.policy.4.sql create mode 100644 src/applications/owners/capability/PhabricatorOwnersDefaultEditCapability.php create mode 100644 src/applications/owners/capability/PhabricatorOwnersDefaultViewCapability.php diff --git a/resources/sql/autopatches/20160215.owners.policy.1.sql b/resources/sql/autopatches/20160215.owners.policy.1.sql new file mode 100644 index 0000000000..ae63906781 --- /dev/null +++ b/resources/sql/autopatches/20160215.owners.policy.1.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_owners.owners_package + ADD viewPolicy VARBINARY(64) NOT NULL; diff --git a/resources/sql/autopatches/20160215.owners.policy.2.sql b/resources/sql/autopatches/20160215.owners.policy.2.sql new file mode 100644 index 0000000000..f55b61a9ff --- /dev/null +++ b/resources/sql/autopatches/20160215.owners.policy.2.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_owners.owners_package + ADD editPolicy VARBINARY(64) NOT NULL; diff --git a/resources/sql/autopatches/20160215.owners.policy.3.sql b/resources/sql/autopatches/20160215.owners.policy.3.sql new file mode 100644 index 0000000000..9d3ae9f112 --- /dev/null +++ b/resources/sql/autopatches/20160215.owners.policy.3.sql @@ -0,0 +1,2 @@ +UPDATE {$NAMESPACE}_owners.owners_package + SET viewPolicy = 'users' WHERE viewPolicy = ''; diff --git a/resources/sql/autopatches/20160215.owners.policy.4.sql b/resources/sql/autopatches/20160215.owners.policy.4.sql new file mode 100644 index 0000000000..e108a6da9c --- /dev/null +++ b/resources/sql/autopatches/20160215.owners.policy.4.sql @@ -0,0 +1,2 @@ +UPDATE {$NAMESPACE}_owners.owners_package + SET editPolicy = 'users' WHERE editPolicy = ''; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 45cfd6fe6a..0a79cfeef1 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2665,6 +2665,8 @@ phutil_register_library_map(array( 'PhabricatorOwnersCustomFieldStorage' => 'applications/owners/storage/PhabricatorOwnersCustomFieldStorage.php', 'PhabricatorOwnersCustomFieldStringIndex' => 'applications/owners/storage/PhabricatorOwnersCustomFieldStringIndex.php', 'PhabricatorOwnersDAO' => 'applications/owners/storage/PhabricatorOwnersDAO.php', + 'PhabricatorOwnersDefaultEditCapability' => 'applications/owners/capability/PhabricatorOwnersDefaultEditCapability.php', + 'PhabricatorOwnersDefaultViewCapability' => 'applications/owners/capability/PhabricatorOwnersDefaultViewCapability.php', 'PhabricatorOwnersDetailController' => 'applications/owners/controller/PhabricatorOwnersDetailController.php', 'PhabricatorOwnersEditController' => 'applications/owners/controller/PhabricatorOwnersEditController.php', 'PhabricatorOwnersListController' => 'applications/owners/controller/PhabricatorOwnersListController.php', @@ -7036,6 +7038,8 @@ phutil_register_library_map(array( 'PhabricatorOwnersCustomFieldStorage' => 'PhabricatorCustomFieldStorage', 'PhabricatorOwnersCustomFieldStringIndex' => 'PhabricatorCustomFieldStringIndexStorage', 'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO', + 'PhabricatorOwnersDefaultEditCapability' => 'PhabricatorPolicyCapability', + 'PhabricatorOwnersDefaultViewCapability' => 'PhabricatorPolicyCapability', 'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController', 'PhabricatorOwnersEditController' => 'PhabricatorOwnersController', 'PhabricatorOwnersListController' => 'PhabricatorOwnersController', diff --git a/src/applications/owners/application/PhabricatorOwnersApplication.php b/src/applications/owners/application/PhabricatorOwnersApplication.php index 574dd6ff7e..4b4841390a 100644 --- a/src/applications/owners/application/PhabricatorOwnersApplication.php +++ b/src/applications/owners/application/PhabricatorOwnersApplication.php @@ -54,4 +54,19 @@ final class PhabricatorOwnersApplication extends PhabricatorApplication { ); } + protected function getCustomCapabilities() { + return array( + PhabricatorOwnersDefaultViewCapability::CAPABILITY => array( + 'caption' => pht('Default view policy for newly created packages.'), + 'template' => PhabricatorOwnersPackagePHIDType::TYPECONST, + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, + ), + PhabricatorOwnersDefaultEditCapability::CAPABILITY => array( + 'caption' => pht('Default edit policy for newly created packages.'), + 'template' => PhabricatorOwnersPackagePHIDType::TYPECONST, + 'capability' => PhabricatorPolicyCapability::CAN_EDIT, + ), + ); + } + } diff --git a/src/applications/owners/capability/PhabricatorOwnersDefaultEditCapability.php b/src/applications/owners/capability/PhabricatorOwnersDefaultEditCapability.php new file mode 100644 index 0000000000..9b89bfb83a --- /dev/null +++ b/src/applications/owners/capability/PhabricatorOwnersDefaultEditCapability.php @@ -0,0 +1,12 @@ +getViewer(); - // TODO: Implement this capability. - $can_edit = true; + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $package, + PhabricatorPolicyCapability::CAN_EDIT); $id = $package->getID(); $edit_uri = $this->getApplicationURI("/edit/{$id}/"); diff --git a/src/applications/owners/controller/PhabricatorOwnersPathsController.php b/src/applications/owners/controller/PhabricatorOwnersPathsController.php index b02f5437be..ccca55b6c5 100644 --- a/src/applications/owners/controller/PhabricatorOwnersPathsController.php +++ b/src/applications/owners/controller/PhabricatorOwnersPathsController.php @@ -12,8 +12,7 @@ final class PhabricatorOwnersPathsController ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, - // TODO: Support this capability. - // PhabricatorPolicyCapability::CAN_EDIT, + PhabricatorPolicyCapability::CAN_EDIT, )) ->needPaths(true) ->executeOne(); diff --git a/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php b/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php index f2cfc5151a..aa5ae1c6f3 100644 --- a/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php +++ b/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php @@ -21,6 +21,9 @@ final class PhabricatorOwnersPackageTransactionEditor $types[] = PhabricatorOwnersPackageTransaction::TYPE_PATHS; $types[] = PhabricatorOwnersPackageTransaction::TYPE_STATUS; + $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; + $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; + return $types; } diff --git a/src/applications/owners/storage/PhabricatorOwnersPackage.php b/src/applications/owners/storage/PhabricatorOwnersPackage.php index 23d540375d..e007b18c00 100644 --- a/src/applications/owners/storage/PhabricatorOwnersPackage.php +++ b/src/applications/owners/storage/PhabricatorOwnersPackage.php @@ -18,6 +18,8 @@ final class PhabricatorOwnersPackage protected $primaryOwnerPHID; protected $mailKey; protected $status; + protected $viewPolicy; + protected $editPolicy; private $paths = self::ATTACHABLE; private $owners = self::ATTACHABLE; @@ -27,8 +29,20 @@ final class PhabricatorOwnersPackage const STATUS_ARCHIVED = 'archived'; public static function initializeNewPackage(PhabricatorUser $actor) { + $app = id(new PhabricatorApplicationQuery()) + ->setViewer($actor) + ->withClasses(array('PhabricatorOwnersApplication')) + ->executeOne(); + + $view_policy = $app->getPolicy( + PhabricatorOwnersDefaultViewCapability::CAPABILITY); + $edit_policy = $app->getPolicy( + PhabricatorOwnersDefaultEditCapability::CAPABILITY); + return id(new PhabricatorOwnersPackage()) ->setAuditingEnabled(0) + ->setViewPolicy($view_policy) + ->setEditPolicy($edit_policy) ->attachPaths(array()) ->setStatus(self::STATUS_ACTIVE) ->attachOwners(array()) @@ -287,8 +301,12 @@ final class PhabricatorOwnersPackage } public function getPolicy($capability) { - // TODO: Implement proper policies. - return PhabricatorPolicies::POLICY_USER; + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return $this->getViewPolicy(); + case PhabricatorPolicyCapability::CAN_EDIT: + return $this->getEditPolicy(); + } } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { From 14ce10c0c241438c4b3912e074dde8f9947601da Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 15 Feb 2016 21:32:35 +0000 Subject: [PATCH 09/42] Clean up list styles in Remarkup Summary: Fixes T8643, makes lists with and without checkboxes align and display better. Test Plan: Lists with and without checkboxes. {F1111426} Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T8643 Differential Revision: https://secure.phabricator.com/D15272 --- resources/celerity/map.php | 6 +++--- webroot/rsrc/css/core/remarkup.css | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 3dd8a542b8..086954ce50 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ */ return array( 'names' => array( - 'core.pkg.css' => '394a6788', + 'core.pkg.css' => '1972333f', 'core.pkg.js' => 'd7daa6d8', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '2de124c9', @@ -104,7 +104,7 @@ return array( 'rsrc/css/application/tokens/tokens.css' => '3d0f239e', 'rsrc/css/application/uiexample/example.css' => '528b19de', 'rsrc/css/core/core.css' => '5b3563c8', - 'rsrc/css/core/remarkup.css' => 'e1c8b32f', + 'rsrc/css/core/remarkup.css' => '2c913a06', 'rsrc/css/core/syntax.css' => '9fd11da8', 'rsrc/css/core/z-index.css' => '5b6fcf3f', 'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa', @@ -768,7 +768,7 @@ return array( 'phabricator-object-selector-css' => '85ee8ce6', 'phabricator-phtize' => 'd254d646', 'phabricator-prefab' => 'e67df814', - 'phabricator-remarkup-css' => 'e1c8b32f', + 'phabricator-remarkup-css' => '2c913a06', 'phabricator-search-results-css' => '7dea472c', 'phabricator-shaped-request' => '7cbe244b', 'phabricator-side-menu-view-css' => '3a3d9f41', diff --git a/webroot/rsrc/css/core/remarkup.css b/webroot/rsrc/css/core/remarkup.css index 3b9e51641f..511d646f8e 100644 --- a/webroot/rsrc/css/core/remarkup.css +++ b/webroot/rsrc/css/core/remarkup.css @@ -97,17 +97,19 @@ list-style: lower-roman; } -.phabricator-remarkup ul.remarkup-list-with-checkmarks { +.phabricator-remarkup .remarkup-list-with-checkmarks .remarkup-checked-item, +.phabricator-remarkup .remarkup-list-with-checkmarks .remarkup-unchecked-item { list-style: none; - margin-left: 18px; + margin-left: -18px; } .phabricator-remarkup .remarkup-list-with-checkmarks input { margin-right: 2px; + opacity: 1; } .phabricator-remarkup .remarkup-list-with-checkmarks .remarkup-checked-item { - color: {$greytext}; + text-decoration: line-through; } .phabricator-remarkup ul.remarkup-list ol.remarkup-list, From 53b963efdb41afd8e905d78055bdce5ac4eeb973 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 15 Feb 2016 15:12:32 -0800 Subject: [PATCH 10/42] Process slowvote description as remarkup (link images, activate mentions, etc) Summary: Fixes T10361. Test Plan: - Created a poll with an embedded file and a mention of a task. - Verified file was attached properly. - Verified mention appeared on task. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10361 Differential Revision: https://secure.phabricator.com/D15277 --- .../storage/PhabricatorSlowvoteTransaction.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/applications/slowvote/storage/PhabricatorSlowvoteTransaction.php b/src/applications/slowvote/storage/PhabricatorSlowvoteTransaction.php index e45fdb013f..fb1b7fc2f3 100644 --- a/src/applications/slowvote/storage/PhabricatorSlowvoteTransaction.php +++ b/src/applications/slowvote/storage/PhabricatorSlowvoteTransaction.php @@ -97,7 +97,20 @@ final class PhabricatorSlowvoteTransaction return parent::getTitle(); } - public function getTitleForFeed() { + public function getRemarkupBlocks() { + $blocks = parent::getRemarkupBlocks(); + + $type = $this->getTransactionType(); + switch ($type) { + case self::TYPE_DESCRIPTION: + $blocks[] = $this->getNewValue(); + break; + } + + return $blocks; + } + + public function getTitleForFeed() { $author_phid = $this->getAuthorPHID(); $object_phid = $this->getObjectPHID(); From e4690a385447a470bc78089daed9da048ef8a824 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 15 Feb 2016 14:43:20 -0800 Subject: [PATCH 11/42] Fix an issue where newly created tasks could appear at the bottom of columns Summary: Ref T10349. At HEAD, if you create a task //on a board//, it floats to the top correctly. If you create a task elsewhere and tag it with the board, you were subject to the whims of the layout engine and it would generally end up on the bottom. Instead, make the rules consistent so that "virtual" positions (of tasks which haven't been committed to a particular position yet) still float to the top. Test Plan: - Created tasks from a board. - Created tasks from Maniphest, then looked at them on a board. - Moved tasks around. - In all cases, newly created tasks floated to the top. - Sorted by natural and priority. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10349 Differential Revision: https://secure.phabricator.com/D15276 --- .../editor/ManiphestTransactionEditor.php | 10 +++++-- .../PhabricatorProjectBoardViewController.php | 6 +++- .../engine/PhabricatorBoardLayoutEngine.php | 7 +++-- .../PhabricatorProjectColumnPosition.php | 29 ++++++++++++++----- 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/applications/maniphest/editor/ManiphestTransactionEditor.php b/src/applications/maniphest/editor/ManiphestTransactionEditor.php index 08cfc66632..97c567c30d 100644 --- a/src/applications/maniphest/editor/ManiphestTransactionEditor.php +++ b/src/applications/maniphest/editor/ManiphestTransactionEditor.php @@ -289,8 +289,14 @@ final class ManiphestTransactionEditor array($select_phids)) ->execute(); - $object_phids = mpull($board_tasks, 'getPHID'); - $object_phids[] = $object_phid; + $board_tasks = mpull($board_tasks, null, 'getPHID'); + $board_tasks[$object_phid] = $object; + + // Make sure tasks are sorted by ID, so we lay out new positions in + // a consistent way. + $board_tasks = msort($board_tasks, 'getID'); + + $object_phids = array_keys($board_tasks); $engine = id(new PhabricatorBoardLayoutEngine()) ->setViewer($omnipotent_viewer) diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php index e8de61e3cc..963cb2a752 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php @@ -124,10 +124,14 @@ final class PhabricatorProjectBoardViewController $board_phid = $project->getPHID(); + // Regardless of display order, pass tasks to the layout engine in ID order + // so layout is consistent. + $board_tasks = msort($tasks, 'getID'); + $layout_engine = id(new PhabricatorBoardLayoutEngine()) ->setViewer($viewer) ->setBoardPHIDs(array($board_phid)) - ->setObjectPHIDs(array_keys($tasks)) + ->setObjectPHIDs(array_keys($board_tasks)) ->setFetchAllBoards(true) ->executeLayout(); diff --git a/src/applications/project/engine/PhabricatorBoardLayoutEngine.php b/src/applications/project/engine/PhabricatorBoardLayoutEngine.php index 3525af5ff0..248677fbec 100644 --- a/src/applications/project/engine/PhabricatorBoardLayoutEngine.php +++ b/src/applications/project/engine/PhabricatorBoardLayoutEngine.php @@ -506,6 +506,7 @@ final class PhabricatorBoardLayoutEngine extends Phobject { } } + $view_sequence = 1; foreach ($object_phids as $object_phid) { $positions = idx($position_groups, $object_phid, array()); @@ -554,7 +555,8 @@ final class PhabricatorBoardLayoutEngine extends Phobject { ->setBoardPHID($board_phid) ->setColumnPHID($proxy_hit) ->setObjectPHID($object_phid) - ->setSequence(0); + ->setSequence(0) + ->setViewSequence($view_sequence++); $this->addQueue[] = $new_position; @@ -578,7 +580,8 @@ final class PhabricatorBoardLayoutEngine extends Phobject { ->setBoardPHID($board_phid) ->setColumnPHID($default_phid) ->setObjectPHID($object_phid) - ->setSequence(0); + ->setSequence(0) + ->setViewSequence($view_sequence++); $this->addQueue[] = $new_position; diff --git a/src/applications/project/storage/PhabricatorProjectColumnPosition.php b/src/applications/project/storage/PhabricatorProjectColumnPosition.php index c59676f8e4..40929606ac 100644 --- a/src/applications/project/storage/PhabricatorProjectColumnPosition.php +++ b/src/applications/project/storage/PhabricatorProjectColumnPosition.php @@ -9,6 +9,7 @@ final class PhabricatorProjectColumnPosition extends PhabricatorProjectDAO protected $sequence; private $column = self::ATTACHABLE; + private $viewSequence = 0; protected function getConfiguration() { return array( @@ -40,18 +41,30 @@ final class PhabricatorProjectColumnPosition extends PhabricatorProjectDAO return $this; } - public function getOrderingKey() { - if (!$this->getID() && !$this->getSequence()) { - return 0; - } + public function setViewSequence($view_sequence) { + $this->viewSequence = $view_sequence; + return $this; + } - // Low sequence numbers go above high sequence numbers. - // High position IDs go above low position IDs. - // Broadly, this makes newly added stuff float to the top. + public function getOrderingKey() { + // We're ordering both real positions and "virtual" positions which we have + // created but not saved yet. + + // Low sequence numbers go above high sequence numbers. Virtual positions + // will have sequence number 0. + + // High virtual sequence numbers go above low virtual sequence numbers. + // The layout engine gets objects in ID order, and this puts them in + // reverse ID order. + + // High IDs go above low IDs. + + // Broadly, this collectively makes newly added stuff float to the top. return sprintf( - '~%012d%012d', + '~%012d%012d%012d', $this->getSequence(), + ((1 << 31) - $this->viewSequence), ((1 << 31) - $this->getID())); } From 5a44c85b6ba24e2c3681626e8b6b0aed9861f8f0 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 Feb 2016 06:24:15 -0800 Subject: [PATCH 12/42] Move uncommon workboard management options to "Manage Board" view Summary: This gives us room for less-common workboard management options like "Disable Board" without overloading the menus on the main board. Particularly, we can add background color options here without anything getting weird. I've left "Add Column" on the main UI since I think it's common enough to leave there. We could probably move "Hide Column" to this UI in the future, though. Test Plan: {F1114475} Reviewers: chad Reviewed By: chad Differential Revision: https://secure.phabricator.com/D15278 --- src/__phutil_library_map__.php | 2 + .../PhabricatorProjectApplication.php | 2 + ...habricatorProjectBoardManageController.php | 155 ++++++++++++++++++ ...abricatorProjectBoardReorderController.php | 4 +- .../PhabricatorProjectBoardViewController.php | 26 +-- 5 files changed, 165 insertions(+), 24 deletions(-) create mode 100644 src/applications/project/controller/PhabricatorProjectBoardManageController.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 0a79cfeef1..c9c52a9d8a 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2872,6 +2872,7 @@ phutil_register_library_map(array( 'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php', 'PhabricatorProjectBoardDisableController' => 'applications/project/controller/PhabricatorProjectBoardDisableController.php', 'PhabricatorProjectBoardImportController' => 'applications/project/controller/PhabricatorProjectBoardImportController.php', + 'PhabricatorProjectBoardManageController' => 'applications/project/controller/PhabricatorProjectBoardManageController.php', 'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php', 'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php', 'PhabricatorProjectCardView' => 'applications/project/view/PhabricatorProjectCardView.php', @@ -7300,6 +7301,7 @@ phutil_register_library_map(array( 'PhabricatorProjectBoardController' => 'PhabricatorProjectController', 'PhabricatorProjectBoardDisableController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardImportController' => 'PhabricatorProjectBoardController', + 'PhabricatorProjectBoardManageController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectCardView' => 'AphrontTagView', diff --git a/src/applications/project/application/PhabricatorProjectApplication.php b/src/applications/project/application/PhabricatorProjectApplication.php index 3a39aefa22..86f050d281 100644 --- a/src/applications/project/application/PhabricatorProjectApplication.php +++ b/src/applications/project/application/PhabricatorProjectApplication.php @@ -84,6 +84,8 @@ final class PhabricatorProjectApplication extends PhabricatorApplication { => 'PhabricatorProjectBoardReorderController', 'disable/' => 'PhabricatorProjectBoardDisableController', + 'manage/' + => 'PhabricatorProjectBoardManageController', ), 'update/(?P[1-9]\d*)/(?P[^/]+)/' => 'PhabricatorProjectUpdateController', diff --git a/src/applications/project/controller/PhabricatorProjectBoardManageController.php b/src/applications/project/controller/PhabricatorProjectBoardManageController.php new file mode 100644 index 0000000000..b21ea0cf46 --- /dev/null +++ b/src/applications/project/controller/PhabricatorProjectBoardManageController.php @@ -0,0 +1,155 @@ +getViewer(); + $board_id = $request->getURIData('projectID'); + + $board = id(new PhabricatorProjectQuery()) + ->setViewer($viewer) + ->withIDs(array($board_id)) + ->needImages(true) + ->executeOne(); + if (!$board) { + return new Aphront404Response(); + } + $this->setProject($board); + + // Perform layout of no tasks to load and populate the columns in the + // correct order. + $layout_engine = id(new PhabricatorBoardLayoutEngine()) + ->setViewer($viewer) + ->setBoardPHIDs(array($board->getPHID())) + ->setObjectPHIDs(array()) + ->setFetchAllBoards(true) + ->executeLayout(); + + $columns = $layout_engine->getColumns($board->getPHID()); + + $board_id = $board->getID(); + + $header = $this->buildHeaderView($board); + $actions = $this->buildActionView($board); + $properties = $this->buildPropertyView($board); + + $properties->setActionList($actions); + + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addTextCrumb(pht('Workboard'), "/project/board/{$board_id}/"); + $crumbs->addTextCrumb(pht('Manage')); + + $box = id(new PHUIObjectBoxView()) + ->setHeader($header) + ->addPropertyList($properties); + + $nav = $this->getProfileMenu(); + + $title = array( + pht('Manage Workboard'), + $board->getDisplayName(), + ); + + $columns_list = $this->buildColumnsList($board, $columns); + + return $this->newPage() + ->setTitle($title) + ->setNavigation($nav) + ->setCrumbs($crumbs) + ->appendChild( + array( + $box, + $columns_list, + )); + } + + private function buildHeaderView(PhabricatorProject $board) { + $viewer = $this->getRequest()->getUser(); + + $header = id(new PHUIHeaderView()) + ->setUser($viewer) + ->setHeader(pht('Workboard: %s', $board->getDisplayName())); + + return $header; + } + + private function buildActionView(PhabricatorProject $board) { + $viewer = $this->getRequest()->getUser(); + $id = $board->getID(); + + $actions = id(new PhabricatorActionListView()) + ->setUser($viewer); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $board, + PhabricatorPolicyCapability::CAN_EDIT); + + $reorder_uri = $this->getApplicationURI("board/{$id}/reorder/"); + + $actions->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-exchange') + ->setName(pht('Reorder Columns')) + ->setHref($reorder_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(true)); + + $disable_uri = $this->getApplicationURI("board/{$id}/disable/"); + + $actions->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-ban') + ->setName(pht('Disable Board')) + ->setHref($disable_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(true)); + + return $actions; + } + + private function buildPropertyView( + PhabricatorProject $board) { + $viewer = $this->getRequest()->getUser(); + + $properties = id(new PHUIPropertyListView()) + ->setUser($viewer) + ->setObject($board); + + return $properties; + } + + private function buildColumnsList( + PhabricatorProject $board, + array $columns) { + assert_instances_of($columns, 'PhabricatorProjectColumn'); + + $board_id = $board->getID(); + + $view = id(new PHUIObjectItemListView()) + ->setNoDataString(pht('This board has no columns.')); + + foreach ($columns as $column) { + $column_id = $column->getID(); + + $detail_uri = "/project/board/{$board_id}/column/{$column_id}/"; + + $item = id(new PHUIObjectItemView()) + ->setHeader($column->getDisplayName()) + ->setHref($detail_uri); + + if ($column->isHidden()) { + $item->setDisabled(true); + } + + $view->addItem($item); + } + + return id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Columns')) + ->setObjectList($view); + } + + +} diff --git a/src/applications/project/controller/PhabricatorProjectBoardReorderController.php b/src/applications/project/controller/PhabricatorProjectBoardReorderController.php index 011bbd069a..05f1dd2d43 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardReorderController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardReorderController.php @@ -23,13 +23,13 @@ final class PhabricatorProjectBoardReorderController $this->setProject($project); $project_id = $project->getID(); - $board_uri = $this->getApplicationURI("board/{$project_id}/"); + $manage_uri = $this->getApplicationURI("board/{$project_id}/manage/"); $reorder_uri = $this->getApplicationURI("board/{$project_id}/reorder/"); if ($request->isFormPost()) { // User clicked "Done", make sure the page reloads to show the new // column order. - return id(new AphrontRedirectResponse())->setURI($board_uri); + return id(new AphrontRedirectResponse())->setURI($manage_uri); } $columns = id(new PhabricatorProjectColumnQuery()) diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php index 963cb2a752..8bb58779dc 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php @@ -680,9 +680,8 @@ final class PhabricatorProjectBoardViewController $id = $project->getID(); - $disable_uri = $this->getApplicationURI("board/{$id}/disable/"); + $manage_uri = $this->getApplicationURI("board/{$id}/manage/"); $add_uri = $this->getApplicationURI("board/{$id}/edit/"); - $reorder_uri = $this->getApplicationURI("board/{$id}/reorder/"); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, @@ -699,11 +698,9 @@ final class PhabricatorProjectBoardViewController ->setWorkflow(true); $manage_items[] = id(new PhabricatorActionView()) - ->setIcon('fa-exchange') - ->setName(pht('Reorder Columns')) - ->setHref($reorder_uri) - ->setDisabled(!$can_edit) - ->setWorkflow(true); + ->setIcon('fa-pencil') + ->setName(pht('Manage Board')) + ->setHref($manage_uri); if ($show_hidden) { $hidden_uri = $this->getURIWithState() @@ -735,13 +732,6 @@ final class PhabricatorProjectBoardViewController ->setHref($batch_edit_uri) ->setDisabled(!$can_batch_edit); - $manage_items[] = id(new PhabricatorActionView()) - ->setIcon('fa-ban') - ->setName(pht('Disable Workboard')) - ->setHref($disable_uri) - ->setWorkflow(true) - ->setDisabled(!$can_edit); - $manage_menu = id(new PhabricatorActionListView()) ->setUser($viewer); foreach ($manage_items as $item) { @@ -826,14 +816,6 @@ final class PhabricatorProjectBoardViewController ->setHref($batch_edit_uri) ->setDisabled(!$can_batch_edit); - $detail_uri = $this->getApplicationURI( - 'board/'.$this->id.'/column/'.$column->getID().'/'); - - $column_items[] = id(new PhabricatorActionView()) - ->setIcon('fa-columns') - ->setName(pht('Column Details')) - ->setHref($detail_uri); - $can_hide = ($can_edit && !$column->isDefaultColumn()); $hide_uri = 'board/'.$this->id.'/hide/'.$column->getID().'/'; $hide_uri = $this->getApplicationURI($hide_uri); From 483d90fac19200e1f6f058daa12a72dd81651d0e Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 Feb 2016 06:52:32 -0800 Subject: [PATCH 13/42] Allow workboard background colors to be configured Summary: Adds a UI for selecting background colors. You can choose "Use Parent", which is the default, and allows you to set a color that all descendants inherit. You can also choose "None", if a parent has a WHACKY BACKGROUND that you refuse to put up with. Test Plan: {F1114588} {F1114589} Reviewers: chad Reviewed By: chad Differential Revision: https://secure.phabricator.com/D15279 --- resources/celerity/map.php | 4 +- src/__phutil_library_map__.php | 4 + .../PhabricatorProjectApplication.php | 2 + ...ricatorProjectWorkboardBackgroundColor.php | 124 +++++++++++++ ...icatorProjectBoardBackgroundController.php | 168 ++++++++++++++++++ ...habricatorProjectBoardManageController.php | 19 ++ .../PhabricatorProjectBoardViewController.php | 18 +- .../PhabricatorProjectTransactionEditor.php | 13 ++ .../project/storage/PhabricatorProject.php | 25 +++ .../storage/PhabricatorProjectTransaction.php | 8 + .../phui/workboards/phui-workboard-color.css | 6 + 11 files changed, 383 insertions(+), 8 deletions(-) create mode 100644 src/applications/project/constants/PhabricatorProjectWorkboardBackgroundColor.php create mode 100644 src/applications/project/controller/PhabricatorProjectBoardBackgroundController.php diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 086954ce50..856da367e1 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -155,7 +155,7 @@ return array( 'rsrc/css/phui/phui-tag-view.css' => '9d5d4400', 'rsrc/css/phui/phui-timeline-view.css' => '2efceff8', 'rsrc/css/phui/phui-two-column-view.css' => 'c75bfc5b', - 'rsrc/css/phui/workboards/phui-workboard-color.css' => '543432b9', + 'rsrc/css/phui/workboards/phui-workboard-color.css' => '2f068cc8', 'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647', 'rsrc/css/phui/workboards/phui-workcard.css' => '3646fb96', 'rsrc/css/phui/workboards/phui-workpanel.css' => '92197373', @@ -842,7 +842,7 @@ return array( 'phui-theme-css' => 'ab7b848c', 'phui-timeline-view-css' => '2efceff8', 'phui-two-column-view-css' => 'c75bfc5b', - 'phui-workboard-color-css' => '543432b9', + 'phui-workboard-color-css' => '2f068cc8', 'phui-workboard-view-css' => 'e6d89647', 'phui-workcard-view-css' => '3646fb96', 'phui-workpanel-view-css' => '92197373', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index c9c52a9d8a..538ea56772 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2869,6 +2869,7 @@ phutil_register_library_map(array( 'PhabricatorProjectAddHeraldAction' => 'applications/project/herald/PhabricatorProjectAddHeraldAction.php', 'PhabricatorProjectApplication' => 'applications/project/application/PhabricatorProjectApplication.php', 'PhabricatorProjectArchiveController' => 'applications/project/controller/PhabricatorProjectArchiveController.php', + 'PhabricatorProjectBoardBackgroundController' => 'applications/project/controller/PhabricatorProjectBoardBackgroundController.php', 'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php', 'PhabricatorProjectBoardDisableController' => 'applications/project/controller/PhabricatorProjectBoardDisableController.php', 'PhabricatorProjectBoardImportController' => 'applications/project/controller/PhabricatorProjectBoardImportController.php', @@ -2970,6 +2971,7 @@ phutil_register_library_map(array( 'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php', 'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php', 'PhabricatorProjectWatcherListView' => 'applications/project/view/PhabricatorProjectWatcherListView.php', + 'PhabricatorProjectWorkboardBackgroundColor' => 'applications/project/constants/PhabricatorProjectWorkboardBackgroundColor.php', 'PhabricatorProjectWorkboardProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php', 'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php', 'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php', @@ -7298,6 +7300,7 @@ phutil_register_library_map(array( 'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction', 'PhabricatorProjectApplication' => 'PhabricatorApplication', 'PhabricatorProjectArchiveController' => 'PhabricatorProjectController', + 'PhabricatorProjectBoardBackgroundController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardController' => 'PhabricatorProjectController', 'PhabricatorProjectBoardDisableController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardImportController' => 'PhabricatorProjectBoardController', @@ -7413,6 +7416,7 @@ phutil_register_library_map(array( 'PhabricatorProjectViewController' => 'PhabricatorProjectController', 'PhabricatorProjectWatchController' => 'PhabricatorProjectController', 'PhabricatorProjectWatcherListView' => 'PhabricatorProjectUserListView', + 'PhabricatorProjectWorkboardBackgroundColor' => 'Phobject', 'PhabricatorProjectWorkboardProfilePanel' => 'PhabricatorProfilePanel', 'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension', 'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField', diff --git a/src/applications/project/application/PhabricatorProjectApplication.php b/src/applications/project/application/PhabricatorProjectApplication.php index 86f050d281..41620ad4e4 100644 --- a/src/applications/project/application/PhabricatorProjectApplication.php +++ b/src/applications/project/application/PhabricatorProjectApplication.php @@ -86,6 +86,8 @@ final class PhabricatorProjectApplication extends PhabricatorApplication { => 'PhabricatorProjectBoardDisableController', 'manage/' => 'PhabricatorProjectBoardManageController', + 'background/' + => 'PhabricatorProjectBoardBackgroundController', ), 'update/(?P[1-9]\d*)/(?P[^/]+)/' => 'PhabricatorProjectUpdateController', diff --git a/src/applications/project/constants/PhabricatorProjectWorkboardBackgroundColor.php b/src/applications/project/constants/PhabricatorProjectWorkboardBackgroundColor.php new file mode 100644 index 0000000000..a6c0fc9b1b --- /dev/null +++ b/src/applications/project/constants/PhabricatorProjectWorkboardBackgroundColor.php @@ -0,0 +1,124 @@ + '', + 'name' => pht('Use Parent Background (Default)'), + 'special' => 'parent', + 'icon' => 'fa-chevron-circle-up', + 'group' => 'basic', + ), + array( + 'key' => 'none', + 'name' => pht('No Background'), + 'special' => 'none', + 'icon' => 'fa-ban', + 'group' => 'basic', + ), + array( + 'key' => 'red', + 'name' => pht('Red'), + ), + array( + 'key' => 'orange', + 'name' => pht('Orange'), + ), + array( + 'key' => 'yellow', + 'name' => pht('Yellow'), + ), + array( + 'key' => 'green', + 'name' => pht('Green'), + ), + array( + 'key' => 'blue', + 'name' => pht('Blue'), + ), + array( + 'key' => 'indigo', + 'name' => pht('Indigo'), + ), + array( + 'key' => 'violet', + 'name' => pht('Violet'), + ), + array( + 'key' => 'sky', + 'name' => pht('Sky'), + ), + array( + 'key' => 'pink', + 'name' => pht('Pink'), + ), + array( + 'key' => 'fire', + 'name' => pht('Fire'), + ), + array( + 'key' => 'grey', + 'name' => pht('Grey'), + ), + array( + 'key' => 'gradient-red', + 'name' => pht('Ripe Peach'), + ), + array( + 'key' => 'gradient-orange', + 'name' => pht('Ripe Orange'), + ), + array( + 'key' => 'gradient-yellow', + 'name' => pht('Ripe Mango'), + ), + array( + 'key' => 'gradient-green', + 'name' => pht('Shallows'), + ), + array( + 'key' => 'gradient-blue', + 'name' => pht('Reef'), + ), + array( + 'key' => 'gradient-bluegrey', + 'name' => pht('Depths'), + ), + array( + 'key' => 'gradient-indigo', + 'name' => pht('This One Is Purple'), + ), + array( + 'key' => 'gradient-violet', + 'name' => pht('Unripe Plum'), + ), + array( + 'key' => 'gradient-sky', + 'name' => pht('Blue Sky'), + ), + array( + 'key' => 'gradient-pink', + 'name' => pht('Intensity'), + ), + array( + 'key' => 'gradient-grey', + 'name' => pht('Into The Expanse'), + ), + ); + + foreach ($options as $key => $option) { + if (empty($option['group'])) { + if (preg_match('/^gradient/', $option['key'])) { + $option['group'] = 'gradient'; + } else { + $option['group'] = 'solid'; + } + } + $options[$key] = $option; + } + + return ipull($options, null, 'key'); + } +} diff --git a/src/applications/project/controller/PhabricatorProjectBoardBackgroundController.php b/src/applications/project/controller/PhabricatorProjectBoardBackgroundController.php new file mode 100644 index 0000000000..3ba1f03a56 --- /dev/null +++ b/src/applications/project/controller/PhabricatorProjectBoardBackgroundController.php @@ -0,0 +1,168 @@ +getUser(); + $board_id = $request->getURIData('projectID'); + + $board = id(new PhabricatorProjectQuery()) + ->setViewer($viewer) + ->withIDs(array($board_id)) + ->needImages(true) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + if (!$board) { + return new Aphront404Response(); + } + + if (!$board->getHasWorkboard()) { + return new Aphront404Response(); + } + + $this->setProject($board); + $id = $board->getID(); + + $manage_uri = $this->getApplicationURI("board/{$id}/manage/"); + + if ($request->isFormPost()) { + $background_key = $request->getStr('backgroundKey'); + + $xactions = array(); + + $xactions[] = id(new PhabricatorProjectTransaction()) + ->setTransactionType(PhabricatorProjectTransaction::TYPE_BACKGROUND) + ->setNewValue($background_key); + + id(new PhabricatorProjectTransactionEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($board, $xactions); + + return id(new AphrontRedirectResponse()) + ->setURI($manage_uri); + } + + $nav = $this->getProfileMenu(); + + $crumbs = id($this->buildApplicationCrumbs()) + ->addTextCrumb(pht('Workboard'), "/project/board/{$board_id}/") + ->addTextCrumb(pht('Manage'), $manage_uri) + ->addTextCrumb(pht('Background Color')); + + $form = id(new AphrontFormView()) + ->setUser($viewer); + + $group_info = array( + 'basic' => array( + 'label' => pht('Basics'), + ), + 'solid' => array( + 'label' => pht('Solid Colors'), + ), + 'gradient' => array( + 'label' => pht('Gradients'), + ), + ); + + $groups = array(); + $options = PhabricatorProjectWorkboardBackgroundColor::getOptions(); + $option_groups = igroup($options, 'group'); + + require_celerity_resource('people-profile-css'); + require_celerity_resource('phui-workboard-color-css'); + Javelin::initBehavior('phabricator-tooltips', array()); + + foreach ($group_info as $group_key => $spec) { + $buttons = array(); + + $available_options = idx($option_groups, $group_key, array()); + foreach ($available_options as $option) { + $buttons[] = $this->renderOptionButton($option); + } + + $form->appendControl( + id(new AphrontFormMarkupControl()) + ->setLabel($spec['label']) + ->setValue($buttons)); + } + + // NOTE: Each button is its own form, so we can't wrap them in a normal + // form. + $layout_view = $form->buildLayoutView(); + + $form_box = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Edit Background Color')) + ->appendChild($layout_view); + + return $this->newPage() + ->setTitle( + array( + pht('Edit Background Color'), + $board->getDisplayName(), + )) + ->setCrumbs($crumbs) + ->setNavigation($nav) + ->appendChild($form_box); + } + + private function renderOptionButton(array $option) { + $viewer = $this->getViewer(); + + $icon = idx($option, 'icon'); + if ($icon) { + $preview_class = null; + $preview_content = id(new PHUIIconView()) + ->setIcon($icon, 'lightbluetext'); + } else { + $preview_class = 'phui-workboard-'.$option['key']; + $preview_content = null; + } + + $preview = phutil_tag( + 'div', + array( + 'class' => 'phui-workboard-color-preview '.$preview_class, + ), + $preview_content); + + $button = javelin_tag( + 'button', + array( + 'class' => 'grey profile-image-button', + 'sigil' => 'has-tooltip', + 'meta' => array( + 'tip' => $option['name'], + 'size' => 300, + ), + ), + $preview); + + $input = phutil_tag( + 'input', + array( + 'type' => 'hidden', + 'name' => 'backgroundKey', + 'value' => $option['key'], + )); + + return phabricator_form( + $viewer, + array( + 'class' => 'profile-image-form', + 'method' => 'POST', + ), + array( + $button, + $input, + )); + } + +} diff --git a/src/applications/project/controller/PhabricatorProjectBoardManageController.php b/src/applications/project/controller/PhabricatorProjectBoardManageController.php index b21ea0cf46..db4dfe31be 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardManageController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardManageController.php @@ -96,6 +96,16 @@ final class PhabricatorProjectBoardManageController ->setDisabled(!$can_edit) ->setWorkflow(true)); + $background_uri = $this->getApplicationURI("board/{$id}/background/"); + + $actions->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-paint-brush') + ->setName(pht('Change Background Color')) + ->setHref($background_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + $disable_uri = $this->getApplicationURI("board/{$id}/disable/"); $actions->addAction( @@ -117,6 +127,15 @@ final class PhabricatorProjectBoardManageController ->setUser($viewer) ->setObject($board); + $background = $board->getDisplayWorkboardBackgroundColor(); + if ($background !== null) { + $map = PhabricatorProjectWorkboardBackgroundColor::getOptions(); + $map = ipull($map, 'name'); + + $name = idx($map, $background, $background); + $properties->addProperty(pht('Background Color'), $name); + } + return $properties; } diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php index 8bb58779dc..9bc024c123 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php @@ -431,12 +431,7 @@ final class PhabricatorProjectBoardViewController $crumbs->addAction($manage_menu); $crumbs->addAction($fullscreen); - // TODO: Wire to Workboard Preferences - // require_celerity_resource('phui-workboard-color-css'); - // ->addClass('phui-workboard-color') - // ->addClass('phui-workboard-bluegrey') - - return $this->newPage() + $page = $this->newPage() ->setTitle( array( $project->getDisplayName(), @@ -454,6 +449,17 @@ final class PhabricatorProjectBoardViewController array( $board_box, )); + + $background = $project->getDisplayWorkboardBackgroundColor(); + if ($background !== null) { + require_celerity_resource('phui-workboard-color-css'); + $background_color_class = "phui-workboard-{$background}"; + + $page->addClass('phui-workboard-color'); + $nav->addClass($background_color_class); + } + + return $page; } private function readRequestState() { diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php index b1aab1e417..aa8c0852ab 100644 --- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php @@ -42,6 +42,7 @@ final class PhabricatorProjectTransactionEditor $types[] = PhabricatorProjectTransaction::TYPE_HASWORKBOARD; $types[] = PhabricatorProjectTransaction::TYPE_DEFAULT_SORT; $types[] = PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER; + $types[] = PhabricatorProjectTransaction::TYPE_BACKGROUND; return $types; } @@ -77,6 +78,8 @@ final class PhabricatorProjectTransactionEditor return $object->getDefaultWorkboardSort(); case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER: return $object->getDefaultWorkboardFilter(); + case PhabricatorProjectTransaction::TYPE_BACKGROUND: + return $object->getWorkboardBackgroundColor(); } return parent::getCustomTransactionOldValue($object, $xaction); @@ -100,6 +103,12 @@ final class PhabricatorProjectTransactionEditor return $xaction->getNewValue(); case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: return (int)$xaction->getNewValue(); + case PhabricatorProjectTransaction::TYPE_BACKGROUND: + $value = $xaction->getNewValue(); + if (!strlen($value)) { + return null; + } + return $value; case PhabricatorProjectTransaction::TYPE_SLUGS: return $this->normalizeSlugs($xaction->getNewValue()); } @@ -153,6 +162,9 @@ final class PhabricatorProjectTransactionEditor case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER: $object->setDefaultWorkboardFilter($xaction->getNewValue()); return; + case PhabricatorProjectTransaction::TYPE_BACKGROUND: + $object->setWorkboardBackgroundColor($xaction->getNewValue()); + return; } return parent::applyCustomInternalTransaction($object, $xaction); @@ -198,6 +210,7 @@ final class PhabricatorProjectTransactionEditor case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: case PhabricatorProjectTransaction::TYPE_DEFAULT_SORT: case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER: + case PhabricatorProjectTransaction::TYPE_BACKGROUND: return; } diff --git a/src/applications/project/storage/PhabricatorProject.php b/src/applications/project/storage/PhabricatorProject.php index 861fac646b..81a5c09dc9 100644 --- a/src/applications/project/storage/PhabricatorProject.php +++ b/src/applications/project/storage/PhabricatorProject.php @@ -579,6 +579,31 @@ final class PhabricatorProject extends PhabricatorProjectDAO return $this->setProperty('workboard.filter.default', $filter); } + public function getWorkboardBackgroundColor() { + return $this->getProperty('workboard.background'); + } + + public function setWorkboardBackgroundColor($color) { + return $this->setProperty('workboard.background', $color); + } + + public function getDisplayWorkboardBackgroundColor() { + $color = $this->getWorkboardBackgroundColor(); + + if ($color === null) { + $parent = $this->getParentProject(); + if ($parent) { + return $parent->getDisplayWorkboardBackgroundColor(); + } + } + + if ($color === 'none') { + $color = null; + } + + return $color; + } + /* -( PhabricatorCustomFieldInterface )------------------------------------ */ diff --git a/src/applications/project/storage/PhabricatorProjectTransaction.php b/src/applications/project/storage/PhabricatorProjectTransaction.php index e812952630..ef5d39b4e8 100644 --- a/src/applications/project/storage/PhabricatorProjectTransaction.php +++ b/src/applications/project/storage/PhabricatorProjectTransaction.php @@ -15,6 +15,7 @@ final class PhabricatorProjectTransaction const TYPE_HASWORKBOARD = 'project:hasworkboard'; const TYPE_DEFAULT_SORT = 'project:sort'; const TYPE_DEFAULT_FILTER = 'project:filter'; + const TYPE_BACKGROUND = 'project:background'; // NOTE: This is deprecated, members are just a normal edge now. const TYPE_MEMBERS = 'project:members'; @@ -73,6 +74,7 @@ final class PhabricatorProjectTransaction case self::TYPE_HASWORKBOARD: case self::TYPE_DEFAULT_SORT: case self::TYPE_DEFAULT_FILTER: + case self::TYPE_BACKGROUND: return true; } @@ -84,6 +86,7 @@ final class PhabricatorProjectTransaction case self::TYPE_HASWORKBOARD: case self::TYPE_DEFAULT_SORT: case self::TYPE_DEFAULT_FILTER: + case self::TYPE_BACKGROUND: return true; } @@ -291,6 +294,11 @@ final class PhabricatorProjectTransaction return pht( '%s changed the default filter for the project workboard.', $author_handle); + + case self::TYPE_BACKGROUND: + return pht( + '%s changed the background color of the project workboard.', + $author_handle); } return parent::getTitle(); diff --git a/webroot/rsrc/css/phui/workboards/phui-workboard-color.css b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css index 280f79782c..8d5584d903 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workboard-color.css +++ b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css @@ -30,6 +30,12 @@ background-color: rgba(255,255,255,.6); } +.phui-workboard-color-preview { + width: 50px; + height: 50px; + font-size: 34px; +} + /* Gradients */ .phui-workboard-gradient-red { From 0e5cd478c4bcedbe5eca21d5352f23b199f92b5b Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 14 Feb 2016 21:29:56 -0800 Subject: [PATCH 14/42] Move rgba rules into CelerityDefaultPostprocessor Summary: Should make it simpler here to have more `rgba` rules in CSS for things like hovers, selected states. Maybe only use `rgb` colors? Color pallette probably needs an overhaul. Test Plan: Bounce around random pages, buttons, menus. Everything appears normal. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15273 --- resources/celerity/map.php | 66 +++++++++---------- .../CelerityDefaultPostprocessor.php | 6 ++ webroot/rsrc/css/aphront/dark-console.css | 2 +- webroot/rsrc/css/aphront/notification.css | 2 +- .../rsrc/css/aphront/phabricator-nav-view.css | 2 +- webroot/rsrc/css/aphront/tooltip.css | 4 +- webroot/rsrc/css/aphront/typeahead.css | 2 +- .../css/application/base/main-menu-view.css | 2 +- .../phabricator-application-launch-view.css | 2 +- .../application/base/standard-page-view.css | 2 +- .../conpherence/durable-column.css | 4 +- .../rsrc/css/application/conpherence/menu.css | 6 +- .../differential/phui-inline-comment.css | 4 +- .../files/global-drag-and-drop.css | 2 +- .../css/application/people/people-profile.css | 2 +- webroot/rsrc/css/application/phame/phame.css | 8 +-- .../rsrc/css/application/pholio/pholio.css | 14 ++-- .../css/application/ponder/ponder-view.css | 4 +- .../css/application/project/project-view.css | 4 +- webroot/rsrc/css/core/remarkup.css | 8 +-- .../css/layout/phabricator-side-menu-view.css | 6 +- webroot/rsrc/css/phui/phui-action-panel.css | 2 +- webroot/rsrc/css/phui/phui-badge.css | 2 +- webroot/rsrc/css/phui/phui-box.css | 4 +- webroot/rsrc/css/phui/phui-button.css | 10 +-- webroot/rsrc/css/phui/phui-crumbs-view.css | 2 +- webroot/rsrc/css/phui/phui-document-pro.css | 2 +- .../rsrc/css/phui/phui-document-summary.css | 2 +- webroot/rsrc/css/phui/phui-form.css | 12 ++-- webroot/rsrc/css/phui/phui-header-view.css | 4 +- webroot/rsrc/css/phui/phui-image-mask.css | 2 +- webroot/rsrc/css/phui/phui-info-view.css | 2 +- webroot/rsrc/css/phui/phui-profile-menu.css | 10 +-- webroot/rsrc/css/phui/phui-tag-view.css | 2 +- .../phui/workboards/phui-workboard-color.css | 10 +-- .../css/phui/workboards/phui-workcard.css | 2 +- .../css/phui/workboards/phui-workpanel.css | 8 +-- 37 files changed, 117 insertions(+), 111 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 856da367e1..791572dac3 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ */ return array( 'names' => array( - 'core.pkg.css' => '1972333f', + 'core.pkg.css' => '7fd6b616', 'core.pkg.js' => 'd7daa6d8', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '2de124c9', @@ -17,19 +17,19 @@ return array( 'maniphest.pkg.css' => '4845691a', 'maniphest.pkg.js' => '949a7498', 'rsrc/css/aphront/aphront-bars.css' => '231ac33c', - 'rsrc/css/aphront/dark-console.css' => '6378ef3d', + 'rsrc/css/aphront/dark-console.css' => 'f54bf286', 'rsrc/css/aphront/dialog-view.css' => 'b4334e08', 'rsrc/css/aphront/lightbox-attachment.css' => '7acac05d', 'rsrc/css/aphront/list-filter-view.css' => '5d6f0526', 'rsrc/css/aphront/multi-column.css' => 'fd18389d', - 'rsrc/css/aphront/notification.css' => '9c279160', + 'rsrc/css/aphront/notification.css' => '7f684b62', 'rsrc/css/aphront/panel-view.css' => '8427b78d', - 'rsrc/css/aphront/phabricator-nav-view.css' => 'a24cb589', + 'rsrc/css/aphront/phabricator-nav-view.css' => 'ac79a758', 'rsrc/css/aphront/table-view.css' => '6d01d468', 'rsrc/css/aphront/tokenizer.css' => '056da01b', - 'rsrc/css/aphront/tooltip.css' => '7672b60f', + 'rsrc/css/aphront/tooltip.css' => '1a07aea8', 'rsrc/css/aphront/typeahead-browse.css' => 'd8581d2c', - 'rsrc/css/aphront/typeahead.css' => '0e403212', + 'rsrc/css/aphront/typeahead.css' => 'd4f16145', 'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af', 'rsrc/css/application/auth/auth.css' => '0877ed6e', 'rsrc/css/application/base/main-menu-view.css' => 'd00a795a', @@ -68,7 +68,7 @@ return array( 'rsrc/css/application/diffusion/diffusion-readme.css' => '2106ea08', 'rsrc/css/application/diffusion/diffusion-source.css' => '075ba788', 'rsrc/css/application/feed/feed.css' => 'ecd4ec57', - 'rsrc/css/application/files/global-drag-and-drop.css' => '697324ad', + 'rsrc/css/application/files/global-drag-and-drop.css' => '5c1b47c2', 'rsrc/css/application/flag/flag.css' => '5337623f', 'rsrc/css/application/harbormaster/harbormaster.css' => 'b0758ca5', 'rsrc/css/application/herald/herald-test.css' => 'a52e323e', @@ -81,10 +81,10 @@ return array( 'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b', 'rsrc/css/application/paste/paste.css' => 'a5157c48', 'rsrc/css/application/people/people-profile.css' => '2473d929', - 'rsrc/css/application/phame/phame.css' => '1dbbacf9', + 'rsrc/css/application/phame/phame.css' => '4ca6fd6c', 'rsrc/css/application/pholio/pholio-edit.css' => '3ad9d1ee', 'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49', - 'rsrc/css/application/pholio/pholio.css' => '95174bdd', + 'rsrc/css/application/pholio/pholio.css' => 'ca89d380', 'rsrc/css/application/phortune/phortune-credit-card-form.css' => '8391eb02', 'rsrc/css/application/phortune/phortune.css' => '9149f103', 'rsrc/css/application/phrequent/phrequent.css' => 'ffc185ad', @@ -92,7 +92,7 @@ return array( 'rsrc/css/application/policy/policy-edit.css' => '815c66f7', 'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43', 'rsrc/css/application/policy/policy.css' => '957ea14c', - 'rsrc/css/application/ponder/ponder-view.css' => '7b0df4da', + 'rsrc/css/application/ponder/ponder-view.css' => '4486434b', 'rsrc/css/application/project/project-card-view.css' => '9418c97d', 'rsrc/css/application/project/project-view.css' => '4c832c27', 'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733', @@ -104,7 +104,7 @@ return array( 'rsrc/css/application/tokens/tokens.css' => '3d0f239e', 'rsrc/css/application/uiexample/example.css' => '528b19de', 'rsrc/css/core/core.css' => '5b3563c8', - 'rsrc/css/core/remarkup.css' => '2c913a06', + 'rsrc/css/core/remarkup.css' => 'fc228f08', 'rsrc/css/core/syntax.css' => '9fd11da8', 'rsrc/css/core/z-index.css' => '5b6fcf3f', 'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa', @@ -124,21 +124,21 @@ return array( 'rsrc/css/phui/phui-badge.css' => 'f25c3476', 'rsrc/css/phui/phui-big-info-view.css' => 'bd903741', 'rsrc/css/phui/phui-box.css' => 'dd1294d3', - 'rsrc/css/phui/phui-button.css' => 'd04a0eb7', + 'rsrc/css/phui/phui-button.css' => 'edf464e9', 'rsrc/css/phui/phui-chart.css' => '6bf6f78e', 'rsrc/css/phui/phui-crumbs-view.css' => '79d536e5', - 'rsrc/css/phui/phui-document-pro.css' => '8799acf7', + 'rsrc/css/phui/phui-document-pro.css' => 'a8872307', 'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf', 'rsrc/css/phui/phui-document.css' => '9c71d2bf', 'rsrc/css/phui/phui-feed-story.css' => '04aec08f', 'rsrc/css/phui/phui-fontkit.css' => '9cda225e', 'rsrc/css/phui/phui-form-view.css' => '4a1a0f5e', - 'rsrc/css/phui/phui-form.css' => '0b98e572', - 'rsrc/css/phui/phui-header-view.css' => 'd53cc835', + 'rsrc/css/phui/phui-form.css' => 'aac1d51d', + 'rsrc/css/phui/phui-header-view.css' => '50c5cb6a', 'rsrc/css/phui/phui-hovercard.css' => 'de1a2119', 'rsrc/css/phui/phui-icon-set-selector.css' => '1ab67aad', 'rsrc/css/phui/phui-icon.css' => '3f33ab57', - 'rsrc/css/phui/phui-image-mask.css' => '5a8b09c8', + 'rsrc/css/phui/phui-image-mask.css' => 'a8498f9c', 'rsrc/css/phui/phui-info-panel.css' => '27ea50a1', 'rsrc/css/phui/phui-info-view.css' => '6d7c3509', 'rsrc/css/phui/phui-list.css' => '9da2aa00', @@ -146,7 +146,7 @@ return array( 'rsrc/css/phui/phui-object-item-list-view.css' => '18b2ce8e', 'rsrc/css/phui/phui-pager.css' => 'bea33d23', 'rsrc/css/phui/phui-pinboard-view.css' => '2495140e', - 'rsrc/css/phui/phui-profile-menu.css' => 'f709256c', + 'rsrc/css/phui/phui-profile-menu.css' => 'f42bedb6', 'rsrc/css/phui/phui-property-list-view.css' => '27b2849e', 'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591', 'rsrc/css/phui/phui-segment-bar-view.css' => '46342871', @@ -518,15 +518,15 @@ return array( 'symbols' => array( 'almanac-css' => 'dbb9b3af', 'aphront-bars' => '231ac33c', - 'aphront-dark-console-css' => '6378ef3d', + 'aphront-dark-console-css' => 'f54bf286', 'aphront-dialog-view-css' => 'b4334e08', 'aphront-list-filter-view-css' => '5d6f0526', 'aphront-multi-column-view-css' => 'fd18389d', 'aphront-panel-view-css' => '8427b78d', 'aphront-table-view-css' => '6d01d468', 'aphront-tokenizer-control-css' => '056da01b', - 'aphront-tooltip-css' => '7672b60f', - 'aphront-typeahead-control-css' => '0e403212', + 'aphront-tooltip-css' => '1a07aea8', + 'aphront-typeahead-control-css' => 'd4f16145', 'auth-css' => '0877ed6e', 'bulk-job-css' => 'df9c1d4a', 'changeset-view-manager' => 'a2828756', @@ -557,7 +557,7 @@ return array( 'font-aleo' => '8bdb2835', 'font-fontawesome' => 'c43323c5', 'font-lato' => 'c7ccd872', - 'global-drag-and-drop-css' => '697324ad', + 'global-drag-and-drop-css' => '5c1b47c2', 'harbormaster-css' => 'b0758ca5', 'herald-css' => '826075fa', 'herald-rule-editor' => '746ca158', @@ -761,14 +761,14 @@ return array( 'phabricator-keyboard-shortcut' => '1ae869f2', 'phabricator-keyboard-shortcut-manager' => 'c1700f6f', 'phabricator-main-menu-view' => 'd00a795a', - 'phabricator-nav-view-css' => 'a24cb589', + 'phabricator-nav-view-css' => 'ac79a758', 'phabricator-notification' => 'ccf1cbf8', - 'phabricator-notification-css' => '9c279160', + 'phabricator-notification-css' => '7f684b62', 'phabricator-notification-menu-css' => 'f31c0bde', 'phabricator-object-selector-css' => '85ee8ce6', 'phabricator-phtize' => 'd254d646', 'phabricator-prefab' => 'e67df814', - 'phabricator-remarkup-css' => '2c913a06', + 'phabricator-remarkup-css' => 'fc228f08', 'phabricator-search-results-css' => '7dea472c', 'phabricator-shaped-request' => '7cbe244b', 'phabricator-side-menu-view-css' => '3a3d9f41', @@ -790,8 +790,8 @@ return array( 'phabricator-uiexample-reactor-sendclass' => '1def2711', 'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee', 'phabricator-zindex-css' => '5b6fcf3f', - 'phame-css' => '1dbbacf9', - 'pholio-css' => '95174bdd', + 'phame-css' => '4ca6fd6c', + 'pholio-css' => 'ca89d380', 'pholio-edit-css' => '3ad9d1ee', 'pholio-inline-comments-css' => '8e545e49', 'phortune-credit-card-form' => '2290aeef', @@ -803,7 +803,7 @@ return array( 'phui-badge-view-css' => 'f25c3476', 'phui-big-info-view-css' => 'bd903741', 'phui-box-css' => 'dd1294d3', - 'phui-button-css' => 'd04a0eb7', + 'phui-button-css' => 'edf464e9', 'phui-calendar-css' => 'ccabe893', 'phui-calendar-day-css' => 'd1cf6f93', 'phui-calendar-list-css' => 'c1c7f338', @@ -812,18 +812,18 @@ return array( 'phui-crumbs-view-css' => '79d536e5', 'phui-document-summary-view-css' => '9ca48bdf', 'phui-document-view-css' => '9c71d2bf', - 'phui-document-view-pro-css' => '8799acf7', + 'phui-document-view-pro-css' => 'a8872307', 'phui-feed-story-css' => '04aec08f', 'phui-font-icon-base-css' => 'ecbbb4c2', 'phui-fontkit-css' => '9cda225e', - 'phui-form-css' => '0b98e572', + 'phui-form-css' => 'aac1d51d', 'phui-form-view-css' => '4a1a0f5e', - 'phui-header-view-css' => 'd53cc835', + 'phui-header-view-css' => '50c5cb6a', 'phui-hovercard' => '1bd28176', 'phui-hovercard-view-css' => 'de1a2119', 'phui-icon-set-selector-css' => '1ab67aad', 'phui-icon-view-css' => '3f33ab57', - 'phui-image-mask-css' => '5a8b09c8', + 'phui-image-mask-css' => 'a8498f9c', 'phui-info-panel-css' => '27ea50a1', 'phui-info-view-css' => '6d7c3509', 'phui-inline-comment-view-css' => '0fdb3667', @@ -832,7 +832,7 @@ return array( 'phui-object-item-list-view-css' => '18b2ce8e', 'phui-pager-css' => 'bea33d23', 'phui-pinboard-view-css' => '2495140e', - 'phui-profile-menu-css' => 'f709256c', + 'phui-profile-menu-css' => 'f42bedb6', 'phui-property-list-view-css' => '27b2849e', 'phui-remarkup-preview-css' => '1a8f2591', 'phui-segment-bar-view-css' => '46342871', @@ -855,7 +855,7 @@ return array( 'policy-css' => '957ea14c', 'policy-edit-css' => '815c66f7', 'policy-transaction-detail-css' => '82100a43', - 'ponder-view-css' => '7b0df4da', + 'ponder-view-css' => '4486434b', 'project-card-view-css' => '9418c97d', 'project-view-css' => '4c832c27', 'releeph-core' => '9b3c5733', diff --git a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php index a47d4a65e4..4fdeaeccb1 100644 --- a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php +++ b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php @@ -72,6 +72,12 @@ final class CelerityDefaultPostprocessor 'hoverselectedblue' => '#e6e9ee', 'borderinset' => 'inset 0 0 0 1px rgba(55,55,55,.15)', + // Alphas + 'alphawhite' => '255,255,255', + 'alphagrey' => '55,55,55', + 'alphablue' => '71,87,120', + 'alphablack' => '0,0,0', + // Base Greys 'lightgreyborder' => '#C7CCD9', 'greyborder' => '#A1A6B0', diff --git a/webroot/rsrc/css/aphront/dark-console.css b/webroot/rsrc/css/aphront/dark-console.css index e1df876dac..40dabd7bf6 100644 --- a/webroot/rsrc/css/aphront/dark-console.css +++ b/webroot/rsrc/css/aphront/dark-console.css @@ -31,7 +31,7 @@ .dark-console-panel, .dark-console-load { border-left: 1px solid #111111; - box-shadow: -2px 0px 2px rgba(0, 0, 0, 0.25); + box-shadow: -2px 0px 2px rgba({$alphablack}, 0.25); } .dark-console-requests { diff --git a/webroot/rsrc/css/aphront/notification.css b/webroot/rsrc/css/aphront/notification.css index 406f3a3143..1940309569 100644 --- a/webroot/rsrc/css/aphront/notification.css +++ b/webroot/rsrc/css/aphront/notification.css @@ -23,7 +23,7 @@ cursor: pointer; border-radius: 3px; - box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.25); + box-shadow: 0px 1px 2px rgba({$alphablack}, 0.25); margin-top: 4px; } diff --git a/webroot/rsrc/css/aphront/phabricator-nav-view.css b/webroot/rsrc/css/aphront/phabricator-nav-view.css index 52a977c13e..7dbb56da07 100644 --- a/webroot/rsrc/css/aphront/phabricator-nav-view.css +++ b/webroot/rsrc/css/aphront/phabricator-nav-view.css @@ -52,7 +52,7 @@ border-width: 0 1px 0 1px; border-color: #fff #999c9e #fff #999c9e; - box-shadow: inset -1px 0px 1px rgba(0, 0, 0, 0.15); + box-shadow: inset -1px 0px 1px rgba({$alphablack}, 0.15); background-image: url(/rsrc/image/divot.png); background-position: center; diff --git a/webroot/rsrc/css/aphront/tooltip.css b/webroot/rsrc/css/aphront/tooltip.css index caa21415d1..f2c3151373 100644 --- a/webroot/rsrc/css/aphront/tooltip.css +++ b/webroot/rsrc/css/aphront/tooltip.css @@ -9,7 +9,7 @@ .jx-tooltip-inner { position: relative; - background: rgba(0,0,0, .9); + background: rgba({$alphablack}, .9); border-radius: 3px; } @@ -28,7 +28,7 @@ width: 0; position: absolute; pointer-events: none; - border-color: rgba(0, 0, 0, 0); + border-color: rgba({$alphablack}, 0); border-width: 5px; } diff --git a/webroot/rsrc/css/aphront/typeahead.css b/webroot/rsrc/css/aphront/typeahead.css index 30ff456d74..0f63918e40 100644 --- a/webroot/rsrc/css/aphront/typeahead.css +++ b/webroot/rsrc/css/aphront/typeahead.css @@ -14,7 +14,7 @@ div.jx-typeahead-results { padding: 0; background: #fefefe; width: 98%; - box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); + box-shadow: 0px 1px 2px rgba({$alphablack}, 0.2); margin: -1px 1% 0; } diff --git a/webroot/rsrc/css/application/base/main-menu-view.css b/webroot/rsrc/css/application/base/main-menu-view.css index 92385e6dc2..2b8fcb1a0b 100644 --- a/webroot/rsrc/css/application/base/main-menu-view.css +++ b/webroot/rsrc/css/application/base/main-menu-view.css @@ -65,7 +65,7 @@ } .device-desktop .phabricator-main-menu-brand:hover { - background-color: rgba(55,55,55,.2); + background-color: rgba({$alphagrey},.2); cursor: hand; } diff --git a/webroot/rsrc/css/application/base/phabricator-application-launch-view.css b/webroot/rsrc/css/application/base/phabricator-application-launch-view.css index a4b97ce83f..714fa849e6 100644 --- a/webroot/rsrc/css/application/base/phabricator-application-launch-view.css +++ b/webroot/rsrc/css/application/base/phabricator-application-launch-view.css @@ -52,7 +52,7 @@ div.phabricator-application-launch-container { } .device-desktop a.phabricator-application-launch-container:hover { - background-color: rgba(0,0,0,.07); + background-color: rgba({$alphablack},.07); text-decoration: none; } diff --git a/webroot/rsrc/css/application/base/standard-page-view.css b/webroot/rsrc/css/application/base/standard-page-view.css index 4b53f61a6a..05686898b6 100644 --- a/webroot/rsrc/css/application/base/standard-page-view.css +++ b/webroot/rsrc/css/application/base/standard-page-view.css @@ -21,7 +21,7 @@ text-align: right; margin: 44px 16px 16px; padding: 12px 0; - border-top: 1px solid rgba(55,55,55,.1); + border-top: 1px solid rgba({$alphagrey},.1); color: {$greytext}; } diff --git a/webroot/rsrc/css/application/conpherence/durable-column.css b/webroot/rsrc/css/application/conpherence/durable-column.css index a416813fc3..d204592cf3 100644 --- a/webroot/rsrc/css/application/conpherence/durable-column.css +++ b/webroot/rsrc/css/application/conpherence/durable-column.css @@ -108,8 +108,8 @@ } .conpherence-durable-column-header { - border-left: 1px solid rgba(0,0,0,.1); - border-right: 1px solid rgba(0,0,0,.1); + border-left: 1px solid rgba({$alphablack},.1); + border-right: 1px solid rgba({$alphablack},.1); } .conpherence-durable-column-header-text { diff --git a/webroot/rsrc/css/application/conpherence/menu.css b/webroot/rsrc/css/application/conpherence/menu.css index 9918b89c71..49549c5738 100644 --- a/webroot/rsrc/css/application/conpherence/menu.css +++ b/webroot/rsrc/css/application/conpherence/menu.css @@ -117,7 +117,7 @@ } .conpherence-menu .conpherence-selected { - background: rgba(0,0,0,0.05); + background: rgba({$alphablack},0.05); border-left: 4px solid {$sky}; } @@ -127,7 +127,7 @@ .device-desktop .conpherence-menu .conpherence-selected.conpherence-menu-item-view:hover { - background-color: rgba(0,0,0,0.07); + background-color: rgba({$alphablack},0.07); } .conpherence-menu .loading { @@ -135,7 +135,7 @@ } .device-desktop .conpherence-menu .conpherence-menu-item-view:hover { - background-color: rgba(0,0,0,0.05); + background-color: rgba({$alphablack},0.05); } .conpherence-menu .conpherence-menu-item-view .conpherence-menu-item-image { diff --git a/webroot/rsrc/css/application/differential/phui-inline-comment.css b/webroot/rsrc/css/application/differential/phui-inline-comment.css index 02998aaaf0..0055f25699 100644 --- a/webroot/rsrc/css/application/differential/phui-inline-comment.css +++ b/webroot/rsrc/css/application/differential/phui-inline-comment.css @@ -340,7 +340,7 @@ } .ghost-icon { - background: rgba(55,55,55,.07); + background: rgba({$alphagrey},.07); float: left; padding: 2px 4px 2px 2px; position: absolute; @@ -389,7 +389,7 @@ } .inline-button-divider { - border-left: 1px solid rgba(55,55,55,.25); + border-left: 1px solid rgba({$alphagrey},.25); margin-left: 8px; } diff --git a/webroot/rsrc/css/application/files/global-drag-and-drop.css b/webroot/rsrc/css/application/files/global-drag-and-drop.css index 7539a046fb..e338b987b0 100644 --- a/webroot/rsrc/css/application/files/global-drag-and-drop.css +++ b/webroot/rsrc/css/application/files/global-drag-and-drop.css @@ -15,7 +15,7 @@ padding: 18px 0; color: #ffffff; - background: rgba(0, 0, 0, 0.8); + background: rgba({$alphablack}, 0.8); border-radius: 18px; } diff --git a/webroot/rsrc/css/application/people/people-profile.css b/webroot/rsrc/css/application/people/people-profile.css index d73b94fb97..c5ec73551a 100644 --- a/webroot/rsrc/css/application/people/people-profile.css +++ b/webroot/rsrc/css/application/people/people-profile.css @@ -38,7 +38,7 @@ form.compose-dialog { } .compose-dialog .compose-icon-bg { - color: rgba(255,255,255,0.8); + color: rgba({$alphawhite},0.8); line-height: 50px; width: 50px; text-align: center; diff --git a/webroot/rsrc/css/application/phame/phame.css b/webroot/rsrc/css/application/phame/phame.css index 3d151426d6..299474fee1 100644 --- a/webroot/rsrc/css/application/phame/phame.css +++ b/webroot/rsrc/css/application/phame/phame.css @@ -23,7 +23,7 @@ display: inline-block; background-repeat: no-repeat; background-size: 100%; - box-shadow: inset 0 0 0 1px rgba(55,55,55,.15); + box-shadow: inset 0 0 0 1px rgba({$alphagrey},.15); width: 40px; height: 40px; border-radius: 3px; @@ -31,7 +31,7 @@ } .phame-blog-description + .phui-property-list-section { - border-top: 1px solid rgba(71, 87, 120, 0.20); + border-top: 1px solid rgba({$alphablue}, 0.20); padding-top: 16px; } @@ -45,7 +45,7 @@ .phame-home-view { background-color: #fff; - border-bottom: 1px solid rgba(55,55,55,.1); + border-bottom: 1px solid rgba({$alphagrey},.1); } .phame-home-view .phame-home-container { @@ -100,7 +100,7 @@ display: inline-block; background-repeat: no-repeat; background-size: 100%; - box-shadow: inset 0 0 0 1px rgba(55,55,55,.15); + box-shadow: inset 0 0 0 1px rgba({$alphagrey},.15); width: 24px; height: 24px; border-radius: 3px; diff --git a/webroot/rsrc/css/application/pholio/pholio.css b/webroot/rsrc/css/application/pholio/pholio.css index 1cd8e604a1..52ff4ca5bd 100644 --- a/webroot/rsrc/css/application/pholio/pholio.css +++ b/webroot/rsrc/css/application/pholio/pholio.css @@ -116,8 +116,8 @@ } .pholio-mock-reticle-selection { - border: 1px solid rgba(0,0,0,.5); - box-shadow: 0 0 0 4px rgba(255,255,255,.5); + border: 1px solid rgba({$alphablack},.5); + box-shadow: 0 0 0 4px rgba({$alphawhite},.5); } .pholio-mock-comment-icon { @@ -132,21 +132,21 @@ .pholio-mock-reticle-draft .pholio-mock-comment-icon { font-size: 2.2em; color: {$yellow}; - text-shadow: 0 3px 8px rgba(0, 0, 0, 0.35); + text-shadow: 0 3px 8px rgba({$alphablack}, 0.35); -webkit-text-stroke: 1px white; } .pholio-mock-reticle-final .pholio-mock-comment-icon { font-size: 2.2em; color: {$pink}; - text-shadow: 0 3px 8px rgba(0, 0, 0, 0.35); + text-shadow: 0 3px 8px rgba({$alphablack}, 0.35); -webkit-text-stroke: 1px white; } .pholio-mock-reticle-draft:hover, .pholio-mock-reticle-final:hover { - border: 1px solid rgba(0,0,0,.5); - box-shadow: 0 0 0 4px rgba(255,255,255,.5); + border: 1px solid rgba({$alphablack},.5); + box-shadow: 0 0 0 4px rgba({$alphawhite},.5); cursor: pointer; } @@ -170,7 +170,7 @@ right: 0; font-size: 14px; padding: 4px 8px; - background: rgba(255,255,255,.6); + background: rgba({$alphawhite},.6); color: {$greytext}; } diff --git a/webroot/rsrc/css/application/ponder/ponder-view.css b/webroot/rsrc/css/application/ponder/ponder-view.css index f1182d531c..565a6af83f 100644 --- a/webroot/rsrc/css/application/ponder/ponder-view.css +++ b/webroot/rsrc/css/application/ponder/ponder-view.css @@ -63,7 +63,7 @@ margin-right: 8px; color: {$anchor}; display: inline-block; - background-color: rgba(71, 87, 120, 0.06); + background-color: rgba({$alphablue}, 0.06); } .ponder-footer-view .ponder-footer-action.ponder-footer-action-helpful { @@ -82,5 +82,5 @@ .ponder-footer-view a:hover { text-decoration: none; - background-color: rgba(71, 87, 120, 0.10); + background-color: rgba({$alphablue}, 0.10); } diff --git a/webroot/rsrc/css/application/project/project-view.css b/webroot/rsrc/css/application/project/project-view.css index 5573780672..f46af0bc74 100644 --- a/webroot/rsrc/css/application/project/project-view.css +++ b/webroot/rsrc/css/application/project/project-view.css @@ -27,7 +27,7 @@ .project-view-home .phui-box.project-view-properties { margin: 0 16px 16px 16px; padding: 4px 12px; - border: 2px solid rgba(55,55,55,.1); + border: 2px solid rgba({$alphagrey},.1); background-color: #F7F7F9; } @@ -37,7 +37,7 @@ .project-view-properties .phui-property-list-container + .phui-property-list-text-content { - border-color: rgba(55,55,55,.15); + border-color: rgba({$alphagrey},.15); } .project-view-properties .phui-property-list-key { diff --git a/webroot/rsrc/css/core/remarkup.css b/webroot/rsrc/css/core/remarkup.css index 511d646f8e..7b537f5d67 100644 --- a/webroot/rsrc/css/core/remarkup.css +++ b/webroot/rsrc/css/core/remarkup.css @@ -30,7 +30,7 @@ padding: 6px 12px; font-size: 13px; font-weight: bold; - background: rgba(71,87,120,0.08); + background: rgba({$alphablue},0.08); display: inline-block; border-top-left-radius: 3px; border-top-right-radius: 3px; @@ -45,7 +45,7 @@ } .phabricator-remarkup .remarkup-code-block pre { - background: rgba(71,87,120,0.08); + background: rgba({$alphablue},0.08); display: block; color: #000; overflow: auto; @@ -59,7 +59,7 @@ .phabricator-remarkup tt.remarkup-monospaced { color: #000; - background: rgba(71,87,120,0.1); + background: rgba({$alphablue},0.1); padding: 1px 4px; border-radius: 3px; white-space: pre-wrap; @@ -330,7 +330,7 @@ .phabricator-remarkup-embed-image { display: inline-block; border: 3px solid white; - box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.20); + box-shadow: 1px 1px 2px rgba({$alphablack}, 0.20); } .phabricator-remarkup-embed-image-full { diff --git a/webroot/rsrc/css/layout/phabricator-side-menu-view.css b/webroot/rsrc/css/layout/phabricator-side-menu-view.css index 5b425c2a5f..77c0b7cf39 100644 --- a/webroot/rsrc/css/layout/phabricator-side-menu-view.css +++ b/webroot/rsrc/css/layout/phabricator-side-menu-view.css @@ -19,7 +19,7 @@ } .phabricator-basic-nav .phabricator-side-menu .phui-list-item-selected { - background-color: rgba(0,0,0,.05); + background-color: rgba({$alphablack},.05); border-left: 4px solid {$sky}; border-top-right-radius: 3px; border-bottom-right-radius: 3px; @@ -29,7 +29,7 @@ .device-desktop .phabricator-basic-nav .phabricator-side-menu .phui-list-item-selected a.phui-list-item-href:hover { - background-color: rgba(0,0,0,.05); + background-color: rgba({$alphablack},.05); } .phabricator-basic-nav .phabricator-side-menu .phui-list-item-selected @@ -49,5 +49,5 @@ .device-desktop .phabricator-basic-nav .phabricator-side-menu a.phui-list-item-href:hover { text-decoration: none; - background-color: rgba(0,0,0,.07); + background-color: rgba({$alphablack},.07); } diff --git a/webroot/rsrc/css/phui/phui-action-panel.css b/webroot/rsrc/css/phui/phui-action-panel.css index 6d7c566de7..8cfd69a94f 100644 --- a/webroot/rsrc/css/phui/phui-action-panel.css +++ b/webroot/rsrc/css/phui/phui-action-panel.css @@ -22,7 +22,7 @@ .device-desktop .phui-action-panel-hitarea:hover { text-decoration: none; - background-color: rgba(255,255,255,.25); + background-color: rgba({$alphawhite},.25); } .device-desktop .phui-action-panel-hitarea:hover .phui-icon-view { diff --git a/webroot/rsrc/css/phui/phui-badge.css b/webroot/rsrc/css/phui/phui-badge.css index f45a88f258..fdb396b539 100644 --- a/webroot/rsrc/css/phui/phui-badge.css +++ b/webroot/rsrc/css/phui/phui-badge.css @@ -88,7 +88,7 @@ .phui-badge-illustration { background-color: #fff; - box-shadow: inset 0 -1px 1px 0 rgba(0,0,0,.1); + box-shadow: inset 0 -1px 1px 0 rgba({$alphablack},.1); text-align: center; height: 100px; width: 100%; diff --git a/webroot/rsrc/css/phui/phui-box.css b/webroot/rsrc/css/phui/phui-box.css index ce95924c72..1516285cf8 100644 --- a/webroot/rsrc/css/phui/phui-box.css +++ b/webroot/rsrc/css/phui/phui-box.css @@ -15,7 +15,7 @@ .phui-box-grey { background-color: #F7F7F9; border-radius: 3px; - border-color: rgba(55,55,55,.1); + border-color: rgba({$alphagrey},.1); } .phui-box-blue { @@ -44,7 +44,7 @@ } .phui-box-grey .phui-header-shell { - border-color: rgba(55,55,55,.1); + border-color: rgba({$alphagrey},.1); } .phui-object-box.phui-box-blue div.phui-info-severity-nodata, diff --git a/webroot/rsrc/css/phui/phui-button.css b/webroot/rsrc/css/phui/phui-button.css index f4cf9ac6aa..0ba3007e5f 100644 --- a/webroot/rsrc/css/phui/phui-button.css +++ b/webroot/rsrc/css/phui/phui-button.css @@ -60,7 +60,7 @@ input[type="submit"].grey, a.grey, a.grey:visited { background-color: #F7F7F9; - border: 1px solid rgba(55,55,55,.1); + border: 1px solid rgba({$alphagrey},.1); color: {$darkgreytext}; } @@ -106,8 +106,8 @@ button:hover { a.button.grey:hover, button.grey:hover { - background-color: rgba(71, 87, 120, 0.1); - border-color: rgba(71, 87, 120, 0.15); + background-color: rgba({$alphablue}, 0.1); + border-color: rgba({$alphablue}, 0.15); transition: 0.1s; } @@ -315,7 +315,7 @@ a.policy-control .caret { .phui-button-bar-borderless .button .phui-icon-view { font-size: 15px; - color: rgba(55,55,55,.4); + color: rgba({$alphagrey},.4); } .phui-button-bar-borderless .button:hover { @@ -324,7 +324,7 @@ a.policy-control .caret { } .phui-button-bar-borderless .button:hover .phui-icon-view { - color: rgba(55,55,55,.9); + color: rgba({$alphagrey},.9); } .phui-button-bar-borderless .button { diff --git a/webroot/rsrc/css/phui/phui-crumbs-view.css b/webroot/rsrc/css/phui/phui-crumbs-view.css index 81c8471c4f..47fd0a4da9 100644 --- a/webroot/rsrc/css/phui/phui-crumbs-view.css +++ b/webroot/rsrc/css/phui/phui-crumbs-view.css @@ -105,7 +105,7 @@ a.phui-crumbs-action .phui-crumbs-action-name { } .phui-crumbs-view.phui-crumbs-border { - border-bottom: 1px solid rgba(55,55,55,.1); + border-bottom: 1px solid rgba({$alphagrey},.1); } body .phui-crumbs-view + .phui-object-box { diff --git a/webroot/rsrc/css/phui/phui-document-pro.css b/webroot/rsrc/css/phui/phui-document-pro.css index 90f268cf66..138eec0955 100644 --- a/webroot/rsrc/css/phui/phui-document-pro.css +++ b/webroot/rsrc/css/phui/phui-document-pro.css @@ -140,7 +140,7 @@ a.button.phui-document-toc { .phui-document-view-pro-box .phui-timeline-view { padding: 16px 0 0 0; background: none; - border-top: 1px solid rgba(71, 87, 120, 0.20); + border-top: 1px solid rgba({$alphablue}, 0.20); } .phui-document-view-pro-box .phui-timeline-image { diff --git a/webroot/rsrc/css/phui/phui-document-summary.css b/webroot/rsrc/css/phui/phui-document-summary.css index 5b09bb9c7f..736efe9d20 100644 --- a/webroot/rsrc/css/phui/phui-document-summary.css +++ b/webroot/rsrc/css/phui/phui-document-summary.css @@ -4,7 +4,7 @@ .phui-document-summary-view { margin-top: 8px; - border-bottom: 1px solid rgba(55,55,55,.1); + border-bottom: 1px solid rgba({$alphagrey},.1); } .phui-document-summary-view.is-draft { diff --git a/webroot/rsrc/css/phui/phui-form.css b/webroot/rsrc/css/phui/phui-form.css index 52f41e4ab6..5c2ef38446 100644 --- a/webroot/rsrc/css/phui/phui-form.css +++ b/webroot/rsrc/css/phui/phui-form.css @@ -48,9 +48,9 @@ div.jx-tokenizer-container { background-color: #ffffff; border: 1px solid {$blueborder}; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-box-shadow: inset 0 1px 1px rgba({$alphablack}, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba({$alphablack}, 0.075); + box-shadow: inset 0 1px 1px rgba({$alphablack}, 0.075); -webkit-transition: border linear .05s, box-shadow linear .05s; -moz-transition: border linear .05s, box-shadow linear .05s; @@ -87,13 +87,13 @@ div.jx-tokenizer-container-focused { /* IE6-9 */ -webkit-box-shadow: - inset 0 1px 1px rgba(0,0,0,.075), + inset 0 1px 1px rgba({$alphablack},.075), 0 0 8px rgba(82,168,236,.6); -moz-box-shadow: - inset 0 1px 1px rgba(0,0,0,.075), + inset 0 1px 1px rgba({$alphablack},.075), 0 0 8px rgba(82,168,236,.6); box-shadow: - inset 0 1px 1px rgba(0,0,0,.075), + inset 0 1px 1px rgba({$alphablack},.075), 0 0 8px rgba(82,168,236,.6); } input[type="radio"], diff --git a/webroot/rsrc/css/phui/phui-header-view.css b/webroot/rsrc/css/phui/phui-header-view.css index 63852820df..5dc0586667 100644 --- a/webroot/rsrc/css/phui/phui-header-view.css +++ b/webroot/rsrc/css/phui/phui-header-view.css @@ -146,7 +146,7 @@ body .phui-header-shell.phui-bleed-header display: block; position: absolute; left: 0; - background: rgba(0,0,0,0.4); + background: rgba({$alphablack},0.4); color: #fff; font-weight: normal; bottom: 4px; @@ -179,7 +179,7 @@ body .phui-header-shell.phui-bleed-header .phui-header-subheader .phui-header-status { padding: 3px 9px; border-radius: 3px; - background: rgba(71, 87, 120, 0.06); + background: rgba({$alphablue}, 0.06); margin-right: 8px; } diff --git a/webroot/rsrc/css/phui/phui-image-mask.css b/webroot/rsrc/css/phui/phui-image-mask.css index a8b2212ce4..70350aa488 100644 --- a/webroot/rsrc/css/phui/phui-image-mask.css +++ b/webroot/rsrc/css/phui/phui-image-mask.css @@ -17,7 +17,7 @@ } .phui-image-mask-mask { - border: 300px solid rgba(0, 0, 0, 0.5); + border: 300px solid rgba({$alphablack}, 0.5); background-clip: content-box; position: absolute; } diff --git a/webroot/rsrc/css/phui/phui-info-view.css b/webroot/rsrc/css/phui/phui-info-view.css index cb7c7e77ba..59405995a5 100644 --- a/webroot/rsrc/css/phui/phui-info-view.css +++ b/webroot/rsrc/css/phui/phui-info-view.css @@ -24,7 +24,7 @@ .phui-info-view-body tt { padding: 0 2px; - background-color: rgba(55,55,55,.1); + background-color: rgba({$alphagrey},.1); } .phui-info-view-actions { diff --git a/webroot/rsrc/css/phui/phui-profile-menu.css b/webroot/rsrc/css/phui/phui-profile-menu.css index 76a51d9130..80e0c04691 100644 --- a/webroot/rsrc/css/phui/phui-profile-menu.css +++ b/webroot/rsrc/css/phui/phui-profile-menu.css @@ -34,7 +34,7 @@ .phui-profile-menu .phabricator-side-menu { background: #525867; - box-shadow: inset -2px 0 2px rgba(0, 0, 0, 0.150); + box-shadow: inset -2px 0 2px rgba({$alphablack}, 0.150); width: 240px; } @@ -107,7 +107,7 @@ .device-desktop .phui-profile-menu .phabricator-side-menu .phui-list-item-href:hover { - background-color: rgba(0,0,0,0.15); + background-color: rgba({$alphablack},0.15); color: {$menu.profile.text.selected}; } @@ -122,18 +122,18 @@ .phui-profile-menu .phabricator-side-menu .phui-list-item-selected .phui-list-item-href { - background-color: rgba(0,0,0,0.3); + background-color: rgba({$alphablack},0.3); color: {$menu.profile.text.selected}; } .phui-profile-menu .phabricator-side-menu .phui-list-item-selected .phui-list-item-href:hover { - background-color: rgba(0,0,0,0.45); + background-color: rgba({$alphablack},0.45); } .phui-profile-menu .phabricator-side-menu .phui-divider { margin: 4px 0; - border-bottom: 1px solid rgba(0, 0, 0, 0.2); + border-bottom: 1px solid rgba({$alphablack}, 0.2); } .phui-profile-menu .phabricator-side-menu .phui-motivator { diff --git a/webroot/rsrc/css/phui/phui-tag-view.css b/webroot/rsrc/css/phui/phui-tag-view.css index c6bb18ed0a..a68ed19484 100644 --- a/webroot/rsrc/css/phui/phui-tag-view.css +++ b/webroot/rsrc/css/phui/phui-tag-view.css @@ -16,7 +16,7 @@ a.phui-tag-view:hover { .phui-tag-core-closed { text-decoration: line-through; - color: rgba(0,0,0,0.5); + color: rgba({$alphablack},0.5); } .phui-tag-core-closed:hover { diff --git a/webroot/rsrc/css/phui/workboards/phui-workboard-color.css b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css index 8d5584d903..d7f95c841c 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workboard-color.css +++ b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css @@ -7,14 +7,14 @@ } .phui-workboard-color .phui-crumbs-view { - background-color: rgba(55,55,55,.15); + background-color: rgba({$alphagrey},.15); border: none; - color: rgba(255,255,255,.8); + color: rgba({$alphawhite},.8); } .phui-workboard-color .phui-crumbs-view a, .phui-workboard-color .phui-crumbs-view .phui-icon-view { - color: rgba(255,255,255,.85); + color: rgba({$alphawhite},.85); } .phui-workboard-color .phui-crumbs-view a:hover, @@ -23,11 +23,11 @@ } .phui-workboard-color .phuix-dropdown-menu { - background-color: rgba(255,255,255,.9); + background-color: rgba({$alphawhite},.9); } .phui-workboard-color .phui-workpanel-view .phui-box-grey { - background-color: rgba(255,255,255,.6); + background-color: rgba({$alphawhite},.6); } .phui-workboard-color-preview { diff --git a/webroot/rsrc/css/phui/workboards/phui-workcard.css b/webroot/rsrc/css/phui/workboards/phui-workcard.css index d35ac11628..f3d92d63e5 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workcard.css +++ b/webroot/rsrc/css/phui/workboards/phui-workcard.css @@ -42,7 +42,7 @@ } .phui-object-item-disabled.phui-workcard { - background-color: rgba(255,255,255,.67); + background-color: rgba({$alphawhite},.67); } .phui-object-item-disabled.phui-workcard .phui-object-item-link { diff --git a/webroot/rsrc/css/phui/workboards/phui-workpanel.css b/webroot/rsrc/css/phui/workboards/phui-workpanel.css index 86c48bb7dd..899c6f74eb 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workpanel.css +++ b/webroot/rsrc/css/phui/workboards/phui-workpanel.css @@ -36,7 +36,7 @@ } .phui-workpanel-view .phui-box-grey { - background-color: rgba(71,87,120,0.1); + background-color: rgba({$alphablue},0.1); } .phui-workpanel-view.phui-workboard-column-milestone .phui-box-grey { @@ -106,12 +106,12 @@ .device-desktop .phui-workpanel-body-content::-webkit-scrollbar { height: 8px; width: 8px; - background: rgba(71,87,120,0.2); + background: rgba({$alphablue},0.2); border-radius: 4px; } .device-desktop .phui-workpanel-body-content::-webkit-scrollbar-thumb { - background: rgba(71,87,120,0.4); + background: rgba({$alphablue},0.4); border-radius: 4px; } @@ -127,7 +127,7 @@ } .project-panel-empty .phui-object-item-list-view.drag-target-list { - background: rgba(255,255,255,.7); + background: rgba({$alphawhite},.7); } .phui-workpanel-view.project-panel-over-limit .phui-header-header { From 376c85a828c8af581c4695699f3d02bc5c70ff7e Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 Feb 2016 10:11:12 -0800 Subject: [PATCH 15/42] Make subproject/milestone watch rules work better Summary: Ref T10349. These got sort of half-weirded-up before I separated subscriptions and watching fully. New rules are: - You can watch whatever you want. - Watching a parent watches everything inside it. - If you're watching "Stonework" and go to "Stonework > Masonry", you'll see a "Watching Ancestor" hint to let you know you're already watching a parent or ancestor. Test Plan: - Watched and unwatched "Stonework". - Watched and unwatched "Stonework > Iteration IV". - While watching "Stonework", visited "Iteration IV" and saw "Watching Ancestor" hint. - Created a task tagged "Stonework > Iteration IV". Got notified about it because I watch "Stonework". Reviewers: chad Reviewed By: chad Maniphest Tasks: T10349 Differential Revision: https://secure.phabricator.com/D15280 --- .../PhabricatorProjectProfileController.php | 22 ++++++++-- .../PhabricatorProjectWatchController.php | 34 +++++++++++++-- .../project/query/PhabricatorProjectQuery.php | 12 +++--- .../project/storage/PhabricatorProject.php | 41 +++++++++++++++++++ ...PhabricatorSubscriptionsEditController.php | 22 ++-------- ...habricatorApplicationTransactionEditor.php | 17 +++++--- 6 files changed, 111 insertions(+), 37 deletions(-) diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php index 0a9bc602d2..81a172ae42 100644 --- a/src/applications/project/controller/PhabricatorProjectProfileController.php +++ b/src/applications/project/controller/PhabricatorProjectProfileController.php @@ -162,19 +162,32 @@ final class PhabricatorProjectProfileController private function renderWatchAction(PhabricatorProject $project) { $viewer = $this->getViewer(); - $viewer_phid = $viewer->getPHID(); $id = $project->getID(); - $is_watcher = ($viewer_phid && $project->isUserWatcher($viewer_phid)); + if (!$viewer->isLoggedIn()) { + $is_watcher = false; + $is_ancestor = false; + } else { + $viewer_phid = $viewer->getPHID(); + $is_watcher = $project->isUserWatcher($viewer_phid); + $is_ancestor = $project->isUserAncestorWatcher($viewer_phid); + } - if (!$is_watcher) { + if ($is_ancestor && !$is_watcher) { + $watch_icon = 'fa-eye'; + $watch_text = pht('Watching Ancestor'); + $watch_href = "/project/watch/{$id}/?via=profile"; + $watch_disabled = true; + } else if (!$is_watcher) { $watch_icon = 'fa-eye'; $watch_text = pht('Watch Project'); $watch_href = "/project/watch/{$id}/?via=profile"; + $watch_disabled = false; } else { $watch_icon = 'fa-eye-slash'; $watch_text = pht('Unwatch Project'); $watch_href = "/project/unwatch/{$id}/?via=profile"; + $watch_disabled = false; } $watch_icon = id(new PHUIIconView()) @@ -185,7 +198,8 @@ final class PhabricatorProjectProfileController ->setWorkflow(true) ->setIcon($watch_icon) ->setText($watch_text) - ->setHref($watch_href); + ->setHref($watch_href) + ->setDisabled($watch_disabled); } private function buildMilestoneList(PhabricatorProject $project) { diff --git a/src/applications/project/controller/PhabricatorProjectWatchController.php b/src/applications/project/controller/PhabricatorProjectWatchController.php index 9624a0b730..00117768e1 100644 --- a/src/applications/project/controller/PhabricatorProjectWatchController.php +++ b/src/applications/project/controller/PhabricatorProjectWatchController.php @@ -25,6 +25,23 @@ final class PhabricatorProjectWatchController $done_uri = "/project/members/{$id}/"; } + $is_watcher = $project->isUserWatcher($viewer->getPHID()); + $is_ancestor = $project->isUserAncestorWatcher($viewer->getPHID()); + if ($is_ancestor && !$is_watcher) { + $ancestor_phid = $project->getWatchedAncestorPHID($viewer->getPHID()); + $handles = $viewer->loadHandles(array($ancestor_phid)); + $ancestor_handle = $handles[$ancestor_phid]; + + return $this->newDialog() + ->setTitle(pht('Watching Ancestor')) + ->appendParagraph( + pht( + 'You are already watching %s, an ancestor of this project, and '. + 'are thus watching all of its subprojects.', + $ancestor_handle->renderTag()->render())) + ->addCancelbutton($done_uri); + } + if ($request->isDialogFormPost()) { $edge_action = null; switch ($action) { @@ -61,10 +78,14 @@ final class PhabricatorProjectWatchController switch ($action) { case 'watch': $title = pht('Watch Project?'); - $body = pht( + $body = array(); + $body[] = pht( 'Watching a project will let you monitor it closely. You will '. 'receive email and notifications about changes to every object '. - 'associated with projects you watch.'); + 'tagged with projects you watch.'); + $body[] = pht( + 'Watching a project also watches all subprojects and milestones of '. + 'that project.'); $submit = pht('Watch Project'); break; case 'unwatch': @@ -78,12 +99,17 @@ final class PhabricatorProjectWatchController return new Aphront404Response(); } - return $this->newDialog() + $dialog = $this->newDialog() ->setTitle($title) ->addHiddenInput('via', $via) - ->appendParagraph($body) ->addCancelButton($done_uri) ->addSubmitButton($submit); + + foreach ((array)$body as $paragraph) { + $dialog->appendParagraph($paragraph); + } + + return $dialog; } } diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php index b72fa1cec2..9ce5fff77c 100644 --- a/src/applications/project/query/PhabricatorProjectQuery.php +++ b/src/applications/project/query/PhabricatorProjectQuery.php @@ -247,7 +247,7 @@ final class PhabricatorProjectQuery $all_graph = $this->getAllReachableAncestors($projects); - if ($this->needAncestorMembers) { + if ($this->needAncestorMembers || $this->needWatchers) { $src_projects = $all_graph; } else { $src_projects = $projects; @@ -255,11 +255,13 @@ final class PhabricatorProjectQuery $all_sources = array(); foreach ($src_projects as $project) { + // For milestones, we need parent members. if ($project->isMilestone()) { - $phid = $project->getParentProjectPHID(); - } else { - $phid = $project->getPHID(); + $parent_phid = $project->getParentProjectPHID(); + $all_sources[$parent_phid] = $parent_phid; } + + $phid = $project->getPHID(); $all_sources[$phid] = $phid; } @@ -318,7 +320,7 @@ final class PhabricatorProjectQuery if ($this->needWatchers) { $watcher_phids = $edge_query->getDestinationPHIDs( - $source_phids, + array($project_phid), array($watcher_type)); $project->attachWatcherPHIDs($watcher_phids); $project->setIsUserWatcher( diff --git a/src/applications/project/storage/PhabricatorProject.php b/src/applications/project/storage/PhabricatorProject.php index 81a5c09dc9..061886f6f0 100644 --- a/src/applications/project/storage/PhabricatorProject.php +++ b/src/applications/project/storage/PhabricatorProject.php @@ -287,6 +287,32 @@ final class PhabricatorProject extends PhabricatorProjectDAO return $this->assertAttachedKey($this->sparseWatchers, $user_phid); } + public function isUserAncestorWatcher($user_phid) { + $is_watcher = $this->isUserWatcher($user_phid); + + if (!$is_watcher) { + $parent = $this->getParentProject(); + if ($parent) { + return $parent->isUserWatcher($user_phid); + } + } + + return $is_watcher; + } + + public function getWatchedAncestorPHID($user_phid) { + if ($this->isUserWatcher($user_phid)) { + return $this->getPHID(); + } + + $parent = $this->getParentProject(); + if ($parent) { + return $parent->getWatchedAncestorPHID($user_phid); + } + + return null; + } + public function setIsUserWatcher($user_phid, $is_watcher) { if ($this->sparseWatchers === self::ATTACHABLE) { $this->sparseWatchers = array(); @@ -304,6 +330,21 @@ final class PhabricatorProject extends PhabricatorProjectDAO return $this->assertAttached($this->watcherPHIDs); } + public function getAllAncestorWatcherPHIDs() { + $parent = $this->getParentProject(); + if ($parent) { + $watchers = $parent->getAllAncestorWatcherPHIDs(); + } else { + $watchers = array(); + } + + foreach ($this->getWatcherPHIDs() as $phid) { + $watchers[$phid] = $phid; + } + + return $watchers; + } + public function attachSlugs(array $slugs) { $this->slugs = $slugs; return $this; diff --git a/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php b/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php index ad089d8f3d..add833c127 100644 --- a/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php +++ b/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php @@ -28,24 +28,10 @@ final class PhabricatorSubscriptionsEditController ->withPHIDs(array($phid)) ->executeOne(); - if (phid_get_type($phid) == PhabricatorProjectProjectPHIDType::TYPECONST) { - // TODO: This is a big hack, but a weak argument for adding some kind - // of "load for role" feature to ObjectQuery, and also not a really great - // argument for adding some kind of "load extra stuff" feature to - // SubscriberInterface. Do this for now and wait for the best way forward - // to become more clear? - - $object = id(new PhabricatorProjectQuery()) - ->setViewer($viewer) - ->withPHIDs(array($phid)) - ->needWatchers(true) - ->executeOne(); - } else { - $object = id(new PhabricatorObjectQuery()) - ->setViewer($viewer) - ->withPHIDs(array($phid)) - ->executeOne(); - } + $object = id(new PhabricatorObjectQuery()) + ->setViewer($viewer) + ->withPHIDs(array($phid)) + ->executeOne(); if (!($object instanceof PhabricatorSubscribableInterface)) { return $this->buildErrorResponse( diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index d6f70cac39..cbfaebb182 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -2607,14 +2607,19 @@ abstract class PhabricatorApplicationTransactionEditor PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); if ($project_phids) { - $watcher_type = PhabricatorObjectHasWatcherEdgeType::EDGECONST; + $projects = id(new PhabricatorProjectQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withPHIDs($project_phids) + ->needWatchers(true) + ->execute(); - $query = id(new PhabricatorEdgeQuery()) - ->withSourcePHIDs($project_phids) - ->withEdgeTypes(array($watcher_type)); - $query->execute(); + $watcher_phids = array(); + foreach ($projects as $project) { + foreach ($project->getAllAncestorWatcherPHIDs() as $phid) { + $watcher_phids[$phid] = $phid; + } + } - $watcher_phids = $query->getDestinationPHIDs(); if ($watcher_phids) { // We need to do a visibility check for all the watchers, as // watching a project is not a guarantee that you can see objects From f35509e30ee76bdd86c9d54dfe6ac6a528505022 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 16 Feb 2016 13:52:12 -0800 Subject: [PATCH 16/42] Update to use PHUIRemarkupView everywhere possible Summary: Moves all the one off object calls to PHUIRemarkupView, adds a "Document" call as well (future plans). Test Plan: Visited most pages I could get access to, but may want extra careful eyes on this diff. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15281 --- ...PhabricatorCalendarEventViewController.php | 6 +---- .../PhabricatorConfigWelcomeController.php | 26 +++++-------------- .../PhabricatorCountdownViewController.php | 6 +---- .../DifferentialRevertPlanField.php | 7 +---- .../customfield/DifferentialSummaryField.php | 7 +---- .../customfield/DifferentialTestPlanField.php | 7 +---- .../DiffusionRepositoryController.php | 5 +--- .../DiffusionRepositoryEditMainController.php | 5 +--- .../controller/DivinerBookController.php | 6 +---- .../controller/DivinerMainController.php | 6 +---- .../option/PhabricatorAsanaConfigOptions.php | 10 ++----- .../FundInitiativeViewController.php | 12 ++------- .../HarbormasterBuildViewController.php | 10 ++----- .../LegalpadDocumentSignController.php | 6 +---- ...ricatorApplicationDetailViewController.php | 9 +++---- ...atorApplicationEmailCommandsController.php | 5 +--- .../NuancePhabricatorFormSourceDefinition.php | 6 +---- .../PhabricatorOwnersDetailController.php | 7 ++--- .../blog/PhameBlogManageController.php | 5 +--- .../controller/PholioInlineController.php | 7 ++--- .../controller/PhortuneCartController.php | 7 +---- .../PhortuneMerchantViewController.php | 6 +---- .../PhabricatorPhurlURLViewController.php | 10 +++---- .../PhabricatorSlowvotePollController.php | 14 +++++----- .../slowvote/view/SlowvoteEmbedView.php | 10 +++---- .../PhabricatorSpacesViewController.php | 7 +---- ...icationTransactionCommentRawController.php | 5 +--- ...ricatorTypeaheadFunctionHelpController.php | 6 +---- .../examples/PhabricatorRemarkupUIExample.php | 5 +--- ...PhabricatorStandardCustomFieldRemarkup.php | 7 +---- .../markup/view/PHUIRemarkupView.php | 21 ++++++++++++++- src/view/form/AphrontFormView.php | 6 ++--- src/view/form/PHUIFormLayoutView.php | 9 +++---- src/view/form/PHUIFormPageView.php | 7 ++--- 34 files changed, 81 insertions(+), 197 deletions(-) diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php index f423860a3b..d494fa7a0b 100644 --- a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php @@ -363,11 +363,7 @@ final class PhabricatorCalendarEventViewController ->getIconLabel($event->getIcon())); if (strlen($event->getDescription())) { - $description = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($event->getDescription()), - 'default', - $viewer); - + $description = new PHUIRemarkupView($viewer, $event->getDescription()); $properties->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); diff --git a/src/applications/config/controller/PhabricatorConfigWelcomeController.php b/src/applications/config/controller/PhabricatorConfigWelcomeController.php index e2d868082a..e2d704e0b7 100644 --- a/src/applications/config/controller/PhabricatorConfigWelcomeController.php +++ b/src/applications/config/controller/PhabricatorConfigWelcomeController.php @@ -341,23 +341,14 @@ final class PhabricatorConfigWelcomeController $header = id(new PHUIHeaderView()) ->setHeader(pht('Welcome to Phabricator')); - $setup_header = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setContent(pht('=Setup and Configuration')), - 'default', - $viewer); + $setup_header = new PHUIRemarkupView( + $viewer, pht('=Setup and Configuration')); - $explore_header = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setContent(pht('=Explore Phabricator')), - 'default', - $viewer); + $explore_header = new PHUIRemarkupView( + $viewer, pht('=Explore Phabricator')); - $quick_header = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setContent(pht('=Quick Start Guides')), - 'default', - $viewer); + $quick_header = new PHUIRemarkupView( + $viewer, pht('=Quick Start Guide')); return id(new PHUIDocumentView()) ->setHeader($header) @@ -376,10 +367,7 @@ final class PhabricatorConfigWelcomeController $icon = id(new PHUIIconView()) ->setIcon($icon.' fa-2x'); - $content = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($content), - 'default', - $viewer); + $content = new PHUIRemarkupView($viewer, $content); $icon = phutil_tag( 'div', diff --git a/src/applications/countdown/controller/PhabricatorCountdownViewController.php b/src/applications/countdown/controller/PhabricatorCountdownViewController.php index 9cd0d982ff..401e159c5f 100644 --- a/src/applications/countdown/controller/PhabricatorCountdownViewController.php +++ b/src/applications/countdown/controller/PhabricatorCountdownViewController.php @@ -132,11 +132,7 @@ final class PhabricatorCountdownViewController $description = $countdown->getDescription(); if (strlen($description)) { - $description = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($description), - 'default', - $viewer); - + $description = new PHUIRemarkupView($viewer, $description); $view->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); diff --git a/src/applications/differential/customfield/DifferentialRevertPlanField.php b/src/applications/differential/customfield/DifferentialRevertPlanField.php index 1d86a794a4..646116c74e 100644 --- a/src/applications/differential/customfield/DifferentialRevertPlanField.php +++ b/src/applications/differential/customfield/DifferentialRevertPlanField.php @@ -44,12 +44,7 @@ final class DifferentialRevertPlanField return null; } - return PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setPreserveLinebreaks(true) - ->setContent($this->getValue()), - 'default', - $this->getViewer()); + return new PHUIRemarkupView($this->getViewer(), $this->getValue()); } public function shouldAppearInGlobalSearch() { diff --git a/src/applications/differential/customfield/DifferentialSummaryField.php b/src/applications/differential/customfield/DifferentialSummaryField.php index 16a3f701c1..5052e238e0 100644 --- a/src/applications/differential/customfield/DifferentialSummaryField.php +++ b/src/applications/differential/customfield/DifferentialSummaryField.php @@ -122,12 +122,7 @@ final class DifferentialSummaryField return null; } - return PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setPreserveLinebreaks(true) - ->setContent($this->getValue()), - 'default', - $this->getViewer()); + return new PHUIRemarkupView($this->getViewer(), $this->getValue()); } public function getApplicationTransactionRemarkupBlocks( diff --git a/src/applications/differential/customfield/DifferentialTestPlanField.php b/src/applications/differential/customfield/DifferentialTestPlanField.php index 32d3c15c31..6a1029dba4 100644 --- a/src/applications/differential/customfield/DifferentialTestPlanField.php +++ b/src/applications/differential/customfield/DifferentialTestPlanField.php @@ -136,12 +136,7 @@ final class DifferentialTestPlanField return null; } - return PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setPreserveLinebreaks(true) - ->setContent($this->getValue()), - 'default', - $this->getViewer()); + return new PHUIRemarkupView($this->getViewer(), $this->getValue()); } public function getApplicationTransactionRemarkupBlocks( diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php index 18989818b6..6504ee50e1 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php @@ -290,10 +290,7 @@ final class DiffusionRepositoryController extends DiffusionController { $description = $repository->getDetail('description'); if (strlen($description)) { - $description = PhabricatorMarkupEngine::renderOneObject( - $repository, - 'description', - $user); + $description = new PHUIRemarkupView($user, $description); $view->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); $view->addTextContent($description); diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php index 886809dedd..4efafc1a0e 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php @@ -309,10 +309,7 @@ final class DiffusionRepositoryEditMainController if (!strlen($description)) { $description = phutil_tag('em', array(), pht('No description provided.')); } else { - $description = PhabricatorMarkupEngine::renderOneObject( - $repository, - 'description', - $viewer); + $description = new PHUIRemarkupView($viewer, $description); } $view->addTextContent($description); diff --git a/src/applications/diviner/controller/DivinerBookController.php b/src/applications/diviner/controller/DivinerBookController.php index 0c84955b2f..11d6a5bbfc 100644 --- a/src/applications/diviner/controller/DivinerBookController.php +++ b/src/applications/diviner/controller/DivinerBookController.php @@ -94,11 +94,7 @@ final class DivinerBookController extends DivinerController { $preface = $book->getPreface(); $preface_view = null; if (strlen($preface)) { - $preface_view = - PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($preface), - 'default', - $viewer); + $preface_view = new PHUIRemarkupView($viewer, $preface); } $document->appendChild($preface_view); diff --git a/src/applications/diviner/controller/DivinerMainController.php b/src/applications/diviner/controller/DivinerMainController.php index 97149778c1..e85769060f 100644 --- a/src/applications/diviner/controller/DivinerMainController.php +++ b/src/applications/diviner/controller/DivinerMainController.php @@ -61,11 +61,7 @@ final class DivinerMainController extends DivinerController { " %s\n\n", 'phabricator/ $ ./bin/diviner generate'); - $text = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($text), - 'default', - $viewer); - + $text = new PHUIRemarkupView($viewer, $text); $document->appendChild($text); } diff --git a/src/applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php b/src/applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php index 991392f484..1771a6615e 100644 --- a/src/applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php +++ b/src/applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php @@ -111,10 +111,7 @@ final class PhabricatorAsanaConfigOptions "The Asana Workspaces your linked account has access to are:\n\n%s", $out); - return PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($out), - 'default', - $viewer); + return new PHUIRemarkupView($viewer, $out); } private function renderContextualProjectDescription( @@ -155,10 +152,7 @@ final class PhabricatorAsanaConfigOptions $out = implode("\n", $out); - return PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($out), - 'default', - $viewer); + return new PHUIRemarkupView($viewer, $out); } } diff --git a/src/applications/fund/controller/FundInitiativeViewController.php b/src/applications/fund/controller/FundInitiativeViewController.php index 71cebc842d..6f780e4e72 100644 --- a/src/applications/fund/controller/FundInitiativeViewController.php +++ b/src/applications/fund/controller/FundInitiativeViewController.php @@ -98,11 +98,7 @@ final class FundInitiativeViewController $description = $initiative->getDescription(); if (strlen($description)) { - $description = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($description), - 'default', - $viewer); - + $description = new PHUIRemarkupView($viewer, $description); $view->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); $view->addTextContent($description); @@ -110,11 +106,7 @@ final class FundInitiativeViewController $risks = $initiative->getRisks(); if (strlen($risks)) { - $risks = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($risks), - 'default', - $viewer); - + $risks = new PHUIRemarkupView($viewer, $risks); $view->addSectionHeader( pht('Risks/Challenges'), 'fa-ambulance'); $view->addTextContent($risks); diff --git a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php index d197174175..a9cbb43ccf 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php @@ -181,16 +181,10 @@ final class HarbormasterBuildViewController if ($step) { $description = $step->getDescription(); if ($description) { - $rendered = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setContent($description) - ->setPreserveLinebreaks(true), - 'default', - $viewer); - + $description = new PHUIRemarkupView($viewer, $description); $properties->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); - $properties->addTextContent($rendered); + $properties->addTextContent($description); } } else { $target_box->setFormErrors( diff --git a/src/applications/legalpad/controller/LegalpadDocumentSignController.php b/src/applications/legalpad/controller/LegalpadDocumentSignController.php index 2221ae4499..00549c1adc 100644 --- a/src/applications/legalpad/controller/LegalpadDocumentSignController.php +++ b/src/applications/legalpad/controller/LegalpadDocumentSignController.php @@ -258,11 +258,7 @@ final class LegalpadDocumentSignController extends LegalpadController { $preamble_box = null; if (strlen($document->getPreamble())) { - $preamble_text = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent( - $document->getPreamble()), - 'default', - $viewer); + $preamble_text = new PHUIRemarkupView($viewer, $document->getPreamble()); // NOTE: We're avoiding `setObject()` here so we don't pick up extra UI // elements like "Subscribers". This information is available on the diff --git a/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php b/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php index 0ef4b83abd..19aad0e6a5 100644 --- a/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php +++ b/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php @@ -104,14 +104,11 @@ final class PhabricatorApplicationDetailViewController } $overview = $application->getOverview(); - if ($overview) { + if (strlen($overview)) { + $overview = new PHUIRemarkupView($viewer, $overview); $properties->addSectionHeader( pht('Overview'), PHUIPropertyListView::ICON_SUMMARY); - $properties->addTextContent( - PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($overview), - 'default', - $viewer)); + $properties->addTextContent($overview); } $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions( diff --git a/src/applications/meta/controller/PhabricatorApplicationEmailCommandsController.php b/src/applications/meta/controller/PhabricatorApplicationEmailCommandsController.php index 060ca951ba..08f8e450b5 100644 --- a/src/applications/meta/controller/PhabricatorApplicationEmailCommandsController.php +++ b/src/applications/meta/controller/PhabricatorApplicationEmailCommandsController.php @@ -117,10 +117,7 @@ final class PhabricatorApplicationEmailCommandsController $crumbs->addTextCrumb($title); $crumbs->setBorder(true); - $content_box = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($content), - 'default', - $viewer); + $content_box = new PHUIRemarkupView($viewer, $content); $info_view = null; if (!PhabricatorEnv::getEnvConfig('metamta.reply-handler-domain')) { diff --git a/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php b/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php index 675b2634a9..cca5a19cd9 100644 --- a/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php +++ b/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php @@ -120,11 +120,7 @@ final class NuancePhabricatorFormSourceDefinition PHUIPropertyListView $view) { $complaint = $item->getNuanceProperty('complaint'); - $complaint = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($complaint), - 'default', - $viewer); - + $complaint = new PHUIRemarkupView($viewer, $complaint); $view->addSectionHeader( pht('Complaint'), 'fa-exclamation-circle'); $view->addTextContent($complaint); diff --git a/src/applications/owners/controller/PhabricatorOwnersDetailController.php b/src/applications/owners/controller/PhabricatorOwnersDetailController.php index e361868ce3..fc09508bfe 100644 --- a/src/applications/owners/controller/PhabricatorOwnersDetailController.php +++ b/src/applications/owners/controller/PhabricatorOwnersDetailController.php @@ -189,13 +189,10 @@ final class PhabricatorOwnersDetailController $description = $package->getDescription(); if (strlen($description)) { + $description = new PHUIRemarkupView($viewer, $description); $view->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); - $view->addTextContent( - $output = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($description), - 'default', - $viewer)); + $view->addTextContent($description); } $view->invokeWillRenderEvent(); diff --git a/src/applications/phame/controller/blog/PhameBlogManageController.php b/src/applications/phame/controller/blog/PhameBlogManageController.php index 0402245d41..14caafaab4 100644 --- a/src/applications/phame/controller/blog/PhameBlogManageController.php +++ b/src/applications/phame/controller/blog/PhameBlogManageController.php @@ -118,10 +118,7 @@ final class PhameBlogManageController extends PhameBlogController { $properties->invokeWillRenderEvent(); if (strlen($blog->getDescription())) { - $description = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($blog->getDescription()), - 'default', - $viewer); + $description = new PHUIRemarkupView($viewer, $description); $properties->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); diff --git a/src/applications/pholio/controller/PholioInlineController.php b/src/applications/pholio/controller/PholioInlineController.php index 101ef9e758..ddced1f9eb 100644 --- a/src/applications/pholio/controller/PholioInlineController.php +++ b/src/applications/pholio/controller/PholioInlineController.php @@ -86,16 +86,13 @@ final class PholioInlineController extends PholioController { ), $author_handle->renderLink()); + $inline_content = new PHUIRemarkupView($viewer, $inline->getContent()); $comment_body = phutil_tag( 'div', array( 'class' => 'pholio-inline-comment-body', ), - PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setContent($inline->getContent()), - 'default', - $viewer)); + $inline_content); return $this->newDialog() ->setTitle(pht('Inline Comment')) diff --git a/src/applications/phortune/controller/PhortuneCartController.php b/src/applications/phortune/controller/PhortuneCartController.php index d3e19f52f1..9fefc7db6e 100644 --- a/src/applications/phortune/controller/PhortuneCartController.php +++ b/src/applications/phortune/controller/PhortuneCartController.php @@ -48,12 +48,7 @@ abstract class PhortuneCartController return null; } - $output = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setPreserveLinebreaks(true) - ->setContent($description), - 'default', - $this->getViewer()); + $output = new PHUIRemarkupView($this->getUser(), $description); $box = id(new PHUIBoxView()) ->addMargin(PHUI::MARGIN_LARGE) diff --git a/src/applications/phortune/controller/PhortuneMerchantViewController.php b/src/applications/phortune/controller/PhortuneMerchantViewController.php index 55bb136dab..1921f0c5a5 100644 --- a/src/applications/phortune/controller/PhortuneMerchantViewController.php +++ b/src/applications/phortune/controller/PhortuneMerchantViewController.php @@ -136,11 +136,7 @@ final class PhortuneMerchantViewController $description = $merchant->getDescription(); if (strlen($description)) { - $description = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($description), - 'default', - $viewer); - + $description = new PHUIRemarkupView($viewer, $description); $view->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); $view->addTextContent($description); diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php b/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php index 51abcd8059..dbc7fbcc79 100644 --- a/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php +++ b/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php @@ -134,16 +134,12 @@ final class PhabricatorPhurlURLViewController $properties->invokeWillRenderEvent(); - if (strlen($url->getDescription())) { - $description = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($url->getDescription()), - 'default', - $viewer); - + $description = $url->getDescription(); + if (strlen($description)) { + $description = new PHUIRemarkupView($viewer, $description); $properties->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); - $properties->addTextContent($description); } diff --git a/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php b/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php index 87de1ff660..fd05afb057 100644 --- a/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php +++ b/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php @@ -122,13 +122,13 @@ final class PhabricatorSlowvotePollController $view->invokeWillRenderEvent(); - if (strlen($poll->getDescription())) { - $view->addTextContent( - $output = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent( - $poll->getDescription()), - 'default', - $viewer)); + $description = $poll->getDescription(); + if (strlen($description)) { + $description = new PHUIRemarkupView($viewer, $description); + $view->addSectionHeader( + pht('Description'), + PHUIPropertyListView::ICON_SUMMARY); + $view->addTextContent($description); } return $view; diff --git a/src/applications/slowvote/view/SlowvoteEmbedView.php b/src/applications/slowvote/view/SlowvoteEmbedView.php index 0270e82f00..3582ea45b3 100644 --- a/src/applications/slowvote/view/SlowvoteEmbedView.php +++ b/src/applications/slowvote/view/SlowvoteEmbedView.php @@ -74,13 +74,9 @@ final class SlowvoteEmbedView extends AphrontView { $header = id(new PHUIHeaderView()) ->setHeader($link_to_slowvote); - $description = null; - if ($poll->getDescription()) { - $description = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent( - $poll->getDescription()), - 'default', - $this->getUser()); + $description = $poll->getDescription(); + if (strlen($description)) { + $description = new PHUIRemarkupView($this->getUser(), $description); $description = phutil_tag( 'div', array( diff --git a/src/applications/spaces/controller/PhabricatorSpacesViewController.php b/src/applications/spaces/controller/PhabricatorSpacesViewController.php index 7515b4dcc2..25bb70f646 100644 --- a/src/applications/spaces/controller/PhabricatorSpacesViewController.php +++ b/src/applications/spaces/controller/PhabricatorSpacesViewController.php @@ -83,15 +83,10 @@ final class PhabricatorSpacesViewController $description = $space->getDescription(); if (strlen($description)) { - $description = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($description), - 'default', - $viewer); - + $description = new PHUIRemarkupView($viewer, $description); $list->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); - $list->addTextContent($description); } diff --git a/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentRawController.php b/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentRawController.php index 6670c147fe..65b6282aa9 100644 --- a/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentRawController.php +++ b/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentRawController.php @@ -50,10 +50,7 @@ final class PhabricatorApplicationTransactionCommentRawController $details_text = pht( 'For full details, run `/bin/mail show-outbound --id %d`', $source_id); - $addendum = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($details_text), - 'default', - $viewer); + $addendum = new PHUIRemarkupView($viewer, $details_text); } } } diff --git a/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php b/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php index 0770bc6d3d..a771bf0747 100644 --- a/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php +++ b/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php @@ -121,11 +121,7 @@ final class PhabricatorTypeaheadFunctionHelpController } $content = implode("\n\n", $content); - - $content_box = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($content), - 'default', - $viewer); + $content_box = new PHUIRemarkupView($viewer, $content); $header = id(new PHUIHeaderView()) ->setHeader($title); diff --git a/src/applications/uiexample/examples/PhabricatorRemarkupUIExample.php b/src/applications/uiexample/examples/PhabricatorRemarkupUIExample.php index 3e338558fd..46f0262bbb 100644 --- a/src/applications/uiexample/examples/PhabricatorRemarkupUIExample.php +++ b/src/applications/uiexample/examples/PhabricatorRemarkupUIExample.php @@ -40,10 +40,7 @@ IMPORTANT: This is not really important. EOCONTENT ); - $remarkup = PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($content), - 'default', - $viewer); + $remarkup = new PHUIRemarkupView($viewer, $content); $frame = id(new PHUIBoxView()) ->addPadding(PHUI::PADDING_LARGE) diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php index be7db42004..7709233454 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php @@ -39,12 +39,7 @@ final class PhabricatorStandardCustomFieldRemarkup // end of the world. $viewer = $this->getViewer(); - return PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff()) - ->setContent($value) - ->setPReserveLinebreaks(true), - 'default', - $viewer); + return new PHUIRemarkupView($viewer, $value); } public function getApplicationTransactionTitle( diff --git a/src/infrastructure/markup/view/PHUIRemarkupView.php b/src/infrastructure/markup/view/PHUIRemarkupView.php index 8a11653589..8a8d9ddf7f 100644 --- a/src/infrastructure/markup/view/PHUIRemarkupView.php +++ b/src/infrastructure/markup/view/PHUIRemarkupView.php @@ -12,22 +12,41 @@ final class PHUIRemarkupView extends AphrontView { private $corpus; + private $markupType; + + const DOCUMENT = 'document'; public function __construct(PhabricatorUser $viewer, $corpus) { $this->setUser($viewer); $this->corpus = $corpus; } + private function setMarkupType($type) { + $this->markupType($type); + return $this; + } + public function render() { $viewer = $this->getUser(); $corpus = $this->corpus; - return PhabricatorMarkupEngine::renderOneObject( + $content = PhabricatorMarkupEngine::renderOneObject( id(new PhabricatorMarkupOneOff()) ->setPreserveLinebreaks(true) ->setContent($corpus), 'default', $viewer); + + if ($this->markupType == self::DOCUMENT) { + return phutil_tag( + 'div', + array( + 'class' => 'phabricator-remarkup phui-document-view', + ), + $content); + } + + return $content; } } diff --git a/src/view/form/AphrontFormView.php b/src/view/form/AphrontFormView.php index f9f281ff20..810208ffae 100644 --- a/src/view/form/AphrontFormView.php +++ b/src/view/form/AphrontFormView.php @@ -85,10 +85,8 @@ final class AphrontFormView extends AphrontView { public function appendRemarkupInstructions($remarkup) { return $this->appendInstructions( - PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($remarkup), - 'default', - $this->getUser())); + new PHUIRemarkupView($this->getUser(), $remarkup)); + } public function buildLayoutView() { diff --git a/src/view/form/PHUIFormLayoutView.php b/src/view/form/PHUIFormLayoutView.php index 1c5ccbb7f2..ffc0eb31d5 100644 --- a/src/view/form/PHUIFormLayoutView.php +++ b/src/view/form/PHUIFormLayoutView.php @@ -35,11 +35,10 @@ final class PHUIFormLayoutView extends AphrontView { throw new PhutilInvalidStateException('setUser'); } - return $this->appendInstructions( - PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($remarkup), - 'default', - $this->getUser())); + $viewer = $this->getUser(); + $instructions = new PHUIRemarkupView($viewer, $remarkup); + + return $this->appendInstructions($instructions); } public function render() { diff --git a/src/view/form/PHUIFormPageView.php b/src/view/form/PHUIFormPageView.php index cb62d33127..aeff9e38f5 100644 --- a/src/view/form/PHUIFormPageView.php +++ b/src/view/form/PHUIFormPageView.php @@ -74,11 +74,8 @@ class PHUIFormPageView extends AphrontView { } public function addRemarkupInstructions($remarkup, $before = null) { - return $this->addInstructions( - PhabricatorMarkupEngine::renderOneObject( - id(new PhabricatorMarkupOneOff())->setContent($remarkup), - 'default', - $this->getUser()), $before); + $remarkup = new PHUIRemarkupView($this->getUser(), $remarkup); + return $this->addInstructions($remarkup, $before); } public function addControl(AphrontFormControl $control) { From 58c2141ffdbf326275cdf63e1846e305dce457bb Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 Feb 2016 14:00:19 -0800 Subject: [PATCH 17/42] Fix limit calculation for largeish Mercurial repsositories Summary: Fixes T10304. In Mercurial, we must enumerate the whole file tree. Currently, we incorrectly count files within directories (which won't be shown) toward the "100 file" limit at top level, so directories with more than 100 subpaths are truncated improperly. This is approxiately the same as @richardvanvelzen's fix. Test Plan: Viewed a large Mercurial repository, saw a complete directory listing. Reviewers: chad Reviewed By: chad Subscribers: richardvanvelzen Maniphest Tasks: T10304 Differential Revision: https://secure.phabricator.com/D15282 --- .../conduit/DiffusionBrowseQueryConduitAPIMethod.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php index f32583708c..29059c283f 100644 --- a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php @@ -246,7 +246,16 @@ final class DiffusionBrowseQueryConduitAPIMethod DiffusionBrowseResultSet::REASON_IS_FILE); return $result; } + $parts = explode('/', $remainder); + $name = reset($parts); + + // If we've already seen this path component, we're looking at a file + // inside a directory we already processed. Just move on. + if (isset($results[$name])) { + continue; + } + if (count($parts) == 1) { $type = DifferentialChangeType::FILE_NORMAL; } else { @@ -254,7 +263,7 @@ final class DiffusionBrowseQueryConduitAPIMethod } if ($count >= $offset) { - $results[reset($parts)] = $type; + $results[$name] = $type; } $count++; From 71be2b06a8eaccd39ab3f5c68081d50b5a37b354 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 16 Feb 2016 14:27:05 -0800 Subject: [PATCH 18/42] Add Workboard UI Color to sidenav, fix fullscreen CSS Summary: Uses the background color changes to show also on the side nav. Places color on entire body so fullscreen doesn't show other body color. Test Plan: Review various workboard colors at normal and fullscreen Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15283 --- resources/celerity/map.php | 10 +++++----- .../PhabricatorProjectBoardViewController.php | 2 +- webroot/rsrc/css/phui/phui-profile-menu.css | 9 ++++++++- .../css/phui/workboards/phui-workboard-color.css | 12 ++++++++++++ 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 791572dac3..f1f53c7379 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ */ return array( 'names' => array( - 'core.pkg.css' => '7fd6b616', + 'core.pkg.css' => '7935f211', 'core.pkg.js' => 'd7daa6d8', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '2de124c9', @@ -146,7 +146,7 @@ return array( 'rsrc/css/phui/phui-object-item-list-view.css' => '18b2ce8e', 'rsrc/css/phui/phui-pager.css' => 'bea33d23', 'rsrc/css/phui/phui-pinboard-view.css' => '2495140e', - 'rsrc/css/phui/phui-profile-menu.css' => 'f42bedb6', + 'rsrc/css/phui/phui-profile-menu.css' => '7e92a89a', 'rsrc/css/phui/phui-property-list-view.css' => '27b2849e', 'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591', 'rsrc/css/phui/phui-segment-bar-view.css' => '46342871', @@ -155,7 +155,7 @@ return array( 'rsrc/css/phui/phui-tag-view.css' => '9d5d4400', 'rsrc/css/phui/phui-timeline-view.css' => '2efceff8', 'rsrc/css/phui/phui-two-column-view.css' => 'c75bfc5b', - 'rsrc/css/phui/workboards/phui-workboard-color.css' => '2f068cc8', + 'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7', 'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647', 'rsrc/css/phui/workboards/phui-workcard.css' => '3646fb96', 'rsrc/css/phui/workboards/phui-workpanel.css' => '92197373', @@ -832,7 +832,7 @@ return array( 'phui-object-item-list-view-css' => '18b2ce8e', 'phui-pager-css' => 'bea33d23', 'phui-pinboard-view-css' => '2495140e', - 'phui-profile-menu-css' => 'f42bedb6', + 'phui-profile-menu-css' => '7e92a89a', 'phui-property-list-view-css' => '27b2849e', 'phui-remarkup-preview-css' => '1a8f2591', 'phui-segment-bar-view-css' => '46342871', @@ -842,7 +842,7 @@ return array( 'phui-theme-css' => 'ab7b848c', 'phui-timeline-view-css' => '2efceff8', 'phui-two-column-view-css' => 'c75bfc5b', - 'phui-workboard-color-css' => '2f068cc8', + 'phui-workboard-color-css' => 'ac6fe6a7', 'phui-workboard-view-css' => 'e6d89647', 'phui-workcard-view-css' => '3646fb96', 'phui-workpanel-view-css' => '92197373', diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php index 9bc024c123..107d3dc0d0 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php @@ -456,7 +456,7 @@ final class PhabricatorProjectBoardViewController $background_color_class = "phui-workboard-{$background}"; $page->addClass('phui-workboard-color'); - $nav->addClass($background_color_class); + $page->addClass($background_color_class); } return $page; diff --git a/webroot/rsrc/css/phui/phui-profile-menu.css b/webroot/rsrc/css/phui/phui-profile-menu.css index 80e0c04691..30855f0a05 100644 --- a/webroot/rsrc/css/phui/phui-profile-menu.css +++ b/webroot/rsrc/css/phui/phui-profile-menu.css @@ -150,11 +150,18 @@ } .phui-profile-menu .phabricator-side-menu .phui-profile-menu-error { - color: {$greytext}; + color: rgba({$alphawhite}, 0.5); font-size: {$smallerfontsize}; padding: 18px 15px; } +.phui-profile-menu .phabricator-side-menu .phui-list-item-disabled + .phui-list-item-href, +.phui-profile-menu .phui-list-sidenav .phui-list-item-disabled + .phui-list-item-href { + color: rgba({$alphawhite}, 0.5); +} + .phui-profile-menu .phabricator-side-menu .phui-profile-segment-bar { color: {$menu.profile.text}; font-size: {$smallerfontsize}; diff --git a/webroot/rsrc/css/phui/workboards/phui-workboard-color.css b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css index d7f95c841c..d983622a99 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workboard-color.css +++ b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css @@ -30,6 +30,18 @@ background-color: rgba({$alphawhite},.6); } +body.phui-workboard-color .phui-profile-menu .phabricator-side-menu { + background-color: rgba({$alphagrey},.3); +} + +body.phui-workboard-color .phabricator-side-menu .phui-profile-menu-footer-1 { + background-color: transparent; +} + +.phui-workboard-color .phui-profile-menu .phabricator-side-menu { + box-shadow: none; +} + .phui-workboard-color-preview { width: 50px; height: 50px; From c6d91938e828b709cf8261c38210c7cc6c1b6887 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 Feb 2016 14:25:11 -0800 Subject: [PATCH 19/42] Show first 10 branches, then "More Branches" for commits on huge numbers of branches Summary: Fixes T9562. We already do this for tags, but didn't have similar logic for branches. Implement that logic. Test Plan: - Set limit to 1, saw "More branches", clicked it, got the correct results. - Verified that branch table with no specified commit still works properly. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9562 Differential Revision: https://secure.phabricator.com/D15284 --- .../DiffusionBranchTableController.php | 16 ++++++--- .../DiffusionCommitBranchesController.php | 36 ++++++++++++------- .../DiffusionCommitTagsController.php | 22 +++++------- 3 files changed, 42 insertions(+), 32 deletions(-) diff --git a/src/applications/diffusion/controller/DiffusionBranchTableController.php b/src/applications/diffusion/controller/DiffusionBranchTableController.php index a040c8084e..3a025e90c5 100644 --- a/src/applications/diffusion/controller/DiffusionBranchTableController.php +++ b/src/applications/diffusion/controller/DiffusionBranchTableController.php @@ -19,13 +19,19 @@ final class DiffusionBranchTableController extends DiffusionController { $pager = id(new PHUIPagerView()) ->readFromRequest($request); - // TODO: Add support for branches that contain commit + $params = array( + 'offset' => $pager->getOffset(), + 'limit' => $pager->getPageSize() + 1, + ); + + $contains = $drequest->getSymbolicCommit(); + if (strlen($contains)) { + $params['contains'] = $contains; + } + $branches = $this->callConduitWithDiffusionRequest( 'diffusion.branchquery', - array( - 'offset' => $pager->getOffset(), - 'limit' => $pager->getPageSize() + 1, - )); + $params); $branches = $pager->sliceResults($branches); $branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches); diff --git a/src/applications/diffusion/controller/DiffusionCommitBranchesController.php b/src/applications/diffusion/controller/DiffusionCommitBranchesController.php index 1e5cc0ac31..e698ff6fc7 100644 --- a/src/applications/diffusion/controller/DiffusionCommitBranchesController.php +++ b/src/applications/diffusion/controller/DiffusionCommitBranchesController.php @@ -15,20 +15,18 @@ final class DiffusionCommitBranchesController extends DiffusionController { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); - switch ($repository->getVersionControlSystem()) { - case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: - $branches = array(); - break; - default: - $branches = $this->callConduitWithDiffusionRequest( - 'diffusion.branchquery', - array( - 'contains' => $drequest->getCommit(), - )); - break; - } + $branch_limit = 10; + $branches = DiffusionRepositoryRef::loadAllFromDictionaries( + $this->callConduitWithDiffusionRequest( + 'diffusion.branchquery', + array( + 'contains' => $drequest->getCommit(), + 'limit' => $branch_limit + 1, + ))); + + $has_more_branches = (count($branches) > $branch_limit); + $branches = array_slice($branches, 0, $branch_limit); - $branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches); $branch_links = array(); foreach ($branches as $branch) { $branch_links[] = phutil_tag( @@ -43,6 +41,18 @@ final class DiffusionCommitBranchesController extends DiffusionController { $branch->getShortName()); } + if ($has_more_branches) { + $branch_links[] = phutil_tag( + 'a', + array( + 'href' => $drequest->generateURI( + array( + 'action' => 'branches', + )), + ), + pht("More Branches\xE2\x80\xA6")); + } + return id(new AphrontAjaxResponse()) ->setContent($branch_links ? implode(', ', $branch_links) : pht('None')); } diff --git a/src/applications/diffusion/controller/DiffusionCommitTagsController.php b/src/applications/diffusion/controller/DiffusionCommitTagsController.php index 0f825a9b64..aef73dd842 100644 --- a/src/applications/diffusion/controller/DiffusionCommitTagsController.php +++ b/src/applications/diffusion/controller/DiffusionCommitTagsController.php @@ -16,20 +16,14 @@ final class DiffusionCommitTagsController extends DiffusionController { $repository = $drequest->getRepository(); $tag_limit = 10; - switch ($repository->getVersionControlSystem()) { - case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: - $tags = array(); - break; - default: - $tags = DiffusionRepositoryTag::newFromConduit( - $this->callConduitWithDiffusionRequest( - 'diffusion.tagsquery', - array( - 'commit' => $drequest->getCommit(), - 'limit' => $tag_limit + 1, - ))); - break; - } + $tags = DiffusionRepositoryTag::newFromConduit( + $this->callConduitWithDiffusionRequest( + 'diffusion.tagsquery', + array( + 'commit' => $drequest->getCommit(), + 'limit' => $tag_limit + 1, + ))); + $has_more_tags = (count($tags) > $tag_limit); $tags = array_slice($tags, 0, $tag_limit); From 73bab571607382a33eb2415e9a24e1ec15166829 Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Tue, 16 Feb 2016 15:14:19 -0800 Subject: [PATCH 20/42] fix the typo in the label field Summary: Fixes T10369 Test Plan: de nada Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Maniphest Tasks: T10369 Differential Revision: https://secure.phabricator.com/D15285 --- .../conduit/query/PhabricatorConduitLogSearchEngine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/conduit/query/PhabricatorConduitLogSearchEngine.php b/src/applications/conduit/query/PhabricatorConduitLogSearchEngine.php index f5d203c772..4c91ce7a8d 100644 --- a/src/applications/conduit/query/PhabricatorConduitLogSearchEngine.php +++ b/src/applications/conduit/query/PhabricatorConduitLogSearchEngine.php @@ -37,7 +37,7 @@ final class PhabricatorConduitLogSearchEngine return array( id(new PhabricatorUsersSearchField()) ->setKey('callerPHIDs') - ->setLabel(pht('Methods')) + ->setLabel(pht('Callers')) ->setAliases(array('caller', 'callers')) ->setDescription(pht('Find calls by specific users.')), id(new PhabricatorSearchStringListField()) From 8c3ca2a72971278b16eaad5a1f13cf34f0ca4068 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 16 Feb 2016 19:53:37 -0800 Subject: [PATCH 21/42] Add ability to setActionList to a PHUIHeaderView Summary: We're using this a little more, so I'd prefer less copy-pasta and one place to manage the UI. Maybe add a caret? Test Plan: grep for 'Actions', test Phriction, Diviner, ect, Action Menus. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15288 --- .../controller/DivinerBookController.php | 10 +--------- .../controller/blog/PhameBlogViewController.php | 11 +---------- .../controller/post/PhamePostViewController.php | 11 +---------- .../controller/PhrictionDocumentController.php | 10 +--------- src/view/phui/PHUIHeaderView.php | 17 +++++++++++++++++ 5 files changed, 21 insertions(+), 38 deletions(-) diff --git a/src/applications/diviner/controller/DivinerBookController.php b/src/applications/diviner/controller/DivinerBookController.php index 11d6a5bbfc..74b2a8f3d9 100644 --- a/src/applications/diviner/controller/DivinerBookController.php +++ b/src/applications/diviner/controller/DivinerBookController.php @@ -29,20 +29,12 @@ final class DivinerBookController extends DivinerController { $book->getShortTitle(), '/book/'.$book->getName().'/'); - $action_button = id(new PHUIButtonView()) - ->setTag('a') - ->setText(pht('Actions')) - ->setHref('#') - ->setIcon('fa-bars') - ->addClass('phui-mobile-menu') - ->setDropdownMenu($actions); - $header = id(new PHUIHeaderView()) ->setHeader($book->getTitle()) ->setUser($viewer) ->setPolicyObject($book) ->setEpoch($book->getDateModified()) - ->addActionLink($action_button); + ->setActionList($actions); // TODO: This could probably look better. if ($book->getRepositoryPHID()) { diff --git a/src/applications/phame/controller/blog/PhameBlogViewController.php b/src/applications/phame/controller/blog/PhameBlogViewController.php index 60972c57b8..ea0822b856 100644 --- a/src/applications/phame/controller/blog/PhameBlogViewController.php +++ b/src/applications/phame/controller/blog/PhameBlogViewController.php @@ -44,16 +44,7 @@ final class PhameBlogViewController extends PhameLiveController { $header->setStatus($header_icon, $header_color, $header_name); $actions = $this->renderActions($blog); - $action_button = id(new PHUIButtonView()) - ->setTag('a') - ->setText(pht('Actions')) - ->setHref('#') - ->setIcon('fa-bars') - ->addClass('phui-mobile-menu') - ->setDropdownMenu($actions); - - $header->addActionLink($action_button); - + $header->setActionList($actions); $header->setPolicyObject($blog); } diff --git a/src/applications/phame/controller/post/PhamePostViewController.php b/src/applications/phame/controller/post/PhamePostViewController.php index 537bfbb713..a4231cc1ac 100644 --- a/src/applications/phame/controller/post/PhamePostViewController.php +++ b/src/applications/phame/controller/post/PhamePostViewController.php @@ -24,17 +24,8 @@ final class PhamePostViewController if (!$is_external) { $actions = $this->renderActions($post); - - $action_button = id(new PHUIButtonView()) - ->setTag('a') - ->setText(pht('Actions')) - ->setHref('#') - ->setIcon('fa-bars') - ->addClass('phui-mobile-menu') - ->setDropdownMenu($actions); - $header->setPolicyObject($post); - $header->addActionLink($action_button); + $header->setActionList($actions); } $document = id(new PHUIDocumentViewPro()) diff --git a/src/applications/phriction/controller/PhrictionDocumentController.php b/src/applications/phriction/controller/PhrictionDocumentController.php index 7d98e9ed3b..7dae0fad03 100644 --- a/src/applications/phriction/controller/PhrictionDocumentController.php +++ b/src/applications/phriction/controller/PhrictionDocumentController.php @@ -203,19 +203,11 @@ final class PhrictionDocumentController $crumbs->addCrumb($view); } - $action_button = id(new PHUIButtonView()) - ->setTag('a') - ->setText(pht('Actions')) - ->setHref('#') - ->setIcon('fa-bars') - ->addClass('phui-mobile-menu') - ->setDropdownMenu($actions); - $header = id(new PHUIHeaderView()) ->setUser($viewer) ->setPolicyObject($document) ->setHeader($page_title) - ->addActionLink($action_button); + ->setActionList($actions); if ($content) { $header->setEpoch($content->getDateCreated()); diff --git a/src/view/phui/PHUIHeaderView.php b/src/view/phui/PHUIHeaderView.php index 50f0e36317..233e5a7c72 100644 --- a/src/view/phui/PHUIHeaderView.php +++ b/src/view/phui/PHUIHeaderView.php @@ -23,6 +23,7 @@ final class PHUIHeaderView extends AphrontTagView { private $actionIcons = array(); private $badges = array(); private $href; + private $actionList; public function setHeader($header) { $this->header = $header; @@ -84,6 +85,11 @@ final class PHUIHeaderView extends AphrontTagView { return $this; } + public function setActionList(PhabricatorActionListView $list) { + $this->actionList = $list; + return $this; + } + public function setPolicyObject(PhabricatorPolicyInterface $object) { $this->policyObject = $object; return $this; @@ -191,6 +197,17 @@ final class PHUIHeaderView extends AphrontTagView { protected function getTagContent() { + if ($this->actionList) { + $action_button = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('Actions')) + ->setHref('#') + ->setIcon('fa-bars') + ->addClass('phui-mobile-menu') + ->setDropdownMenu($this->actionList); + $this->addActionLink($action_button); + } + $image = null; if ($this->image) { $image_href = null; From 9a16e5c1aade26f0303383e16ee63d7d87f22877 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 17 Feb 2016 11:37:01 -0800 Subject: [PATCH 22/42] Allow users to seach for projects by watcher Summary: Ref T10349. This capability didn't make a ton of sense when you had to be a member to watch a project and watch rules were simple, but makes more sense now. A particular use case might be finding all the stuff you're watching so you can prune it. Test Plan: Searched for stuff I was watching, got accurate results. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10349 Differential Revision: https://secure.phabricator.com/D15289 --- .../project/query/PhabricatorProjectQuery.php | 23 ++++++++++++++++++- .../query/PhabricatorProjectSearchEngine.php | 8 +++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php index 9ce5fff77c..9845628b4b 100644 --- a/src/applications/project/query/PhabricatorProjectQuery.php +++ b/src/applications/project/query/PhabricatorProjectQuery.php @@ -6,6 +6,7 @@ final class PhabricatorProjectQuery private $ids; private $phids; private $memberPHIDs; + private $watcherPHIDs; private $slugs; private $slugNormals; private $slugMap; @@ -62,6 +63,11 @@ final class PhabricatorProjectQuery return $this; } + public function withWatcherPHIDs(array $watcher_phids) { + $this->watcherPHIDs = $watcher_phids; + return $this; + } + public function withSlugs(array $slugs) { $this->slugs = $slugs; return $this; @@ -436,6 +442,13 @@ final class PhabricatorProjectQuery $this->memberPHIDs); } + if ($this->watcherPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'w.dst IN (%Ls)', + $this->watcherPHIDs); + } + if ($this->slugs !== null) { $where[] = qsprintf( $conn, @@ -549,7 +562,7 @@ final class PhabricatorProjectQuery } protected function shouldGroupQueryResultRows() { - if ($this->memberPHIDs || $this->nameTokens) { + if ($this->memberPHIDs || $this->watcherPHIDs || $this->nameTokens) { return true; } return parent::shouldGroupQueryResultRows(); @@ -566,6 +579,14 @@ final class PhabricatorProjectQuery PhabricatorProjectMaterializedMemberEdgeType::EDGECONST); } + if ($this->watcherPHIDs !== null) { + $joins[] = qsprintf( + $conn, + 'JOIN %T w ON w.src = p.phid AND w.type = %d', + PhabricatorEdgeConfig::TABLE_NAME_EDGE, + PhabricatorObjectHasWatcherEdgeType::EDGECONST); + } + if ($this->slugs !== null) { $joins[] = qsprintf( $conn, diff --git a/src/applications/project/query/PhabricatorProjectSearchEngine.php b/src/applications/project/query/PhabricatorProjectSearchEngine.php index ea91dc9754..cfb1868a61 100644 --- a/src/applications/project/query/PhabricatorProjectSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectSearchEngine.php @@ -26,6 +26,10 @@ final class PhabricatorProjectSearchEngine ->setLabel(pht('Members')) ->setKey('memberPHIDs') ->setAliases(array('member', 'members')), + id(new PhabricatorUsersSearchField()) + ->setLabel(pht('Watchers')) + ->setKey('watcherPHIDs') + ->setAliases(array('watcher', 'watchers')), id(new PhabricatorSearchSelectField()) ->setLabel(pht('Status')) ->setKey('status') @@ -54,6 +58,10 @@ final class PhabricatorProjectSearchEngine $query->withMemberPHIDs($map['memberPHIDs']); } + if ($map['watcherPHIDs']) { + $query->withWatcherPHIDs($map['watcherPHIDs']); + } + if ($map['status']) { $status = idx($this->getStatusValues(), $map['status']); if ($status) { From 12d8520059622e13ba257c83a3f581411c8bc75f Mon Sep 17 00:00:00 2001 From: Chad Little Date: Wed, 17 Feb 2016 12:43:38 -0800 Subject: [PATCH 23/42] Convert PHUIObjectBoxView to AphrontTagView Summary: Attempting to clean PHUIObjectBoxView up a little as well as finally being able to `addClass` on the sucker. I'm running into some issue with `addTabs` though, which on Files isn't firing. Test Plan: Bounce around tons of screens. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15291 --- src/__phutil_library_map__.php | 2 +- ...PhabricatorPeopleProfileViewController.php | 4 +- .../PhabricatorProjectProfileController.php | 8 +- src/view/phui/PHUIObjectBoxView.php | 260 +++++++++--------- 4 files changed, 133 insertions(+), 141 deletions(-) diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 538ea56772..d277b62547 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -5711,7 +5711,7 @@ phutil_register_library_map(array( 'PHUIListView' => 'AphrontTagView', 'PHUIListViewTestCase' => 'PhabricatorTestCase', 'PHUIMainMenuView' => 'AphrontView', - 'PHUIObjectBoxView' => 'AphrontView', + 'PHUIObjectBoxView' => 'AphrontTagView', 'PHUIObjectItemListExample' => 'PhabricatorUIExample', 'PHUIObjectItemListView' => 'AphrontTagView', 'PHUIObjectItemView' => 'AphrontTagView', diff --git a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php index 9978c6198e..9c85a5b7b3 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php @@ -174,7 +174,7 @@ final class PhabricatorPeopleProfileViewController $box = id(new PHUIObjectBoxView()) ->setHeader($header) ->appendChild($list) - ->setBackground(PHUIBoxView::GREY); + ->setBackground(PHUIObjectBoxView::GREY); return $box; } @@ -218,7 +218,7 @@ final class PhabricatorPeopleProfileViewController $box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Badges')) ->appendChild($flex) - ->setBackground(PHUIBoxView::GREY); + ->setBackground(PHUIObjectBoxView::GREY); return $box; } diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php index 81a172ae42..f944bf5128 100644 --- a/src/applications/project/controller/PhabricatorProjectProfileController.php +++ b/src/applications/project/controller/PhabricatorProjectProfileController.php @@ -59,14 +59,14 @@ final class PhabricatorProjectProfileController ->setUser($viewer) ->setProject($project) ->setLimit(5) - ->setBackground(PHUIBoxView::GREY) + ->setBackground(PHUIObjectBoxView::GREY) ->setUserPHIDs($project->getMemberPHIDs()); $watcher_list = id(new PhabricatorProjectWatcherListView()) ->setUser($viewer) ->setProject($project) ->setLimit(5) - ->setBackground(PHUIBoxView::GREY) + ->setBackground(PHUIObjectBoxView::GREY) ->setUserPHIDs($project->getWatcherPHIDs()); $nav = $this->getProfileMenu(); @@ -244,7 +244,7 @@ final class PhabricatorProjectProfileController return id(new PHUIObjectBoxView()) ->setHeader($header) - ->setBackground(PHUIBoxView::GREY) + ->setBackground(PHUIObjectBoxView::GREY) ->setObjectList($milestone_list); } @@ -292,7 +292,7 @@ final class PhabricatorProjectProfileController return id(new PHUIObjectBoxView()) ->setHeader($header) - ->setBackground(PHUIBoxView::GREY) + ->setBackground(PHUIObjectBoxView::GREY) ->setObjectList($subproject_list); } diff --git a/src/view/phui/PHUIObjectBoxView.php b/src/view/phui/PHUIObjectBoxView.php index 32440a09cf..5cbb3a58b6 100644 --- a/src/view/phui/PHUIObjectBoxView.php +++ b/src/view/phui/PHUIObjectBoxView.php @@ -1,6 +1,6 @@ sigils[] = $sigil; - return $this; - } - - public function setMetadata(array $metadata) { - $this->metadata = $metadata; - return $this; - } + const BLUE = 'phui-box-blue'; + const GREY = 'phui-box-grey'; public function addPropertyList( PHUIPropertyListView $property_list, @@ -144,11 +137,6 @@ final class PHUIObjectBoxView extends AphrontView { return $this; } - public function setID($id) { - $this->id = $id; - return $this; - } - public function setHeader($header) { $this->header = $header; return $this; @@ -195,7 +183,109 @@ final class PHUIObjectBoxView extends AphrontView { return $this; } - public function render() { + public function willRender() { + $tab_lists = array(); + $property_lists = array(); + $tab_map = array(); + + $default_key = 'tab.default'; + + // Find the selected tab, or select the first tab if none are selected. + if ($this->tabs) { + $selected_tab = null; + foreach ($this->tabs as $key => $tab) { + if ($tab->getSelected()) { + $selected_tab = $key; + break; + } + } + if ($selected_tab === null) { + head($this->tabs)->setSelected(true); + $selected_tab = head_key($this->tabs); + } + } + + foreach ($this->propertyLists as $key => $list) { + $group = new PHUIPropertyGroupView(); + $i = 0; + foreach ($list as $item) { + $group->addPropertyList($item); + if ($i > 0) { + $item->addClass('phui-property-list-section-noninitial'); + } + $i++; + } + + if ($this->tabs && $key != $default_key) { + $tab_id = celerity_generate_unique_node_id(); + $tab_map[$key] = $tab_id; + + if ($key === $selected_tab) { + $style = null; + } else { + $style = 'display: none'; + } + + $tab_lists[] = phutil_tag( + 'div', + array( + 'style' => $style, + 'id' => $tab_id, + ), + $group); + } else { + if ($this->tabs) { + $group->addClass('phui-property-group-noninitial'); + } + $property_lists[] = $group; + } + $this->propertyList = $property_lists; + $this->tabMap = $tab_map; + $this->tabLists = $tab_lists; + } + } + + protected function getTagAttributes() { + $classes = array(); + $classes[] = 'phui-box'; + $classes[] = 'phui-box-border'; + $classes[] = 'phui-object-box'; + $classes[] = 'mlt mll mlr'; + + if ($this->color) { + $classes[] = 'phui-object-box-'.$this->color; + } + + if ($this->collapsed) { + $classes[] = 'phui-object-box-collapsed'; + } + + if ($this->flush) { + $classes[] = 'phui-object-box-flush'; + } + + if ($this->background) { + $classes[] = $this->background; + } + + $sigil = null; + $metadata = null; + if ($this->tabs) { + $sigil = 'phui-object-box'; + $metadata = array( + 'tabMap' => $this->tabMap, + ); + } + + return array( + 'class' => implode(' ', $classes), + 'sigil' => $sigil, + 'meta' => $metadata, + ); + } + + protected function getTagContent() { + require_celerity_resource('phui-box-css'); require_celerity_resource('phui-object-box-css'); $header = $this->header; @@ -296,63 +386,6 @@ final class PHUIObjectBoxView extends AphrontView { } } - $tab_lists = array(); - $property_lists = array(); - $tab_map = array(); - - $default_key = 'tab.default'; - - // Find the selected tab, or select the first tab if none are selected. - if ($this->tabs) { - $selected_tab = null; - foreach ($this->tabs as $key => $tab) { - if ($tab->getSelected()) { - $selected_tab = $key; - break; - } - } - if ($selected_tab === null) { - head($this->tabs)->setSelected(true); - $selected_tab = head_key($this->tabs); - } - } - - foreach ($this->propertyLists as $key => $list) { - $group = new PHUIPropertyGroupView(); - $i = 0; - foreach ($list as $item) { - $group->addPropertyList($item); - if ($i > 0) { - $item->addClass('phui-property-list-section-noninitial'); - } - $i++; - } - - if ($this->tabs && $key != $default_key) { - $tab_id = celerity_generate_unique_node_id(); - $tab_map[$key] = $tab_id; - - if ($key === $selected_tab) { - $style = null; - } else { - $style = 'display: none'; - } - - $tab_lists[] = phutil_tag( - 'div', - array( - 'style' => $style, - 'id' => $tab_id, - ), - $group); - } else { - if ($this->tabs) { - $group->addClass('phui-property-group-noninitial'); - } - $property_lists[] = $group; - } - } - $tabs = null; if ($this->tabs) { $tabs = id(new PHUIListView()) @@ -360,69 +393,28 @@ final class PHUIObjectBoxView extends AphrontView { foreach ($this->tabs as $tab) { $tabs->addMenuItem($tab); } - Javelin::initBehavior('phui-object-box-tabs'); } - $content = id(new PHUIBoxView()) - ->appendChild( - array( - ($this->showHideOpen == false ? $this->anchor : null), - $header, - $this->infoView, - $this->formErrors, - $this->formSaved, - $exception_errors, - $this->form, - $tabs, - $tab_lists, - $showhide, - ($this->showHideOpen == true ? $this->anchor : null), - $property_lists, - $this->table, - $this->renderChildren(), - )) - ->setBorder(true) - ->setID($this->id) - ->addMargin(PHUI::MARGIN_LARGE_TOP) - ->addMargin(PHUI::MARGIN_LARGE_LEFT) - ->addMargin(PHUI::MARGIN_LARGE_RIGHT) - ->addClass('phui-object-box'); - - if ($this->color) { - $content->addClass('phui-object-box-'.$this->color); - } - - if ($this->background) { - $content->setColor($this->background); - } - - if ($this->collapsed) { - $content->addClass('phui-object-box-collapsed'); - } - - if ($this->tabs) { - $content->addSigil('phui-object-box'); - $content->setMetadata( - array( - 'tabMap' => $tab_map, - )); - } - - if ($this->flush) { - $content->addClass('phui-object-box-flush'); - } - - foreach ($this->sigils as $sigil) { - $content->addSigil($sigil); - } - - if ($this->metadata !== null) { - $content->setMetadata($this->metadata); - } + $content = array( + ($this->showHideOpen == false ? $this->anchor : null), + $header, + $this->infoView, + $this->formErrors, + $this->formSaved, + $exception_errors, + $this->form, + $tabs, + $this->tabLists, + $showhide, + ($this->showHideOpen == true ? $this->anchor : null), + $this->propertyList, + $this->table, + $this->renderChildren(), + ); if ($this->objectList) { - $content->appendChild($this->objectList); + $content[] = $this->objectList; } return $content; From f5e2f9587c5ecca25f6e5fec263e2132bc127af2 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Wed, 17 Feb 2016 13:05:24 -0800 Subject: [PATCH 24/42] Add setHeader to PHUITwoColumnView for consistent page layouts Summary: Working towards making PHUITwoColumnView into a page layout engine. Adds header support. Test Plan: Use new header on Profile and Profiles. No visual changes, less duplicated code. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15292 --- resources/celerity/map.php | 8 ++++---- .../PhabricatorPeopleProfileViewController.php | 18 +++++------------- .../PhabricatorProjectProfileController.php | 16 ++++------------ src/view/phui/PHUITwoColumnView.php | 17 +++++++++++++++-- .../css/application/project/project-view.css | 2 +- webroot/rsrc/css/phui/phui-two-column-view.css | 2 +- 6 files changed, 30 insertions(+), 33 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index f1f53c7379..abb3fbb36a 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -94,7 +94,7 @@ return array( 'rsrc/css/application/policy/policy.css' => '957ea14c', 'rsrc/css/application/ponder/ponder-view.css' => '4486434b', 'rsrc/css/application/project/project-card-view.css' => '9418c97d', - 'rsrc/css/application/project/project-view.css' => '4c832c27', + 'rsrc/css/application/project/project-view.css' => '83bb6654', 'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733', 'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5', 'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd', @@ -154,7 +154,7 @@ return array( 'rsrc/css/phui/phui-status.css' => '888cedb8', 'rsrc/css/phui/phui-tag-view.css' => '9d5d4400', 'rsrc/css/phui/phui-timeline-view.css' => '2efceff8', - 'rsrc/css/phui/phui-two-column-view.css' => 'c75bfc5b', + 'rsrc/css/phui/phui-two-column-view.css' => '0763177e', 'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7', 'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647', 'rsrc/css/phui/workboards/phui-workcard.css' => '3646fb96', @@ -841,7 +841,7 @@ return array( 'phui-tag-view-css' => '9d5d4400', 'phui-theme-css' => 'ab7b848c', 'phui-timeline-view-css' => '2efceff8', - 'phui-two-column-view-css' => 'c75bfc5b', + 'phui-two-column-view-css' => '0763177e', 'phui-workboard-color-css' => 'ac6fe6a7', 'phui-workboard-view-css' => 'e6d89647', 'phui-workcard-view-css' => '3646fb96', @@ -857,7 +857,7 @@ return array( 'policy-transaction-detail-css' => '82100a43', 'ponder-view-css' => '4486434b', 'project-card-view-css' => '9418c97d', - 'project-view-css' => '4c832c27', + 'project-view-css' => '83bb6654', 'releeph-core' => '9b3c5733', 'releeph-preview-branch' => 'b7a6f4a5', 'releeph-request-differential-create-dialog' => '8d8b92cd', diff --git a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php index 9c85a5b7b3..ce15aa2b38 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php @@ -56,9 +56,11 @@ final class PhabricatorPeopleProfileViewController $projects = $this->buildProjectsView($user); $badges = $this->buildBadgesView($user); + require_celerity_resource('project-view-css'); - $columns = id(new PHUITwoColumnView()) - ->addClass('project-view-badges') + $home = id(new PHUITwoColumnView()) + ->setHeader($header) + ->addClass('project-view-home') ->setMainColumn( array( $properties, @@ -76,17 +78,6 @@ final class PhabricatorPeopleProfileViewController $crumbs = $this->buildApplicationCrumbs(); $crumbs->setBorder(true); - require_celerity_resource('project-view-css'); - $home = phutil_tag( - 'div', - array( - 'class' => 'project-view-home', - ), - array( - $header, - $columns, - )); - return $this->newPage() ->setTitle($user->getUsername()) ->setNavigation($nav) @@ -217,6 +208,7 @@ final class PhabricatorPeopleProfileViewController $box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Badges')) + ->addClass('project-view-badges') ->appendChild($flex) ->setBackground(PHUIObjectBoxView::GREY); diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php index f944bf5128..8220592074 100644 --- a/src/applications/project/controller/PhabricatorProjectProfileController.php +++ b/src/applications/project/controller/PhabricatorProjectProfileController.php @@ -83,8 +83,11 @@ final class PhabricatorProjectProfileController $feed = $this->renderStories($stories); $feed = phutil_tag_div('project-view-feed', $feed); + require_celerity_resource('project-view-css'); - $columns = id(new PHUITwoColumnView()) + $home = id(new PHUITwoColumnView()) + ->setHeader($header) + ->addClass('project-view-home') ->setMainColumn( array( $properties, @@ -101,17 +104,6 @@ final class PhabricatorProjectProfileController $crumbs = $this->buildApplicationCrumbs(); $crumbs->setBorder(true); - require_celerity_resource('project-view-css'); - $home = phutil_tag( - 'div', - array( - 'class' => 'project-view-home', - ), - array( - $header, - $columns, - )); - return $this->newPage() ->setNavigation($nav) ->setCrumbs($crumbs) diff --git a/src/view/phui/PHUITwoColumnView.php b/src/view/phui/PHUITwoColumnView.php index a01ce913b8..97ff269d83 100644 --- a/src/view/phui/PHUITwoColumnView.php +++ b/src/view/phui/PHUITwoColumnView.php @@ -5,6 +5,7 @@ final class PHUITwoColumnView extends AphrontTagView { private $mainColumn; private $sideColumn; private $display; + private $header; const DISPLAY_LEFT = 'phui-side-column-left'; const DISPLAY_RIGHT = 'phui-side-column-right'; @@ -19,6 +20,11 @@ final class PHUITwoColumnView extends AphrontTagView { return $this; } + public function setHeader(PHUIHeaderView $header) { + $this->header = $header; + return $this; + } + public function setDisplay($display) { $this->display = $display; return $this; @@ -35,7 +41,6 @@ final class PHUITwoColumnView extends AphrontTagView { protected function getTagAttributes() { $classes = array(); $classes[] = 'phui-two-column-view'; - $classes[] = 'grouped'; $classes[] = $this->getDisplay(); return array( @@ -66,6 +71,14 @@ final class PHUITwoColumnView extends AphrontTagView { $order = array($main, $side); } - return phutil_tag_div('phui-two-column-row', $order); + $inner = phutil_tag_div('phui-two-column-row', $order); + $table = phutil_tag_div('phui-two-column-content', $inner); + + $header = null; + if ($this->header) { + $header = phutil_tag_div('phui-two-column-header', $this->header); + } + + return array($header, $table); } } diff --git a/webroot/rsrc/css/application/project/project-view.css b/webroot/rsrc/css/application/project/project-view.css index f46af0bc74..f84790cf12 100644 --- a/webroot/rsrc/css/application/project/project-view.css +++ b/webroot/rsrc/css/application/project/project-view.css @@ -51,7 +51,7 @@ } .project-view-feed .phui-object-box.phui-box-border { - padding: 0 16px 8px 16px; + padding: 0 4px 8px 4px; border: none; } diff --git a/webroot/rsrc/css/phui/phui-two-column-view.css b/webroot/rsrc/css/phui/phui-two-column-view.css index 21c4c36393..e08bd5a603 100644 --- a/webroot/rsrc/css/phui/phui-two-column-view.css +++ b/webroot/rsrc/css/phui/phui-two-column-view.css @@ -2,7 +2,7 @@ * @provides phui-two-column-view-css */ -.phui-two-column-view { +.phui-two-column-content { display: table; width: 100%; } From 973b8ace8616f96d77cede4ec620248be262e3ff Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 17 Feb 2016 16:45:22 -0800 Subject: [PATCH 25/42] Remove dependence on callsigns from `bin/commit-hook` Summary: Ref T4245. Two effects: - First, let hooks work for future repositories without callsigns. - Second, provide a better error when users push directly to hosted repositories. Test Plan: Ran `bin/commit-hook PHID-REPO-xxx`. Reviewers: chad, avivey Reviewed By: avivey Maniphest Tasks: T4245 Differential Revision: https://secure.phabricator.com/D15293 --- scripts/repository/commit_hook.php | 11 +++--- .../PhabricatorRepositoryPullEngine.php | 2 +- .../user/userguide/diffusion_hosting.diviner | 35 +++++++++++++++++++ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/scripts/repository/commit_hook.php b/scripts/repository/commit_hook.php index 56e828ab7a..4f6997c52a 100755 --- a/scripts/repository/commit_hook.php +++ b/scripts/repository/commit_hook.php @@ -32,14 +32,14 @@ $root = dirname(dirname(dirname(__FILE__))); require_once $root.'/scripts/__init_script__.php'; if ($argc < 2) { - throw new Exception(pht('usage: commit-hook ')); + throw new Exception(pht('usage: commit-hook ')); } $engine = new DiffusionCommitHookEngine(); $repository = id(new PhabricatorRepositoryQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withCallsigns(array($argv[1])) + ->withIdentifiers(array($argv[1])) ->needProjectPHIDs(true) ->executeOne(); @@ -62,8 +62,9 @@ if ($repository->isGit() || $repository->isHg()) { if (!strlen($username)) { throw new Exception( pht( - 'Usage: %s should be defined!', - DiffusionCommitHookEngine::ENV_USER)); + 'No Direct Pushes: You are pushing directly to a repository hosted '. + 'by Phabricator. This will not work. See "No Direct Pushes" in the '. + 'documentation for more information.')); } if ($repository->isHg()) { @@ -77,7 +78,7 @@ if ($repository->isGit() || $repository->isHg()) { // specify the correct user; read this user out of the commit log. if ($argc < 4) { - throw new Exception(pht('usage: commit-hook ')); + throw new Exception(pht('usage: commit-hook ')); } $svn_repo = $argv[2]; diff --git a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php index 30ab234027..690f9425a4 100644 --- a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php +++ b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php @@ -192,7 +192,7 @@ final class PhabricatorRepositoryPullEngine } private function getHookContextIdentifier(PhabricatorRepository $repository) { - $identifier = $repository->getCallsign(); + $identifier = $repository->getPHID(); $instance = PhabricatorEnv::getEnvConfig('cluster.instance'); if (strlen($instance)) { diff --git a/src/docs/user/userguide/diffusion_hosting.diviner b/src/docs/user/userguide/diffusion_hosting.diviner index 6427be92e5..f4baddf5a5 100644 --- a/src/docs/user/userguide/diffusion_hosting.diviner +++ b/src/docs/user/userguide/diffusion_hosting.diviner @@ -371,6 +371,41 @@ with `sudoers` configuration. is caused by SVN wiping the environment (including PATH) when invoking commit hooks. +No Direct Pushes +================ + +You may get an error about "No Direct Pushes" when trying to push. This means +you are pushing directly to the repository instead of pushing through +Phabricator. This is not supported: writes to hosted repositories must go +through Phabricator so it can perform authentication, enforce permissions, +write logs, proxy requests, apply rewriting, etc. + +One way to do a direct push by mistake is to use a `file:///` URI to interact +with the repository from the same machine. This is not supported. Instead, use +one of the repository URIs provided in the web interface, even if you're +working on the same machine. + +Another way to do a direct push is to misconfigure SSH (or not configure it at +all) so that none of the logic described above runs and you just connect +normally as a system user. In this case, the `ssh` test described above will +fail (you'll get a command prompt when you connect, instead of the message you +are supposed to get, as described above). + +If you encounter this error: make sure you're using a remote URI given to +you by Diffusion in the web interface, then run through the troubleshooting +steps above carefully. + +Sometimes users encounter this problem because they skip this whole document +assuming they don't need to configure anything. This will not work, and you +MUST configure things as described above for hosted repositories to work. + +The technical reason this error occurs is that the `PHABRICATOR_USER` variable +is not defined in the environment when commit hooks run. This variable is set +by Phabricator when a request passes through the authentication layer that this +document provides instructions for configuring. Its absence indicates that the +request did not pass through Phabricator. + + = Next Steps = Once hosted repositories are set up: From 925a0d3d5918888f87bd65f24673acbdf75360a5 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 17 Feb 2016 16:58:50 -0800 Subject: [PATCH 26/42] Pass repository PHID to custom hooks in PHABRICATOR_REPOSITORY instead of callsign Summary: Ref T4245. We pass this exclusively for use by additional third-party hooks. This is technically a backward compatibility break, but I suspect it doesn't affect anyone: - Probably almost no one is using this (there are few reasons to, even for the tiny number of installs with custom commit hooks). - If they are, there's a good chance the PHID will work anyway, since nearly all scripts and Conduit methods will accept it in place of a callsign now, and if it's in logging or debugging code the PHID is a reasonable substitute - Even if it doesn't just keep working, the break should be very obvious in most reasonable cases. I'll call this out explicitly in the changelog, though -- almost everything else will just continue working, but this is a strict compatibility break. Test Plan: - Ugh. - Picked a hosted Git repo out of Diffusion. - Went to the path on disk. - Went into `hooks/`. - Went into `pre-receive-phabricator.d/`. - Wrote this hook and gave it `chmod +x`: ```name=stuff.sh #!/bin/sh echo $PHABRICATOR_REPOSITORY >> /tmp/stuff.log ``` - Pushed to the repository. - Saw a PHID show up in the log: ``` $ cat /tmp/stuff.log PHID-REPO-bqkcdp47euwnwlasrsrh ``` Reviewers: chad, avivey Reviewed By: avivey Subscribers: avivey Maniphest Tasks: T4245 Differential Revision: https://secure.phabricator.com/D15294 --- .../diffusion/engine/DiffusionCommitHookEngine.php | 3 ++- src/docs/user/userguide/diffusion_hooks.diviner | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php index 741b21bd19..53dc417be4 100644 --- a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php +++ b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php @@ -10,6 +10,7 @@ */ final class DiffusionCommitHookEngine extends Phobject { + const ENV_REPOSITORY = 'PHABRICATOR_REPOSITORY'; const ENV_USER = 'PHABRICATOR_USER'; const ENV_REMOTE_ADDRESS = 'PHABRICATOR_REMOTE_ADDRESS'; const ENV_REMOTE_PROTOCOL = 'PHABRICATOR_REMOTE_PROTOCOL'; @@ -610,7 +611,7 @@ final class DiffusionCommitHookEngine extends Phobject { $console = PhutilConsole::getConsole(); $env = array( - 'PHABRICATOR_REPOSITORY' => $this->getRepository()->getCallsign(), + self::ENV_REPOSITORY => $this->getRepository()->getPHID(), self::ENV_USER => $this->getViewer()->getUsername(), self::ENV_REMOTE_PROTOCOL => $this->getRemoteProtocol(), self::ENV_REMOTE_ADDRESS => $this->getRemoteAddress(), diff --git a/src/docs/user/userguide/diffusion_hooks.diviner b/src/docs/user/userguide/diffusion_hooks.diviner index db52c0667d..54c93a933b 100644 --- a/src/docs/user/userguide/diffusion_hooks.diviner +++ b/src/docs/user/userguide/diffusion_hooks.diviner @@ -42,7 +42,7 @@ These hooks act like normal `pre-commit` or `pre-receive` hooks: These additional variables will be available in the environment, in addition to the variables the VCS normally provides: - - `PHABRICATOR_REPOSITORY` The callsign of the repository the hook is + - `PHABRICATOR_REPOSITORY` The PHID of the repository the hook is executing for. - `PHABRICATOR_USER` The Phabricator username that the session is authenticated under. From 93d7b0122252c3f4d7dbf4f065e9d5db1024d9f8 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 17 Feb 2016 17:13:33 -0800 Subject: [PATCH 27/42] Remove uncalled DiffusionRequest->getCallsign() Summary: Ref T4245. This has no callers. Test Plan: - Ran `git grep -i 'getCallsign('` and visually verified that no callers could reasonably be `DiffusionRequest` objects (there are only 23 remaining sites, and about half are `$this->...` in `PhabricatorRepository`. - Browsed around directory/file/branch/content/diff/etc pages in Diffusion. Reviewers: chad, avivey Reviewed By: avivey Maniphest Tasks: T4245 Differential Revision: https://secure.phabricator.com/D15295 --- src/applications/diffusion/request/DiffusionRequest.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/applications/diffusion/request/DiffusionRequest.php b/src/applications/diffusion/request/DiffusionRequest.php index 8d326b81bc..f86170f129 100644 --- a/src/applications/diffusion/request/DiffusionRequest.php +++ b/src/applications/diffusion/request/DiffusionRequest.php @@ -243,10 +243,6 @@ abstract class DiffusionRequest extends Phobject { return $this->repository; } - public function getCallsign() { - return $this->getRepository()->getCallsign(); - } - public function setPath($path) { $this->path = $path; return $this; From 097bcb397061213a1eecd9bdec2a4a385b9d1f85 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 17 Feb 2016 17:20:15 -0800 Subject: [PATCH 28/42] Make `bin/diviner generate --repository ` accept identifiers Summary: Ref T4245. This currently accepts only callsigns; prepare it for the bright new callsign-optional world. Test Plan: - Ran `./bin/diviner generate --repository 1 --book src/docs/book/flavor.book --clean`, got a good result. - Ran `./bin/diviner generate --repository 239238 --book src/docs/book/flavor.book --clean`, got an appropraite error about a bad repository identifier. Reviewers: chad, avivey Reviewed By: avivey Maniphest Tasks: T4245 Differential Revision: https://secure.phabricator.com/D15296 --- .../diviner/workflow/DivinerGenerateWorkflow.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/applications/diviner/workflow/DivinerGenerateWorkflow.php b/src/applications/diviner/workflow/DivinerGenerateWorkflow.php index ad671bbbea..86b75c8480 100644 --- a/src/applications/diviner/workflow/DivinerGenerateWorkflow.php +++ b/src/applications/diviner/workflow/DivinerGenerateWorkflow.php @@ -27,7 +27,7 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow { ), array( 'name' => 'repository', - 'param' => 'callsign', + 'param' => 'identifier', 'help' => pht('Repository that the documentation belongs to.'), ), )); @@ -192,19 +192,19 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow { } $publisher = newv($publisher_class, array()); - $callsign = $args->getArg('repository'); + $identifier = $args->getArg('repository'); $repository = null; - if ($callsign) { + if (strlen($identifier)) { $repository = id(new PhabricatorRepositoryQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withCallsigns(array($callsign)) + ->withIdentifiers(array($identifier)) ->executeOne(); if (!$repository) { throw new PhutilArgumentUsageException( pht( - "Repository '%s' does not exist.", - $callsign)); + 'Repository "%s" does not exist.', + $identifier)); } $publisher->setRepositoryPHID($repository->getPHID()); From 82ca92a9ef1ac9f4234806b7d62a0a0280f8a4d7 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 17 Feb 2016 18:58:29 -0800 Subject: [PATCH 29/42] Don't show archived project tags on workboard cards Summary: Ref T10349. Test Plan: - Added archived and unarchived project tags to a task. - Saw unarchived tags, only, on cards. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10349 Differential Revision: https://secure.phabricator.com/D15297 --- src/applications/phid/view/PHUIHandleTagListView.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/applications/phid/view/PHUIHandleTagListView.php b/src/applications/phid/view/PHUIHandleTagListView.php index 0bbfc4d249..baad938051 100644 --- a/src/applications/phid/view/PHUIHandleTagListView.php +++ b/src/applications/phid/view/PHUIHandleTagListView.php @@ -52,6 +52,13 @@ final class PHUIHandleTagListView extends AphrontTagView { protected function getTagContent() { $handles = $this->handles; + // Remove any archived projects from the list. + foreach ($handles as $key => $handle) { + if ($handle->getStatus() == PhabricatorObjectHandle::STATUS_CLOSED) { + unset($handles[$key]); + } + } + // If the list is empty, we may render a "No Projects" tag. if (!$handles) { if (strlen($this->noDataString)) { From f557fc9caab1e0b8ed0b0dc4cdf564b1f0207de4 Mon Sep 17 00:00:00 2001 From: Alex Monk Date: Thu, 18 Feb 2016 09:54:47 -0800 Subject: [PATCH 30/42] Return 404 instead of undefined variable error when trying to edit a non-existent form Summary: E.g. https://phab-01.wmflabs.org/transactions/editengine/transactions.editengine.config/view/13/ Test Plan: * Go to /transactions/editengine/transactions.editengine.config/view/1000000/ * Observe error * Apply patch * Observe 404 Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: epriestley Differential Revision: https://secure.phabricator.com/D15307 --- .../transactions/controller/PhabricatorEditEngineController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/applications/transactions/controller/PhabricatorEditEngineController.php b/src/applications/transactions/controller/PhabricatorEditEngineController.php index b02bf8302f..d4b5e0e9f0 100644 --- a/src/applications/transactions/controller/PhabricatorEditEngineController.php +++ b/src/applications/transactions/controller/PhabricatorEditEngineController.php @@ -70,6 +70,8 @@ abstract class PhabricatorEditEngineController ->executeOne(); if ($config) { $engine = $config->getEngine(); + } else { + return null; } if (!$engine->isEngineConfigurable()) { From 78fba426f6616c0024849da9408966ff8ea4adc1 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 18 Feb 2016 03:09:59 -0800 Subject: [PATCH 31/42] Update import/clear symbols scripts for callsigns Summary: Ref T4245. Accept identifiers instead of callsigns in these scripts so things continue to work in a future callsign-optional world. Test Plan: Ran these scripts with both valid and invalid arguments; saw success and errors. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4245 Differential Revision: https://secure.phabricator.com/D15300 --- scripts/symbols/clear_repository_symbols.php | 16 +++++++++------- scripts/symbols/import_repository_symbols.php | 16 +++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/scripts/symbols/clear_repository_symbols.php b/scripts/symbols/clear_repository_symbols.php index 8807262cd5..701034c6cc 100755 --- a/scripts/symbols/clear_repository_symbols.php +++ b/scripts/symbols/clear_repository_symbols.php @@ -6,7 +6,7 @@ require_once $root.'/scripts/__init_script__.php'; $args = new PhutilArgumentParser($argv); $args->setSynopsis(<<parseStandardArguments(); $args->parse( array( array( - 'name' => 'callsign', + 'name' => 'repository', 'wildcard' => true, ), )); -$callsigns = $args->getArg('callsign'); -if (count($callsigns) !== 1) { +$identifiers = $args->getArg('repository'); +if (count($identifiers) !== 1) { $args->printHelpAndExit(); } -$callsign = head($callsigns); +$identifier = head($identifiers); $repository = id(new PhabricatorRepositoryQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withCallsigns($callsigns) + ->withIdentifiers($identifiers) ->executeOne(); if (!$repository) { - echo pht("Repository '%s' does not exist.", $callsign); + echo tsprintf( + "%s\n", + pht('Repository "%s" does not exist.', $identifier)); exit(1); } diff --git a/scripts/symbols/import_repository_symbols.php b/scripts/symbols/import_repository_symbols.php index c8dabc8508..b84aea3485 100755 --- a/scripts/symbols/import_repository_symbols.php +++ b/scripts/symbols/import_repository_symbols.php @@ -6,7 +6,7 @@ require_once $root.'/scripts/__init_script__.php'; $args = new PhutilArgumentParser($argv); $args->setSynopsis(<<parse( 'be part of a single transaction.'), ), array( - 'name' => 'callsign', + 'name' => 'repository', 'wildcard' => true, ), )); -$callsigns = $args->getArg('callsign'); -if (count($callsigns) !== 1) { +$identifiers = $args->getArg('repository'); +if (count($identifiers) !== 1) { $args->printHelpAndExit(); } -$callsign = head($callsigns); +$identifier = head($identifiers); $repository = id(new PhabricatorRepositoryQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withCallsigns($callsigns) + ->withIdentifiers($identifiers) ->executeOne(); if (!$repository) { - echo pht("Repository '%s' does not exist.", $callsign); + echo tsprintf( + "%s\n", + pht('Repository "%s" does not exist.', $identifier)); exit(1); } From c2b8dd28d8856aa76b5bd21b9c4c92cb2337072c Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 18 Feb 2016 04:06:13 -0800 Subject: [PATCH 32/42] Support ID-based repository URIs, and canonicalize repository URIs Summary: Ref T4245. Make `/diffusion/123/` work, but redirect the user to `/diffusion/XYZ/` if the repository has a callsign. (Right now, every repository has a callsign, so this always redirects.) Also redirect `/R123:abcdef` if the repository has a callsign. Also also, move the Pull garbage collector somewhere more sensible. Test Plan: - Added test coverage. - Visited `/diffusion/1/`, was redirected. - Visited `/diffusion/R1:abcdef`, was redirected. - Browsed Diffusion normally. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4245 Differential Revision: https://secure.phabricator.com/D15301 --- src/__phutil_library_map__.php | 2 +- .../PhabricatorDiffusionApplication.php | 11 +++-- .../controller/DiffusionController.php | 14 ++++++ .../DiffusionPullEventGarbageCollector.php | 0 .../storage/PhabricatorRepository.php | 49 +++++++++++++++++++ .../PhabricatorRepositoryURITestCase.php | 41 ++++++++++++++++ 6 files changed, 113 insertions(+), 4 deletions(-) rename src/applications/diffusion/{ => garbagecollector}/DiffusionPullEventGarbageCollector.php (100%) diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index d277b62547..ae699acf07 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -689,7 +689,7 @@ phutil_register_library_map(array( 'DiffusionPreCommitRefRepositoryHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefRepositoryHeraldField.php', 'DiffusionPreCommitRefRepositoryProjectsHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefRepositoryProjectsHeraldField.php', 'DiffusionPreCommitRefTypeHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefTypeHeraldField.php', - 'DiffusionPullEventGarbageCollector' => 'applications/diffusion/DiffusionPullEventGarbageCollector.php', + 'DiffusionPullEventGarbageCollector' => 'applications/diffusion/garbagecollector/DiffusionPullEventGarbageCollector.php', 'DiffusionPushCapability' => 'applications/diffusion/capability/DiffusionPushCapability.php', 'DiffusionPushEventViewController' => 'applications/diffusion/controller/DiffusionPushEventViewController.php', 'DiffusionPushLogController' => 'applications/diffusion/controller/DiffusionPushLogController.php', diff --git a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php index fa82b4e80f..b8766920fc 100644 --- a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php +++ b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php @@ -64,7 +64,11 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication { '(?:query/(?P[^/]+)/)?' => 'DiffusionPushLogListController', 'view/(?P\d+)/' => 'DiffusionPushEventViewController', ), - '(?P[A-Z]+)/' => array( + '(?:'. + '(?P[A-Z]+)'. + '|'. + '(?P[1-9]\d*)'. + ')/' => array( '' => 'DiffusionRepositoryController', 'repository/(?P.*)' => 'DiffusionRepositoryController', @@ -115,8 +119,9 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication { // catch-all for serving repositories over HTTP. We must accept // requests without the trailing "/" because SVN commands don't // necessarily include it. - '(?P[A-Z]+)(?:/.*)?' => - 'DiffusionRepositoryDefaultController', + '(?:(?P[A-Z]+)|(?P[1-9]\d*))'. + '(?:/.*)?' + => 'DiffusionRepositoryDefaultController', 'inline/' => array( 'edit/(?P[^/]+)/' => 'DiffusionInlineCommentController', diff --git a/src/applications/diffusion/controller/DiffusionController.php b/src/applications/diffusion/controller/DiffusionController.php index 00bb0ce4d4..c83990cc15 100644 --- a/src/applications/diffusion/controller/DiffusionController.php +++ b/src/applications/diffusion/controller/DiffusionController.php @@ -64,6 +64,20 @@ abstract class DiffusionController extends PhabricatorController { return new Aphront404Response(); } + // If the client is making a request like "/diffusion/1/...", but the + // repository has a different canonical path like "/diffusion/XYZ/...", + // redirect them to the canonical path. + + $request_path = $request->getPath(); + $repository = $drequest->getRepository(); + + $canonical_path = $repository->getCanonicalPath($request_path); + if ($canonical_path !== null) { + if ($canonical_path != $request_path) { + return id(new AphrontRedirectResponse())->setURI($canonical_path); + } + } + $this->diffusionRequest = $drequest; return null; diff --git a/src/applications/diffusion/DiffusionPullEventGarbageCollector.php b/src/applications/diffusion/garbagecollector/DiffusionPullEventGarbageCollector.php similarity index 100% rename from src/applications/diffusion/DiffusionPullEventGarbageCollector.php rename to src/applications/diffusion/garbagecollector/DiffusionPullEventGarbageCollector.php diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 9b38f911d4..461f495c55 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -687,6 +687,55 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO return "/r{$callsign}{$identifier}"; } + public function getCanonicalPath($request_path) { + $standard_pattern = + '(^'. + '(?P/diffusion/)'. + '(?P[^/]+)'. + '(?P(?:/.*)?)'. + '\z)'; + + $matches = null; + if (preg_match($standard_pattern, $request_path, $matches)) { + $prefix = $matches['prefix']; + + $callsign = $this->getCallsign(); + if ($callsign) { + $identifier = $callsign; + } else { + $identifier = $this->getID(); + } + + $suffix = $matches['suffix']; + if (!strlen($suffix)) { + $suffix = '/'; + } + + return $prefix.$identifier.$suffix; + } + + $commit_pattern = + '(^'. + '(?P/)'. + '(?P'. + '(?:'. + 'r(?P[A-Z]+)'. + '|'. + 'R(?P[1-9]\d*):'. + ')'. + '(?P[a-f0-9]+)'. + ')'. + '\z)'; + + $matches = null; + if (preg_match($commit_pattern, $request_path, $matches)) { + $commit = $matches['commit']; + return $this->getCommitURI($commit); + } + + return null; + } + public function generateURI(array $params) { $req_branch = false; $req_commit = false; diff --git a/src/applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php b/src/applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php index 0d13cf0e08..3994567225 100644 --- a/src/applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php +++ b/src/applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php @@ -9,6 +9,47 @@ final class PhabricatorRepositoryURITestCase ); } + public function testRepositoryURICanonicalization() { + $repo = id(new PhabricatorRepository()) + ->makeEphemeral() + ->setID(123); + + $tests = array( + '/diffusion/123' => '/diffusion/123/', + '/diffusion/123/' => '/diffusion/123/', + '/diffusion/123/browse/master/' => '/diffusion/123/browse/master/', + '/kangaroo/' => null, + ); + + foreach ($tests as $input => $expect) { + $this->assertEqual( + $expect, + $repo->getCanonicalPath($input), + pht('Canonical Path (ID, No Callsign): %s', $input)); + } + + $repo->setCallsign('XYZ'); + + $tests = array( + '/diffusion/123' => '/diffusion/XYZ/', + '/diffusion/123/' => '/diffusion/XYZ/', + '/diffusion/123/browse/master/' => '/diffusion/XYZ/browse/master/', + '/diffusion/XYZ' => '/diffusion/XYZ/', + '/diffusion/XYZ/' => '/diffusion/XYZ/', + '/diffusion/XYZ/browse/master/' => '/diffusion/XYZ/browse/master/', + '/diffusion/ABC/' => '/diffusion/XYZ/', + '/kangaroo/' => null, + '/R1:abcdef' => '/rXYZabcdef', + ); + + foreach ($tests as $input => $expect) { + $this->assertEqual( + $expect, + $repo->getCanonicalPath($input), + pht('Canonical Path (ID, Callsign): %s', $input)); + } + } + public function testURIGeneration() { $svn = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN; $git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT; From 74a79aa634ef9f1ecd02f68f5757063cd5db68f1 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 18 Feb 2016 04:38:11 -0800 Subject: [PATCH 33/42] Make serving repositories work with alternate URIs Summary: Ref T4245. Consolidates the URI parsing/rewriting logic so that repositories can be served from either `/diffusion/XYZ/` or `/diffusion/123/`, over both HTTP and SSH. Test Plan: - Pulled a Git repository by ID and callsign over HTTP and SSH. - Pulled a Mercurial repository by ID and callsign over HTTP and SSH. - Pulled a Subversion repository by ID and callsign over SSH (no HTTP support for SVN). Reviewers: chad Reviewed By: chad Maniphest Tasks: T4245 Differential Revision: https://secure.phabricator.com/D15302 --- .../controller/DiffusionServeController.php | 16 ++++++--- .../diffusion/ssh/DiffusionSSHWorkflow.php | 31 ++++++++++------ .../DiffusionSubversionServeSSHWorkflow.php | 14 ++++---- .../storage/PhabricatorRepository.php | 35 +++++++++++++++++++ 4 files changed, 74 insertions(+), 22 deletions(-) diff --git a/src/applications/diffusion/controller/DiffusionServeController.php b/src/applications/diffusion/controller/DiffusionServeController.php index 9de692d620..84010d76af 100644 --- a/src/applications/diffusion/controller/DiffusionServeController.php +++ b/src/applications/diffusion/controller/DiffusionServeController.php @@ -262,17 +262,23 @@ final class DiffusionServeController extends DiffusionController { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $result = new PhabricatorVCSResponse( 500, - pht('This is not a Git repository.')); + pht( + 'This repository ("%s") is not a Git repository.', + $repository->getDisplayName())); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $result = new PhabricatorVCSResponse( 500, - pht('This is not a Mercurial repository.')); + pht( + 'This repository ("%s") is not a Mercurial repository.', + $repository->getDisplayName())); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $result = new PhabricatorVCSResponse( 500, - pht('This is not a Subversion repository.')); + pht( + 'This repository ("%s") is not a Subversion repository.', + $repository->getDisplayName())); break; default: $result = new PhabricatorVCSResponse( @@ -480,7 +486,9 @@ final class DiffusionServeController extends DiffusionController { private function getRequestDirectoryPath(PhabricatorRepository $repository) { $request = $this->getRequest(); $request_path = $request->getRequestURI()->getPath(); - $base_path = preg_replace('@^/diffusion/[A-Z]+@', '', $request_path); + + $info = PhabricatorRepository::parseRepositoryServicePath($request_path); + $base_path = $info['path']; // For Git repositories, strip an optional directory component if it // isn't the name of a known Git resource. This allows users to clone diff --git a/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php index 2b2c820773..15333ff360 100644 --- a/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php +++ b/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php @@ -6,6 +6,7 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow { private $repository; private $hasWriteAccess; private $proxyURI; + private $baseRequestPath; public function getRepository() { if (!$this->repository) { @@ -45,6 +46,10 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow { abstract protected function identifyRepository(); abstract protected function executeRepositoryOperations(); + protected function getBaseRequestPath() { + return $this->baseRequestPath; + } + protected function writeError($message) { $this->getErrorChannel()->write($message); return $this; @@ -149,25 +154,29 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow { protected function loadRepositoryWithPath($path) { $viewer = $this->getUser(); - $regex = '@^/?diffusion/(?P[A-Z]+)(?:/|\z)@'; - $matches = null; - if (!preg_match($regex, $path, $matches)) { + $info = PhabricatorRepository::parseRepositoryServicePath($path); + if ($info === null) { throw new Exception( pht( - 'Unrecognized repository path "%s". Expected a path like "%s".', + 'Unrecognized repository path "%s". Expected a path like "%s" '. + 'or "%s".', $path, - '/diffusion/X/')); + '/diffusion/X/', + '/diffusion/123/')); } - $callsign = $matches[1]; + $identifier = $info['identifier']; + $base = $info['base']; + + $this->baseRequestPath = $base; + $repository = id(new PhabricatorRepositoryQuery()) ->setViewer($viewer) - ->withCallsigns(array($callsign)) + ->withIdentifiers(array($identifier)) ->executeOne(); - if (!$repository) { throw new Exception( - pht('No repository "%s" exists!', $callsign)); + pht('No repository "%s" exists!', $identifier)); } switch ($repository->getServeOverSSH()) { @@ -179,7 +188,9 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow { case PhabricatorRepository::SERVE_OFF: default: throw new Exception( - pht('This repository is not available over SSH.')); + pht( + 'This repository ("%s") is not available over SSH.', + $repository->getDisplayName())); } return $repository; diff --git a/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php index 257e6e9adb..4e591d318b 100644 --- a/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php +++ b/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php @@ -377,14 +377,12 @@ final class DiffusionSubversionServeSSHWorkflow $repository = $this->getRepository(); $path = $this->getPathFromSubversionURI($uri_string); - $path = preg_replace( - '(^/diffusion/[A-Z]+)', - rtrim($repository->getLocalPath(), '/'), - $path); + $external_base = $this->getBaseRequestPath(); - if (preg_match('(^/diffusion/[A-Z]+/\z)', $path)) { - $path = rtrim($path, '/'); - } + // Replace "/diffusion/X" in the request with the repository local path, + // so "/diffusion/X/master/" becomes "/path/to/repository/X/master/". + $local_path = rtrim($repository->getLocalPath(), '/'); + $path = $local_path.substr($path, strlen($external_base)); // NOTE: We are intentionally NOT removing username information from the // URI. Subversion retains it over the course of the request and considers @@ -398,7 +396,7 @@ final class DiffusionSubversionServeSSHWorkflow if ($this->externalBaseURI === null) { $pre = (string)id(clone $uri)->setPath(''); - $external_path = '/diffusion/'.$repository->getCallsign(); + $external_path = $external_base; $external_path = $this->normalizeSVNPath($external_path); $this->externalBaseURI = $pre.$external_path; diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 461f495c55..3b1f2c8561 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -687,6 +687,41 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO return "/r{$callsign}{$identifier}"; } + public static function parseRepositoryServicePath($request_path) { + // NOTE: In Mercurial over SSH, the path will begin without a leading "/", + // so we're matching it optionally. + + $patterns = array( + '(^'. + '(?P/?diffusion/(?P[A-Z]+|[0-9]\d*))'. + '(?P(?:/.*)?)'. + '\z)', + ); + + $identifier = null; + foreach ($patterns as $pattern) { + $matches = null; + if (!preg_match($pattern, $request_path, $matches)) { + continue; + } + + $identifier = $matches['identifier']; + $base = $matches['base']; + $path = $matches['path']; + break; + } + + if ($identifier === null) { + return null; + } + + return array( + 'identifier' => $identifier, + 'base' => $base, + 'path' => $path, + ); + } + public function getCanonicalPath($request_path) { $standard_pattern = '(^'. From b63eb09cacf1214fe98e03558336231bd087ead2 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 18 Feb 2016 05:27:01 -0800 Subject: [PATCH 34/42] Don't require a callsign to set a repository's local path Summary: Ref T4245. When creating new repositories, set a default local path based on the repository ID instead of callsign. Test Plan: - Created a new repository. - Saw it get a reasonable, ID-based local path. - Edited a repository to make sure the `applyFinalEffects()` wasn't doing anything whacky. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4245 Differential Revision: https://secure.phabricator.com/D15303 --- .../DiffusionRepositoryCreateController.php | 11 --------- .../editor/PhabricatorRepositoryEditor.php | 23 +++++++++++++++++++ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php index c6e2796fc1..4b876f3cf9 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php @@ -134,7 +134,6 @@ final class DiffusionRepositoryCreateController $type_name = PhabricatorRepositoryTransaction::TYPE_NAME; $type_vcs = PhabricatorRepositoryTransaction::TYPE_VCS; $type_activate = PhabricatorRepositoryTransaction::TYPE_ACTIVATE; - $type_local_path = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH; $type_remote_uri = PhabricatorRepositoryTransaction::TYPE_REMOTE_URI; $type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING; $type_http = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP; @@ -179,16 +178,6 @@ final class DiffusionRepositoryCreateController ->setTransactionType($type_service) ->setNewValue($service->getPHID()); } - - $default_local_path = PhabricatorEnv::getEnvConfig( - 'repository.default-local-path'); - - $default_local_path = rtrim($default_local_path, '/'); - $default_local_path = $default_local_path.'/'.$callsign.'/'; - - $xactions[] = id(clone $template) - ->setTransactionType($type_local_path) - ->setNewValue($default_local_path); } if ($is_init) { diff --git a/src/applications/repository/editor/PhabricatorRepositoryEditor.php b/src/applications/repository/editor/PhabricatorRepositoryEditor.php index 54f737c232..f6674835fc 100644 --- a/src/applications/repository/editor/PhabricatorRepositoryEditor.php +++ b/src/applications/repository/editor/PhabricatorRepositoryEditor.php @@ -522,4 +522,27 @@ final class PhabricatorRepositoryEditor return true; } + protected function applyFinalEffects( + PhabricatorLiskDAO $object, + array $xactions) { + + // If the repository does not have a local path yet, assign it one based + // on its ID. We can't do this earlier because we won't have an ID yet. + $local_path = $object->getDetail('local-path'); + if (!strlen($local_path)) { + $local_key = 'repository.default-local-path'; + + $local_root = PhabricatorEnv::getEnvConfig($local_key); + $local_root = rtrim($local_root, '/'); + + $id = $object->getID(); + $local_path = "{$local_root}/{$id}/"; + + $object->setDetail('local-path', $local_path); + $object->save(); + } + + return $xactions; + } + } From dfc8f8bcb465aa509b944eca6f6d87af075ce015 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 18 Feb 2016 05:54:50 -0800 Subject: [PATCH 35/42] Make callsigns editable on repository basic information Summary: Ref T4245. This is a prelude to removing them from the "create" screen. Currently, if you try to delete the callsign you get an unceremonious database error, but the next diff (or maybe two) will permit that, so I didn't put any "this is required yada yada" text in. This could also maybe use some big flashing warning lights and a "if you edit this, all your URIs break" but I'll save that for later. Test Plan: Changed the callsign for a repository. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4245 Differential Revision: https://secure.phabricator.com/D15304 --- ...DiffusionRepositoryEditBasicController.php | 19 +++++++ .../editor/PhabricatorRepositoryEditor.php | 50 ++++++++++++++++++- .../storage/PhabricatorRepository.php | 28 ++++++++++- .../PhabricatorRepositoryTransaction.php | 21 ++++++++ 4 files changed, 115 insertions(+), 3 deletions(-) diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php index 65f7dd0974..e23a57e655 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php @@ -18,11 +18,13 @@ final class DiffusionRepositoryEditBasicController $v_name = $repository->getName(); $v_desc = $repository->getDetail('description'); $v_slug = $repository->getRepositorySlug(); + $v_callsign = $repository->getCallsign(); $v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs( $repository->getPHID(), PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); $e_name = true; $e_slug = null; + $e_callsign = null; $errors = array(); $validation_exception = null; @@ -31,6 +33,7 @@ final class DiffusionRepositoryEditBasicController $v_desc = $request->getStr('description'); $v_projects = $request->getArr('projectPHIDs'); $v_slug = $request->getStr('slug'); + $v_callsign = $request->getStr('callsign'); if (!strlen($v_name)) { $e_name = pht('Required'); @@ -47,6 +50,7 @@ final class DiffusionRepositoryEditBasicController $type_desc = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION; $type_edge = PhabricatorTransactions::TYPE_EDGE; $type_slug = PhabricatorRepositoryTransaction::TYPE_SLUG; + $type_callsign = PhabricatorRepositoryTransaction::TYPE_CALLSIGN; $xactions[] = id(clone $template) ->setTransactionType($type_name) @@ -60,6 +64,10 @@ final class DiffusionRepositoryEditBasicController ->setTransactionType($type_slug) ->setNewValue($v_slug); + $xactions[] = id(clone $template) + ->setTransactionType($type_callsign) + ->setNewValue($v_callsign); + $xactions[] = id(clone $template) ->setTransactionType($type_edge) ->setMetadataValue( @@ -78,11 +86,16 @@ final class DiffusionRepositoryEditBasicController try { $editor->applyTransactions($repository, $xactions); + // The preferred edit URI may have changed if the callsign or slug + // were adjusted, so grab a fresh copy. + $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); + return id(new AphrontRedirectResponse())->setURI($edit_uri); } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; $e_slug = $ex->getShortMessage($type_slug); + $e_callsign = $ex->getShortMessage($type_callsign); } } } @@ -106,6 +119,12 @@ final class DiffusionRepositoryEditBasicController ->setLabel(pht('Short Name')) ->setValue($v_slug) ->setError($e_slug)) + ->appendChild( + id(new AphrontFormTextControl()) + ->setName('callsign') + ->setLabel(pht('Callsign')) + ->setValue($v_callsign) + ->setError($e_callsign)) ->appendChild( id(new PhabricatorRemarkupControl()) ->setUser($viewer) diff --git a/src/applications/repository/editor/PhabricatorRepositoryEditor.php b/src/applications/repository/editor/PhabricatorRepositoryEditor.php index f6674835fc..fa585a826a 100644 --- a/src/applications/repository/editor/PhabricatorRepositoryEditor.php +++ b/src/applications/repository/editor/PhabricatorRepositoryEditor.php @@ -45,6 +45,7 @@ final class PhabricatorRepositoryEditor $types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES; $types[] = PhabricatorRepositoryTransaction::TYPE_STAGING_URI; $types[] = PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS; + $types[] = PhabricatorRepositoryTransaction::TYPE_CALLSIGN; $types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; @@ -110,6 +111,8 @@ final class PhabricatorRepositoryEditor return $object->getDetail('staging-uri'); case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: return $object->getDetail('automation.blueprintPHIDs', array()); + case PhabricatorRepositoryTransaction::TYPE_CALLSIGN: + return $object->getCallsign(); } } @@ -148,6 +151,7 @@ final class PhabricatorRepositoryEditor case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: return $xaction->getNewValue(); case PhabricatorRepositoryTransaction::TYPE_SLUG: + case PhabricatorRepositoryTransaction::TYPE_CALLSIGN: $name = $xaction->getNewValue(); if (strlen($name)) { return $name; @@ -240,6 +244,9 @@ final class PhabricatorRepositoryEditor 'automation.blueprintPHIDs', $xaction->getNewValue()); return; + case PhabricatorRepositoryTransaction::TYPE_CALLSIGN: + $object->setCallsign($xaction->getNewValue()); + return; case PhabricatorRepositoryTransaction::TYPE_ENCODING: // Make sure the encoding is valid by converting to UTF-8. This tests // that the user has mbstring installed, and also that they didn't type @@ -468,7 +475,7 @@ final class PhabricatorRepositoryEditor } try { - PhabricatorRepository::asssertValidRepositorySlug($new); + PhabricatorRepository::assertValidRepositorySlug($new); } catch (Exception $ex) { $errors[] = new PhabricatorApplicationTransactionValidationError( $type, @@ -495,6 +502,47 @@ final class PhabricatorRepositoryEditor } break; + case PhabricatorRepositoryTransaction::TYPE_CALLSIGN: + foreach ($xactions as $xaction) { + $old = $xaction->getOldValue(); + $new = $xaction->getNewValue(); + + if (!strlen($new)) { + continue; + } + + if ($new === $old) { + continue; + } + + try { + PhabricatorRepository::assertValidCallsign($new); + } catch (Exception $ex) { + $errors[] = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Invalid'), + $ex->getMessage(), + $xaction); + continue; + } + + $other = id(new PhabricatorRepositoryQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withCallsigns(array($new)) + ->executeOne(); + if ($other && ($other->getID() !== $object->getID())) { + $errors[] = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Duplicate'), + pht( + 'The selected callsign ("%s") is already in use by another '. + 'repository. Choose a unique callsign.', + $new), + $xaction); + continue; + } + } + break; } return $errors; diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 3b1f2c8561..d4977d2b89 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -317,14 +317,14 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO public static function isValidRepositorySlug($slug) { try { - self::asssertValidRepositorySlug($slug); + self::assertValidRepositorySlug($slug); return true; } catch (Exception $ex) { return false; } } - public static function asssertValidRepositorySlug($slug) { + public static function assertValidRepositorySlug($slug) { if (!strlen($slug)) { throw new Exception( pht( @@ -391,6 +391,30 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO } } + public static function assertValidCallsign($callsign) { + if (!strlen($callsign)) { + throw new Exception( + pht( + 'A repository callsign must be at least one character long.')); + } + + if (strlen($callsign) > 32) { + throw new Exception( + pht( + 'The callsign "%s" is not a valid repository callsign. Callsigns '. + 'must be no more than 32 bytes long.', + $callsign)); + } + + if (!preg_match('/^[A-Z]+\z/', $callsign)) { + throw new Exception( + pht( + 'The callsign "%s" is not a valid repository callsign. Callsigns '. + 'may only contain UPPERCASE letters.', + $callsign)); + } + } + /* -( Remote Command Execution )------------------------------------------- */ diff --git a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php index a10ac0a60b..20c975cb33 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php +++ b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php @@ -29,6 +29,7 @@ final class PhabricatorRepositoryTransaction const TYPE_SYMBOLS_LANGUAGE = 'repo:symbol-language'; const TYPE_STAGING_URI = 'repo:staging-uri'; const TYPE_AUTOMATION_BLUEPRINTS = 'repo:automation-blueprints'; + const TYPE_CALLSIGN = 'repo:callsign'; // TODO: Clean up these legacy transaction types. const TYPE_SSH_LOGIN = 'repo:ssh-login'; @@ -466,6 +467,26 @@ final class PhabricatorRepositoryTransaction new PhutilNumber(count($rem)), $this->renderHandleList($rem)); } + + case self::TYPE_CALLSIGN: + if ($old === null) { + return pht( + '%s set the callsign for this repository to "%s".', + $this->renderHandleLink($author_phid), + $new); + } else if ($new === null) { + return pht( + '%s removed the callsign ("%s") for this repository.', + $this->renderHandleLink($author_phid), + $old); + } else { + return pht( + '%s changed the callsign for this repository from "%s" to "%s".', + $this->renderHandleLink($author_phid), + $old, + $new); + } + } return parent::getTitle(); From dc7d0b4a56d83a032306c9d6aada7e855641eed4 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 18 Feb 2016 06:03:41 -0800 Subject: [PATCH 36/42] Make repository callsigns optional Summary: Ref T4245. This could still use a little UI smoothing, but: - Don't require a callsign on the create flow (you can add one later in "Edit Basic Information" if you want). - Allow existing callsigns to be removed. Test Plan: - Created a new repository with no callsign. - Cloned it; pushed to it. - Browsed around Diffusion a bunch. - Visited a commit URI. - Added a callsign to it. - Removed the callsign again. - Referenced it with `R22` in remarkup. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4245 Differential Revision: https://secure.phabricator.com/D15305 --- .../sql/autopatches/20160218.callsigns.1.sql | 4 ++ .../DiffusionRepositoryCreateController.php | 61 +------------------ .../DiffusionRepositoryEditMainController.php | 7 ++- .../storage/PhabricatorRepository.php | 41 ++++++++++--- 4 files changed, 46 insertions(+), 67 deletions(-) create mode 100644 resources/sql/autopatches/20160218.callsigns.1.sql diff --git a/resources/sql/autopatches/20160218.callsigns.1.sql b/resources/sql/autopatches/20160218.callsigns.1.sql new file mode 100644 index 0000000000..09d1dd5a1b --- /dev/null +++ b/resources/sql/autopatches/20160218.callsigns.1.sql @@ -0,0 +1,4 @@ +/* Make callsigns nullable, and thus optional. */ + +ALTER TABLE {$NAMESPACE}_repository.repository + CHANGE callsign callsign VARCHAR(32) COLLATE {$COLLATE_SORT}; diff --git a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php index 4b876f3cf9..1333bf67f9 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php @@ -149,14 +149,6 @@ final class DiffusionRepositoryCreateController // If we're creating a new repository, set all this core stuff. if ($is_create) { - $callsign = $form->getPage('name') - ->getControl('callsign')->getValue(); - - // We must set this to a unique value to save the repository - // initially, and it's immutable, so we don't bother using - // transactions to apply this change. - $repository->setCallsign($callsign); - $xactions[] = id(clone $template) ->setTransactionType($type_name) ->setNewValue( @@ -343,7 +335,7 @@ final class DiffusionRepositoryCreateController } -/* -( Page: Name and Callsign )-------------------------------------------- */ +/* -( Page: Name )--------------------------------------------------------- */ private function buildNamePage() { @@ -359,23 +351,7 @@ final class DiffusionRepositoryCreateController ->addControl( id(new AphrontFormTextControl()) ->setName('name') - ->setLabel(pht('Name')) - ->setCaption(pht('Human-readable repository name.'))) - ->addRemarkupInstructions( - pht( - '**Choose a "Callsign" for the repository.** This is a short, '. - 'unique string which identifies commits elsewhere in Phabricator. '. - 'For example, you might use `M` for your mobile app repository '. - 'and `B` for your backend repository.'. - "\n\n". - '**Callsigns must be UPPERCASE**, and can not be edited after the '. - 'repository is created. Generally, you should choose short '. - 'callsigns.')) - ->addControl( - id(new AphrontFormTextControl()) - ->setName('callsign') - ->setLabel(pht('Callsign')) - ->setCaption(pht('Short UPPERCASE identifier.'))); + ->setLabel(pht('Name'))); } public function validateNamePage(PHUIFormPageView $page) { @@ -387,38 +363,7 @@ final class DiffusionRepositoryCreateController pht('You must choose a name for this repository.')); } - $c_call = $page->getControl('callsign'); - $v_call = $c_call->getValue(); - if (!strlen($v_call)) { - $c_call->setError(pht('Required')); - $page->addPageError( - pht('You must choose a callsign for this repository.')); - } else if (!preg_match('/^[A-Z]+\z/', $v_call)) { - $c_call->setError(pht('Invalid')); - $page->addPageError( - pht('The callsign must contain only UPPERCASE letters.')); - } else { - $exists = false; - try { - $repo = id(new PhabricatorRepositoryQuery()) - ->setViewer($this->getRequest()->getUser()) - ->withCallsigns(array($v_call)) - ->executeOne(); - $exists = (bool)$repo; - } catch (PhabricatorPolicyException $ex) { - $exists = true; - } - if ($exists) { - $c_call->setError(pht('Not Unique')); - $page->addPageError( - pht( - 'Another repository already uses that callsign. You must choose '. - 'a unique callsign.')); - } - } - - return $c_name->isValid() && - $c_call->isValid(); + return $c_name->isValid(); } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php index 4efafc1a0e..0ec8cdd6b8 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php @@ -284,7 +284,12 @@ final class DiffusionRepositoryEditMainController $repository->getVersionControlSystem()); $view->addProperty(pht('Type'), $type); - $view->addProperty(pht('Callsign'), $repository->getCallsign()); + + $callsign = $repository->getCallsign(); + if (!strlen($callsign)) { + $callsign = phutil_tag('em', array(), pht('No Callsign')); + } + $view->addProperty(pht('Callsign'), $callsign); $short_name = $repository->getRepositorySlug(); if ($short_name === null) { diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index d4977d2b89..c2ed8023f3 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -93,7 +93,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO ), self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'sort255', - 'callsign' => 'sort32', + 'callsign' => 'sort32?', 'repositorySlug' => 'sort64?', 'versionControlSystem' => 'text32', 'uuid' => 'text64?', @@ -149,13 +149,21 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO } public function getMonogram() { - return 'r'.$this->getCallsign(); + $callsign = $this->getCallsign(); + if (strlen($callsign)) { + return "r{$callsign}"; + } + + $id = $this->getID(); + return "R{$id}"; } public function getDisplayName() { - // TODO: This is intended to produce a human-readable name that is not - // necessarily a global, unique identifier. Eventually, it may just return - // a string like "skynet" instead of "rSKYNET". + $slug = $this->getRepositorySlug(); + if (strlen($slug)) { + return $slug; + } + return $this->getMonogram(); } @@ -699,7 +707,13 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO } public function getURI() { - return '/diffusion/'.$this->getCallsign().'/'; + $callsign = $this->getCallsign(); + if (strlen($callsign)) { + return "/diffusion/{$callsign}/"; + } + + $id = $this->getID(); + return "/diffusion/{$id}/"; } public function getPathURI($path) { @@ -708,7 +722,12 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO public function getCommitURI($identifier) { $callsign = $this->getCallsign(); - return "/r{$callsign}{$identifier}"; + if (strlen($callsign)) { + return "/r{$callsign}{$identifier}"; + } + + $id = $this->getID(); + return "/R{$id}:{$identifier}"; } public static function parseRepositoryServicePath($request_path) { @@ -1063,7 +1082,13 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO } if ($need_scope) { - $scope = 'r'.$this->getCallsign(); + $callsign = $this->getCallsign(); + if ($callsign) { + $scope = "r{$callsign}"; + } else { + $id = $this->getID(); + $scope = "R{$id}:"; + } $name = $scope.$name; } From af1fef242c66390a368db1d51ab62509dc08b7ab Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 18 Feb 2016 09:47:08 -0800 Subject: [PATCH 37/42] Fix an issue with editing pre-space objects using a form with no visibility controls Summary: WMF ran into this after their update. Here's the setup: - When you enable Spaces, we leave all existing objects set to `null`, which means "these belong to the default space". This is so we don't have to go update a trillion objects. - New objects get set to the default space explicitly (`PHID-SPCE-...`) but older ones stay with `null`. - If you edit an older object (like a task) from the time before Spaces, //and// the form doesn't have a Visbility/Spaces control, we would incorrectly poplate the value with `null` when the effective value should be the default space PHID. - This caused a "You must choose a space." error in the UI. Instead, populate the control with the effective value instead of the literal database value. This makes the edit go through cleanly. Also add a note about this for future-me. Test Plan: - Disabled "Visibility" control in task edit form. - Edited an old task which had `null` as a `spacePHID` in the database. - Before patch: UI error about selecting a Space. - After patch: edit goes through cleanly. Reviewers: chad, 20after4 Reviewed By: chad, 20after4 Subscribers: 20after4, aklapper Differential Revision: https://secure.phabricator.com/D15306 --- .../policy/editor/PhabricatorPolicyEditEngineExtension.php | 5 ++++- .../storage/PhabricatorApplicationTransaction.php | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/applications/policy/editor/PhabricatorPolicyEditEngineExtension.php b/src/applications/policy/editor/PhabricatorPolicyEditEngineExtension.php index fd973c0da7..568b7bc399 100644 --- a/src/applications/policy/editor/PhabricatorPolicyEditEngineExtension.php +++ b/src/applications/policy/editor/PhabricatorPolicyEditEngineExtension.php @@ -101,6 +101,9 @@ final class PhabricatorPolicyEditEngineExtension if ($capability == PhabricatorPolicyCapability::CAN_VIEW) { $type_space = PhabricatorTransactions::TYPE_SPACE; if (isset($types[$type_space])) { + $space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID( + $object); + $space_field = id(new PhabricatorSpaceEditField()) ->setKey('spacePHID') ->setLabel(pht('Space')) @@ -114,7 +117,7 @@ final class PhabricatorPolicyEditEngineExtension ->setConduitDescription( pht('Shift the object between spaces.')) ->setConduitTypeDescription(pht('New space PHID.')) - ->setValue($object->getSpacePHID()); + ->setValue($space_phid); $fields[] = $space_field; $space_field->setPolicyField($policy_field); diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index 6fa6a6458e..7d50f8495c 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -527,6 +527,11 @@ abstract class PhabricatorApplicationTransaction // TODO: Remove this eventually, this is handling old changes during // object creation prior to the introduction of "create" and "default" // transaction display flags. + + // NOTE: We can also hit this case with Space transactions that later + // update a default space (`null`) to an explicit space, so handling + // the Space case may require some finesse. + if ($this->getOldValue() === null) { return true; } else { From 5eecff91cdd3caeab222b46a773c6058676b82ac Mon Sep 17 00:00:00 2001 From: Chad Little Date: Fri, 19 Feb 2016 04:18:33 +0000 Subject: [PATCH 38/42] Move Diffusion ReadmeView to PHUIDocumentProView Summary: Swapping out to PHUIDocumentProView to remove all calls to PHUIDocumentView. Test Plan: Review the Phabricator Readme.MD in Diffusion Reviewers: epriestley, avivey Reviewed By: avivey Subscribers: avivey, Korvin Differential Revision: https://secure.phabricator.com/D15308 --- resources/celerity/map.php | 4 ++-- .../diffusion/view/DiffusionReadmeView.php | 11 +++++++---- .../css/application/diffusion/diffusion-readme.css | 13 ++++++------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index abb3fbb36a..f92d47c1c8 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -65,7 +65,7 @@ return array( 'rsrc/css/application/differential/revision-list.css' => 'f3c47d33', 'rsrc/css/application/differential/table-of-contents.css' => 'ae4b7a55', 'rsrc/css/application/diffusion/diffusion-icons.css' => '2941baf1', - 'rsrc/css/application/diffusion/diffusion-readme.css' => '2106ea08', + 'rsrc/css/application/diffusion/diffusion-readme.css' => '356a4f3c', 'rsrc/css/application/diffusion/diffusion-source.css' => '075ba788', 'rsrc/css/application/feed/feed.css' => 'ecd4ec57', 'rsrc/css/application/files/global-drag-and-drop.css' => '5c1b47c2', @@ -551,7 +551,7 @@ return array( 'differential-revision-list-css' => 'f3c47d33', 'differential-table-of-contents-css' => 'ae4b7a55', 'diffusion-icons-css' => '2941baf1', - 'diffusion-readme-css' => '2106ea08', + 'diffusion-readme-css' => '356a4f3c', 'diffusion-source-css' => '075ba788', 'diviner-shared-css' => 'aa3656aa', 'font-aleo' => '8bdb2835', diff --git a/src/applications/diffusion/view/DiffusionReadmeView.php b/src/applications/diffusion/view/DiffusionReadmeView.php index 4f4e70ed0d..16924ddd98 100644 --- a/src/applications/diffusion/view/DiffusionReadmeView.php +++ b/src/applications/diffusion/view/DiffusionReadmeView.php @@ -109,11 +109,14 @@ final class DiffusionReadmeView extends DiffusionView { $header = id(new PHUIHeaderView()) ->setHeader($readme_name); - return id(new PHUIDocumentView()) + $document = id(new PHUIDocumentViewPro()) ->setFluid(true) - ->appendChild($readme_content) - ->addClass('diffusion-readme-view') - ->setHeader($header); + ->appendChild($readme_content); + + return id(new PHUIObjectBoxView()) + ->setHeader($header) + ->appendChild($document) + ->addClass('diffusion-readme-view'); } } diff --git a/webroot/rsrc/css/application/diffusion/diffusion-readme.css b/webroot/rsrc/css/application/diffusion/diffusion-readme.css index 17f9baa04c..f7f8506e2d 100644 --- a/webroot/rsrc/css/application/diffusion/diffusion-readme.css +++ b/webroot/rsrc/css/application/diffusion/diffusion-readme.css @@ -2,13 +2,12 @@ * @provides diffusion-readme-css */ -.device .diffusion-readme-view { - margin: 16px 8px 0; - background-color: #fff; - border: 1px solid {$lightblueborder}; - border-bottom: 1px solid {$blueborder}; +.device-desktop .diffusion-readme-view .phui-document-fluid + .phui-document-view { + margin: 0; + padding: 0 8px; } -.device-tablet .diffusion-readme-view { - margin: 16px; +.diffusion-readme-view .phui-document-container { + border: none; } From 4e40b17acae31f470d5a46402198d37edc2ce68a Mon Sep 17 00:00:00 2001 From: Alexander Ljungberg Date: Fri, 19 Feb 2016 05:06:14 -0800 Subject: [PATCH 39/42] Don't mutate DOM by showing a tooltip on touch events Summary: This fix further addresses T10229. The problem and solution are the same: - If the DOM is mutated during a touch, it never registers as a 'click' so the tapped button does not activate. - This was partially addressed in D15136, which covered taps on code lines in a Differential view. - Tapping on some buttons, like "Reply" or "Hide Comment" still caused the problem by showing a tooltip. - There are probably similar buttons elsewhere, other than in Differential, exhibiting the same 'needs multiple taps to work' behaviour. - The testing in the iOS simulator performed for D15136 did not reveal that the problem with "Hide comment" and such remained because the small device size used for testing triggered the `!= 'desktop'` path for tooltips. To fix it: - Don't show tooltips for touch events. You can't 'hover' with a finger (with today's tech) so that UI paradigm doesn't apply. - Show the tooltips for regular mouse events, even if they are on the same device. Some devices have both touch and a mouse. - No longer try to rely on a distinction between 'desktop' and 'mobile' devices. Mobile devices like the iPad Pro are essentially desktop like, and as mentioned above, a single device could be both touch and mouse enabled. It's not about the nature of the device, it's about the nature of the interaction. Test Plan: - Tapped "Hide Comment", "Reply" on an iPad Pro running iOS 9.2 and got single touch responses with no tooltips. - Tried the same on an iPhone 6 running iOS 9.2. - Hovered over the same on a regular desktop in Safari and saw tooltips. Clicked and saw regular reactions. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15310 --- resources/celerity/map.php | 18 +++++++++--------- webroot/rsrc/js/core/behavior-tooltip.js | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index f92d47c1c8..bfac438b60 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -8,7 +8,7 @@ return array( 'names' => array( 'core.pkg.css' => '7935f211', - 'core.pkg.js' => 'd7daa6d8', + 'core.pkg.js' => '298d5888', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '2de124c9', 'differential.pkg.js' => 'd0cd0df6', @@ -501,7 +501,7 @@ return array( 'rsrc/js/core/behavior-time-typeahead.js' => 'f80d6bf0', 'rsrc/js/core/behavior-toggle-class.js' => '5d7c9f33', 'rsrc/js/core/behavior-tokenizer.js' => 'b3a4b884', - 'rsrc/js/core/behavior-tooltip.js' => '3ee3408b', + 'rsrc/js/core/behavior-tooltip.js' => '42fcb747', 'rsrc/js/core/behavior-watch-anchor.js' => '9f36c42d', 'rsrc/js/core/behavior-workflow.js' => '0a3f3021', 'rsrc/js/core/phtize.js' => 'd254d646', @@ -648,7 +648,7 @@ return array( 'javelin-behavior-phabricator-reveal-content' => '60821bc7', 'javelin-behavior-phabricator-search-typeahead' => '06c32383', 'javelin-behavior-phabricator-show-older-transactions' => 'dbbf48b6', - 'javelin-behavior-phabricator-tooltips' => '3ee3408b', + 'javelin-behavior-phabricator-tooltips' => '42fcb747', 'javelin-behavior-phabricator-transaction-comment-form' => 'b23b49e6', 'javelin-behavior-phabricator-transaction-list' => '13c739ea', 'javelin-behavior-phabricator-watch-anchor' => '9f36c42d', @@ -1119,12 +1119,6 @@ return array( 'javelin-util', 'javelin-uri', ), - '3ee3408b' => array( - 'javelin-behavior', - 'javelin-behavior-device', - 'javelin-stratcom', - 'phabricator-tooltip', - ), '3f5d6dbf' => array( 'javelin-behavior', 'javelin-dom', @@ -1140,6 +1134,12 @@ return array( 'javelin-dom', 'javelin-request', ), + '42fcb747' => array( + 'javelin-behavior', + 'javelin-behavior-device', + 'javelin-stratcom', + 'phabricator-tooltip', + ), '44959b73' => array( 'javelin-util', 'javelin-uri', diff --git a/webroot/rsrc/js/core/behavior-tooltip.js b/webroot/rsrc/js/core/behavior-tooltip.js index fd88956aef..8b9b8d4f23 100644 --- a/webroot/rsrc/js/core/behavior-tooltip.js +++ b/webroot/rsrc/js/core/behavior-tooltip.js @@ -18,7 +18,7 @@ JX.behavior('phabricator-tooltips', function() { return; } - if (JX.Device.getDevice() != 'desktop') { + if (e.getIsTouchEvent()) { return; } From 50f910ce67ac35ed2694d3198bfe7da412be91db Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 19 Feb 2016 11:43:19 -0800 Subject: [PATCH 40/42] Always install the "icon" and "emoji" remarkup rules Summary: Ref T10394. Currently, these rules are only active if the Macro application is installed. Instead, install them unconditionally. Test Plan: - Used `{icon camera}` with Macro installed and uninstalled. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10394 Differential Revision: https://secure.phabricator.com/D15311 --- .../macro/application/PhabricatorMacroApplication.php | 7 ------- src/infrastructure/markup/PhabricatorMarkupEngine.php | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/applications/macro/application/PhabricatorMacroApplication.php b/src/applications/macro/application/PhabricatorMacroApplication.php index fc7dcabab6..3519a3f520 100644 --- a/src/applications/macro/application/PhabricatorMacroApplication.php +++ b/src/applications/macro/application/PhabricatorMacroApplication.php @@ -42,13 +42,6 @@ final class PhabricatorMacroApplication extends PhabricatorApplication { ); } - public function getRemarkupRules() { - return array( - new PhabricatorIconRemarkupRule(), - new PhabricatorEmojiRemarkupRule(), - ); - } - protected function getCustomCapabilities() { return array( PhabricatorMacroManageCapability::CAPABILITY => array( diff --git a/src/infrastructure/markup/PhabricatorMarkupEngine.php b/src/infrastructure/markup/PhabricatorMarkupEngine.php index ed51b00175..c3d9dab02a 100644 --- a/src/infrastructure/markup/PhabricatorMarkupEngine.php +++ b/src/infrastructure/markup/PhabricatorMarkupEngine.php @@ -492,6 +492,9 @@ final class PhabricatorMarkupEngine extends Phobject { $rules[] = new PhabricatorYoutubeRemarkupRule(); } + $rules[] = new PhabricatorIconRemarkupRule(); + $rules[] = new PhabricatorEmojiRemarkupRule(); + $applications = PhabricatorApplication::getAllInstalledApplications(); foreach ($applications as $application) { foreach ($application->getRemarkupRules() as $rule) { From 7b1b146620b574c643623a4eee724da2e9817427 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 19 Feb 2016 13:41:03 -0800 Subject: [PATCH 41/42] Save, then restore scroll position in Chrome textareas on remarkup assist Summary: Fixes T10396. Seems like this has been around for a while (references from 2011): http://stackoverflow.com/questions/4002312/chrome-resets-the-textarea-scroll-bar-scrolltop-when-focus-is-called https://bugs.chromium.org/p/chromium/issues/detail?id=75072 Commenting out this `focus()` seemed to fix the issue locally, at the cost of not focusing. Saving, focusing, then restoring seems to produce the correct behavior everywhere. Test Plan: - In Safari, Firefox and Chrome, typed a ton of text into a remarkup area (more than the height of the area, so it has a scrollbar). - Selected some text near the top. - Clicked "B" to bold the text. - Scroll position remained the same in all browsers (previously: in Chrome, it changed). Reviewers: chad Reviewed By: chad Maniphest Tasks: T10396 Differential Revision: https://secure.phabricator.com/D15313 --- resources/celerity/map.php | 16 ++++++++-------- webroot/rsrc/js/core/TextAreaUtils.js | 7 +++++++ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index bfac438b60..9a9d5cd527 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -8,7 +8,7 @@ return array( 'names' => array( 'core.pkg.css' => '7935f211', - 'core.pkg.js' => '298d5888', + 'core.pkg.js' => '7d8faf57', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '2de124c9', 'differential.pkg.js' => 'd0cd0df6', @@ -462,7 +462,7 @@ return array( 'rsrc/js/core/Notification.js' => 'ccf1cbf8', 'rsrc/js/core/Prefab.js' => 'e67df814', 'rsrc/js/core/ShapedRequest.js' => '7cbe244b', - 'rsrc/js/core/TextAreaUtils.js' => '9e54692d', + 'rsrc/js/core/TextAreaUtils.js' => '5813016a', 'rsrc/js/core/Title.js' => 'df5e11d2', 'rsrc/js/core/ToolTip.js' => '6323f942', 'rsrc/js/core/behavior-active-nav.js' => 'e379b58e', @@ -775,7 +775,7 @@ return array( 'phabricator-slowvote-css' => 'da0afb1b', 'phabricator-source-code-view-css' => 'cbeef983', 'phabricator-standard-page-view' => 'e709f6d0', - 'phabricator-textareautils' => '9e54692d', + 'phabricator-textareautils' => '5813016a', 'phabricator-title' => 'df5e11d2', 'phabricator-tooltip' => '6323f942', 'phabricator-ui-example-css' => '528b19de', @@ -1275,6 +1275,11 @@ return array( 'javelin-request', 'javelin-util', ), + '5813016a' => array( + 'javelin-install', + 'javelin-dom', + 'javelin-vector', + ), '59a7976a' => array( 'javelin-install', 'javelin-dom', @@ -1621,11 +1626,6 @@ return array( 'phabricator-phtize', 'changeset-view-manager', ), - '9e54692d' => array( - 'javelin-install', - 'javelin-dom', - 'javelin-vector', - ), '9f36c42d' => array( 'javelin-behavior', 'javelin-stratcom', diff --git a/webroot/rsrc/js/core/TextAreaUtils.js b/webroot/rsrc/js/core/TextAreaUtils.js index f8c84c97a7..8f8a81067b 100644 --- a/webroot/rsrc/js/core/TextAreaUtils.js +++ b/webroot/rsrc/js/core/TextAreaUtils.js @@ -33,7 +33,14 @@ JX.install('TextAreaUtils', { setSelectionRange : function(area, start, end) { if ('setSelectionRange' in area) { + + // Chrome scrolls the textarea to the bottom as a side effect of + // calling focus(), so save the scroll position, focus, then restore + // the scroll position. + var scroll_top = area.scrollTop; area.focus(); + area.scrollTop = scroll_top; + area.setSelectionRange(start, end); } }, From 929b4ccb5cd29ea932a081ab5d69bf5e06a3ffd3 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Fri, 19 Feb 2016 14:45:05 -0800 Subject: [PATCH 42/42] Remove Similar Questions column from Ponder Summary: Not terribly useful. Also removed close your stuff reminder. Test Plan: View question I asked and strangers question. Both layout more normal like. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15312 --- resources/celerity/map.php | 4 +- .../PonderQuestionViewController.php | 76 ++----------------- .../css/application/ponder/ponder-view.css | 8 -- 3 files changed, 7 insertions(+), 81 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 9a9d5cd527..b632ba7d1e 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -92,7 +92,7 @@ return array( 'rsrc/css/application/policy/policy-edit.css' => '815c66f7', 'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43', 'rsrc/css/application/policy/policy.css' => '957ea14c', - 'rsrc/css/application/ponder/ponder-view.css' => '4486434b', + 'rsrc/css/application/ponder/ponder-view.css' => 'b40dc156', 'rsrc/css/application/project/project-card-view.css' => '9418c97d', 'rsrc/css/application/project/project-view.css' => '83bb6654', 'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733', @@ -855,7 +855,7 @@ return array( 'policy-css' => '957ea14c', 'policy-edit-css' => '815c66f7', 'policy-transaction-detail-css' => '82100a43', - 'ponder-view-css' => '4486434b', + 'ponder-view-css' => 'b40dc156', 'project-card-view-css' => '9418c97d', 'project-view-css' => '83bb6654', 'releeph-core' => '9b3c5733', diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php index 2ec911bdde..38ca4cad30 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -44,7 +44,6 @@ final class PonderQuestionViewController extends PonderController { $actions = $this->buildActionListView($question); $properties = $this->buildPropertyListView($question, $actions); - $sidebar = $this->buildSidebar($question); $content_id = celerity_generate_unique_node_id(); $timeline = $this->buildTransactionTimeline( @@ -81,20 +80,6 @@ final class PonderQuestionViewController extends PonderController { ->addPropertyList($properties) ->appendChild($footer); - if ($viewer->getPHID() == $question->getAuthorPHID()) { - $status = $question->getStatus(); - $answers_list = $question->getAnswers(); - if ($answers_list && ($status == PonderQuestionStatus::STATUS_OPEN)) { - $info_view = id(new PHUIInfoView()) - ->setSeverity(PHUIInfoView::SEVERITY_WARNING) - ->appendChild( - pht( - 'If this question has been resolved, please consider closing - the question and marking the answer as helpful.')); - $object_box->setInfoView($info_view); - } - } - $crumbs = $this->buildApplicationCrumbs($this->buildSideNavView()); $crumbs->addTextCrumb('Q'.$id, '/Q'.$id); @@ -107,21 +92,14 @@ final class PonderQuestionViewController extends PonderController { ->appendChild($answer); } - $ponder_view = id(new PHUITwoColumnView()) - ->setMainColumn(array( - $object_box, - $comment_view, - $answer_wiki, - $answers, - $answer_add_panel, - )) - ->setSideColumn($sidebar) - ->addClass('ponder-question-view'); - return $this->buildApplicationPage( array( $crumbs, - $ponder_view, + $object_box, + $comment_view, + $answer_wiki, + $answers, + $answer_add_panel, ), array( 'title' => 'Q'.$question->getID().' '.$question->getTitle(), @@ -261,48 +239,4 @@ final class PonderQuestionViewController extends PonderController { return $view; } - private function buildSidebar(PonderQuestion $question) { - $viewer = $this->getViewer(); - $status = $question->getStatus(); - $id = $question->getID(); - - $questions = id(new PonderQuestionQuery()) - ->setViewer($viewer) - ->withStatuses(array($status)) - ->withEdgeLogicPHIDs( - PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, - PhabricatorQueryConstraint::OPERATOR_OR, - $question->getProjectPHIDs()) - ->setLimit(10) - ->execute(); - - $list = id(new PHUIObjectItemListView()) - ->setUser($viewer) - ->setNoDataString(pht('No similar questions found.')); - - foreach ($questions as $question) { - if ($id == $question->getID()) { - continue; - } - $item = new PHUIObjectItemView(); - $item->setObjectName('Q'.$question->getID()); - $item->setHeader($question->getTitle()); - $item->setHref('/Q'.$question->getID()); - $item->setObject($question); - - $item->addAttribute( - pht( - '%s Answer(s)', - new PhutilNumber($question->getAnswerCount()))); - - $list->addItem($item); - } - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Similar Questions')) - ->setObjectList($list); - - return $box; - } - } diff --git a/webroot/rsrc/css/application/ponder/ponder-view.css b/webroot/rsrc/css/application/ponder/ponder-view.css index 565a6af83f..da0eab6e38 100644 --- a/webroot/rsrc/css/application/ponder/ponder-view.css +++ b/webroot/rsrc/css/application/ponder/ponder-view.css @@ -14,14 +14,6 @@ border-right: 1px solid {$lightblueborder}; } -.ponder-question-view .phui-property-list-properties-wrap { - width: 66%; -} - -.ponder-question-view .phui-property-list-actions { - width: 30%; -} - .ponder-answer-view { margin-top: 16px; }