From a5ad11d2d2f1d440b42681b8e65c71234c0b5e3c Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 14 May 2017 17:32:24 -0700 Subject: [PATCH 01/81] Add Member/Watcher info to search results Summary: Fixes T12707 Adds additional information to search results for if user is a member or a watcher of a project. Also removed the icon colors, which I'll find a better way to denote in future. Test Plan: Join a project, watch a project, view results list in /projects/ Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T12707 Differential Revision: https://secure.phabricator.com/D17880 --- .../query/PhabricatorProjectSearchEngine.php | 4 +++- .../project/view/PhabricatorProjectListView.php | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/applications/project/query/PhabricatorProjectSearchEngine.php b/src/applications/project/query/PhabricatorProjectSearchEngine.php index 75c2045f50..70931a8b00 100644 --- a/src/applications/project/query/PhabricatorProjectSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectSearchEngine.php @@ -13,7 +13,9 @@ final class PhabricatorProjectSearchEngine public function newQuery() { return id(new PhabricatorProjectQuery()) - ->needImages(true); + ->needImages(true) + ->needMembers(true) + ->needWatchers(true); } protected function buildCustomSearchFields() { diff --git a/src/applications/project/view/PhabricatorProjectListView.php b/src/applications/project/view/PhabricatorProjectListView.php index 3d6044f2b1..38c845167c 100644 --- a/src/applications/project/view/PhabricatorProjectListView.php +++ b/src/applications/project/view/PhabricatorProjectListView.php @@ -15,6 +15,7 @@ final class PhabricatorProjectListView extends AphrontView { public function renderList() { $viewer = $this->getUser(); + $viewer_phid = $viewer->getPHID(); $projects = $this->getProjects(); $handles = $viewer->loadHandles(mpull($projects, 'getPHID')); @@ -26,10 +27,8 @@ final class PhabricatorProjectListView extends AphrontView { $id = $project->getID(); $icon = $project->getDisplayIconIcon(); - $color = $project->getColor(); - $icon_icon = id(new PHUIIconView()) - ->setIcon("{$icon} {$color}"); + ->setIcon($icon); $icon_name = $project->getDisplayIconName(); @@ -49,6 +48,17 @@ final class PhabricatorProjectListView extends AphrontView { $item->setDisabled(true); } + $is_member = $project->isUserMember($viewer_phid); + $is_watcher = $project->isUserWatcher($viewer_phid); + + if ($is_member) { + $item->addIcon('fa-user', pht('Member')); + } + + if ($is_watcher) { + $item->addIcon('fa-eye', pht('Watching')); + } + $list->addItem($item); } From 89e567ffd94594bd7ab8f2deb7fab3b2f632d1a9 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 14 May 2017 14:15:06 -0700 Subject: [PATCH 02/81] Move Board Manage actions up a level Summary: Moves "reorder columns" and "change background" up a level, redesigns "manage" page to be a little cleaner. Test Plan: Change colors, reorder columns, manage page, disable board, re-enable board. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17879 --- ...icatorProjectBoardBackgroundController.php | 3 +- ...habricatorProjectBoardManageController.php | 103 +++++------------- ...abricatorProjectBoardReorderController.php | 4 +- .../PhabricatorProjectBoardViewController.php | 29 +++-- 4 files changed, 51 insertions(+), 88 deletions(-) diff --git a/src/applications/project/controller/PhabricatorProjectBoardBackgroundController.php b/src/applications/project/controller/PhabricatorProjectBoardBackgroundController.php index 3ba1f03a56..99260d1770 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardBackgroundController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardBackgroundController.php @@ -28,6 +28,7 @@ final class PhabricatorProjectBoardBackgroundController $this->setProject($board); $id = $board->getID(); + $view_uri = $this->getApplicationURI("board/{$id}/"); $manage_uri = $this->getApplicationURI("board/{$id}/manage/"); if ($request->isFormPost()) { @@ -47,7 +48,7 @@ final class PhabricatorProjectBoardBackgroundController ->applyTransactions($board, $xactions); return id(new AphrontRedirectResponse()) - ->setURI($manage_uri); + ->setURI($view_uri); } $nav = $this->getProfileMenu(); diff --git a/src/applications/project/controller/PhabricatorProjectBoardManageController.php b/src/applications/project/controller/PhabricatorProjectBoardManageController.php index 75bff07106..aecb5aa42a 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardManageController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardManageController.php @@ -31,112 +31,58 @@ final class PhabricatorProjectBoardManageController $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); + $crumbs->setBorder(true); $nav = $this->getProfileMenu(); + $columns_list = $this->buildColumnsList($board, $columns); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($columns_list); $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, - )); + ->appendChild($view); } 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); + $viewer = $this->getViewer(); $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)); - - $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)); - + $id = $board->getID(); $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)); + $button = id(new PHUIButtonView()) + ->setTag('a') + ->setIcon('fa-ban') + ->setText(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()) + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Workboard: %s', $board->getDisplayName())) ->setUser($viewer) - ->setObject($board); + ->setPolicyObject($board) + ->setProfileHeader(true) + ->addActionLink($button); - $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; + return $header; } private function buildColumnsList( @@ -165,6 +111,11 @@ final class PhabricatorProjectBoardManageController if ($column->isHidden()) { $item->setDisabled(true); + $item->addAttribute(pht('Hidden')); + $item->setImageIcon('fa-columns grey'); + } else { + $item->addAttribute(pht('Visible')); + $item->setImageIcon('fa-columns'); } $view->addItem($item); diff --git a/src/applications/project/controller/PhabricatorProjectBoardReorderController.php b/src/applications/project/controller/PhabricatorProjectBoardReorderController.php index 05f1dd2d43..fc348bb02f 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(); - $manage_uri = $this->getApplicationURI("board/{$project_id}/manage/"); + $view_uri = $this->getApplicationURI("board/{$project_id}/"); $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($manage_uri); + return id(new AphrontRedirectResponse())->setURI($view_uri); } $columns = id(new PhabricatorProjectColumnQuery()) diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php index 3b117c747f..6a7083b10d 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php @@ -705,10 +705,21 @@ final class PhabricatorProjectBoardViewController ->setDisabled(!$can_edit) ->setWorkflow(true); + $reorder_uri = $this->getApplicationURI("board/{$id}/reorder/"); $manage_items[] = id(new PhabricatorActionView()) - ->setIcon('fa-pencil') - ->setName(pht('Manage Board')) - ->setHref($manage_uri); + ->setIcon('fa-exchange') + ->setName(pht('Reorder Columns')) + ->setHref($reorder_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(true); + + $background_uri = $this->getApplicationURI("board/{$id}/background/"); + $manage_items[] = id(new PhabricatorActionView()) + ->setIcon('fa-paint-brush') + ->setName(pht('Change Background Color')) + ->setHref($background_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(false); if ($show_hidden) { $hidden_uri = $this->getURIWithState() @@ -727,6 +738,12 @@ final class PhabricatorProjectBoardViewController ->setName($hidden_text) ->setHref($hidden_uri); + $manage_uri = $this->getApplicationURI("board/{$id}/manage/"); + $manage_items[] = id(new PhabricatorActionView()) + ->setIcon('fa-gear') + ->setName(pht('Manage Workboard')) + ->setHref($manage_uri); + $batch_edit_uri = $request->getRequestURI(); $batch_edit_uri->setQueryParam('batch', self::BATCH_EDIT_ALL); $can_batch_edit = PhabricatorPolicyFilter::hasCapability( @@ -734,12 +751,6 @@ final class PhabricatorProjectBoardViewController PhabricatorApplication::getByClass('PhabricatorManiphestApplication'), ManiphestBulkEditCapability::CAPABILITY); - $manage_items[] = id(new PhabricatorActionView()) - ->setIcon('fa-list-ul') - ->setName(pht('Batch Edit Visible Tasks...')) - ->setHref($batch_edit_uri) - ->setDisabled(!$can_batch_edit); - $manage_menu = id(new PhabricatorActionListView()) ->setUser($viewer); foreach ($manage_items as $item) { From 7f54f79fd1c3a37d1c0942e3b3d27daae2e4efff Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 15 May 2017 06:36:28 -0700 Subject: [PATCH 03/81] Add required needMembers/needWatchers calls to Project Profile/Subprojects tabs Summary: Fixes T12710. See that task for discussion. This is pretty ugly/redundant but not broken. (Feel free to reject this and pursue something else.) Test Plan: - For a project with active subprojects/milestones, viewed the project profile and subprojects tabs. - After patch: they're ugly, but no longer fatal. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12710 Differential Revision: https://secure.phabricator.com/D17882 --- .../controller/PhabricatorProjectProfileController.php | 2 ++ .../controller/PhabricatorProjectSubprojectsController.php | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php index 4c726f5884..60254ddcca 100644 --- a/src/applications/project/controller/PhabricatorProjectProfileController.php +++ b/src/applications/project/controller/PhabricatorProjectProfileController.php @@ -262,6 +262,8 @@ final class PhabricatorProjectProfileController ->setViewer($viewer) ->withParentProjectPHIDs(array($project->getPHID())) ->needImages(true) + ->needMembers(true) + ->needWatchers(true) ->withStatuses( array( PhabricatorProjectStatus::STATUS_ACTIVE, diff --git a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php index 4a89bb4cde..a25a41fad0 100644 --- a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php +++ b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php @@ -31,6 +31,8 @@ final class PhabricatorProjectSubprojectsController ->setViewer($viewer) ->withParentProjectPHIDs(array($project->getPHID())) ->needImages(true) + ->needMembers(true) + ->needWatchers(true) ->withIsMilestone(false) ->execute(); } else { @@ -42,6 +44,8 @@ final class PhabricatorProjectSubprojectsController ->setViewer($viewer) ->withParentProjectPHIDs(array($project->getPHID())) ->needImages(true) + ->needMembers(true) + ->needWatchers(true) ->withIsMilestone(true) ->setOrderVector(array('milestoneNumber', 'id')) ->execute(); From 6fa91caf32aa80339e7ea5fef769f23f0220924a Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 14 May 2017 18:58:33 -0700 Subject: [PATCH 04/81] Clean up Project Board Manage / Column Manage pages Summary: Slightly nicer, more consistent UI. Also removed "Column History" from dropdowns as this is available on the general board manage page. Test Plan: Review Board and Column management pages. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17881 --- ...habricatorProjectBoardManageController.php | 5 +- .../PhabricatorProjectBoardViewController.php | 6 -- ...abricatorProjectColumnDetailController.php | 70 +++++++------------ .../PhabricatorProjectProfileController.php | 5 +- 4 files changed, 30 insertions(+), 56 deletions(-) diff --git a/src/applications/project/controller/PhabricatorProjectBoardManageController.php b/src/applications/project/controller/PhabricatorProjectBoardManageController.php index aecb5aa42a..4607b50c2a 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardManageController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardManageController.php @@ -40,8 +40,12 @@ final class PhabricatorProjectBoardManageController $nav = $this->getProfileMenu(); $columns_list = $this->buildColumnsList($board, $columns); + require_celerity_resource('project-view-css'); + $view = id(new PHUITwoColumnView()) ->setHeader($header) + ->addClass('project-view-home') + ->addClass('project-view-people-home') ->setFooter($columns_list); $title = array( @@ -78,7 +82,6 @@ final class PhabricatorProjectBoardManageController $header = id(new PHUIHeaderView()) ->setHeader(pht('Workboard: %s', $board->getDisplayName())) ->setUser($viewer) - ->setPolicyObject($board) ->setProfileHeader(true) ->addActionLink($button); diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php index 6a7083b10d..da1f0ccb95 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php @@ -879,12 +879,6 @@ final class PhabricatorProjectBoardViewController ->setWorkflow(true); } - $details_uri = 'board/'.$this->id.'/column/'.$column->getID().'/'; - $column_items[] = id(new PhabricatorActionView()) - ->setName(pht('Column History')) - ->setIcon('fa-columns') - ->setHref($this->getApplicationURI($details_uri)); - $column_menu = id(new PhabricatorActionListView()) ->setUser($viewer); foreach ($column_items as $item) { diff --git a/src/applications/project/controller/PhabricatorProjectColumnDetailController.php b/src/applications/project/controller/PhabricatorProjectColumnDetailController.php index d922d24714..56eb16613f 100644 --- a/src/applications/project/controller/PhabricatorProjectColumnDetailController.php +++ b/src/applications/project/controller/PhabricatorProjectColumnDetailController.php @@ -44,36 +44,39 @@ final class PhabricatorProjectColumnDetailController $title = $column->getDisplayName(); $header = $this->buildHeaderView($column); - $actions = $this->buildActionView($column); - $properties = $this->buildPropertyView($column, $actions); + $properties = $this->buildPropertyView($column); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Workboard'), "/project/board/{$project_id}/"); $crumbs->addTextCrumb(pht('Column: %s', $title)); - - $box = id(new PHUIObjectBoxView()) - ->setHeader($header) - ->addPropertyList($properties); + $crumbs->setBorder(true); $nav = $this->getProfileMenu(); + require_celerity_resource('project-view-css'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->addClass('project-view-home') + ->addClass('project-view-people-home') + ->setMainColumn(array( + $properties, + $timeline, + )); return $this->newPage() ->setTitle($title) ->setNavigation($nav) ->setCrumbs($crumbs) - ->appendChild( - array( - $box, - $timeline, - )); + ->appendChild($view); } private function buildHeaderView(PhabricatorProjectColumn $column) { - $viewer = $this->getRequest()->getUser(); + $viewer = $this->getViewer(); $header = id(new PHUIHeaderView()) + ->setHeader(pht('Column: %s', $column->getDisplayName())) ->setUser($viewer) - ->setHeader($column->getDisplayName()); + ->setProfileHeader(true); if ($column->isHidden()) { $header->setStatus('fa-ban', 'dark', pht('Hidden')); @@ -82,41 +85,13 @@ final class PhabricatorProjectColumnDetailController return $header; } - private function buildActionView(PhabricatorProjectColumn $column) { - $viewer = $this->getRequest()->getUser(); - - $id = $column->getID(); - $project_id = $this->getProject()->getID(); - $base_uri = '/board/'.$project_id.'/'; - - $actions = id(new PhabricatorActionListView()) - ->setUser($viewer); - - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $column, - PhabricatorPolicyCapability::CAN_EDIT); - - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Edit Column')) - ->setIcon('fa-pencil') - ->setHref($this->getApplicationURI($base_uri.'edit/'.$id.'/')) - ->setDisabled(!$can_edit) - ->setWorkflow(true)); - - return $actions; - } - private function buildPropertyView( - PhabricatorProjectColumn $column, - PhabricatorActionListView $actions) { - $viewer = $this->getRequest()->getUser(); + PhabricatorProjectColumn $column) { + $viewer = $this->getViewer(); $properties = id(new PHUIPropertyListView()) ->setUser($viewer) - ->setObject($column) - ->setActionList($actions); + ->setObject($column); $limit = $column->getPointLimit(); if ($limit === null) { @@ -126,7 +101,12 @@ final class PhabricatorProjectColumnDetailController } $properties->addProperty(pht('Point Limit'), $limit_text); - return $properties; + $box = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Details')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->appendChild($properties); + + return $box; } } diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php index 60254ddcca..e5ae78fd20 100644 --- a/src/applications/project/controller/PhabricatorProjectProfileController.php +++ b/src/applications/project/controller/PhabricatorProjectProfileController.php @@ -113,10 +113,7 @@ final class PhabricatorProjectProfileController ->setCrumbs($crumbs) ->setTitle($project->getDisplayName()) ->setPageObjectPHIDs(array($project->getPHID())) - ->appendChild( - array( - $home, - )); + ->appendChild($home); } private function buildPropertyListView( From ea874a28251bdb0b19dd231168087b435e2bd18d Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 15 May 2017 09:02:39 -0700 Subject: [PATCH 05/81] Restrict watching and member project display better Summary: Fixes T12713. We don't need to show watching and member info on other views other than ApplicationSearch (for now) so add a few methods to restrict the calls. Test Plan: Visit project search, profile, project home, project home with subprojects Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T12713 Differential Revision: https://secure.phabricator.com/D17883 --- .../PhabricatorProjectProfileController.php | 2 -- ...habricatorProjectSubprojectsController.php | 4 --- .../query/PhabricatorProjectSearchEngine.php | 2 ++ .../view/PhabricatorProjectListView.php | 29 ++++++++++++++----- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php index e5ae78fd20..32dbec3c0f 100644 --- a/src/applications/project/controller/PhabricatorProjectProfileController.php +++ b/src/applications/project/controller/PhabricatorProjectProfileController.php @@ -259,8 +259,6 @@ final class PhabricatorProjectProfileController ->setViewer($viewer) ->withParentProjectPHIDs(array($project->getPHID())) ->needImages(true) - ->needMembers(true) - ->needWatchers(true) ->withStatuses( array( PhabricatorProjectStatus::STATUS_ACTIVE, diff --git a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php index a25a41fad0..4a89bb4cde 100644 --- a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php +++ b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php @@ -31,8 +31,6 @@ final class PhabricatorProjectSubprojectsController ->setViewer($viewer) ->withParentProjectPHIDs(array($project->getPHID())) ->needImages(true) - ->needMembers(true) - ->needWatchers(true) ->withIsMilestone(false) ->execute(); } else { @@ -44,8 +42,6 @@ final class PhabricatorProjectSubprojectsController ->setViewer($viewer) ->withParentProjectPHIDs(array($project->getPHID())) ->needImages(true) - ->needMembers(true) - ->needWatchers(true) ->withIsMilestone(true) ->setOrderVector(array('milestoneNumber', 'id')) ->execute(); diff --git a/src/applications/project/query/PhabricatorProjectSearchEngine.php b/src/applications/project/query/PhabricatorProjectSearchEngine.php index 70931a8b00..df13278812 100644 --- a/src/applications/project/query/PhabricatorProjectSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectSearchEngine.php @@ -229,6 +229,8 @@ final class PhabricatorProjectSearchEngine $list = id(new PhabricatorProjectListView()) ->setUser($viewer) ->setProjects($projects) + ->setShowWatching(true) + ->setShowMember(true) ->renderList(); return id(new PhabricatorApplicationSearchResultView()) diff --git a/src/applications/project/view/PhabricatorProjectListView.php b/src/applications/project/view/PhabricatorProjectListView.php index 38c845167c..994e78ce76 100644 --- a/src/applications/project/view/PhabricatorProjectListView.php +++ b/src/applications/project/view/PhabricatorProjectListView.php @@ -3,6 +3,8 @@ final class PhabricatorProjectListView extends AphrontView { private $projects; + private $showMember; + private $showWatching; public function setProjects(array $projects) { $this->projects = $projects; @@ -13,6 +15,16 @@ final class PhabricatorProjectListView extends AphrontView { return $this->projects; } + public function setShowWatching($watching) { + $this->showWatching = $watching; + return $this; + } + + public function setShowMember($member) { + $this->showMember = $member; + return $this; + } + public function renderList() { $viewer = $this->getUser(); $viewer_phid = $viewer->getPHID(); @@ -48,15 +60,18 @@ final class PhabricatorProjectListView extends AphrontView { $item->setDisabled(true); } - $is_member = $project->isUserMember($viewer_phid); - $is_watcher = $project->isUserWatcher($viewer_phid); - - if ($is_member) { - $item->addIcon('fa-user', pht('Member')); + if ($this->showMember) { + $is_member = $project->isUserMember($viewer_phid); + if ($is_member) { + $item->addIcon('fa-user', pht('Member')); + } } - if ($is_watcher) { - $item->addIcon('fa-eye', pht('Watching')); + if ($this->showWatching) { + $is_watcher = $project->isUserWatcher($viewer_phid); + if ($is_watcher) { + $item->addIcon('fa-eye', pht('Watching')); + } } $list->addItem($item); From d6a620be4577941092965429e4998e98f9cc469a Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 15 May 2017 10:23:20 -0700 Subject: [PATCH 06/81] Update Maniphest for modular transactions Summary: Ref T12671. This modernized Maniphest transactions to modular transactions. Test Plan: - Create Task - Edit Task - Raise Priority - Change Status - Merge as a duplicate - Create Subtask - Claim Task - Assign Project - Move on Workboard - Set a cover image - Assign story points - Change story points - Generate lots via lipsum - Bulk edit tasks - Leave comments - Award Token I'm sure I'm missing something. Reviewers: epriestley Reviewed By: epriestley Subscribers: hazelyang, Korvin Maniphest Tasks: T12671 Differential Revision: https://secure.phabricator.com/D17844 --- .../autopatches/20140321.mstatus.2.mig.php | 3 +- src/__phutil_library_map__.php | 32 +- .../__tests__/PhabricatorFileTestCase.php | 5 +- .../__tests__/ManiphestTaskTestCase.php | 10 +- .../bulk/ManiphestTaskEditBulkJobType.php | 14 +- .../command/ManiphestAssignEmailCommand.php | 2 +- .../command/ManiphestClaimEmailCommand.php | 2 +- .../command/ManiphestCloseEmailCommand.php | 2 +- .../command/ManiphestPriorityEmailCommand.php | 2 +- .../command/ManiphestStatusEmailCommand.php | 2 +- .../conduit/ManiphestConduitAPIMethod.php | 12 +- .../controller/ManiphestReportController.php | 2 +- .../ManiphestSubpriorityController.php | 4 +- .../ManiphestTaskDetailController.php | 2 +- .../maniphest/editor/ManiphestEditEngine.php | 14 +- .../editor/ManiphestTransactionEditor.php | 345 +------- .../ManiphestTaskAssignHeraldAction.php | 2 +- .../ManiphestTaskPriorityHeraldAction.php | 2 +- .../ManiphestTaskStatusHeraldAction.php | 2 +- ...bricatorManiphestTaskTestDataGenerator.php | 10 +- .../maniphest/mail/ManiphestReplyHandler.php | 5 +- .../ManiphestTaskRelationship.php | 6 +- .../storage/ManiphestTransaction.php | 807 +----------------- .../ManiphestTaskAttachTransaction.php | 91 ++ .../ManiphestTaskCoverImageTransaction.php | 106 +++ .../ManiphestTaskDescriptionTransaction.php | 61 ++ .../xaction/ManiphestTaskEdgeTransaction.php | 31 + .../ManiphestTaskMergedFromTransaction.php | 45 + .../ManiphestTaskMergedIntoTransaction.php | 47 + .../xaction/ManiphestTaskOwnerTransaction.php | 158 ++++ .../ManiphestTaskParentTransaction.php | 60 ++ .../ManiphestTaskPointsTransaction.php | 76 ++ .../ManiphestTaskPriorityTransaction.php | 119 +++ .../ManiphestTaskStatusTransaction.php | 232 +++++ .../ManiphestTaskSubpriorityTransaction.php | 21 + .../xaction/ManiphestTaskTitleTransaction.php | 74 ++ .../xaction/ManiphestTaskTransactionType.php | 16 + .../ManiphestTaskUnblockTransaction.php | 125 +++ .../nuance/item/NuanceGitHubEventItemType.php | 6 +- .../PhabricatorProjectCoreTestCase.php | 2 +- .../PhabricatorProjectCoverController.php | 2 +- .../PhabricatorProjectMoveController.php | 5 +- ...torRepositoryCommitMessageParserWorker.php | 3 +- 43 files changed, 1401 insertions(+), 1166 deletions(-) create mode 100644 src/applications/maniphest/xaction/ManiphestTaskAttachTransaction.php create mode 100644 src/applications/maniphest/xaction/ManiphestTaskCoverImageTransaction.php create mode 100644 src/applications/maniphest/xaction/ManiphestTaskDescriptionTransaction.php create mode 100644 src/applications/maniphest/xaction/ManiphestTaskEdgeTransaction.php create mode 100644 src/applications/maniphest/xaction/ManiphestTaskMergedFromTransaction.php create mode 100644 src/applications/maniphest/xaction/ManiphestTaskMergedIntoTransaction.php create mode 100644 src/applications/maniphest/xaction/ManiphestTaskOwnerTransaction.php create mode 100644 src/applications/maniphest/xaction/ManiphestTaskParentTransaction.php create mode 100644 src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php create mode 100644 src/applications/maniphest/xaction/ManiphestTaskPriorityTransaction.php create mode 100644 src/applications/maniphest/xaction/ManiphestTaskStatusTransaction.php create mode 100644 src/applications/maniphest/xaction/ManiphestTaskSubpriorityTransaction.php create mode 100644 src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php create mode 100644 src/applications/maniphest/xaction/ManiphestTaskTransactionType.php create mode 100644 src/applications/maniphest/xaction/ManiphestTaskUnblockTransaction.php diff --git a/resources/sql/autopatches/20140321.mstatus.2.mig.php b/resources/sql/autopatches/20140321.mstatus.2.mig.php index 7f91e00e1b..654ca7881c 100644 --- a/resources/sql/autopatches/20140321.mstatus.2.mig.php +++ b/resources/sql/autopatches/20140321.mstatus.2.mig.php @@ -35,7 +35,8 @@ foreach (new LiskMigrationIterator(new ManiphestTransaction()) as $xaction) { $id = $xaction->getID(); echo pht('Migrating %d...', $id)."\n"; - if ($xaction->getTransactionType() == ManiphestTransaction::TYPE_STATUS) { + $xn_type = ManiphestTaskStatusTransaction::TRANSACTIONTYPE; + if ($xaction->getTransactionType() == $xn_type) { $old = $xaction->getOldValue(); if ($old !== null && isset($status_map[$old])) { $old = $status_map[$old]; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 611b2c118c..024a8c93b2 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1511,14 +1511,18 @@ phutil_register_library_map(array( 'ManiphestTaskAssignOtherHeraldAction' => 'applications/maniphest/herald/ManiphestTaskAssignOtherHeraldAction.php', 'ManiphestTaskAssignSelfHeraldAction' => 'applications/maniphest/herald/ManiphestTaskAssignSelfHeraldAction.php', 'ManiphestTaskAssigneeHeraldField' => 'applications/maniphest/herald/ManiphestTaskAssigneeHeraldField.php', + 'ManiphestTaskAttachTransaction' => 'applications/maniphest/xaction/ManiphestTaskAttachTransaction.php', 'ManiphestTaskAuthorHeraldField' => 'applications/maniphest/herald/ManiphestTaskAuthorHeraldField.php', 'ManiphestTaskAuthorPolicyRule' => 'applications/maniphest/policyrule/ManiphestTaskAuthorPolicyRule.php', 'ManiphestTaskCloseAsDuplicateRelationship' => 'applications/maniphest/relationship/ManiphestTaskCloseAsDuplicateRelationship.php', 'ManiphestTaskClosedStatusDatasource' => 'applications/maniphest/typeahead/ManiphestTaskClosedStatusDatasource.php', + 'ManiphestTaskCoverImageTransaction' => 'applications/maniphest/xaction/ManiphestTaskCoverImageTransaction.php', 'ManiphestTaskDependedOnByTaskEdgeType' => 'applications/maniphest/edge/ManiphestTaskDependedOnByTaskEdgeType.php', 'ManiphestTaskDependsOnTaskEdgeType' => 'applications/maniphest/edge/ManiphestTaskDependsOnTaskEdgeType.php', 'ManiphestTaskDescriptionHeraldField' => 'applications/maniphest/herald/ManiphestTaskDescriptionHeraldField.php', + 'ManiphestTaskDescriptionTransaction' => 'applications/maniphest/xaction/ManiphestTaskDescriptionTransaction.php', 'ManiphestTaskDetailController' => 'applications/maniphest/controller/ManiphestTaskDetailController.php', + 'ManiphestTaskEdgeTransaction' => 'applications/maniphest/xaction/ManiphestTaskEdgeTransaction.php', 'ManiphestTaskEditBulkJobType' => 'applications/maniphest/bulk/ManiphestTaskEditBulkJobType.php', 'ManiphestTaskEditController' => 'applications/maniphest/controller/ManiphestTaskEditController.php', 'ManiphestTaskEditEngineLock' => 'applications/maniphest/editor/ManiphestTaskEditEngineLock.php', @@ -1541,14 +1545,20 @@ phutil_register_library_map(array( 'ManiphestTaskListView' => 'applications/maniphest/view/ManiphestTaskListView.php', 'ManiphestTaskMailReceiver' => 'applications/maniphest/mail/ManiphestTaskMailReceiver.php', 'ManiphestTaskMergeInRelationship' => 'applications/maniphest/relationship/ManiphestTaskMergeInRelationship.php', + 'ManiphestTaskMergedFromTransaction' => 'applications/maniphest/xaction/ManiphestTaskMergedFromTransaction.php', + 'ManiphestTaskMergedIntoTransaction' => 'applications/maniphest/xaction/ManiphestTaskMergedIntoTransaction.php', 'ManiphestTaskOpenStatusDatasource' => 'applications/maniphest/typeahead/ManiphestTaskOpenStatusDatasource.php', + 'ManiphestTaskOwnerTransaction' => 'applications/maniphest/xaction/ManiphestTaskOwnerTransaction.php', 'ManiphestTaskPHIDResolver' => 'applications/maniphest/httpparametertype/ManiphestTaskPHIDResolver.php', 'ManiphestTaskPHIDType' => 'applications/maniphest/phid/ManiphestTaskPHIDType.php', + 'ManiphestTaskParentTransaction' => 'applications/maniphest/xaction/ManiphestTaskParentTransaction.php', 'ManiphestTaskPoints' => 'applications/maniphest/constants/ManiphestTaskPoints.php', + 'ManiphestTaskPointsTransaction' => 'applications/maniphest/xaction/ManiphestTaskPointsTransaction.php', 'ManiphestTaskPriority' => 'applications/maniphest/constants/ManiphestTaskPriority.php', 'ManiphestTaskPriorityDatasource' => 'applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php', 'ManiphestTaskPriorityHeraldAction' => 'applications/maniphest/herald/ManiphestTaskPriorityHeraldAction.php', 'ManiphestTaskPriorityHeraldField' => 'applications/maniphest/herald/ManiphestTaskPriorityHeraldField.php', + 'ManiphestTaskPriorityTransaction' => 'applications/maniphest/xaction/ManiphestTaskPriorityTransaction.php', 'ManiphestTaskQuery' => 'applications/maniphest/query/ManiphestTaskQuery.php', 'ManiphestTaskRelationship' => 'applications/maniphest/relationship/ManiphestTaskRelationship.php', 'ManiphestTaskRelationshipSource' => 'applications/search/relationship/ManiphestTaskRelationshipSource.php', @@ -1560,9 +1570,14 @@ phutil_register_library_map(array( 'ManiphestTaskStatusHeraldAction' => 'applications/maniphest/herald/ManiphestTaskStatusHeraldAction.php', 'ManiphestTaskStatusHeraldField' => 'applications/maniphest/herald/ManiphestTaskStatusHeraldField.php', 'ManiphestTaskStatusTestCase' => 'applications/maniphest/constants/__tests__/ManiphestTaskStatusTestCase.php', + 'ManiphestTaskStatusTransaction' => 'applications/maniphest/xaction/ManiphestTaskStatusTransaction.php', + 'ManiphestTaskSubpriorityTransaction' => 'applications/maniphest/xaction/ManiphestTaskSubpriorityTransaction.php', 'ManiphestTaskSubtypeDatasource' => 'applications/maniphest/typeahead/ManiphestTaskSubtypeDatasource.php', 'ManiphestTaskTestCase' => 'applications/maniphest/__tests__/ManiphestTaskTestCase.php', 'ManiphestTaskTitleHeraldField' => 'applications/maniphest/herald/ManiphestTaskTitleHeraldField.php', + 'ManiphestTaskTitleTransaction' => 'applications/maniphest/xaction/ManiphestTaskTitleTransaction.php', + 'ManiphestTaskTransactionType' => 'applications/maniphest/xaction/ManiphestTaskTransactionType.php', + 'ManiphestTaskUnblockTransaction' => 'applications/maniphest/xaction/ManiphestTaskUnblockTransaction.php', 'ManiphestTransaction' => 'applications/maniphest/storage/ManiphestTransaction.php', 'ManiphestTransactionComment' => 'applications/maniphest/storage/ManiphestTransactionComment.php', 'ManiphestTransactionEditor' => 'applications/maniphest/editor/ManiphestTransactionEditor.php', @@ -6569,14 +6584,18 @@ phutil_register_library_map(array( 'ManiphestTaskAssignOtherHeraldAction' => 'ManiphestTaskAssignHeraldAction', 'ManiphestTaskAssignSelfHeraldAction' => 'ManiphestTaskAssignHeraldAction', 'ManiphestTaskAssigneeHeraldField' => 'ManiphestTaskHeraldField', + 'ManiphestTaskAttachTransaction' => 'ManiphestTaskTransactionType', 'ManiphestTaskAuthorHeraldField' => 'ManiphestTaskHeraldField', 'ManiphestTaskAuthorPolicyRule' => 'PhabricatorPolicyRule', 'ManiphestTaskCloseAsDuplicateRelationship' => 'ManiphestTaskRelationship', 'ManiphestTaskClosedStatusDatasource' => 'PhabricatorTypeaheadDatasource', + 'ManiphestTaskCoverImageTransaction' => 'ManiphestTaskTransactionType', 'ManiphestTaskDependedOnByTaskEdgeType' => 'PhabricatorEdgeType', 'ManiphestTaskDependsOnTaskEdgeType' => 'PhabricatorEdgeType', 'ManiphestTaskDescriptionHeraldField' => 'ManiphestTaskHeraldField', + 'ManiphestTaskDescriptionTransaction' => 'ManiphestTaskTransactionType', 'ManiphestTaskDetailController' => 'ManiphestController', + 'ManiphestTaskEdgeTransaction' => 'ManiphestTaskTransactionType', 'ManiphestTaskEditBulkJobType' => 'PhabricatorWorkerBulkJobType', 'ManiphestTaskEditController' => 'ManiphestController', 'ManiphestTaskEditEngineLock' => 'PhabricatorEditEngineLock', @@ -6599,14 +6618,20 @@ phutil_register_library_map(array( 'ManiphestTaskListView' => 'ManiphestView', 'ManiphestTaskMailReceiver' => 'PhabricatorObjectMailReceiver', 'ManiphestTaskMergeInRelationship' => 'ManiphestTaskRelationship', + 'ManiphestTaskMergedFromTransaction' => 'ManiphestTaskTransactionType', + 'ManiphestTaskMergedIntoTransaction' => 'ManiphestTaskTransactionType', 'ManiphestTaskOpenStatusDatasource' => 'PhabricatorTypeaheadDatasource', + 'ManiphestTaskOwnerTransaction' => 'ManiphestTaskTransactionType', 'ManiphestTaskPHIDResolver' => 'PhabricatorPHIDResolver', 'ManiphestTaskPHIDType' => 'PhabricatorPHIDType', + 'ManiphestTaskParentTransaction' => 'ManiphestTaskTransactionType', 'ManiphestTaskPoints' => 'Phobject', + 'ManiphestTaskPointsTransaction' => 'ManiphestTaskTransactionType', 'ManiphestTaskPriority' => 'ManiphestConstants', 'ManiphestTaskPriorityDatasource' => 'PhabricatorTypeaheadDatasource', 'ManiphestTaskPriorityHeraldAction' => 'HeraldAction', 'ManiphestTaskPriorityHeraldField' => 'ManiphestTaskHeraldField', + 'ManiphestTaskPriorityTransaction' => 'ManiphestTaskTransactionType', 'ManiphestTaskQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'ManiphestTaskRelationship' => 'PhabricatorObjectRelationship', 'ManiphestTaskRelationshipSource' => 'PhabricatorObjectRelationshipSource', @@ -6618,10 +6643,15 @@ phutil_register_library_map(array( 'ManiphestTaskStatusHeraldAction' => 'HeraldAction', 'ManiphestTaskStatusHeraldField' => 'ManiphestTaskHeraldField', 'ManiphestTaskStatusTestCase' => 'PhabricatorTestCase', + 'ManiphestTaskStatusTransaction' => 'ManiphestTaskTransactionType', + 'ManiphestTaskSubpriorityTransaction' => 'ManiphestTaskTransactionType', 'ManiphestTaskSubtypeDatasource' => 'PhabricatorTypeaheadDatasource', 'ManiphestTaskTestCase' => 'PhabricatorTestCase', 'ManiphestTaskTitleHeraldField' => 'ManiphestTaskHeraldField', - 'ManiphestTransaction' => 'PhabricatorApplicationTransaction', + 'ManiphestTaskTitleTransaction' => 'ManiphestTaskTransactionType', + 'ManiphestTaskTransactionType' => 'PhabricatorModularTransactionType', + 'ManiphestTaskUnblockTransaction' => 'ManiphestTaskTransactionType', + 'ManiphestTransaction' => 'PhabricatorModularTransaction', 'ManiphestTransactionComment' => 'PhabricatorApplicationTransactionComment', 'ManiphestTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 'ManiphestTransactionQuery' => 'PhabricatorApplicationTransactionQuery', diff --git a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php index 936676b902..65dc0a12a2 100644 --- a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php +++ b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php @@ -94,11 +94,12 @@ final class PhabricatorFileTestCase extends PhabricatorTestCase { $xactions = array(); $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_TITLE) + ->setTransactionType(ManiphestTaskTitleTransaction::TRANSACTIONTYPE) ->setNewValue(pht('File Scramble Test Task')); $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_DESCRIPTION) + ->setTransactionType( + ManiphestTaskDescriptionTransaction::TRANSACTIONTYPE) ->setNewValue('{'.$file->getMonogram().'}'); id(new ManiphestTransactionEditor()) diff --git a/src/applications/maniphest/__tests__/ManiphestTaskTestCase.php b/src/applications/maniphest/__tests__/ManiphestTaskTestCase.php index cf1da51bca..5f5c198a84 100644 --- a/src/applications/maniphest/__tests__/ManiphestTaskTestCase.php +++ b/src/applications/maniphest/__tests__/ManiphestTaskTestCase.php @@ -133,7 +133,7 @@ final class ManiphestTaskTestCase extends PhabricatorTestCase { $xactions = array(); $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_TITLE) + ->setTransactionType(ManiphestTaskTitleTransaction::TRANSACTIONTYPE) ->setNewValue($title); @@ -169,11 +169,11 @@ final class ManiphestTaskTestCase extends PhabricatorTestCase { $xactions = array(); $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_PRIORITY) + ->setTransactionType(ManiphestTaskPriorityTransaction::TRANSACTIONTYPE) ->setNewValue($pri); $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_SUBPRIORITY) + ->setTransactionType(ManiphestTaskSubpriorityTransaction::TRANSACTIONTYPE) ->setNewValue($sub); return $this->applyTaskTransactions($viewer, $src, $xactions); @@ -192,11 +192,11 @@ final class ManiphestTaskTestCase extends PhabricatorTestCase { $xactions = array(); $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_PRIORITY) + ->setTransactionType(ManiphestTaskPriorityTransaction::TRANSACTIONTYPE) ->setNewValue($pri); $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_SUBPRIORITY) + ->setTransactionType(ManiphestTaskSubpriorityTransaction::TRANSACTIONTYPE) ->setNewValue($sub); return $this->applyTaskTransactions($viewer, $src, $xactions); diff --git a/src/applications/maniphest/bulk/ManiphestTaskEditBulkJobType.php b/src/applications/maniphest/bulk/ManiphestTaskEditBulkJobType.php index f4ca24863b..0e11530b51 100644 --- a/src/applications/maniphest/bulk/ManiphestTaskEditBulkJobType.php +++ b/src/applications/maniphest/bulk/ManiphestTaskEditBulkJobType.php @@ -76,9 +76,9 @@ final class ManiphestTaskEditBulkJobType $value_map = array(); $type_map = array( 'add_comment' => PhabricatorTransactions::TYPE_COMMENT, - 'assign' => ManiphestTransaction::TYPE_OWNER, - 'status' => ManiphestTransaction::TYPE_STATUS, - 'priority' => ManiphestTransaction::TYPE_PRIORITY, + 'assign' => ManiphestTaskOwnerTransaction::TRANSACTIONTYPE, + 'status' => ManiphestTaskStatusTransaction::TRANSACTIONTYPE, + 'priority' => ManiphestTaskPriorityTransaction::TRANSACTIONTYPE, 'add_project' => PhabricatorTransactions::TYPE_EDGE, 'remove_project' => PhabricatorTransactions::TYPE_EDGE, 'add_ccs' => PhabricatorTransactions::TYPE_SUBSCRIBERS, @@ -114,13 +114,13 @@ final class ManiphestTaskEditBulkJobType case PhabricatorTransactions::TYPE_COMMENT: $current = null; break; - case ManiphestTransaction::TYPE_OWNER: + case ManiphestTaskOwnerTransaction::TRANSACTIONTYPE: $current = $task->getOwnerPHID(); break; - case ManiphestTransaction::TYPE_STATUS: + case ManiphestTaskStatusTransaction::TRANSACTIONTYPE: $current = $task->getStatus(); break; - case ManiphestTransaction::TYPE_PRIORITY: + case ManiphestTaskPriorityTransaction::TRANSACTIONTYPE: $current = $task->getPriority(); break; case PhabricatorTransactions::TYPE_EDGE: @@ -153,7 +153,7 @@ final class ManiphestTaskEditBulkJobType } $value = head($value); break; - case ManiphestTransaction::TYPE_OWNER: + case ManiphestTaskOwnerTransaction::TRANSACTIONTYPE: if (empty($value)) { continue 2; } diff --git a/src/applications/maniphest/command/ManiphestAssignEmailCommand.php b/src/applications/maniphest/command/ManiphestAssignEmailCommand.php index 98e8a913a8..de014b6c66 100644 --- a/src/applications/maniphest/command/ManiphestAssignEmailCommand.php +++ b/src/applications/maniphest/command/ManiphestAssignEmailCommand.php @@ -53,7 +53,7 @@ final class ManiphestAssignEmailCommand } $xactions[] = $object->getApplicationTransactionTemplate() - ->setTransactionType(ManiphestTransaction::TYPE_OWNER) + ->setTransactionType(ManiphestTaskOwnerTransaction::TRANSACTIONTYPE) ->setNewValue($assign_phid); return $xactions; diff --git a/src/applications/maniphest/command/ManiphestClaimEmailCommand.php b/src/applications/maniphest/command/ManiphestClaimEmailCommand.php index 4a6a348dbb..babc853b67 100644 --- a/src/applications/maniphest/command/ManiphestClaimEmailCommand.php +++ b/src/applications/maniphest/command/ManiphestClaimEmailCommand.php @@ -23,7 +23,7 @@ final class ManiphestClaimEmailCommand $xactions = array(); $xactions[] = $object->getApplicationTransactionTemplate() - ->setTransactionType(ManiphestTransaction::TYPE_OWNER) + ->setTransactionType(ManiphestTaskOwnerTransaction::TRANSACTIONTYPE) ->setNewValue($viewer->getPHID()); return $xactions; diff --git a/src/applications/maniphest/command/ManiphestCloseEmailCommand.php b/src/applications/maniphest/command/ManiphestCloseEmailCommand.php index 8104fd8b8d..eab01ad615 100644 --- a/src/applications/maniphest/command/ManiphestCloseEmailCommand.php +++ b/src/applications/maniphest/command/ManiphestCloseEmailCommand.php @@ -24,7 +24,7 @@ final class ManiphestCloseEmailCommand $xactions = array(); $xactions[] = $object->getApplicationTransactionTemplate() - ->setTransactionType(ManiphestTransaction::TYPE_STATUS) + ->setTransactionType(ManiphestTaskStatusTransaction::TRANSACTIONTYPE) ->setNewValue(ManiphestTaskStatus::getDefaultClosedStatus()); return $xactions; diff --git a/src/applications/maniphest/command/ManiphestPriorityEmailCommand.php b/src/applications/maniphest/command/ManiphestPriorityEmailCommand.php index f774578240..5d7bbb1eea 100644 --- a/src/applications/maniphest/command/ManiphestPriorityEmailCommand.php +++ b/src/applications/maniphest/command/ManiphestPriorityEmailCommand.php @@ -71,7 +71,7 @@ final class ManiphestPriorityEmailCommand } $xactions[] = $object->getApplicationTransactionTemplate() - ->setTransactionType(ManiphestTransaction::TYPE_PRIORITY) + ->setTransactionType(ManiphestTaskPriorityTransaction::TRANSACTIONTYPE) ->setNewValue($priority); return $xactions; diff --git a/src/applications/maniphest/command/ManiphestStatusEmailCommand.php b/src/applications/maniphest/command/ManiphestStatusEmailCommand.php index dace0cb255..0387cae34e 100644 --- a/src/applications/maniphest/command/ManiphestStatusEmailCommand.php +++ b/src/applications/maniphest/command/ManiphestStatusEmailCommand.php @@ -73,7 +73,7 @@ final class ManiphestStatusEmailCommand } $xactions[] = $object->getApplicationTransactionTemplate() - ->setTransactionType(ManiphestTransaction::TYPE_STATUS) + ->setTransactionType(ManiphestTaskStatusTransaction::TRANSACTIONTYPE) ->setNewValue($status); return $xactions; diff --git a/src/applications/maniphest/conduit/ManiphestConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestConduitAPIMethod.php index d2a8de7d81..5a6a8cea33 100644 --- a/src/applications/maniphest/conduit/ManiphestConduitAPIMethod.php +++ b/src/applications/maniphest/conduit/ManiphestConduitAPIMethod.php @@ -60,7 +60,7 @@ abstract class ManiphestConduitAPIMethod extends ConduitAPIMethod { if ($is_new) { $task->setTitle((string)$request->getValue('title')); $task->setDescription((string)$request->getValue('description')); - $changes[ManiphestTransaction::TYPE_STATUS] = + $changes[ManiphestTaskStatusTransaction::TRANSACTIONTYPE] = ManiphestTaskStatus::getDefaultStatus(); $changes[PhabricatorTransactions::TYPE_SUBSCRIBERS] = array('+' => array($request->getUser()->getPHID())); @@ -73,12 +73,12 @@ abstract class ManiphestConduitAPIMethod extends ConduitAPIMethod { $title = $request->getValue('title'); if ($title !== null) { - $changes[ManiphestTransaction::TYPE_TITLE] = $title; + $changes[ManiphestTaskTitleTransaction::TRANSACTIONTYPE] = $title; } $desc = $request->getValue('description'); if ($desc !== null) { - $changes[ManiphestTransaction::TYPE_DESCRIPTION] = $desc; + $changes[ManiphestTaskDescriptionTransaction::TRANSACTIONTYPE] = $desc; } $status = $request->getValue('status'); @@ -88,7 +88,7 @@ abstract class ManiphestConduitAPIMethod extends ConduitAPIMethod { throw id(new ConduitException('ERR-INVALID-PARAMETER')) ->setErrorDescription(pht('Status set to invalid value.')); } - $changes[ManiphestTransaction::TYPE_STATUS] = $status; + $changes[ManiphestTaskStatusTransaction::TRANSACTIONTYPE] = $status; } } @@ -99,7 +99,7 @@ abstract class ManiphestConduitAPIMethod extends ConduitAPIMethod { throw id(new ConduitException('ERR-INVALID-PARAMETER')) ->setErrorDescription(pht('Priority set to invalid value.')); } - $changes[ManiphestTransaction::TYPE_PRIORITY] = $priority; + $changes[ManiphestTaskPriorityTransaction::TRANSACTIONTYPE] = $priority; } $owner_phid = $request->getValue('ownerPHID'); @@ -108,7 +108,7 @@ abstract class ManiphestConduitAPIMethod extends ConduitAPIMethod { array($owner_phid), PhabricatorPeopleUserPHIDType::TYPECONST, 'ownerPHID'); - $changes[ManiphestTransaction::TYPE_OWNER] = $owner_phid; + $changes[ManiphestTaskOwnerTransaction::TRANSACTIONTYPE] = $owner_phid; } $ccs = $request->getValue('ccPHIDs'); diff --git a/src/applications/maniphest/controller/ManiphestReportController.php b/src/applications/maniphest/controller/ManiphestReportController.php index f16281691b..3cc420a4c6 100644 --- a/src/applications/maniphest/controller/ManiphestReportController.php +++ b/src/applications/maniphest/controller/ManiphestReportController.php @@ -93,7 +93,7 @@ final class ManiphestReportController extends ManiphestController { ORDER BY x.dateCreated ASC', $table->getTableName(), $joins, - ManiphestTransaction::TYPE_STATUS); + ManiphestTaskStatusTransaction::TRANSACTIONTYPE); $stats = array(); $day_buckets = array(); diff --git a/src/applications/maniphest/controller/ManiphestSubpriorityController.php b/src/applications/maniphest/controller/ManiphestSubpriorityController.php index 25920212c8..e91cc65d4a 100644 --- a/src/applications/maniphest/controller/ManiphestSubpriorityController.php +++ b/src/applications/maniphest/controller/ManiphestSubpriorityController.php @@ -43,11 +43,11 @@ final class ManiphestSubpriorityController extends ManiphestController { $xactions = array(); $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_PRIORITY) + ->setTransactionType(ManiphestTaskPriorityTransaction::TRANSACTIONTYPE) ->setNewValue($pri); $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_SUBPRIORITY) + ->setTransactionType(ManiphestTaskSubpriorityTransaction::TRANSACTIONTYPE) ->setNewValue($sub); $editor = id(new ManiphestTransactionEditor()) diff --git a/src/applications/maniphest/controller/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/ManiphestTaskDetailController.php index 514090dda8..5384825de8 100644 --- a/src/applications/maniphest/controller/ManiphestTaskDetailController.php +++ b/src/applications/maniphest/controller/ManiphestTaskDetailController.php @@ -277,7 +277,7 @@ final class ManiphestTaskDetailController extends ManiphestController { $can_create = (bool)$edit_config; $can_reassign = $edit_engine->hasEditAccessToTransaction( - ManiphestTransaction::TYPE_OWNER); + ManiphestTaskOwnerTransaction::TRANSACTIONTYPE); if ($can_create) { $form_key = $edit_config->getIdentifier(); diff --git a/src/applications/maniphest/editor/ManiphestEditEngine.php b/src/applications/maniphest/editor/ManiphestEditEngine.php index ad0171592c..52491ce6e6 100644 --- a/src/applications/maniphest/editor/ManiphestEditEngine.php +++ b/src/applications/maniphest/editor/ManiphestEditEngine.php @@ -150,7 +150,7 @@ EODOCS ->setConduitDescription(pht('Create as a subtask of another task.')) ->setConduitTypeDescription(pht('PHID of the parent task.')) ->setAliases(array('parentPHID')) - ->setTransactionType(ManiphestTransaction::TYPE_PARENT) + ->setTransactionType(ManiphestTaskParentTransaction::TRANSACTIONTYPE) ->setHandleParameterType(new ManiphestTaskListHTTPParameterType()) ->setSingleValue(null) ->setIsReorderable(false) @@ -179,7 +179,7 @@ EODOCS ->setDescription(pht('Name of the task.')) ->setConduitDescription(pht('Rename the task.')) ->setConduitTypeDescription(pht('New task name.')) - ->setTransactionType(ManiphestTransaction::TYPE_TITLE) + ->setTransactionType(ManiphestTaskTitleTransaction::TRANSACTIONTYPE) ->setIsRequired(true) ->setValue($object->getTitle()), id(new PhabricatorUsersEditField()) @@ -190,7 +190,7 @@ EODOCS ->setConduitDescription(pht('Reassign the task.')) ->setConduitTypeDescription( pht('New task owner, or `null` to unassign.')) - ->setTransactionType(ManiphestTransaction::TYPE_OWNER) + ->setTransactionType(ManiphestTaskOwnerTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setSingleValue($object->getOwnerPHID()) ->setCommentActionLabel(pht('Assign / Claim')) @@ -201,7 +201,7 @@ EODOCS ->setDescription(pht('Status of the task.')) ->setConduitDescription(pht('Change the task status.')) ->setConduitTypeDescription(pht('New task status constant.')) - ->setTransactionType(ManiphestTransaction::TYPE_STATUS) + ->setTransactionType(ManiphestTaskStatusTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setValue($object->getStatus()) ->setOptions($status_map) @@ -213,7 +213,7 @@ EODOCS ->setDescription(pht('Priority of the task.')) ->setConduitDescription(pht('Change the priority of the task.')) ->setConduitTypeDescription(pht('New task priority constant.')) - ->setTransactionType(ManiphestTransaction::TYPE_PRIORITY) + ->setTransactionType(ManiphestTaskPriorityTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setValue($object->getPriority()) ->setOptions($priority_map) @@ -230,7 +230,7 @@ EODOCS ->setDescription(pht('Point value of the task.')) ->setConduitDescription(pht('Change the task point value.')) ->setConduitTypeDescription(pht('New task point value.')) - ->setTransactionType(ManiphestTransaction::TYPE_POINTS) + ->setTransactionType(ManiphestTaskPointsTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setValue($object->getPoints()) ->setCommentActionLabel($action_label); @@ -242,7 +242,7 @@ EODOCS ->setDescription(pht('Task description.')) ->setConduitDescription(pht('Update the task description.')) ->setConduitTypeDescription(pht('New task description.')) - ->setTransactionType(ManiphestTransaction::TYPE_DESCRIPTION) + ->setTransactionType(ManiphestTaskDescriptionTransaction::TRANSACTIONTYPE) ->setValue($object->getDescription()) ->setPreviewPanel( id(new PHUIRemarkupPreviewPanel()) diff --git a/src/applications/maniphest/editor/ManiphestTransactionEditor.php b/src/applications/maniphest/editor/ManiphestTransactionEditor.php index 0a4ea8469a..c9e17cd4de 100644 --- a/src/applications/maniphest/editor/ManiphestTransactionEditor.php +++ b/src/applications/maniphest/editor/ManiphestTransactionEditor.php @@ -18,18 +18,6 @@ final class ManiphestTransactionEditor $types[] = PhabricatorTransactions::TYPE_COMMENT; $types[] = PhabricatorTransactions::TYPE_EDGE; - $types[] = ManiphestTransaction::TYPE_PRIORITY; - $types[] = ManiphestTransaction::TYPE_STATUS; - $types[] = ManiphestTransaction::TYPE_TITLE; - $types[] = ManiphestTransaction::TYPE_DESCRIPTION; - $types[] = ManiphestTransaction::TYPE_OWNER; - $types[] = ManiphestTransaction::TYPE_SUBPRIORITY; - $types[] = ManiphestTransaction::TYPE_MERGED_INTO; - $types[] = ManiphestTransaction::TYPE_MERGED_FROM; - $types[] = ManiphestTransaction::TYPE_UNBLOCK; - $types[] = ManiphestTransaction::TYPE_PARENT; - $types[] = ManiphestTransaction::TYPE_COVER_IMAGE; - $types[] = ManiphestTransaction::TYPE_POINTS; $types[] = PhabricatorTransactions::TYPE_COLUMNS; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; @@ -37,47 +25,19 @@ final class ManiphestTransactionEditor return $types; } + public function getCreateObjectTitle($author, $object) { + return pht('%s created this task.', $author); + } + + public function getCreateObjectTitleForFeed($author, $object) { + return pht('%s created %s.', $author, $object); + } + protected function getCustomTransactionOldValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case ManiphestTransaction::TYPE_PRIORITY: - if ($this->getIsNewObject()) { - return null; - } - return (int)$object->getPriority(); - case ManiphestTransaction::TYPE_STATUS: - if ($this->getIsNewObject()) { - return null; - } - return $object->getStatus(); - case ManiphestTransaction::TYPE_TITLE: - if ($this->getIsNewObject()) { - return null; - } - return $object->getTitle(); - case ManiphestTransaction::TYPE_DESCRIPTION: - if ($this->getIsNewObject()) { - return null; - } - return $object->getDescription(); - case ManiphestTransaction::TYPE_OWNER: - return nonempty($object->getOwnerPHID(), null); - case ManiphestTransaction::TYPE_SUBPRIORITY: - return $object->getSubpriority(); - case ManiphestTransaction::TYPE_COVER_IMAGE: - return $object->getCoverImageFilePHID(); - case ManiphestTransaction::TYPE_POINTS: - $points = $object->getPoints(); - if ($points !== null) { - $points = (double)$points; - } - return $points; - case ManiphestTransaction::TYPE_MERGED_INTO: - case ManiphestTransaction::TYPE_MERGED_FROM: - return null; - case ManiphestTransaction::TYPE_PARENT: case PhabricatorTransactions::TYPE_COLUMNS: return null; } @@ -88,31 +48,8 @@ final class ManiphestTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case ManiphestTransaction::TYPE_PRIORITY: - return (int)$xaction->getNewValue(); - case ManiphestTransaction::TYPE_OWNER: - return nonempty($xaction->getNewValue(), null); - case ManiphestTransaction::TYPE_STATUS: - case ManiphestTransaction::TYPE_TITLE: - case ManiphestTransaction::TYPE_DESCRIPTION: - case ManiphestTransaction::TYPE_SUBPRIORITY: - case ManiphestTransaction::TYPE_MERGED_INTO: - case ManiphestTransaction::TYPE_MERGED_FROM: - case ManiphestTransaction::TYPE_UNBLOCK: - case ManiphestTransaction::TYPE_COVER_IMAGE: - return $xaction->getNewValue(); - case ManiphestTransaction::TYPE_PARENT: case PhabricatorTransactions::TYPE_COLUMNS: return $xaction->getNewValue(); - case ManiphestTransaction::TYPE_POINTS: - $value = $xaction->getNewValue(); - if (!strlen($value)) { - $value = null; - } - if ($value !== null) { - $value = (double)$value; - } - return $value; } } @@ -136,72 +73,6 @@ final class ManiphestTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case ManiphestTransaction::TYPE_PRIORITY: - return $object->setPriority($xaction->getNewValue()); - case ManiphestTransaction::TYPE_STATUS: - return $object->setStatus($xaction->getNewValue()); - case ManiphestTransaction::TYPE_TITLE: - return $object->setTitle($xaction->getNewValue()); - case ManiphestTransaction::TYPE_DESCRIPTION: - return $object->setDescription($xaction->getNewValue()); - case ManiphestTransaction::TYPE_OWNER: - $phid = $xaction->getNewValue(); - - // Update the "ownerOrdering" column to contain the full name of the - // owner, if the task is assigned. - - $handle = null; - if ($phid) { - $handle = id(new PhabricatorHandleQuery()) - ->setViewer($this->getActor()) - ->withPHIDs(array($phid)) - ->executeOne(); - } - - if ($handle) { - $object->setOwnerOrdering($handle->getName()); - } else { - $object->setOwnerOrdering(null); - } - - return $object->setOwnerPHID($phid); - case ManiphestTransaction::TYPE_SUBPRIORITY: - $object->setSubpriority($xaction->getNewValue()); - return; - case ManiphestTransaction::TYPE_MERGED_INTO: - $object->setStatus(ManiphestTaskStatus::getDuplicateStatus()); - return; - case ManiphestTransaction::TYPE_COVER_IMAGE: - $file_phid = $xaction->getNewValue(); - - if ($file_phid) { - $file = id(new PhabricatorFileQuery()) - ->setViewer($this->getActor()) - ->withPHIDs(array($file_phid)) - ->executeOne(); - } else { - $file = null; - } - - if (!$file || !$file->isTransformableImage()) { - $object->setProperty('cover.filePHID', null); - $object->setProperty('cover.thumbnailPHID', null); - return; - } - - $xform_key = PhabricatorFileThumbnailTransform::TRANSFORM_WORKCARD; - - $xform = PhabricatorFileTransform::getTransformByKey($xform_key) - ->executeTransform($file); - - $object->setProperty('cover.filePHID', $file->getPHID()); - $object->setProperty('cover.thumbnailPHID', $xform->getPHID()); - return; - case ManiphestTransaction::TYPE_POINTS: - $object->setPoints($xaction->getNewValue()); - return; - case ManiphestTransaction::TYPE_MERGED_FROM: - case ManiphestTransaction::TYPE_PARENT: case PhabricatorTransactions::TYPE_COLUMNS: return; } @@ -212,22 +83,11 @@ final class ManiphestTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case ManiphestTransaction::TYPE_PARENT: - $parent_phid = $xaction->getNewValue(); - $parent_type = ManiphestTaskDependsOnTaskEdgeType::EDGECONST; - $task_phid = $object->getPHID(); - - id(new PhabricatorEdgeEditor()) - ->addEdge($parent_phid, $parent_type, $task_phid) - ->save(); - break; case PhabricatorTransactions::TYPE_COLUMNS: foreach ($xaction->getNewValue() as $move) { $this->applyBoardMove($object, $move); } break; - default: - break; } } @@ -240,7 +100,7 @@ final class ManiphestTransactionEditor $unblock_xaction = null; foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { - case ManiphestTransaction::TYPE_STATUS: + case ManiphestTaskStatusTransaction::TRANSACTIONTYPE: $unblock_xaction = $xaction; break; } @@ -265,7 +125,8 @@ final class ManiphestTransactionEditor foreach ($blocked_tasks as $blocked_task) { $parent_xaction = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_UNBLOCK) + ->setTransactionType( + ManiphestTaskUnblockTransaction::TRANSACTIONTYPE) ->setOldValue(array($object->getPHID() => $old)) ->setNewValue(array($object->getPHID() => $new)); @@ -398,7 +259,7 @@ final class ManiphestTransactionEditor protected function shouldPublishFeedStory( PhabricatorLiskDAO $object, array $xactions) { - return $this->shouldSendMail($object, $xactions); + return true; } protected function supportsSearch() { @@ -426,11 +287,11 @@ final class ManiphestTransactionEditor parent::requireCapabilities($object, $xaction); $app_capability_map = array( - ManiphestTransaction::TYPE_PRIORITY => + ManiphestTaskPriorityTransaction::TRANSACTIONTYPE => ManiphestEditPriorityCapability::CAPABILITY, - ManiphestTransaction::TYPE_STATUS => + ManiphestTaskStatusTransaction::TRANSACTIONTYPE => ManiphestEditStatusCapability::CAPABILITY, - ManiphestTransaction::TYPE_OWNER => + ManiphestTaskOwnerTransaction::TRANSACTIONTYPE => ManiphestEditAssignCapability::CAPABILITY, PhabricatorTransactions::TYPE_EDIT_POLICY => ManiphestEditPoliciesCapability::CAPABILITY, @@ -471,7 +332,7 @@ final class ManiphestTransactionEditor $copy = parent::adjustObjectForPolicyChecks($object, $xactions); foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { - case ManiphestTransaction::TYPE_OWNER: + case ManiphestTaskOwnerTransaction::TRANSACTIONTYPE: $copy->setOwnerPHID($xaction->getNewValue()); break; default: @@ -629,157 +490,6 @@ final class ManiphestTransactionEditor return array($dst->getPriority(), $sub); } - protected function validateTransaction( - PhabricatorLiskDAO $object, - $type, - array $xactions) { - - $errors = parent::validateTransaction($object, $type, $xactions); - - switch ($type) { - case ManiphestTransaction::TYPE_TITLE: - $missing = $this->validateIsEmptyTextField( - $object->getTitle(), - $xactions); - - if ($missing) { - $error = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Required'), - pht('Task title is required.'), - nonempty(last($xactions), null)); - - $error->setIsMissingFieldError(true); - $errors[] = $error; - } - break; - case ManiphestTransaction::TYPE_PARENT: - $with_effect = array(); - foreach ($xactions as $xaction) { - $task_phid = $xaction->getNewValue(); - if (!$task_phid) { - continue; - } - - $with_effect[] = $xaction; - - $task = id(new ManiphestTaskQuery()) - ->setViewer($this->getActor()) - ->withPHIDs(array($task_phid)) - ->executeOne(); - if (!$task) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'Parent task identifier "%s" does not identify a visible '. - 'task.', - $task_phid), - $xaction); - } - } - - if ($with_effect && !$this->getIsNewObject()) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'You can only select a parent task when creating a '. - 'transaction for the first time.'), - last($with_effect)); - } - break; - case ManiphestTransaction::TYPE_OWNER: - foreach ($xactions as $xaction) { - $old = $xaction->getOldValue(); - $new = $xaction->getNewValue(); - if (!strlen($new)) { - continue; - } - - if ($new === $old) { - continue; - } - - $assignee_list = id(new PhabricatorPeopleQuery()) - ->setViewer($this->getActor()) - ->withPHIDs(array($new)) - ->execute(); - if (!$assignee_list) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'User "%s" is not a valid user.', - $new), - $xaction); - } - } - break; - case ManiphestTransaction::TYPE_COVER_IMAGE: - foreach ($xactions as $xaction) { - $old = $xaction->getOldValue(); - $new = $xaction->getNewValue(); - if (!$new) { - continue; - } - - if ($new === $old) { - continue; - } - - $file = id(new PhabricatorFileQuery()) - ->setViewer($this->getActor()) - ->withPHIDs(array($new)) - ->executeOne(); - if (!$file) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht('File "%s" is not valid.', $new), - $xaction); - continue; - } - - if (!$file->isTransformableImage()) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht('File "%s" is not a valid image file.', $new), - $xaction); - continue; - } - } - break; - - case ManiphestTransaction::TYPE_POINTS: - foreach ($xactions as $xaction) { - $new = $xaction->getNewValue(); - if (strlen($new) && !is_numeric($new)) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht('Points value must be numeric or empty.'), - $xaction); - continue; - } - - if ((double)$new < 0) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht('Points value must be nonnegative.'), - $xaction); - continue; - } - } - break; - - } - - return $errors; - } - protected function validateAllTransactions( PhabricatorLiskDAO $object, array $xactions) { @@ -806,7 +516,8 @@ final class ManiphestTransactionEditor $any_assign = false; foreach ($xactions as $xaction) { - if ($xaction->getTransactionType() == ManiphestTransaction::TYPE_OWNER) { + if ($xaction->getTransactionType() == + ManiphestTaskOwnerTransaction::TRANSACTIONTYPE) { $any_assign = true; break; } @@ -817,7 +528,7 @@ final class ManiphestTransactionEditor $new_status = null; foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { - case ManiphestTransaction::TYPE_STATUS: + case ManiphestTaskStatusTransaction::TRANSACTIONTYPE: $new_status = $xaction->getNewValue(); break; } @@ -838,7 +549,7 @@ final class ManiphestTransactionEditor // Don't claim the task if the status is configured to not claim. if ($actor_phid && $is_claim) { $results[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_OWNER) + ->setTransactionType(ManiphestTaskOwnerTransaction::TRANSACTIONTYPE) ->setNewValue($actor_phid); } } @@ -881,7 +592,7 @@ final class ManiphestTransactionEditor $this->moreValidationErrors[] = $error; } break; - case ManiphestTransaction::TYPE_OWNER: + case ManiphestTaskOwnerTransaction::TRANSACTIONTYPE: // If this is a no-op update, don't expand it. $old_value = $object->getOwnerPHID(); $new_value = $xaction->getNewValue(); @@ -906,20 +617,6 @@ final class ManiphestTransactionEditor return $results; } - protected function extractFilePHIDsFromCustomTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - $phids = parent::extractFilePHIDsFromCustomTransaction($object, $xaction); - - switch ($xaction->getTransactionType()) { - case ManiphestTransaction::TYPE_COVER_IMAGE: - $phids[] = $xaction->getNewValue(); - break; - } - - return $phids; - } - private function buildMoveTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { diff --git a/src/applications/maniphest/herald/ManiphestTaskAssignHeraldAction.php b/src/applications/maniphest/herald/ManiphestTaskAssignHeraldAction.php index 00f539d4a7..fffd0bac7d 100644 --- a/src/applications/maniphest/herald/ManiphestTaskAssignHeraldAction.php +++ b/src/applications/maniphest/herald/ManiphestTaskAssignHeraldAction.php @@ -40,7 +40,7 @@ abstract class ManiphestTaskAssignHeraldAction } $xaction = $adapter->newTransaction() - ->setTransactionType(ManiphestTransaction::TYPE_OWNER) + ->setTransactionType(ManiphestTaskOwnerTransaction::TRANSACTIONTYPE) ->setNewValue($phid); $adapter->queueTransaction($xaction); diff --git a/src/applications/maniphest/herald/ManiphestTaskPriorityHeraldAction.php b/src/applications/maniphest/herald/ManiphestTaskPriorityHeraldAction.php index c055266a44..ff8420544d 100644 --- a/src/applications/maniphest/herald/ManiphestTaskPriorityHeraldAction.php +++ b/src/applications/maniphest/herald/ManiphestTaskPriorityHeraldAction.php @@ -40,7 +40,7 @@ final class ManiphestTaskPriorityHeraldAction } $xaction = $adapter->newTransaction() - ->setTransactionType(ManiphestTransaction::TYPE_PRIORITY) + ->setTransactionType(ManiphestTaskPriorityTransaction::TRANSACTIONTYPE) ->setNewValue($priority); $adapter->queueTransaction($xaction); diff --git a/src/applications/maniphest/herald/ManiphestTaskStatusHeraldAction.php b/src/applications/maniphest/herald/ManiphestTaskStatusHeraldAction.php index 26e212d9c7..c1ff3bcdc7 100644 --- a/src/applications/maniphest/herald/ManiphestTaskStatusHeraldAction.php +++ b/src/applications/maniphest/herald/ManiphestTaskStatusHeraldAction.php @@ -40,7 +40,7 @@ final class ManiphestTaskStatusHeraldAction } $xaction = $adapter->newTransaction() - ->setTransactionType(ManiphestTransaction::TYPE_STATUS) + ->setTransactionType(ManiphestTaskStatusTransaction::TRANSACTIONTYPE) ->setNewValue($status); $adapter->queueTransaction($xaction); diff --git a/src/applications/maniphest/lipsum/PhabricatorManiphestTaskTestDataGenerator.php b/src/applications/maniphest/lipsum/PhabricatorManiphestTaskTestDataGenerator.php index 404a36af8f..2d6a6807ce 100644 --- a/src/applications/maniphest/lipsum/PhabricatorManiphestTaskTestDataGenerator.php +++ b/src/applications/maniphest/lipsum/PhabricatorManiphestTaskTestDataGenerator.php @@ -22,15 +22,15 @@ final class PhabricatorManiphestTaskTestDataGenerator $template = new ManiphestTransaction(); // Accumulate Transactions $changes = array(); - $changes[ManiphestTransaction::TYPE_TITLE] = + $changes[ManiphestTaskTitleTransaction::TRANSACTIONTYPE] = $this->generateTitle(); - $changes[ManiphestTransaction::TYPE_DESCRIPTION] = + $changes[ManiphestTaskDescriptionTransaction::TRANSACTIONTYPE] = $this->generateDescription(); - $changes[ManiphestTransaction::TYPE_OWNER] = + $changes[ManiphestTaskOwnerTransaction::TRANSACTIONTYPE] = $this->loadOwnerPHID(); - $changes[ManiphestTransaction::TYPE_STATUS] = + $changes[ManiphestTaskStatusTransaction::TRANSACTIONTYPE] = $this->generateTaskStatus(); - $changes[ManiphestTransaction::TYPE_PRIORITY] = + $changes[ManiphestTaskPriorityTransaction::TRANSACTIONTYPE] = $this->generateTaskPriority(); $changes[PhabricatorTransactions::TYPE_SUBSCRIBERS] = array('=' => $this->getCCPHIDs()); diff --git a/src/applications/maniphest/mail/ManiphestReplyHandler.php b/src/applications/maniphest/mail/ManiphestReplyHandler.php index bc9b2456d4..bbcfe86551 100644 --- a/src/applications/maniphest/mail/ManiphestReplyHandler.php +++ b/src/applications/maniphest/mail/ManiphestReplyHandler.php @@ -25,11 +25,12 @@ final class ManiphestReplyHandler if ($is_new) { $xactions[] = $this->newTransaction() - ->setTransactionType(ManiphestTransaction::TYPE_TITLE) + ->setTransactionType(ManiphestTaskTitleTransaction::TRANSACTIONTYPE) ->setNewValue(nonempty($mail->getSubject(), pht('Untitled Task'))); $xactions[] = $this->newTransaction() - ->setTransactionType(ManiphestTransaction::TYPE_DESCRIPTION) + ->setTransactionType( + ManiphestTaskDescriptionTransaction::TRANSACTIONTYPE) ->setNewValue($body); $actor_phid = $actor->getPHID(); diff --git a/src/applications/maniphest/relationship/ManiphestTaskRelationship.php b/src/applications/maniphest/relationship/ManiphestTaskRelationship.php index 928d49e87d..04f78b8523 100644 --- a/src/applications/maniphest/relationship/ManiphestTaskRelationship.php +++ b/src/applications/maniphest/relationship/ManiphestTaskRelationship.php @@ -19,7 +19,8 @@ abstract class ManiphestTaskRelationship protected function newMergeIntoTransactions(ManiphestTask $task) { return array( id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_MERGED_INTO) + ->setTransactionType( + ManiphestTaskMergedIntoTransaction::TRANSACTIONTYPE) ->setNewValue($task->getPHID()), ); } @@ -34,7 +35,8 @@ abstract class ManiphestTaskRelationship ->setNewValue(array('+' => $subscriber_phids)); $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_MERGED_FROM) + ->setTransactionType( + ManiphestTaskMergedFromTransaction::TRANSACTIONTYPE) ->setNewValue(mpull($tasks, 'getPHID')); return $xactions; diff --git a/src/applications/maniphest/storage/ManiphestTransaction.php b/src/applications/maniphest/storage/ManiphestTransaction.php index b9b8560b34..0d27ddc5da 100644 --- a/src/applications/maniphest/storage/ManiphestTransaction.php +++ b/src/applications/maniphest/storage/ManiphestTransaction.php @@ -1,25 +1,7 @@ getTransactionType()) { - case self::TYPE_EDGE: - case self::TYPE_UNBLOCK: + case ManiphestTaskEdgeTransaction::TRANSACTIONTYPE: + case ManiphestTaskUnblockTransaction::TRANSACTIONTYPE: return false; } return parent::shouldGenerateOldValue(); } - protected function newRemarkupChanges() { - $changes = array(); - - switch ($this->getTransactionType()) { - case self::TYPE_DESCRIPTION: - $changes[] = $this->newRemarkupChange() - ->setOldValue($this->getOldValue()) - ->setNewValue($this->getNewValue()); - break; - } - - return $changes; - } - public function getRequiredHandlePHIDs() { $phids = parent::getRequiredHandlePHIDs(); @@ -75,7 +47,7 @@ final class ManiphestTransaction $old = $this->getOldValue(); switch ($this->getTransactionType()) { - case self::TYPE_OWNER: + case ManiphestTaskOwnerTransaction::TRANSACTIONTYPE: if ($new) { $phids[] = $new; } @@ -84,13 +56,13 @@ final class ManiphestTransaction $phids[] = $old; } break; - case self::TYPE_MERGED_INTO: + case ManiphestTaskMergedIntoTransaction::TRANSACTIONTYPE: $phids[] = $new; break; - case self::TYPE_MERGED_FROM: + case ManiphestTaskMergedFromTransaction::TRANSACTIONTYPE: $phids = array_merge($phids, $new); break; - case self::TYPE_EDGE: + case ManiphestTaskEdgeTransaction::TRANSACTIONTYPE: $phids = array_mergev( array( $phids, @@ -98,7 +70,7 @@ final class ManiphestTransaction array_keys(nonempty($new, array())), )); break; - case self::TYPE_ATTACH: + case ManiphestTaskAttachTransaction::TRANSACTIONTYPE: $old = nonempty($old, array()); $new = nonempty($new, array()); $phids = array_mergev( @@ -108,12 +80,12 @@ final class ManiphestTransaction array_keys(idx($old, 'FILE', array())), )); break; - case self::TYPE_UNBLOCK: + case ManiphestTaskUnblockTransaction::TRANSACTIONTYPE: foreach (array_keys($new) as $phid) { $phids[] = $phid; } break; - case self::TYPE_STATUS: + case ManiphestTaskStatusTransaction::TRANSACTIONTYPE: $commit_phid = $this->getMetadataValue('commitPHID'); if ($commit_phid) { $phids[] = $commit_phid; @@ -124,275 +96,27 @@ final class ManiphestTransaction return $phids; } - public function shouldHide() { - switch ($this->getTransactionType()) { - case PhabricatorTransactions::TYPE_EDGE: - $commit_phid = $this->getMetadataValue('commitPHID'); - $edge_type = $this->getMetadataValue('edge:type'); - - if ($edge_type == ManiphestTaskHasCommitEdgeType::EDGECONST) { - if ($commit_phid) { - return true; - } - } - break; - case self::TYPE_DESCRIPTION: - case self::TYPE_PRIORITY: - case self::TYPE_STATUS: - if ($this->getOldValue() === null) { - return true; - } else { - return false; - } - break; - case self::TYPE_SUBPRIORITY: - case self::TYPE_PARENT: - return true; - case self::TYPE_COVER_IMAGE: - // At least for now, don't show these. - return true; - case self::TYPE_POINTS: - if (!ManiphestTaskPoints::getIsEnabled()) { - return true; - } - } - - return parent::shouldHide(); - } - - public function shouldHideForMail(array $xactions) { - switch ($this->getTransactionType()) { - case self::TYPE_POINTS: - return true; - } - - return parent::shouldHideForMail($xactions); - } - - public function shouldHideForFeed() { - switch ($this->getTransactionType()) { - case self::TYPE_UNBLOCK: - // Hide "alice created X, a task blocking Y." from feed because it - // will almost always appear adjacent to "alice created Y". - $is_new = $this->getMetadataValue('blocker.new'); - if ($is_new) { - return true; - } - break; - case self::TYPE_POINTS: - return true; - } - - return parent::shouldHideForFeed(); - } - - public function getActionStrength() { - switch ($this->getTransactionType()) { - case self::TYPE_TITLE: - return 1.4; - case self::TYPE_STATUS: - return 1.3; - case self::TYPE_OWNER: - return 1.2; - case self::TYPE_PRIORITY: - return 1.1; - } - - return parent::getActionStrength(); - } - - - public function getColor() { - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_OWNER: - if ($this->getAuthorPHID() == $new) { - return 'green'; - } else if (!$new) { - return 'black'; - } else if (!$old) { - return 'green'; - } else { - return 'green'; - } - - case self::TYPE_STATUS: - $color = ManiphestTaskStatus::getStatusColor($new); - if ($color !== null) { - return $color; - } - - if (ManiphestTaskStatus::isOpenStatus($new)) { - return 'green'; - } else { - return 'indigo'; - } - - case self::TYPE_PRIORITY: - if ($old == ManiphestTaskPriority::getDefaultPriority()) { - return 'green'; - } else if ($old > $new) { - return 'grey'; - } else { - return 'yellow'; - } - - case self::TYPE_MERGED_FROM: - return 'orange'; - - case self::TYPE_MERGED_INTO: - return 'indigo'; - } - - return parent::getColor(); - } - public function getActionName() { $old = $this->getOldValue(); $new = $this->getNewValue(); - switch ($this->getTransactionType()) { - case self::TYPE_TITLE: - if ($old === null) { - return pht('Created'); - } - - return pht('Retitled'); - - case self::TYPE_STATUS: - $action = ManiphestTaskStatus::getStatusActionName($new); - if ($action) { - return $action; - } - - $old_closed = ManiphestTaskStatus::isClosedStatus($old); - $new_closed = ManiphestTaskStatus::isClosedStatus($new); - - if ($new_closed && !$old_closed) { - return pht('Closed'); - } else if (!$new_closed && $old_closed) { - return pht('Reopened'); - } else { - return pht('Changed Status'); - } - - case self::TYPE_DESCRIPTION: - return pht('Edited'); - - case self::TYPE_OWNER: - if ($this->getAuthorPHID() == $new) { - return pht('Claimed'); - } else if (!$new) { - return pht('Unassigned'); - } else if (!$old) { - return pht('Assigned'); - } else { - return pht('Reassigned'); - } - case PhabricatorTransactions::TYPE_COLUMNS: return pht('Changed Project Column'); - - case self::TYPE_PRIORITY: - if ($old == ManiphestTaskPriority::getDefaultPriority()) { - return pht('Triaged'); - } else if ($old > $new) { - return pht('Lowered Priority'); - } else { - return pht('Raised Priority'); - } - - case self::TYPE_EDGE: - case self::TYPE_ATTACH: - return pht('Attached'); - - case self::TYPE_UNBLOCK: - $old_status = head($old); - $new_status = head($new); - - $old_closed = ManiphestTaskStatus::isClosedStatus($old_status); - $new_closed = ManiphestTaskStatus::isClosedStatus($new_status); - - if ($old_closed && !$new_closed) { - return pht('Block'); - } else if (!$old_closed && $new_closed) { - return pht('Unblock'); - } else { - return pht('Blocker'); - } - - case self::TYPE_MERGED_INTO: - case self::TYPE_MERGED_FROM: - return pht('Merged'); - } return parent::getActionName(); } public function getIcon() { - $old = $this->getOldValue(); - $new = $this->getNewValue(); - switch ($this->getTransactionType()) { - case self::TYPE_OWNER: - return 'fa-user'; - - case self::TYPE_TITLE: - if ($old === null) { - return 'fa-pencil'; - } - - return 'fa-pencil'; - - case self::TYPE_STATUS: - $action = ManiphestTaskStatus::getStatusIcon($new); - if ($action !== null) { - return $action; - } - - if (ManiphestTaskStatus::isClosedStatus($new)) { - return 'fa-check'; - } else { - return 'fa-pencil'; - } - - case self::TYPE_DESCRIPTION: - return 'fa-pencil'; - case PhabricatorTransactions::TYPE_COLUMNS: return 'fa-columns'; - - case self::TYPE_MERGED_INTO: - return 'fa-check'; - case self::TYPE_MERGED_FROM: - return 'fa-compress'; - - case self::TYPE_PRIORITY: - if ($old == ManiphestTaskPriority::getDefaultPriority()) { - return 'fa-arrow-right'; - } else if ($old > $new) { - return 'fa-arrow-down'; - } else { - return 'fa-arrow-up'; - } - - case self::TYPE_EDGE: - case self::TYPE_ATTACH: - return 'fa-thumb-tack'; - - case self::TYPE_UNBLOCK: - return 'fa-shield'; - } return parent::getIcon(); } - public function getTitle() { $author_phid = $this->getAuthorPHID(); @@ -400,244 +124,13 @@ final class ManiphestTransaction $new = $this->getNewValue(); switch ($this->getTransactionType()) { - case PhabricatorTransactions::TYPE_CREATE: - return pht( - '%s created this task.', - $this->renderHandleLink($author_phid)); case PhabricatorTransactions::TYPE_SUBTYPE: return pht( '%s changed the subtype of this task from "%s" to "%s".', $this->renderHandleLink($author_phid), $this->renderSubtypeName($old), $this->renderSubtypeName($new)); - case self::TYPE_TITLE: - if ($old === null) { - return pht( - '%s created this task.', - $this->renderHandleLink($author_phid)); - } - return pht( - '%s changed the title from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); - - case self::TYPE_DESCRIPTION: - return pht( - '%s edited the task description.', - $this->renderHandleLink($author_phid)); - - case self::TYPE_STATUS: - $old_closed = ManiphestTaskStatus::isClosedStatus($old); - $new_closed = ManiphestTaskStatus::isClosedStatus($new); - - $old_name = ManiphestTaskStatus::getTaskStatusName($old); - $new_name = ManiphestTaskStatus::getTaskStatusName($new); - - $commit_phid = $this->getMetadataValue('commitPHID'); - - if ($new_closed && !$old_closed) { - if ($new == ManiphestTaskStatus::getDuplicateStatus()) { - if ($commit_phid) { - return pht( - '%s closed this task as a duplicate by committing %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($commit_phid)); - } else { - return pht( - '%s closed this task as a duplicate.', - $this->renderHandleLink($author_phid)); - } - } else { - if ($commit_phid) { - return pht( - '%s closed this task as "%s" by committing %s.', - $this->renderHandleLink($author_phid), - $new_name, - $this->renderHandleLink($commit_phid)); - } else { - return pht( - '%s closed this task as "%s".', - $this->renderHandleLink($author_phid), - $new_name); - } - } - } else if (!$new_closed && $old_closed) { - if ($commit_phid) { - return pht( - '%s reopened this task as "%s" by committing %s.', - $this->renderHandleLink($author_phid), - $new_name, - $this->renderHandleLink($commit_phid)); - } else { - return pht( - '%s reopened this task as "%s".', - $this->renderHandleLink($author_phid), - $new_name); - } - } else { - if ($commit_phid) { - return pht( - '%s changed the task status from "%s" to "%s" by committing %s.', - $this->renderHandleLink($author_phid), - $old_name, - $new_name, - $this->renderHandleLink($commit_phid)); - } else { - return pht( - '%s changed the task status from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old_name, - $new_name); - } - } - - case self::TYPE_UNBLOCK: - $blocker_phid = key($new); - $old_status = head($old); - $new_status = head($new); - - $old_closed = ManiphestTaskStatus::isClosedStatus($old_status); - $new_closed = ManiphestTaskStatus::isClosedStatus($new_status); - - $old_name = ManiphestTaskStatus::getTaskStatusName($old_status); - $new_name = ManiphestTaskStatus::getTaskStatusName($new_status); - - if ($this->getMetadataValue('blocker.new')) { - return pht( - '%s created subtask %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($blocker_phid)); - } else if ($old_closed && !$new_closed) { - return pht( - '%s reopened subtask %s as "%s".', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($blocker_phid), - $new_name); - } else if (!$old_closed && $new_closed) { - return pht( - '%s closed subtask %s as "%s".', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($blocker_phid), - $new_name); - } else { - return pht( - '%s changed the status of subtask %s from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($blocker_phid), - $old_name, - $new_name); - } - - case self::TYPE_OWNER: - if ($author_phid == $new) { - return pht( - '%s claimed this task.', - $this->renderHandleLink($author_phid)); - } else if (!$new) { - return pht( - '%s removed %s as the assignee of this task.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($old)); - } else if (!$old) { - return pht( - '%s assigned this task to %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($new)); - } else { - return pht( - '%s reassigned this task from %s to %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($old), - $this->renderHandleLink($new)); - } - - case self::TYPE_PRIORITY: - $old_name = ManiphestTaskPriority::getTaskPriorityName($old); - $new_name = ManiphestTaskPriority::getTaskPriorityName($new); - - if ($old == ManiphestTaskPriority::getDefaultPriority()) { - return pht( - '%s triaged this task as "%s" priority.', - $this->renderHandleLink($author_phid), - $new_name); - } else if ($old > $new) { - return pht( - '%s lowered the priority of this task from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old_name, - $new_name); - } else { - return pht( - '%s raised the priority of this task from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old_name, - $new_name); - } - - case self::TYPE_ATTACH: - $old = nonempty($old, array()); - $new = nonempty($new, array()); - $new = array_keys(idx($new, 'FILE', array())); - $old = array_keys(idx($old, 'FILE', array())); - - $added = array_diff($new, $old); - $removed = array_diff($old, $new); - if ($added && !$removed) { - return pht( - '%s attached %s file(s): %s.', - $this->renderHandleLink($author_phid), - phutil_count($added), - $this->renderHandleList($added)); - } else if ($removed && !$added) { - return pht( - '%s detached %s file(s): %s.', - $this->renderHandleLink($author_phid), - phutil_count($removed), - $this->renderHandleList($removed)); - } else { - return pht( - '%s changed file(s), attached %s: %s; detached %s: %s.', - $this->renderHandleLink($author_phid), - phutil_count($added), - $this->renderHandleList($added), - phutil_count($removed), - $this->renderHandleList($removed)); - } - - case self::TYPE_MERGED_INTO: - return pht( - '%s closed this task as a duplicate of %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($new)); break; - - case self::TYPE_MERGED_FROM: - return pht( - '%s merged %s task(s): %s.', - $this->renderHandleLink($author_phid), - phutil_count($new), - $this->renderHandleList($new)); - break; - - case self::TYPE_POINTS: - if ($old === null) { - return pht( - '%s set the point value for this task to %s.', - $this->renderHandleLink($author_phid), - $new); - } else if ($new === null) { - return pht( - '%s removed the point value for this task.', - $this->renderHandleLink($author_phid)); - } else { - return pht( - '%s changed the point value for this task from %s to %s.', - $this->renderHandleLink($author_phid), - $old, - $new); - } - } return parent::getTitle(); @@ -651,236 +144,6 @@ final class ManiphestTransaction $new = $this->getNewValue(); switch ($this->getTransactionType()) { - case self::TYPE_TITLE: - if ($old === null) { - return pht( - '%s created %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - } - - return pht( - '%s renamed %s from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - $old, - $new); - - case self::TYPE_DESCRIPTION: - return pht( - '%s edited the description of %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - - case self::TYPE_STATUS: - $old_closed = ManiphestTaskStatus::isClosedStatus($old); - $new_closed = ManiphestTaskStatus::isClosedStatus($new); - - $old_name = ManiphestTaskStatus::getTaskStatusName($old); - $new_name = ManiphestTaskStatus::getTaskStatusName($new); - - $commit_phid = $this->getMetadataValue('commitPHID'); - - if ($new_closed && !$old_closed) { - if ($new == ManiphestTaskStatus::getDuplicateStatus()) { - if ($commit_phid) { - return pht( - '%s closed %s as a duplicate by committing %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - $this->renderHandleLink($commit_phid)); - } else { - return pht( - '%s closed %s as a duplicate.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - } - } else { - if ($commit_phid) { - return pht( - '%s closed %s as "%s" by committing %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - $new_name, - $this->renderHandleLink($commit_phid)); - } else { - return pht( - '%s closed %s as "%s".', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - $new_name); - } - } - } else if (!$new_closed && $old_closed) { - if ($commit_phid) { - return pht( - '%s reopened %s as "%s" by committing %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - $new_name, - $this->renderHandleLink($commit_phid)); - } else { - return pht( - '%s reopened %s as "%s".', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - $new_name); - } - } else { - if ($commit_phid) { - return pht( - '%s changed the status of %s from "%s" to "%s" by committing %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - $old_name, - $new_name, - $this->renderHandleLink($commit_phid)); - } else { - return pht( - '%s changed the status of %s from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - $old_name, - $new_name); - } - } - - case self::TYPE_UNBLOCK: - $blocker_phid = key($new); - $old_status = head($old); - $new_status = head($new); - - $old_closed = ManiphestTaskStatus::isClosedStatus($old_status); - $new_closed = ManiphestTaskStatus::isClosedStatus($new_status); - - $old_name = ManiphestTaskStatus::getTaskStatusName($old_status); - $new_name = ManiphestTaskStatus::getTaskStatusName($new_status); - - if ($old_closed && !$new_closed) { - return pht( - '%s reopened %s, a subtask of %s, as "%s".', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($blocker_phid), - $this->renderHandleLink($object_phid), - $new_name); - } else if (!$old_closed && $new_closed) { - return pht( - '%s closed %s, a subtask of %s, as "%s".', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($blocker_phid), - $this->renderHandleLink($object_phid), - $new_name); - } else { - return pht( - '%s changed the status of %s, a subtask of %s, '. - 'from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($blocker_phid), - $this->renderHandleLink($object_phid), - $old_name, - $new_name); - } - - case self::TYPE_OWNER: - if ($author_phid == $new) { - return pht( - '%s claimed %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - } else if (!$new) { - return pht( - '%s placed %s up for grabs.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - } else if (!$old) { - return pht( - '%s assigned %s to %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - $this->renderHandleLink($new)); - } else { - return pht( - '%s reassigned %s from %s to %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - $this->renderHandleLink($old), - $this->renderHandleLink($new)); - } - - case self::TYPE_PRIORITY: - $old_name = ManiphestTaskPriority::getTaskPriorityName($old); - $new_name = ManiphestTaskPriority::getTaskPriorityName($new); - - if ($old == ManiphestTaskPriority::getDefaultPriority()) { - return pht( - '%s triaged %s as "%s" priority.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - $new_name); - } else if ($old > $new) { - return pht( - '%s lowered the priority of %s from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - $old_name, - $new_name); - } else { - return pht( - '%s raised the priority of %s from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - $old_name, - $new_name); - } - - case self::TYPE_ATTACH: - $old = nonempty($old, array()); - $new = nonempty($new, array()); - $new = array_keys(idx($new, 'FILE', array())); - $old = array_keys(idx($old, 'FILE', array())); - - $added = array_diff($new, $old); - $removed = array_diff($old, $new); - if ($added && !$removed) { - return pht( - '%s attached %d file(s) of %s: %s', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - count($added), - $this->renderHandleList($added)); - } else if ($removed && !$added) { - return pht( - '%s detached %d file(s) of %s: %s', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - count($removed), - $this->renderHandleList($removed)); - } else { - return pht( - '%s changed file(s) for %s, attached %d: %s; detached %d: %s', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - count($added), - $this->renderHandleList($added), - count($removed), - $this->renderHandleList($removed)); - } - - case self::TYPE_MERGED_INTO: - return pht( - '%s merged task %s into %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - $this->renderHandleLink($new)); - - case self::TYPE_MERGED_FROM: - return pht( - '%s merged %s task(s) %s into %s.', - $this->renderHandleLink($author_phid), - phutil_count($new), - $this->renderHandleList($new), - $this->renderHandleLink($object_phid)); - case PhabricatorTransactions::TYPE_SUBTYPE: return pht( '%s changed the subtype of %s from "%s" to "%s".', @@ -893,39 +156,14 @@ final class ManiphestTransaction return parent::getTitleForFeed(); } - private function renderSubtypeName($value) { - $object = $this->getObject(); - $map = $object->newEditEngineSubtypeMap(); - if (!isset($map[$value])) { - return $value; - } - - return $map[$value]->getName(); - } - - public function hasChangeDetails() { - switch ($this->getTransactionType()) { - case self::TYPE_DESCRIPTION: - return true; - } - return parent::hasChangeDetails(); - } - - public function renderChangeDetails(PhabricatorUser $viewer) { - return $this->renderTextCorpusChangeDetails( - $viewer, - $this->getOldValue(), - $this->getNewValue()); - } - public function getMailTags() { $tags = array(); switch ($this->getTransactionType()) { - case self::TYPE_MERGED_INTO: - case self::TYPE_STATUS: + case ManiphestTaskMergedIntoTransaction::TRANSACTIONTYPE: + case ManiphestTaskStatusTransaction::TRANSACTIONTYPE: $tags[] = self::MAILTAG_STATUS; break; - case self::TYPE_OWNER: + case ManiphestTaskOwnerTransaction::TRANSACTIONTYPE: $tags[] = self::MAILTAG_OWNER; break; case PhabricatorTransactions::TYPE_SUBSCRIBERS: @@ -941,10 +179,10 @@ final class ManiphestTransaction break; } break; - case self::TYPE_PRIORITY: + case ManiphestTaskPriorityTransaction::TRANSACTIONTYPE: $tags[] = self::MAILTAG_PRIORITY; break; - case self::TYPE_UNBLOCK: + case ManiphestTaskUnblockTransaction::TRANSACTIONTYPE: $tags[] = self::MAILTAG_UNBLOCK; break; case PhabricatorTransactions::TYPE_COLUMNS: @@ -961,13 +199,12 @@ final class ManiphestTransaction } public function getNoEffectDescription() { - switch ($this->getTransactionType()) { - case self::TYPE_STATUS: + case ManiphestTaskStatusTransaction::TRANSACTIONTYPE: return pht('The task already has the selected status.'); - case self::TYPE_OWNER: + case ManiphestTaskOwnerTransaction::TRANSACTIONTYPE: return pht('The task already has the selected owner.'); - case self::TYPE_PRIORITY: + case ManiphestTaskPriorityTransaction::TRANSACTIONTYPE: return pht('The task already has the selected priority.'); } diff --git a/src/applications/maniphest/xaction/ManiphestTaskAttachTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskAttachTransaction.php new file mode 100644 index 0000000000..e794c03bdc --- /dev/null +++ b/src/applications/maniphest/xaction/ManiphestTaskAttachTransaction.php @@ -0,0 +1,91 @@ +getOldValue(); + $new = $this->getNewValue(); + + $old = nonempty($old, array()); + $new = nonempty($new, array()); + $new = array_keys(idx($new, 'FILE', array())); + $old = array_keys(idx($old, 'FILE', array())); + + $added = array_diff($new, $old); + $removed = array_diff($old, $new); + if ($added && !$removed) { + return pht( + '%s attached %s file(s): %s.', + $this->renderAuthor(), + phutil_count($added), + $this->renderHandleList($added)); + } else if ($removed && !$added) { + return pht( + '%s detached %s file(s): %s.', + $this->renderAuthor(), + phutil_count($removed), + $this->renderHandleList($removed)); + } else { + return pht( + '%s changed file(s), attached %s: %s; detached %s: %s.', + $this->renderAuthor(), + phutil_count($added), + $this->renderHandleList($added), + phutil_count($removed), + $this->renderHandleList($removed)); + } + + } + + public function getTitleForFeed() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $old = nonempty($old, array()); + $new = nonempty($new, array()); + $new = array_keys(idx($new, 'FILE', array())); + $old = array_keys(idx($old, 'FILE', array())); + + $added = array_diff($new, $old); + $removed = array_diff($old, $new); + if ($added && !$removed) { + return pht( + '%s attached %d file(s) of %s: %s', + $this->renderAuthor(), + $this->renderObject(), + count($added), + $this->renderHandleList($added)); + } else if ($removed && !$added) { + return pht( + '%s detached %d file(s) of %s: %s', + $this->renderAuthor(), + $this->renderObject(), + count($removed), + $this->renderHandleList($removed)); + } else { + return pht( + '%s changed file(s) for %s, attached %d: %s; detached %d: %s', + $this->renderAuthor(), + $this->renderObject(), + count($added), + $this->renderHandleList($added), + count($removed), + $this->renderHandleList($removed)); + } + } + + public function getIcon() { + return 'fa-thumb-tack'; + } + +} diff --git a/src/applications/maniphest/xaction/ManiphestTaskCoverImageTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskCoverImageTransaction.php new file mode 100644 index 0000000000..eb29c711f8 --- /dev/null +++ b/src/applications/maniphest/xaction/ManiphestTaskCoverImageTransaction.php @@ -0,0 +1,106 @@ +getCoverImageFilePHID(); + } + + public function applyInternalEffects($object, $value) { + $file_phid = $value; + + if ($file_phid) { + $file = id(new PhabricatorFileQuery()) + ->setViewer($this->getActor()) + ->withPHIDs(array($file_phid)) + ->executeOne(); + } else { + $file = null; + } + + if (!$file || !$file->isTransformableImage()) { + $object->setProperty('cover.filePHID', null); + $object->setProperty('cover.thumbnailPHID', null); + return; + } + + $xform_key = PhabricatorFileThumbnailTransform::TRANSFORM_WORKCARD; + $xform = PhabricatorFileTransform::getTransformByKey($xform_key) + ->executeTransform($file); + + $object->setProperty('cover.filePHID', $file->getPHID()); + $object->setProperty('cover.thumbnailPHID', $xform->getPHID()); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($old === null) { + return pht( + '%s set the cover image to %s.', + $this->renderAuthor(), + $this->renderHandle($new)); + } + + return pht( + '%s updated the cover image to %s.', + $this->renderAuthor(), + $this->renderHandle($new)); + + } + + public function getTitleForFeed() { + $old = $this->getOldValue(); + if ($old === null) { + return pht( + '%s added a cover image to %s.', + $this->renderAuthor(), + $this->renderObject()); + } + + return pht( + '%s updated the cover image for %s.', + $this->renderAuthor(), + $this->renderObject()); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + $viewer = $this->getActor(); + + foreach ($xactions as $xaction) { + $file_phid = $xaction->getNewValue(); + + $file = id(new PhabricatorFileQuery()) + ->setViewer($viewer) + ->withPHIDs(array($file_phid)) + ->executeOne(); + + if (!$file) { + $errors[] = $this->newInvalidError( + pht('"%s" is not a valid file PHID.', + $file_phid)); + } else { + if (!$file->isViewableImage()) { + $mime_type = $file->getMimeType(); + $errors[] = $this->newInvalidError( + pht('File mime type of "%s" is not a valid viewable image.', + $mime_type)); + } + } + + } + + return $errors; + } + + public function getIcon() { + return 'fa-image'; + } + + +} diff --git a/src/applications/maniphest/xaction/ManiphestTaskDescriptionTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskDescriptionTransaction.php new file mode 100644 index 0000000000..009327ed9b --- /dev/null +++ b/src/applications/maniphest/xaction/ManiphestTaskDescriptionTransaction.php @@ -0,0 +1,61 @@ +getDescription(); + } + + public function applyInternalEffects($object, $value) { + $object->setDescription($value); + } + + public function getActionName() { + return pht('Edited'); + } + + public function getTitle() { + return pht( + '%s updated the task description.', + $this->renderAuthor()); + } + + public function getTitleForFeed() { + return pht( + '%s updated the task description for %s.', + $this->renderAuthor(), + $this->renderObject()); + } + + public function hasChangeDetailView() { + return true; + } + + public function getMailDiffSectionHeader() { + return pht('CHANGES TO TASK DESCRIPTION'); + } + + public function newChangeDetailView() { + $viewer = $this->getViewer(); + + return id(new PhabricatorApplicationTransactionTextDiffDetailView()) + ->setViewer($viewer) + ->setOldText($this->getOldValue()) + ->setNewText($this->getNewValue()); + } + + public function newRemarkupChanges() { + $changes = array(); + + $changes[] = $this->newRemarkupChange() + ->setOldValue($this->getOldValue()) + ->setNewValue($this->getNewValue()); + + return $changes; + } + + +} diff --git a/src/applications/maniphest/xaction/ManiphestTaskEdgeTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskEdgeTransaction.php new file mode 100644 index 0000000000..18f9a1da1f --- /dev/null +++ b/src/applications/maniphest/xaction/ManiphestTaskEdgeTransaction.php @@ -0,0 +1,31 @@ +getMetadataValue('commitPHID'); + $edge_type = $this->getMetadataValue('edge:type'); + + if ($edge_type == ManiphestTaskHasCommitEdgeType::EDGECONST) { + if ($commit_phid) { + return true; + } + } + } + + public function getActionName() { + return pht('Attached'); + } + + public function getIcon() { + return 'fa-thumb-tack'; + } + +} diff --git a/src/applications/maniphest/xaction/ManiphestTaskMergedFromTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskMergedFromTransaction.php new file mode 100644 index 0000000000..a4a0440604 --- /dev/null +++ b/src/applications/maniphest/xaction/ManiphestTaskMergedFromTransaction.php @@ -0,0 +1,45 @@ +getNewValue(); + + return pht( + '%s merged %s task(s): %s.', + $this->renderAuthor(), + phutil_count($new), + $this->renderHandleList($new)); + } + + public function getTitleForFeed() { + $new = $this->getNewValue(); + + return pht( + '%s merged %s task(s) %s into %s.', + $this->renderAuthor(), + phutil_count($new), + $this->renderHandleList($new), + $this->renderObject()); + } + + public function getIcon() { + return 'fa-compress'; + } + + public function getColor() { + return 'orange'; + } + +} diff --git a/src/applications/maniphest/xaction/ManiphestTaskMergedIntoTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskMergedIntoTransaction.php new file mode 100644 index 0000000000..cd0cad6a39 --- /dev/null +++ b/src/applications/maniphest/xaction/ManiphestTaskMergedIntoTransaction.php @@ -0,0 +1,47 @@ +setStatus(ManiphestTaskStatus::getDuplicateStatus()); + } + + public function getActionName() { + return pht('Merged'); + } + + public function getTitle() { + $new = $this->getNewValue(); + + return pht( + '%s closed this task as a duplicate of %s.', + $this->renderAuthor(), + $this->renderHandle($new)); + } + + public function getTitleForFeed() { + $new = $this->getNewValue(); + + return pht( + '%s merged task %s into %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderHandle($new)); + } + + public function getIcon() { + return 'fa-check'; + } + + public function getColor() { + return 'indigo'; + } + +} diff --git a/src/applications/maniphest/xaction/ManiphestTaskOwnerTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskOwnerTransaction.php new file mode 100644 index 0000000000..d510fe8fbc --- /dev/null +++ b/src/applications/maniphest/xaction/ManiphestTaskOwnerTransaction.php @@ -0,0 +1,158 @@ +getOwnerPHID(), null); + } + + public function applyInternalEffects($object, $value) { + // Update the "ownerOrdering" column to contain the full name of the + // owner, if the task is assigned. + + $handle = null; + if ($value) { + $handle = id(new PhabricatorHandleQuery()) + ->setViewer($this->getActor()) + ->withPHIDs(array($value)) + ->executeOne(); + } + + if ($handle) { + $object->setOwnerOrdering($handle->getName()); + } else { + $object->setOwnerOrdering(null); + } + + $object->setOwnerPHID($value); + } + + public function getActionStrength() { + return 1.2; + } + + public function getActionName() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($this->getAuthorPHID() == $new) { + return pht('Claimed'); + } else if (!$new) { + return pht('Unassigned'); + } else if (!$old) { + return pht('Assigned'); + } else { + return pht('Reassigned'); + } + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($this->getAuthorPHID() == $new) { + return pht( + '%s claimed this task.', + $this->renderAuthor()); + } else if (!$new) { + return pht( + '%s removed %s as the assignee of this task.', + $this->renderAuthor(), + $this->renderHandle($old)); + } else if (!$old) { + return pht( + '%s assigned this task to %s.', + $this->renderAuthor(), + $this->renderHandle($new)); + } else { + return pht( + '%s reassigned this task from %s to %s.', + $this->renderAuthor(), + $this->renderHandle($old), + $this->renderHandle($new)); + } + } + + public function getTitleForFeed() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($this->getAuthorPHID() == $new) { + return pht( + '%s claimed %s.', + $this->renderAuthor(), + $this->renderObject()); + } else if (!$new) { + return pht( + '%s placed %s up for grabs.', + $this->renderAuthor(), + $this->renderObject()); + } else if (!$old) { + return pht( + '%s assigned %s to %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderHandle($new)); + } else { + return pht( + '%s reassigned %s from %s to %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderHandle($old), + $this->renderHandle($new)); + } + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + foreach ($xactions as $xaction) { + $old = $xaction->getOldValue(); + $new = $xaction->getNewValue(); + if (!strlen($new)) { + continue; + } + + if ($new === $old) { + continue; + } + + $assignee_list = id(new PhabricatorPeopleQuery()) + ->setViewer($this->getActor()) + ->withPHIDs(array($new)) + ->execute(); + + if (!$assignee_list) { + $errors[] = $this->newInvalidError( + pht('User "%s" is not a valid user.', + $new)); + } + } + return $errors; + } + + public function getIcon() { + return 'fa-user'; + } + + public function getColor() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($this->getAuthorPHID() == $new) { + return 'green'; + } else if (!$new) { + return 'black'; + } else if (!$old) { + return 'green'; + } else { + return 'green'; + } + + } + + +} diff --git a/src/applications/maniphest/xaction/ManiphestTaskParentTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskParentTransaction.php new file mode 100644 index 0000000000..d703adc8b5 --- /dev/null +++ b/src/applications/maniphest/xaction/ManiphestTaskParentTransaction.php @@ -0,0 +1,60 @@ +getPHID(); + + id(new PhabricatorEdgeEditor()) + ->addEdge($parent_phid, $parent_type, $task_phid) + ->save(); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + $with_effect = array(); + foreach ($xactions as $xaction) { + $task_phid = $xaction->getNewValue(); + if (!$task_phid) { + continue; + } + + $with_effect[] = $xaction; + + $task = id(new ManiphestTaskQuery()) + ->setViewer($this->getActor()) + ->withPHIDs(array($task_phid)) + ->executeOne(); + if (!$task) { + $errors[] = $this->newInvalidError( + pht( + 'Parent task identifier "%s" does not identify a visible '. + 'task.', + $task_phid)); + } + } + + if ($with_effect && !$this->isNewObject()) { + $errors[] = $this->newInvalidError( + pht( + 'You can only select a parent task when creating a '. + 'transaction for the first time.')); + } + + return $errors; + } +} diff --git a/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php new file mode 100644 index 0000000000..8a5276ae7e --- /dev/null +++ b/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php @@ -0,0 +1,76 @@ +getPoints(); + } + + public function applyInternalEffects($object, $value) { + if (!strlen($value)) { + $value = null; + } + if ($value !== null) { + $value = (double)$value; + } + $object->setPoints($value); + } + + public function shouldHideForFeed() { + return true; + } + + public function shouldHide() { + if (!ManiphestTaskPoints::getIsEnabled()) { + return true; + } + return false; + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($old === null) { + return pht( + '%s set the point value for this task to %s.', + $this->renderAuthor(), + $this->renderNewValue()); + } else if ($new === null) { + return pht( + '%s removed the point value for this task.', + $this->renderAuthor()); + } else { + return pht( + '%s changed the point value for this task from %s to %s.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + foreach ($xactions as $xaction) { + $new = $xaction->getNewValue(); + if (strlen($new) && !is_numeric($new)) { + $errors[] = $this->newInvalidError( + pht('Points value must be numeric or empty.')); + continue; + } + + if ((double)$new < 0) { + $errors[] = $this->newInvalidError( + pht('Points value must be nonnegative.')); + continue; + } + } + + return $errors; + } + +} diff --git a/src/applications/maniphest/xaction/ManiphestTaskPriorityTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskPriorityTransaction.php new file mode 100644 index 0000000000..f7d58911ba --- /dev/null +++ b/src/applications/maniphest/xaction/ManiphestTaskPriorityTransaction.php @@ -0,0 +1,119 @@ +isNewObject()) { + return null; + } + return $object->getPriority(); + } + + public function applyInternalEffects($object, $value) { + $object->setPriority($value); + } + + public function getActionStrength() { + return 1.1; + } + + public function getActionName() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($old == ManiphestTaskPriority::getDefaultPriority()) { + return pht('Triaged'); + } else if ($old > $new) { + return pht('Lowered Priority'); + } else { + return pht('Raised Priority'); + } + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $old_name = ManiphestTaskPriority::getTaskPriorityName($old); + $new_name = ManiphestTaskPriority::getTaskPriorityName($new); + + if ($old == ManiphestTaskPriority::getDefaultPriority()) { + return pht( + '%s triaged this task as %s priority.', + $this->renderAuthor(), + $this->renderValue($new_name)); + } else if ($old > $new) { + return pht( + '%s lowered the priority of this task from %s to %s.', + $this->renderAuthor(), + $this->renderValue($old_name), + $this->renderValue($new_name)); + } else { + return pht( + '%s raised the priority of this task from %s to %s.', + $this->renderAuthor(), + $this->renderValue($old_name), + $this->renderValue($new_name)); + } + } + + public function getTitleForFeed() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $old_name = ManiphestTaskPriority::getTaskPriorityName($old); + $new_name = ManiphestTaskPriority::getTaskPriorityName($new); + + if ($old == ManiphestTaskPriority::getDefaultPriority()) { + return pht( + '%s triaged %s as %s priority.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderValue($new_name)); + } else if ($old > $new) { + return pht( + '%s lowered the priority of %s from %s to %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderValue($old_name), + $this->renderValue($new_name)); + } else { + return pht( + '%s raised the priority of %s from %s to %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderValue($old_name), + $this->renderValue($new_name)); + } + } + + public function getIcon() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($old == ManiphestTaskPriority::getDefaultPriority()) { + return 'fa-arrow-right'; + } else if ($old > $new) { + return 'fa-arrow-down'; + } else { + return 'fa-arrow-up'; + } + } + + public function getColor() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($old == ManiphestTaskPriority::getDefaultPriority()) { + return 'green'; + } else if ($old > $new) { + return 'grey'; + } else { + return 'yellow'; + } + } + +} diff --git a/src/applications/maniphest/xaction/ManiphestTaskStatusTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskStatusTransaction.php new file mode 100644 index 0000000000..5e1cd44611 --- /dev/null +++ b/src/applications/maniphest/xaction/ManiphestTaskStatusTransaction.php @@ -0,0 +1,232 @@ +isNewObject()) { + return null; + } + return $object->getStatus(); + } + + public function applyInternalEffects($object, $value) { + $object->setStatus($value); + } + + public function shouldHide() { + if ($this->getOldValue() === null) { + return true; + } else { + return false; + } + } + + public function getActionStrength() { + return 1.3; + } + + public function getActionName() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $action = ManiphestTaskStatus::getStatusActionName($new); + if ($action) { + return $action; + } + + $old_closed = ManiphestTaskStatus::isClosedStatus($old); + $new_closed = ManiphestTaskStatus::isClosedStatus($new); + + if ($new_closed && !$old_closed) { + return pht('Closed'); + } else if (!$new_closed && $old_closed) { + return pht('Reopened'); + } else { + return pht('Changed Status'); + } + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $old_closed = ManiphestTaskStatus::isClosedStatus($old); + $new_closed = ManiphestTaskStatus::isClosedStatus($new); + + $old_name = ManiphestTaskStatus::getTaskStatusName($old); + $new_name = ManiphestTaskStatus::getTaskStatusName($new); + + $commit_phid = $this->getMetadataValue('commitPHID'); + + if ($new_closed && !$old_closed) { + if ($new == ManiphestTaskStatus::getDuplicateStatus()) { + if ($commit_phid) { + return pht( + '%s closed this task as a duplicate by committing %s.', + $this->renderAuthor(), + $this->renderHandle($commit_phid)); + } else { + return pht( + '%s closed this task as a duplicate.', + $this->renderAuthor()); + } + } else { + if ($commit_phid) { + return pht( + '%s closed this task as %s by committing %s.', + $this->renderAuthor(), + $this->renderValue($new_name), + $this->renderHandle($commit_phid)); + } else { + return pht( + '%s closed this task as %s.', + $this->renderAuthor(), + $this->renderValue($new_name)); + } + } + } else if (!$new_closed && $old_closed) { + if ($commit_phid) { + return pht( + '%s reopened this task as %s by committing %s.', + $this->renderAuthor(), + $this->renderValue($new_name), + $this->renderHandle($commit_phid)); + } else { + return pht( + '%s reopened this task as %s.', + $this->renderAuthor(), + $this->renderValue($new_name)); + } + } else { + if ($commit_phid) { + return pht( + '%s changed the task status from %s to %s by committing %s.', + $this->renderAuthor(), + $this->renderValue($old_name), + $this->renderValue($new_name), + $this->renderHandle($commit_phid)); + } else { + return pht( + '%s changed the task status from %s to %s.', + $this->renderAuthor(), + $this->renderValue($old_name), + $this->renderValue($new_name)); + } + } + + } + + public function getTitleForFeed() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $old_closed = ManiphestTaskStatus::isClosedStatus($old); + $new_closed = ManiphestTaskStatus::isClosedStatus($new); + + $old_name = ManiphestTaskStatus::getTaskStatusName($old); + $new_name = ManiphestTaskStatus::getTaskStatusName($new); + + $commit_phid = $this->getMetadataValue('commitPHID'); + + if ($new_closed && !$old_closed) { + if ($new == ManiphestTaskStatus::getDuplicateStatus()) { + if ($commit_phid) { + return pht( + '%s closed %s as a duplicate by committing %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderHandle($commit_phid)); + } else { + return pht( + '%s closed %s as a duplicate.', + $this->renderAuthor(), + $this->renderObject()); + } + } else { + if ($commit_phid) { + return pht( + '%s closed %s as %s by committing %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderValue($new_name), + $this->renderHandle($commit_phid)); + } else { + return pht( + '%s closed %s as %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderValue($new_name)); + } + } + } else if (!$new_closed && $old_closed) { + if ($commit_phid) { + return pht( + '%s reopened %s as %s by committing %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderValue($new_name), + $this->renderHandle($commit_phid)); + } else { + return pht( + '%s reopened %s as "%s".', + $this->renderAuthor(), + $this->renderObject(), + $new_name); + } + } else { + if ($commit_phid) { + return pht( + '%s changed the status of %s from %s to %s by committing %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderValue($old_name), + $this->renderValue($new_name), + $this->renderHandle($commit_phid)); + } else { + return pht( + '%s changed the status of %s from %s to %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderValue($old_name), + $this->renderValue($new_name)); + } + } + } + + public function getIcon() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $action = ManiphestTaskStatus::getStatusIcon($new); + if ($action !== null) { + return $action; + } + + if (ManiphestTaskStatus::isClosedStatus($new)) { + return 'fa-check'; + } else { + return 'fa-pencil'; + } + } + + public function getColor() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $color = ManiphestTaskStatus::getStatusColor($new); + if ($color !== null) { + return $color; + } + + if (ManiphestTaskStatus::isOpenStatus($new)) { + return 'green'; + } else { + return 'indigo'; + } + + } + +} diff --git a/src/applications/maniphest/xaction/ManiphestTaskSubpriorityTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskSubpriorityTransaction.php new file mode 100644 index 0000000000..49d227b7f1 --- /dev/null +++ b/src/applications/maniphest/xaction/ManiphestTaskSubpriorityTransaction.php @@ -0,0 +1,21 @@ +getSubpriority(); + } + + public function applyInternalEffects($object, $value) { + $object->setSubpriority($value); + } + + public function shouldHide() { + return true; + } + + +} diff --git a/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php new file mode 100644 index 0000000000..dcaf959a57 --- /dev/null +++ b/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php @@ -0,0 +1,74 @@ +getTitle(); + } + + public function applyInternalEffects($object, $value) { + $object->setTitle($value); + } + + public function getActionStrength() { + return 1.4; + } + + public function getActionName() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + if ($old === null) { + return pht('Created'); + } + + return pht('Retitled'); + } + + public function getTitle() { + $old = $this->getOldValue(); + if ($old === null) { + return pht( + '%s created this task.', + $this->renderAuthor()); + } + + return pht( + '%s changed the title from %s to %s.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + + } + + public function getTitleForFeed() { + $old = $this->getOldValue(); + if ($old === null) { + return pht( + '%s created %s.', + $this->renderAuthor(), + $this->renderObject()); + } + + return pht( + '%s changed %s title from %s to %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderOldValue(), + $this->renderNewValue()); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + if ($this->isEmptyTextTransaction($object->getTitle(), $xactions)) { + $errors[] = $this->newRequiredError( + pht('Tasks must have a title.')); + } + + return $errors; + } + +} diff --git a/src/applications/maniphest/xaction/ManiphestTaskTransactionType.php b/src/applications/maniphest/xaction/ManiphestTaskTransactionType.php new file mode 100644 index 0000000000..699ef11631 --- /dev/null +++ b/src/applications/maniphest/xaction/ManiphestTaskTransactionType.php @@ -0,0 +1,16 @@ +getObject(); + $map = $object->newEditEngineSubtypeMap(); + if (!isset($map[$value])) { + return $value; + } + + return $map[$value]->getName(); + } + +} diff --git a/src/applications/maniphest/xaction/ManiphestTaskUnblockTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskUnblockTransaction.php new file mode 100644 index 0000000000..905554a3a3 --- /dev/null +++ b/src/applications/maniphest/xaction/ManiphestTaskUnblockTransaction.php @@ -0,0 +1,125 @@ +getMetadataValue('blocker.new'); + if ($is_new) { + return true; + } + } + + public function getActionName() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $old_status = head($old); + $new_status = head($new); + + $old_closed = ManiphestTaskStatus::isClosedStatus($old_status); + $new_closed = ManiphestTaskStatus::isClosedStatus($new_status); + + if ($old_closed && !$new_closed) { + return pht('Block'); + } else if (!$old_closed && $new_closed) { + return pht('Unblock'); + } else { + return pht('Blocker'); + } + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $blocker_phid = key($new); + $old_status = head($old); + $new_status = head($new); + + $old_closed = ManiphestTaskStatus::isClosedStatus($old_status); + $new_closed = ManiphestTaskStatus::isClosedStatus($new_status); + + $old_name = ManiphestTaskStatus::getTaskStatusName($old_status); + $new_name = ManiphestTaskStatus::getTaskStatusName($new_status); + + if ($this->getMetadataValue('blocker.new')) { + return pht( + '%s created subtask %s.', + $this->renderAuthor(), + $this->renderHandle($blocker_phid)); + } else if ($old_closed && !$new_closed) { + return pht( + '%s reopened subtask %s as %s.', + $this->renderAuthor(), + $this->renderHandle($blocker_phid), + $this->renderValue($new_name)); + } else if (!$old_closed && $new_closed) { + return pht( + '%s closed subtask %s as %s.', + $this->renderAuthor(), + $this->renderHandle($blocker_phid), + $this->renderValue($new_name)); + } else { + return pht( + '%s changed the status of subtask %s from %s to %s.', + $this->renderAuthor(), + $this->renderHandle($blocker_phid), + $this->renderValue($old_name), + $this->renderValue($new_name)); + } + } + + public function getTitleForFeed() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + $blocker_phid = key($new); + $old_status = head($old); + $new_status = head($new); + + $old_closed = ManiphestTaskStatus::isClosedStatus($old_status); + $new_closed = ManiphestTaskStatus::isClosedStatus($new_status); + + $old_name = ManiphestTaskStatus::getTaskStatusName($old_status); + $new_name = ManiphestTaskStatus::getTaskStatusName($new_status); + + if ($old_closed && !$new_closed) { + return pht( + '%s reopened %s, a subtask of %s, as %s.', + $this->renderAuthor(), + $this->renderHandle($blocker_phid), + $this->renderObject(), + $this->renderValue($new_name)); + } else if (!$old_closed && $new_closed) { + return pht( + '%s closed %s, a subtask of %s, as %s.', + $this->renderAuthor(), + $this->renderHandle($blocker_phid), + $this->renderObject(), + $this->renderValue($new_name)); + } else { + return pht( + '%s changed the status of %s, a subtask of %s, '. + 'from %s to %s.', + $this->renderAuthor(), + $this->renderHandle($blocker_phid), + $this->renderObject(), + $this->renderValue($old_name), + $this->renderValue($new_name)); + } + } + + public function getIcon() { + return 'fa-shield'; + } + + +} diff --git a/src/applications/nuance/item/NuanceGitHubEventItemType.php b/src/applications/nuance/item/NuanceGitHubEventItemType.php index 3fef8b3da7..b7b90690b7 100644 --- a/src/applications/nuance/item/NuanceGitHubEventItemType.php +++ b/src/applications/nuance/item/NuanceGitHubEventItemType.php @@ -390,12 +390,14 @@ final class NuanceGitHubEventItemType $state = $xobj->getProperty('task.state'); $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_TITLE) + ->setTransactionType( + ManiphestTaskTitleTransaction::TRANSACTIONTYPE) ->setNewValue($title) ->setDateCreated($created); $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_DESCRIPTION) + ->setTransactionType( + ManiphestTaskDescriptionTransaction::TRANSACTIONTYPE) ->setNewValue($description) ->setDateCreated($created); diff --git a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php index 71161b031e..3943a7c0c3 100644 --- a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php +++ b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php @@ -1337,7 +1337,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $xactions = array(); $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_TITLE) + ->setTransactionType(ManiphestTaskTitleTransaction::TRANSACTIONTYPE) ->setNewValue($name); if ($projects) { diff --git a/src/applications/project/controller/PhabricatorProjectCoverController.php b/src/applications/project/controller/PhabricatorProjectCoverController.php index 22f787e56b..98f6c1c995 100644 --- a/src/applications/project/controller/PhabricatorProjectCoverController.php +++ b/src/applications/project/controller/PhabricatorProjectCoverController.php @@ -36,7 +36,7 @@ final class PhabricatorProjectCoverController $xactions = array(); $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_COVER_IMAGE) + ->setTransactionType(ManiphestTaskCoverImageTransaction::TRANSACTIONTYPE) ->setNewValue($file->getPHID()); $editor = id(new ManiphestTransactionEditor()) diff --git a/src/applications/project/controller/PhabricatorProjectMoveController.php b/src/applications/project/controller/PhabricatorProjectMoveController.php index c92c843204..68915664cb 100644 --- a/src/applications/project/controller/PhabricatorProjectMoveController.php +++ b/src/applications/project/controller/PhabricatorProjectMoveController.php @@ -153,10 +153,11 @@ final class PhabricatorProjectMoveController $xactions = array(); if ($pri !== null) { $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_PRIORITY) + ->setTransactionType(ManiphestTaskPriorityTransaction::TRANSACTIONTYPE) ->setNewValue($pri); $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_SUBPRIORITY) + ->setTransactionType( + ManiphestTaskSubpriorityTransaction::TRANSACTIONTYPE) ->setNewValue($sub); } diff --git a/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php b/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php index 767cfe5290..f7e3a735a4 100644 --- a/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php +++ b/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php @@ -367,7 +367,8 @@ abstract class PhabricatorRepositoryCommitMessageParserWorker if ($status) { if ($task->getStatus() != $status) { $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_STATUS) + ->setTransactionType( + ManiphestTaskStatusTransaction::TRANSACTIONTYPE) ->setMetadataValue('commitPHID', $commit->getPHID()) ->setNewValue($status); From 904480dc3cfbd78615a06ed87202683d21f5cb33 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 15 May 2017 11:16:34 -0700 Subject: [PATCH 07/81] Generate newValue for ManiphestTaskPointTransaction Summary: I think this is the correct fix, sets a consistent value for transactions, old and new, for Maniphest point values. Test Plan: Edit title, see no point feed story, set points, see point story, set points to same value, see no story, remove points, see remove point story. {F4958233} Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17885 --- .../ManiphestTaskPointsTransaction.php | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php index 8a5276ae7e..7f324d1bca 100644 --- a/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php +++ b/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php @@ -6,16 +6,14 @@ final class ManiphestTaskPointsTransaction const TRANSACTIONTYPE = 'points'; public function generateOldValue($object) { - return $object->getPoints(); + return $this->getValueForPoints($object->getPoints()); + } + + public function generateNewValue($object, $value) { + return $this->getValueForPoints($value); } public function applyInternalEffects($object, $value) { - if (!strlen($value)) { - $value = null; - } - if ($value !== null) { - $value = (double)$value; - } $object->setPoints($value); } @@ -73,4 +71,14 @@ final class ManiphestTaskPointsTransaction return $errors; } + private function getValueForPoints($value) { + if (!strlen($value)) { + $value = null; + } + if ($value !== null) { + $value = (double)$value; + } + return $value; + } + } From 78544334cd698af62650b7d3c6ee2e40330351b7 Mon Sep 17 00:00:00 2001 From: Austin McKinley Date: Mon, 15 May 2017 13:22:05 -0700 Subject: [PATCH 08/81] Fix breakage of Pholio Summary: Removal of `PholioMockEditor::applyCustomInternalTransaction()` in D17868 broke creation of new mocks in Pholio. Puts the empty method back until we finish migrating Pholio to modular transactions. Test Plan: Created some mocks, observed lack of unhandled exception. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: epriestley Differential Revision: https://secure.phabricator.com/D17889 --- src/applications/pholio/editor/PholioMockEditor.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/applications/pholio/editor/PholioMockEditor.php b/src/applications/pholio/editor/PholioMockEditor.php index 6f5bf1df2b..322eaa7267 100644 --- a/src/applications/pholio/editor/PholioMockEditor.php +++ b/src/applications/pholio/editor/PholioMockEditor.php @@ -184,6 +184,12 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor { return null; } + protected function applyCustomInternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + return; + } + protected function applyCustomExternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { From f600bc0811fa936a13cd14feaf9a20d9d01527a4 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 15 May 2017 15:44:50 -0700 Subject: [PATCH 09/81] Clean up watchers and members project page Summary: Various little fixes, mostly moves information from the "Details" section either into the curtain or into the specific watchers or members list based on user viewership. I think this page is both cleaner and more informative. Test Plan: Lock, Unlock, Watch, Join, various projects with multiple users. {F4959101} Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17891 --- resources/celerity/map.php | 6 +- ...habricatorProjectMembersViewController.php | 148 +++++------------- .../view/PhabricatorProjectMemberListView.php | 33 +++- .../view/PhabricatorProjectUserListView.php | 8 + .../PhabricatorProjectWatcherListView.php | 15 +- src/view/form/PHUIInfoView.php | 12 +- webroot/rsrc/css/phui/phui-info-view.css | 8 + 7 files changed, 115 insertions(+), 115 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 930beaa0eb..8430bf43b9 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ return array( 'names' => array( 'conpherence.pkg.css' => 'ff161f2d', 'conpherence.pkg.js' => 'b5b51108', - 'core.pkg.css' => '24ffbe93', + 'core.pkg.css' => 'd1bf3405', 'core.pkg.js' => '2ff7879f', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '90b30783', @@ -160,7 +160,7 @@ return array( 'rsrc/css/phui/phui-icon.css' => '12b387a1', 'rsrc/css/phui/phui-image-mask.css' => 'a8498f9c', 'rsrc/css/phui/phui-info-panel.css' => '27ea50a1', - 'rsrc/css/phui/phui-info-view.css' => 'ec92802a', + 'rsrc/css/phui/phui-info-view.css' => '6e217679', 'rsrc/css/phui/phui-invisible-character-view.css' => '6993d9f0', 'rsrc/css/phui/phui-lightbox.css' => '0a035e40', 'rsrc/css/phui/phui-list.css' => '12eb8ce6', @@ -867,7 +867,7 @@ return array( 'phui-icon-view-css' => '12b387a1', 'phui-image-mask-css' => 'a8498f9c', 'phui-info-panel-css' => '27ea50a1', - 'phui-info-view-css' => 'ec92802a', + 'phui-info-view-css' => '6e217679', 'phui-inline-comment-view-css' => 'be663c95', 'phui-invisible-character-view-css' => '6993d9f0', 'phui-lightbox-css' => '0a035e40', diff --git a/src/applications/project/controller/PhabricatorProjectMembersViewController.php b/src/applications/project/controller/PhabricatorProjectMembersViewController.php index 959927c9f3..cd80d0c724 100644 --- a/src/applications/project/controller/PhabricatorProjectMembersViewController.php +++ b/src/applications/project/controller/PhabricatorProjectMembersViewController.php @@ -20,15 +20,8 @@ final class PhabricatorProjectMembersViewController $this->setProject($project); $title = pht('Members and Watchers'); - - $properties = $this->buildProperties($project); $curtain = $this->buildCurtainView($project); - $object_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Details')) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->addPropertyList($properties); - $member_list = id(new PhabricatorProjectMemberListView()) ->setUser($viewer) ->setProject($project) @@ -52,16 +45,18 @@ final class PhabricatorProjectMembersViewController ->setHeader($title) ->setHeaderIcon('fa-group'); + require_celerity_resource('project-view-css'); + $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setCurtain($curtain) + ->addClass('project-view-home') + ->addClass('project-view-people-home') ->setMainColumn(array( - $object_box, $member_list, $watcher_list, )); - return $this->newPage() ->setNavigation($nav) ->setCrumbs($crumbs) @@ -69,105 +64,6 @@ final class PhabricatorProjectMembersViewController ->appendChild($view); } - private function buildProperties(PhabricatorProject $project) { - $viewer = $this->getViewer(); - - $view = id(new PHUIPropertyListView()) - ->setUser($viewer) - ->setObject($project); - - if ($project->isMilestone()) { - $icon_key = PhabricatorProjectIconSet::getMilestoneIconKey(); - $icon = PhabricatorProjectIconSet::getIconIcon($icon_key); - $target = PhabricatorProjectIconSet::getIconName($icon_key); - $note = pht( - 'Members of the parent project are members of this project.'); - $show_join = false; - } else if ($project->getHasSubprojects()) { - $icon = 'fa-sitemap'; - $target = pht('Parent Project'); - $note = pht( - 'Members of all subprojects are members of this project.'); - $show_join = false; - } else if ($project->getIsMembershipLocked()) { - $icon = 'fa-lock'; - $target = pht('Locked Project'); - $note = pht( - 'Users with access may join this project, but may not leave.'); - $show_join = true; - } else { - $icon = 'fa-briefcase'; - $target = pht('Normal Project'); - $note = pht('Users with access may join and leave this project.'); - $show_join = true; - } - - $item = id(new PHUIStatusItemView()) - ->setIcon($icon) - ->setTarget(phutil_tag('strong', array(), $target)) - ->setNote($note); - - $status = id(new PHUIStatusListView()) - ->addItem($item); - - $view->addProperty(pht('Membership'), $status); - - if ($show_join) { - $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions( - $viewer, - $project); - - $view->addProperty( - pht('Joinable By'), - $descriptions[PhabricatorPolicyCapability::CAN_JOIN]); - } - - $viewer_phid = $viewer->getPHID(); - - if ($project->isUserWatcher($viewer_phid)) { - $watch_item = id(new PHUIStatusItemView()) - ->setIcon('fa-eye green') - ->setTarget(phutil_tag('strong', array(), pht('Watching'))) - ->setNote( - pht( - 'You will receive mail about changes made to any related '. - 'object.')); - - $watch_status = id(new PHUIStatusListView()) - ->addItem($watch_item); - - $view->addProperty(pht('Watching'), $watch_status); - } - - if ($project->isUserMember($viewer_phid)) { - $is_silenced = $this->isProjectSilenced($project); - if ($is_silenced) { - $mail_icon = 'fa-envelope-o grey'; - $mail_target = pht('Disabled'); - $mail_note = pht( - 'When mail is sent to project members, you will not receive '. - 'a copy.'); - } else { - $mail_icon = 'fa-envelope-o green'; - $mail_target = pht('Enabled'); - $mail_note = pht( - 'You will receive mail that is sent to project members.'); - } - - $mail_item = id(new PHUIStatusItemView()) - ->setIcon($mail_icon) - ->setTarget(phutil_tag('strong', array(), $mail_target)) - ->setNote($mail_note); - - $mail_status = id(new PHUIStatusListView()) - ->addItem($mail_item); - - $view->addProperty(pht('Mail to Members'), $mail_status); - } - - return $view; - } - private function buildCurtainView(PhabricatorProject $project) { $viewer = $this->getViewer(); $id = $project->getID(); @@ -272,6 +168,42 @@ final class PhabricatorProjectMembersViewController ->setDisabled(!$can_lock) ->setWorkflow(true)); + if ($project->isMilestone()) { + $icon_key = PhabricatorProjectIconSet::getMilestoneIconKey(); + $header = PhabricatorProjectIconSet::getIconName($icon_key); + $note = pht( + 'Members of the parent project are members of this project.'); + $show_join = false; + } else if ($project->getHasSubprojects()) { + $header = pht('Parent Project'); + $note = pht( + 'Members of all subprojects are members of this project.'); + $show_join = false; + } else if ($project->getIsMembershipLocked()) { + $header = pht('Locked Project'); + $note = pht( + 'Users with access may join this project, but may not leave.'); + $show_join = true; + } else { + $header = pht('Normal Project'); + $note = pht('Users with access may join and leave this project.'); + $show_join = true; + } + + $curtain->newPanel() + ->setHeaderText($header) + ->appendChild($note); + + if ($show_join) { + $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions( + $viewer, + $project); + + $curtain->newPanel() + ->setHeaderText(pht('Joinable By')) + ->appendChild($descriptions[PhabricatorPolicyCapability::CAN_JOIN]); + } + return $curtain; } diff --git a/src/applications/project/view/PhabricatorProjectMemberListView.php b/src/applications/project/view/PhabricatorProjectMemberListView.php index 12b7cc7a76..cf8a3a1465 100644 --- a/src/applications/project/view/PhabricatorProjectMemberListView.php +++ b/src/applications/project/view/PhabricatorProjectMemberListView.php @@ -4,7 +4,7 @@ final class PhabricatorProjectMemberListView extends PhabricatorProjectUserListView { protected function canEditList() { - $viewer = $this->getUser(); + $viewer = $this->getViewer(); $project = $this->getProject(); if (!$project->supportsEditMembers()) { @@ -31,4 +31,35 @@ final class PhabricatorProjectMemberListView return pht('Members'); } + protected function getMembershipNote() { + $viewer = $this->getViewer(); + $viewer_phid = $viewer->getPHID(); + $project = $this->getProject(); + + if (!$viewer_phid) { + return null; + } + + $note = null; + if ($project->isUserMember($viewer_phid)) { + $edge_type = PhabricatorProjectSilencedEdgeType::EDGECONST; + $silenced = PhabricatorEdgeQuery::loadDestinationPHIDs( + $project->getPHID(), + $edge_type); + $silenced = array_fuse($silenced); + $is_silenced = isset($silenced[$viewer_phid]); + if ($is_silenced) { + $note = pht( + 'You have disabled mail. When mail is sent to project members, '. + 'you will not receive a copy.'); + } else { + $note = pht( + 'You are a member and you will receive mail that is sent to all '. + 'project members.'); + } + } + + return $note; + } + } diff --git a/src/applications/project/view/PhabricatorProjectUserListView.php b/src/applications/project/view/PhabricatorProjectUserListView.php index d590cbb559..0c0e2c1d2b 100644 --- a/src/applications/project/view/PhabricatorProjectUserListView.php +++ b/src/applications/project/view/PhabricatorProjectUserListView.php @@ -43,6 +43,7 @@ abstract class PhabricatorProjectUserListView extends AphrontView { abstract protected function getNoDataString(); abstract protected function getRemoveURI($phid); abstract protected function getHeaderText(); + abstract protected function getMembershipNote(); public function render() { $viewer = $this->getViewer(); @@ -135,6 +136,13 @@ abstract class PhabricatorProjectUserListView extends AphrontView { ->setHeader($header) ->setObjectList($list); + if ($this->getMembershipNote()) { + $info = id(new PHUIInfoView()) + ->setSeverity(PHUIInfoView::SEVERITY_PLAIN) + ->appendChild($this->getMembershipNote()); + $box->setInfoView($info); + } + if ($this->background) { $box->setBackground($this->background); } diff --git a/src/applications/project/view/PhabricatorProjectWatcherListView.php b/src/applications/project/view/PhabricatorProjectWatcherListView.php index 7aa9638afc..2d16cc7ec2 100644 --- a/src/applications/project/view/PhabricatorProjectWatcherListView.php +++ b/src/applications/project/view/PhabricatorProjectWatcherListView.php @@ -4,7 +4,7 @@ final class PhabricatorProjectWatcherListView extends PhabricatorProjectUserListView { protected function canEditList() { - $viewer = $this->getUser(); + $viewer = $this->getViewer(); $project = $this->getProject(); return PhabricatorPolicyFilter::hasCapability( @@ -27,4 +27,17 @@ final class PhabricatorProjectWatcherListView return pht('Watchers'); } + protected function getMembershipNote() { + $viewer = $this->getViewer(); + $viewer_phid = $viewer->getPHID(); + $project = $this->getProject(); + + $note = null; + if ($project->isUserWatcher($viewer_phid)) { + $note = pht('You are watching this project and will receive mail about '. + 'changes made to any related object.'); + } + return $note; + } + } diff --git a/src/view/form/PHUIInfoView.php b/src/view/form/PHUIInfoView.php index ca01ff294b..8ba74056b8 100644 --- a/src/view/form/PHUIInfoView.php +++ b/src/view/form/PHUIInfoView.php @@ -7,6 +7,7 @@ final class PHUIInfoView extends AphrontTagView { const SEVERITY_NOTICE = 'notice'; const SEVERITY_NODATA = 'nodata'; const SEVERITY_SUCCESS = 'success'; + const SEVERITY_PLAIN = 'plain'; private $title; private $errors; @@ -52,8 +53,14 @@ final class PHUIInfoView extends AphrontTagView { return $this; } - public function setIcon(PHUIIconView $icon) { - $this->icon = $icon; + public function setIcon($icon) { + if ($icon instanceof PHUIIconView) { + $this->icon = $icon; + } else { + $icon = id(new PHUIIconView()) + ->setIcon($icon); + } + return $this; } @@ -72,6 +79,7 @@ final class PHUIInfoView extends AphrontTagView { case self::SEVERITY_NOTICE: $icon = 'fa-info-circle'; break; + case self::SEVERITY_PLAIN: case self::SEVERITY_NODATA: return null; break; diff --git a/webroot/rsrc/css/phui/phui-info-view.css b/webroot/rsrc/css/phui/phui-info-view.css index 1642822b77..105adf9ed4 100644 --- a/webroot/rsrc/css/phui/phui-info-view.css +++ b/webroot/rsrc/css/phui/phui-info-view.css @@ -11,6 +11,14 @@ border-radius: 3px; } +div.phui-info-view.phui-info-severity-plain { + background: {$lightgreybackground}; + color: {$bluetext}; + border: none; + padding: 8px 12px; + margin-bottom: 4px !important; +} + .phui-info-view.phui-info-view-flush { margin: 0 0 20px 0; } From 545d6347dd8f88db0652e0ea47b2ce9740d6a152 Mon Sep 17 00:00:00 2001 From: Austin McKinley Date: Mon, 15 May 2017 16:00:50 -0700 Subject: [PATCH 10/81] Convert remaining Pholio image transactions to modular transaction framework Summary: Also cleans up now-dead code relating to old transactions Test Plan: Created lots of mocks, replaced their images, added/removed images, changed the sequence, verified expected DB xaction rows, Mock updates, and correct rendering of timeline. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: epriestley Differential Revision: https://secure.phabricator.com/D17892 --- src/__phutil_library_map__.php | 6 + .../controller/PholioMockEditController.php | 8 +- .../pholio/editor/PholioMockEditor.php | 181 +----------------- .../pholio/storage/PholioTransaction.php | 131 +------------ .../xaction/PholioImageFileTransaction.php | 120 ++++++++++++ .../xaction/PholioImageReplaceTransaction.php | 68 +++++++ .../PholioImageSequenceTransaction.php | 60 ++++++ 7 files changed, 267 insertions(+), 307 deletions(-) create mode 100644 src/applications/pholio/xaction/PholioImageFileTransaction.php create mode 100644 src/applications/pholio/xaction/PholioImageReplaceTransaction.php create mode 100644 src/applications/pholio/xaction/PholioImageSequenceTransaction.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 024a8c93b2..f08a02579e 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -4376,9 +4376,12 @@ phutil_register_library_map(array( 'PholioDefaultViewCapability' => 'applications/pholio/capability/PholioDefaultViewCapability.php', 'PholioImage' => 'applications/pholio/storage/PholioImage.php', 'PholioImageDescriptionTransaction' => 'applications/pholio/xaction/PholioImageDescriptionTransaction.php', + 'PholioImageFileTransaction' => 'applications/pholio/xaction/PholioImageFileTransaction.php', 'PholioImageNameTransaction' => 'applications/pholio/xaction/PholioImageNameTransaction.php', 'PholioImagePHIDType' => 'applications/pholio/phid/PholioImagePHIDType.php', 'PholioImageQuery' => 'applications/pholio/query/PholioImageQuery.php', + 'PholioImageReplaceTransaction' => 'applications/pholio/xaction/PholioImageReplaceTransaction.php', + 'PholioImageSequenceTransaction' => 'applications/pholio/xaction/PholioImageSequenceTransaction.php', 'PholioImageTransactionType' => 'applications/pholio/xaction/PholioImageTransactionType.php', 'PholioImageUploadController' => 'applications/pholio/controller/PholioImageUploadController.php', 'PholioInlineController' => 'applications/pholio/controller/PholioInlineController.php', @@ -9938,9 +9941,12 @@ phutil_register_library_map(array( 'PhabricatorPolicyInterface', ), 'PholioImageDescriptionTransaction' => 'PholioImageTransactionType', + 'PholioImageFileTransaction' => 'PholioImageTransactionType', 'PholioImageNameTransaction' => 'PholioImageTransactionType', 'PholioImagePHIDType' => 'PhabricatorPHIDType', 'PholioImageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PholioImageReplaceTransaction' => 'PholioImageTransactionType', + 'PholioImageSequenceTransaction' => 'PholioImageTransactionType', 'PholioImageTransactionType' => 'PholioTransactionType', 'PholioImageUploadController' => 'PholioController', 'PholioInlineController' => 'PholioController', diff --git a/src/applications/pholio/controller/PholioMockEditController.php b/src/applications/pholio/controller/PholioMockEditController.php index e97af5da27..2d477c70aa 100644 --- a/src/applications/pholio/controller/PholioMockEditController.php +++ b/src/applications/pholio/controller/PholioMockEditController.php @@ -151,7 +151,7 @@ final class PholioMockEditController extends PholioController { ->setSequence($sequence); $xactions[] = id(new PholioTransaction()) ->setTransactionType( - PholioTransaction::TYPE_IMAGE_REPLACE) + PholioImageReplaceTransaction::TRANSACTIONTYPE) ->setNewValue($replace_image); $posted_mock_images[] = $replace_image; } else if (!$existing_image) { // this is an add @@ -162,7 +162,7 @@ final class PholioMockEditController extends PholioController { ->setDescription($description) ->setSequence($sequence); $xactions[] = id(new PholioTransaction()) - ->setTransactionType(PholioTransaction::TYPE_IMAGE_FILE) + ->setTransactionType(PholioImageFileTransaction::TRANSACTIONTYPE) ->setNewValue( array('+' => array($add_image))); $posted_mock_images[] = $add_image; @@ -178,7 +178,7 @@ final class PholioMockEditController extends PholioController { array($existing_image->getPHID() => $description)); $xactions[] = id(new PholioTransaction()) ->setTransactionType( - PholioTransaction::TYPE_IMAGE_SEQUENCE) + PholioImageSequenceTransaction::TRANSACTIONTYPE) ->setNewValue( array($existing_image->getPHID() => $sequence)); @@ -189,7 +189,7 @@ final class PholioMockEditController extends PholioController { if (!isset($files[$file_phid]) && !isset($replaces[$file_phid])) { // this is an outright delete $xactions[] = id(new PholioTransaction()) - ->setTransactionType(PholioTransaction::TYPE_IMAGE_FILE) + ->setTransactionType(PholioImageFileTransaction::TRANSACTIONTYPE) ->setNewValue( array('-' => array($mock_image))); } diff --git a/src/applications/pholio/editor/PholioMockEditor.php b/src/applications/pholio/editor/PholioMockEditor.php index 322eaa7267..ef315512ec 100644 --- a/src/applications/pholio/editor/PholioMockEditor.php +++ b/src/applications/pholio/editor/PholioMockEditor.php @@ -17,7 +17,8 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor { $this->newImages = $new_images; return $this; } - private function getNewImages() { + + public function getNewImages() { return $this->newImages; } @@ -31,89 +32,9 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor { $types[] = PholioTransaction::TYPE_INLINE; - $types[] = PholioTransaction::TYPE_IMAGE_FILE; - $types[] = PholioTransaction::TYPE_IMAGE_REPLACE; - $types[] = PholioTransaction::TYPE_IMAGE_SEQUENCE; - return $types; } - protected function getCustomTransactionOldValue( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PholioTransaction::TYPE_IMAGE_FILE: - $images = $object->getImages(); - return mpull($images, 'getPHID'); - case PholioTransaction::TYPE_IMAGE_REPLACE: - $raw = $xaction->getNewValue(); - return $raw->getReplacesImagePHID(); - case PholioTransaction::TYPE_IMAGE_SEQUENCE: - $sequence = null; - $phid = null; - $image = $this->getImageForXaction($object, $xaction); - if ($image) { - $sequence = $image->getSequence(); - $phid = $image->getPHID(); - } - return array($phid => $sequence); - } - } - - protected function getCustomTransactionNewValue( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PholioTransaction::TYPE_IMAGE_SEQUENCE: - return $xaction->getNewValue(); - case PholioTransaction::TYPE_IMAGE_REPLACE: - $raw = $xaction->getNewValue(); - return $raw->getPHID(); - case PholioTransaction::TYPE_IMAGE_FILE: - $raw_new_value = $xaction->getNewValue(); - $new_value = array(); - foreach ($raw_new_value as $key => $images) { - $new_value[$key] = mpull($images, 'getPHID'); - } - $xaction->setNewValue($new_value); - return $this->getPHIDTransactionNewValue($xaction); - } - } - - protected function extractFilePHIDsFromCustomTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - $images = $this->getNewImages(); - $images = mpull($images, null, 'getPHID'); - - switch ($xaction->getTransactionType()) { - case PholioTransaction::TYPE_IMAGE_FILE: - $file_phids = array(); - foreach ($xaction->getNewValue() as $image_phid) { - $image = idx($images, $image_phid); - if (!$image) { - continue; - } - $file_phids[] = $image->getFilePHID(); - } - return $file_phids; - case PholioTransaction::TYPE_IMAGE_REPLACE: - $image_phid = $xaction->getNewValue(); - $image = idx($images, $image_phid); - - if ($image) { - return array($image->getFilePHID()); - } - break; - } - - return parent::extractFilePHIDsFromCustomTransaction($object, $xaction); - } - - protected function transactionHasEffect( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { @@ -132,8 +53,8 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor { foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { - case PholioTransaction::TYPE_IMAGE_FILE: - case PholioTransaction::TYPE_IMAGE_REPLACE: + case PholioImageFileTransaction::TRANSACTIONTYPE: + case PholioImageReplaceTransaction::TRANSACTIONTYPE: return true; break; } @@ -148,7 +69,7 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor { $new_images = array(); foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { - case PholioTransaction::TYPE_IMAGE_FILE: + case PholioImageFileTransaction::TRANSACTIONTYPE: $new_value = $xaction->getNewValue(); foreach ($new_value as $key => $txn_images) { if ($key != '+') { @@ -160,7 +81,7 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor { } } break; - case PholioTransaction::TYPE_IMAGE_REPLACE: + case PholioImageReplaceTransaction::TRANSACTIONTYPE: $image = $xaction->getNewValue(); $image->save(); $new_images[] = $image; @@ -170,67 +91,6 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor { $this->setNewImages($new_images); } - private function getImageForXaction( - PholioMock $mock, - PhabricatorApplicationTransaction $xaction) { - $raw_new_value = $xaction->getNewValue(); - $image_phid = key($raw_new_value); - $images = $mock->getImages(); - foreach ($images as $image) { - if ($image->getPHID() == $image_phid) { - return $image; - } - } - return null; - } - - protected function applyCustomInternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - return; - } - - protected function applyCustomExternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PholioTransaction::TYPE_IMAGE_FILE: - $old_map = array_fuse($xaction->getOldValue()); - $new_map = array_fuse($xaction->getNewValue()); - - $obsolete_map = array_diff_key($old_map, $new_map); - $images = $object->getImages(); - foreach ($images as $seq => $image) { - if (isset($obsolete_map[$image->getPHID()])) { - $image->setIsObsolete(1); - $image->save(); - unset($images[$seq]); - } - } - $object->attachImages($images); - break; - case PholioTransaction::TYPE_IMAGE_REPLACE: - $old = $xaction->getOldValue(); - $images = $object->getImages(); - foreach ($images as $seq => $image) { - if ($image->getPHID() == $old) { - $image->setIsObsolete(1); - $image->save(); - unset($images[$seq]); - } - } - $object->attachImages($images); - break; - case PholioTransaction::TYPE_IMAGE_SEQUENCE: - $image = $this->getImageForXaction($object, $xaction); - $value = (int)head($xaction->getNewValue()); - $image->setSequence($value); - $image->save(); - break; - } - } - protected function applyFinalEffects( PhabricatorLiskDAO $object, array $xactions) { @@ -244,35 +104,6 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor { return $xactions; } - protected function mergeTransactions( - PhabricatorApplicationTransaction $u, - PhabricatorApplicationTransaction $v) { - - $type = $u->getTransactionType(); - switch ($type) { - case PholioTransaction::TYPE_IMAGE_REPLACE: - $u_img = $u->getNewValue(); - $v_img = $v->getNewValue(); - if ($u_img->getReplacesImagePHID() == $v_img->getReplacesImagePHID()) { - return $v; - } - break; - case PholioTransaction::TYPE_IMAGE_FILE: - return $this->mergePHIDOrEdgeTransactions($u, $v); - case PholioTransaction::TYPE_IMAGE_SEQUENCE: - $raw_new_value_u = $u->getNewValue(); - $raw_new_value_v = $v->getNewValue(); - $phid_u = key($raw_new_value_u); - $phid_v = key($raw_new_value_v); - if ($phid_u == $phid_v) { - return $v; - } - break; - } - - return parent::mergeTransactions($u, $v); - } - protected function shouldSendMail( PhabricatorLiskDAO $object, array $xactions) { diff --git a/src/applications/pholio/storage/PholioTransaction.php b/src/applications/pholio/storage/PholioTransaction.php index 84a833b29f..346f0e4561 100644 --- a/src/applications/pholio/storage/PholioTransaction.php +++ b/src/applications/pholio/storage/PholioTransaction.php @@ -2,11 +2,6 @@ final class PholioTransaction extends PhabricatorModularTransaction { - // Edits to images within the mock - const TYPE_IMAGE_FILE = 'image-file'; - const TYPE_IMAGE_REPLACE = 'image-replace'; - const TYPE_IMAGE_SEQUENCE = 'image-sequence'; - // Your witty commentary at the mock : image : x,y level const TYPE_INLINE = 'inline'; @@ -35,56 +30,10 @@ final class PholioTransaction extends PhabricatorModularTransaction { return new PholioTransactionView(); } - public function getRequiredHandlePHIDs() { - $phids = parent::getRequiredHandlePHIDs(); - $phids[] = $this->getObjectPHID(); - - $new = $this->getNewValue(); - $old = $this->getOldValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_IMAGE_FILE: - $phids = array_merge($phids, $new, $old); - break; - case self::TYPE_IMAGE_REPLACE: - $phids[] = $new; - $phids[] = $old; - break; - case PholioImageDescriptionTransaction::TRANSACTIONTYPE: - case PholioImageNameTransaction::TRANSACTIONTYPE: - case self::TYPE_IMAGE_SEQUENCE: - $phids[] = key($new); - break; - } - - return $phids; - } - - public function shouldHide() { - $old = $this->getOldValue(); - - switch ($this->getTransactionType()) { - // this is boring / silly to surface; changing sequence is NBD - case self::TYPE_IMAGE_SEQUENCE: - return true; - } - - return parent::shouldHide(); - } - public function getIcon() { - - $new = $this->getNewValue(); - $old = $this->getOldValue(); - switch ($this->getTransactionType()) { case self::TYPE_INLINE: return 'fa-comment'; - case self::TYPE_IMAGE_SEQUENCE: - return 'fa-pencil'; - case self::TYPE_IMAGE_FILE: - case self::TYPE_IMAGE_REPLACE: - return 'fa-picture-o'; } return parent::getIcon(); @@ -104,9 +53,9 @@ final class PholioTransaction extends PhabricatorModularTransaction { case PholioMockDescriptionTransaction::TRANSACTIONTYPE: case PholioImageNameTransaction::TRANSACTIONTYPE: case PholioImageDescriptionTransaction::TRANSACTIONTYPE: - case self::TYPE_IMAGE_SEQUENCE: - case self::TYPE_IMAGE_FILE: - case self::TYPE_IMAGE_REPLACE: + case PholioImageSequenceTransaction::TRANSACTIONTYPE: + case PholioImageFileTransaction::TRANSACTIONTYPE: + case PholioImageReplaceTransaction::TRANSACTIONTYPE: $tags[] = self::MAILTAG_UPDATED; break; default: @@ -137,45 +86,6 @@ final class PholioTransaction extends PhabricatorModularTransaction { $this->renderHandleLink($author_phid), $count); break; - case self::TYPE_IMAGE_REPLACE: - return pht( - '%s replaced %s with %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($old), - $this->renderHandleLink($new)); - break; - case self::TYPE_IMAGE_FILE: - $add = array_diff($new, $old); - $rem = array_diff($old, $new); - - if ($add && $rem) { - return pht( - '%s edited image(s), added %d: %s; removed %d: %s.', - $this->renderHandleLink($author_phid), - count($add), - $this->renderHandleList($add), - count($rem), - $this->renderHandleList($rem)); - } else if ($add) { - return pht( - '%s added %d image(s): %s.', - $this->renderHandleLink($author_phid), - count($add), - $this->renderHandleList($add)); - } else { - return pht( - '%s removed %d image(s): %s.', - $this->renderHandleLink($author_phid), - count($rem), - $this->renderHandleList($rem)); - } - break; - case self::TYPE_IMAGE_SEQUENCE: - return pht( - '%s updated an image\'s (%s) sequence.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink(key($new))); - break; } return parent::getTitle(); @@ -196,44 +106,9 @@ final class PholioTransaction extends PhabricatorModularTransaction { $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); break; - case self::TYPE_IMAGE_REPLACE: - case self::TYPE_IMAGE_FILE: - return pht( - '%s updated images of %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - break; - case self::TYPE_IMAGE_SEQUENCE: - return pht( - '%s updated image sequence of %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - break; } return parent::getTitleForFeed(); } - public function getColor() { - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_IMAGE_REPLACE: - return PhabricatorTransactions::COLOR_YELLOW; - case self::TYPE_IMAGE_FILE: - $add = array_diff($new, $old); - $rem = array_diff($old, $new); - if ($add && $rem) { - return PhabricatorTransactions::COLOR_YELLOW; - } else if ($add) { - return PhabricatorTransactions::COLOR_GREEN; - } else { - return PhabricatorTransactions::COLOR_RED; - } - } - - return parent::getColor(); - } - } diff --git a/src/applications/pholio/xaction/PholioImageFileTransaction.php b/src/applications/pholio/xaction/PholioImageFileTransaction.php new file mode 100644 index 0000000000..5f68dad9f1 --- /dev/null +++ b/src/applications/pholio/xaction/PholioImageFileTransaction.php @@ -0,0 +1,120 @@ +getImages(); + return array_values(mpull($images, 'getPHID')); + } + + public function generateNewValue($object, $value) { + $new_value = array(); + foreach ($value as $key => $images) { + $new_value[$key] = mpull($images, 'getPHID'); + } + $old = array_fuse($this->getOldValue()); + return $this->getEditor()->getPHIDList($old, $new_value); + } + + public function applyInternalEffects($object, $value) { + $old_map = array_fuse($this->getOldValue()); + $new_map = array_fuse($this->getNewValue()); + + $obsolete_map = array_diff_key($old_map, $new_map); + $images = $object->getImages(); + foreach ($images as $seq => $image) { + if (isset($obsolete_map[$image->getPHID()])) { + $image->setIsObsolete(1); + $image->save(); + unset($images[$seq]); + } + } + $object->attachImages($images); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $add = array_diff($new, $old); + $rem = array_diff($old, $new); + + if ($add && $rem) { + return pht( + '%s edited image(s), added %d: %s; removed %d: %s.', + $this->renderAuthor(), + count($add), + $this->renderHandleList($add), + count($rem), + $this->renderHandleList($rem)); + } else if ($add) { + return pht( + '%s added %d image(s): %s.', + $this->renderAuthor(), + count($add), + $this->renderHandleList($add)); + } else { + return pht( + '%s removed %d image(s): %s.', + $this->renderAuthor(), + count($rem), + $this->renderHandleList($rem)); + } + } + + public function getTitleForFeed() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + return pht( + '%s updated images of %s.', + $this->renderAuthor(), + $this->renderObject()); + } + + public function getIcon() { + return 'fa-picture-o'; + } + + public function getColor() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $add = array_diff($new, $old); + $rem = array_diff($old, $new); + if ($add && $rem) { + return PhabricatorTransactions::COLOR_YELLOW; + } else if ($add) { + return PhabricatorTransactions::COLOR_GREEN; + } else { + return PhabricatorTransactions::COLOR_RED; + } + } + + public function extractFilePHIDs($object, $value) { + $images = $this->getEditor()->getNewImages(); + $images = mpull($images, null, 'getPHID'); + + + $file_phids = array(); + foreach ($value as $image_phid) { + $image = idx($images, $image_phid); + if (!$image) { + continue; + } + $file_phids[] = $image->getFilePHID(); + } + return $file_phids; + } + + public function mergeTransactions( + $object, + PhabricatorApplicationTransaction $u, + PhabricatorApplicationTransaction $v) { + return $this->getEditor()->mergePHIDOrEdgeTransactions($u, $v); + } + +} diff --git a/src/applications/pholio/xaction/PholioImageReplaceTransaction.php b/src/applications/pholio/xaction/PholioImageReplaceTransaction.php new file mode 100644 index 0000000000..e6d45dfc7a --- /dev/null +++ b/src/applications/pholio/xaction/PholioImageReplaceTransaction.php @@ -0,0 +1,68 @@ +getNewValue(); + return $new_image->getReplacesImagePHID(); + } + + public function generateNewValue($object, $value) { + return $value->getPHID(); + } + + public function applyInternalEffects($object, $value) { + $old = $this->getOldValue(); + $images = $object->getImages(); + foreach ($images as $seq => $image) { + if ($image->getPHID() == $old) { + $image->setIsObsolete(1); + $image->save(); + unset($images[$seq]); + } + } + $object->attachImages($images); + } + + public function getTitle() { + return pht( + '%s replaced %s with %s.', + $this->renderAuthor(), + $this->renderOldHandle(), + $this->renderNewHandle()); + } + + public function getTitleForFeed() { + return pht( + '%s updated images of %s.', + $this->renderAuthor(), + $this->renderObject()); + } + + public function getIcon() { + return 'fa-picture-o'; + } + + public function getColor() { + return PhabricatorTransactions::COLOR_YELLOW; + } + + public function mergeTransactions( + $object, + PhabricatorApplicationTransaction $u, + PhabricatorApplicationTransaction $v) { + $u_img = $u->getNewValue(); + $v_img = $v->getNewValue(); + if ($u_img->getReplacesImagePHID() == $v_img->getReplacesImagePHID()) { + return $v; + } + } + + public function extractFilePHIDs($object, $value) { + return array($value); + } + +} diff --git a/src/applications/pholio/xaction/PholioImageSequenceTransaction.php b/src/applications/pholio/xaction/PholioImageSequenceTransaction.php new file mode 100644 index 0000000000..c98c199adf --- /dev/null +++ b/src/applications/pholio/xaction/PholioImageSequenceTransaction.php @@ -0,0 +1,60 @@ +getImageForXaction($object); + if ($image) { + $sequence = $image->getSequence(); + $phid = $image->getPHID(); + } + return array($phid => $sequence); + } + + public function applyInternalEffects($object, $value) { + $image = $this->getImageForXaction($object); + $value = (int)head($this->getNewValue()); + $image->setSequence($value); + $image->save(); + } + + public function getTitle() { + $new = $this->getNewValue(); + + return pht( + '%s updated an image\'s (%s) sequence.', + $this->renderAuthor(), + $this->renderHandleLink(key($new))); + } + + public function getTitleForFeed() { + return pht( + '%s updated image sequence of %s.', + $this->renderAuthor(), + $this->renderObject()); + } + + public function shouldHide() { + // this is boring / silly to surface; changing sequence is NBD + return true; + } + + public function mergeTransactions( + $object, + PhabricatorApplicationTransaction $u, + PhabricatorApplicationTransaction $v) { + $raw_new_value_u = $u->getNewValue(); + $raw_new_value_v = $v->getNewValue(); + $phid_u = key($raw_new_value_u); + $phid_v = key($raw_new_value_v); + if ($phid_u == $phid_v) { + return $v; + } + } + +} From 404fe482d91492308dc839b5635de579b17dff5b Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 8 May 2017 07:58:02 -0700 Subject: [PATCH 11/81] Add a Quicksand-aware page-level container to diffs Summary: Ref T12616. Ref T8047. Ref T11093. We currently have several bugs where diff state sticks across Quicksand pages. Add a new top-level object to handle this, with `sleep()` and `wake()` methods. In `sleep()`, future changes will remove/deacivate all the reticles/editors/etc. See T12616 for high-level discussion of plans here. This general idea is likely to become more formal eventually (e.g. for "sheets" or whatever we call them, in T10469) but I think this is probably a reasonable place to draw a line for now. Test Plan: - Added some logging to sleep(), wake() and construct(). - Viewed changes in Differential. - With Quicksand on, browsed around; saw state change logs fire properly. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616, T11093, T8047 Differential Revision: https://secure.phabricator.com/D17840 --- resources/celerity/map.php | 26 ++++---- .../js/application/diff/DiffChangesetList.js | 25 ++++++++ .../differential/behavior-populate.js | 60 ++++++++++++++++++- 3 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 webroot/rsrc/js/application/diff/DiffChangesetList.js diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 8430bf43b9..a2933b83aa 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '2ff7879f', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '90b30783', - 'differential.pkg.js' => 'ddfeb49b', + 'differential.pkg.js' => '84d27954', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -390,6 +390,7 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', + 'rsrc/js/application/diff/DiffChangesetList.js' => '58c4c0d6', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/ChangesetViewManager.js' => 'a2828756', 'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '2e3f9738', @@ -399,7 +400,7 @@ return array( 'rsrc/js/application/differential/behavior-dropdown-menus.js' => '9a6b9324', 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '4fbbc3e9', 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '92904457', - 'rsrc/js/application/differential/behavior-populate.js' => '8694b1df', + 'rsrc/js/application/differential/behavior-populate.js' => 'c0c44c3e', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', 'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => 'c93358e3', @@ -628,7 +629,7 @@ return array( 'javelin-behavior-differential-edit-inline-comments' => '4fbbc3e9', 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-keyboard-navigation' => '92904457', - 'javelin-behavior-differential-populate' => '8694b1df', + 'javelin-behavior-differential-populate' => 'c0c44c3e', 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', 'javelin-behavior-differential-user-select' => 'a8d8459d', 'javelin-behavior-diffusion-browse-file' => '054a0f0b', @@ -786,6 +787,7 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', + 'phabricator-diff-changeset-list' => '58c4c0d6', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -1353,6 +1355,9 @@ return array( 'javelin-vector', 'javelin-dom', ), + '58c4c0d6' => array( + 'javelin-install', + ), '58dea2fa' => array( 'javelin-install', 'javelin-util', @@ -1558,13 +1563,6 @@ return array( 'phabricator-notification', 'conpherence-thread-manager', ), - '8694b1df' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - 'phabricator-tooltip', - 'changeset-view-manager', - ), '88236f00' => array( 'javelin-behavior', 'phabricator-keyboard-shortcut', @@ -1948,6 +1946,14 @@ return array( 'javelin-install', 'javelin-dom', ), + 'c0c44c3e' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-stratcom', + 'phabricator-tooltip', + 'changeset-view-manager', + 'phabricator-diff-changeset-list', + ), 'c420b0b9' => array( 'javelin-behavior', 'javelin-behavior-device', diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js new file mode 100644 index 0000000000..d62c2abd7a --- /dev/null +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -0,0 +1,25 @@ +/** + * @provides phabricator-diff-changeset-list + * @requires javelin-install + * @javelin + */ + +JX.install('DiffChangesetList', { + + construct: function() { + + }, + + members: { + + sleep: function() { + + }, + + wake: function() { + + } + + } + +}); diff --git a/webroot/rsrc/js/application/differential/behavior-populate.js b/webroot/rsrc/js/application/differential/behavior-populate.js index 6d0aabb213..a7e54ddd37 100644 --- a/webroot/rsrc/js/application/differential/behavior-populate.js +++ b/webroot/rsrc/js/application/differential/behavior-populate.js @@ -5,9 +5,67 @@ * javelin-stratcom * phabricator-tooltip * changeset-view-manager + * phabricator-diff-changeset-list + * @javelin */ -JX.behavior('differential-populate', function(config) { +JX.behavior('differential-populate', function(config, statics) { + + // When we perform a Quicksand navigation, deactivate the changeset lists on + // the current page and activate the changeset lists on the new page. + var onredraw = function(page_id) { + // If the current page is already active, we don't need to do anything. + if (statics.pageID === page_id) { + return; + } + + var ii; + + // Put the old lists to sleep. + var old_lists = get_lists(statics.pageID); + for (ii = 0; ii < old_lists.length; ii++) { + old_lists[ii].sleep(); + } + statics.pageID = null; + + // Awaken the new lists, if they exist. + if (statics.pages.hasOwnProperty(page_id)) { + var new_lists = get_lists(page_id); + for (ii = 0; ii < new_lists.length; ii++) { + new_lists[ii].wake(); + } + + statics.pageID = page_id; + } + }; + + // Get changeset lists on the current page. + var get_lists = function(page_id) { + if (page_id === null) { + return []; + } + + return statics.pages[page_id] || []; + }; + + if (!statics.installed) { + statics.installed = true; + statics.pages = {}; + statics.pageID = null; + + JX.Stratcom.listen('quicksand-redraw', null, function(e) { + onredraw(e.getData().newResponseID); + }); + } + + var changeset_list = new JX.DiffChangesetList(); + + // Install and activate the current page. + var page_id = JX.Quicksand.getCurrentPageID(); + statics.pages[page_id] = [changeset_list]; + onredraw(page_id); + + for (var ii = 0; ii < config.changesetViewIDs.length; ii++) { var id = config.changesetViewIDs[ii]; From 993d94211722d0114b7765ec4ace326329bac9bd Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 8 May 2017 09:17:16 -0700 Subject: [PATCH 12/81] Gently move some listeners into DiffChangesetList Summary: Ref T12616. Put these listeners in DiffChangesetList so the wake/sleep properly for Quicksand. Test Plan: - Added some logging. - With quicksand, moved between diffs. - Saw "load" and "show more" fire exactly once on each page, with the correct changeset list listener. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616 Differential Revision: https://secure.phabricator.com/D17841 --- resources/celerity/map.php | 32 +++++----- .../js/application/diff/DiffChangesetList.js | 64 ++++++++++++++++++- .../differential/behavior-populate.js | 33 ---------- 3 files changed, 79 insertions(+), 50 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index a2933b83aa..8029980969 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '2ff7879f', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '90b30783', - 'differential.pkg.js' => '84d27954', + 'differential.pkg.js' => '8532657e', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -390,7 +390,7 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangesetList.js' => '58c4c0d6', + 'rsrc/js/application/diff/DiffChangesetList.js' => '9137a890', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/ChangesetViewManager.js' => 'a2828756', 'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '2e3f9738', @@ -400,7 +400,7 @@ return array( 'rsrc/js/application/differential/behavior-dropdown-menus.js' => '9a6b9324', 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '4fbbc3e9', 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '92904457', - 'rsrc/js/application/differential/behavior-populate.js' => 'c0c44c3e', + 'rsrc/js/application/differential/behavior-populate.js' => 'cf707904', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', 'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => 'c93358e3', @@ -629,7 +629,7 @@ return array( 'javelin-behavior-differential-edit-inline-comments' => '4fbbc3e9', 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-keyboard-navigation' => '92904457', - 'javelin-behavior-differential-populate' => 'c0c44c3e', + 'javelin-behavior-differential-populate' => 'cf707904', 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', 'javelin-behavior-differential-user-select' => 'a8d8459d', 'javelin-behavior-diffusion-browse-file' => '054a0f0b', @@ -787,7 +787,7 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset-list' => '58c4c0d6', + 'phabricator-diff-changeset-list' => '9137a890', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -1355,9 +1355,6 @@ return array( 'javelin-vector', 'javelin-dom', ), - '58c4c0d6' => array( - 'javelin-install', - ), '58dea2fa' => array( 'javelin-install', 'javelin-util', @@ -1621,6 +1618,9 @@ return array( 'javelin-dom', 'javelin-request', ), + '9137a890' => array( + 'javelin-install', + ), 92904457 => array( 'javelin-behavior', 'javelin-dom', @@ -1946,14 +1946,6 @@ return array( 'javelin-install', 'javelin-dom', ), - 'c0c44c3e' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - 'phabricator-tooltip', - 'changeset-view-manager', - 'phabricator-diff-changeset-list', - ), 'c420b0b9' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -2041,6 +2033,14 @@ return array( 'cd2b9b77' => array( 'phui-oi-list-view-css', ), + 'cf707904' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-stratcom', + 'phabricator-tooltip', + 'changeset-view-manager', + 'phabricator-diff-changeset-list', + ), 'd0c516d5' => array( 'javelin-behavior', 'javelin-dom', diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index d62c2abd7a..132a911c6a 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -8,16 +8,78 @@ JX.install('DiffChangesetList', { construct: function() { + var onload = JX.bind(this, this._ifawake, this._onload); + JX.Stratcom.listen('click', 'differential-load', onload); + + var onmore = JX.bind(this, this._ifawake, this._onmore); + JX.Stratcom.listen('click', 'show-more', onmore); }, members: { + _asleep: true, sleep: function() { - + this._asleep = true; }, wake: function() { + this._asleep = false; + }, + isAsleep: function() { + return this._asleep; + }, + + getChangesetForNode: function(node) { + return JX.ChangesetViewManager.getForNode(node); + }, + + _ifawake: function(f) { + // This function takes another function and only calls it if the + // changeset list is awake, so we basically just ignore events when we + // are asleep. This may move up the stack at some point as we do more + // with Quicksand/Sheets. + + if (this.isAsleep()) { + return; + } + + return f.apply(this, [].slice.call(arguments, 1)); + }, + + _onload: function(e) { + var data = e.getNodeData('differential-load'); + + // NOTE: We can trigger a load from either an explicit "Load" link on + // the changeset, or by clicking a link in the table of contents. If + // the event was a table of contents link, we let the anchor behavior + // run normally. + if (data.kill) { + e.kill(); + } + + var node = JX.$(data.id); + var changeset = this.getChangesetForNode(node); + + changeset.load(); + + // TODO: Move this into Changeset. + var routable = changeset.getRoutable(); + if (routable) { + routable.setPriority(2000); + } + }, + + _onmore: function(e) { + e.kill(); + + var node = e.getNode('differential-changeset'); + var changeset = this.getChangesetForNode(node); + + var data = e.getNodeData('show-more'); + var target = e.getNode('context-target'); + + changeset.loadContext(data.range, target); } } diff --git a/webroot/rsrc/js/application/differential/behavior-populate.js b/webroot/rsrc/js/application/differential/behavior-populate.js index a7e54ddd37..b3663b1119 100644 --- a/webroot/rsrc/js/application/differential/behavior-populate.js +++ b/webroot/rsrc/js/application/differential/behavior-populate.js @@ -75,39 +75,6 @@ JX.behavior('differential-populate', function(config, statics) { } } - JX.Stratcom.listen( - 'click', - 'differential-load', - function(e) { - var meta = e.getNodeData('differential-load'); - var changeset = JX.$(meta.id); - var view = JX.ChangesetViewManager.getForNode(changeset); - - view.load(); - var routable = view.getRoutable(); - if (routable) { - routable.setPriority(2000); - } - - if (meta.kill) { - e.kill(); - } - }); - - JX.Stratcom.listen( - 'click', - 'show-more', - function(e) { - e.kill(); - - var changeset = e.getNode('differential-changeset'); - var view = JX.ChangesetViewManager.getForNode(changeset); - var data = e.getNodeData('show-more'); - var target = e.getNode('context-target'); - - view.loadContext(data.range, target); - }); - var highlighted = null; var highlight_class = null; From 2bd25d7399c53d07de54685352b7ffde6803cfae Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 8 May 2017 09:52:16 -0700 Subject: [PATCH 13/81] Rename "DifferentialChangesetViewManager" to "DiffChangeset" Summary: Ref T12616. This class is already mostly-reasonable as a representation of an individual changeset, so I plan to just adjust it a little bit. Test Plan: - Used `git grep` to search for `ChangesetViewManager`. - Used `git grep` to search for `changeset-view-manager`. - Browsed around and interacted with changesets. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616 Differential Revision: https://secure.phabricator.com/D17842 --- resources/celerity/map.php | 99 ++++++++++--------- resources/celerity/packages.php | 3 +- .../DiffChangeset.js} | 6 +- .../js/application/diff/DiffChangesetList.js | 2 +- .../differential/behavior-dropdown-menus.js | 6 +- .../behavior-edit-inline-comments.js | 4 +- .../differential/behavior-populate.js | 4 +- 7 files changed, 63 insertions(+), 61 deletions(-) rename webroot/rsrc/js/application/{differential/ChangesetViewManager.js => diff/DiffChangeset.js} (98%) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 8029980969..70636c688e 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '2ff7879f', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '90b30783', - 'differential.pkg.js' => '8532657e', + 'differential.pkg.js' => '7e4a9c9c', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -390,17 +390,17 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangesetList.js' => '9137a890', + 'rsrc/js/application/diff/DiffChangeset.js' => 'a1189df6', + 'rsrc/js/application/diff/DiffChangesetList.js' => '2329e40e', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', - 'rsrc/js/application/differential/ChangesetViewManager.js' => 'a2828756', 'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '2e3f9738', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', - 'rsrc/js/application/differential/behavior-dropdown-menus.js' => '9a6b9324', - 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '4fbbc3e9', + 'rsrc/js/application/differential/behavior-dropdown-menus.js' => 'f45a2836', + 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => 'c0f1c3b5', 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '92904457', - 'rsrc/js/application/differential/behavior-populate.js' => 'cf707904', + 'rsrc/js/application/differential/behavior-populate.js' => '00d88bc4', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', 'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => 'c93358e3', @@ -557,7 +557,6 @@ return array( 'application-search-view-css' => '66ee5d46', 'auth-css' => '0877ed6e', 'bulk-job-css' => 'df9c1d4a', - 'changeset-view-manager' => 'a2828756', 'conduit-api-css' => '7bc725c4', 'config-options-css' => '0ede4c9b', 'config-page-css' => 'c1d5121b', @@ -625,11 +624,11 @@ return array( 'javelin-behavior-diff-preview-link' => '051c7832', 'javelin-behavior-differential-comment-jump' => '4fdb476d', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', - 'javelin-behavior-differential-dropdown-menus' => '9a6b9324', - 'javelin-behavior-differential-edit-inline-comments' => '4fbbc3e9', + 'javelin-behavior-differential-dropdown-menus' => 'f45a2836', + 'javelin-behavior-differential-edit-inline-comments' => 'c0f1c3b5', 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-keyboard-navigation' => '92904457', - 'javelin-behavior-differential-populate' => 'cf707904', + 'javelin-behavior-differential-populate' => '00d88bc4', 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', 'javelin-behavior-differential-user-select' => 'a8d8459d', 'javelin-behavior-diffusion-browse-file' => '054a0f0b', @@ -787,7 +786,8 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset-list' => '9137a890', + 'phabricator-diff-changeset' => 'a1189df6', + 'phabricator-diff-changeset-list' => '2329e40e', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -922,6 +922,14 @@ return array( 'unhandled-exception-css' => '4c96257a', ), 'requires' => array( + '00d88bc4' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-stratcom', + 'phabricator-tooltip', + 'phabricator-diff-changeset-list', + 'phabricator-diff-changeset', + ), '013ffff9' => array( 'javelin-install', 'javelin-util', @@ -1079,6 +1087,9 @@ return array( 'javelin-workflow', 'javelin-util', ), + '2329e40e' => array( + 'javelin-install', + ), 26167537 => array( 'javelin-install', 'javelin-dom', @@ -1296,14 +1307,6 @@ return array( 'javelin-stratcom', 'javelin-dom', ), - '4fbbc3e9' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - 'javelin-util', - 'javelin-vector', - 'differential-inline-comment-editor', - ), '4fdb476d' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1618,9 +1621,6 @@ return array( 'javelin-dom', 'javelin-request', ), - '9137a890' => array( - 'javelin-install', - ), 92904457 => array( 'javelin-behavior', 'javelin-dom', @@ -1669,18 +1669,6 @@ return array( 'javelin-dom', 'javelin-reactor-dom', ), - '9a6b9324' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-workflow', - 'phuix-dropdown-menu', - 'phuix-action-list-view', - 'phuix-action-view', - 'phabricator-phtize', - 'changeset-view-manager', - ), '9a6dd75c' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1718,12 +1706,7 @@ return array( 'javelin-util', 'phabricator-keyboard-shortcut', ), - 'a155550f' => array( - 'javelin-install', - 'javelin-dom', - 'javelin-reactor-dom', - ), - 'a2828756' => array( + 'a1189df6' => array( 'javelin-dom', 'javelin-util', 'javelin-stratcom', @@ -1733,6 +1716,11 @@ return array( 'javelin-behavior-device', 'javelin-vector', ), + 'a155550f' => array( + 'javelin-install', + 'javelin-dom', + 'javelin-reactor-dom', + ), 'a3a63478' => array( 'phui-workcard-view-css', ), @@ -1946,6 +1934,14 @@ return array( 'javelin-install', 'javelin-dom', ), + 'c0f1c3b5' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + 'javelin-util', + 'javelin-vector', + 'differential-inline-comment-editor', + ), 'c420b0b9' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -2033,14 +2029,6 @@ return array( 'cd2b9b77' => array( 'phui-oi-list-view-css', ), - 'cf707904' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - 'phabricator-tooltip', - 'changeset-view-manager', - 'phabricator-diff-changeset-list', - ), 'd0c516d5' => array( 'javelin-behavior', 'javelin-dom', @@ -2211,6 +2199,18 @@ return array( 'f12cbc9f' => array( 'phui-oi-list-view-css', ), + 'f45a2836' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-workflow', + 'phuix-dropdown-menu', + 'phuix-action-list-view', + 'phuix-action-view', + 'phabricator-phtize', + 'phabricator-diff-changeset', + ), 'f50152ad' => array( 'phui-timeline-view-css', ), @@ -2469,7 +2469,8 @@ return array( 'javelin-behavior-differential-toggle-files', 'javelin-behavior-differential-user-select', 'javelin-behavior-aphront-more', - 'changeset-view-manager', + 'phabricator-diff-changeset', + 'phabricator-diff-changeset-list', ), 'diffusion.pkg.css' => array( 'diffusion-icons-css', diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php index d906c738da..ed281822a8 100644 --- a/resources/celerity/packages.php +++ b/resources/celerity/packages.php @@ -208,7 +208,8 @@ return array( 'javelin-behavior-differential-toggle-files', 'javelin-behavior-differential-user-select', 'javelin-behavior-aphront-more', - 'changeset-view-manager', + 'phabricator-diff-changeset', + 'phabricator-diff-changeset-list', ), 'diffusion.pkg.css' => array( 'diffusion-icons-css', diff --git a/webroot/rsrc/js/application/differential/ChangesetViewManager.js b/webroot/rsrc/js/application/diff/DiffChangeset.js similarity index 98% rename from webroot/rsrc/js/application/differential/ChangesetViewManager.js rename to webroot/rsrc/js/application/diff/DiffChangeset.js index 0c23e18737..27058dbc42 100644 --- a/webroot/rsrc/js/application/differential/ChangesetViewManager.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -1,5 +1,5 @@ /** - * @provides changeset-view-manager + * @provides phabricator-diff-changeset * @requires javelin-dom * javelin-util * javelin-stratcom @@ -11,7 +11,7 @@ */ -JX.install('ChangesetViewManager', { +JX.install('DiffChangeset', { construct : function(node) { this._node = node; @@ -389,7 +389,7 @@ JX.install('ChangesetViewManager', { getForNode: function(node) { var data = JX.Stratcom.getData(node); if (!data.changesetViewManager) { - data.changesetViewManager = new JX.ChangesetViewManager(node); + data.changesetViewManager = new JX.DiffChangeset(node); } return data.changesetViewManager; } diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index 132a911c6a..c8282dba32 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -31,7 +31,7 @@ JX.install('DiffChangesetList', { }, getChangesetForNode: function(node) { - return JX.ChangesetViewManager.getForNode(node); + return JX.DiffChangeset.getForNode(node); }, _ifawake: function(f) { diff --git a/webroot/rsrc/js/application/differential/behavior-dropdown-menus.js b/webroot/rsrc/js/application/differential/behavior-dropdown-menus.js index 1905e3a433..ab91818143 100644 --- a/webroot/rsrc/js/application/differential/behavior-dropdown-menus.js +++ b/webroot/rsrc/js/application/differential/behavior-dropdown-menus.js @@ -9,14 +9,14 @@ * phuix-action-list-view * phuix-action-view * phabricator-phtize - * changeset-view-manager + * phabricator-diff-changeset */ JX.behavior('differential-dropdown-menus', function(config) { var pht = JX.phtize(config.pht); function show_more(container) { - var view = JX.ChangesetViewManager.getForNode(container); + var view = JX.DiffChangeset.getForNode(container); var nodes = JX.DOM.scry(container, 'tr', 'context-target'); for (var ii = 0; ii < nodes.length; ii++) { @@ -59,7 +59,7 @@ JX.behavior('differential-dropdown-menus', function(config) { 'div', 'differential-changeset'); - var view = JX.ChangesetViewManager.getForNode(changeset); + var view = JX.DiffChangeset.getForNode(changeset); var menu = new JX.PHUIXDropdownMenu(button); var list = new JX.PHUIXActionListView(); diff --git a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js index 032b8cec68..1859ca1754 100644 --- a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js +++ b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js @@ -274,7 +274,7 @@ JX.behavior('differential-edit-inline-comments', function(config) { insert = target.parentNode; } - var view = JX.ChangesetViewManager.getForNode(root); + var view = JX.DiffChangeset.getForNode(root); editor = new JX.DifferentialInlineCommentEditor(config.uri) .setTemplates(view.getUndoTemplates()) @@ -390,7 +390,7 @@ JX.behavior('differential-edit-inline-comments', function(config) { node, 'div', 'differential-changeset'); - var view = JX.ChangesetViewManager.getForNode(changeset_root); + var view = JX.DiffChangeset.getForNode(changeset_root); editor = new JX.DifferentialInlineCommentEditor(config.uri) .setTemplates(view.getUndoTemplates()) diff --git a/webroot/rsrc/js/application/differential/behavior-populate.js b/webroot/rsrc/js/application/differential/behavior-populate.js index b3663b1119..4c6187189f 100644 --- a/webroot/rsrc/js/application/differential/behavior-populate.js +++ b/webroot/rsrc/js/application/differential/behavior-populate.js @@ -4,8 +4,8 @@ * javelin-dom * javelin-stratcom * phabricator-tooltip - * changeset-view-manager * phabricator-diff-changeset-list + * phabricator-diff-changeset * @javelin */ @@ -69,7 +69,7 @@ JX.behavior('differential-populate', function(config, statics) { for (var ii = 0; ii < config.changesetViewIDs.length; ii++) { var id = config.changesetViewIDs[ii]; - var view = JX.ChangesetViewManager.getForNode(JX.$(id)); + var view = JX.DiffChangeset.getForNode(JX.$(id)); if (view.shouldAutoload()) { view.setStabilize(true).load(); } From 63450cc48eca9761f3cd292e6e33716a9b0de74a Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 8 May 2017 10:00:33 -0700 Subject: [PATCH 14/81] Remove "Show All Context" button from Diffusion Summary: Ref T12616. Diffusion, only, has a "Show All Context" button which expands the full context on all changes. I don't remember the exact history on this, but it hasn't existed in Differential for some time and no one has complained. I suspect that the "View Options > Show All Context" on each file may replace it. I can't really come up with good reasons to use it, offhand. If we want to restore it, I think global options after T1591 is promising. {F4945561} Test Plan: - Loaded a commit in Diffusion, no longer saw a button. - Grepped for relevant sigils. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616 Differential Revision: https://secure.phabricator.com/D17843 --- resources/celerity/map.php | 30 +++++++++---------- .../view/PHUIDiffTableOfContentsListView.php | 19 +----------- .../differential/behavior-dropdown-menus.js | 14 --------- 3 files changed, 16 insertions(+), 47 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 70636c688e..6107ae0e71 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '2ff7879f', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '90b30783', - 'differential.pkg.js' => '7e4a9c9c', + 'differential.pkg.js' => 'ef6c7cfc', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -397,7 +397,7 @@ return array( 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', - 'rsrc/js/application/differential/behavior-dropdown-menus.js' => 'f45a2836', + 'rsrc/js/application/differential/behavior-dropdown-menus.js' => 'c3d216cb', 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => 'c0f1c3b5', 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '92904457', 'rsrc/js/application/differential/behavior-populate.js' => '00d88bc4', @@ -624,7 +624,7 @@ return array( 'javelin-behavior-diff-preview-link' => '051c7832', 'javelin-behavior-differential-comment-jump' => '4fdb476d', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', - 'javelin-behavior-differential-dropdown-menus' => 'f45a2836', + 'javelin-behavior-differential-dropdown-menus' => 'c3d216cb', 'javelin-behavior-differential-edit-inline-comments' => 'c0f1c3b5', 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-keyboard-navigation' => '92904457', @@ -1942,6 +1942,18 @@ return array( 'javelin-vector', 'differential-inline-comment-editor', ), + 'c3d216cb' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-workflow', + 'phuix-dropdown-menu', + 'phuix-action-list-view', + 'phuix-action-view', + 'phabricator-phtize', + 'phabricator-diff-changeset', + ), 'c420b0b9' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -2199,18 +2211,6 @@ return array( 'f12cbc9f' => array( 'phui-oi-list-view-css', ), - 'f45a2836' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-workflow', - 'phuix-dropdown-menu', - 'phuix-action-list-view', - 'phuix-action-view', - 'phabricator-phtize', - 'phabricator-diff-changeset', - ), 'f50152ad' => array( 'phui-timeline-view-css', ), diff --git a/src/infrastructure/diff/view/PHUIDiffTableOfContentsListView.php b/src/infrastructure/diff/view/PHUIDiffTableOfContentsListView.php index e6ca6cc53d..47c8f633e7 100644 --- a/src/infrastructure/diff/view/PHUIDiffTableOfContentsListView.php +++ b/src/infrastructure/diff/view/PHUIDiffTableOfContentsListView.php @@ -103,22 +103,6 @@ final class PHUIDiffTableOfContentsListView extends AphrontView { } } - $reveal_link = javelin_tag( - 'a', - array( - 'sigil' => 'differential-reveal-all', - 'mustcapture' => true, - 'class' => 'button differential-toc-reveal-all', - ), - pht('Show All Context')); - - $buttons = phutil_tag( - 'div', - array( - 'class' => 'differential-toc-buttons grouped', - ), - $reveal_link); - $table = id(new AphrontTableView($rows)) ->setRowClasses($rowc) ->setHeaders( @@ -185,8 +169,7 @@ final class PHUIDiffTableOfContentsListView extends AphrontView { ->setHeader($header) ->setBackground($this->background) ->setTable($table) - ->appendChild($anchor) - ->appendChild($buttons); + ->appendChild($anchor); if ($this->infoView) { $box->setInfoView($this->infoView); diff --git a/webroot/rsrc/js/application/differential/behavior-dropdown-menus.js b/webroot/rsrc/js/application/differential/behavior-dropdown-menus.js index ab91818143..822f1b5ac4 100644 --- a/webroot/rsrc/js/application/differential/behavior-dropdown-menus.js +++ b/webroot/rsrc/js/application/differential/behavior-dropdown-menus.js @@ -31,20 +31,6 @@ JX.behavior('differential-dropdown-menus', function(config) { } } - JX.Stratcom.listen( - 'click', - 'differential-reveal-all', - function(e) { - var containers = JX.DOM.scry( - JX.$('differential-review-stage'), - 'div', - 'differential-changeset'); - for (var i=0; i < containers.length; i++) { - show_more(containers[i]); - } - e.kill(); - }); - var buildmenu = function(e) { var button = e.getNode('differential-view-options'); var data = JX.Stratcom.getData(button); From 64a54aac9d572e7d151382ab06f5a802188752fa Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 8 May 2017 10:20:59 -0700 Subject: [PATCH 15/81] Merge "differential-dropdown-menus" behavior into DiffChangesetList Summary: Ref T12616. This ends up being a little messy ("one giant function") and maybe I'll clean it up a bit later, but continue consolidating the wild jungle of behaviors into a smaller set of responsible objects. Test Plan: Clicked all the menu options, saw them work properly. Grepped for removed methods. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616 Differential Revision: https://secure.phabricator.com/D17845 --- resources/celerity/map.php | 71 ++---- resources/celerity/packages.php | 1 - .../view/DifferentialChangesetListView.php | 45 ++-- .../rsrc/js/application/diff/DiffChangeset.js | 14 + .../js/application/diff/DiffChangesetList.js | 220 ++++++++++++++++ .../differential/behavior-dropdown-menus.js | 240 ------------------ .../differential/behavior-populate.js | 3 +- 7 files changed, 285 insertions(+), 309 deletions(-) delete mode 100644 webroot/rsrc/js/application/differential/behavior-dropdown-menus.js diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 6107ae0e71..4d571aa9e9 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '2ff7879f', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '90b30783', - 'differential.pkg.js' => 'ef6c7cfc', + 'differential.pkg.js' => '51d9bebe', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -390,17 +390,16 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => 'a1189df6', - 'rsrc/js/application/diff/DiffChangesetList.js' => '2329e40e', + 'rsrc/js/application/diff/DiffChangeset.js' => 'ed7bc580', + 'rsrc/js/application/diff/DiffChangesetList.js' => 'f9ea2d8b', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '2e3f9738', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', - 'rsrc/js/application/differential/behavior-dropdown-menus.js' => 'c3d216cb', 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => 'c0f1c3b5', 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '92904457', - 'rsrc/js/application/differential/behavior-populate.js' => '00d88bc4', + 'rsrc/js/application/differential/behavior-populate.js' => '7356b23d', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', 'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => 'c93358e3', @@ -624,11 +623,10 @@ return array( 'javelin-behavior-diff-preview-link' => '051c7832', 'javelin-behavior-differential-comment-jump' => '4fdb476d', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', - 'javelin-behavior-differential-dropdown-menus' => 'c3d216cb', 'javelin-behavior-differential-edit-inline-comments' => 'c0f1c3b5', 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-keyboard-navigation' => '92904457', - 'javelin-behavior-differential-populate' => '00d88bc4', + 'javelin-behavior-differential-populate' => '7356b23d', 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', 'javelin-behavior-differential-user-select' => 'a8d8459d', 'javelin-behavior-diffusion-browse-file' => '054a0f0b', @@ -786,8 +784,8 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => 'a1189df6', - 'phabricator-diff-changeset-list' => '2329e40e', + 'phabricator-diff-changeset' => 'ed7bc580', + 'phabricator-diff-changeset-list' => 'f9ea2d8b', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -922,14 +920,6 @@ return array( 'unhandled-exception-css' => '4c96257a', ), 'requires' => array( - '00d88bc4' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - 'phabricator-tooltip', - 'phabricator-diff-changeset-list', - 'phabricator-diff-changeset', - ), '013ffff9' => array( 'javelin-install', 'javelin-util', @@ -1087,9 +1077,6 @@ return array( 'javelin-workflow', 'javelin-util', ), - '2329e40e' => array( - 'javelin-install', - ), 26167537 => array( 'javelin-install', 'javelin-dom', @@ -1471,6 +1458,14 @@ return array( 'javelin-behavior', 'javelin-dom', ), + '7356b23d' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-stratcom', + 'phabricator-tooltip', + 'phabricator-diff-changeset-list', + 'phabricator-diff-changeset', + ), '73d09eef' => array( 'javelin-behavior', 'javelin-vector', @@ -1706,16 +1701,6 @@ return array( 'javelin-util', 'phabricator-keyboard-shortcut', ), - 'a1189df6' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - ), 'a155550f' => array( 'javelin-install', 'javelin-dom', @@ -1942,18 +1927,6 @@ return array( 'javelin-vector', 'differential-inline-comment-editor', ), - 'c3d216cb' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-workflow', - 'phuix-dropdown-menu', - 'phuix-action-list-view', - 'phuix-action-view', - 'phabricator-phtize', - 'phabricator-diff-changeset', - ), 'c420b0b9' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -2182,6 +2155,16 @@ return array( 'javelin-dom', 'phabricator-draggable-list', ), + 'ed7bc580' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + ), 'eded9ee8' => array( 'javelin-behavior', 'javelin-typeahead-ondemand-source', @@ -2239,6 +2222,9 @@ return array( 'javelin-install', 'javelin-dom', ), + 'f9ea2d8b' => array( + 'javelin-install', + ), 'fbe497e7' => array( 'javelin-behavior', 'javelin-util', @@ -2465,7 +2451,6 @@ return array( 'javelin-behavior-repository-crossreference', 'javelin-behavior-load-blame', 'differential-inline-comment-editor', - 'javelin-behavior-differential-dropdown-menus', 'javelin-behavior-differential-toggle-files', 'javelin-behavior-differential-user-select', 'javelin-behavior-aphront-more', diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php index ed281822a8..cac2eff5f8 100644 --- a/resources/celerity/packages.php +++ b/resources/celerity/packages.php @@ -204,7 +204,6 @@ return array( 'javelin-behavior-load-blame', 'differential-inline-comment-editor', - 'javelin-behavior-differential-dropdown-menus', 'javelin-behavior-differential-toggle-files', 'javelin-behavior-differential-user-select', 'javelin-behavior-aphront-more', diff --git a/src/applications/differential/view/DifferentialChangesetListView.php b/src/applications/differential/view/DifferentialChangesetListView.php index 533f579b0a..66ce4fa403 100644 --- a/src/applications/differential/view/DifferentialChangesetListView.php +++ b/src/applications/differential/view/DifferentialChangesetListView.php @@ -138,29 +138,6 @@ final class DifferentialChangesetListView extends AphrontView { ), )); - Javelin::initBehavior( - 'differential-dropdown-menus', - array( - 'pht' => array( - 'Open in Editor' => pht('Open in Editor'), - 'Show All Context' => pht('Show All Context'), - 'All Context Shown' => pht('All Context Shown'), - "Can't Toggle Unloaded File" => pht("Can't Toggle Unloaded File"), - 'Expand File' => pht('Expand File'), - 'Collapse File' => pht('Collapse File'), - 'Browse in Diffusion' => pht('Browse in Diffusion'), - 'View Standalone' => pht('View Standalone'), - 'Show Raw File (Left)' => pht('Show Raw File (Left)'), - 'Show Raw File (Right)' => pht('Show Raw File (Right)'), - 'Configure Editor' => pht('Configure Editor'), - 'Load Changes' => pht('Load Changes'), - 'View Side-by-Side' => pht('View Side-by-Side'), - 'View Unified' => pht('View Unified'), - 'Change Text Encoding...' => pht('Change Text Encoding...'), - 'Highlight As...' => pht('Highlight As...'), - ), - )); - $renderer = DifferentialChangesetParser::getDefaultRendererForViewer( $viewer); @@ -238,8 +215,28 @@ final class DifferentialChangesetListView extends AphrontView { $this->requireResource('aphront-tooltip-css'); - $this->initBehavior('differential-populate', array( + $this->initBehavior( + 'differential-populate', + array( 'changesetViewIDs' => $ids, + 'pht' => array( + 'Open in Editor' => pht('Open in Editor'), + 'Show All Context' => pht('Show All Context'), + 'All Context Shown' => pht('All Context Shown'), + "Can't Toggle Unloaded File" => pht("Can't Toggle Unloaded File"), + 'Expand File' => pht('Expand File'), + 'Collapse File' => pht('Collapse File'), + 'Browse in Diffusion' => pht('Browse in Diffusion'), + 'View Standalone' => pht('View Standalone'), + 'Show Raw File (Left)' => pht('Show Raw File (Left)'), + 'Show Raw File (Right)' => pht('Show Raw File (Right)'), + 'Configure Editor' => pht('Configure Editor'), + 'Load Changes' => pht('Load Changes'), + 'View Side-by-Side' => pht('View Side-by-Side'), + 'View Unified' => pht('View Unified'), + 'Change Text Encoding...' => pht('Change Text Encoding...'), + 'Highlight As...' => pht('Highlight As...'), + ), )); $this->initBehavior('differential-comment-jump', array()); diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index 27058dbc42..c61be63d77 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -174,6 +174,20 @@ JX.install('DiffChangeset', { return this; }, + loadAllContext: function() { + var nodes = JX.DOM.scry(this._node, 'tr', 'context-target'); + for (var ii = 0; ii < nodes.length; ii++) { + var show = JX.DOM.scry(nodes[ii], 'a', 'show-more'); + for (var jj = 0; jj < show.length; jj++) { + var data = JX.Stratcom.getData(show[jj]); + if (data.type != 'all') { + continue; + } + this.loadContext(data.range, nodes[ii], true); + } + } + }, + _startContentWorkflow: function(workflow) { var routable = workflow.getRoutable(); diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index c8282dba32..2c8710dd2c 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -13,6 +13,13 @@ JX.install('DiffChangesetList', { var onmore = JX.bind(this, this._ifawake, this._onmore); JX.Stratcom.listen('click', 'show-more', onmore); + + var onmenu = JX.bind(this, this._ifawake, this._onmenu); + JX.Stratcom.listen('click', 'differential-view-options', onmenu); + }, + + properties: { + translations: null }, members: { @@ -80,8 +87,221 @@ JX.install('DiffChangesetList', { var target = e.getNode('context-target'); changeset.loadContext(data.range, target); + }, + + _onmenu: function(e) { + var button = e.getNode('differential-view-options'); + + var data = JX.Stratcom.getData(button); + if (data.menu) { + // We've already built this menu, so we can let the menu itself handle + // the event. + return; + } + + e.prevent(); + + var pht = this.getTranslations(); + + var node = JX.DOM.findAbove( + button, + 'div', + 'differential-changeset'); + + var changeset = this.getChangesetForNode(node); + + var menu = new JX.PHUIXDropdownMenu(button); + var list = new JX.PHUIXActionListView(); + + var add_link = function(icon, name, href, local) { + if (!href) { + return; + } + + var link = new JX.PHUIXActionView() + .setIcon(icon) + .setName(name) + .setHref(href) + .setHandler(function(e) { + if (local) { + window.location.assign(href); + } else { + window.open(href); + } + menu.close(); + e.prevent(); + }); + + list.addItem(link); + return link; + }; + + var reveal_item = new JX.PHUIXActionView() + .setIcon('fa-eye'); + list.addItem(reveal_item); + + var visible_item = new JX.PHUIXActionView() + .setHandler(function(e) { + var diff = JX.DOM.scry( + JX.$(data.containerID), + 'table', + 'differential-diff'); + + JX.Stratcom.invoke('differential-toggle-file', null, {diff: diff}); + e.prevent(); + menu.close(); + }); + list.addItem(visible_item); + + add_link('fa-file-text', pht('Browse in Diffusion'), data.diffusionURI); + add_link('fa-file-o', pht('View Standalone'), data.standaloneURI); + + var up_item = new JX.PHUIXActionView() + .setHandler(function(e) { + if (changeset.isLoaded()) { + var renderer = changeset.getRenderer(); + if (renderer == '1up') { + renderer = '2up'; + } else { + renderer = '1up'; + } + changeset.setRenderer(renderer); + } + changeset.reload(); + + e.prevent(); + menu.close(); + }); + list.addItem(up_item); + + var encoding_item = new JX.PHUIXActionView() + .setIcon('fa-font') + .setName(pht('Change Text Encoding...')) + .setHandler(function(e) { + var params = { + encoding: changeset.getEncoding() + }; + + new JX.Workflow('/services/encoding/', params) + .setHandler(function(r) { + changeset.setEncoding(r.encoding); + changeset.reload(); + }) + .start(); + + e.prevent(); + menu.close(); + }); + list.addItem(encoding_item); + + var highlight_item = new JX.PHUIXActionView() + .setIcon('fa-sun-o') + .setName(pht('Highlight As...')) + .setHandler(function(e) { + var params = { + highlight: changeset.getHighlight() + }; + + new JX.Workflow('/services/highlight/', params) + .setHandler(function(r) { + changeset.setHighlight(r.highlight); + changeset.reload(); + }) + .start(); + + e.prevent(); + menu.close(); + }); + list.addItem(highlight_item); + + add_link('fa-arrow-left', pht('Show Raw File (Left)'), data.leftURI); + add_link('fa-arrow-right', pht('Show Raw File (Right)'), data.rightURI); + add_link('fa-pencil', pht('Open in Editor'), data.editor, true); + add_link('fa-wrench', pht('Configure Editor'), data.editorConfigure); + + menu.setContent(list.getNode()); + + menu.listen('open', function() { + // When the user opens the menu, check if there are any "Show More" + // links in the changeset body. If there aren't, disable the "Show + // Entire File" menu item since it won't change anything. + + var nodes = JX.DOM.scry(JX.$(data.containerID), 'a', 'show-more'); + if (nodes.length) { + reveal_item + .setDisabled(false) + .setName(pht('Show All Context')) + .setIcon('fa-file-o') + .setHandler(function(e) { + changeset.loadAllContext(); + e.prevent(); + menu.close(); + }); + } else { + reveal_item + .setDisabled(true) + .setIcon('fa-file') + .setName(pht('All Context Shown')) + .setHandler(function(e) { e.prevent(); }); + } + + encoding_item.setDisabled(!changeset.isLoaded()); + highlight_item.setDisabled(!changeset.isLoaded()); + + if (changeset.isLoaded()) { + if (changeset.getRenderer() == '2up') { + up_item + .setIcon('fa-list-alt') + .setName(pht('View Unified')); + } else { + up_item + .setIcon('fa-files-o') + .setName(pht('View Side-by-Side')); + } + } else { + up_item + .setIcon('fa-refresh') + .setName(pht('Load Changes')); + } + + visible_item + .setDisabled(true) + .setIcon('fa-expand') + .setName(pht('Can\'t Toggle Unloaded File')); + var diffs = JX.DOM.scry( + JX.$(data.containerID), + 'table', + 'differential-diff'); + + if (diffs.length > 1) { + JX.$E( + 'More than one node with sigil "differential-diff" was found in "'+ + data.containerID+'."'); + } else if (diffs.length == 1) { + var diff = diffs[0]; + visible_item.setDisabled(false); + if (JX.Stratcom.getData(diff).hidden) { + visible_item + .setName(pht('Expand File')) + .setIcon('fa-expand'); + } else { + visible_item + .setName(pht('Collapse File')) + .setIcon('fa-compress'); + } + } else { + // Do nothing when there is no diff shown in the table. For example, + // the file is binary. + } + + }); + + data.menu = menu; + menu.open(); } + + } }); diff --git a/webroot/rsrc/js/application/differential/behavior-dropdown-menus.js b/webroot/rsrc/js/application/differential/behavior-dropdown-menus.js deleted file mode 100644 index 822f1b5ac4..0000000000 --- a/webroot/rsrc/js/application/differential/behavior-dropdown-menus.js +++ /dev/null @@ -1,240 +0,0 @@ -/** - * @provides javelin-behavior-differential-dropdown-menus - * @requires javelin-behavior - * javelin-dom - * javelin-util - * javelin-stratcom - * javelin-workflow - * phuix-dropdown-menu - * phuix-action-list-view - * phuix-action-view - * phabricator-phtize - * phabricator-diff-changeset - */ - -JX.behavior('differential-dropdown-menus', function(config) { - var pht = JX.phtize(config.pht); - - function show_more(container) { - var view = JX.DiffChangeset.getForNode(container); - - var nodes = JX.DOM.scry(container, 'tr', 'context-target'); - for (var ii = 0; ii < nodes.length; ii++) { - var show = JX.DOM.scry(nodes[ii], 'a', 'show-more'); - for (var jj = 0; jj < show.length; jj++) { - var data = JX.Stratcom.getData(show[jj]); - if (data.type != 'all') { - continue; - } - view.loadContext(data.range, nodes[ii], true); - } - } - } - - var buildmenu = function(e) { - var button = e.getNode('differential-view-options'); - var data = JX.Stratcom.getData(button); - if (data.menu) { - return; - } - - e.prevent(); - - var changeset = JX.DOM.findAbove( - button, - 'div', - 'differential-changeset'); - - var view = JX.DiffChangeset.getForNode(changeset); - var menu = new JX.PHUIXDropdownMenu(button); - var list = new JX.PHUIXActionListView(); - - var add_link = function(icon, name, href, local) { - if (!href) { - return; - } - - var link = new JX.PHUIXActionView() - .setIcon(icon) - .setName(name) - .setHref(href) - .setHandler(function(e) { - if (local) { - window.location.assign(href); - } else { - window.open(href); - } - menu.close(); - e.prevent(); - }); - - list.addItem(link); - return link; - }; - - var reveal_item = new JX.PHUIXActionView() - .setIcon('fa-eye'); - list.addItem(reveal_item); - - var visible_item = new JX.PHUIXActionView() - .setHandler(function(e) { - var diff = JX.DOM.scry( - JX.$(data.containerID), - 'table', - 'differential-diff'); - - JX.Stratcom.invoke('differential-toggle-file', null, {diff: diff}); - e.prevent(); - menu.close(); - }); - list.addItem(visible_item); - - add_link('fa-file-text', pht('Browse in Diffusion'), data.diffusionURI); - add_link('fa-file-o', pht('View Standalone'), data.standaloneURI); - - var up_item = new JX.PHUIXActionView() - .setHandler(function(e) { - if (view.isLoaded()) { - var renderer = view.getRenderer(); - if (renderer == '1up') { - renderer = '2up'; - } else { - renderer = '1up'; - } - view.setRenderer(renderer); - } - view.reload(); - - e.prevent(); - menu.close(); - }); - list.addItem(up_item); - - var encoding_item = new JX.PHUIXActionView() - .setIcon('fa-font') - .setName(pht('Change Text Encoding...')) - .setHandler(function(e) { - var params = { - encoding: view.getEncoding() - }; - - new JX.Workflow('/services/encoding/', params) - .setHandler(function(r) { - view.setEncoding(r.encoding); - view.reload(); - }) - .start(); - - e.prevent(); - menu.close(); - }); - list.addItem(encoding_item); - - var highlight_item = new JX.PHUIXActionView() - .setIcon('fa-sun-o') - .setName(pht('Highlight As...')) - .setHandler(function(e) { - var params = { - highlight: view.getHighlight() - }; - - new JX.Workflow('/services/highlight/', params) - .setHandler(function(r) { - view.setHighlight(r.highlight); - view.reload(); - }) - .start(); - - e.prevent(); - menu.close(); - }); - list.addItem(highlight_item); - - add_link('fa-arrow-left', pht('Show Raw File (Left)'), data.leftURI); - add_link('fa-arrow-right', pht('Show Raw File (Right)'), data.rightURI); - add_link('fa-pencil', pht('Open in Editor'), data.editor, true); - add_link('fa-wrench', pht('Configure Editor'), data.editorConfigure); - - menu.setContent(list.getNode()); - - menu.listen('open', function() { - // When the user opens the menu, check if there are any "Show More" - // links in the changeset body. If there aren't, disable the "Show - // Entire File" menu item since it won't change anything. - - var nodes = JX.DOM.scry(JX.$(data.containerID), 'a', 'show-more'); - if (nodes.length) { - reveal_item - .setDisabled(false) - .setName(pht('Show All Context')) - .setIcon('fa-file-o') - .setHandler(function(e) { - show_more(JX.$(data.containerID)); - e.prevent(); - menu.close(); - }); - } else { - reveal_item - .setDisabled(true) - .setIcon('fa-file') - .setName(pht('All Context Shown')) - .setHandler(function(e) { e.prevent(); }); - } - - encoding_item.setDisabled(!view.isLoaded()); - highlight_item.setDisabled(!view.isLoaded()); - - if (view.isLoaded()) { - if (view.getRenderer() == '2up') { - up_item - .setIcon('fa-list-alt') - .setName(pht('View Unified')); - } else { - up_item - .setIcon('fa-files-o') - .setName(pht('View Side-by-Side')); - } - } else { - up_item - .setIcon('fa-refresh') - .setName(pht('Load Changes')); - } - - visible_item - .setDisabled(true) - .setIcon('fa-expand') - .setName(pht('Can\'t Toggle Unloaded File')); - var diffs = JX.DOM.scry( - JX.$(data.containerID), - 'table', - 'differential-diff'); - - if (diffs.length > 1) { - JX.$E( - 'More than one node with sigil "differential-diff" was found in "'+ - data.containerID+'."'); - } else if (diffs.length == 1) { - var diff = diffs[0]; - visible_item.setDisabled(false); - if (JX.Stratcom.getData(diff).hidden) { - visible_item - .setName(pht('Expand File')) - .setIcon('fa-expand'); - } else { - visible_item - .setName(pht('Collapse File')) - .setIcon('fa-compress'); - } - } else { - // Do nothing when there is no diff shown in the table. For example, - // the file is binary. - } - - }); - - data.menu = menu; - menu.open(); - }; - - JX.Stratcom.listen('click', 'differential-view-options', buildmenu); -}); diff --git a/webroot/rsrc/js/application/differential/behavior-populate.js b/webroot/rsrc/js/application/differential/behavior-populate.js index 4c6187189f..5fe219b00e 100644 --- a/webroot/rsrc/js/application/differential/behavior-populate.js +++ b/webroot/rsrc/js/application/differential/behavior-populate.js @@ -58,7 +58,8 @@ JX.behavior('differential-populate', function(config, statics) { }); } - var changeset_list = new JX.DiffChangesetList(); + var changeset_list = new JX.DiffChangesetList() + .setTranslations(JX.phtize(config.pht)); // Install and activate the current page. var page_id = JX.Quicksand.getCurrentPageID(); From fe44e987fb1677be2f42eb98663b2cb313bdf2f2 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 8 May 2017 11:27:11 -0700 Subject: [PATCH 16/81] Translate "Loading..." text in inline comments Summary: Ref T12616. This cements the relationship between ChangesetList (parent container) and Changeset (child) and passes translations down so Changeset can use them to translate the text "Loading..." Test Plan: Viewed loading changes. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616 Differential Revision: https://secure.phabricator.com/D17846 --- resources/celerity/map.php | 56 +++++++++---------- .../view/DifferentialChangesetListView.php | 2 + .../rsrc/js/application/diff/DiffChangeset.js | 12 +++- .../js/application/diff/DiffChangesetList.js | 11 ++++ .../differential/behavior-populate.js | 8 ++- 5 files changed, 55 insertions(+), 34 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 4d571aa9e9..f7315a9829 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '2ff7879f', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '90b30783', - 'differential.pkg.js' => '51d9bebe', + 'differential.pkg.js' => '2de0157a', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -390,8 +390,8 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => 'ed7bc580', - 'rsrc/js/application/diff/DiffChangesetList.js' => 'f9ea2d8b', + 'rsrc/js/application/diff/DiffChangeset.js' => '2cbf5575', + 'rsrc/js/application/diff/DiffChangesetList.js' => '16c14b02', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '2e3f9738', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', @@ -399,7 +399,7 @@ return array( 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => 'c0f1c3b5', 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '92904457', - 'rsrc/js/application/differential/behavior-populate.js' => '7356b23d', + 'rsrc/js/application/differential/behavior-populate.js' => '8991de30', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', 'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => 'c93358e3', @@ -626,7 +626,7 @@ return array( 'javelin-behavior-differential-edit-inline-comments' => 'c0f1c3b5', 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-keyboard-navigation' => '92904457', - 'javelin-behavior-differential-populate' => '7356b23d', + 'javelin-behavior-differential-populate' => '8991de30', 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', 'javelin-behavior-differential-user-select' => 'a8d8459d', 'javelin-behavior-diffusion-browse-file' => '054a0f0b', @@ -784,8 +784,8 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => 'ed7bc580', - 'phabricator-diff-changeset-list' => 'f9ea2d8b', + 'phabricator-diff-changeset' => '2cbf5575', + 'phabricator-diff-changeset-list' => '16c14b02', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -1004,6 +1004,9 @@ return array( 'javelin-dom', 'javelin-history', ), + '16c14b02' => array( + 'javelin-install', + ), '17bb8539' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1113,6 +1116,16 @@ return array( 'javelin-install', 'javelin-event', ), + '2cbf5575' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + ), '2e3f9738' => array( 'javelin-dom', 'javelin-util', @@ -1458,14 +1471,6 @@ return array( 'javelin-behavior', 'javelin-dom', ), - '7356b23d' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - 'phabricator-tooltip', - 'phabricator-diff-changeset-list', - 'phabricator-diff-changeset', - ), '73d09eef' => array( 'javelin-behavior', 'javelin-vector', @@ -1585,6 +1590,14 @@ return array( 'phabricator-draggable-list', 'javelin-workboard-column', ), + '8991de30' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-stratcom', + 'phabricator-tooltip', + 'phabricator-diff-changeset-list', + 'phabricator-diff-changeset', + ), '8a41885b' => array( 'javelin-install', 'javelin-dom', @@ -2155,16 +2168,6 @@ return array( 'javelin-dom', 'phabricator-draggable-list', ), - 'ed7bc580' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - ), 'eded9ee8' => array( 'javelin-behavior', 'javelin-typeahead-ondemand-source', @@ -2222,9 +2225,6 @@ return array( 'javelin-install', 'javelin-dom', ), - 'f9ea2d8b' => array( - 'javelin-install', - ), 'fbe497e7' => array( 'javelin-behavior', 'javelin-util', diff --git a/src/applications/differential/view/DifferentialChangesetListView.php b/src/applications/differential/view/DifferentialChangesetListView.php index 66ce4fa403..313bcbe694 100644 --- a/src/applications/differential/view/DifferentialChangesetListView.php +++ b/src/applications/differential/view/DifferentialChangesetListView.php @@ -236,6 +236,8 @@ final class DifferentialChangesetListView extends AphrontView { 'View Unified' => pht('View Unified'), 'Change Text Encoding...' => pht('Change Text Encoding...'), 'Highlight As...' => pht('Highlight As...'), + + 'Loading...' => pht('Loading...'), ), )); diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index c61be63d77..c8a8462519 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -26,6 +26,10 @@ JX.install('DiffChangeset', { this._loaded = data.loaded; }, + properties: { + changesetList: null + }, + members: { _node: null, _loaded: false, @@ -121,6 +125,7 @@ JX.install('DiffChangeset', { this._sequence++; var params = this._getViewParameters(); + var pht = this.getChangesetList().getTranslations(); var workflow = new JX.Workflow(this._renderURI, params) .setHandler(JX.bind(this, this._onresponse, this._sequence)); @@ -132,7 +137,7 @@ JX.install('DiffChangeset', { JX.$N( 'div', {className: 'differential-loading'}, - 'Loading...')); + pht('Loading...'))); return this; }, @@ -152,9 +157,10 @@ JX.install('DiffChangeset', { var params = this._getViewParameters(); params.range = range; + var pht = this.getChangesetList().getTranslations(); + var container = JX.DOM.scry(target, 'td')[0]; - // TODO: pht() - JX.DOM.setContent(container, 'Loading...'); + JX.DOM.setContent(container, pht('Loading...')); JX.DOM.alterClass(target, 'differential-show-more-loading', true); var workflow = new JX.Workflow(this._renderURI, params) diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index 2c8710dd2c..5bcbc63626 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -7,6 +7,7 @@ JX.install('DiffChangesetList', { construct: function() { + this._changesets = []; var onload = JX.bind(this, this._ifawake, this._onload); JX.Stratcom.listen('click', 'differential-load', onload); @@ -24,6 +25,7 @@ JX.install('DiffChangesetList', { members: { _asleep: true, + _changesets: null, sleep: function() { this._asleep = true; @@ -37,6 +39,15 @@ JX.install('DiffChangesetList', { return this._asleep; }, + newChangesetForNode: function(node) { + var changeset = JX.DiffChangeset.getForNode(node); + + this._changesets.push(changeset); + changeset.setChangesetList(this); + + return changeset; + }, + getChangesetForNode: function(node) { return JX.DiffChangeset.getForNode(node); }, diff --git a/webroot/rsrc/js/application/differential/behavior-populate.js b/webroot/rsrc/js/application/differential/behavior-populate.js index 5fe219b00e..02b8c1e896 100644 --- a/webroot/rsrc/js/application/differential/behavior-populate.js +++ b/webroot/rsrc/js/application/differential/behavior-populate.js @@ -70,9 +70,11 @@ JX.behavior('differential-populate', function(config, statics) { for (var ii = 0; ii < config.changesetViewIDs.length; ii++) { var id = config.changesetViewIDs[ii]; - var view = JX.DiffChangeset.getForNode(JX.$(id)); - if (view.shouldAutoload()) { - view.setStabilize(true).load(); + var node = JX.$(id); + + var changeset = changeset_list.newChangesetForNode(node); + if (changeset.shouldAutoload()) { + changeset.setStabilize(true).load(); } } From 4fd4ec3d275d0d90a429419e0c5a278df87b76da Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 9 May 2017 12:09:45 -0700 Subject: [PATCH 17/81] Hide inlines one-by-one, instead of in a big group Summary: Ref T12616. Fixes T12153. Currently, when you hide inlines, they hide completely and turn into a little bubble on the previous line. Instead, collapse them to a single line one-by-one. Narrowly, this fixes T12153. In the future, I plan to make these changes so this feature makes more sense: - Introduce global "hide everything" states (T8909) so you can completely hide stuff if you want, and this represents more of a halfway state between "nuke it" and "view it". - Make the actual rendering better, so it says "epriestley: blah blah..." instead of just "..." -- and looks less dumb. The real goal here is to introduce `DiffInline` and continue moving stuff from the tangled jungle of a million top-level behaviors to sensible smooth statefulness. Test Plan: - Hid and revealed inlines in unified and two-up modes. - These look pretty junk for now: {F4948659} Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616, T12153 Differential Revision: https://secure.phabricator.com/D17861 --- resources/celerity/map.php | 88 ++++++++++--------- .../DifferentialChangesetOneUpRenderer.php | 32 ------- .../DifferentialChangesetTwoUpRenderer.php | 25 ------ .../view/DifferentialChangesetDetailView.php | 2 +- .../view/DifferentialChangesetListView.php | 7 +- .../view/PHUIDiffInlineCommentDetailView.php | 15 +++- .../view/PHUIDiffInlineCommentRowScaffold.php | 23 +++-- .../view/PHUIDiffInlineCommentUndoView.php | 4 + .../diff/view/PHUIDiffInlineCommentView.php | 12 +++ .../PHUIDiffOneUpInlineCommentRowScaffold.php | 12 ++- .../diff/view/PHUIDiffRevealIconView.php | 6 +- .../PHUIDiffTwoUpInlineCommentRowScaffold.php | 13 ++- .../differential/phui-inline-comment.css | 35 ++++++-- .../rsrc/js/application/diff/DiffChangeset.js | 19 ++++ .../js/application/diff/DiffChangesetList.js | 31 ++++++- .../rsrc/js/application/diff/DiffInline.js | 56 ++++++++++++ .../behavior-edit-inline-comments.js | 83 ----------------- .../differential/behavior-populate.js | 4 +- 18 files changed, 250 insertions(+), 217 deletions(-) create mode 100644 webroot/rsrc/js/application/diff/DiffInline.js diff --git a/resources/celerity/map.php b/resources/celerity/map.php index f7315a9829..5dbb7d0a59 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -12,8 +12,8 @@ return array( 'core.pkg.css' => 'd1bf3405', 'core.pkg.js' => '2ff7879f', 'darkconsole.pkg.js' => '1f9a31bc', - 'differential.pkg.css' => '90b30783', - 'differential.pkg.js' => '2de0157a', + 'differential.pkg.css' => '58712637', + 'differential.pkg.js' => '70685319', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -66,7 +66,7 @@ return array( 'rsrc/css/application/differential/add-comment.css' => 'c47f8c40', 'rsrc/css/application/differential/changeset-view.css' => '41af6d25', 'rsrc/css/application/differential/core.css' => '5b7b8ff4', - 'rsrc/css/application/differential/phui-inline-comment.css' => 'be663c95', + 'rsrc/css/application/differential/phui-inline-comment.css' => '3fd8ca64', 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', 'rsrc/css/application/differential/revision-history.css' => '0e8eb855', 'rsrc/css/application/differential/revision-list.css' => 'f3c47d33', @@ -390,16 +390,17 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => '2cbf5575', - 'rsrc/js/application/diff/DiffChangesetList.js' => '16c14b02', + 'rsrc/js/application/diff/DiffChangeset.js' => '80ac3298', + 'rsrc/js/application/diff/DiffChangesetList.js' => 'a34b9821', + 'rsrc/js/application/diff/DiffInline.js' => 'f9e76f2d', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '2e3f9738', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', - 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => 'c0f1c3b5', + 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => 'e7e9551e', 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '92904457', - 'rsrc/js/application/differential/behavior-populate.js' => '8991de30', + 'rsrc/js/application/differential/behavior-populate.js' => '5e41c819', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', 'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => 'c93358e3', @@ -623,10 +624,10 @@ return array( 'javelin-behavior-diff-preview-link' => '051c7832', 'javelin-behavior-differential-comment-jump' => '4fdb476d', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', - 'javelin-behavior-differential-edit-inline-comments' => 'c0f1c3b5', + 'javelin-behavior-differential-edit-inline-comments' => 'e7e9551e', 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-keyboard-navigation' => '92904457', - 'javelin-behavior-differential-populate' => '8991de30', + 'javelin-behavior-differential-populate' => '5e41c819', 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', 'javelin-behavior-differential-user-select' => 'a8d8459d', 'javelin-behavior-diffusion-browse-file' => '054a0f0b', @@ -784,8 +785,9 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => '2cbf5575', - 'phabricator-diff-changeset-list' => '16c14b02', + 'phabricator-diff-changeset' => '80ac3298', + 'phabricator-diff-changeset-list' => 'a34b9821', + 'phabricator-diff-inline' => 'f9e76f2d', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -868,7 +870,7 @@ return array( 'phui-image-mask-css' => 'a8498f9c', 'phui-info-panel-css' => '27ea50a1', 'phui-info-view-css' => '6e217679', - 'phui-inline-comment-view-css' => 'be663c95', + 'phui-inline-comment-view-css' => '3fd8ca64', 'phui-invisible-character-view-css' => '6993d9f0', 'phui-lightbox-css' => '0a035e40', 'phui-list-view-css' => '12eb8ce6', @@ -1004,9 +1006,6 @@ return array( 'javelin-dom', 'javelin-history', ), - '16c14b02' => array( - 'javelin-install', - ), '17bb8539' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1116,16 +1115,6 @@ return array( 'javelin-install', 'javelin-event', ), - '2cbf5575' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - ), '2e3f9738' => array( 'javelin-dom', 'javelin-util', @@ -1388,6 +1377,14 @@ return array( 'phabricator-phtize', 'javelin-dom', ), + '5e41c819' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-stratcom', + 'phabricator-tooltip', + 'phabricator-diff-changeset-list', + 'phabricator-diff-changeset', + ), '5e9f347c' => array( 'javelin-behavior', 'multirow-row-manager', @@ -1538,6 +1535,17 @@ return array( 'javelin-vector', 'javelin-stratcom', ), + '80ac3298' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + 'phabricator-diff-inline', + ), '834a1173' => array( 'javelin-behavior', 'javelin-scrollbar', @@ -1590,14 +1598,6 @@ return array( 'phabricator-draggable-list', 'javelin-workboard-column', ), - '8991de30' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - 'phabricator-tooltip', - 'phabricator-diff-changeset-list', - 'phabricator-diff-changeset', - ), '8a41885b' => array( 'javelin-install', 'javelin-dom', @@ -1719,6 +1719,9 @@ return array( 'javelin-dom', 'javelin-reactor-dom', ), + 'a34b9821' => array( + 'javelin-install', + ), 'a3a63478' => array( 'phui-workcard-view-css', ), @@ -1932,14 +1935,6 @@ return array( 'javelin-install', 'javelin-dom', ), - 'c0f1c3b5' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - 'javelin-util', - 'javelin-vector', - 'differential-inline-comment-editor', - ), 'c420b0b9' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -2161,6 +2156,14 @@ return array( 'javelin-workflow', 'javelin-magical-init', ), + 'e7e9551e' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + 'javelin-util', + 'javelin-vector', + 'differential-inline-comment-editor', + ), 'e9581f08' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2225,6 +2228,9 @@ return array( 'javelin-install', 'javelin-dom', ), + 'f9e76f2d' => array( + 'javelin-dom', + ), 'fbe497e7' => array( 'javelin-behavior', 'javelin-util', diff --git a/src/applications/differential/render/DifferentialChangesetOneUpRenderer.php b/src/applications/differential/render/DifferentialChangesetOneUpRenderer.php index cbb5cbc6f4..7a694cfd98 100644 --- a/src/applications/differential/render/DifferentialChangesetOneUpRenderer.php +++ b/src/applications/differential/render/DifferentialChangesetOneUpRenderer.php @@ -41,8 +41,6 @@ final class DifferentialChangesetOneUpRenderer $column_width = 4; - $hidden = new PHUIDiffRevealIconView(); - $out = array(); foreach ($primitives as $k => $p) { $type = $p['type']; @@ -53,27 +51,6 @@ final class DifferentialChangesetOneUpRenderer case 'new-file': $is_old = ($type == 'old' || $type == 'old-file'); - $o_hidden = array(); - $n_hidden = array(); - - for ($look = $k + 1; isset($primitives[$look]); $look++) { - $next = $primitives[$look]; - switch ($next['type']) { - case 'inline': - $comment = $next['comment']; - if ($comment->isHidden()) { - if ($next['right']) { - $n_hidden[] = $comment; - } else { - $o_hidden[] = $comment; - } - } - break; - default: - break 2; - } - } - $cells = array(); if ($is_old) { if ($p['htype']) { @@ -93,9 +70,6 @@ final class DifferentialChangesetOneUpRenderer } $line = $p['line']; - if ($o_hidden) { - $line = array($hidden, $line); - } $cells[] = phutil_tag( 'th', @@ -122,9 +96,6 @@ final class DifferentialChangesetOneUpRenderer } $oline = $p['oline']; - if ($o_hidden) { - $oline = array($hidden, $oline); - } $cells[] = phutil_tag('th', array('id' => $left_id), $oline); } @@ -140,9 +111,6 @@ final class DifferentialChangesetOneUpRenderer } $line = $p['line']; - if ($n_hidden) { - $line = array($hidden, $line); - } $cells[] = phutil_tag( 'th', diff --git a/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php b/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php index 9e4f9c049d..688544513e 100644 --- a/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php +++ b/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php @@ -69,8 +69,6 @@ final class DifferentialChangesetTwoUpRenderer $depths = $this->getDepths(); $mask = $this->getMask(); - $hidden = new PHUIDiffRevealIconView(); - for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) { if (empty($mask[$ii])) { // If we aren't going to show this line, we've just entered a gap. @@ -241,9 +239,6 @@ final class DifferentialChangesetTwoUpRenderer $new_comments = $this->getNewComments(); $scaffolds = array(); - $o_hidden = array(); - $n_hidden = array(); - if ($o_num && isset($old_comments[$o_num])) { foreach ($old_comments[$o_num] as $comment) { $inline = $this->buildInlineComment( @@ -251,10 +246,6 @@ final class DifferentialChangesetTwoUpRenderer $on_right = false); $scaffold = $this->getRowScaffoldForInline($inline); - if ($comment->isHidden()) { - $o_hidden[] = $comment; - } - if ($n_num && isset($new_comments[$n_num])) { foreach ($new_comments[$n_num] as $key => $new_comment) { if ($comment->isCompatible($new_comment)) { @@ -262,10 +253,6 @@ final class DifferentialChangesetTwoUpRenderer $new_comment, $on_right = true); - if ($new_comment->isHidden()) { - $n_hidden = $new_comment; - } - $scaffold->addInlineView($companion); unset($new_comments[$n_num][$key]); break; @@ -284,22 +271,10 @@ final class DifferentialChangesetTwoUpRenderer $comment, $on_right = true); - if ($comment->isHidden()) { - $n_hidden[] = $comment; - } - $scaffolds[] = $this->getRowScaffoldForInline($inline); } } - if ($o_hidden) { - $o_num = array($hidden, $o_num); - } - - if ($n_hidden) { - $n_num = array($hidden, $n_num); - } - // NOTE: This is a unicode zero-width space, which we use as a hint when // intercepting 'copy' events to make sure sensible text ends up on the // clipboard. See the 'phabricator-oncopy' behavior. diff --git a/src/applications/differential/view/DifferentialChangesetDetailView.php b/src/applications/differential/view/DifferentialChangesetDetailView.php index eed5467b63..219dfaaa1f 100644 --- a/src/applications/differential/view/DifferentialChangesetDetailView.php +++ b/src/applications/differential/view/DifferentialChangesetDetailView.php @@ -166,7 +166,7 @@ final class DifferentialChangesetDetailView extends AphrontView { 'ref' => $this->getRenderingRef(), 'autoload' => $this->getAutoload(), 'loaded' => $this->getLoaded(), - 'undoTemplates' => $renderer->renderUndoTemplates(), + 'undoTemplates' => hsprintf('%s', $renderer->renderUndoTemplates()), ), 'class' => $class, 'id' => $id, diff --git a/src/applications/differential/view/DifferentialChangesetListView.php b/src/applications/differential/view/DifferentialChangesetListView.php index 313bcbe694..eb60a3dee1 100644 --- a/src/applications/differential/view/DifferentialChangesetListView.php +++ b/src/applications/differential/view/DifferentialChangesetListView.php @@ -146,11 +146,6 @@ final class DifferentialChangesetListView extends AphrontView { foreach ($changesets as $key => $changeset) { $file = $changeset->getFilename(); - $class = 'differential-changeset'; - if (!$this->inlineURI) { - $class .= ' differential-changeset-noneditable'; - } - $ref = $this->references[$key]; $detail = id(new DifferentialChangesetDetailView()) @@ -219,6 +214,7 @@ final class DifferentialChangesetListView extends AphrontView { 'differential-populate', array( 'changesetViewIDs' => $ids, + 'inlineURI' => $this->inlineURI, 'pht' => array( 'Open in Editor' => pht('Open in Editor'), 'Show All Context' => pht('Show All Context'), @@ -247,7 +243,6 @@ final class DifferentialChangesetListView extends AphrontView { Javelin::initBehavior('differential-edit-inline-comments', array( 'uri' => $this->inlineURI, 'stage' => 'differential-review-stage', - 'revealIcon' => hsprintf('%s', new PHUIDiffRevealIconView()), )); } diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php index f01bfbdb65..9f5616f1cc 100644 --- a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php +++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php @@ -450,7 +450,20 @@ final class PHUIDiffInlineCommentDetailView phutil_tag_div('phabricator-remarkup', $content)), )); - return $markup; + $summary = phutil_tag( + 'div', + array( + 'class' => 'differential-inline-summary', + ), + + // TODO: Render something a little more useful here as a hint about the + // inline content, like "alincoln: first line of text...". + pht('...')); + + return array( + $markup, + $summary, + ); } private function canHide() { diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentRowScaffold.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentRowScaffold.php index b0c0b4ccac..7c6b1c54b5 100644 --- a/src/infrastructure/diff/view/PHUIDiffInlineCommentRowScaffold.php +++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentRowScaffold.php @@ -21,21 +21,28 @@ abstract class PHUIDiffInlineCommentRowScaffold extends AphrontView { } protected function getRowAttributes() { - // TODO: This is semantic information used by the JS when placing comments - // and using keyboard navigation; we should move it out of class names. - - $style = null; + $is_hidden = false; foreach ($this->getInlineViews() as $view) { if ($view->isHidden()) { - $style = 'display: none'; + $is_hidden = true; } } - return array( - 'class' => 'inline', + $classes = array(); + $classes[] = 'inline'; + if ($is_hidden) { + $classes[] = 'inline-hidden'; + } + + $result = array( + 'class' => implode(' ', $classes), 'sigil' => 'inline-row', - 'style' => $style, + 'meta' => array( + 'hidden' => $is_hidden, + ), ); + + return $result; } } diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentUndoView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentUndoView.php index c4bdd65bf8..4abdb00e0b 100644 --- a/src/infrastructure/diff/view/PHUIDiffInlineCommentUndoView.php +++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentUndoView.php @@ -9,6 +9,10 @@ final class PHUIDiffInlineCommentUndoView extends PHUIDiffInlineCommentView { + public function isHideable() { + return false; + } + public function render() { $link = javelin_tag( 'a', diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentView.php index b62160e232..e2c89e238c 100644 --- a/src/infrastructure/diff/view/PHUIDiffInlineCommentView.php +++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentView.php @@ -21,4 +21,16 @@ abstract class PHUIDiffInlineCommentView extends AphrontView { return false; } + public function isHideable() { + return true; + } + + public function newHiddenIcon() { + if ($this->isHideable()) { + return new PHUIDiffRevealIconView(); + } else { + return null; + } + } + } diff --git a/src/infrastructure/diff/view/PHUIDiffOneUpInlineCommentRowScaffold.php b/src/infrastructure/diff/view/PHUIDiffOneUpInlineCommentRowScaffold.php index 708b70b360..53c2255dc8 100644 --- a/src/infrastructure/diff/view/PHUIDiffOneUpInlineCommentRowScaffold.php +++ b/src/infrastructure/diff/view/PHUIDiffOneUpInlineCommentRowScaffold.php @@ -22,9 +22,17 @@ final class PHUIDiffOneUpInlineCommentRowScaffold 'id' => $inline->getScaffoldCellID(), ); + if ($inline->getIsOnRight()) { + $left_hidden = null; + $right_hidden = $inline->newHiddenIcon(); + } else { + $left_hidden = $inline->newHiddenIcon(); + $right_hidden = null; + } + $cells = array( - phutil_tag('th', array()), - phutil_tag('th', array()), + phutil_tag('th', array(), $left_hidden), + phutil_tag('th', array(), $right_hidden), phutil_tag('td', $attrs, $inline), ); diff --git a/src/infrastructure/diff/view/PHUIDiffRevealIconView.php b/src/infrastructure/diff/view/PHUIDiffRevealIconView.php index 284b72b2be..8ca3eae2c1 100644 --- a/src/infrastructure/diff/view/PHUIDiffRevealIconView.php +++ b/src/infrastructure/diff/view/PHUIDiffRevealIconView.php @@ -8,7 +8,7 @@ final class PHUIDiffRevealIconView extends AphrontView { ->addSigil('has-tooltip') ->setMetadata( array( - 'tip' => pht('Show Hidden Comments'), + 'tip' => pht('Show Hidden Comment'), 'align' => 'E', 'size' => 275, )); @@ -17,8 +17,8 @@ final class PHUIDiffRevealIconView extends AphrontView { 'a', array( 'href' => '#', - 'class' => 'reveal-inlines', - 'sigil' => 'reveal-inlines', + 'class' => 'reveal-inline', + 'sigil' => 'reveal-inline', 'mustcapture' => true, ), $icon); diff --git a/src/infrastructure/diff/view/PHUIDiffTwoUpInlineCommentRowScaffold.php b/src/infrastructure/diff/view/PHUIDiffTwoUpInlineCommentRowScaffold.php index 4fac5088d1..81b0edaf49 100644 --- a/src/infrastructure/diff/view/PHUIDiffTwoUpInlineCommentRowScaffold.php +++ b/src/infrastructure/diff/view/PHUIDiffTwoUpInlineCommentRowScaffold.php @@ -27,9 +27,15 @@ final class PHUIDiffTwoUpInlineCommentRowScaffold if ($inline->getIsOnRight()) { $left_side = null; $right_side = $inline; + + $left_hidden = null; + $right_hidden = $inline->newHiddenIcon(); } else { $left_side = $inline; $right_side = null; + + $left_hidden = $inline->newHiddenIcon(); + $right_hidden = null; } } else { list($u, $v) = $inlines; @@ -48,6 +54,9 @@ final class PHUIDiffTwoUpInlineCommentRowScaffold $left_side = $v; $right_side = $u; } + + $left_hidden = null; + $right_hidden = null; } $left_attrs = array( @@ -62,9 +71,9 @@ final class PHUIDiffTwoUpInlineCommentRowScaffold ); $cells = array( - phutil_tag('th', array()), + phutil_tag('th', array(), $left_hidden), phutil_tag('td', $left_attrs, $left_side), - phutil_tag('th', array()), + phutil_tag('th', array(), $right_hidden), phutil_tag('td', $right_attrs, $right_side), ); diff --git a/webroot/rsrc/css/application/differential/phui-inline-comment.css b/webroot/rsrc/css/application/differential/phui-inline-comment.css index f93b2c8d6c..3e28bcd0d4 100644 --- a/webroot/rsrc/css/application/differential/phui-inline-comment.css +++ b/webroot/rsrc/css/application/differential/phui-inline-comment.css @@ -374,17 +374,36 @@ /* - Hiding Inlines ------------------------------------------------------------ */ -.reveal-inlines { - float: left; - margin-left: 4px; +.reveal-inline { + color: {$lightbluetext}; + margin: 4px 0; + display: none; +} + +.inline-hidden .reveal-inline { + display: block; +} + +.inline-hidden .differential-inline-comment { + display: none; +} + +.differential-inline-summary { + background: {$greybackground}; + padding: 0 4px; + color: {$greytext}; + display: none; +} + +.inline-hidden .differential-inline-summary { + display: block; +} + +.reveal-inline span.phui-icon-view { color: {$lightbluetext}; } -.reveal-inlines span.phui-icon-view { - color: {$lightbluetext}; -} - -.reveal-inlines:hover span.phui-icon-view { +.reveal-inline:hover span.phui-icon-view { color: {$darkbluetext}; } diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index c8a8462519..107df87b0d 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -8,6 +8,8 @@ * javelin-router * javelin-behavior-device * javelin-vector + * phabricator-diff-inline + * @javelin */ @@ -24,6 +26,8 @@ JX.install('DiffChangeset', { this._highlight = data.highlight; this._encoding = data.encoding; this._loaded = data.loaded; + + this._inlines = []; }, properties: { @@ -44,6 +48,7 @@ JX.install('DiffChangeset', { _encoding: null, _undoTemplates: null, + _inlines: null, /** * Has the content of this changeset been loaded? @@ -401,6 +406,20 @@ JX.install('DiffChangeset', { _getRoutableKey: function() { return 'changeset-view.' + this._ref + '.' + this._sequence; + }, + + getInlineForRow: function(node) { + var data = JX.Stratcom.getData(node); + + if (!data.inline) { + var inline = new JX.DiffInline(node) + .setChangeset(this); + + this._inlines.push(inline); + data.inline = inline; + } + + return data.inline; } }, diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index 5bcbc63626..36208bb9bf 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -17,10 +17,17 @@ JX.install('DiffChangesetList', { var onmenu = JX.bind(this, this._ifawake, this._onmenu); JX.Stratcom.listen('click', 'differential-view-options', onmenu); + + var onhide = JX.bind(this, this._ifawake, this._onhide); + JX.Stratcom.listen('click', 'hide-inline', onhide); + + var onreveal = JX.bind(this, this._ifawake, this._onreveal); + JX.Stratcom.listen('click', 'reveal-inline', onreveal); }, properties: { - translations: null + translations: null, + inlineURI: null }, members: { @@ -309,10 +316,28 @@ JX.install('DiffChangesetList', { data.menu = menu; menu.open(); + }, + + _onhide: function(e) { + this._onhidereveal(e, true); + }, + + _onreveal: function(e) { + this._onhidereveal(e, false); + }, + + _onhidereveal: function(e, is_hide) { + e.kill(); + + var node = e.getNode('differential-changeset'); + var changeset = this.getChangesetForNode(node); + + var inline_node = e.getNode('inline-row'); + var inline = changeset.getInlineForRow(inline_node); + + inline.setHidden(is_hide); } - - } }); diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js new file mode 100644 index 0000000000..67f2e3f116 --- /dev/null +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -0,0 +1,56 @@ +/** + * @provides phabricator-diff-inline + * @requires javelin-dom + * @javelin + */ + +JX.install('DiffInline', { + + construct : function(row) { + this._row = row; + + var data = JX.Stratcom.getData(row); + this._hidden = data.hidden || false; + + // TODO: Get smarter about this once we do more editing, this is pretty + // hacky. + var comment = JX.DOM.find(row, 'div', 'differential-inline-comment'); + this._id = JX.Stratcom.getData(comment).id; + }, + + properties: { + changeset: null + }, + + members: { + _id: null, + _row: null, + _hidden: false, + + setHidden: function(hidden) { + this._hidden = hidden; + + JX.DOM.alterClass(this._row, 'inline-hidden', this._hidden); + + var op; + if (hidden) { + op = 'hide'; + } else { + op = 'show'; + } + + var inline_uri = this._getChangesetList().getInlineURI(); + var comment_id = this._id; + + new JX.Workflow(inline_uri, {op: op, ids: comment_id}) + .setHandler(JX.bag) + .start(); + }, + + _getChangesetList: function() { + var changeset = this.getChangeset(); + return changeset.getChangesetList(); + } + } + +}); diff --git a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js index 1859ca1754..942ef74775 100644 --- a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js +++ b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js @@ -425,87 +425,4 @@ JX.behavior('differential-edit-inline-comments', function(config) { handle_inline_action(data.node, data.op); }); - // Respond to the user clicking the "Hide Inline" button on an inline - // comment. - JX.Stratcom.listen('click', 'hide-inline', function(e) { - e.kill(); - - var row = e.getNode('inline-row'); - JX.DOM.hide(row); - - var prev = row.previousSibling; - while (prev && JX.Stratcom.hasSigil(prev, 'inline-row')) { - prev = prev.previousSibling; - } - - if (!prev) { - return; - } - - var comment = e.getNodeData('differential-inline-comment'); - - var slots = []; - for (var ii = 0; ii < prev.childNodes.length; ii++) { - if (JX.DOM.isType(prev.childNodes[ii], 'th')) { - slots.push(prev.childNodes[ii]); - } - } - - // Select the right-hand side if the comment is on the right. - var slot = (comment.on_right && slots[1]) || slots[0]; - - var reveal = JX.DOM.scry(slot, 'a', 'reveal-inlines')[0]; - if (!reveal) { - reveal = JX.$N( - 'a', - { - className: 'reveal-inlines', - sigil: 'reveal-inlines' - }, - JX.$H(config.revealIcon)); - - JX.DOM.prependContent(slot, reveal); - } - - new JX.Workflow(config.uri, {op: 'hide', ids: comment.id}) - .setHandler(JX.bag) - .start(); - }); - - JX.Stratcom.listen('click', 'reveal-inlines', function(e) { - e.kill(); - - var row = e.getNode('tag:tr'); - var next = row.nextSibling; - - var ids = []; - var ii; - - // Show any hidden inline comment rows directly below this one. - while (next && JX.Stratcom.hasSigil(next, 'inline-row')) { - JX.DOM.show(next); - - var comments = JX.DOM.scry(next, 'div', 'differential-inline-comment'); - for (ii = 0; ii < comments.length; ii++) { - var id = JX.Stratcom.getData(comments[ii]).id; - if (id) { - ids.push(id); - } - } - - next = next.nextSibling; - } - - // Remove any "reveal" icons on the row. - var reveals = JX.DOM.scry(row, 'a', 'reveal-inlines'); - for (ii = 0; ii < reveals.length; ii++) { - JX.DOM.remove(reveals[ii]); - } - - new JX.Workflow(config.uri, {op: 'show', ids: ids.join(',')}) - .setHandler(JX.bag) - .start(); - }); - - }); diff --git a/webroot/rsrc/js/application/differential/behavior-populate.js b/webroot/rsrc/js/application/differential/behavior-populate.js index 02b8c1e896..e5b0d5039d 100644 --- a/webroot/rsrc/js/application/differential/behavior-populate.js +++ b/webroot/rsrc/js/application/differential/behavior-populate.js @@ -59,7 +59,8 @@ JX.behavior('differential-populate', function(config, statics) { } var changeset_list = new JX.DiffChangesetList() - .setTranslations(JX.phtize(config.pht)); + .setTranslations(JX.phtize(config.pht)) + .setInlineURI(config.inlineURI); // Install and activate the current page. var page_id = JX.Quicksand.getCurrentPageID(); @@ -71,7 +72,6 @@ JX.behavior('differential-populate', function(config, statics) { for (var ii = 0; ii < config.changesetViewIDs.length; ii++) { var id = config.changesetViewIDs[ii]; var node = JX.$(id); - var changeset = changeset_list.newChangesetForNode(node); if (changeset.shouldAutoload()) { changeset.setStabilize(true).load(); From 798c8ba696433cf57fd09764a4b723df4a784377 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 15 May 2017 12:48:55 -0700 Subject: [PATCH 18/81] Mostly move inline editing to DiffInline Summary: Ref T12616. This doesn't pull over everything (some UI feedback didn't make it yet, and you can't cancel + undo cancelling edits yet) but editing comments technically works. This is a little shaky, but feels less shaky than every other approach I've tried, so I think I'm finally on a reasonable track here. Test Plan: Edited some inline comments. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616 Differential Revision: https://secure.phabricator.com/D17887 --- resources/celerity/map.php | 42 ++-- .../js/application/diff/DiffChangesetList.js | 33 +++- .../rsrc/js/application/diff/DiffInline.js | 180 +++++++++++++++++- .../behavior-edit-inline-comments.js | 37 ++-- 4 files changed, 249 insertions(+), 43 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 5dbb7d0a59..40f90cde8c 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '2ff7879f', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', - 'differential.pkg.js' => '70685319', + 'differential.pkg.js' => 'aa750623', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -391,14 +391,14 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', 'rsrc/js/application/diff/DiffChangeset.js' => '80ac3298', - 'rsrc/js/application/diff/DiffChangesetList.js' => 'a34b9821', - 'rsrc/js/application/diff/DiffInline.js' => 'f9e76f2d', + 'rsrc/js/application/diff/DiffChangesetList.js' => 'ecc9542e', + 'rsrc/js/application/diff/DiffInline.js' => '586c15ff', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '2e3f9738', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', - 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => 'e7e9551e', + 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '974bab6a', 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '92904457', 'rsrc/js/application/differential/behavior-populate.js' => '5e41c819', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', @@ -624,7 +624,7 @@ return array( 'javelin-behavior-diff-preview-link' => '051c7832', 'javelin-behavior-differential-comment-jump' => '4fdb476d', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', - 'javelin-behavior-differential-edit-inline-comments' => 'e7e9551e', + 'javelin-behavior-differential-edit-inline-comments' => '974bab6a', 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-keyboard-navigation' => '92904457', 'javelin-behavior-differential-populate' => '5e41c819', @@ -786,8 +786,8 @@ return array( 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', 'phabricator-diff-changeset' => '80ac3298', - 'phabricator-diff-changeset-list' => 'a34b9821', - 'phabricator-diff-inline' => 'f9e76f2d', + 'phabricator-diff-changeset-list' => 'ecc9542e', + 'phabricator-diff-inline' => '586c15ff', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -1347,6 +1347,9 @@ return array( 'javelin-vector', 'javelin-dom', ), + '586c15ff' => array( + 'javelin-dom', + ), '58dea2fa' => array( 'javelin-install', 'javelin-util', @@ -1672,6 +1675,14 @@ return array( 'javelin-mask', 'phabricator-drag-and-drop-file-upload', ), + '974bab6a' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + 'javelin-util', + 'javelin-vector', + 'differential-inline-comment-editor', + ), '988040b4' => array( 'javelin-install', 'javelin-dom', @@ -1719,9 +1730,6 @@ return array( 'javelin-dom', 'javelin-reactor-dom', ), - 'a34b9821' => array( - 'javelin-install', - ), 'a3a63478' => array( 'phui-workcard-view-css', ), @@ -2156,14 +2164,6 @@ return array( 'javelin-workflow', 'javelin-magical-init', ), - 'e7e9551e' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - 'javelin-util', - 'javelin-vector', - 'differential-inline-comment-editor', - ), 'e9581f08' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2171,6 +2171,9 @@ return array( 'javelin-dom', 'phabricator-draggable-list', ), + 'ecc9542e' => array( + 'javelin-install', + ), 'eded9ee8' => array( 'javelin-behavior', 'javelin-typeahead-ondemand-source', @@ -2228,9 +2231,6 @@ return array( 'javelin-install', 'javelin-dom', ), - 'f9e76f2d' => array( - 'javelin-dom', - ), 'fbe497e7' => array( 'javelin-behavior', 'javelin-util', diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index 36208bb9bf..701a9f8461 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -23,6 +23,12 @@ JX.install('DiffChangesetList', { var onreveal = JX.bind(this, this._ifawake, this._onreveal); JX.Stratcom.listen('click', 'reveal-inline', onreveal); + + var onedit = JX.bind(this, this._ifawake, this._onaction, 'edit'); + JX.Stratcom.listen( + 'click', + ['differential-inline-comment', 'differential-inline-edit'], + onedit); }, properties: { @@ -329,13 +335,32 @@ JX.install('DiffChangesetList', { _onhidereveal: function(e, is_hide) { e.kill(); + var inline = this._getInlineForEvent(e); + + inline.setHidden(is_hide); + }, + + _onaction: function(action, e) { + // TODO: This can become a kill once things fully switch over.. + e.prevent(); + + var inline = this._getInlineForEvent(e); + + // TODO: For normal operations, highlight the inline range here. + + switch (action) { + case 'edit': + inline.edit(); + break; + } + }, + + _getInlineForEvent: function(e) { var node = e.getNode('differential-changeset'); var changeset = this.getChangesetForNode(node); - var inline_node = e.getNode('inline-row'); - var inline = changeset.getInlineForRow(inline_node); - - inline.setHidden(is_hide); + var inline_row = e.getNode('inline-row'); + return changeset.getInlineForRow(inline_row); } } diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js index 67f2e3f116..c6bc71a5f4 100644 --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -9,13 +9,30 @@ JX.install('DiffInline', { construct : function(row) { this._row = row; - var data = JX.Stratcom.getData(row); - this._hidden = data.hidden || false; + var row_data = JX.Stratcom.getData(row); + this._hidden = row_data.hidden || false; // TODO: Get smarter about this once we do more editing, this is pretty // hacky. var comment = JX.DOM.find(row, 'div', 'differential-inline-comment'); - this._id = JX.Stratcom.getData(comment).id; + var data = JX.Stratcom.getData(comment); + + this._id = data.id; + + // TODO: This is very, very, very, very, very, very, very hacky. + var td = comment.parentNode; + var th = td.previousSibling; + if (th.parentNode.firstChild != th) { + this._displaySide = 'right'; + } else { + this._displaySide = 'left'; + } + + this._number = data.number; + this._length = data.length; + this._isNewFile = + (this.getDisplaySide() == 'right') || + (data.left != data.right); }, properties: { @@ -26,6 +43,10 @@ JX.install('DiffInline', { _id: null, _row: null, _hidden: false, + _number: null, + _length: null, + _displaySide: null, + _isNewFile: null, setHidden: function(hidden) { this._hidden = hidden; @@ -47,6 +68,159 @@ JX.install('DiffInline', { .start(); }, + edit: function() { + var handler = JX.bind(this, this._oneditresponse); + var uri = this.getChangeset().getChangesetList().getInlineURI(); + var data = this._newRequestData(); + + // TODO: Set state to "loading". + + new JX.Request(uri, handler) + .setData(data) + .send(); + }, + + getDisplaySide: function() { + return this._displaySide; + }, + + getLineNumber: function() { + return this._number; + }, + + getLineLength: function() { + return this._length; + }, + + isNewFile: function() { + return this._isNewFile; + }, + + _newRequestData: function() { + return { + op: 'edit', + id: this._id, + on_right: ((this.getDisplaySide() == 'right') ? 1 : 0), + renderer: this.getChangeset().getRenderer(), + number: this.getLineNumber(), + length: this.getLineLength(), + is_new: this.isNewFile(), + replyToCommentPHID: '' + }; + }, + + _oneditresponse: function(response) { + var rows = JX.$H(response).getNode(); + + this._drawEditRows(rows); + + // TODO: Set the row state to "hidden". + }, + + _drawEditRows: function(rows) { + var first_row = JX.DOM.scry(rows, 'tr')[0]; + var row = first_row; + var cursor = this._row; + + while (row) { + cursor.parentNode.insertBefore(row, cursor.nextSibling); + cursor = row; + + var row_meta = { + node: row, + type: 'edit', + listeners: [] + }; + + row_meta.listeners.push( + JX.DOM.listen( + row, + ['submit', 'didSyntheticSubmit'], + 'inline-edit-form', + JX.bind(this, this._onsubmit, row_meta))); + + row_meta.listeners.push( + JX.DOM.listen( + row, + 'click', + 'inline-edit-cancel', + JX.bind(this, this._oncancel, row_meta))); + + row = row.nextSibling; + } + + return first_row; + }, + + _onsubmit: function(row, e) { + e.kill(); + + var handler = JX.bind(this, this._onsubmitresponse, row); + + JX.Workflow.newFromForm(e.getTarget()) + .setHandler(handler) + .start(); + + // TODO: Set state to "loading". + }, + + _oncancel: function(row, e) { + e.kill(); + + // TODO: Capture edited text and offer "undo". + + JX.DOM.remove(row.node); + this._removeListeners(row.listeners); + + // TODO: Restore state to "normal". + }, + + _onsubmitresponse: function(row, response) { + + JX.DOM.remove(row.node); + this._removeListeners(row.listeners); + + // TODO: Restore state to "normal". + + this._onupdate(response); + }, + + _onupdate: function(response) { + var new_row; + if (response.markup) { + new_row = this._drawEditRows(JX.$H(response.markup).getNode()); + } + + // TODO: Save the old row so the action it's undo-able if it was a + // delete. + var remove_old = true; + if (remove_old) { + JX.DOM.remove(this._row); + } + + this._row = new_row; + + this._didUpdate(); + }, + + _didUpdate: function() { + // After making changes to inline comments, refresh the transaction + // preview at the bottom of the page. + + // TODO: This isn't the cleanest way to find the preview form, but + // rendering no longer has direct access to it. + var forms = JX.DOM.scry(document.body, 'form', 'transaction-append'); + if (forms.length) { + JX.DOM.invoke(forms[0], 'shouldRefresh'); + } + }, + + _removeListeners: function(listeners) { + for (var ii = 0; ii < listeners.length; ii++) { + listeners[ii].remove(); + } + }, + _getChangesetList: function() { var changeset = this.getChangeset(); return changeset.getChangesetList(); diff --git a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js index 942ef74775..904d8f655e 100644 --- a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js +++ b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js @@ -310,7 +310,9 @@ JX.behavior('differential-edit-inline-comments', function(config) { }); var action_handler = function(op, e) { - e.kill(); + // NOTE: We prevent this event, rather than killing it, because some + // actions are now handled by DiffChangesetList. + e.prevent(); if (editor) { return; @@ -392,20 +394,25 @@ JX.behavior('differential-edit-inline-comments', function(config) { 'differential-changeset'); var view = JX.DiffChangeset.getForNode(changeset_root); - editor = new JX.DifferentialInlineCommentEditor(config.uri) - .setTemplates(view.getUndoTemplates()) - .setOperation(op) - .setID(data.id) - .setChangesetID(data.changesetID) - .setLineNumber(data.number) - .setLength(data.length) - .setOnRight(data.on_right) - .setOriginalText(original) - .setRow(row) - .setTable(row.parentNode) - .setReplyToCommentPHID(reply_phid) - .setRenderer(view.getRenderer()) - .start(); + if (op == 'edit') { + // This is now handled by DiffChangesetList. + editor = true; + } else { + editor = new JX.DifferentialInlineCommentEditor(config.uri) + .setTemplates(view.getUndoTemplates()) + .setOperation(op) + .setID(data.id) + .setChangesetID(data.changesetID) + .setLineNumber(data.number) + .setLength(data.length) + .setOnRight(data.on_right) + .setOriginalText(original) + .setRow(row) + .setTable(row.parentNode) + .setReplyToCommentPHID(reply_phid) + .setRenderer(view.getRenderer()) + .start(); + } set_link_state(true); }; From 3c18cb77fb66b19c41f87754f183c15e18c4d32a Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 15 May 2017 13:08:55 -0700 Subject: [PATCH 19/81] Move inline "done" checkboxing to DiffInline Summary: Ref T12616. This updates clicking the "Done" checkbox for the new stuff. This one is pretty clean since the "Done" checkbox doesn't do too much weird magic. Test Plan: Clicked the box a few times. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616 Differential Revision: https://secure.phabricator.com/D17888 --- resources/celerity/map.php | 48 +++++++++---------- .../PhabricatorInlineCommentController.php | 4 ++ .../js/application/diff/DiffChangesetList.js | 9 ++++ .../rsrc/js/application/diff/DiffInline.js | 46 ++++++++++++++++-- .../DifferentialInlineCommentEditor.js | 22 --------- .../behavior-edit-inline-comments.js | 9 +--- 6 files changed, 80 insertions(+), 58 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 40f90cde8c..484d0ca1c5 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '2ff7879f', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', - 'differential.pkg.js' => 'aa750623', + 'differential.pkg.js' => 'e01579c4', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -391,14 +391,14 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', 'rsrc/js/application/diff/DiffChangeset.js' => '80ac3298', - 'rsrc/js/application/diff/DiffChangesetList.js' => 'ecc9542e', - 'rsrc/js/application/diff/DiffInline.js' => '586c15ff', + 'rsrc/js/application/diff/DiffChangesetList.js' => '12575699', + 'rsrc/js/application/diff/DiffInline.js' => 'bd1b3258', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', - 'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '2e3f9738', + 'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '9ed8d2b6', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', - 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '974bab6a', + 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '97f363fc', 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '92904457', 'rsrc/js/application/differential/behavior-populate.js' => '5e41c819', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', @@ -572,7 +572,7 @@ return array( 'd3' => 'a11a5ff2', 'differential-changeset-view-css' => '41af6d25', 'differential-core-view-css' => '5b7b8ff4', - 'differential-inline-comment-editor' => '2e3f9738', + 'differential-inline-comment-editor' => '9ed8d2b6', 'differential-revision-add-comment-css' => 'c47f8c40', 'differential-revision-comment-css' => '14b8565a', 'differential-revision-history-css' => '0e8eb855', @@ -624,7 +624,7 @@ return array( 'javelin-behavior-diff-preview-link' => '051c7832', 'javelin-behavior-differential-comment-jump' => '4fdb476d', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', - 'javelin-behavior-differential-edit-inline-comments' => '974bab6a', + 'javelin-behavior-differential-edit-inline-comments' => '97f363fc', 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-keyboard-navigation' => '92904457', 'javelin-behavior-differential-populate' => '5e41c819', @@ -786,8 +786,8 @@ return array( 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', 'phabricator-diff-changeset' => '80ac3298', - 'phabricator-diff-changeset-list' => 'ecc9542e', - 'phabricator-diff-inline' => '586c15ff', + 'phabricator-diff-changeset-list' => '12575699', + 'phabricator-diff-inline' => 'bd1b3258', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -1000,6 +1000,9 @@ return array( 'javelin-dom', 'javelin-typeahead-normalizer', ), + 12575699 => array( + 'javelin-install', + ), '1499a8cb' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1115,14 +1118,6 @@ return array( 'javelin-install', 'javelin-event', ), - '2e3f9738' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-request', - 'javelin-workflow', - ), '2ee659ce' => array( 'javelin-install', ), @@ -1347,9 +1342,6 @@ return array( 'javelin-vector', 'javelin-dom', ), - '586c15ff' => array( - 'javelin-dom', - ), '58dea2fa' => array( 'javelin-install', 'javelin-util', @@ -1675,7 +1667,7 @@ return array( 'javelin-mask', 'phabricator-drag-and-drop-file-upload', ), - '974bab6a' => array( + '97f363fc' => array( 'javelin-behavior', 'javelin-stratcom', 'javelin-dom', @@ -1711,6 +1703,14 @@ return array( '9d9685d6' => array( 'phui-oi-list-view-css', ), + '9ed8d2b6' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-request', + 'javelin-workflow', + ), '9f36c42d' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1910,6 +1910,9 @@ return array( 'javelin-vector', 'phui-hovercard', ), + 'bd1b3258' => array( + 'javelin-dom', + ), 'bdaf4d04' => array( 'javelin-behavior', 'javelin-dom', @@ -2171,9 +2174,6 @@ return array( 'javelin-dom', 'phabricator-draggable-list', ), - 'ecc9542e' => array( - 'javelin-install', - ), 'eded9ee8' => array( 'javelin-behavior', 'javelin-typeahead-ondemand-source', diff --git a/src/infrastructure/diff/PhabricatorInlineCommentController.php b/src/infrastructure/diff/PhabricatorInlineCommentController.php index 1c92b167b0..1c624c1ad8 100644 --- a/src/infrastructure/diff/PhabricatorInlineCommentController.php +++ b/src/infrastructure/diff/PhabricatorInlineCommentController.php @@ -130,12 +130,14 @@ abstract class PhabricatorInlineCommentController $inline = $this->loadCommentForDone($this->getCommentID()); $is_draft_state = false; + $is_checked = false; switch ($inline->getFixedState()) { case PhabricatorInlineCommentInterface::STATE_DRAFT: $next_state = PhabricatorInlineCommentInterface::STATE_UNDONE; break; case PhabricatorInlineCommentInterface::STATE_UNDRAFT: $next_state = PhabricatorInlineCommentInterface::STATE_DONE; + $is_checked = true; break; case PhabricatorInlineCommentInterface::STATE_DONE: $next_state = PhabricatorInlineCommentInterface::STATE_UNDRAFT; @@ -145,6 +147,7 @@ abstract class PhabricatorInlineCommentController case PhabricatorInlineCommentInterface::STATE_UNDONE: $next_state = PhabricatorInlineCommentInterface::STATE_DRAFT; $is_draft_state = true; + $is_checked = true; break; } @@ -153,6 +156,7 @@ abstract class PhabricatorInlineCommentController return id(new AphrontAjaxResponse()) ->setContent( array( + 'isChecked' => $is_checked, 'draftState' => $is_draft_state, )); case 'delete': diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index 701a9f8461..70b360d427 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -29,6 +29,12 @@ JX.install('DiffChangesetList', { 'click', ['differential-inline-comment', 'differential-inline-edit'], onedit); + + var ondone = JX.bind(this, this._ifawake, this._onaction, 'done'); + JX.Stratcom.listen( + 'click', + ['differential-inline-comment', 'differential-inline-done'], + ondone); }, properties: { @@ -352,6 +358,9 @@ JX.install('DiffChangesetList', { case 'edit': inline.edit(); break; + case 'done': + inline.toggleDone(); + break; } }, diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js index c6bc71a5f4..5566fe0f7c 100644 --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -60,7 +60,7 @@ JX.install('DiffInline', { op = 'show'; } - var inline_uri = this._getChangesetList().getInlineURI(); + var inline_uri = this._getInlineURI(); var comment_id = this._id; new JX.Workflow(inline_uri, {op: op, ids: comment_id}) @@ -68,9 +68,46 @@ JX.install('DiffInline', { .start(); }, + toggleDone: function() { + var uri = this._getInlineURI(); + var data = { + op: 'done', + id: this._id + }; + + var ondone = JX.bind(this, this._ondone); + + new JX.Workflow(uri, data) + .setHandler(ondone) + .start(); + }, + + _ondone: function(response) { + var checkbox = JX.DOM.find( + this._row, + 'input', + 'differential-inline-done'); + + checkbox.checked = (response.isChecked ? 'checked' : null); + + var comment = JX.DOM.findAbove( + checkbox, + 'div', + 'differential-inline-comment'); + + JX.DOM.alterClass(comment, 'inline-is-done', response.isChecked); + + // NOTE: This is marking the inline as having an unsubmitted checkmark, + // as opposed to a submitted checkmark. This is different from the + // top-level "draft" state of unsubmitted comments. + JX.DOM.alterClass(comment, 'inline-state-is-draft', response.draftState); + + this._didUpdate(); + }, + edit: function() { var handler = JX.bind(this, this._oneditresponse); - var uri = this.getChangeset().getChangesetList().getInlineURI(); + var uri = this._getInlineURI(); var data = this._newRequestData(); // TODO: Set state to "loading". @@ -221,9 +258,10 @@ JX.install('DiffInline', { } }, - _getChangesetList: function() { + _getInlineURI: function() { var changeset = this.getChangeset(); - return changeset.getChangesetList(); + var list = changeset.getChangesetList(); + return list.getInlineURI(); } } diff --git a/webroot/rsrc/js/application/differential/DifferentialInlineCommentEditor.js b/webroot/rsrc/js/application/differential/DifferentialInlineCommentEditor.js index 7e6015d969..7068e94c14 100644 --- a/webroot/rsrc/js/application/differential/DifferentialInlineCommentEditor.js +++ b/webroot/rsrc/js/application/differential/DifferentialInlineCommentEditor.js @@ -312,28 +312,6 @@ JX.install('DifferentialInlineCommentEditor', { .start(); }, - toggleCheckbox: function(id, checkbox) { - var data = { - op: 'done', - id: id - }; - - new JX.Workflow(this._uri, data) - .setHandler(JX.bind(this, function(r) { - checkbox.checked = !checkbox.checked; - - var comment = JX.DOM.findAbove( - checkbox, - 'div', - 'differential-inline-comment'); - JX.DOM.alterClass(comment, 'inline-is-done', !!checkbox.checked); - JX.DOM.alterClass(comment, 'inline-state-is-draft', r.draftState); - - this._didUpdate(); - })) - .start(); - }, - _didUpdate: function() { // After making changes to inline comments, refresh the transaction // preview at the bottom of the page. diff --git a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js index 904d8f655e..20eec1a3d5 100644 --- a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js +++ b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js @@ -371,13 +371,6 @@ JX.behavior('differential-edit-inline-comments', function(config) { } } - if (op == 'done') { - var checkbox = JX.DOM.find(node, 'input', 'differential-inline-done'); - new JX.DifferentialInlineCommentEditor(config.uri) - .toggleCheckbox(data.id, checkbox); - return; - } - var original = data.original; var reply_phid = null; if (op == 'reply') { @@ -417,7 +410,7 @@ JX.behavior('differential-edit-inline-comments', function(config) { set_link_state(true); }; - for (var op in {'edit': 1, 'delete': 1, 'reply': 1, 'done': 1}) { + for (var op in {'edit': 1, 'delete': 1, 'reply': 1}) { JX.Stratcom.listen( 'click', ['differential-inline-comment', 'differential-inline-' + op], From d97f80bc901832af7471d96fde7977ecfd4854ae Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 15 May 2017 15:03:30 -0700 Subject: [PATCH 20/81] Make new DiffInline code handle most "delete" operations Summary: Ref T12616. This moves the delete actions to the new, more stateful way of doing things. These are a little tricky because you can click "Delete" on an inline, but you can also click "Delete" from the preview area at the bottom of the page. If you do, the inline you are deleting may or may not be present on the page. This has a few bugs -- notably, deleting from the preview without interacting with the on-page inline first won't actually delete the on-page inline yet -- but nothing too serious. Test Plan: Deleted inlines, undid deletion, deleted from preview. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616 Differential Revision: https://secure.phabricator.com/D17890 --- resources/celerity/map.php | 68 +++--- .../rsrc/js/application/diff/DiffChangeset.js | 18 +- .../js/application/diff/DiffChangesetList.js | 76 +++++++ .../rsrc/js/application/diff/DiffInline.js | 213 ++++++++++++++---- .../behavior-edit-inline-comments.js | 75 ++---- 5 files changed, 312 insertions(+), 138 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 484d0ca1c5..5b33419132 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '2ff7879f', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', - 'differential.pkg.js' => 'e01579c4', + 'differential.pkg.js' => '271a1e1e', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -390,15 +390,15 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => '80ac3298', - 'rsrc/js/application/diff/DiffChangesetList.js' => '12575699', - 'rsrc/js/application/diff/DiffInline.js' => 'bd1b3258', + 'rsrc/js/application/diff/DiffChangeset.js' => '57b29223', + 'rsrc/js/application/diff/DiffChangesetList.js' => '50bc5b50', + 'rsrc/js/application/diff/DiffInline.js' => '64dfc791', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '9ed8d2b6', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', - 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '97f363fc', + 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '9d9dbc38', 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '92904457', 'rsrc/js/application/differential/behavior-populate.js' => '5e41c819', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', @@ -624,7 +624,7 @@ return array( 'javelin-behavior-diff-preview-link' => '051c7832', 'javelin-behavior-differential-comment-jump' => '4fdb476d', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', - 'javelin-behavior-differential-edit-inline-comments' => '97f363fc', + 'javelin-behavior-differential-edit-inline-comments' => '9d9dbc38', 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-keyboard-navigation' => '92904457', 'javelin-behavior-differential-populate' => '5e41c819', @@ -785,9 +785,9 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => '80ac3298', - 'phabricator-diff-changeset-list' => '12575699', - 'phabricator-diff-inline' => 'bd1b3258', + 'phabricator-diff-changeset' => '57b29223', + 'phabricator-diff-changeset-list' => '50bc5b50', + 'phabricator-diff-inline' => '64dfc791', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -1000,9 +1000,6 @@ return array( 'javelin-dom', 'javelin-typeahead-normalizer', ), - 12575699 => array( - 'javelin-install', - ), '1499a8cb' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1301,6 +1298,9 @@ return array( 'javelin-typeahead-source', 'javelin-util', ), + '50bc5b50' => array( + 'javelin-install', + ), '519705ea' => array( 'javelin-install', 'javelin-dom', @@ -1342,6 +1342,17 @@ return array( 'javelin-vector', 'javelin-dom', ), + '57b29223' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + 'phabricator-diff-inline', + ), '58dea2fa' => array( 'javelin-install', 'javelin-util', @@ -1411,6 +1422,9 @@ return array( 'javelin-workflow', 'javelin-dom', ), + '64dfc791' => array( + 'javelin-dom', + ), '680ea2c8' => array( 'javelin-install', 'javelin-dom', @@ -1530,17 +1544,6 @@ return array( 'javelin-vector', 'javelin-stratcom', ), - '80ac3298' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - 'phabricator-diff-inline', - ), '834a1173' => array( 'javelin-behavior', 'javelin-scrollbar', @@ -1667,14 +1670,6 @@ return array( 'javelin-mask', 'phabricator-drag-and-drop-file-upload', ), - '97f363fc' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - 'javelin-util', - 'javelin-vector', - 'differential-inline-comment-editor', - ), '988040b4' => array( 'javelin-install', 'javelin-dom', @@ -1703,6 +1698,14 @@ return array( '9d9685d6' => array( 'phui-oi-list-view-css', ), + '9d9dbc38' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + 'javelin-util', + 'javelin-vector', + 'differential-inline-comment-editor', + ), '9ed8d2b6' => array( 'javelin-dom', 'javelin-util', @@ -1910,9 +1913,6 @@ return array( 'javelin-vector', 'phui-hovercard', ), - 'bd1b3258' => array( - 'javelin-dom', - ), 'bdaf4d04' => array( 'javelin-behavior', 'javelin-dom', diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index 107df87b0d..b52e54aaed 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -322,7 +322,6 @@ JX.install('DiffChangeset', { return JX.Stratcom.getData(this._node); }, - _onresponse: function(sequence, response) { if (sequence != this._sequence) { // If this isn't the most recent request, ignore it. This normally @@ -420,8 +419,25 @@ JX.install('DiffChangeset', { } return data.inline; + }, + + getInlineByID: function(id) { + // TODO: Currently, this will only find inlines which the user has + // already interacted with! Inlines are built lazily as events arrive. + // This can not yet find inlines which are passively present in the + // document. + + for (var ii = 0; ii < this._inlines.length; ii++) { + var inline = this._inlines[ii]; + if (inline.getID() == id) { + return inline; + } + } + + return null; } + }, statics: { diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index 70b360d427..e46a33bb56 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -35,6 +35,12 @@ JX.install('DiffChangesetList', { 'click', ['differential-inline-comment', 'differential-inline-done'], ondone); + + var ondelete = JX.bind(this, this._ifawake, this._onaction, 'delete'); + JX.Stratcom.listen( + 'click', + ['differential-inline-comment', 'differential-inline-delete'], + ondelete); }, properties: { @@ -71,6 +77,19 @@ JX.install('DiffChangesetList', { return JX.DiffChangeset.getForNode(node); }, + getInlineByID: function(id) { + var inline = null; + + for (var ii = 0; ii < this._changesets.length; ii++) { + inline = this._changesets[ii].getInlineByID(id); + if (inline) { + break; + } + } + + return inline; + }, + _ifawake: function(f) { // This function takes another function and only calls it if the // changeset list is awake, so we basically just ignore events when we @@ -351,6 +370,33 @@ JX.install('DiffChangesetList', { e.prevent(); var inline = this._getInlineForEvent(e); + var is_ref = false; + + // If we don't have a natural inline object, the user may have clicked + // an action (like "Delete") inside a preview element at the bottom of + // the page. + + // If they did, try to find an associated normal inline to act on, and + // pretend they clicked that instead. This makes the overall state of + // the page more consistent. + + // However, there may be no normal inline (for example, because it is + // on a version of the diff which is not visible). In this case, we + // act by reference. + + if (inline === null) { + var data = e.getNodeData('differential-inline-comment'); + inline = this.getInlineByID(data.id); + if (inline) { + is_ref = true; + } else { + switch (action) { + case 'delete': + this._deleteInlineByID(data.id); + return; + } + } + } // TODO: For normal operations, highlight the inline range here. @@ -361,11 +407,41 @@ JX.install('DiffChangesetList', { case 'done': inline.toggleDone(); break; + case 'delete': + inline.delete(is_ref); + break; } }, + redrawPreview: function() { + // TODO: This isn't the cleanest way to find the preview form, but + // rendering no longer has direct access to it. + var forms = JX.DOM.scry(document.body, 'form', 'transaction-append'); + if (forms.length) { + JX.DOM.invoke(forms[0], 'shouldRefresh'); + } + }, + + _deleteInlineByID: function(id) { + var uri = this.getInlineURI(); + var data = { + op: 'refdelete', + id: id + }; + + var handler = JX.bind(this, this.redrawPreview); + + new JX.Workflow(uri, data) + .setHandler(handler) + .start(); + }, + _getInlineForEvent: function(e) { var node = e.getNode('differential-changeset'); + if (!node) { + return null; + } + var changeset = this.getChangesetForNode(node); var inline_row = e.getNode('inline-row'); diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js index 5566fe0f7c..efa16bf9cd 100644 --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -47,6 +47,11 @@ JX.install('DiffInline', { _length: null, _displaySide: null, _isNewFile: null, + _undoRow: null, + + _isDeleted: false, + _isInvisible: false, + _isLoading: false, setHidden: function(hidden) { this._hidden = hidden; @@ -106,17 +111,47 @@ JX.install('DiffInline', { }, edit: function() { - var handler = JX.bind(this, this._oneditresponse); var uri = this._getInlineURI(); - var data = this._newRequestData(); + var handler = JX.bind(this, this._oneditresponse); + var data = this._newRequestData('edit'); - // TODO: Set state to "loading". + this.setLoading(true); new JX.Request(uri, handler) .setData(data) .send(); }, + delete: function(is_ref) { + var uri = this._getInlineURI(); + var handler = JX.bind(this, this._ondeleteresponse); + + // NOTE: This may be a direct delete (the user clicked on the inline + // itself) or a "refdelete" (the user clicked somewhere else, like the + // preview, but the inline is present on the page). + + // For a "refdelete", we prompt the user to confirm that they want to + // delete the comment, because they can not undo deletions from the + // preview. We could jump the user to the inline instead, but this would + // be somewhat disruptive and make deleting several comments more + // difficult. + + var op; + if (is_ref) { + op = 'refdelete'; + } else { + op = 'delete'; + } + + var data = this._newRequestData(op); + + this.setLoading(true); + + new JX.Workflow(uri, data) + .setHandler(handler) + .start(); + }, + getDisplaySide: function() { return this._displaySide; }, @@ -133,9 +168,31 @@ JX.install('DiffInline', { return this._isNewFile; }, - _newRequestData: function() { + getID: function() { + return this._id; + }, + + setDeleted: function(deleted) { + this._isDeleted = deleted; + this._redraw(); + return this; + }, + + setInvisible: function(invisible) { + this._isInvisible = invisible; + this._redraw(); + return this; + }, + + setLoading: function(loading) { + this._isLoading = loading; + this._redraw(); + return this; + }, + + _newRequestData: function(operation) { return { - op: 'edit', + op: operation, id: this._id, on_right: ((this.getDisplaySide() == 'right') ? 1 : 0), renderer: this.getChangeset().getRenderer(), @@ -151,42 +208,89 @@ JX.install('DiffInline', { this._drawEditRows(rows); - // TODO: Set the row state to "hidden". + this.setLoading(false); + this.setInvisible(true); + }, + + _ondeleteresponse: function() { + this._drawUndoRows(); + + this.setLoading(false); + this.setDeleted(true); + + this._didUpdate(); + }, + + _drawUndoRows: function() { + var templates = this.getChangeset().getUndoTemplates(); + + var template; + if (this.getDisplaySide() == 'right') { + template = templates.r; + } else { + template = templates.l; + } + template = JX.$H(template).getNode(); + + this._undoRow = this._drawRows(template, this._row, 'undo'); }, _drawEditRows: function(rows) { - var first_row = JX.DOM.scry(rows, 'tr')[0]; - var row = first_row; - var cursor = this._row; + return this._drawRows(rows, null, 'edit'); + }, + _drawRows: function(rows, cursor, type) { + var first_row = JX.DOM.scry(rows, 'tr')[0]; + var first_meta; + var row = first_row; + cursor = cursor || this._row.nextSibling; + + var next_row; while (row) { + // Grab this first, since it's going to change once we insert the row + // into the document. + next_row = row.nextSibling; + cursor.parentNode.insertBefore(row, cursor.nextSibling); cursor = row; var row_meta = { node: row, - type: 'edit', + type: type, listeners: [] }; - row_meta.listeners.push( - JX.DOM.listen( - row, - ['submit', 'didSyntheticSubmit'], - 'inline-edit-form', - JX.bind(this, this._onsubmit, row_meta))); + if (!first_meta) { + first_meta = row_meta; + } - row_meta.listeners.push( - JX.DOM.listen( - row, - 'click', - 'inline-edit-cancel', - JX.bind(this, this._oncancel, row_meta))); + if (type == 'edit') { + row_meta.listeners.push( + JX.DOM.listen( + row, + ['submit', 'didSyntheticSubmit'], + 'inline-edit-form', + JX.bind(this, this._onsubmit, row_meta))); - row = row.nextSibling; + row_meta.listeners.push( + JX.DOM.listen( + row, + 'click', + 'inline-edit-cancel', + JX.bind(this, this._oncancel, row_meta))); + } else { + row_meta.listeners.push( + JX.DOM.listen( + row, + 'click', + 'differential-inline-comment-undo', + JX.bind(this, this._onundo, row_meta))); + } + + row = next_row; } - return first_row; + return first_meta; }, _onsubmit: function(row, e) { @@ -194,11 +298,33 @@ JX.install('DiffInline', { var handler = JX.bind(this, this._onsubmitresponse, row); + this.setLoading(true); + JX.Workflow.newFromForm(e.getTarget()) .setHandler(handler) .start(); + }, - // TODO: Set state to "loading". + _onundo: function(row, e) { + e.kill(); + + this._removeRow(row); + + var uri = this._getInlineURI(); + var data = this._newRequestData('undelete'); + var handler = JX.bind(this, this._onundelete); + + this.setDeleted(false); + this.setLoading(true); + + new JX.Request(uri, handler) + .setData(data) + .send(); + }, + + _onundelete: function() { + this.setLoading(false); + this._didUpdate(); }, _oncancel: function(row, e) { @@ -206,18 +332,15 @@ JX.install('DiffInline', { // TODO: Capture edited text and offer "undo". - JX.DOM.remove(row.node); - this._removeListeners(row.listeners); + this._removeRow(row); - // TODO: Restore state to "normal". + this.setInvisible(false); }, _onsubmitresponse: function(row, response) { + this._removeRow(row); - JX.DOM.remove(row.node); - this._removeListeners(row.listeners); - - // TODO: Restore state to "normal". + this.setInvisible(false); this._onupdate(response); }, @@ -225,7 +348,7 @@ JX.install('DiffInline', { _onupdate: function(response) { var new_row; if (response.markup) { - new_row = this._drawEditRows(JX.$H(response.markup).getNode()); + new_row = this._drawEditRows(JX.$H(response.markup).getNode()).node; } // TODO: Save the old row so the action it's undo-able if it was a @@ -243,18 +366,22 @@ JX.install('DiffInline', { _didUpdate: function() { // After making changes to inline comments, refresh the transaction // preview at the bottom of the page. - - // TODO: This isn't the cleanest way to find the preview form, but - // rendering no longer has direct access to it. - var forms = JX.DOM.scry(document.body, 'form', 'transaction-append'); - if (forms.length) { - JX.DOM.invoke(forms[0], 'shouldRefresh'); - } + this.getChangeset().getChangesetList().redrawPreview(); }, - _removeListeners: function(listeners) { - for (var ii = 0; ii < listeners.length; ii++) { - listeners[ii].remove(); + _redraw: function() { + var is_invisible = (this._isInvisible || this._isDeleted); + var is_loading = (this._isLoading); + + var row = this._row; + JX.DOM.alterClass(row, 'differential-inline-hidden', is_invisible); + JX.DOM.alterClass(row, 'differential-inline-loading', is_loading); + }, + + _removeRow: function(row) { + JX.DOM.remove(row.node); + for (var ii = 0; ii < row.listeners.length; ii++) { + row.listeners[ii].remove(); } }, diff --git a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js index 20eec1a3d5..b6aaeb3cf4 100644 --- a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js +++ b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js @@ -331,46 +331,6 @@ JX.behavior('differential-edit-inline-comments', function(config) { var handle_inline_action = function(node, op) { var data = JX.Stratcom.getData(node); - // If you click an action in the preview at the bottom of the page, we - // find the corresponding node and simulate clicking that, if it's - // present on the page. This gives the editor a more consistent view - // of the document. - if (JX.Stratcom.hasSigil(node, 'differential-inline-comment-preview')) { - var nodes = JX.DOM.scry( - JX.DOM.getContentFrame(), - 'div', - 'differential-inline-comment'); - - var found = false; - var node_data; - for (var ii = 0; ii < nodes.length; ++ii) { - if (nodes[ii] == node) { - // Don't match the preview itself. - continue; - } - node_data = JX.Stratcom.getData(nodes[ii]); - if (node_data.id == data.id) { - node = nodes[ii]; - data = node_data; - found = true; - break; - } - } - - if (!found) { - switch (op) { - case 'delete': - new JX.DifferentialInlineCommentEditor(config.uri) - .deleteByID(data.id); - return; - } - } - - if (op == 'delete') { - op = 'refdelete'; - } - } - var original = data.original; var reply_phid = null; if (op == 'reply') { @@ -387,30 +347,25 @@ JX.behavior('differential-edit-inline-comments', function(config) { 'differential-changeset'); var view = JX.DiffChangeset.getForNode(changeset_root); - if (op == 'edit') { - // This is now handled by DiffChangesetList. - editor = true; - } else { - editor = new JX.DifferentialInlineCommentEditor(config.uri) - .setTemplates(view.getUndoTemplates()) - .setOperation(op) - .setID(data.id) - .setChangesetID(data.changesetID) - .setLineNumber(data.number) - .setLength(data.length) - .setOnRight(data.on_right) - .setOriginalText(original) - .setRow(row) - .setTable(row.parentNode) - .setReplyToCommentPHID(reply_phid) - .setRenderer(view.getRenderer()) - .start(); - } + editor = new JX.DifferentialInlineCommentEditor(config.uri) + .setTemplates(view.getUndoTemplates()) + .setOperation(op) + .setID(data.id) + .setChangesetID(data.changesetID) + .setLineNumber(data.number) + .setLength(data.length) + .setOnRight(data.on_right) + .setOriginalText(original) + .setRow(row) + .setTable(row.parentNode) + .setReplyToCommentPHID(reply_phid) + .setRenderer(view.getRenderer()) + .start(); set_link_state(true); }; - for (var op in {'edit': 1, 'delete': 1, 'reply': 1}) { + for (var op in {'reply': 1}) { JX.Stratcom.listen( 'click', ['differential-inline-comment', 'differential-inline-' + op], From 58dded555bdedc234b6d52850f7dc9c38f0d9c4b Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 15 May 2017 16:26:45 -0700 Subject: [PATCH 21/81] Move inline comment creation to new DiffInline code Summary: Ref T12616. This makes creating inlines use the new code. Creation and editing is now slightly more consistent in how it uses nodes. This will simplify the next change (replies), which I ran into some trouble with in an earlier iteration. Note that this (and other changes in the series) allow you to create and edit multiple inlines simultaneously. This is mostly a feature, although I expect we'll need to lock it down a little bit. I have some UI ideas to help avoid errors. Test Plan: Created inlines on a single line; on a range of lines; on the same line; multiple inlines at the same time. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616 Differential Revision: https://secure.phabricator.com/D17893 --- resources/celerity/map.php | 58 +++---- .../rsrc/js/application/diff/DiffChangeset.js | 18 ++- .../rsrc/js/application/diff/DiffInline.js | 143 ++++++++++++++---- .../behavior-edit-inline-comments.js | 25 +-- 4 files changed, 171 insertions(+), 73 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 5b33419132..e32bccdcda 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '2ff7879f', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', - 'differential.pkg.js' => '271a1e1e', + 'differential.pkg.js' => '3a7c5866', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -390,15 +390,15 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => '57b29223', + 'rsrc/js/application/diff/DiffChangeset.js' => '454cfe59', 'rsrc/js/application/diff/DiffChangesetList.js' => '50bc5b50', - 'rsrc/js/application/diff/DiffInline.js' => '64dfc791', + 'rsrc/js/application/diff/DiffInline.js' => '38a957be', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '9ed8d2b6', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', - 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '9d9dbc38', + 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '995c805a', 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '92904457', 'rsrc/js/application/differential/behavior-populate.js' => '5e41c819', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', @@ -624,7 +624,7 @@ return array( 'javelin-behavior-diff-preview-link' => '051c7832', 'javelin-behavior-differential-comment-jump' => '4fdb476d', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', - 'javelin-behavior-differential-edit-inline-comments' => '9d9dbc38', + 'javelin-behavior-differential-edit-inline-comments' => '995c805a', 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-keyboard-navigation' => '92904457', 'javelin-behavior-differential-populate' => '5e41c819', @@ -785,9 +785,9 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => '57b29223', + 'phabricator-diff-changeset' => '454cfe59', 'phabricator-diff-changeset-list' => '50bc5b50', - 'phabricator-diff-inline' => '64dfc791', + 'phabricator-diff-inline' => '38a957be', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -1132,6 +1132,9 @@ return array( 'javelin-dom', 'javelin-workflow', ), + '38a957be' => array( + 'javelin-dom', + ), '3ab51e2c' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -1211,6 +1214,17 @@ return array( 'javelin-behavior', 'javelin-dom', ), + '454cfe59' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + 'phabricator-diff-inline', + ), '469c0d9e' => array( 'javelin-behavior', 'javelin-dom', @@ -1342,17 +1356,6 @@ return array( 'javelin-vector', 'javelin-dom', ), - '57b29223' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - 'phabricator-diff-inline', - ), '58dea2fa' => array( 'javelin-install', 'javelin-util', @@ -1422,9 +1425,6 @@ return array( 'javelin-workflow', 'javelin-dom', ), - '64dfc791' => array( - 'javelin-dom', - ), '680ea2c8' => array( 'javelin-install', 'javelin-dom', @@ -1675,6 +1675,14 @@ return array( 'javelin-dom', 'javelin-reactor-dom', ), + '995c805a' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + 'javelin-util', + 'javelin-vector', + 'differential-inline-comment-editor', + ), '9a6dd75c' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1698,14 +1706,6 @@ return array( '9d9685d6' => array( 'phui-oi-list-view-css', ), - '9d9dbc38' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - 'javelin-util', - 'javelin-vector', - 'differential-inline-comment-editor', - ), '9ed8d2b6' => array( 'javelin-dom', 'javelin-util', diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index b52e54aaed..b114198b4f 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -411,16 +411,28 @@ JX.install('DiffChangeset', { var data = JX.Stratcom.getData(node); if (!data.inline) { - var inline = new JX.DiffInline(node) - .setChangeset(this); + var inline = new JX.DiffInline() + .setChangeset(this) + .bindToRow(node); this._inlines.push(inline); - data.inline = inline; } return data.inline; }, + newInlineForRange: function(data) { + var inline = new JX.DiffInline() + .setChangeset(this) + .bindToRange(data); + + this._inlines.push(inline); + + inline.create(); + + return inline; + }, + getInlineByID: function(id) { // TODO: Currently, this will only find inlines which the user has // already interacted with! Inlines are built lazily as events arrive. diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js index efa16bf9cd..9eaf177ca8 100644 --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -6,33 +6,7 @@ JX.install('DiffInline', { - construct : function(row) { - this._row = row; - - var row_data = JX.Stratcom.getData(row); - this._hidden = row_data.hidden || false; - - // TODO: Get smarter about this once we do more editing, this is pretty - // hacky. - var comment = JX.DOM.find(row, 'div', 'differential-inline-comment'); - var data = JX.Stratcom.getData(comment); - - this._id = data.id; - - // TODO: This is very, very, very, very, very, very, very hacky. - var td = comment.parentNode; - var th = td.previousSibling; - if (th.parentNode.firstChild != th) { - this._displaySide = 'right'; - } else { - this._displaySide = 'left'; - } - - this._number = data.number; - this._length = data.length; - this._isNewFile = - (this.getDisplaySide() == 'right') || - (data.left != data.right); + construct : function() { }, properties: { @@ -41,6 +15,8 @@ JX.install('DiffInline', { members: { _id: null, + _phid: null, + _changesetID: null, _row: null, _hidden: false, _number: null, @@ -53,6 +29,80 @@ JX.install('DiffInline', { _isInvisible: false, _isLoading: false, + bindToRow: function(row) { + this._row = row; + + var row_data = JX.Stratcom.getData(row); + row_data.inline = this; + + this._hidden = row_data.hidden || false; + + // TODO: Get smarter about this once we do more editing, this is pretty + // hacky. + var comment = JX.DOM.find(row, 'div', 'differential-inline-comment'); + var data = JX.Stratcom.getData(comment); + + this._id = data.id; + this._phid = data.phid; + + // TODO: This is very, very, very, very, very, very, very hacky. + var td = comment.parentNode; + var th = td.previousSibling; + if (th.parentNode.firstChild != th) { + this._displaySide = 'right'; + } else { + this._displaySide = 'left'; + } + + this._number = data.number; + this._length = data.length; + this._isNewFile = + (this.getDisplaySide() == 'right') || + (data.left != data.right); + + this.setInvisible(false); + + return this; + }, + + bindToRange: function(data) { + this._id = null; + this._phid = null; + + this._hidden = false; + + this._displaySide = data.displaySide; + this._number = data.number; + this._length = data.length; + this._isNewFile = data.isNewFile; + this._changesetID = data.changesetID; + + var row = this._newRow(); + JX.Stratcom.getData(row).inline = this; + this._row = row; + + this.setInvisible(true); + + // Insert the comment after any other comments which already appear on + // the same row. + var parent_row = JX.DOM.findAbove(data.target, 'tr'); + var target_row = parent_row.nextSibling; + while (target_row && JX.Stratcom.hasSigil(target_row, 'inline-row')) { + target_row = target_row.nextSibling; + } + parent_row.parentNode.insertBefore(row, target_row); + + return this; + }, + + _newRow: function() { + var attributes = { + sigil: 'inline-row' + }; + + return JX.$N('tr', attributes); + }, + setHidden: function(hidden) { this._hidden = hidden; @@ -110,6 +160,18 @@ JX.install('DiffInline', { this._didUpdate(); }, + create: function() { + var uri = this._getInlineURI(); + var handler = JX.bind(this, this._oncreateresponse); + var data = this._newRequestData('new'); + + this.setLoading(true); + + new JX.Request(uri, handler) + .setData(data) + .send(); + }, + edit: function() { var uri = this._getInlineURI(); var handler = JX.bind(this, this._oneditresponse); @@ -172,6 +234,10 @@ JX.install('DiffInline', { return this._id; }, + getChangesetID: function() { + return this._changesetID; + }, + setDeleted: function(deleted) { this._isDeleted = deleted; this._redraw(); @@ -199,6 +265,7 @@ JX.install('DiffInline', { number: this.getLineNumber(), length: this.getLineLength(), is_new: this.isNewFile(), + changesetID: this.getChangesetID(), replyToCommentPHID: '' }; }, @@ -212,6 +279,12 @@ JX.install('DiffInline', { this.setInvisible(true); }, + _oncreateresponse: function(response) { + var rows = JX.$H(response).getNode(); + + this._drawEditRows(rows); + }, + _ondeleteresponse: function() { this._drawUndoRows(); @@ -251,7 +324,7 @@ JX.install('DiffInline', { // into the document. next_row = row.nextSibling; - cursor.parentNode.insertBefore(row, cursor.nextSibling); + cursor.parentNode.insertBefore(row, cursor); cursor = row; var row_meta = { @@ -287,6 +360,17 @@ JX.install('DiffInline', { JX.bind(this, this._onundo, row_meta))); } + // If the row has a textarea, focus it. This allows the user to start + // typing a comment immediately after a "new", "edit", or "reply" + // action. + var textareas = JX.DOM.scry( + row, + 'textarea', + 'differential-inline-comment-edit-textarea'); + if (textareas.length) { + textareas[0].focus(); + } + row = next_row; } @@ -340,6 +424,7 @@ JX.install('DiffInline', { _onsubmitresponse: function(row, response) { this._removeRow(row); + this.setLoading(false); this.setInvisible(false); this._onupdate(response); @@ -358,7 +443,7 @@ JX.install('DiffInline', { JX.DOM.remove(this._row); } - this._row = new_row; + this.bindToRow(new_row); this._didUpdate(); }, diff --git a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js index b6aaeb3cf4..9b34234e0e 100644 --- a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js +++ b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js @@ -276,18 +276,19 @@ JX.behavior('differential-edit-inline-comments', function(config) { var view = JX.DiffChangeset.getForNode(root); - editor = new JX.DifferentialInlineCommentEditor(config.uri) - .setTemplates(view.getUndoTemplates()) - .setOperation('new') - .setChangesetID(changeset) - .setLineNumber(o) - .setLength(len) - .setIsNew(isNewFile(target) ? 1 : 0) - .setOnRight(isOnRight(target) ? 1 : 0) - .setRow(insert.nextSibling) - .setTable(insert.parentNode) - .setRenderer(view.getRenderer()) - .start(); + view.newInlineForRange({ + origin: origin, + target: target, + number: o, + length: len, + changesetID: changeset, + isNewFile: isNewFile(target), + displaySide: isOnRight(target) ? 'right' : 'left' + }); + + selecting = false; + origin = null; + target = null; set_link_state(true); From 41379f39debbd0c80f0376a41e92ca36f48b867b Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 15 May 2017 16:35:06 -0700 Subject: [PATCH 22/81] Move inline replies to new code and remove DifferentialInlineEditor Summary: Ref T12616. This moves "reply" to the new stuff and deletes DifferentialInlineEditor, which no longer does anything. (This breaks some keyboard shortcuts, but I'll rebase D17859 shortly.) Test Plan: Replied to inlines; things seemed to work properly. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616 Differential Revision: https://secure.phabricator.com/D17894 --- resources/celerity/map.php | 75 ++-- resources/celerity/packages.php | 3 +- .../PhabricatorInlineCommentController.php | 13 - .../rsrc/js/application/diff/DiffChangeset.js | 12 + .../js/application/diff/DiffChangesetList.js | 9 + .../rsrc/js/application/diff/DiffInline.js | 63 ++- .../DifferentialInlineCommentEditor.js | 360 ------------------ .../behavior-edit-inline-comments.js | 93 ----- 8 files changed, 105 insertions(+), 523 deletions(-) delete mode 100644 webroot/rsrc/js/application/differential/DifferentialInlineCommentEditor.js diff --git a/resources/celerity/map.php b/resources/celerity/map.php index e32bccdcda..70b982beae 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '2ff7879f', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', - 'differential.pkg.js' => '3a7c5866', + 'differential.pkg.js' => '6375358e', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -390,15 +390,14 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => '454cfe59', - 'rsrc/js/application/diff/DiffChangesetList.js' => '50bc5b50', - 'rsrc/js/application/diff/DiffInline.js' => '38a957be', + 'rsrc/js/application/diff/DiffChangeset.js' => '4c9c47ad', + 'rsrc/js/application/diff/DiffChangesetList.js' => '589a30aa', + 'rsrc/js/application/diff/DiffInline.js' => '98c12b2f', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', - 'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '9ed8d2b6', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', - 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '995c805a', + 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '89d11432', 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '92904457', 'rsrc/js/application/differential/behavior-populate.js' => '5e41c819', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', @@ -572,7 +571,6 @@ return array( 'd3' => 'a11a5ff2', 'differential-changeset-view-css' => '41af6d25', 'differential-core-view-css' => '5b7b8ff4', - 'differential-inline-comment-editor' => '9ed8d2b6', 'differential-revision-add-comment-css' => 'c47f8c40', 'differential-revision-comment-css' => '14b8565a', 'differential-revision-history-css' => '0e8eb855', @@ -624,7 +622,7 @@ return array( 'javelin-behavior-diff-preview-link' => '051c7832', 'javelin-behavior-differential-comment-jump' => '4fdb476d', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', - 'javelin-behavior-differential-edit-inline-comments' => '995c805a', + 'javelin-behavior-differential-edit-inline-comments' => '89d11432', 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-keyboard-navigation' => '92904457', 'javelin-behavior-differential-populate' => '5e41c819', @@ -785,9 +783,9 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => '454cfe59', - 'phabricator-diff-changeset-list' => '50bc5b50', - 'phabricator-diff-inline' => '38a957be', + 'phabricator-diff-changeset' => '4c9c47ad', + 'phabricator-diff-changeset-list' => '589a30aa', + 'phabricator-diff-inline' => '98c12b2f', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -1132,9 +1130,6 @@ return array( 'javelin-dom', 'javelin-workflow', ), - '38a957be' => array( - 'javelin-dom', - ), '3ab51e2c' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -1214,17 +1209,6 @@ return array( 'javelin-behavior', 'javelin-dom', ), - '454cfe59' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - 'phabricator-diff-inline', - ), '469c0d9e' => array( 'javelin-behavior', 'javelin-dom', @@ -1286,6 +1270,17 @@ return array( 'javelin-uri', 'phabricator-notification', ), + '4c9c47ad' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + 'phabricator-diff-inline', + ), '4d863052' => array( 'javelin-dom', 'javelin-util', @@ -1312,9 +1307,6 @@ return array( 'javelin-typeahead-source', 'javelin-util', ), - '50bc5b50' => array( - 'javelin-install', - ), '519705ea' => array( 'javelin-install', 'javelin-dom', @@ -1356,6 +1348,9 @@ return array( 'javelin-vector', 'javelin-dom', ), + '589a30aa' => array( + 'javelin-install', + ), '58dea2fa' => array( 'javelin-install', 'javelin-util', @@ -1596,6 +1591,13 @@ return array( 'phabricator-draggable-list', 'javelin-workboard-column', ), + '89d11432' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + 'javelin-util', + 'javelin-vector', + ), '8a41885b' => array( 'javelin-install', 'javelin-dom', @@ -1675,13 +1677,8 @@ return array( 'javelin-dom', 'javelin-reactor-dom', ), - '995c805a' => array( - 'javelin-behavior', - 'javelin-stratcom', + '98c12b2f' => array( 'javelin-dom', - 'javelin-util', - 'javelin-vector', - 'differential-inline-comment-editor', ), '9a6dd75c' => array( 'javelin-behavior', @@ -1706,14 +1703,6 @@ return array( '9d9685d6' => array( 'phui-oi-list-view-css', ), - '9ed8d2b6' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-request', - 'javelin-workflow', - ), '9f36c42d' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2456,10 +2445,10 @@ return array( 'javelin-behavior-phabricator-object-selector', 'javelin-behavior-repository-crossreference', 'javelin-behavior-load-blame', - 'differential-inline-comment-editor', 'javelin-behavior-differential-toggle-files', 'javelin-behavior-differential-user-select', 'javelin-behavior-aphront-more', + 'phabricator-diff-inline', 'phabricator-diff-changeset', 'phabricator-diff-changeset-list', ), diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php index cac2eff5f8..08752c1b53 100644 --- a/resources/celerity/packages.php +++ b/resources/celerity/packages.php @@ -203,10 +203,11 @@ return array( 'javelin-behavior-repository-crossreference', 'javelin-behavior-load-blame', - 'differential-inline-comment-editor', 'javelin-behavior-differential-toggle-files', 'javelin-behavior-differential-user-select', 'javelin-behavior-aphront-more', + + 'phabricator-diff-inline', 'phabricator-diff-changeset', 'phabricator-diff-changeset-list', ), diff --git a/src/infrastructure/diff/PhabricatorInlineCommentController.php b/src/infrastructure/diff/PhabricatorInlineCommentController.php index 1c624c1ad8..d7f1a9dd3a 100644 --- a/src/infrastructure/diff/PhabricatorInlineCommentController.php +++ b/src/infrastructure/diff/PhabricatorInlineCommentController.php @@ -94,19 +94,6 @@ abstract class PhabricatorInlineCommentController $op = $this->getOperation(); switch ($op) { - case 'busy': - if ($request->isFormPost()) { - return new AphrontAjaxResponse(); - } - - return $this->newDialog() - ->setTitle(pht('Already Editing')) - ->appendParagraph( - pht( - 'You are already editing an inline comment. Finish editing '. - 'your current comment before adding new comments.')) - ->addCancelButton('/') - ->addSubmitButton(pht('Jump to Inline')); case 'hide': case 'show': if (!$request->validateCSRF()) { diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index b114198b4f..b1552cf8ce 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -433,6 +433,18 @@ JX.install('DiffChangeset', { return inline; }, + newInlineReply: function(original) { + var inline = new JX.DiffInline() + .setChangeset(this) + .bindToReply(original); + + this._inlines.push(inline); + + inline.create(); + + return inline; + }, + getInlineByID: function(id) { // TODO: Currently, this will only find inlines which the user has // already interacted with! Inlines are built lazily as events arrive. diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index e46a33bb56..0f07f4447b 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -41,6 +41,12 @@ JX.install('DiffChangesetList', { 'click', ['differential-inline-comment', 'differential-inline-delete'], ondelete); + + var onreply = JX.bind(this, this._ifawake, this._onaction, 'reply'); + JX.Stratcom.listen( + 'click', + ['differential-inline-comment', 'differential-inline-reply'], + onreply); }, properties: { @@ -410,6 +416,9 @@ JX.install('DiffChangesetList', { case 'delete': inline.delete(is_ref); break; + case 'reply': + inline.reply(); + break; } }, diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js index 9eaf177ca8..530f621caf 100644 --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -24,6 +24,7 @@ JX.install('DiffInline', { _displaySide: null, _isNewFile: null, _undoRow: null, + _replyToCommentPHID: null, _isDeleted: false, _isInvisible: false, @@ -66,23 +67,12 @@ JX.install('DiffInline', { }, bindToRange: function(data) { - this._id = null; - this._phid = null; - - this._hidden = false; - this._displaySide = data.displaySide; this._number = data.number; this._length = data.length; this._isNewFile = data.isNewFile; this._changesetID = data.changesetID; - var row = this._newRow(); - JX.Stratcom.getData(row).inline = this; - this._row = row; - - this.setInvisible(true); - // Insert the comment after any other comments which already appear on // the same row. var parent_row = JX.DOM.findAbove(data.target, 'tr'); @@ -90,8 +80,37 @@ JX.install('DiffInline', { while (target_row && JX.Stratcom.hasSigil(target_row, 'inline-row')) { target_row = target_row.nextSibling; } + + var row = this._newRow(); parent_row.parentNode.insertBefore(row, target_row); + this.setInvisible(true); + + return this; + }, + + bindToReply: function(inline) { + this._displaySide = inline._displaySide; + this._number = inline._number; + this._length = inline._length; + this._isNewFile = inline._isNewFile; + this._changesetID = inline._changesetID; + + this._replyToCommentPHID = inline._phid; + + // TODO: This should insert correctly into the thread, not just at the + // bottom. + var parent_row = inline._row; + var target_row = parent_row.nextSibling; + while (target_row && JX.Stratcom.hasSigil(target_row, 'inline-row')) { + target_row = target_row.nextSibling; + } + + var row = this._newRow(); + parent_row.parentNode.insertBefore(row, target_row); + + this.setInvisible(true); + return this; }, @@ -100,7 +119,16 @@ JX.install('DiffInline', { sigil: 'inline-row' }; - return JX.$N('tr', attributes); + var row = JX.$N('tr', attributes); + + JX.Stratcom.getData(row).inline = this; + this._row = row; + + this._id = null; + this._phid = null; + this._hidden = false; + + return row; }, setHidden: function(hidden) { @@ -172,6 +200,11 @@ JX.install('DiffInline', { .send(); }, + reply: function() { + var changeset = this.getChangeset(); + return changeset.newInlineReply(this); + }, + edit: function() { var uri = this._getInlineURI(); var handler = JX.bind(this, this._oneditresponse); @@ -238,6 +271,10 @@ JX.install('DiffInline', { return this._changesetID; }, + getReplyToCommentPHID: function() { + return this._replyToCommentPHID; + }, + setDeleted: function(deleted) { this._isDeleted = deleted; this._redraw(); @@ -266,7 +303,7 @@ JX.install('DiffInline', { length: this.getLineLength(), is_new: this.isNewFile(), changesetID: this.getChangesetID(), - replyToCommentPHID: '' + replyToCommentPHID: this.getReplyToCommentPHID() || '', }; }, diff --git a/webroot/rsrc/js/application/differential/DifferentialInlineCommentEditor.js b/webroot/rsrc/js/application/differential/DifferentialInlineCommentEditor.js deleted file mode 100644 index 7068e94c14..0000000000 --- a/webroot/rsrc/js/application/differential/DifferentialInlineCommentEditor.js +++ /dev/null @@ -1,360 +0,0 @@ -/** - * @provides differential-inline-comment-editor - * @requires javelin-dom - * javelin-util - * javelin-stratcom - * javelin-install - * javelin-request - * javelin-workflow - */ - -JX.install('DifferentialInlineCommentEditor', { - - construct : function(uri) { - this._uri = uri; - }, - - events : ['done'], - - members : { - _uri : null, - _undoText : null, - _completed: false, - _skipOverInlineCommentRows : function(node) { - // TODO: Move this semantic information out of class names. - while (node && node.className.indexOf('inline') !== -1) { - node = node.nextSibling; - } - return node; - }, - _buildRequestData : function() { - return { - op : this.getOperation(), - on_right : this.getOnRight(), - id : this.getID(), - number : this.getLineNumber(), - is_new : (this.getIsNew() ? 1 : 0), - length : this.getLength(), - changesetID : this.getChangesetID(), - text : this.getText() || '', - renderer: this.getRenderer(), - replyToCommentPHID: this.getReplyToCommentPHID() || '', - }; - }, - _draw : function(content, exact_row) { - var row = this.getRow(); - var table = this.getTable(); - var target = exact_row ? row : this._skipOverInlineCommentRows(row); - - function copyRows(dst, src, before) { - var rows = JX.DOM.scry(src, 'tr'); - for (var ii = 0; ii < rows.length; ii++) { - - // Find the table this belongs to. If it's a sub-table, like a - // table in an inline comment, don't copy it. - if (JX.DOM.findAbove(rows[ii], 'table') !== src) { - continue; - } - - if (before) { - dst.insertBefore(rows[ii], before); - } else { - dst.appendChild(rows[ii]); - } - } - return rows; - } - - return copyRows(table, content, target); - }, - _removeUndoLink : function() { - var rows = JX.DifferentialInlineCommentEditor._undoRows; - if (rows) { - for (var ii = 0; ii < rows.length; ii++) { - JX.DOM.remove(rows[ii]); - } - } - JX.DifferentialInlineCommentEditor._undoRows = []; - }, - _undo : function() { - this._removeUndoLink(); - - if (this._undoText) { - this.setText(this._undoText); - } else { - this.setOperation('undelete'); - } - - this.start(); - }, - _registerUndoListener : function() { - if (!JX.DifferentialInlineCommentEditor._activeEditor) { - JX.Stratcom.listen( - 'click', - 'differential-inline-comment-undo', - function(e) { - JX.DifferentialInlineCommentEditor._activeEditor._undo(); - e.kill(); - }); - } - JX.DifferentialInlineCommentEditor._activeEditor = this; - }, - _setRowState : function(state) { - var is_hidden = (state == 'hidden'); - var is_loading = (state == 'loading'); - var row = this.getRow(); - JX.DOM.alterClass(row, 'differential-inline-hidden', is_hidden); - JX.DOM.alterClass(row, 'differential-inline-loading', is_loading); - }, - _didContinueWorkflow : function(response) { - var drawn = this._draw(JX.$H(response).getNode()); - - var op = this.getOperation(); - if (op == 'edit') { - this._setRowState('hidden'); - } - - JX.DOM.find( - drawn[0], - 'textarea', - 'differential-inline-comment-edit-textarea').focus(); - - var oncancel = JX.bind(this, function(e) { - e.kill(); - - this._didCancelWorkflow(); - - if (op == 'edit') { - this._setRowState('visible'); - } - - JX.DOM.remove(drawn[0]); - }); - JX.DOM.listen(drawn[0], 'click', 'inline-edit-cancel', oncancel); - - var onsubmit = JX.bind(this, function(e) { - e.kill(); - - JX.Workflow.newFromForm(e.getTarget()) - .setHandler(JX.bind(this, function(response) { - JX.DOM.remove(drawn[0]); - if (op == 'edit') { - this._setRowState('visible'); - } - this._didCompleteWorkflow(response); - })) - .start(); - - JX.DOM.alterClass(drawn[0], 'differential-inline-loading', true); - }); - JX.DOM.listen( - drawn[0], - ['submit', 'didSyntheticSubmit'], - 'inline-edit-form', - onsubmit); - }, - - - _didCompleteWorkflow : function(response) { - var op = this.getOperation(); - - // We don't get any markup back if the user deletes a comment, or saves - // an empty comment (which effects a delete). - if (response.markup) { - this._draw(JX.$H(response.markup).getNode()); - } - - if (op == 'delete' || op == 'refdelete') { - this._undoText = null; - this._drawUndo(); - } else { - this._removeUndoLink(); - } - - // These operations remove the old row (edit adds a new row first). - var remove_old = (op == 'edit' || op == 'delete' || op == 'refdelete'); - if (remove_old) { - this._setRowState('hidden'); - } - - if (op == 'undelete') { - this._setRowState('visible'); - } - - this._completed = true; - - this._didUpdate(); - this.invoke('done'); - }, - - - _didCancelWorkflow : function() { - this.invoke('done'); - - switch (this.getOperation()) { - case 'delete': - case 'refdelete': - if (!this._completed) { - this._setRowState('visible'); - } - return; - case 'undelete': - return; - } - - var textarea; - try { - textarea = JX.DOM.find( - document.body, // TODO: use getDialogRootNode() when available - 'textarea', - 'differential-inline-comment-edit-textarea'); - } catch (ex) { - // The close handler is called whenever the dialog closes, even if the - // user closed it by completing the workflow with "Save". The - // JX.Workflow API should probably be refined to allow programmatic - // distinction of close caused by 'cancel' vs 'submit'. Testing for - // presence of the textarea serves as a proxy for detecting a 'cancel'. - return; - } - - var text = textarea.value; - - // If the user hasn't edited the text (i.e., no change from original for - // 'edit' or no text at all), don't offer them an undo. - if (text == this.getOriginalText() || text === '') { - return; - } - - // Save the text so we can 'undo' back to it. - this._undoText = text; - - this._drawUndo(); - }, - - _drawUndo: function() { - var templates = this.getTemplates(); - var template = this.getOnRight() ? templates.r : templates.l; - template = JX.$H(template).getNode(); - - // NOTE: Operation order matters here; we can't remove anything until - // after we draw the new rows because _draw uses the old rows to figure - // out where to place the comment. - - // We use 'exact_row' to put the "undo" text directly above the affected - // comment. - var exact_row = true; - var rows = this._draw(template, exact_row); - - this._removeUndoLink(); - - JX.DifferentialInlineCommentEditor._undoRows = rows; - }, - - _onBusyWorkflow: function() { - // If the user clicks the "Jump to Inline" button, scroll to the row - // being edited. - JX.DOM.scrollTo(this.getRow()); - }, - - start : function() { - var op = this.getOperation(); - - // The user is already editing a comment, we're going to give them an - // error message. - if (op == 'busy') { - var onbusy = JX.bind(this, this._onBusyWorkflow); - - new JX.Workflow(this._uri, {op: op}) - .setHandler(onbusy) - .start(); - - return this; - } - - this._registerUndoListener(); - var data = this._buildRequestData(); - - if (op == 'delete' || op == 'refdelete' || op == 'undelete') { - this._setRowState('loading'); - - var oncomplete = JX.bind(this, this._didCompleteWorkflow); - var oncancel = JX.bind(this, this._didCancelWorkflow); - - new JX.Workflow(this._uri, data) - .setHandler(oncomplete) - .setCloseHandler(oncancel) - .start(); - } else { - var handler = JX.bind(this, this._didContinueWorkflow); - - if (op == 'edit') { - this._setRowState('loading'); - } - - new JX.Request(this._uri, handler) - .setData(data) - .send(); - } - - return this; - }, - - deleteByID: function(id) { - var data = { - op: 'refdelete', - id: id - }; - - new JX.Workflow(this._uri, data) - .setHandler(JX.bind(this, function() { - this._didUpdate(); - })) - .start(); - }, - - _didUpdate: function() { - // After making changes to inline comments, refresh the transaction - // preview at the bottom of the page. - - // TODO: This isn't the cleanest way to find the preview form, but - // rendering no longer has direct access to it. - var forms = JX.DOM.scry(document.body, 'form', 'transaction-append'); - if (forms.length) { - JX.DOM.invoke(forms[0], 'shouldRefresh'); - } - } - - }, - - statics : { - /** - * Global refernece to the 'undo' rows currently rendered in the document. - */ - _undoRows : null, - - /** - * Global listener for the 'undo' click associated with the currently - * displayed 'undo' link. When an editor is start()ed, it becomes the active - * editor. - */ - _activeEditor : null - }, - - properties : { - operation : null, - row : null, - table : null, - onRight : null, - ID : null, - lineNumber : null, - changesetID : null, - length : null, - isNew : null, - text : null, - templates : null, - originalText : null, - renderer: null, - replyToCommentPHID: null - } - -}); diff --git a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js index 9b34234e0e..05410c8c7a 100644 --- a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js +++ b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js @@ -5,7 +5,6 @@ * javelin-dom * javelin-util * javelin-vector - * differential-inline-comment-editor */ JX.behavior('differential-edit-inline-comments', function(config) { @@ -126,13 +125,6 @@ JX.behavior('differential-edit-inline-comments', function(config) { setSelectedCells([]); } - JX.DifferentialInlineCommentEditor.listen('done', function() { - selecting = false; - editor = false; - hideReticle(); - set_link_state(false); - }); - function isOnRight(node) { return node.parentNode.firstChild != node; } @@ -150,10 +142,6 @@ JX.behavior('differential-edit-inline-comments', function(config) { } } - var set_link_state = function(active) { - JX.DOM.alterClass(JX.$(config.stage), 'inline-editor-active', active); - }; - JX.Stratcom.listen( 'mousedown', ['differential-changeset', 'tag:th'], @@ -163,14 +151,6 @@ JX.behavior('differential-edit-inline-comments', function(config) { return; } - if (editor) { - new JX.DifferentialInlineCommentEditor(config.uri) - .setOperation('busy') - .setRow(editor.getRow().previousSibling) - .start(); - return; - } - if (selecting) { return; } @@ -290,8 +270,6 @@ JX.behavior('differential-edit-inline-comments', function(config) { origin = null; target = null; - set_link_state(true); - e.kill(); }); @@ -310,75 +288,4 @@ JX.behavior('differential-edit-inline-comments', function(config) { } }); - var action_handler = function(op, e) { - // NOTE: We prevent this event, rather than killing it, because some - // actions are now handled by DiffChangesetList. - e.prevent(); - - if (editor) { - return; - } - - var node = e.getNode('differential-inline-comment'); - - // If we're on a touch device, we didn't highlight the affected lines - // earlier because we can't use hover events to mutate the document. - // Highlight them now. - updateReticleForComment(e); - - handle_inline_action(node, op); - }; - - var handle_inline_action = function(node, op) { - var data = JX.Stratcom.getData(node); - - var original = data.original; - var reply_phid = null; - if (op == 'reply') { - // If the user hit "reply", the original text is empty (a new reply), not - // the text of the comment they're replying to. - original = ''; - reply_phid = data.phid; - } - - var row = JX.DOM.findAbove(node, 'tr'); - var changeset_root = JX.DOM.findAbove( - node, - 'div', - 'differential-changeset'); - var view = JX.DiffChangeset.getForNode(changeset_root); - - editor = new JX.DifferentialInlineCommentEditor(config.uri) - .setTemplates(view.getUndoTemplates()) - .setOperation(op) - .setID(data.id) - .setChangesetID(data.changesetID) - .setLineNumber(data.number) - .setLength(data.length) - .setOnRight(data.on_right) - .setOriginalText(original) - .setRow(row) - .setTable(row.parentNode) - .setReplyToCommentPHID(reply_phid) - .setRenderer(view.getRenderer()) - .start(); - - set_link_state(true); - }; - - for (var op in {'reply': 1}) { - JX.Stratcom.listen( - 'click', - ['differential-inline-comment', 'differential-inline-' + op], - JX.bind(null, action_handler, op)); - } - - JX.Stratcom.listen( - 'differential-inline-action', - null, - function(e) { - var data = e.getData(); - handle_inline_action(data.node, data.op); - }); - }); From 588a66c04d0332140ed41a23f03b04e07d7e4a05 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 8 May 2017 13:24:15 -0700 Subject: [PATCH 23/81] Move most Differetial keyboard shortcuts into DiffChangesetList Summary: Ref T12616. This moves most keyboard shortcuts into DiffChangesetList. It breaks some shortcuts that I plan to restore later, noted in T12616 (toggle file, edit inline, reply to inline), since I think ripping them out now and rebuilding them in a little bit will make things much simpler. Test Plan: - Used j, k, n, p, J, K shortcuts to navigate a revision. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616 Differential Revision: https://secure.phabricator.com/D17859 --- resources/celerity/map.php | 47 ++-- resources/celerity/packages.php | 1 - .../DifferentialRevisionViewController.php | 1 - .../view/DifferentialChangesetListView.php | 10 + .../controller/DiffusionCommitController.php | 2 - .../rsrc/js/application/diff/DiffChangeset.js | 82 ++++++ .../js/application/diff/DiffChangesetList.js | 162 +++++++++++ .../differential/behavior-keyboard-nav.js | 264 ------------------ 8 files changed, 273 insertions(+), 296 deletions(-) delete mode 100644 webroot/rsrc/js/application/differential/behavior-keyboard-nav.js diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 70b982beae..a06069612a 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '2ff7879f', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', - 'differential.pkg.js' => '6375358e', + 'differential.pkg.js' => 'e6129b80', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -390,15 +390,14 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => '4c9c47ad', - 'rsrc/js/application/diff/DiffChangesetList.js' => '589a30aa', + 'rsrc/js/application/diff/DiffChangeset.js' => '3d4b3c5e', + 'rsrc/js/application/diff/DiffChangesetList.js' => 'e2c315d9', 'rsrc/js/application/diff/DiffInline.js' => '98c12b2f', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '89d11432', - 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '92904457', 'rsrc/js/application/differential/behavior-populate.js' => '5e41c819', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', @@ -624,7 +623,6 @@ return array( 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', 'javelin-behavior-differential-edit-inline-comments' => '89d11432', 'javelin-behavior-differential-feedback-preview' => 'b064af76', - 'javelin-behavior-differential-keyboard-navigation' => '92904457', 'javelin-behavior-differential-populate' => '5e41c819', 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', 'javelin-behavior-differential-user-select' => 'a8d8459d', @@ -783,8 +781,8 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => '4c9c47ad', - 'phabricator-diff-changeset-list' => '589a30aa', + 'phabricator-diff-changeset' => '3d4b3c5e', + 'phabricator-diff-changeset-list' => 'e2c315d9', 'phabricator-diff-inline' => '98c12b2f', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', @@ -1160,6 +1158,17 @@ return array( 'javelin-util', 'javelin-uri', ), + '3d4b3c5e' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + 'phabricator-diff-inline', + ), '3dbf94d5' => array( 'javelin-behavior', 'javelin-dom', @@ -1270,17 +1279,6 @@ return array( 'javelin-uri', 'phabricator-notification', ), - '4c9c47ad' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - 'phabricator-diff-inline', - ), '4d863052' => array( 'javelin-dom', 'javelin-util', @@ -1348,9 +1346,6 @@ return array( 'javelin-vector', 'javelin-dom', ), - '589a30aa' => array( - 'javelin-install', - ), '58dea2fa' => array( 'javelin-install', 'javelin-util', @@ -1629,12 +1624,6 @@ return array( 'javelin-dom', 'javelin-request', ), - 92904457 => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - 'phabricator-keyboard-shortcut', - ), '92b9ec77' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2119,6 +2108,9 @@ return array( 'javelin-stratcom', 'javelin-dom', ), + 'e2c315d9' => array( + 'javelin-install', + ), 'e2e0a072' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2440,7 +2432,6 @@ return array( 'javelin-behavior-differential-populate', 'javelin-behavior-differential-diff-radios', 'javelin-behavior-differential-comment-jump', - 'javelin-behavior-differential-keyboard-navigation', 'javelin-behavior-aphront-drag-and-drop-textarea', 'javelin-behavior-phabricator-object-selector', 'javelin-behavior-repository-crossreference', diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php index 08752c1b53..364a695f18 100644 --- a/resources/celerity/packages.php +++ b/resources/celerity/packages.php @@ -197,7 +197,6 @@ return array( 'javelin-behavior-differential-populate', 'javelin-behavior-differential-diff-radios', 'javelin-behavior-differential-comment-jump', - 'javelin-behavior-differential-keyboard-navigation', 'javelin-behavior-aphront-drag-and-drop-textarea', 'javelin-behavior-phabricator-object-selector', 'javelin-behavior-repository-crossreference', diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index 6cb39c1510..769ac37e1a 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -465,7 +465,6 @@ final class DifferentialRevisionViewController extends DifferentialController { } Javelin::initBehavior('differential-user-select'); - Javelin::initBehavior('differential-keyboard-navigation'); $view = id(new PHUITwoColumnView()) ->setHeader($header) diff --git a/src/applications/differential/view/DifferentialChangesetListView.php b/src/applications/differential/view/DifferentialChangesetListView.php index eb60a3dee1..715865d7b2 100644 --- a/src/applications/differential/view/DifferentialChangesetListView.php +++ b/src/applications/differential/view/DifferentialChangesetListView.php @@ -234,6 +234,16 @@ final class DifferentialChangesetListView extends AphrontView { 'Highlight As...' => pht('Highlight As...'), 'Loading...' => pht('Loading...'), + + 'Jump to next change.' => pht('Jump to next change.'), + 'Jump to previous change.' => pht('Jump to previous change.'), + 'Jump to next file.' => pht('Jump to next file.'), + 'Jump to previous file.' => pht('Jump to previous file.'), + 'Jump to next inline comment.' => pht('Jump to next inline comment.'), + 'Jump to previous inline comment.' => + pht('Jump to previous inline comment.'), + 'Jump to the table of contents.' => + pht('Jump to the table of contents.'), ), )); diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php index 10677b762f..554ad64e5e 100644 --- a/src/applications/diffusion/controller/DiffusionCommitController.php +++ b/src/applications/diffusion/controller/DiffusionCommitController.php @@ -720,8 +720,6 @@ final class DiffusionCommitController extends DiffusionController { $request = $this->getRequest(); $viewer = $request->getUser(); - Javelin::initBehavior('differential-keyboard-navigation'); - // TODO: This is pretty awkward, unify the CSS between Diffusion and // Differential better. require_celerity_resource('differential-core-view-css'); diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index b1552cf8ce..b592eb0cec 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -318,6 +318,88 @@ JX.install('DiffChangeset', { return this._highlight; }, + getSelectableItems: function() { + var items = []; + + items.push({ + type: 'file', + changeset: this, + target: this, + nodes: { + begin: this._node, + end: null + } + }); + + var rows = JX.DOM.scry(this._node, 'tr'); + + var blocks = []; + var block; + var ii; + for (ii = 0; ii < rows.length; ii++) { + var type = this._getRowType(rows[ii]); + + if (!block || (block.type !== type)) { + block = { + type: type, + items: [] + }; + blocks.push(block); + } + + block.items.push(rows[ii]); + } + + for (ii = 0; ii < blocks.length; ii++) { + block = blocks[ii]; + + if (block.type == 'change') { + items.push({ + type: block.type, + changeset: this, + target: block.items[0], + nodes: { + begin: block.items[0], + end: block.items[block.items.length - 1] + } + }); + } + + if (block.type == 'comment') { + for (var jj = 0; jj < block.items.length; jj++) { + items.push({ + type: block.type, + changeset: this, + target: block.items[jj], + nodes: { + begin: block.items[jj], + end: block.items[jj] + } + }); + } + } + } + + return items; + }, + + _getRowType: function(row) { + // NOTE: Don't do "className.indexOf()" elsewhere. This is evil legacy + // magic. + + if (row.className.indexOf('inline') !== -1) { + return 'comment'; + } + + var cells = JX.DOM.scry(row, 'td'); + for (var ii = 0; ii < cells.length; ii++) { + if (cells[ii].className.indexOf('old') !== -1 || + cells[ii].className.indexOf('new') !== -1) { + return 'change'; + } + } + }, + _getNodeData: function() { return JX.Stratcom.getData(this._node); }, diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index 0f07f4447b..39bc6aa289 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -55,15 +55,49 @@ JX.install('DiffChangesetList', { }, members: { + _initialized: false, _asleep: true, _changesets: null, + _cursorItem: null, + _lastKeyboardManager: null, + sleep: function() { this._asleep = true; }, wake: function() { this._asleep = false; + + if (this._initialized) { + return; + } + + this._initialized = true; + var pht = this.getTranslations(); + + var label; + + label = pht('Jump to next change.'); + this._installJumpKey('j', label, 1); + + label = pht('Jump to previous change.'); + this._installJumpKey('k', label, -1); + + label = pht('Jump to next file.'); + this._installJumpKey('J', label, 1, 'file'); + + label = pht('Jump to previous file.'); + this._installJumpKey('K', label, -1, 'file'); + + label = pht('Jump to next inline comment.'); + this._installJumpKey('n', label, 1, 'comment'); + + label = pht('Jump to previous inline comment.'); + this._installJumpKey('p', label, -1, 'comment'); + + label = pht('Jump to the table of contents.'); + this._installKey('t', label, this._ontoc); }, isAsleep: function() { @@ -132,6 +166,134 @@ JX.install('DiffChangesetList', { } }, + _installKey: function(key, label, handler) { + handler = JX.bind(this, this._ifawake, handler); + + return new JX.KeyboardShortcut(key, label) + .setHandler(handler) + .register(); + }, + + _installJumpKey: function(key, label, delta, filter) { + filter = filter || null; + var handler = JX.bind(this, this._onjumpkey, delta, filter); + return this._installKey(key, label, handler); + }, + + _ontoc: function(manager) { + var toc = JX.$('toc'); + manager.scrollTo(toc); + }, + + _onjumpkey: function(delta, filter, manager) { + var state = this._getSelectionState(); + + var cursor = state.cursor; + var items = state.items; + + // If there's currently no selection and the user tries to go back, + // don't do anything. + if ((cursor === null) && (delta < 0)) { + return; + } + + while (true) { + if (cursor === null) { + cursor = 0; + } else { + cursor = cursor + delta; + } + + // If we've gone backward past the first change, bail out. + if (cursor < 0) { + return; + } + + // If we've gone forward off the end of the list, bail out. + if (cursor >= items.length) { + return; + } + + // If we're selecting things of a particular type (like only files) + // and the next item isn't of that type, move past it. + if (filter !== null) { + if (items[cursor].type !== filter) { + continue; + } + } + + // Otherwise, we've found a valid item to select. + break; + } + + this._setSelectionState(items[cursor], manager); + }, + + _getSelectionState: function() { + var items = this._getSelectableItems(); + + var cursor = null; + if (this._cursorItem !== null) { + for (var ii = 0; ii < items.length; ii++) { + var item = items[ii]; + if (this._cursorItem.target === item.target) { + cursor = ii; + break; + } + } + } + + return { + cursor: cursor, + items: items + }; + }, + + _setSelectionState: function(item, manager) { + this._cursorItem = item; + + this._redrawSelection(manager, true); + + return this; + }, + + _redrawSelection: function(manager, scroll) { + manager = manager || this._lastKeyboardManager; + this._lastKeyboardManager = manager; + + if (this.isAsleep()) { + manager.focusOn(null); + return; + } + + var cursor = this._cursorItem; + if (!cursor) { + manager.focusOn(null); + return; + } + + manager.focusOn(cursor.nodes.begin, cursor.nodes.end); + + if (scroll) { + manager.scrollTo(cursor.nodes.begin); + } + + return this; + }, + + _getSelectableItems: function() { + var result = []; + + for (var ii = 0; ii < this._changesets.length; ii++) { + var items = this._changesets[ii].getSelectableItems(); + for (var jj = 0; jj < items.length; jj++) { + result.push(items[jj]); + } + } + + return result; + }, + _onmore: function(e) { e.kill(); diff --git a/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js b/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js deleted file mode 100644 index f48df4d3dc..0000000000 --- a/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js +++ /dev/null @@ -1,264 +0,0 @@ -/** - * @provides javelin-behavior-differential-keyboard-navigation - * @requires javelin-behavior - * javelin-dom - * javelin-stratcom - * phabricator-keyboard-shortcut - */ - -JX.behavior('differential-keyboard-navigation', function(config) { - - var cursor = -1; - var changesets; - - var selection_begin = null; - var selection_end = null; - - var refreshFocus = function() {}; - - function init() { - if (changesets) { - return; - } - changesets = JX.DOM.scry(document.body, 'div', 'differential-changeset'); - } - - function getBlocks(cursor) { - // TODO: This might not be terribly fast; we can't currently memoize it - // because it can change as ajax requests come in (e.g., content loads). - - var rows = JX.DOM.scry(changesets[cursor], 'tr'); - var blocks = [[changesets[cursor], changesets[cursor]]]; - var start = null; - var type; - var ii; - - // Don't show code blocks inside a collapsed file. - var diff = JX.DOM.scry(changesets[cursor], 'table', 'differential-diff'); - if (diff.length == 1 && JX.Stratcom.getData(diff[0]).hidden) { - return blocks; - } - - function push() { - if (start) { - blocks.push([start, rows[ii - 1]]); - } - start = null; - } - - for (ii = 0; ii < rows.length; ii++) { - type = getRowType(rows[ii]); - if (type == 'comment') { - // If we see these types of rows, make a block for each one. - push(); - } - if (!type) { - push(); - } else if (type && !start) { - start = rows[ii]; - } - } - push(); - - return blocks; - } - - function getRowType(row) { - // NOTE: Being somewhat over-general here to allow other types of objects - // to be easily focused in the future (inline comments, 'show more..'). - - if (row.className.indexOf('inline') !== -1) { - return 'comment'; - } - - if (row.className.indexOf('differential-changeset') !== -1) { - return 'file'; - } - - var cells = JX.DOM.scry(row, 'td'); - - for (var ii = 0; ii < cells.length; ii++) { - // NOTE: The semantic use of classnames here is for performance; don't - // emulate this elsewhere since it's super terrible. - if (cells[ii].className.indexOf('old') !== -1 || - cells[ii].className.indexOf('new') !== -1) { - return 'change'; - } - } - - return null; - } - - function jump(manager, delta, jump_to_type) { - init(); - - if (cursor < 0) { - if (delta < 0) { - // If the user goes "back" without a selection, just reject the action. - return; - } else { - cursor = 0; - } - } - - while (true) { - var blocks = getBlocks(cursor); - var focus; - if (delta < 0) { - focus = blocks.length; - } else { - focus = -1; - } - - for (var ii = 0; ii < blocks.length; ii++) { - if (blocks[ii][0] == selection_begin) { - focus = ii; - break; - } - } - - while (true) { - focus += delta; - - if (blocks[focus]) { - var row_type = getRowType(blocks[focus][0]); - if (jump_to_type && row_type != jump_to_type) { - continue; - } - - selection_begin = blocks[focus][0]; - selection_end = blocks[focus][1]; - - manager.scrollTo(selection_begin); - - refreshFocus = function() { - manager.focusOn(selection_begin, selection_end); - }; - - refreshFocus(); - - return; - } else { - var adjusted = (cursor + delta); - if (adjusted < 0 || adjusted >= changesets.length) { - // Stop cursor movement when the user reaches either end. - return; - } - cursor = adjusted; - - // Break the inner loop and go to the next file. - break; - } - } - } - - } - - // When inline comments are updated, wipe out our cache of blocks since - // comments may have been added or deleted. - JX.Stratcom.listen( - null, - 'differential-inline-comment-update', - function() { - changesets = null; - }); - // Same thing when a file is hidden or shown; don't want to highlight - // invisible code. - JX.Stratcom.listen( - 'differential-toggle-file-toggled', - null, - function() { - changesets = null; - init(); - refreshFocus(); - }); - - new JX.KeyboardShortcut('j', 'Jump to next change.') - .setHandler(function(manager) { - jump(manager, 1); - }) - .register(); - - new JX.KeyboardShortcut('k', 'Jump to previous change.') - .setHandler(function(manager) { - jump(manager, -1); - }) - .register(); - - new JX.KeyboardShortcut('J', 'Jump to next file.') - .setHandler(function(manager) { - jump(manager, 1, 'file'); - }) - .register(); - - new JX.KeyboardShortcut('K', 'Jump to previous file.') - .setHandler(function(manager) { - jump(manager, -1, 'file'); - }) - .register(); - - new JX.KeyboardShortcut('n', 'Jump to next inline comment.') - .setHandler(function(manager) { - jump(manager, 1, 'comment'); - }) - .register(); - - new JX.KeyboardShortcut('p', 'Jump to previous inline comment.') - .setHandler(function(manager) { - jump(manager, -1, 'comment'); - }) - .register(); - - - new JX.KeyboardShortcut('t', 'Jump to the table of contents.') - .setHandler(function(manager) { - var toc = JX.$('toc'); - manager.scrollTo(toc); - }) - .register(); - - new JX.KeyboardShortcut( - 'h', - 'Collapse or expand the file display (after jump).') - .setHandler(function() { - if (!changesets || !changesets[cursor]) { - return; - } - JX.Stratcom.invoke('differential-toggle-file', null, { - diff: JX.DOM.scry(changesets[cursor], 'table', 'differential-diff') - }); - }) - .register(); - - - function inline_op(node, op) { - // nothing selected - if (!node) { - return; - } - if (!JX.DOM.scry(node, 'a', 'differential-inline-' + op)) { - // No link for this operation, e.g. editing a comment you can't edit. - return; - } - - var data = { - node: JX.DOM.find(node, 'div', 'differential-inline-comment'), - op: op - }; - - JX.Stratcom.invoke('differential-inline-action', null, data); - } - - new JX.KeyboardShortcut('r', 'Reply to selected inline comment.') - .setHandler(function() { - inline_op(selection_begin, 'reply'); - }) - .register(); - - new JX.KeyboardShortcut('e', 'Edit selected inline comment.') - .setHandler(function() { - inline_op(selection_begin, 'edit'); - }) - .register(); - -}); From 2fb1edfeb198bc279f2d956635a649ca95b77440 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 15 May 2017 17:31:58 -0700 Subject: [PATCH 24/81] Restore the Differential "edit" and "reply" keyboard shortcuts Summary: Ref T12616. This makes "edit" and "reply" work again. Test Plan: Used "e" and "r" to edit and reply. Also used them in bogus ways and got useful UI feedback. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616 Differential Revision: https://secure.phabricator.com/D17895 --- resources/celerity/map.php | 48 ++++++++-------- .../view/DifferentialChangesetListView.php | 8 +++ .../rsrc/js/application/diff/DiffChangeset.js | 9 +++ .../js/application/diff/DiffChangesetList.js | 55 ++++++++++++++++++- .../rsrc/js/application/diff/DiffInline.js | 25 +++++++++ 5 files changed, 119 insertions(+), 26 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index a06069612a..13f0f6bc61 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '2ff7879f', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', - 'differential.pkg.js' => 'e6129b80', + 'differential.pkg.js' => '5ee318c2', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -390,9 +390,9 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => '3d4b3c5e', - 'rsrc/js/application/diff/DiffChangesetList.js' => 'e2c315d9', - 'rsrc/js/application/diff/DiffInline.js' => '98c12b2f', + 'rsrc/js/application/diff/DiffChangeset.js' => 'f7100923', + 'rsrc/js/application/diff/DiffChangesetList.js' => 'f10fd7a3', + 'rsrc/js/application/diff/DiffInline.js' => '00db3c3a', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', @@ -781,9 +781,9 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => '3d4b3c5e', - 'phabricator-diff-changeset-list' => 'e2c315d9', - 'phabricator-diff-inline' => '98c12b2f', + 'phabricator-diff-changeset' => 'f7100923', + 'phabricator-diff-changeset-list' => 'f10fd7a3', + 'phabricator-diff-inline' => '00db3c3a', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -918,6 +918,9 @@ return array( 'unhandled-exception-css' => '4c96257a', ), 'requires' => array( + '00db3c3a' => array( + 'javelin-dom', + ), '013ffff9' => array( 'javelin-install', 'javelin-util', @@ -1158,17 +1161,6 @@ return array( 'javelin-util', 'javelin-uri', ), - '3d4b3c5e' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - 'phabricator-diff-inline', - ), '3dbf94d5' => array( 'javelin-behavior', 'javelin-dom', @@ -1666,9 +1658,6 @@ return array( 'javelin-dom', 'javelin-reactor-dom', ), - '98c12b2f' => array( - 'javelin-dom', - ), '9a6dd75c' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2108,9 +2097,6 @@ return array( 'javelin-stratcom', 'javelin-dom', ), - 'e2c315d9' => array( - 'javelin-install', - ), 'e2e0a072' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2181,6 +2167,9 @@ return array( 'javelin-workflow', 'javelin-json', ), + 'f10fd7a3' => array( + 'javelin-install', + ), 'f12cbc9f' => array( 'phui-oi-list-view-css', ), @@ -2199,6 +2188,17 @@ return array( 'phuix-icon-view', 'phabricator-prefab', ), + 'f7100923' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + 'phabricator-diff-inline', + ), 'f7fc67ec' => array( 'javelin-install', 'javelin-typeahead', diff --git a/src/applications/differential/view/DifferentialChangesetListView.php b/src/applications/differential/view/DifferentialChangesetListView.php index 715865d7b2..fea7a2da55 100644 --- a/src/applications/differential/view/DifferentialChangesetListView.php +++ b/src/applications/differential/view/DifferentialChangesetListView.php @@ -244,6 +244,14 @@ final class DifferentialChangesetListView extends AphrontView { pht('Jump to previous inline comment.'), 'Jump to the table of contents.' => pht('Jump to the table of contents.'), + 'Reply to selected inline comment.' => + pht('Reply to selected inline comment.'), + 'Edit selected inline comment.' => + pht('Edit selected inline comment.'), + 'You must select a comment to reply to.' => + pht('You must select a comment to reply to.'), + 'You must select a comment to edit.' => + pht('You must select a comment to edit.'), ), )); diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index b592eb0cec..e74adfcb79 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -367,10 +367,19 @@ JX.install('DiffChangeset', { if (block.type == 'comment') { for (var jj = 0; jj < block.items.length; jj++) { + var inline = this.getInlineForRow(block.items[jj]); + + // If this inline has been collapsed, don't select it with the + // keyboard cursor. + if (inline.isHidden()) { + continue; + } + items.push({ type: block.type, changeset: this, target: block.items[jj], + inline: inline, nodes: { begin: block.items[jj], end: block.items[jj] diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index 39bc6aa289..27b539a311 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -98,6 +98,12 @@ JX.install('DiffChangesetList', { label = pht('Jump to the table of contents.'); this._installKey('t', label, this._ontoc); + + label = pht('Reply to selected inline comment.'); + this._installKey('r', label, this._onreply); + + label = pht('Edit selected inline comment.'); + this._installKey('e', label, this._onedit); }, isAsleep: function() { @@ -185,6 +191,52 @@ JX.install('DiffChangesetList', { manager.scrollTo(toc); }, + _onreply: function(manager) { + var cursor = this._cursorItem; + + if (cursor) { + if (cursor.type == 'comment') { + var inline = cursor.inline; + if (inline.canReply()) { + manager.focusOn(null); + + inline.reply(); + return; + } + } + } + + var pht = this.getTranslations(); + this._warnUser(pht('You must select a comment to reply to.')); + }, + + _onedit: function(manager) { + var cursor = this._cursorItem; + + if (cursor) { + if (cursor.type == 'comment') { + var inline = cursor.inline; + if (inline.canEdit()) { + manager.focusOn(null); + + inline.edit(); + return; + } + } + } + + var pht = this.getTranslations(); + this._warnUser(pht('You must select a comment to edit.')); + }, + + _warnUser: function(message) { + new JX.Notification() + .setContent(message) + .alterClassName('jx-notification-alert', true) + .setDuration(1000) + .show(); + }, + _onjumpkey: function(delta, filter, manager) { var state = this._getSelectionState(); @@ -534,8 +586,7 @@ JX.install('DiffChangesetList', { }, _onaction: function(action, e) { - // TODO: This can become a kill once things fully switch over.. - e.prevent(); + e.kill(); var inline = this._getInlineForEvent(e); var is_ref = false; diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js index 530f621caf..76706fd8f1 100644 --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -114,6 +114,27 @@ JX.install('DiffInline', { return this; }, + canReply: function() { + if (!this._hasAction('reply')) { + return false; + } + + return true; + }, + + canEdit: function() { + if (!this._hasAction('edit')) { + return false; + } + + return true; + }, + + _hasAction: function(action) { + var nodes = JX.DOM.scry(this._row, 'a', 'differential-inline-' + action); + return (nodes.length > 0); + }, + _newRow: function() { var attributes = { sigil: 'inline-row' @@ -151,6 +172,10 @@ JX.install('DiffInline', { .start(); }, + isHidden: function() { + return this._hidden; + }, + toggleDone: function() { var uri = this._getInlineURI(); var data = { From 1e47ba2481e1f696b280081bf35d2bb621f46520 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 15 May 2017 21:41:47 -0700 Subject: [PATCH 25/81] Use setDrag UI for reordering workboard columns Summary: This UI can use the setDrag call to reduce clutter on the reodering dialog. Test Plan: Reorder some columns, save. {F4959906} Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17898 --- resources/celerity/map.php | 12 ++++++------ .../PhabricatorProjectBoardReorderController.php | 8 ++------ .../rsrc/css/phui/object-item/phui-oi-drag-ui.css | 8 ++++++++ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 13f0f6bc61..b457445caa 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ return array( 'names' => array( 'conpherence.pkg.css' => 'ff161f2d', 'conpherence.pkg.js' => 'b5b51108', - 'core.pkg.css' => 'd1bf3405', + 'core.pkg.css' => 'ee5f28cd', 'core.pkg.js' => '2ff7879f', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', @@ -129,7 +129,7 @@ return array( 'rsrc/css/phui/calendar/phui-calendar.css' => '477acfaa', 'rsrc/css/phui/object-item/phui-oi-big-ui.css' => '19f9369b', 'rsrc/css/phui/object-item/phui-oi-color.css' => 'cd2b9b77', - 'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => 'f12cbc9f', + 'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => '08f4ccc3', 'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '9d9685d6', 'rsrc/css/phui/object-item/phui-oi-list-view.css' => '7c8ec27a', 'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => 'a8beebea', @@ -873,7 +873,7 @@ return array( 'phui-object-box-css' => '9cff003c', 'phui-oi-big-ui-css' => '19f9369b', 'phui-oi-color-css' => 'cd2b9b77', - 'phui-oi-drag-ui-css' => 'f12cbc9f', + 'phui-oi-drag-ui-css' => '08f4ccc3', 'phui-oi-flush-ui-css' => '9d9685d6', 'phui-oi-list-view-css' => '7c8ec27a', 'phui-oi-simple-ui-css' => 'a8beebea', @@ -977,6 +977,9 @@ return array( 'javelin-stratcom', 'javelin-vector', ), + '08f4ccc3' => array( + 'phui-oi-list-view-css', + ), '0a0b10e9' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2170,9 +2173,6 @@ return array( 'f10fd7a3' => array( 'javelin-install', ), - 'f12cbc9f' => array( - 'phui-oi-list-view-css', - ), 'f50152ad' => array( 'phui-timeline-view-css', ), diff --git a/src/applications/project/controller/PhabricatorProjectBoardReorderController.php b/src/applications/project/controller/PhabricatorProjectBoardReorderController.php index fc348bb02f..8cf75ab2a4 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardReorderController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardReorderController.php @@ -94,7 +94,8 @@ final class PhabricatorProjectBoardReorderController $list = id(new PHUIObjectItemListView()) ->setUser($viewer) ->setID($list_id) - ->setFlush(true); + ->setFlush(true) + ->setDrag(true); foreach ($columns as $column) { // Don't allow milestone columns to be reordered. @@ -134,14 +135,9 @@ final class PhabricatorProjectBoardReorderController 'reorderURI' => $reorder_uri, )); - $note = id(new PHUIInfoView()) - ->appendChild(pht('Drag and drop columns to reorder them.')) - ->setSeverity(PHUIInfoView::SEVERITY_NOTICE); - return $this->newDialog() ->setTitle(pht('Reorder Columns')) ->setWidth(AphrontDialogView::WIDTH_FORM) - ->appendChild($note) ->appendChild($list) ->addSubmitButton(pht('Done')); } diff --git a/webroot/rsrc/css/phui/object-item/phui-oi-drag-ui.css b/webroot/rsrc/css/phui/object-item/phui-oi-drag-ui.css index caf9ff8dd0..5f24e2b983 100644 --- a/webroot/rsrc/css/phui/object-item/phui-oi-drag-ui.css +++ b/webroot/rsrc/css/phui/object-item/phui-oi-drag-ui.css @@ -23,6 +23,10 @@ margin-top: 4px; } +.phui-oi-drag .phui-oi-name { + padding-left: 0; +} + .phui-oi-drag.phui-oi-with-image-icon .phui-oi-frame, .phui-oi-drag.phui-oi-with-image .phui-oi-frame, .phui-oi-drag .phui-oi-frame { @@ -57,3 +61,7 @@ .phui-oi-list-drag .drag-ghost { margin-top: 4px; } + +.phui-oi-list-drag .phui-object-icon-pane { + padding-right: 8px; +} From ef839192aa5a351c6b28575320f27efa0aa15826 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 15 May 2017 19:50:59 -0700 Subject: [PATCH 26/81] Only show member/watcher notes on Members page in Projects Summary: Restricts the view of the membership privileges to just the Members page itself, and not other pages like Home/Details. Test Plan: Test Home, Test Members, see correct layouts. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17896 --- ...PhabricatorProjectMembersViewController.php | 6 ++++-- .../view/PhabricatorProjectUserListView.php | 18 +++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/applications/project/controller/PhabricatorProjectMembersViewController.php b/src/applications/project/controller/PhabricatorProjectMembersViewController.php index cd80d0c724..30ff425a51 100644 --- a/src/applications/project/controller/PhabricatorProjectMembersViewController.php +++ b/src/applications/project/controller/PhabricatorProjectMembersViewController.php @@ -26,13 +26,15 @@ final class PhabricatorProjectMembersViewController ->setUser($viewer) ->setProject($project) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->setUserPHIDs($project->getMemberPHIDs()); + ->setUserPHIDs($project->getMemberPHIDs()) + ->setShowNote(true); $watcher_list = id(new PhabricatorProjectWatcherListView()) ->setUser($viewer) ->setProject($project) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->setUserPHIDs($project->getWatcherPHIDs()); + ->setUserPHIDs($project->getWatcherPHIDs()) + ->setShowNote(true); $nav = $this->getProfileMenu(); $nav->selectFilter(PhabricatorProject::ITEM_MEMBERS); diff --git a/src/applications/project/view/PhabricatorProjectUserListView.php b/src/applications/project/view/PhabricatorProjectUserListView.php index 0c0e2c1d2b..51c2ced6d1 100644 --- a/src/applications/project/view/PhabricatorProjectUserListView.php +++ b/src/applications/project/view/PhabricatorProjectUserListView.php @@ -6,6 +6,7 @@ abstract class PhabricatorProjectUserListView extends AphrontView { private $userPHIDs; private $limit; private $background; + private $showNote; public function setProject(PhabricatorProject $project) { $this->project = $project; @@ -39,6 +40,11 @@ abstract class PhabricatorProjectUserListView extends AphrontView { return $this; } + public function setShowNote($show) { + $this->showNote = $show; + return $this; + } + abstract protected function canEditList(); abstract protected function getNoDataString(); abstract protected function getRemoveURI($phid); @@ -136,11 +142,13 @@ abstract class PhabricatorProjectUserListView extends AphrontView { ->setHeader($header) ->setObjectList($list); - if ($this->getMembershipNote()) { - $info = id(new PHUIInfoView()) - ->setSeverity(PHUIInfoView::SEVERITY_PLAIN) - ->appendChild($this->getMembershipNote()); - $box->setInfoView($info); + if ($this->showNote) { + if ($this->getMembershipNote()) { + $info = id(new PHUIInfoView()) + ->setSeverity(PHUIInfoView::SEVERITY_PLAIN) + ->appendChild($this->getMembershipNote()); + $box->setInfoView($info); + } } if ($this->background) { From 06c933781e9704e167084e9dd6d642905ff7f4a3 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 May 2017 06:36:03 -0700 Subject: [PATCH 27/81] Move keyboard focus reticle code to Differential Summary: Ref T12634. Using keyboard shortcuts in Differential currently relies on focus behavior in `KeyboardShortcutManager`. This possibly made sense long ago, but no longer does, and leads to a whole slew of bugs where the reticle doesn't interact properly with anything else. Move it to Differential so it can be made reasonably aware of edit operations, Quicksand navigation, etc. This just moves the code; future diffs will actually fix bugs. Test Plan: Used "n", "j", etc., saw the same behavior as before. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12634 Differential Revision: https://secure.phabricator.com/D17899 --- .../js/application/diff/DiffChangesetList.js | 62 +++++++++++++++---- .../rsrc/js/core/KeyboardShortcutManager.js | 43 ------------- 2 files changed, 49 insertions(+), 56 deletions(-) diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index 27b539a311..b5fde20329 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -60,7 +60,10 @@ JX.install('DiffChangesetList', { _changesets: null, _cursorItem: null, - _lastKeyboardManager: null, + + _focusNode: null, + _focusStart: null, + _focusEnd: null, sleep: function() { this._asleep = true; @@ -198,7 +201,7 @@ JX.install('DiffChangesetList', { if (cursor.type == 'comment') { var inline = cursor.inline; if (inline.canReply()) { - manager.focusOn(null); + this.setFocus(null); inline.reply(); return; @@ -217,7 +220,7 @@ JX.install('DiffChangesetList', { if (cursor.type == 'comment') { var inline = cursor.inline; if (inline.canEdit()) { - manager.focusOn(null); + this.setFocus(null); inline.edit(); return; @@ -310,21 +313,13 @@ JX.install('DiffChangesetList', { }, _redrawSelection: function(manager, scroll) { - manager = manager || this._lastKeyboardManager; - this._lastKeyboardManager = manager; - - if (this.isAsleep()) { - manager.focusOn(null); - return; - } - var cursor = this._cursorItem; if (!cursor) { - manager.focusOn(null); + this.setFocus(null); return; } - manager.focusOn(cursor.nodes.begin, cursor.nodes.end); + this.setFocus(cursor.nodes.begin, cursor.nodes.end); if (scroll) { manager.scrollTo(cursor.nodes.begin); @@ -644,6 +639,47 @@ JX.install('DiffChangesetList', { } }, + setFocus: function(node, extended_node) { + this._focusStart = node; + this._focusEnd = extended_node; + this._redrawFocus(); + }, + + _redrawFocus: function() { + var node = this._focusStart; + var extended_node = this._focusEnd || node; + + var reticle = this._getFocusNode(); + if (!node) { + JX.DOM.remove(reticle); + return; + } + + // Outset the reticle some pixels away from the element, so there's some + // space between the focused element and the outline. + var p = JX.Vector.getPos(node); + var s = JX.Vector.getAggregateScrollForNode(node); + + p.add(s).add(-4, -4).setPos(reticle); + // Compute the size we need to extend to the full extent of the focused + // nodes. + JX.Vector.getPos(extended_node) + .add(-p.x, -p.y) + .add(JX.Vector.getDim(extended_node)) + .add(8, 8) + .setDim(reticle); + + JX.DOM.getContentFrame().appendChild(reticle); + }, + + _getFocusNode: function() { + if (!this._focusNode) { + var node = JX.$N('div', {className : 'keyboard-focus-focus-reticle'}); + this._focusNode = node; + } + return this._focusNode; + }, + _deleteInlineByID: function(id) { var uri = this.getInlineURI(); var data = { diff --git a/webroot/rsrc/js/core/KeyboardShortcutManager.js b/webroot/rsrc/js/core/KeyboardShortcutManager.js index c9a1fbe64e..281c12a8f3 100644 --- a/webroot/rsrc/js/core/KeyboardShortcutManager.js +++ b/webroot/rsrc/js/core/KeyboardShortcutManager.js @@ -54,7 +54,6 @@ JX.install('KeyboardShortcutManager', { members : { _shortcuts : null, - _focusReticle : null, /** * Instead of calling this directly, you should call @@ -83,48 +82,6 @@ JX.install('KeyboardShortcutManager', { JX.DOM.scrollToPosition(0, node_position.y + scroll_distance.y - 60); }, - /** - * Move the keyboard shortcut focus to an element. - * - * @param Node Node to focus, or pass null to clear the focus. - * @param Node To focus multiple nodes (like rows in a table), specify the - * top-left node as the first parameter and the bottom-right - * node as the focus extension. - * @return void - */ - focusOn : function(node, extended_node) { - this._clearReticle(); - - if (!node) { - return; - } - - var r = JX.$N('div', {className : 'keyboard-focus-focus-reticle'}); - - extended_node = extended_node || node; - - // Outset the reticle some pixels away from the element, so there's some - // space between the focused element and the outline. - var p = JX.Vector.getPos(node); - var s = JX.Vector.getAggregateScrollForNode(node); - - p.add(s).add(-4, -4).setPos(r); - // Compute the size we need to extend to the full extent of the focused - // nodes. - JX.Vector.getPos(extended_node) - .add(-p.x, -p.y) - .add(JX.Vector.getDim(extended_node)) - .add(8, 8) - .setDim(r); - JX.DOM.getContentFrame().appendChild(r); - - this._focusReticle = r; - }, - - _clearReticle : function() { - this._focusReticle && JX.DOM.remove(this._focusReticle); - this._focusReticle = null; - }, _onkeypress : function(e) { if (!(this._getKey(e) in JX.KeyboardShortcutManager._downkeys)) { this._onkeyhit(e); From 5d7202526f3dcbe09513985ee1aa89fdf8f7dbe9 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 May 2017 06:40:50 -0700 Subject: [PATCH 28/81] Hide the Differential keyboard focus reticle after Quicksand navigation Summary: Ref T8047. Ref T12634. When we sleep, hide the reticle. Restore it when we wake. Test Plan: - With Quicksand enabled.. - Used "j" to select a change in a revision. - Navigated away by clicking a link. - WOW! Reticle vanished properly! - Used "back" to return. - Reticle returned properly. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12634, T8047 Differential Revision: https://secure.phabricator.com/D17900 --- resources/celerity/map.php | 32 +++++++++---------- .../js/application/diff/DiffChangesetList.js | 6 +++- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index b457445caa..0a321e9a59 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -10,10 +10,10 @@ return array( 'conpherence.pkg.css' => 'ff161f2d', 'conpherence.pkg.js' => 'b5b51108', 'core.pkg.css' => 'ee5f28cd', - 'core.pkg.js' => '2ff7879f', + 'core.pkg.js' => '115cb4da', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', - 'differential.pkg.js' => '5ee318c2', + 'differential.pkg.js' => 'd831041b', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -391,7 +391,7 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', 'rsrc/js/application/diff/DiffChangeset.js' => 'f7100923', - 'rsrc/js/application/diff/DiffChangesetList.js' => 'f10fd7a3', + 'rsrc/js/application/diff/DiffChangesetList.js' => 'c457058f', 'rsrc/js/application/diff/DiffInline.js' => '00db3c3a', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', @@ -474,7 +474,7 @@ return array( 'rsrc/js/core/FileUpload.js' => '680ea2c8', 'rsrc/js/core/Hovercard.js' => '1bd28176', 'rsrc/js/core/KeyboardShortcut.js' => '1ae869f2', - 'rsrc/js/core/KeyboardShortcutManager.js' => '4a021c10', + 'rsrc/js/core/KeyboardShortcutManager.js' => 'c19dd9b9', 'rsrc/js/core/MultirowRowManager.js' => 'b5d57730', 'rsrc/js/core/Notification.js' => 'ccf1cbf8', 'rsrc/js/core/Prefab.js' => 'c5af80a2', @@ -782,7 +782,7 @@ return array( 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', 'phabricator-diff-changeset' => 'f7100923', - 'phabricator-diff-changeset-list' => 'f10fd7a3', + 'phabricator-diff-changeset-list' => 'c457058f', 'phabricator-diff-inline' => '00db3c3a', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', @@ -793,7 +793,7 @@ return array( 'phabricator-filetree-view-css' => 'fccf9f82', 'phabricator-flag-css' => 'bba8f811', 'phabricator-keyboard-shortcut' => '1ae869f2', - 'phabricator-keyboard-shortcut-manager' => '4a021c10', + 'phabricator-keyboard-shortcut-manager' => 'c19dd9b9', 'phabricator-main-menu-view' => '5294060f', 'phabricator-nav-view-css' => 'faf6a6fc', 'phabricator-notification' => 'ccf1cbf8', @@ -1253,13 +1253,6 @@ return array( 'javelin-dom', 'javelin-stratcom', ), - '4a021c10' => array( - 'javelin-install', - 'javelin-util', - 'javelin-stratcom', - 'javelin-dom', - 'javelin-vector', - ), '4b3c4443' => array( 'phuix-icon-view', ), @@ -1916,12 +1909,22 @@ return array( 'javelin-install', 'javelin-dom', ), + 'c19dd9b9' => array( + 'javelin-install', + 'javelin-util', + 'javelin-stratcom', + 'javelin-dom', + 'javelin-vector', + ), 'c420b0b9' => array( 'javelin-behavior', 'javelin-behavior-device', 'javelin-stratcom', 'phabricator-tooltip', ), + 'c457058f' => array( + 'javelin-install', + ), 'c587b80f' => array( 'javelin-install', ), @@ -2170,9 +2173,6 @@ return array( 'javelin-workflow', 'javelin-json', ), - 'f10fd7a3' => array( - 'javelin-install', - ), 'f50152ad' => array( 'phui-timeline-view-css', ), diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index b5fde20329..f9a3c5d5d9 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -67,11 +67,15 @@ JX.install('DiffChangesetList', { sleep: function() { this._asleep = true; + + this._redrawFocus(); }, wake: function() { this._asleep = false; + this._redrawFocus(); + if (this._initialized) { return; } @@ -650,7 +654,7 @@ JX.install('DiffChangesetList', { var extended_node = this._focusEnd || node; var reticle = this._getFocusNode(); - if (!node) { + if (!node || this.isAsleep()) { JX.DOM.remove(reticle); return; } From 7d6133929a6650bc910761e7360f07a69880a0e5 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 May 2017 06:50:47 -0700 Subject: [PATCH 29/81] Resize the Differential keyboard focus reticle when the window is resized Summary: Fixes T12632. Ref T12634. Currently, the keyboard focus reticle does not redraw properly after a window resize. Test Plan: - Used "n" to select a block. - Resized the window. - Saw the reticle also resize properly. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12634, T12632 Differential Revision: https://secure.phabricator.com/D17901 --- resources/celerity/map.php | 12 ++++++------ .../rsrc/js/application/diff/DiffChangesetList.js | 7 +++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 0a321e9a59..ab3628f264 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '115cb4da', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', - 'differential.pkg.js' => 'd831041b', + 'differential.pkg.js' => '14ef6888', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -391,7 +391,7 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', 'rsrc/js/application/diff/DiffChangeset.js' => 'f7100923', - 'rsrc/js/application/diff/DiffChangesetList.js' => 'c457058f', + 'rsrc/js/application/diff/DiffChangesetList.js' => 'e5c5e171', 'rsrc/js/application/diff/DiffInline.js' => '00db3c3a', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', @@ -782,7 +782,7 @@ return array( 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', 'phabricator-diff-changeset' => 'f7100923', - 'phabricator-diff-changeset-list' => 'c457058f', + 'phabricator-diff-changeset-list' => 'e5c5e171', 'phabricator-diff-inline' => '00db3c3a', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', @@ -1922,9 +1922,6 @@ return array( 'javelin-stratcom', 'phabricator-tooltip', ), - 'c457058f' => array( - 'javelin-install', - ), 'c587b80f' => array( 'javelin-install', ), @@ -2140,6 +2137,9 @@ return array( 'javelin-workflow', 'javelin-magical-init', ), + 'e5c5e171' => array( + 'javelin-install', + ), 'e9581f08' => array( 'javelin-behavior', 'javelin-stratcom', diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index f9a3c5d5d9..cdb61c0a15 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -47,6 +47,9 @@ JX.install('DiffChangesetList', { 'click', ['differential-inline-comment', 'differential-inline-reply'], onreply); + + var onresize = JX.bind(this, this._ifawake, this._onresize); + JX.Stratcom.listen('resize', null, onresize); }, properties: { @@ -584,6 +587,10 @@ JX.install('DiffChangesetList', { inline.setHidden(is_hide); }, + _onresize: function() { + this._redrawFocus(); + }, + _onaction: function(action, e) { e.kill(); From 3c0da816196741ec790fa15878fc5623dcae767a Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 May 2017 06:59:32 -0700 Subject: [PATCH 30/81] When the Differential filetree is toggled, resize the keyboard reticle properly Summary: Ref T9270. Ref T12634. Emit a resize event after toggling the filetree so that things can recalculate layout. This just does the keyboard reticle, not the mouse/edit reticle. Test Plan: - Used "n" to select a block. - Used "f" to toggle the filetree. - Saw reticle resize properly. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12634, T9270 Differential Revision: https://secure.phabricator.com/D17902 --- resources/celerity/map.php | 20 +++++++++---------- .../rsrc/js/core/behavior-phabricator-nav.js | 4 ++++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index ab3628f264..c662ac7c88 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -10,7 +10,7 @@ return array( 'conpherence.pkg.css' => 'ff161f2d', 'conpherence.pkg.js' => 'b5b51108', 'core.pkg.css' => 'ee5f28cd', - 'core.pkg.js' => '115cb4da', + 'core.pkg.js' => '8c5f913d', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', 'differential.pkg.js' => '14ef6888', @@ -506,7 +506,7 @@ return array( 'rsrc/js/core/behavior-more.js' => 'a80d0378', 'rsrc/js/core/behavior-object-selector.js' => 'e0ec7f2f', 'rsrc/js/core/behavior-oncopy.js' => '2926fff2', - 'rsrc/js/core/behavior-phabricator-nav.js' => '08675c6d', + 'rsrc/js/core/behavior-phabricator-nav.js' => '08163386', 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => 'acd29eee', 'rsrc/js/core/behavior-read-only-warning.js' => 'ba158207', 'rsrc/js/core/behavior-refresh-csrf.js' => 'ab2f381b', @@ -664,7 +664,7 @@ return array( 'javelin-behavior-phabricator-keyboard-pager' => 'a8da01f0', 'javelin-behavior-phabricator-keyboard-shortcuts' => '01fca1f0', 'javelin-behavior-phabricator-line-linker' => '1499a8cb', - 'javelin-behavior-phabricator-nav' => '08675c6d', + 'javelin-behavior-phabricator-nav' => '08163386', 'javelin-behavior-phabricator-notification-example' => '8ce821c5', 'javelin-behavior-phabricator-object-selector' => 'e0ec7f2f', 'javelin-behavior-phabricator-oncopy' => '2926fff2', @@ -955,13 +955,7 @@ return array( 'javelin-stratcom', 'javelin-workflow', ), - '0825c27a' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - 'javelin-util', - ), - '08675c6d' => array( + '08163386' => array( 'javelin-behavior', 'javelin-behavior-device', 'javelin-stratcom', @@ -971,6 +965,12 @@ return array( 'javelin-request', 'javelin-util', ), + '0825c27a' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-stratcom', + 'javelin-util', + ), '087e919c' => array( 'javelin-install', 'javelin-dom', diff --git a/webroot/rsrc/js/core/behavior-phabricator-nav.js b/webroot/rsrc/js/core/behavior-phabricator-nav.js index ca7fc0d14a..cd132550a8 100644 --- a/webroot/rsrc/js/core/behavior-phabricator-nav.js +++ b/webroot/rsrc/js/core/behavior-phabricator-nav.js @@ -113,6 +113,10 @@ JX.behavior('phabricator-nav', function(config) { new JX.Request('/settings/adjust/', JX.bag) .setData({ key : 'nav-collapsed', value : (collapsed ? 1 : 0) }) .send(); + + // Invoke a resize event so page elements can redraw if they need to. One + // example is the selection reticles in Differential. + JX.Stratcom.invoke('resize'); }); From 665ff4fdf6e7501832859df5ffd2112cbc711753 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 May 2017 07:03:28 -0700 Subject: [PATCH 31/81] Redraw the Differential keyboard reticle after collapsing/un-collapsing an inline Summary: Ref T12634. Fixes T10049. Toggling an inline currently leaves the reticle oddly-positioned. Test Plan: - Selected a comment with the keyboard. - Collapsed it. - Saw reticle behave reasonably. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12634, T10049 Differential Revision: https://secure.phabricator.com/D17903 --- resources/celerity/map.php | 12 ++++++------ webroot/rsrc/js/application/diff/DiffInline.js | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index c662ac7c88..cf4ec03237 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '8c5f913d', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', - 'differential.pkg.js' => '14ef6888', + 'differential.pkg.js' => 'a185599e', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -392,7 +392,7 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', 'rsrc/js/application/diff/DiffChangeset.js' => 'f7100923', 'rsrc/js/application/diff/DiffChangesetList.js' => 'e5c5e171', - 'rsrc/js/application/diff/DiffInline.js' => '00db3c3a', + 'rsrc/js/application/diff/DiffInline.js' => 'f3af20b1', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', @@ -783,7 +783,7 @@ return array( 'phabricator-dashboard-css' => 'fe5b1869', 'phabricator-diff-changeset' => 'f7100923', 'phabricator-diff-changeset-list' => 'e5c5e171', - 'phabricator-diff-inline' => '00db3c3a', + 'phabricator-diff-inline' => 'f3af20b1', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -918,9 +918,6 @@ return array( 'unhandled-exception-css' => '4c96257a', ), 'requires' => array( - '00db3c3a' => array( - 'javelin-dom', - ), '013ffff9' => array( 'javelin-install', 'javelin-util', @@ -2173,6 +2170,9 @@ return array( 'javelin-workflow', 'javelin-json', ), + 'f3af20b1' => array( + 'javelin-dom', + ), 'f50152ad' => array( 'phui-timeline-view-css', ), diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js index 76706fd8f1..50ec18e452 100644 --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -170,6 +170,8 @@ JX.install('DiffInline', { new JX.Workflow(inline_uri, {op: op, ids: comment_id}) .setHandler(JX.bag) .start(); + + JX.Stratcom.invoke('resize'); }, isHidden: function() { From 1493f08272dce974667e57c43b84f0e70c0dc187 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 May 2017 07:11:43 -0700 Subject: [PATCH 32/81] Emit resize events after making document changes during inline editing Summary: Ref T12634. Fixes T12633. These events allow the keyboard reticle to resize properly. (I expect to possibly hide/disable the reticle in the future during edits, but at least make the behavior sensible for now.) Test Plan: - Used "n" to select a block. - Clicked a line number in that block to start a new inline comment. - Saw reticle resize properly. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12634, T12633 Differential Revision: https://secure.phabricator.com/D17904 --- resources/celerity/map.php | 12 ++++++------ webroot/rsrc/js/application/diff/DiffInline.js | 6 ++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index cf4ec03237..2d5db2671d 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '8c5f913d', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', - 'differential.pkg.js' => 'a185599e', + 'differential.pkg.js' => '6ee9a850', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -392,7 +392,7 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', 'rsrc/js/application/diff/DiffChangeset.js' => 'f7100923', 'rsrc/js/application/diff/DiffChangesetList.js' => 'e5c5e171', - 'rsrc/js/application/diff/DiffInline.js' => 'f3af20b1', + 'rsrc/js/application/diff/DiffInline.js' => '4bbefc49', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', @@ -783,7 +783,7 @@ return array( 'phabricator-dashboard-css' => 'fe5b1869', 'phabricator-diff-changeset' => 'f7100923', 'phabricator-diff-changeset-list' => 'e5c5e171', - 'phabricator-diff-inline' => 'f3af20b1', + 'phabricator-diff-inline' => '4bbefc49', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -1259,6 +1259,9 @@ return array( 'javelin-util', 'phabricator-shaped-request', ), + '4bbefc49' => array( + 'javelin-dom', + ), '4c193c96' => array( 'javelin-behavior', 'javelin-uri', @@ -2170,9 +2173,6 @@ return array( 'javelin-workflow', 'javelin-json', ), - 'f3af20b1' => array( - 'javelin-dom', - ), 'f50152ad' => array( 'phui-timeline-view-css', ), diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js index 50ec18e452..16dc024d6e 100644 --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -438,6 +438,8 @@ JX.install('DiffInline', { row = next_row; } + JX.Stratcom.invoke('resize'); + return first_meta; }, @@ -516,6 +518,10 @@ JX.install('DiffInline', { // After making changes to inline comments, refresh the transaction // preview at the bottom of the page. this.getChangeset().getChangesetList().redrawPreview(); + + // Emit a resize event so that UI elements like the keyboad focus + // reticle can redraw properly. + JX.Stratcom.invoke('resize'); }, _redraw: function() { From a154407efb324f4c023e15d8353f0aa708e53287 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 May 2017 07:24:20 -0700 Subject: [PATCH 33/81] Retain keyboard cursor state across more inline edit operations in Differential Summary: Ref T12634. Fixes T8131. Currently, most edit operations (edit, reply, collapse, mark done) lose the keyboard cursor state. Instead, bind the state more tighlty to the inline object itself (instead of the rows which happen to be in the document), and then do a bit of recalculation to try to keep it selected across edits. Test Plan: - Used "n" to select an inline. - Clicked "Done" checkbox. - Pressed "n". - Went to the next inline (previously: lost position in document). - Behavior is also better for: edit, reply, collapse/expand. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12634, T8131 Differential Revision: https://secure.phabricator.com/D17905 --- resources/celerity/map.php | 48 +++++++++---------- .../rsrc/js/application/diff/DiffChangeset.js | 10 +--- .../js/application/diff/DiffChangesetList.js | 27 +++++++++-- .../rsrc/js/application/diff/DiffInline.js | 2 + 4 files changed, 52 insertions(+), 35 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 2d5db2671d..7741821c83 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '8c5f913d', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', - 'differential.pkg.js' => '6ee9a850', + 'differential.pkg.js' => '0bfd141c', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -390,9 +390,9 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => 'f7100923', - 'rsrc/js/application/diff/DiffChangesetList.js' => 'e5c5e171', - 'rsrc/js/application/diff/DiffInline.js' => '4bbefc49', + 'rsrc/js/application/diff/DiffChangeset.js' => '145c34e2', + 'rsrc/js/application/diff/DiffChangesetList.js' => 'ca3b6387', + 'rsrc/js/application/diff/DiffInline.js' => 'b5b1f167', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', @@ -781,9 +781,9 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => 'f7100923', - 'phabricator-diff-changeset-list' => 'e5c5e171', - 'phabricator-diff-inline' => '4bbefc49', + 'phabricator-diff-changeset' => '145c34e2', + 'phabricator-diff-changeset-list' => 'ca3b6387', + 'phabricator-diff-inline' => 'b5b1f167', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -999,6 +999,17 @@ return array( 'javelin-dom', 'javelin-typeahead-normalizer', ), + '145c34e2' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + 'phabricator-diff-inline', + ), '1499a8cb' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1259,9 +1270,6 @@ return array( 'javelin-util', 'phabricator-shaped-request', ), - '4bbefc49' => array( - 'javelin-dom', - ), '4c193c96' => array( 'javelin-behavior', 'javelin-uri', @@ -1834,6 +1842,9 @@ return array( 'javelin-dom', 'phabricator-draggable-list', ), + 'b5b1f167' => array( + 'javelin-dom', + ), 'b5c256b8' => array( 'javelin-install', 'javelin-dom', @@ -1973,6 +1984,9 @@ return array( 'phabricator-shaped-request', 'conpherence-thread-manager', ), + 'ca3b6387' => array( + 'javelin-install', + ), 'ca3f91eb' => array( 'javelin-behavior', 'javelin-dom', @@ -2137,9 +2151,6 @@ return array( 'javelin-workflow', 'javelin-magical-init', ), - 'e5c5e171' => array( - 'javelin-install', - ), 'e9581f08' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2188,17 +2199,6 @@ return array( 'phuix-icon-view', 'phabricator-prefab', ), - 'f7100923' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - 'phabricator-diff-inline', - ), 'f7fc67ec' => array( 'javelin-install', 'javelin-typeahead', diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index e74adfcb79..ba018478a6 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -369,17 +369,11 @@ JX.install('DiffChangeset', { for (var jj = 0; jj < block.items.length; jj++) { var inline = this.getInlineForRow(block.items[jj]); - // If this inline has been collapsed, don't select it with the - // keyboard cursor. - if (inline.isHidden()) { - continue; - } - items.push({ type: block.type, changeset: this, - target: block.items[jj], - inline: inline, + target: inline, + hidden: inline.isHidden(), nodes: { begin: block.items[jj], end: block.items[jj] diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index cdb61c0a15..a83e21e0ae 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -206,7 +206,7 @@ JX.install('DiffChangesetList', { if (cursor) { if (cursor.type == 'comment') { - var inline = cursor.inline; + var inline = cursor.target; if (inline.canReply()) { this.setFocus(null); @@ -225,7 +225,7 @@ JX.install('DiffChangesetList', { if (cursor) { if (cursor.type == 'comment') { - var inline = cursor.inline; + var inline = cursor.target; if (inline.canEdit()) { this.setFocus(null); @@ -284,6 +284,12 @@ JX.install('DiffChangesetList', { } } + // If the item is hidden, don't select it when iterating with jump + // keys. It can still potentially be selected in other ways. + if (items[cursor].hidden) { + continue; + } + // Otherwise, we've found a valid item to select. break; } @@ -328,13 +334,28 @@ JX.install('DiffChangesetList', { this.setFocus(cursor.nodes.begin, cursor.nodes.end); - if (scroll) { + if (manager && scroll) { manager.scrollTo(cursor.nodes.begin); } return this; }, + redrawCursor: function() { + // NOTE: This is setting the cursor to the current cursor. Usually, this + // would have no effect. + + // However, if the old cursor pointed at an inline and the inline has + // been edited so the rows have changed, this updates the cursor to point + // at the new inline with the proper rows for the current state, and + // redraws the reticle correctly. + + var state = this._getSelectionState(); + if (state.cursor !== null) { + this._setSelectionState(state.items[state.cursor]); + } + }, + _getSelectableItems: function() { var result = []; diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js index 16dc024d6e..2a4c444a86 100644 --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -519,6 +519,8 @@ JX.install('DiffInline', { // preview at the bottom of the page. this.getChangeset().getChangesetList().redrawPreview(); + this.getChangeset().getChangesetList().redrawCursor(); + // Emit a resize event so that UI elements like the keyboad focus // reticle can redraw properly. JX.Stratcom.invoke('resize'); From 1b5a276a02d664ff2c48929ccea895022dd61a49 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 May 2017 08:03:43 -0700 Subject: [PATCH 34/81] Add Differential keyboard shortcuts for "mark done" and "hide/show" Summary: Fixes T8130. Allows selected comments to be shown/hidden (with "q") or marked done/not-done (with "w"). (These key selections are because "qwer" are right next to each other on QWERTY keyboards, and now mean "hide, done, edit, reply".) Also, allow "N" and "P" to do next/previous inline, including hidden inlines. This makes "q" to hide/show a little more powerful and a little easier to undo. Test Plan: Used "q", "w", "N" and "P" to navigate and interact with comments. Reviewers: chad Reviewed By: chad Maniphest Tasks: T8130 Differential Revision: https://secure.phabricator.com/D17906 --- resources/celerity/map.php | 22 +++--- .../view/DifferentialChangesetListView.php | 15 ++++ .../js/application/diff/DiffChangesetList.js | 71 ++++++++++++++++--- .../rsrc/js/application/diff/DiffInline.js | 24 ++++++- 4 files changed, 109 insertions(+), 23 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 7741821c83..c16ac91ac1 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '8c5f913d', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', - 'differential.pkg.js' => '0bfd141c', + 'differential.pkg.js' => 'e486afd0', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -391,8 +391,8 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', 'rsrc/js/application/diff/DiffChangeset.js' => '145c34e2', - 'rsrc/js/application/diff/DiffChangesetList.js' => 'ca3b6387', - 'rsrc/js/application/diff/DiffInline.js' => 'b5b1f167', + 'rsrc/js/application/diff/DiffChangesetList.js' => 'd93e34c2', + 'rsrc/js/application/diff/DiffInline.js' => 'bdf6b568', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', @@ -782,8 +782,8 @@ return array( 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', 'phabricator-diff-changeset' => '145c34e2', - 'phabricator-diff-changeset-list' => 'ca3b6387', - 'phabricator-diff-inline' => 'b5b1f167', + 'phabricator-diff-changeset-list' => 'd93e34c2', + 'phabricator-diff-inline' => 'bdf6b568', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -1842,9 +1842,6 @@ return array( 'javelin-dom', 'phabricator-draggable-list', ), - 'b5b1f167' => array( - 'javelin-dom', - ), 'b5c256b8' => array( 'javelin-install', 'javelin-dom', @@ -1893,6 +1890,9 @@ return array( 'javelin-util', 'javelin-request', ), + 'bdf6b568' => array( + 'javelin-dom', + ), 'bea6e7f4' => array( 'javelin-install', 'javelin-dom', @@ -1984,9 +1984,6 @@ return array( 'phabricator-shaped-request', 'conpherence-thread-manager', ), - 'ca3b6387' => array( - 'javelin-install', - ), 'ca3f91eb' => array( 'javelin-behavior', 'javelin-dom', @@ -2082,6 +2079,9 @@ return array( 'javelin-util', 'phabricator-shaped-request', ), + 'd93e34c2' => array( + 'javelin-install', + ), 'de2e896f' => array( 'javelin-behavior', 'javelin-dom', diff --git a/src/applications/differential/view/DifferentialChangesetListView.php b/src/applications/differential/view/DifferentialChangesetListView.php index fea7a2da55..48b2ea5e12 100644 --- a/src/applications/differential/view/DifferentialChangesetListView.php +++ b/src/applications/differential/view/DifferentialChangesetListView.php @@ -252,6 +252,21 @@ final class DifferentialChangesetListView extends AphrontView { pht('You must select a comment to reply to.'), 'You must select a comment to edit.' => pht('You must select a comment to edit.'), + + 'Mark or unmark selected inline comment as done.' => + pht('Mark or unmark selected inline comment as done.'), + 'You must select a comment to mark done.' => + pht('You must select a comment to mark done.'), + + 'Hide or show inline comment.' => + pht('Hide or show inline comment.'), + 'You must select a comment to hide.' => + pht('You must select a comment to hide.'), + + 'Jump to next inline comment, including hidden comments.' => + pht('Jump to next inline comment, including hidden comments.'), + 'Jump to previous inline comment, including hidden comments.' => + pht('Jump to previous inline comment, including hidden comments.'), ), )); diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index a83e21e0ae..01275578fe 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -106,14 +106,27 @@ JX.install('DiffChangesetList', { label = pht('Jump to previous inline comment.'); this._installJumpKey('p', label, -1, 'comment'); + label = pht('Jump to next inline comment, including hidden comments.'); + this._installJumpKey('N', label, 1, 'comment', true); + + label = pht( + 'Jump to previous inline comment, including hidden comments.'); + this._installJumpKey('P', label, -1, 'comment', true); + label = pht('Jump to the table of contents.'); this._installKey('t', label, this._ontoc); label = pht('Reply to selected inline comment.'); - this._installKey('r', label, this._onreply); + this._installKey('r', label, this._onkeyreply); label = pht('Edit selected inline comment.'); - this._installKey('e', label, this._onedit); + this._installKey('e', label, this._onkeyedit); + + label = pht('Mark or unmark selected inline comment as done.'); + this._installKey('w', label, this._onkeydone); + + label = pht('Hide or show inline comment.'); + this._installKey('q', label, this._onkeyhide); }, isAsleep: function() { @@ -190,9 +203,9 @@ JX.install('DiffChangesetList', { .register(); }, - _installJumpKey: function(key, label, delta, filter) { + _installJumpKey: function(key, label, delta, filter, show_hidden) { filter = filter || null; - var handler = JX.bind(this, this._onjumpkey, delta, filter); + var handler = JX.bind(this, this._onjumpkey, delta, filter, show_hidden); return this._installKey(key, label, handler); }, @@ -201,7 +214,7 @@ JX.install('DiffChangesetList', { manager.scrollTo(toc); }, - _onreply: function(manager) { + _onkeyreply: function() { var cursor = this._cursorItem; if (cursor) { @@ -220,7 +233,7 @@ JX.install('DiffChangesetList', { this._warnUser(pht('You must select a comment to reply to.')); }, - _onedit: function(manager) { + _onkeyedit: function() { var cursor = this._cursorItem; if (cursor) { @@ -239,6 +252,44 @@ JX.install('DiffChangesetList', { this._warnUser(pht('You must select a comment to edit.')); }, + _onkeydone: function() { + var cursor = this._cursorItem; + + if (cursor) { + if (cursor.type == 'comment') { + var inline = cursor.target; + if (inline.canDone()) { + this.setFocus(null); + + inline.toggleDone(); + return; + } + } + } + + var pht = this.getTranslations(); + this._warnUser(pht('You must select a comment to mark done.')); + }, + + _onkeyhide: function() { + var cursor = this._cursorItem; + + if (cursor) { + if (cursor.type == 'comment') { + var inline = cursor.target; + if (inline.canHide()) { + this.setFocus(null); + + inline.setHidden(!inline.isHidden()); + return; + } + } + } + + var pht = this.getTranslations(); + this._warnUser(pht('You must select a comment to hide.')); + }, + _warnUser: function(message) { new JX.Notification() .setContent(message) @@ -247,7 +298,7 @@ JX.install('DiffChangesetList', { .show(); }, - _onjumpkey: function(delta, filter, manager) { + _onjumpkey: function(delta, filter, show_hidden, manager) { var state = this._getSelectionState(); var cursor = state.cursor; @@ -286,8 +337,10 @@ JX.install('DiffChangesetList', { // If the item is hidden, don't select it when iterating with jump // keys. It can still potentially be selected in other ways. - if (items[cursor].hidden) { - continue; + if (!show_hidden) { + if (items[cursor].hidden) { + continue; + } } // Otherwise, we've found a valid item to select. diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js index 2a4c444a86..04f9a03db1 100644 --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -130,6 +130,22 @@ JX.install('DiffInline', { return true; }, + canDone: function() { + if (!JX.DOM.scry(this._row, 'input', 'differential-inline-done').length) { + return false; + } + + return true; + }, + + canHide: function() { + if (!JX.DOM.scry(this._row, 'a', 'hide-inline').length) { + return false; + } + + return true; + }, + _hasAction: function(action) { var nodes = JX.DOM.scry(this._row, 'a', 'differential-inline-' + action); return (nodes.length > 0); @@ -171,7 +187,7 @@ JX.install('DiffInline', { .setHandler(JX.bag) .start(); - JX.Stratcom.invoke('resize'); + this._didUpdate(true); }, isHidden: function() { @@ -514,10 +530,12 @@ JX.install('DiffInline', { this._didUpdate(); }, - _didUpdate: function() { + _didUpdate: function(local_only) { // After making changes to inline comments, refresh the transaction // preview at the bottom of the page. - this.getChangeset().getChangesetList().redrawPreview(); + if (!local_only) { + this.getChangeset().getChangesetList().redrawPreview(); + } this.getChangeset().getChangesetList().redrawCursor(); From bf753c8b5a0859ce0d89d1ec5d71b60a017a3862 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 16 May 2017 08:50:55 -0700 Subject: [PATCH 35/81] Make passing an object to newCurtain optional Summary: We seem to already support this, just takes it fully there. We don't need to see things like "Flag", etc, on certain subpages of projects/people/etc. Test Plan: Review Members, Subproject pages, no longer see "Flag for Later" which only is for the Project itself. Check manage, still there. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17897 --- .../base/controller/PhabricatorController.php | 10 ++++++---- .../PhabricatorProjectMembersViewController.php | 2 +- .../PhabricatorProjectSubprojectsController.php | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php index 58d8f6cf6f..c4d1b47c40 100644 --- a/src/applications/base/controller/PhabricatorController.php +++ b/src/applications/base/controller/PhabricatorController.php @@ -471,7 +471,7 @@ abstract class PhabricatorController extends AphrontController { ->setViewer($this->getViewer()); } - public function newCurtainView($object) { + public function newCurtainView($object = null) { $viewer = $this->getViewer(); $action_id = celerity_generate_unique_node_id(); @@ -491,9 +491,11 @@ abstract class PhabricatorController extends AphrontController { ->setViewer($viewer) ->setActionList($action_list); - $panels = PHUICurtainExtension::buildExtensionPanels($viewer, $object); - foreach ($panels as $panel) { - $curtain->addPanel($panel); + if ($object) { + $panels = PHUICurtainExtension::buildExtensionPanels($viewer, $object); + foreach ($panels as $panel) { + $curtain->addPanel($panel); + } } return $curtain; diff --git a/src/applications/project/controller/PhabricatorProjectMembersViewController.php b/src/applications/project/controller/PhabricatorProjectMembersViewController.php index 30ff425a51..ad553eb664 100644 --- a/src/applications/project/controller/PhabricatorProjectMembersViewController.php +++ b/src/applications/project/controller/PhabricatorProjectMembersViewController.php @@ -70,7 +70,7 @@ final class PhabricatorProjectMembersViewController $viewer = $this->getViewer(); $id = $project->getID(); - $curtain = $this->newCurtainView($project); + $curtain = $this->newCurtainView(); $is_locked = $project->getIsMembershipLocked(); diff --git a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php index 4a89bb4cde..1aa0c0baac 100644 --- a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php +++ b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php @@ -203,7 +203,7 @@ final class PhabricatorProjectSubprojectsController $allows_milestones = $project->supportsMilestones(); $allows_subprojects = $project->supportsSubprojects(); - $curtain = $this->newCurtainView($project); + $curtain = $this->newCurtainView(); if ($allows_milestones && $milestones) { $milestone_text = pht('Create Next Milestone'); From 29cfcc82ef7f84580e798aebeb2abcb8ffec57d7 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 16 May 2017 09:26:15 -0700 Subject: [PATCH 36/81] Update Milestone/Subproject page Summary: Cleans up the UI, moves details over to curtain, adds some fallback no data strings. Test Plan: Review with and without subprojects, milestones. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17907 --- ...habricatorProjectSubprojectsController.php | 161 +++++++----------- .../view/PhabricatorProjectListView.php | 14 +- 2 files changed, 76 insertions(+), 99 deletions(-) diff --git a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php index 1aa0c0baac..f516f87e32 100644 --- a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php +++ b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php @@ -26,6 +26,9 @@ final class PhabricatorProjectSubprojectsController $allows_subprojects = $project->supportsSubprojects(); $allows_milestones = $project->supportsMilestones(); + $subproject_list = null; + $milestone_list = null; + if ($allows_subprojects) { $subprojects = id(new PhabricatorProjectQuery()) ->setViewer($viewer) @@ -33,6 +36,16 @@ final class PhabricatorProjectSubprojectsController ->needImages(true) ->withIsMilestone(false) ->execute(); + + $subproject_list = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('%s Subprojects', $project->getName())) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->setObjectList( + id(new PhabricatorProjectListView()) + ->setUser($viewer) + ->setProjects($subprojects) + ->setNoDataString(pht('This project has no subprojects.')) + ->renderList()); } else { $subprojects = array(); } @@ -45,52 +58,25 @@ final class PhabricatorProjectSubprojectsController ->withIsMilestone(true) ->setOrderVector(array('milestoneNumber', 'id')) ->execute(); - } else { - $milestones = array(); - } - if ($milestones) { $milestone_list = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Milestones')) + ->setHeaderText(pht('%s Milestones', $project->getName())) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setObjectList( id(new PhabricatorProjectListView()) ->setUser($viewer) ->setProjects($milestones) + ->setNoDataString(pht('This project has no milestones.')) ->renderList()); } else { - $milestone_list = null; + $milestones = array(); } - if ($subprojects) { - $subproject_list = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Subprojects')) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->setObjectList( - id(new PhabricatorProjectListView()) - ->setUser($viewer) - ->setProjects($subprojects) - ->renderList()); - } else { - $subproject_list = null; - } - - $property_list = $this->buildPropertyList( - $project, - $milestones, - $subprojects); - $curtain = $this->buildCurtainView( $project, $milestones, $subprojects); - - $details = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Details')) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->addPropertyList($property_list); - $nav = $this->getProfileMenu(); $nav->selectFilter(PhabricatorProject::ITEM_SUBPROJECTS); @@ -102,11 +88,24 @@ final class PhabricatorProjectSubprojectsController ->setHeader(pht('Subprojects and Milestones')) ->setHeaderIcon('fa-sitemap'); + require_celerity_resource('project-view-css'); + + // This page isn't reachable via UI, but make it pretty anyways. + $info_view = null; + if (!$milestone_list && !$subproject_list) { + $info_view = id(new PHUIInfoView()) + ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) + ->appendChild(pht('Milestone projects do not support subprojects '. + 'or milestones.')); + } + $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setCurtain($curtain) + ->addClass('project-view-home') + ->addClass('project-view-people-home') ->setMainColumn(array( - $details, + $info_view, $milestone_list, $subproject_list, )); @@ -118,73 +117,6 @@ final class PhabricatorProjectSubprojectsController ->appendChild($view); } - private function buildPropertyList( - PhabricatorProject $project, - array $milestones, - array $subprojects) { - $viewer = $this->getViewer(); - - $view = id(new PHUIPropertyListView()) - ->setUser($viewer); - - $view->addProperty( - pht('Prototype'), - $this->renderStatus( - 'fa-exclamation-triangle red', - pht('Warning'), - pht('Subprojects and milestones are only partially implemented.'))); - - if (!$project->supportsMilestones()) { - $milestone_status = $this->renderStatus( - 'fa-times grey', - pht('Already Milestone'), - pht( - 'This project is already a milestone, and milestones may not '. - 'have their own milestones.')); - } else { - if (!$milestones) { - $milestone_status = $this->renderStatus( - 'fa-check grey', - pht('None Created'), - pht( - 'You can create milestones for this project.')); - } else { - $milestone_status = $this->renderStatus( - 'fa-check green', - pht('Has Milestones'), - pht('This project has milestones.')); - } - } - - $view->addProperty(pht('Milestones'), $milestone_status); - - if (!$project->supportsSubprojects()) { - $subproject_status = $this->renderStatus( - 'fa-times grey', - pht('Milestone'), - pht( - 'This project is a milestone, and milestones may not have '. - 'subprojects.')); - } else { - if (!$subprojects) { - $subproject_status = $this->renderStatus( - 'fa-check grey', - pht('None Created'), - pht('You can create subprojects for this project.')); - } else { - $subproject_status = $this->renderStatus( - 'fa-check green', - pht('Has Subprojects'), - pht( - 'This project has subprojects.')); - } - } - - $view->addProperty(pht('Subprojects'), $subproject_status); - - return $view; - } - private function buildCurtainView( PhabricatorProject $project, array $milestones, @@ -244,6 +176,39 @@ final class PhabricatorProjectSubprojectsController ->setDisabled($subproject_disabled) ->setWorkflow($subproject_workflow)); + + if (!$project->supportsMilestones()) { + $note = pht( + 'This project is already a milestone, and milestones may not '. + 'have their own milestones.'); + } else { + if (!$milestones) { + $note = pht('You can create milestones for this project.'); + } else { + $note = pht('This project has milestones.'); + } + } + + $curtain->newPanel() + ->setHeaderText(pht('Milestones')) + ->appendChild($note); + + if (!$project->supportsSubprojects()) { + $note = pht( + 'This project is a milestone, and milestones may not have '. + 'subprojects.'); + } else { + if (!$subprojects) { + $note = pht('You can create subprojects for this project.'); + } else { + $note = pht('This project has subprojects.'); + } + } + + $curtain->newPanel() + ->setHeaderText(pht('Subprojects')) + ->appendChild($note); + return $curtain; } diff --git a/src/applications/project/view/PhabricatorProjectListView.php b/src/applications/project/view/PhabricatorProjectListView.php index 994e78ce76..d1d0792855 100644 --- a/src/applications/project/view/PhabricatorProjectListView.php +++ b/src/applications/project/view/PhabricatorProjectListView.php @@ -5,6 +5,7 @@ final class PhabricatorProjectListView extends AphrontView { private $projects; private $showMember; private $showWatching; + private $noDataString; public function setProjects(array $projects) { $this->projects = $projects; @@ -25,6 +26,11 @@ final class PhabricatorProjectListView extends AphrontView { return $this; } + public function setNoDataString($text) { + $this->noDataString = $text; + return $this; + } + public function renderList() { $viewer = $this->getUser(); $viewer_phid = $viewer->getPHID(); @@ -32,8 +38,14 @@ final class PhabricatorProjectListView extends AphrontView { $handles = $viewer->loadHandles(mpull($projects, 'getPHID')); + $no_data = pht('No projects found.'); + if ($this->noDataString) { + $no_data = $this->noDataString; + } + $list = id(new PHUIObjectItemListView()) - ->setUser($viewer); + ->setUser($viewer) + ->setNoDataString($no_data); foreach ($projects as $key => $project) { $id = $project->getID(); From 8052ab84bfd8b3f3cb571d693ea2fe566b1f9b65 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 May 2017 09:29:05 -0700 Subject: [PATCH 37/81] Remove "^" (Prev) and "V" (Next) actions on Differential inline comments Summary: Ref T12616. Fixes T12715. I suspect these are very rarely used. (I think you tried to get rid of them before but I pushed back since we couldn't really offer great alternatives at the time?) Now that the code is in a better place: - Click an inline's header (just the colored part) to select it with the keyboard selection cursor. - Click again to deselect it. - You can use "n" and "p" to jump to comments, so "click + n" is the same as the old "V" action. - This also makes it easier to swap between keyboard and mouse workflows, since you can jump into things with the keyboard at any inline. Also, make "Reply" render more consistently. Test Plan: - Did all that stuff, things seemed to work OK. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12715, T12616 Differential Revision: https://secure.phabricator.com/D17908 --- resources/celerity/map.php | 20 ++-- resources/celerity/packages.php | 1 - .../view/DifferentialChangesetListView.php | 2 - .../view/PHUIDiffInlineCommentDetailView.php | 100 ++++++++---------- .../js/application/diff/DiffChangesetList.js | 47 ++++++++ .../differential/behavior-comment-jump.js | 32 ------ 6 files changed, 95 insertions(+), 107 deletions(-) delete mode 100644 webroot/rsrc/js/application/differential/behavior-comment-jump.js diff --git a/resources/celerity/map.php b/resources/celerity/map.php index c16ac91ac1..7e2a517ed8 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '8c5f913d', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', - 'differential.pkg.js' => 'e486afd0', + 'differential.pkg.js' => 'bd321b6e', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -391,10 +391,9 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', 'rsrc/js/application/diff/DiffChangeset.js' => '145c34e2', - 'rsrc/js/application/diff/DiffChangesetList.js' => 'd93e34c2', + 'rsrc/js/application/diff/DiffChangesetList.js' => 'f1101e6e', 'rsrc/js/application/diff/DiffInline.js' => 'bdf6b568', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', - 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '89d11432', @@ -619,7 +618,6 @@ return array( 'javelin-behavior-detect-timezone' => '4c193c96', 'javelin-behavior-device' => 'bb1dd507', 'javelin-behavior-diff-preview-link' => '051c7832', - 'javelin-behavior-differential-comment-jump' => '4fdb476d', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', 'javelin-behavior-differential-edit-inline-comments' => '89d11432', 'javelin-behavior-differential-feedback-preview' => 'b064af76', @@ -782,7 +780,7 @@ return array( 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', 'phabricator-diff-changeset' => '145c34e2', - 'phabricator-diff-changeset-list' => 'd93e34c2', + 'phabricator-diff-changeset-list' => 'f1101e6e', 'phabricator-diff-inline' => 'bdf6b568', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', @@ -1291,11 +1289,6 @@ return array( 'javelin-stratcom', 'javelin-dom', ), - '4fdb476d' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - ), '503e17fd' => array( 'javelin-install', 'javelin-typeahead-source', @@ -2079,9 +2072,6 @@ return array( 'javelin-util', 'phabricator-shaped-request', ), - 'd93e34c2' => array( - 'javelin-install', - ), 'de2e896f' => array( 'javelin-behavior', 'javelin-dom', @@ -2184,6 +2174,9 @@ return array( 'javelin-workflow', 'javelin-json', ), + 'f1101e6e' => array( + 'javelin-install', + ), 'f50152ad' => array( 'phui-timeline-view-css', ), @@ -2431,7 +2424,6 @@ return array( 'javelin-behavior-differential-edit-inline-comments', 'javelin-behavior-differential-populate', 'javelin-behavior-differential-diff-radios', - 'javelin-behavior-differential-comment-jump', 'javelin-behavior-aphront-drag-and-drop-textarea', 'javelin-behavior-phabricator-object-selector', 'javelin-behavior-repository-crossreference', diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php index 364a695f18..9ea3fa6b35 100644 --- a/resources/celerity/packages.php +++ b/resources/celerity/packages.php @@ -196,7 +196,6 @@ return array( 'javelin-behavior-differential-edit-inline-comments', 'javelin-behavior-differential-populate', 'javelin-behavior-differential-diff-radios', - 'javelin-behavior-differential-comment-jump', 'javelin-behavior-aphront-drag-and-drop-textarea', 'javelin-behavior-phabricator-object-selector', 'javelin-behavior-repository-crossreference', diff --git a/src/applications/differential/view/DifferentialChangesetListView.php b/src/applications/differential/view/DifferentialChangesetListView.php index 48b2ea5e12..c69eef008f 100644 --- a/src/applications/differential/view/DifferentialChangesetListView.php +++ b/src/applications/differential/view/DifferentialChangesetListView.php @@ -270,8 +270,6 @@ final class DifferentialChangesetListView extends AphrontView { ), )); - $this->initBehavior('differential-comment-jump', array()); - if ($this->inlineURI) { Javelin::initBehavior('differential-edit-inline-comments', array( 'uri' => $this->inlineURI, diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php index 9f5616f1cc..cd8c3202b7 100644 --- a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php +++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php @@ -190,61 +190,31 @@ final class PHUIDiffInlineCommentDetailView } } - $nextprev = null; - if (!$this->preview) { - $nextprev = new PHUIButtonBarView(); - $nextprev->setBorderless(true); - $nextprev->addClass('inline-button-divider'); - - - $up = id(new PHUIButtonView()) - ->setTag('a') - ->setTooltip(pht('Previous')) - ->setIcon('fa-chevron-up') - ->addSigil('differential-inline-prev') - ->setMustCapture(true); - - $down = id(new PHUIButtonView()) - ->setTag('a') - ->setTooltip(pht('Next')) - ->setIcon('fa-chevron-down') - ->addSigil('differential-inline-next') - ->setMustCapture(true); - - if ($this->canHide()) { - $hide = id(new PHUIButtonView()) - ->setTag('a') - ->setTooltip(pht('Hide Comment')) - ->setIcon('fa-times') - ->addSigil('hide-inline') - ->setMustCapture(true); - - $nextprev->addButton($hide); - } - - $nextprev->addButton($up); - $nextprev->addButton($down); - - $action_buttons = array(); - if ($this->allowReply) { - if (!$is_synthetic) { - // NOTE: No product reason why you can't reply to these, but the reply - // mechanism currently sends the inline comment ID to the server, not - // file/line information, and synthetic comments don't have an inline - // comment ID. - - $action_buttons[] = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon('fa-reply') - ->setTooltip(pht('Reply')) - ->addSigil('differential-inline-reply') - ->setMustCapture(true); - } - } - } - $anchor_name = $this->getAnchorName(); + $action_buttons = array(); + + $can_reply = + (!$this->editable) && + (!$this->preview) && + ($this->allowReply) && + + // NOTE: No product reason why you can't reply to synthetic comments, + // but the reply mechanism currently sends the inline comment ID to the + // server, not file/line information, and synthetic comments don't have + // an inline comment ID. + (!$is_synthetic); + + + if ($can_reply) { + $action_buttons[] = id(new PHUIButtonView()) + ->setTag('a') + ->setIcon('fa-reply') + ->setTooltip(pht('Reply')) + ->addSigil('differential-inline-reply') + ->setMustCapture(true); + } + if ($this->editable && !$this->preview) { $action_buttons[] = id(new PHUIButtonView()) ->setTag('a') @@ -280,6 +250,15 @@ final class PHUIDiffInlineCommentDetailView ->setMustCapture(true); } + if (!$this->preview && $this->canHide()) { + $action_buttons[] = id(new PHUIButtonView()) + ->setTag('a') + ->setTooltip(pht('Hide Comment')) + ->setIcon('fa-times') + ->addSigil('hide-inline') + ->setMustCapture(true); + } + $done_button = null; if (!$is_synthetic) { @@ -430,7 +409,6 @@ final class PHUIDiffInlineCommentDetailView $done_button, $links, $actions, - $nextprev, )); $markup = javelin_tag( @@ -441,10 +419,16 @@ final class PHUIDiffInlineCommentDetailView 'meta' => $metadata, ), array( - phutil_tag_div('differential-inline-comment-head grouped', array( - $group_left, - $group_right, - )), + javelin_tag( + 'div', + array( + 'class' => 'differential-inline-comment-head grouped', + 'sigil' => 'differential-inline-header', + ), + array( + $group_left, + $group_right, + )), phutil_tag_div( 'differential-inline-comment-content', phutil_tag_div('phabricator-remarkup', $content)), diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index 01275578fe..6380ccbf42 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -50,6 +50,12 @@ JX.install('DiffChangesetList', { var onresize = JX.bind(this, this._ifawake, this._onresize); JX.Stratcom.listen('resize', null, onresize); + + var onselect = JX.bind(this, this._ifawake, this._onselect); + JX.Stratcom.listen( + 'mousedown', + ['differential-inline-comment', 'differential-inline-header'], + onselect); }, properties: { @@ -665,6 +671,47 @@ JX.install('DiffChangesetList', { this._redrawFocus(); }, + _onselect: function(e) { + // If the user clicked some element inside the header, like an action + // icon, ignore the event. They have to click the header element itself. + if (e.getTarget() !== e.getNode('differential-inline-header')) { + return; + } + + var inline = this._getInlineForEvent(e); + if (!inline) { + return; + } + + // The user definitely clicked an inline, so we're going to handle the + // event. + e.kill(); + + var selection = this._getSelectionState(); + var item; + + // If the comment the user clicked is currently selected, deselect it. + // This makes it easy to undo things if you clicked by mistake. + if (selection.cursor !== null) { + item = selection.items[selection.cursor]; + if (item.target === inline) { + this._setSelectionState(null); + return; + } + } + + // Otherwise, select the item that the user clicked. This makes it + // easier to resume keyboard operations after using the mouse to do + // something else. + var items = selection.items; + for (var ii = 0; ii < items.length; ii++) { + item = items[ii]; + if (item.target === inline) { + this._setSelectionState(item); + } + } + }, + _onaction: function(action, e) { e.kill(); diff --git a/webroot/rsrc/js/application/differential/behavior-comment-jump.js b/webroot/rsrc/js/application/differential/behavior-comment-jump.js deleted file mode 100644 index 53d43fd05e..0000000000 --- a/webroot/rsrc/js/application/differential/behavior-comment-jump.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @provides javelin-behavior-differential-comment-jump - * @requires javelin-behavior - * javelin-stratcom - * javelin-dom - */ - -JX.behavior('differential-comment-jump', function() { - function handle_jump(offset) { - return function(e) { - var parent = JX.$('differential-review-stage'); - var clicked = e.getNode('differential-inline-comment'); - var inlines = JX.DOM.scry(parent, 'div', 'differential-inline-comment'); - var jumpto = null; - - for (var ii = 0; ii < inlines.length; ii++) { - if (inlines[ii] == clicked) { - jumpto = inlines[(ii + offset + inlines.length) % inlines.length]; - break; - } - } - JX.Stratcom.invoke('differential-toggle-file-request', null, { - element: jumpto - }); - JX.DOM.scrollTo(jumpto); - e.kill(); - }; - } - - JX.Stratcom.listen('click', 'differential-inline-prev', handle_jump(-1)); - JX.Stratcom.listen('click', 'differential-inline-next', handle_jump(+1)); -}); From fdf001739c3af403a37695fd99d8b542c14b2578 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 16 May 2017 09:53:38 -0700 Subject: [PATCH 38/81] Normalize headers and actions in Project sub pages Summary: Run through all the pages in projects and make sure they all feel similar. Adds back curtain on board manage page, even though it is sad for only having a single action. Test Plan: Test all pages on a project for consistency in UI. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17909 --- ...habricatorProjectBoardManageController.php | 41 +++++++++++-------- ...abricatorProjectColumnDetailController.php | 3 +- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/applications/project/controller/PhabricatorProjectBoardManageController.php b/src/applications/project/controller/PhabricatorProjectBoardManageController.php index 4607b50c2a..5c71dcfb61 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardManageController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardManageController.php @@ -31,6 +31,7 @@ final class PhabricatorProjectBoardManageController $board_id = $board->getID(); $header = $this->buildHeaderView($board); + $curtain = $this->buildCurtainView($board); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Workboard'), "/project/board/{$board_id}/"); @@ -46,7 +47,8 @@ final class PhabricatorProjectBoardManageController ->setHeader($header) ->addClass('project-view-home') ->addClass('project-view-people-home') - ->setFooter($columns_list); + ->setCurtain($curtain) + ->setMainColumn($columns_list); $title = array( pht('Manage Workboard'), @@ -63,29 +65,35 @@ final class PhabricatorProjectBoardManageController private function buildHeaderView(PhabricatorProject $board) { $viewer = $this->getViewer(); + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Workboard: %s', $board->getDisplayName())) + ->setUser($viewer); + + return $header; + } + + private function buildCurtainView(PhabricatorProject $board) { + $viewer = $this->getViewer(); + $id = $board->getID(); + + $curtain = $this->newCurtainView(); + $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $board, PhabricatorPolicyCapability::CAN_EDIT); - $id = $board->getID(); $disable_uri = $this->getApplicationURI("board/{$id}/disable/"); - $button = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon('fa-ban') - ->setText(pht('Disable Board')) - ->setHref($disable_uri) - ->setDisabled(!$can_edit) - ->setWorkflow(true); + $curtain->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-ban') + ->setName(pht('Disable Workboard')) + ->setHref($disable_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(true)); - $header = id(new PHUIHeaderView()) - ->setHeader(pht('Workboard: %s', $board->getDisplayName())) - ->setUser($viewer) - ->setProfileHeader(true) - ->addActionLink($button); - - return $header; + return $curtain; } private function buildColumnsList( @@ -126,6 +134,7 @@ final class PhabricatorProjectBoardManageController return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Columns')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setObjectList($view); } diff --git a/src/applications/project/controller/PhabricatorProjectColumnDetailController.php b/src/applications/project/controller/PhabricatorProjectColumnDetailController.php index 56eb16613f..24efec5ebb 100644 --- a/src/applications/project/controller/PhabricatorProjectColumnDetailController.php +++ b/src/applications/project/controller/PhabricatorProjectColumnDetailController.php @@ -75,8 +75,7 @@ final class PhabricatorProjectColumnDetailController $header = id(new PHUIHeaderView()) ->setHeader(pht('Column: %s', $column->getDisplayName())) - ->setUser($viewer) - ->setProfileHeader(true); + ->setUser($viewer); if ($column->isHidden()) { $header->setStatus('fa-ban', 'dark', pht('Hidden')); From 86b9deb8a9e9f0de568c75971f6d4acd4e677cb9 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 May 2017 10:02:09 -0700 Subject: [PATCH 39/81] Move inline anchors up, to dolumn-level Summary: Fixes T8420. Now that hidden inlines no longer fold into a big clump, anchors can just jump to them in a normal way. Move the anchors up a smidge so thing work. Test Plan: Clicked an anchor pointed at a hidden inline, ended up in the right place. Reviewers: chad Reviewed By: chad Maniphest Tasks: T8420 Differential Revision: https://secure.phabricator.com/D17910 --- .../diff/view/PHUIDiffInlineCommentDetailView.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php index cd8c3202b7..9c3c55292e 100644 --- a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php +++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php @@ -405,7 +405,6 @@ final class PHUIDiffInlineCommentDetailView 'class' => 'inline-head-right', ), array( - $anchor, $done_button, $links, $actions, @@ -445,6 +444,7 @@ final class PHUIDiffInlineCommentDetailView pht('...')); return array( + $anchor, $markup, $summary, ); From 325682248a8e461f1ab59223815f8c8cf08bf1a7 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 May 2017 10:18:31 -0700 Subject: [PATCH 40/81] If there's an anchor in the URL in Differential, don't stick to the bottom of the page as content loads Summary: Fixes T11784. A lot of things are interacting here, but this probably gets slightly better results slightly more often? Basically: - When we load content, we try to keep the viewport "stable" on the page, so the page doesn't jump around like crazy. - If you're near the top or bottom of the page, we try to stick to the top (e.g., reading the summary) or bottom (e.g., writing a comment). - But, if you followed an anchor to a comment that's close to the bottom of the page, we might stick to the bottom intead of staying with the anchor. Kind of do a better job by not sticking to the bottom if you have an anchor. This will get things wrong if you follow an anchor, scroll down, start writing a comment, etc. But this whole thing is a pile of guesses anyway. Test Plan: - Followed an anchor, saw non-sticky stabilization. - Loaded the page normally, scrolled to the bottom real fast, saw sticky stabilization. Reviewers: chad Reviewed By: chad Maniphest Tasks: T11784 Differential Revision: https://secure.phabricator.com/D17911 --- resources/celerity/map.php | 28 +++++++++---------- .../rsrc/js/application/diff/DiffChangeset.js | 6 ++++ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 7e2a517ed8..b389a84ba5 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '8c5f913d', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '58712637', - 'differential.pkg.js' => 'bd321b6e', + 'differential.pkg.js' => 'f1b636fb', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -390,7 +390,7 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => '145c34e2', + 'rsrc/js/application/diff/DiffChangeset.js' => 'c5742feb', 'rsrc/js/application/diff/DiffChangesetList.js' => 'f1101e6e', 'rsrc/js/application/diff/DiffInline.js' => 'bdf6b568', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', @@ -779,7 +779,7 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => '145c34e2', + 'phabricator-diff-changeset' => 'c5742feb', 'phabricator-diff-changeset-list' => 'f1101e6e', 'phabricator-diff-inline' => 'bdf6b568', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', @@ -997,17 +997,6 @@ return array( 'javelin-dom', 'javelin-typeahead-normalizer', ), - '145c34e2' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - 'phabricator-diff-inline', - ), '1499a8cb' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1926,6 +1915,17 @@ return array( 'javelin-stratcom', 'phabricator-tooltip', ), + 'c5742feb' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + 'phabricator-diff-inline', + ), 'c587b80f' => array( 'javelin-install', ), diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index ba018478a6..3130fced44 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -440,6 +440,12 @@ JX.install('DiffChangeset', { var near_top = (old_pos.y <= sticky); var near_bot = ((old_pos.y + old_view.y) >= (old_dim.y - sticky)); + // If we have an anchor in the URL, never stick to the bottom of the + // page. See T11784 for discussion. + if (window.location.hash) { + near_bot = false; + } + var target_pos = JX.Vector.getPos(target); var target_dim = JX.Vector.getDim(target); var target_mid = (target_pos.y + (target_dim.y / 2)); From a53d387ea6a08498fd19b8f53a9d3526fc2df68b Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 16 May 2017 10:53:01 -0700 Subject: [PATCH 41/81] Move Phriction Title transaction to Modular Transactions Summary: Ref T12625. Moves TYPE_TITLE to modular transaction. Test Plan: New Document, Edit Document, test validation, verify feed stories. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T12625 Differential Revision: https://secure.phabricator.com/D17912 --- src/__phutil_library_map__.php | 6 +- .../PhrictionCreateConduitAPIMethod.php | 2 +- .../conduit/PhrictionEditConduitAPIMethod.php | 2 +- .../controller/PhrictionEditController.php | 5 +- .../editor/PhrictionTransactionEditor.php | 46 ++------- .../storage/PhrictionTransaction.php | 68 ++------------ .../PhrictionDocumentTitleTransaction.php | 94 +++++++++++++++++++ .../PhrictionDocumentTransactionType.php | 4 + .../storage/PhabricatorModularTransaction.php | 4 +- 9 files changed, 125 insertions(+), 106 deletions(-) create mode 100644 src/applications/phriction/xaction/PhrictionDocumentTitleTransaction.php create mode 100644 src/applications/phriction/xaction/PhrictionDocumentTransactionType.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index f08a02579e..ba74adb658 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -4621,6 +4621,8 @@ phutil_register_library_map(array( 'PhrictionDocumentQuery' => 'applications/phriction/query/PhrictionDocumentQuery.php', 'PhrictionDocumentStatus' => 'applications/phriction/constants/PhrictionDocumentStatus.php', 'PhrictionDocumentTitleHeraldField' => 'applications/phriction/herald/PhrictionDocumentTitleHeraldField.php', + 'PhrictionDocumentTitleTransaction' => 'applications/phriction/xaction/PhrictionDocumentTitleTransaction.php', + 'PhrictionDocumentTransactionType' => 'applications/phriction/xaction/PhrictionDocumentTransactionType.php', 'PhrictionEditConduitAPIMethod' => 'applications/phriction/conduit/PhrictionEditConduitAPIMethod.php', 'PhrictionEditController' => 'applications/phriction/controller/PhrictionEditController.php', 'PhrictionHistoryConduitAPIMethod' => 'applications/phriction/conduit/PhrictionHistoryConduitAPIMethod.php', @@ -10257,6 +10259,8 @@ phutil_register_library_map(array( 'PhrictionDocumentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhrictionDocumentStatus' => 'PhrictionConstants', 'PhrictionDocumentTitleHeraldField' => 'PhrictionDocumentHeraldField', + 'PhrictionDocumentTitleTransaction' => 'PhrictionDocumentTransactionType', + 'PhrictionDocumentTransactionType' => 'PhabricatorModularTransactionType', 'PhrictionEditConduitAPIMethod' => 'PhrictionConduitAPIMethod', 'PhrictionEditController' => 'PhrictionController', 'PhrictionHistoryConduitAPIMethod' => 'PhrictionConduitAPIMethod', @@ -10270,7 +10274,7 @@ phutil_register_library_map(array( 'PhrictionReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'PhrictionSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'PhrictionSearchEngine' => 'PhabricatorApplicationSearchEngine', - 'PhrictionTransaction' => 'PhabricatorApplicationTransaction', + 'PhrictionTransaction' => 'PhabricatorModularTransaction', 'PhrictionTransactionComment' => 'PhabricatorApplicationTransactionComment', 'PhrictionTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 'PhrictionTransactionQuery' => 'PhabricatorApplicationTransactionQuery', diff --git a/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php b/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php index 36c74f4a68..4b05874410 100644 --- a/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php +++ b/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php @@ -47,7 +47,7 @@ final class PhrictionCreateConduitAPIMethod extends PhrictionConduitAPIMethod { $xactions = array(); $xactions[] = id(new PhrictionTransaction()) - ->setTransactionType(PhrictionTransaction::TYPE_TITLE) + ->setTransactionType(PhrictionDocumentTitleTransaction::TRANSACTIONTYPE) ->setNewValue($request->getValue('title')); $xactions[] = id(new PhrictionTransaction()) ->setTransactionType(PhrictionTransaction::TYPE_CONTENT) diff --git a/src/applications/phriction/conduit/PhrictionEditConduitAPIMethod.php b/src/applications/phriction/conduit/PhrictionEditConduitAPIMethod.php index e99a866529..d4c2b63b5a 100644 --- a/src/applications/phriction/conduit/PhrictionEditConduitAPIMethod.php +++ b/src/applications/phriction/conduit/PhrictionEditConduitAPIMethod.php @@ -42,7 +42,7 @@ final class PhrictionEditConduitAPIMethod extends PhrictionConduitAPIMethod { $xactions = array(); $xactions[] = id(new PhrictionTransaction()) - ->setTransactionType(PhrictionTransaction::TYPE_TITLE) + ->setTransactionType(PhrictionDocumentTitleTransaction::TRANSACTIONTYPE) ->setNewValue($request->getValue('title')); $xactions[] = id(new PhrictionTransaction()) ->setTransactionType(PhrictionTransaction::TYPE_CONTENT) diff --git a/src/applications/phriction/controller/PhrictionEditController.php b/src/applications/phriction/controller/PhrictionEditController.php index 038861d136..1917348c24 100644 --- a/src/applications/phriction/controller/PhrictionEditController.php +++ b/src/applications/phriction/controller/PhrictionEditController.php @@ -133,7 +133,7 @@ final class PhrictionEditController $xactions = array(); $xactions[] = id(new PhrictionTransaction()) - ->setTransactionType(PhrictionTransaction::TYPE_TITLE) + ->setTransactionType(PhrictionDocumentTitleTransaction::TRANSACTIONTYPE) ->setNewValue($title); $xactions[] = id(new PhrictionTransaction()) ->setTransactionType(PhrictionTransaction::TYPE_CONTENT) @@ -174,7 +174,8 @@ final class PhrictionEditController } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; $e_title = nonempty( - $ex->getShortMessage(PhrictionTransaction::TYPE_TITLE), + $ex->getShortMessage( + PhrictionDocumentTitleTransaction::TRANSACTIONTYPE), true); $e_content = nonempty( $ex->getShortMessage(PhrictionTransaction::TYPE_CONTENT), diff --git a/src/applications/phriction/editor/PhrictionTransactionEditor.php b/src/applications/phriction/editor/PhrictionTransactionEditor.php index af238dfcf9..95575a7619 100644 --- a/src/applications/phriction/editor/PhrictionTransactionEditor.php +++ b/src/applications/phriction/editor/PhrictionTransactionEditor.php @@ -29,7 +29,7 @@ final class PhrictionTransactionEditor return $this; } - private function getOldContent() { + public function getOldContent() { return $this->oldContent; } @@ -38,7 +38,7 @@ final class PhrictionTransactionEditor return $this; } - private function getNewContent() { + public function getNewContent() { return $this->newContent; } @@ -80,7 +80,6 @@ final class PhrictionTransactionEditor public function getTransactionTypes() { $types = parent::getTransactionTypes(); - $types[] = PhrictionTransaction::TYPE_TITLE; $types[] = PhrictionTransaction::TYPE_CONTENT; $types[] = PhrictionTransaction::TYPE_DELETE; $types[] = PhrictionTransaction::TYPE_MOVE_TO; @@ -99,11 +98,6 @@ final class PhrictionTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhrictionTransaction::TYPE_TITLE: - if ($this->getIsNewObject()) { - return null; - } - return $this->getOldContent()->getTitle(); case PhrictionTransaction::TYPE_CONTENT: if ($this->getIsNewObject()) { return null; @@ -121,7 +115,6 @@ final class PhrictionTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhrictionTransaction::TYPE_TITLE: case PhrictionTransaction::TYPE_CONTENT: case PhrictionTransaction::TYPE_DELETE: return $xaction->getNewValue(); @@ -154,7 +147,7 @@ final class PhrictionTransactionEditor foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { - case PhrictionTransaction::TYPE_TITLE: + case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: case PhrictionTransaction::TYPE_CONTENT: case PhrictionTransaction::TYPE_DELETE: case PhrictionTransaction::TYPE_MOVE_TO: @@ -178,7 +171,6 @@ final class PhrictionTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhrictionTransaction::TYPE_TITLE: case PhrictionTransaction::TYPE_CONTENT: case PhrictionTransaction::TYPE_MOVE_TO: $object->setStatus(PhrictionDocumentStatus::STATUS_EXISTS); @@ -232,9 +224,6 @@ final class PhrictionTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhrictionTransaction::TYPE_TITLE: - $this->getNewContent()->setTitle($xaction->getNewValue()); - break; case PhrictionTransaction::TYPE_CONTENT: $this->getNewContent()->setContent($xaction->getNewValue()); break; @@ -270,7 +259,7 @@ final class PhrictionTransactionEditor $save_content = false; foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { - case PhrictionTransaction::TYPE_TITLE: + case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: case PhrictionTransaction::TYPE_CONTENT: case PhrictionTransaction::TYPE_DELETE: case PhrictionTransaction::TYPE_MOVE_AWAY: @@ -312,7 +301,8 @@ final class PhrictionTransactionEditor $slug); $stub_xactions = array(); $stub_xactions[] = id(new PhrictionTransaction()) - ->setTransactionType(PhrictionTransaction::TYPE_TITLE) + ->setTransactionType( + PhrictionDocumentTitleTransaction::TRANSACTIONTYPE) ->setNewValue(PhabricatorSlug::getDefaultTitle($slug)) ->setMetadataValue('stub:create:phid', $object->getPHID()); $stub_xactions[] = id(new PhrictionTransaction()) @@ -477,30 +467,6 @@ final class PhrictionTransactionEditor foreach ($xactions as $xaction) { switch ($type) { - case PhrictionTransaction::TYPE_TITLE: - $title = $object->getContent()->getTitle(); - $missing = $this->validateIsEmptyTextField( - $title, - $xactions); - - if ($missing) { - $error = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Required'), - pht('Document title is required.'), - nonempty(last($xactions), null)); - - $error->setIsMissingFieldError(true); - $errors[] = $error; - } else if ($this->getProcessContentVersionError()) { - $error = $this->validateContentVersion($object, $type, $xaction); - if ($error) { - $this->setProcessContentVersionError(false); - $errors[] = $error; - } - } - break; - case PhrictionTransaction::TYPE_CONTENT: if ($xaction->getMetadataValue('stub:create:phid')) { continue; diff --git a/src/applications/phriction/storage/PhrictionTransaction.php b/src/applications/phriction/storage/PhrictionTransaction.php index a3ae6adb9d..40e7b4499e 100644 --- a/src/applications/phriction/storage/PhrictionTransaction.php +++ b/src/applications/phriction/storage/PhrictionTransaction.php @@ -1,9 +1,8 @@ getNewValue(); @@ -35,14 +38,13 @@ final class PhrictionTransaction case self::TYPE_MOVE_AWAY: $phids[] = $new['phid']; break; - case self::TYPE_TITLE: + case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: if ($this->getMetadataValue('stub:create:phid')) { $phids[] = $this->getMetadataValue('stub:create:phid'); } break; } - return $phids; } @@ -77,7 +79,7 @@ final class PhrictionTransaction case self::TYPE_MOVE_TO: case self::TYPE_MOVE_AWAY: return true; - case self::TYPE_TITLE: + case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: return $this->getMetadataValue('stub:create:phid', false); } return parent::shouldHideForMail($xactions); @@ -88,7 +90,7 @@ final class PhrictionTransaction case self::TYPE_MOVE_TO: case self::TYPE_MOVE_AWAY: return true; - case self::TYPE_TITLE: + case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: return $this->getMetadataValue('stub:create:phid', false); } return parent::shouldHideForFeed(); @@ -96,8 +98,6 @@ final class PhrictionTransaction public function getActionStrength() { switch ($this->getTransactionType()) { - case self::TYPE_TITLE: - return 1.4; case self::TYPE_CONTENT: return 1.3; case self::TYPE_DELETE: @@ -115,29 +115,14 @@ final class PhrictionTransaction $new = $this->getNewValue(); switch ($this->getTransactionType()) { - case self::TYPE_TITLE: - if ($old === null) { - if ($this->getMetadataValue('stub:create:phid')) { - return pht('Stubbed'); - } else { - return pht('Created'); - } - } - - return pht('Retitled'); - case self::TYPE_CONTENT: return pht('Edited'); - case self::TYPE_DELETE: return pht('Deleted'); - case self::TYPE_MOVE_TO: return pht('Moved'); - case self::TYPE_MOVE_AWAY: return pht('Moved Away'); - } return parent::getActionName(); @@ -148,7 +133,6 @@ final class PhrictionTransaction $new = $this->getNewValue(); switch ($this->getTransactionType()) { - case self::TYPE_TITLE: case self::TYPE_CONTENT: return 'fa-pencil'; case self::TYPE_DELETE: @@ -169,26 +153,6 @@ final class PhrictionTransaction $new = $this->getNewValue(); switch ($this->getTransactionType()) { - case self::TYPE_TITLE: - if ($old === null) { - if ($this->getMetadataValue('stub:create:phid')) { - return pht( - '%s stubbed out this document when creating %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink( - $this->getMetadataValue('stub:create:phid'))); - } else { - return pht( - '%s created this document.', - $this->renderHandleLink($author_phid)); - } - } - return pht( - '%s changed the title from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); - case self::TYPE_CONTENT: return pht( '%s edited the document content.', @@ -224,20 +188,6 @@ final class PhrictionTransaction $new = $this->getNewValue(); switch ($this->getTransactionType()) { - case self::TYPE_TITLE: - if ($old === null) { - return pht( - '%s created %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - } - - return pht( - '%s renamed %s from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - $old, - $new); case self::TYPE_CONTENT: return pht( @@ -273,7 +223,7 @@ final class PhrictionTransaction public function getMailTags() { $tags = array(); switch ($this->getTransactionType()) { - case self::TYPE_TITLE: + case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: $tags[] = self::MAILTAG_TITLE; break; case self::TYPE_CONTENT: diff --git a/src/applications/phriction/xaction/PhrictionDocumentTitleTransaction.php b/src/applications/phriction/xaction/PhrictionDocumentTitleTransaction.php new file mode 100644 index 0000000000..730b062f34 --- /dev/null +++ b/src/applications/phriction/xaction/PhrictionDocumentTitleTransaction.php @@ -0,0 +1,94 @@ +isNewObject()) { + return null; + } + return $this->getEditor()->getOldContent()->getTitle(); + } + + public function applyInternalEffects($object, $value) { + $object->setStatus(PhrictionDocumentStatus::STATUS_EXISTS); + $this->getEditor()->getNewContent()->setTitle($value); + } + + public function getActionStrength() { + return 1.4; + } + + public function getActionName() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($old === null) { + if ($this->getMetadataValue('stub:create:phid')) { + return pht('Stubbed'); + } else { + return pht('Created'); + } + } + return pht('Retitled'); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($old === null) { + if ($this->getMetadataValue('stub:create:phid')) { + return pht( + '%s stubbed out this document when creating %s.', + $this->renderAuthor(), + $this->renderHandleLink( + $this->getMetadataValue('stub:create:phid'))); + } else { + return pht( + '%s created this document.', + $this->renderAuthor()); + } + } + + return pht( + '%s changed the title from %s to %s.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } + + public function getTitleForFeed() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($old === null) { + return pht( + '%s created %s.', + $this->renderAuthor(), + $this->renderObject()); + } + + return pht( + '%s renamed %s from %s to %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderOldValue(), + $this->renderNewValue()); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + $title = $object->getContent()->getTitle(); + if ($this->isEmptyTextTransaction($title, $xactions)) { + $errors[] = $this->newRequiredError( + pht('Documents must have a title.')); + } + + return $errors; + } + +} diff --git a/src/applications/phriction/xaction/PhrictionDocumentTransactionType.php b/src/applications/phriction/xaction/PhrictionDocumentTransactionType.php new file mode 100644 index 0000000000..d99b53a303 --- /dev/null +++ b/src/applications/phriction/xaction/PhrictionDocumentTransactionType.php @@ -0,0 +1,4 @@ +getTransactionImplementation()->hasChangeDetailView()) { return true; } @@ -168,7 +168,7 @@ abstract class PhabricatorModularTransaction return parent::hasChangeDetails(); } - final public function renderChangeDetails(PhabricatorUser $viewer) { + /* final */ public function renderChangeDetails(PhabricatorUser $viewer) { $impl = $this->getTransactionImplementation(); $impl->setViewer($viewer); $view = $impl->newChangeDetailView(); From 6a9dd61c427b57b85a8f1fd4f7c0a8569a307563 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 May 2017 10:57:30 -0700 Subject: [PATCH 42/81] Make collapsed inlines more useful and anchor target highlights more accurate Summary: Ref T12616. Fixes T11648. Currently, we snug up replies with a negative margin (from T10563) but this throws off the anchor highlighting. Instead: - Remove padding from these dolumns. - Use margins on the stuff inside them instead. - Less margins for replies. - Less margins for collapsed comments. - Show some text for collapsed comments. Test Plan: {F4960890} {F4960891} Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616, T11648 Differential Revision: https://secure.phabricator.com/D17913 --- resources/celerity/map.php | 16 ++++++------ .../view/PHUIDiffInlineCommentDetailView.php | 13 +++++++--- .../differential/changeset-view.css | 6 +---- .../differential/phui-inline-comment.css | 26 ++++++++++++++----- 4 files changed, 38 insertions(+), 23 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index b389a84ba5..b9fb14d72c 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -12,7 +12,7 @@ return array( 'core.pkg.css' => 'ee5f28cd', 'core.pkg.js' => '8c5f913d', 'darkconsole.pkg.js' => '1f9a31bc', - 'differential.pkg.css' => '58712637', + 'differential.pkg.css' => '7b1c772c', 'differential.pkg.js' => 'f1b636fb', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', @@ -64,9 +64,9 @@ return array( 'rsrc/css/application/dashboard/dashboard.css' => 'fe5b1869', 'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a', 'rsrc/css/application/differential/add-comment.css' => 'c47f8c40', - 'rsrc/css/application/differential/changeset-view.css' => '41af6d25', + 'rsrc/css/application/differential/changeset-view.css' => '69a3c268', 'rsrc/css/application/differential/core.css' => '5b7b8ff4', - 'rsrc/css/application/differential/phui-inline-comment.css' => '3fd8ca64', + 'rsrc/css/application/differential/phui-inline-comment.css' => 'e0a2b52e', 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', 'rsrc/css/application/differential/revision-history.css' => '0e8eb855', 'rsrc/css/application/differential/revision-list.css' => 'f3c47d33', @@ -567,7 +567,7 @@ return array( 'conpherence-thread-manager' => '4d863052', 'conpherence-transaction-css' => '85129c68', 'd3' => 'a11a5ff2', - 'differential-changeset-view-css' => '41af6d25', + 'differential-changeset-view-css' => '69a3c268', 'differential-core-view-css' => '5b7b8ff4', 'differential-revision-add-comment-css' => 'c47f8c40', 'differential-revision-comment-css' => '14b8565a', @@ -864,7 +864,7 @@ return array( 'phui-image-mask-css' => 'a8498f9c', 'phui-info-panel-css' => '27ea50a1', 'phui-info-view-css' => '6e217679', - 'phui-inline-comment-view-css' => '3fd8ca64', + 'phui-inline-comment-view-css' => 'e0a2b52e', 'phui-invisible-character-view-css' => '6993d9f0', 'phui-lightbox-css' => '0a035e40', 'phui-list-view-css' => '12eb8ce6', @@ -1182,9 +1182,6 @@ return array( 'javelin-dom', 'javelin-reactor-dom', ), - '41af6d25' => array( - 'phui-inline-comment-view-css', - ), 42126667 => array( 'javelin-behavior', 'javelin-dom', @@ -1401,6 +1398,9 @@ return array( '6882e80a' => array( 'javelin-dom', ), + '69a3c268' => array( + 'phui-inline-comment-view-css', + ), '69adf288' => array( 'javelin-install', ), diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php index 9c3c55292e..91f27f0e2b 100644 --- a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php +++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php @@ -433,15 +433,20 @@ final class PHUIDiffInlineCommentDetailView phutil_tag_div('phabricator-remarkup', $content)), )); + $snippet = id(new PhutilUTF8StringTruncator()) + ->setMaximumGlyphs(96) + ->truncateString($inline->getContent()); + $summary = phutil_tag( 'div', array( 'class' => 'differential-inline-summary', ), - - // TODO: Render something a little more useful here as a hint about the - // inline content, like "alincoln: first line of text...". - pht('...')); + array( + phutil_tag('strong', array(), pht('%s:', $author)), + ' ', + $snippet, + )); return array( $anchor, diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index 1bd7d48635..1c14883a36 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -67,10 +67,6 @@ padding: 1px 4px; } -.device .differential-diff .inline > td { - padding: 4px; -} - .differential-diff td .zwsp { position: absolute; width: 0; @@ -315,7 +311,7 @@ td.cov-I { } .differential-diff .inline > td { - padding: 8px 12px; + padding: 0; } .differential-loading { diff --git a/webroot/rsrc/css/application/differential/phui-inline-comment.css b/webroot/rsrc/css/application/differential/phui-inline-comment.css index 3e28bcd0d4..11585d67ce 100644 --- a/webroot/rsrc/css/application/differential/phui-inline-comment.css +++ b/webroot/rsrc/css/application/differential/phui-inline-comment.css @@ -28,14 +28,17 @@ background: #fff; border: 1px solid {$sh-yellowborder}; font: {$basefont}; - margin: 0; - width: 100%; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; overflow: hidden; white-space: normal; border-radius: 3px; + margin: 8px 12px; +} + +.device .differential-inline-comment { + margin: 4px; } .inline-state-is-draft { @@ -61,7 +64,7 @@ /* Tighten up spacing on replies */ .differential-inline-comment.inline-comment-is-reply { - margin-top: -12px; + margin-top: 0; } .differential-inline-comment .inline-head-right { @@ -315,6 +318,7 @@ .differential-inline-undo { padding: 8px; + margin: 8px 12px; text-align: center; background: {$sh-yellowbackground}; border: 1px solid {$sh-yellowborder}; @@ -389,10 +393,20 @@ } .differential-inline-summary { - background: {$greybackground}; - padding: 0 4px; - color: {$greytext}; + background: {$lightgreybackground}; + padding: 2px 16px; + color: {$lightgreytext}; + font-size: {$smallerfontsize}; display: none; + font: {$basefont}; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.device .differential-inline-summary { + padding-left: 4px; + padding-right: 4px; } .inline-hidden .differential-inline-summary { From 6888472b567e175b9d9b3aaf1ee7df44e80d9064 Mon Sep 17 00:00:00 2001 From: Austin McKinley Date: Tue, 16 May 2017 12:10:00 -0700 Subject: [PATCH 43/81] Migrate Pholio inline comments to modular transactions Summary: Fixes T12626. Test Plan: Made lots of comments, confirmed no UI changes Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: epriestley Maniphest Tasks: T12626 Differential Revision: https://secure.phabricator.com/D17914 --- src/__phutil_library_map__.php | 2 + .../PholioMockCommentController.php | 2 +- .../pholio/editor/PholioMockEditor.php | 20 +------ .../pholio/storage/PholioTransaction.php | 60 +------------------ .../pholio/view/PholioTransactionView.php | 9 +-- .../xaction/PholioMockInlineTransaction.php | 33 ++++++++++ 6 files changed, 45 insertions(+), 81 deletions(-) create mode 100644 src/applications/pholio/xaction/PholioMockInlineTransaction.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index ba74adb658..0b4e330338 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -4401,6 +4401,7 @@ phutil_register_library_map(array( 'PholioMockHeraldField' => 'applications/pholio/herald/PholioMockHeraldField.php', 'PholioMockHeraldFieldGroup' => 'applications/pholio/herald/PholioMockHeraldFieldGroup.php', 'PholioMockImagesView' => 'applications/pholio/view/PholioMockImagesView.php', + 'PholioMockInlineTransaction' => 'applications/pholio/xaction/PholioMockInlineTransaction.php', 'PholioMockListController' => 'applications/pholio/controller/PholioMockListController.php', 'PholioMockMailReceiver' => 'applications/pholio/mail/PholioMockMailReceiver.php', 'PholioMockNameHeraldField' => 'applications/pholio/herald/PholioMockNameHeraldField.php', @@ -9981,6 +9982,7 @@ phutil_register_library_map(array( 'PholioMockHeraldField' => 'HeraldField', 'PholioMockHeraldFieldGroup' => 'HeraldFieldGroup', 'PholioMockImagesView' => 'AphrontView', + 'PholioMockInlineTransaction' => 'PholioMockTransactionType', 'PholioMockListController' => 'PholioController', 'PholioMockMailReceiver' => 'PhabricatorObjectMailReceiver', 'PholioMockNameHeraldField' => 'PholioMockHeraldField', diff --git a/src/applications/pholio/controller/PholioMockCommentController.php b/src/applications/pholio/controller/PholioMockCommentController.php index b127d0b1da..1888c7369e 100644 --- a/src/applications/pholio/controller/PholioMockCommentController.php +++ b/src/applications/pholio/controller/PholioMockCommentController.php @@ -45,7 +45,7 @@ final class PholioMockCommentController extends PholioController { foreach ($inline_comments as $inline_comment) { $xactions[] = id(new PholioTransaction()) - ->setTransactionType(PholioTransaction::TYPE_INLINE) + ->setTransactionType(PholioMockInlineTransaction::TRANSACTIONTYPE) ->attachComment($inline_comment); } diff --git a/src/applications/pholio/editor/PholioMockEditor.php b/src/applications/pholio/editor/PholioMockEditor.php index ef315512ec..ebd5703170 100644 --- a/src/applications/pholio/editor/PholioMockEditor.php +++ b/src/applications/pholio/editor/PholioMockEditor.php @@ -30,23 +30,9 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor { $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; - $types[] = PholioTransaction::TYPE_INLINE; - return $types; } - protected function transactionHasEffect( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PholioTransaction::TYPE_INLINE: - return true; - } - - return parent::transactionHasEffect($object, $xaction); - } - protected function shouldApplyInitialEffects( PhabricatorLiskDAO $object, array $xactions) { @@ -147,7 +133,7 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor { } $comment = $xaction->getComment(); switch ($xaction->getTransactionType()) { - case PholioTransaction::TYPE_INLINE: + case PholioMockInlineTransaction::TRANSACTIONTYPE: if ($comment && strlen($comment->getContent())) { $inline_comments[] = $comment; } @@ -237,7 +223,7 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor { // Move inline comments to the end, so the comments precede them. foreach ($xactions as $xaction) { $type = $xaction->getTransactionType(); - if ($type == PholioTransaction::TYPE_INLINE) { + if ($type == PholioMockInlineTransaction::TRANSACTIONTYPE) { $tail[] = $xaction; } else { $head[] = $xaction; @@ -252,7 +238,7 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor { PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PholioTransaction::TYPE_INLINE: + case PholioMockInlineTransaction::TRANSACTIONTYPE: return true; } diff --git a/src/applications/pholio/storage/PholioTransaction.php b/src/applications/pholio/storage/PholioTransaction.php index 346f0e4561..a932cf2a60 100644 --- a/src/applications/pholio/storage/PholioTransaction.php +++ b/src/applications/pholio/storage/PholioTransaction.php @@ -2,9 +2,6 @@ final class PholioTransaction extends PhabricatorModularTransaction { - // Your witty commentary at the mock : image : x,y level - const TYPE_INLINE = 'inline'; - const MAILTAG_STATUS = 'pholio-status'; const MAILTAG_COMMENT = 'pholio-comment'; const MAILTAG_UPDATED = 'pholio-updated'; @@ -30,19 +27,10 @@ final class PholioTransaction extends PhabricatorModularTransaction { return new PholioTransactionView(); } - public function getIcon() { - switch ($this->getTransactionType()) { - case self::TYPE_INLINE: - return 'fa-comment'; - } - - return parent::getIcon(); - } - public function getMailTags() { $tags = array(); switch ($this->getTransactionType()) { - case self::TYPE_INLINE: + case PholioMockInlineTransaction::TRANSACTIONTYPE: case PhabricatorTransactions::TYPE_COMMENT: $tags[] = self::MAILTAG_COMMENT; break; @@ -65,50 +53,4 @@ final class PholioTransaction extends PhabricatorModularTransaction { return $tags; } - public function getTitle() { - $author_phid = $this->getAuthorPHID(); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - $type = $this->getTransactionType(); - switch ($type) { - case self::TYPE_INLINE: - $count = 1; - foreach ($this->getTransactionGroup() as $xaction) { - if ($xaction->getTransactionType() == $type) { - $count++; - } - } - - return pht( - '%s added %d inline comment(s).', - $this->renderHandleLink($author_phid), - $count); - break; - } - - return parent::getTitle(); - } - - public function getTitleForFeed() { - $author_phid = $this->getAuthorPHID(); - $object_phid = $this->getObjectPHID(); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - $type = $this->getTransactionType(); - switch ($type) { - case self::TYPE_INLINE: - return pht( - '%s added an inline comment to %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - break; - } - - return parent::getTitleForFeed(); - } - } diff --git a/src/applications/pholio/view/PholioTransactionView.php b/src/applications/pholio/view/PholioTransactionView.php index 92624feec6..69613c5428 100644 --- a/src/applications/pholio/view/PholioTransactionView.php +++ b/src/applications/pholio/view/PholioTransactionView.php @@ -30,14 +30,14 @@ final class PholioTransactionView switch ($u->getTransactionType()) { case PhabricatorTransactions::TYPE_COMMENT: - case PholioTransaction::TYPE_INLINE: + case PholioMockInlineTransaction::TRANSACTIONTYPE: break; default: return false; } switch ($v->getTransactionType()) { - case PholioTransaction::TYPE_INLINE: + case PholioMockInlineTransaction::TRANSACTIONTYPE: return true; } @@ -50,7 +50,8 @@ final class PholioTransactionView $out = array(); $group = $xaction->getTransactionGroup(); - if ($xaction->getTransactionType() == PholioTransaction::TYPE_INLINE) { + $type = $xaction->getTransactionType(); + if ($type == PholioMockInlineTransaction::TRANSACTIONTYPE) { array_unshift($group, $xaction); } else { $out[] = parent::renderTransactionContent($xaction); @@ -63,7 +64,7 @@ final class PholioTransactionView $inlines = array(); foreach ($group as $xaction) { switch ($xaction->getTransactionType()) { - case PholioTransaction::TYPE_INLINE: + case PholioMockInlineTransaction::TRANSACTIONTYPE: $inlines[] = $xaction; break; default: diff --git a/src/applications/pholio/xaction/PholioMockInlineTransaction.php b/src/applications/pholio/xaction/PholioMockInlineTransaction.php new file mode 100644 index 0000000000..f89fed0c3a --- /dev/null +++ b/src/applications/pholio/xaction/PholioMockInlineTransaction.php @@ -0,0 +1,33 @@ +renderAuthor()); + } + + public function getTitleForFeed() { + return pht( + '%s added an inline comment to %s.', + $this->renderAuthor(), + $this->renderObject()); + } + + public function getIcon() { + return 'fa-comment'; + } + + public function getTransactionHasEffect($object, $old, $new) { + return true; + } + +} From 2aa146ffaca4eb6e2e8adb977dc8b1b907910a76 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 16 May 2017 12:38:11 -0700 Subject: [PATCH 44/81] Set an icon for Maniphest story points Summary: It's an icon. For story points. Test Plan: Set some points, see icon. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17915 --- .../maniphest/xaction/ManiphestTaskPointsTransaction.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php index 7f324d1bca..6d8548fdb4 100644 --- a/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php +++ b/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php @@ -71,6 +71,10 @@ final class ManiphestTaskPointsTransaction return $errors; } + public function getIcon() { + return 'fa-calculator'; + } + private function getValueForPoints($value) { if (!strlen($value)) { $value = null; From 772afc5ed82c6b869dbfd911ec172da42f8a3b3d Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 May 2017 13:03:45 -0700 Subject: [PATCH 45/81] Allow cancelled inlines, edits, and replies to be undone to get the text back again Summary: Ref T12616. The ability to do {nav Edit > Cancel > Undo} to get your text back on inlines got dropped during the conversion. Restore it. Test Plan: Created, replied, and edited inlines, typed text, then cancelled. Was able to undo. Also undid normal deletion. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616 Differential Revision: https://secure.phabricator.com/D17916 --- resources/celerity/map.php | 18 ++--- .../differential/phui-inline-comment.css | 4 +- .../rsrc/js/application/diff/DiffInline.js | 77 ++++++++++++++----- 3 files changed, 69 insertions(+), 30 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index b9fb14d72c..b1d28cf453 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -12,8 +12,8 @@ return array( 'core.pkg.css' => 'ee5f28cd', 'core.pkg.js' => '8c5f913d', 'darkconsole.pkg.js' => '1f9a31bc', - 'differential.pkg.css' => '7b1c772c', - 'differential.pkg.js' => 'f1b636fb', + 'differential.pkg.css' => '4ff77743', + 'differential.pkg.js' => '85543704', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -66,7 +66,7 @@ return array( 'rsrc/css/application/differential/add-comment.css' => 'c47f8c40', 'rsrc/css/application/differential/changeset-view.css' => '69a3c268', 'rsrc/css/application/differential/core.css' => '5b7b8ff4', - 'rsrc/css/application/differential/phui-inline-comment.css' => 'e0a2b52e', + 'rsrc/css/application/differential/phui-inline-comment.css' => 'ffd1a542', 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', 'rsrc/css/application/differential/revision-history.css' => '0e8eb855', 'rsrc/css/application/differential/revision-list.css' => 'f3c47d33', @@ -392,7 +392,7 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', 'rsrc/js/application/diff/DiffChangeset.js' => 'c5742feb', 'rsrc/js/application/diff/DiffChangesetList.js' => 'f1101e6e', - 'rsrc/js/application/diff/DiffInline.js' => 'bdf6b568', + 'rsrc/js/application/diff/DiffInline.js' => 'a81c29d4', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', @@ -781,7 +781,7 @@ return array( 'phabricator-dashboard-css' => 'fe5b1869', 'phabricator-diff-changeset' => 'c5742feb', 'phabricator-diff-changeset-list' => 'f1101e6e', - 'phabricator-diff-inline' => 'bdf6b568', + 'phabricator-diff-inline' => 'a81c29d4', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -864,7 +864,7 @@ return array( 'phui-image-mask-css' => 'a8498f9c', 'phui-info-panel-css' => '27ea50a1', 'phui-info-view-css' => '6e217679', - 'phui-inline-comment-view-css' => 'e0a2b52e', + 'phui-inline-comment-view-css' => 'ffd1a542', 'phui-invisible-character-view-css' => '6993d9f0', 'phui-lightbox-css' => '0a035e40', 'phui-list-view-css' => '12eb8ce6', @@ -1718,6 +1718,9 @@ return array( 'javelin-stratcom', 'javelin-dom', ), + 'a81c29d4' => array( + 'javelin-dom', + ), 'a8beebea' => array( 'phui-oi-list-view-css', ), @@ -1872,9 +1875,6 @@ return array( 'javelin-util', 'javelin-request', ), - 'bdf6b568' => array( - 'javelin-dom', - ), 'bea6e7f4' => array( 'javelin-install', 'javelin-dom', diff --git a/webroot/rsrc/css/application/differential/phui-inline-comment.css b/webroot/rsrc/css/application/differential/phui-inline-comment.css index 11585d67ce..146117b1cb 100644 --- a/webroot/rsrc/css/application/differential/phui-inline-comment.css +++ b/webroot/rsrc/css/application/differential/phui-inline-comment.css @@ -318,11 +318,10 @@ .differential-inline-undo { padding: 8px; - margin: 8px 12px; + margin: 4px 12px; text-align: center; background: {$sh-yellowbackground}; border: 1px solid {$sh-yellowborder}; - margin: 4px 0; color: {$darkgreytext}; font: {$basefont}; font-size: {$normalfontsize}; @@ -396,7 +395,6 @@ background: {$lightgreybackground}; padding: 2px 16px; color: {$lightgreytext}; - font-size: {$smallerfontsize}; display: none; font: {$basefont}; white-space: nowrap; diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js index 04f9a03db1..b7034fa653 100644 --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -25,6 +25,7 @@ JX.install('DiffInline', { _isNewFile: null, _undoRow: null, _replyToCommentPHID: null, + _originalText: null, _isDeleted: false, _isInvisible: false, @@ -57,6 +58,7 @@ JX.install('DiffInline', { this._number = data.number; this._length = data.length; + this._originalText = data.original; this._isNewFile = (this.getDisplaySide() == 'right') || (data.left != data.right); @@ -165,6 +167,8 @@ JX.install('DiffInline', { this._phid = null; this._hidden = false; + this._originalText = null; + return row; }, @@ -231,10 +235,10 @@ JX.install('DiffInline', { this._didUpdate(); }, - create: function() { + create: function(text) { var uri = this._getInlineURI(); var handler = JX.bind(this, this._oncreateresponse); - var data = this._newRequestData('new'); + var data = this._newRequestData('new', text); this.setLoading(true); @@ -248,10 +252,10 @@ JX.install('DiffInline', { return changeset.newInlineReply(this); }, - edit: function() { + edit: function(text) { var uri = this._getInlineURI(); var handler = JX.bind(this, this._oneditresponse); - var data = this._newRequestData('edit'); + var data = this._newRequestData('edit', text || null); this.setLoading(true); @@ -336,7 +340,7 @@ JX.install('DiffInline', { return this; }, - _newRequestData: function(operation) { + _newRequestData: function(operation, text) { return { op: operation, id: this._id, @@ -347,6 +351,7 @@ JX.install('DiffInline', { is_new: this.isNewFile(), changesetID: this.getChangesetID(), replyToCommentPHID: this.getReplyToCommentPHID() || '', + text: text || '' }; }, @@ -366,7 +371,7 @@ JX.install('DiffInline', { }, _ondeleteresponse: function() { - this._drawUndoRows(); + this._drawUndeleteRows(); this.setLoading(false); this.setDeleted(true); @@ -374,7 +379,15 @@ JX.install('DiffInline', { this._didUpdate(); }, - _drawUndoRows: function() { + _drawUndeleteRows: function() { + return this._drawUndoRows('undelete', this._row); + }, + + _drawUneditRows: function(text) { + return this._drawUndoRows('unedit', null, text); + }, + + _drawUndoRows: function(mode, cursor, text) { var templates = this.getChangeset().getUndoTemplates(); var template; @@ -385,14 +398,14 @@ JX.install('DiffInline', { } template = JX.$H(template).getNode(); - this._undoRow = this._drawRows(template, this._row, 'undo'); + this._undoRow = this._drawRows(template, cursor, mode, text); }, _drawEditRows: function(rows) { return this._drawRows(rows, null, 'edit'); }, - _drawRows: function(rows, cursor, type) { + _drawRows: function(rows, cursor, type, text) { var first_row = JX.DOM.scry(rows, 'tr')[0]; var first_meta; var row = first_row; @@ -410,6 +423,7 @@ JX.install('DiffInline', { var row_meta = { node: row, type: type, + text: text || null, listeners: [] }; @@ -476,16 +490,26 @@ JX.install('DiffInline', { this._removeRow(row); - var uri = this._getInlineURI(); - var data = this._newRequestData('undelete'); - var handler = JX.bind(this, this._onundelete); + if (row.type == 'undelete') { + var uri = this._getInlineURI(); + var data = this._newRequestData('undelete'); + var handler = JX.bind(this, this._onundelete); - this.setDeleted(false); - this.setLoading(true); + this.setDeleted(false); + this.setLoading(true); - new JX.Request(uri, handler) - .setData(data) - .send(); + new JX.Request(uri, handler) + .setData(data) + .send(); + } + + if (row.type == 'unedit') { + if (this.getID()) { + this.edit(row.text); + } else { + this.create(row.text); + } + } }, _onundelete: function() { @@ -496,13 +520,30 @@ JX.install('DiffInline', { _oncancel: function(row, e) { e.kill(); - // TODO: Capture edited text and offer "undo". + var text = this._readText(row.node); + if (text && text.length && (text != this._originalText)) { + this._drawUneditRows(text); + } this._removeRow(row); this.setInvisible(false); }, + _readText: function(row) { + var textarea; + try { + textarea = JX.DOM.find( + row, + 'textarea', + 'differential-inline-comment-edit-textarea'); + } catch (ex) { + return null; + } + + return textarea.value; + }, + _onsubmitresponse: function(row, response) { this._removeRow(row); From 0ed496de228271add3a63a7f08f3228d21427ec8 Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Tue, 16 May 2017 14:53:26 -0700 Subject: [PATCH 46/81] Throw an exception if `local.json` can't be read Summary: Our `local.json` configuration file contains various secrets, including database usernames and passwords. As such, we recently changed the permissions on this file from `0644` to `0640`. After doing so, however, I constantly forget to run commands with `sudo`. This is made worse by the fact that `PhabricatorConfigLocalSource` seems to simply ignore `local.json` is it isn't readable, whereas throwing an `Exception` would have saved me a lot of debugging. Test Plan: ```name=Before > /usr/local/src/phabricator/bin/config get mysql.pass { "config": [ { "key": "mysql.pass", "source": "local", "value": null, "status": "unset", "errorInfo": null }, { "key": "mysql.pass", "source": "database", "value": null, "status": "error", "errorInfo": "Database source is not configured properly" } ] } ``` ```name=After > /usr/local/src/phabricator/bin/config get mysql.pass [2017-05-16 21:49:26] EXCEPTION: (FilesystemException) Path '/usr/local/src/phabricator/conf/local/local.json' is not readable. at [/src/filesystem/Filesystem.php:1124] arcanist(head=stable, ref.master=3c4735795a29, ref.stable=20ad47f27331), phabricator(head=stable, ref.master=3dae9701298f, ref.stable=fcebaa5097f3), phutil(head=stable, ref.master=a900d7b63e95, ref.stable=d02cc05931b0) #0 Filesystem::assertReadable(string) called at [/src/filesystem/Filesystem.php:39] #1 Filesystem::readFile(string) called at [/src/infrastructure/env/PhabricatorConfigLocalSource.php:25] #2 PhabricatorConfigLocalSource::loadConfig() called at [/src/infrastructure/env/PhabricatorConfigLocalSource.php:6] #3 PhabricatorConfigLocalSource::__construct() called at [/src/infrastructure/env/PhabricatorEnv.php:195] #4 PhabricatorEnv::buildConfigurationSourceStack(boolean) called at [/src/infrastructure/env/PhabricatorEnv.php:95] #5 PhabricatorEnv::initializeCommonEnvironment(boolean) called at [/src/infrastructure/env/PhabricatorEnv.php:75] #6 PhabricatorEnv::initializeScriptEnvironment(boolean) called at [/scripts/init/lib.php:22] #7 init_phabricator_script(array) called at [/scripts/init/init-setup.php:11] #8 require_once(string) called at [/scripts/setup/manage_config.php:5] ``` Reviewers: #blessed_reviewers, joshuaspence Reviewed By: joshuaspence Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17917 --- .../env/PhabricatorConfigLocalSource.php | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/infrastructure/env/PhabricatorConfigLocalSource.php b/src/infrastructure/env/PhabricatorConfigLocalSource.php index 3a2295eb5e..16dc43a9bc 100644 --- a/src/infrastructure/env/PhabricatorConfigLocalSource.php +++ b/src/infrastructure/env/PhabricatorConfigLocalSource.php @@ -21,17 +21,35 @@ final class PhabricatorConfigLocalSource extends PhabricatorConfigProxySource { private function loadConfig() { $path = $this->getConfigPath(); - if (@file_exists($path)) { - $data = @file_get_contents($path); - if ($data) { - $data = json_decode($data, true); - if (is_array($data)) { - return $data; - } - } + + if (!Filesystem::pathExists($path)) { + return array(); } - return array(); + try { + $data = Filesystem::readFile($path); + } catch (FilesystemException $ex) { + throw new PhutilProxyException( + pht( + 'Configuration file "%s" exists, but could not be read.', + $path), + $ex); + } + + try { + $result = phutil_json_decode($data); + } catch (PhutilJSONParserException $ex) { + throw new PhutilProxyException( + pht( + 'Configuration file "%s" exists and is readable, but the content '. + 'is not valid JSON. You may have edited this file manually and '. + 'introduced a syntax error by mistake. Correct the file syntax '. + 'to continue.', + $path), + $ex); + } + + return $result; } private function saveConfig() { From abc9bc77b27bfccf2800fc2b11fce5eeb6f5ece3 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 16 May 2017 15:52:21 -0700 Subject: [PATCH 47/81] Move Phriction MOVE_TO transaction to Modular Transactions Summary: Moves this transaction over to modular transactions. Test Plan: Move a document, re-title a document, try to move over an existing document. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17918 --- src/__phutil_library_map__.php | 2 + .../controller/PhrictionMoveController.php | 6 +- .../editor/PhrictionTransactionEditor.php | 58 ++-------- .../storage/PhrictionTransaction.php | 17 +-- .../PhrictionDocumentMoveToTransaction.php | 102 ++++++++++++++++++ 5 files changed, 119 insertions(+), 66 deletions(-) create mode 100644 src/applications/phriction/xaction/PhrictionDocumentMoveToTransaction.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 0b4e330338..fbc972328c 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -4617,6 +4617,7 @@ phutil_register_library_map(array( 'PhrictionDocumentHeraldAdapter' => 'applications/phriction/herald/PhrictionDocumentHeraldAdapter.php', 'PhrictionDocumentHeraldField' => 'applications/phriction/herald/PhrictionDocumentHeraldField.php', 'PhrictionDocumentHeraldFieldGroup' => 'applications/phriction/herald/PhrictionDocumentHeraldFieldGroup.php', + 'PhrictionDocumentMoveToTransaction' => 'applications/phriction/xaction/PhrictionDocumentMoveToTransaction.php', 'PhrictionDocumentPHIDType' => 'applications/phriction/phid/PhrictionDocumentPHIDType.php', 'PhrictionDocumentPathHeraldField' => 'applications/phriction/herald/PhrictionDocumentPathHeraldField.php', 'PhrictionDocumentQuery' => 'applications/phriction/query/PhrictionDocumentQuery.php', @@ -10256,6 +10257,7 @@ phutil_register_library_map(array( 'PhrictionDocumentHeraldAdapter' => 'HeraldAdapter', 'PhrictionDocumentHeraldField' => 'HeraldField', 'PhrictionDocumentHeraldFieldGroup' => 'HeraldFieldGroup', + 'PhrictionDocumentMoveToTransaction' => 'PhrictionDocumentTransactionType', 'PhrictionDocumentPHIDType' => 'PhabricatorPHIDType', 'PhrictionDocumentPathHeraldField' => 'PhrictionDocumentHeraldField', 'PhrictionDocumentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', diff --git a/src/applications/phriction/controller/PhrictionMoveController.php b/src/applications/phriction/controller/PhrictionMoveController.php index 53e5ae16ec..f25465c3c0 100644 --- a/src/applications/phriction/controller/PhrictionMoveController.php +++ b/src/applications/phriction/controller/PhrictionMoveController.php @@ -64,7 +64,8 @@ final class PhrictionMoveController extends PhrictionController { $xactions = array(); $xactions[] = id(new PhrictionTransaction()) - ->setTransactionType(PhrictionTransaction::TYPE_MOVE_TO) + ->setTransactionType( + PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE) ->setNewValue($document); $target_document = id(new PhrictionDocumentQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) @@ -88,7 +89,8 @@ final class PhrictionMoveController extends PhrictionController { return id(new AphrontRedirectResponse())->setURI($redir_uri); } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; - $e_slug = $ex->getShortMessage(PhrictionTransaction::TYPE_MOVE_TO); + $e_slug = $ex->getShortMessage( + PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE); } } diff --git a/src/applications/phriction/editor/PhrictionTransactionEditor.php b/src/applications/phriction/editor/PhrictionTransactionEditor.php index 95575a7619..cbe6e426d0 100644 --- a/src/applications/phriction/editor/PhrictionTransactionEditor.php +++ b/src/applications/phriction/editor/PhrictionTransactionEditor.php @@ -82,7 +82,6 @@ final class PhrictionTransactionEditor $types[] = PhrictionTransaction::TYPE_CONTENT; $types[] = PhrictionTransaction::TYPE_DELETE; - $types[] = PhrictionTransaction::TYPE_MOVE_TO; $types[] = PhrictionTransaction::TYPE_MOVE_AWAY; $types[] = PhabricatorTransactions::TYPE_EDGE; @@ -104,7 +103,6 @@ final class PhrictionTransactionEditor } return $this->getOldContent()->getContent(); case PhrictionTransaction::TYPE_DELETE: - case PhrictionTransaction::TYPE_MOVE_TO: case PhrictionTransaction::TYPE_MOVE_AWAY: return null; } @@ -118,17 +116,11 @@ final class PhrictionTransactionEditor case PhrictionTransaction::TYPE_CONTENT: case PhrictionTransaction::TYPE_DELETE: return $xaction->getNewValue(); - case PhrictionTransaction::TYPE_MOVE_TO: + case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: $document = $xaction->getNewValue(); // grab the real object now for the sub-editor to come $this->moveAwayDocument = $document; - $dict = array( - 'id' => $document->getID(), - 'phid' => $document->getPHID(), - 'content' => $document->getContent()->getContent(), - 'title' => $document->getContent()->getTitle(), - ); - return $dict; + return; case PhrictionTransaction::TYPE_MOVE_AWAY: $document = $xaction->getNewValue(); $dict = array( @@ -150,7 +142,7 @@ final class PhrictionTransactionEditor case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: case PhrictionTransaction::TYPE_CONTENT: case PhrictionTransaction::TYPE_DELETE: - case PhrictionTransaction::TYPE_MOVE_TO: + case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: case PhrictionTransaction::TYPE_MOVE_AWAY: return true; } @@ -172,9 +164,6 @@ final class PhrictionTransactionEditor switch ($xaction->getTransactionType()) { case PhrictionTransaction::TYPE_CONTENT: - case PhrictionTransaction::TYPE_MOVE_TO: - $object->setStatus(PhrictionDocumentStatus::STATUS_EXISTS); - return; case PhrictionTransaction::TYPE_MOVE_AWAY: $object->setStatus(PhrictionDocumentStatus::STATUS_MOVED); return; @@ -202,7 +191,7 @@ final class PhrictionTransactionEditor ->setMetadataValue('contentDelete', true); } break; - case PhrictionTransaction::TYPE_MOVE_TO: + case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: $document = $xaction->getNewValue(); $xactions[] = id(new PhrictionTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) @@ -232,14 +221,6 @@ final class PhrictionTransactionEditor $this->getNewContent()->setChangeType( PhrictionChangeType::CHANGE_DELETE); break; - case PhrictionTransaction::TYPE_MOVE_TO: - $dict = $xaction->getNewValue(); - $this->getNewContent()->setContent($dict['content']); - $this->getNewContent()->setTitle($dict['title']); - $this->getNewContent()->setChangeType( - PhrictionChangeType::CHANGE_MOVE_HERE); - $this->getNewContent()->setChangeRef($dict['id']); - break; case PhrictionTransaction::TYPE_MOVE_AWAY: $dict = $xaction->getNewValue(); $this->getNewContent()->setContent(''); @@ -263,7 +244,7 @@ final class PhrictionTransactionEditor case PhrictionTransaction::TYPE_CONTENT: case PhrictionTransaction::TYPE_DELETE: case PhrictionTransaction::TYPE_MOVE_AWAY: - case PhrictionTransaction::TYPE_MOVE_TO: + case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: $save_content = true; break; default: @@ -448,7 +429,7 @@ final class PhrictionTransactionEditor foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { - case PhrictionTransaction::TYPE_MOVE_TO: + case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: $dict = $xaction->getNewValue(); $phids[] = $dict['phid']; break; @@ -510,31 +491,8 @@ final class PhrictionTransactionEditor break; - case PhrictionTransaction::TYPE_MOVE_TO: + case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: $source_document = $xaction->getNewValue(); - switch ($source_document->getStatus()) { - case PhrictionDocumentStatus::STATUS_DELETED: - $e_text = pht('A deleted document can not be moved.'); - break; - case PhrictionDocumentStatus::STATUS_MOVED: - $e_text = pht('A moved document can not be moved again.'); - break; - case PhrictionDocumentStatus::STATUS_STUB: - $e_text = pht('A stub document can not be moved.'); - break; - default: - $e_text = null; - break; - } - - if ($e_text) { - $error = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Can not move document.'), - $e_text, - $xaction); - $errors[] = $error; - } $ancestry_errors = $this->validateAncestry( $object, @@ -611,7 +569,7 @@ final class PhrictionTransactionEditor return $errors; } - private function validateAncestry( + public function validateAncestry( PhabricatorLiskDAO $object, $type, PhabricatorApplicationTransaction $xaction, diff --git a/src/applications/phriction/storage/PhrictionTransaction.php b/src/applications/phriction/storage/PhrictionTransaction.php index 40e7b4499e..d3d78ff665 100644 --- a/src/applications/phriction/storage/PhrictionTransaction.php +++ b/src/applications/phriction/storage/PhrictionTransaction.php @@ -5,7 +5,6 @@ final class PhrictionTransaction const TYPE_CONTENT = 'content'; const TYPE_DELETE = 'delete'; - const TYPE_MOVE_TO = 'move-to'; const TYPE_MOVE_AWAY = 'move-away'; const MAILTAG_TITLE = 'phriction-title'; @@ -34,7 +33,7 @@ final class PhrictionTransaction $phids = parent::getRequiredHandlePHIDs(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { - case self::TYPE_MOVE_TO: + case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: case self::TYPE_MOVE_AWAY: $phids[] = $new['phid']; break; @@ -76,7 +75,7 @@ final class PhrictionTransaction public function shouldHideForMail(array $xactions) { switch ($this->getTransactionType()) { - case self::TYPE_MOVE_TO: + case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: case self::TYPE_MOVE_AWAY: return true; case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: @@ -87,7 +86,7 @@ final class PhrictionTransaction public function shouldHideForFeed() { switch ($this->getTransactionType()) { - case self::TYPE_MOVE_TO: + case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: case self::TYPE_MOVE_AWAY: return true; case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: @@ -102,7 +101,6 @@ final class PhrictionTransaction return 1.3; case self::TYPE_DELETE: return 1.5; - case self::TYPE_MOVE_TO: case self::TYPE_MOVE_AWAY: return 1.0; } @@ -119,8 +117,6 @@ final class PhrictionTransaction return pht('Edited'); case self::TYPE_DELETE: return pht('Deleted'); - case self::TYPE_MOVE_TO: - return pht('Moved'); case self::TYPE_MOVE_AWAY: return pht('Moved Away'); } @@ -137,7 +133,6 @@ final class PhrictionTransaction return 'fa-pencil'; case self::TYPE_DELETE: return 'fa-times'; - case self::TYPE_MOVE_TO: case self::TYPE_MOVE_AWAY: return 'fa-arrows'; } @@ -163,12 +158,6 @@ final class PhrictionTransaction '%s deleted this document.', $this->renderHandleLink($author_phid)); - case self::TYPE_MOVE_TO: - return pht( - '%s moved this document from %s', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($new['phid'])); - case self::TYPE_MOVE_AWAY: return pht( '%s moved this document to %s', diff --git a/src/applications/phriction/xaction/PhrictionDocumentMoveToTransaction.php b/src/applications/phriction/xaction/PhrictionDocumentMoveToTransaction.php new file mode 100644 index 0000000000..a513b8fe36 --- /dev/null +++ b/src/applications/phriction/xaction/PhrictionDocumentMoveToTransaction.php @@ -0,0 +1,102 @@ + $document->getID(), + 'phid' => $document->getPHID(), + 'content' => $document->getContent()->getContent(), + 'title' => $document->getContent()->getTitle(), + ); + return $dict; + } + + public function applyInternalEffects($object, $value) { + $object->setStatus(PhrictionDocumentStatus::STATUS_EXISTS); + $this->getEditor()->getNewContent()->setTitle($value); + } + + public function applyExternalEffects($object, $value) { + $dict = $value; + $this->getEditor()->getNewContent()->setContent($dict['content']); + $this->getEditor()->getNewContent()->setTitle($dict['title']); + $this->getEditor()->getNewContent()->setChangeType( + PhrictionChangeType::CHANGE_MOVE_HERE); + $this->getEditor()->getNewContent()->setChangeRef($dict['id']); + } + + public function getActionStrength() { + return 1.0; + } + + public function getActionName() { + return pht('Moved'); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + return pht( + '%s moved this document from %s', + $this->renderAuthor(), + $this->renderHandle($new['phid'])); + } + + public function getTitleForFeed() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + return pht( + '%s moved %s from %s', + $this->renderAuthor(), + $this->renderObject(), + $this->renderHandle($new['phid'])); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + $e_text = null; + foreach ($xactions as $xaction) { + $source_document = $xaction->getNewValue(); + switch ($source_document->getStatus()) { + case PhrictionDocumentStatus::STATUS_DELETED: + $e_text = pht('A deleted document can not be moved.'); + break; + case PhrictionDocumentStatus::STATUS_MOVED: + $e_text = pht('A moved document can not be moved again.'); + break; + case PhrictionDocumentStatus::STATUS_STUB: + $e_text = pht('A stub document can not be moved.'); + break; + default: + $e_text = null; + break; + } + + if ($e_text !== null) { + $errors[] = $this->newInvalidError($e_text); + } + + } + + // TODO: Move Ancestry validation here once all types are converted. + + return $errors; + } + + public function getIcon() { + return 'fa-arrows'; + } + +} From 0ca49fbeb95103295db921a9a54e682f962b2522 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 May 2017 14:53:21 -0700 Subject: [PATCH 48/81] Move "hover over an inline to see the affected lines" code to the new class tree Summary: Fixes T8047. Ref T12616. Fixes T9270. This moves the "hover" part of the hover/drag behavior to the new code, leaving the "drag" part for a followup change. The new hover UI behaves properly with Quicksand (T8047) and the filetree (T9270). Test Plan: Hovered over inlines, saw lines select properly. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616, T9270, T8047 Differential Revision: https://secure.phabricator.com/D17919 --- resources/celerity/map.php | 78 +++++----- .../differential/changeset-view.css | 5 +- .../rsrc/js/application/diff/DiffChangeset.js | 14 ++ .../js/application/diff/DiffChangesetList.js | 142 ++++++++++++++++++ .../rsrc/js/application/diff/DiffInline.js | 24 ++- .../behavior-edit-inline-comments.js | 73 +-------- 6 files changed, 216 insertions(+), 120 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index b1d28cf453..575ce9a2b1 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -12,8 +12,8 @@ return array( 'core.pkg.css' => 'ee5f28cd', 'core.pkg.js' => '8c5f913d', 'darkconsole.pkg.js' => '1f9a31bc', - 'differential.pkg.css' => '4ff77743', - 'differential.pkg.js' => '85543704', + 'differential.pkg.css' => 'ea471cb0', + 'differential.pkg.js' => '7f24021f', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -64,7 +64,7 @@ return array( 'rsrc/css/application/dashboard/dashboard.css' => 'fe5b1869', 'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a', 'rsrc/css/application/differential/add-comment.css' => 'c47f8c40', - 'rsrc/css/application/differential/changeset-view.css' => '69a3c268', + 'rsrc/css/application/differential/changeset-view.css' => '6a77323e', 'rsrc/css/application/differential/core.css' => '5b7b8ff4', 'rsrc/css/application/differential/phui-inline-comment.css' => 'ffd1a542', 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', @@ -390,13 +390,13 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => 'c5742feb', - 'rsrc/js/application/diff/DiffChangesetList.js' => 'f1101e6e', - 'rsrc/js/application/diff/DiffInline.js' => 'a81c29d4', + 'rsrc/js/application/diff/DiffChangeset.js' => '34e513e2', + 'rsrc/js/application/diff/DiffChangesetList.js' => 'fcbf80e9', + 'rsrc/js/application/diff/DiffInline.js' => 'c2e9ff4c', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', - 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '89d11432', + 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => 'd59300f5', 'rsrc/js/application/differential/behavior-populate.js' => '5e41c819', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', @@ -567,7 +567,7 @@ return array( 'conpherence-thread-manager' => '4d863052', 'conpherence-transaction-css' => '85129c68', 'd3' => 'a11a5ff2', - 'differential-changeset-view-css' => '69a3c268', + 'differential-changeset-view-css' => '6a77323e', 'differential-core-view-css' => '5b7b8ff4', 'differential-revision-add-comment-css' => 'c47f8c40', 'differential-revision-comment-css' => '14b8565a', @@ -619,7 +619,7 @@ return array( 'javelin-behavior-device' => 'bb1dd507', 'javelin-behavior-diff-preview-link' => '051c7832', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', - 'javelin-behavior-differential-edit-inline-comments' => '89d11432', + 'javelin-behavior-differential-edit-inline-comments' => 'd59300f5', 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-populate' => '5e41c819', 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', @@ -779,9 +779,9 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => 'c5742feb', - 'phabricator-diff-changeset-list' => 'f1101e6e', - 'phabricator-diff-inline' => 'a81c29d4', + 'phabricator-diff-changeset' => '34e513e2', + 'phabricator-diff-changeset-list' => 'fcbf80e9', + 'phabricator-diff-inline' => 'c2e9ff4c', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -1129,6 +1129,17 @@ return array( 'javelin-dom', 'javelin-workflow', ), + '34e513e2' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + 'phabricator-diff-inline', + ), '3ab51e2c' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -1398,12 +1409,12 @@ return array( '6882e80a' => array( 'javelin-dom', ), - '69a3c268' => array( - 'phui-inline-comment-view-css', - ), '69adf288' => array( 'javelin-install', ), + '6a77323e' => array( + 'phui-inline-comment-view-css', + ), '6ad39b6f' => array( 'javelin-install', 'javelin-event', @@ -1564,13 +1575,6 @@ return array( 'phabricator-draggable-list', 'javelin-workboard-column', ), - '89d11432' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - 'javelin-util', - 'javelin-vector', - ), '8a41885b' => array( 'javelin-install', 'javelin-dom', @@ -1718,9 +1722,6 @@ return array( 'javelin-stratcom', 'javelin-dom', ), - 'a81c29d4' => array( - 'javelin-dom', - ), 'a8beebea' => array( 'phui-oi-list-view-css', ), @@ -1909,23 +1910,15 @@ return array( 'javelin-dom', 'javelin-vector', ), + 'c2e9ff4c' => array( + 'javelin-dom', + ), 'c420b0b9' => array( 'javelin-behavior', 'javelin-behavior-device', 'javelin-stratcom', 'phabricator-tooltip', ), - 'c5742feb' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - 'phabricator-diff-inline', - ), 'c587b80f' => array( 'javelin-install', ), @@ -2043,6 +2036,13 @@ return array( 'javelin-dom', 'javelin-stratcom', ), + 'd59300f5' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + 'javelin-util', + 'javelin-vector', + ), 'd5a2d665' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2174,9 +2174,6 @@ return array( 'javelin-workflow', 'javelin-json', ), - 'f1101e6e' => array( - 'javelin-install', - ), 'f50152ad' => array( 'phui-timeline-view-css', ), @@ -2224,6 +2221,9 @@ return array( 'javelin-dom', 'phortune-credit-card-form', ), + 'fcbf80e9' => array( + 'javelin-install', + ), 'fe287620' => array( 'javelin-install', 'javelin-dom', diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index 1c14883a36..92a544c24c 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -109,10 +109,6 @@ color: {$darkgreytext}; } -.differential-diff th.selected { - background-color: {$sh-yellowbackground}; -} - .differential-changeset-immutable .differential-diff th { cursor: auto; } @@ -308,6 +304,7 @@ td.cov-I { top: 0; left: 0; box-sizing: border-box; + pointer-events: none; } .differential-diff .inline > td { diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index 3130fced44..8176e4f12b 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -27,6 +27,9 @@ JX.install('DiffChangeset', { this._encoding = data.encoding; this._loaded = data.loaded; + this._leftID = data.left; + this._rightID = data.right; + this._inlines = []; }, @@ -48,8 +51,19 @@ JX.install('DiffChangeset', { _encoding: null, _undoTemplates: null, + _leftID: null, + _rightID: null, + _inlines: null, + getLeftChangesetID: function() { + return this._leftID; + }, + + getRightChangesetID: function() { + return this._rightID; + }, + /** * Has the content of this changeset been loaded? * diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index 6380ccbf42..d9f1e35991 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -56,6 +56,12 @@ JX.install('DiffChangesetList', { 'mousedown', ['differential-inline-comment', 'differential-inline-header'], onselect); + + var onhover = JX.bind(this, this._ifawake, this._onhover); + JX.Stratcom.listen( + ['mouseover', 'mouseout'], + 'differential-inline-comment', + onhover); }, properties: { @@ -74,16 +80,24 @@ JX.install('DiffChangesetList', { _focusStart: null, _focusEnd: null, + _hoverNode: null, + _hoverInline: null, + _hoverOrigin: null, + _hoverTarget: null, + sleep: function() { this._asleep = true; this._redrawFocus(); + this._redrawSelection(); + this.resetHover(); }, wake: function() { this._asleep = false; this._redrawFocus(); + this._redrawSelection(); if (this._initialized) { return; @@ -428,6 +442,21 @@ JX.install('DiffChangesetList', { return result; }, + _onhover: function(e) { + if (e.getIsTouchEvent()) { + return; + } + + var inline; + if (e.getType() == 'mouseout') { + inline = null; + } else { + inline = this._getInlineForEvent(e); + } + + this._setHoverInline(inline); + }, + _onmore: function(e) { e.kill(); @@ -669,6 +698,8 @@ JX.install('DiffChangesetList', { _onresize: function() { this._redrawFocus(); + this._redrawSelection(); + this._redrawHover(); }, _onselect: function(e) { @@ -769,6 +800,11 @@ JX.install('DiffChangesetList', { if (forms.length) { JX.DOM.invoke(forms[0], 'shouldRefresh'); } + + // Clear the mouse hover reticle after a substantive edit: we don't get + // a "mouseout" event if the row vanished because of row being removed + // after an edit. + this.reseHover(); }, setFocus: function(node, extended_node) { @@ -812,6 +848,112 @@ JX.install('DiffChangesetList', { return this._focusNode; }, + _setHoverInline: function(inline) { + this._hoverInline = inline; + + if (inline) { + var changeset = inline.getChangeset(); + + var changeset_id; + var side = inline.getDisplaySide(); + if (side == 'right') { + changeset_id = changeset.getRightChangesetID(); + } else { + changeset_id = changeset.getLeftChangesetID(); + } + + var new_part; + if (inline.isNewFile()) { + new_part = 'N'; + } else { + new_part = 'O'; + } + + var prefix = 'C' + changeset_id + new_part + 'L'; + + var number = inline.getLineNumber(); + var length = inline.getLineLength(); + + var origin = JX.$(prefix + number); + var target = JX.$(prefix + (number + length)); + + this._hoverOrigin = origin; + this._hoverTarget = target; + } else { + this._hoverOrigin = null; + this._hoverTarget = null; + } + + this._redrawHover(); + }, + + resetHover: function() { + this._setHoverInline(null); + }, + + _redrawHover: function() { + var reticle = this._getHoverNode(); + if (!this._hoverOrigin || this.isAsleep()) { + JX.DOM.remove(reticle); + return; + } + + JX.DOM.getContentFrame().appendChild(reticle); + + var top = this._hoverOrigin; + var bot = this._hoverTarget; + if (JX.$V(top).y > JX.$V(bot).y) { + var tmp = top; + top = bot; + bot = tmp; + } + + // Find the leftmost cell that we're going to highlight: this is the next + // in the row. In 2up views, it should be directly adjacent. In + // 1up views, we may have to skip over the other line number column. + var l = top; + while (JX.DOM.isType(l, 'th')) { + l = l.nextSibling; + } + + // Find the rightmost cell that we're going to highlight: this is the + // farthest consecutive, adjacent in the row. Sometimes the left + // and right nodes are the same (left side of 2up view); sometimes we're + // going to highlight several nodes (copy + code + coverage). + var r = l; + while (r.nextSibling && JX.DOM.isType(r.nextSibling, 'td')) { + r = r.nextSibling; + } + + var pos = JX.$V(l) + .add(JX.Vector.getAggregateScrollForNode(l)); + + var dim = JX.$V(r) + .add(JX.Vector.getAggregateScrollForNode(r)) + .add(-pos.x, -pos.y) + .add(JX.Vector.getDim(r)); + + var bpos = JX.$V(bot) + .add(JX.Vector.getAggregateScrollForNode(bot)); + dim.y = (bpos.y - pos.y) + JX.Vector.getDim(bot).y; + + pos.setPos(reticle); + dim.setDim(reticle); + + JX.DOM.show(reticle); + }, + + _getHoverNode: function() { + if (!this._hoverNode) { + var attributes = { + className: 'differential-reticle' + }; + this._hoverNode = JX.$N('div', attributes); + } + + return this._hoverNode; + }, + _deleteInlineByID: function(id) { var uri = this.getInlineURI(); var data = { diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js index b7034fa653..38c2d7c82b 100644 --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -56,8 +56,8 @@ JX.install('DiffInline', { this._displaySide = 'left'; } - this._number = data.number; - this._length = data.length; + this._number = parseInt(data.number, 10); + this._length = parseInt(data.length, 10); this._originalText = data.original; this._isNewFile = (this.getDisplaySide() == 'right') || @@ -70,8 +70,8 @@ JX.install('DiffInline', { bindToRange: function(data) { this._displaySide = data.displaySide; - this._number = data.number; - this._length = data.length; + this._number = parseInt(data.number, 10); + this._length = parseInt(data.length, 10); this._isNewFile = data.isNewFile; this._changesetID = data.changesetID; @@ -365,9 +365,13 @@ JX.install('DiffInline', { }, _oncreateresponse: function(response) { + try { var rows = JX.$H(response).getNode(); this._drawEditRows(rows); + } catch (e) { + JX.log(e); + } }, _ondeleteresponse: function() { @@ -409,6 +413,7 @@ JX.install('DiffInline', { var first_row = JX.DOM.scry(rows, 'tr')[0]; var first_meta; var row = first_row; + var anchor = cursor || this._row; cursor = cursor || this._row.nextSibling; var next_row; @@ -417,7 +422,11 @@ JX.install('DiffInline', { // into the document. next_row = row.nextSibling; - cursor.parentNode.insertBefore(row, cursor); + // Bind edit and undo rows to this DiffInline object so that + // interactions like hovering work properly. + JX.Stratcom.getData(row).inline = this; + + anchor.parentNode.insertBefore(row, cursor); cursor = row; var row_meta = { @@ -528,6 +537,8 @@ JX.install('DiffInline', { this._removeRow(row); this.setInvisible(false); + + this._didUpdate(true); }, _readText: function(row) { @@ -579,8 +590,9 @@ JX.install('DiffInline', { } this.getChangeset().getChangesetList().redrawCursor(); + this.getChangeset().getChangesetList().resetHover(); - // Emit a resize event so that UI elements like the keyboad focus + // Emit a resize event so that UI elements like the keyboard focus // reticle can redraw properly. JX.Stratcom.invoke('resize'); }, diff --git a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js index 05410c8c7a..4b7b59decb 100644 --- a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js +++ b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js @@ -11,7 +11,6 @@ JX.behavior('differential-edit-inline-comments', function(config) { var selecting = false; var reticle = JX.$N('div', {className: 'differential-reticle'}); - var old_cells = []; JX.DOM.hide(reticle); var origin = null; @@ -19,28 +18,6 @@ JX.behavior('differential-edit-inline-comments', function(config) { var root = null; var changeset = null; - var editor = null; - - function updateReticleForComment(e) { - root = e.getNode('differential-changeset'); - if (!root) { - return; - } - - var data = e.getNodeData('differential-inline-comment'); - var change = e.getNodeData('differential-changeset'); - - var id_part = data.on_right ? change.right : change.left; - var new_part = data.isNewFile ? 'N' : 'O'; - var prefix = 'C' + id_part + new_part + 'L'; - - origin = JX.$(prefix + data.number); - target = JX.$(prefix + (parseInt(data.number, 10) + - parseInt(data.length, 10))); - - updateReticle(); - } - function updateReticle() { JX.DOM.getContentFrame().appendChild(reticle); @@ -85,44 +62,10 @@ JX.behavior('differential-edit-inline-comments', function(config) { dim.setDim(reticle); JX.DOM.show(reticle); - - // Find all the cells in the same row position between the top and bottom - // cell, so we can highlight them. - var seq = 0; - var row = top.parentNode; - for (seq = 0; seq < row.childNodes.length; seq++) { - if (row.childNodes[seq] == top) { - break; - } - } - - var cells = []; - while (true) { - cells.push(row.childNodes[seq]); - if (row.childNodes[seq] == bot) { - break; - } - row = row.nextSibling; - } - - setSelectedCells(cells); - } - - function setSelectedCells(new_cells) { - updateSelectedCellsClass(old_cells, false); - updateSelectedCellsClass(new_cells, true); - old_cells = new_cells; - } - - function updateSelectedCellsClass(cells, selected) { - for (var ii = 0; ii < cells.length; ii++) { - JX.DOM.alterClass(cells[ii], 'selected', selected); - } } function hideReticle() { JX.DOM.hide(reticle); - setSelectedCells([]); } function isOnRight(node) { @@ -180,13 +123,6 @@ JX.behavior('differential-edit-inline-comments', function(config) { return; } - if (editor) { - // Don't update the reticle if we're editing a comment, since this - // would be distracting and we want to keep the lines corresponding - // to the comment highlighted during the edit. - return; - } - if (getRowNumber(e.getTarget()) === undefined) { // Don't update the reticle if this "" doesn't correspond to a // line number. For instance, this may be a dead line number, like the @@ -236,7 +172,7 @@ JX.behavior('differential-edit-inline-comments', function(config) { 'mouseup', null, function(e) { - if (editor || !selecting) { + if (!selecting) { return; } @@ -280,12 +216,7 @@ JX.behavior('differential-edit-inline-comments', function(config) { if (e.getIsTouchEvent()) { return; } - - if (e.getType() == 'mouseout') { - hideReticle(); - } else { - updateReticleForComment(e); - } + hideReticle(); }); }); From e4e91ebf6f15936a90ab7fff4e249b3fb6dd0058 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 May 2017 16:52:31 -0700 Subject: [PATCH 49/81] In Differential, allow "r" to create comments and "R" to quote Summary: Ref T11401. Fixes T5232. Ref T12616. Partly, this moves more code over to the new stuff. This also allows "r" to work if you have code selected (not just comments). If you "reply" to code, you start a new comment. You can "R" a comment to quote it. This just starts a new comment normally if you "R" a block of code. This is sort of a power-user version of "quote" since it seems like it probably doesn't really make sense to put it in the UI ever (maybe). With the new click-to-select, you can click + "R" to reply-with-quote. Test Plan: Used "r" and "R" to reply to comments and code. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616, T11401, T5232 Differential Revision: https://secure.phabricator.com/D17920 --- resources/celerity/map.php | 66 +++++++------- .../view/DifferentialChangesetListView.php | 12 ++- .../rsrc/js/application/diff/DiffChangeset.js | 36 +++++++- .../js/application/diff/DiffChangesetList.js | 91 +++++++++++++++++-- .../rsrc/js/application/diff/DiffInline.js | 18 ++-- .../behavior-edit-inline-comments.js | 15 +-- 6 files changed, 170 insertions(+), 68 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 575ce9a2b1..125568ab64 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '8c5f913d', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => 'ea471cb0', - 'differential.pkg.js' => '7f24021f', + 'differential.pkg.js' => 'ae6f5198', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -390,13 +390,13 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => '34e513e2', - 'rsrc/js/application/diff/DiffChangesetList.js' => 'fcbf80e9', - 'rsrc/js/application/diff/DiffInline.js' => 'c2e9ff4c', + 'rsrc/js/application/diff/DiffChangeset.js' => '68758d99', + 'rsrc/js/application/diff/DiffChangesetList.js' => '57c491b5', + 'rsrc/js/application/diff/DiffInline.js' => '1afe9760', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', - 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => 'd59300f5', + 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '1c6bc8cf', 'rsrc/js/application/differential/behavior-populate.js' => '5e41c819', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', @@ -619,7 +619,7 @@ return array( 'javelin-behavior-device' => 'bb1dd507', 'javelin-behavior-diff-preview-link' => '051c7832', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', - 'javelin-behavior-differential-edit-inline-comments' => 'd59300f5', + 'javelin-behavior-differential-edit-inline-comments' => '1c6bc8cf', 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-populate' => '5e41c819', 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', @@ -779,9 +779,9 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => '34e513e2', - 'phabricator-diff-changeset-list' => 'fcbf80e9', - 'phabricator-diff-inline' => 'c2e9ff4c', + 'phabricator-diff-changeset' => '68758d99', + 'phabricator-diff-changeset-list' => '57c491b5', + 'phabricator-diff-inline' => '1afe9760', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -1030,6 +1030,9 @@ return array( 'javelin-util', 'phabricator-keyboard-shortcut-manager', ), + '1afe9760' => array( + 'javelin-dom', + ), '1bd28176' => array( 'javelin-install', 'javelin-dom', @@ -1037,6 +1040,13 @@ return array( 'javelin-request', 'javelin-uri', ), + '1c6bc8cf' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + 'javelin-util', + 'javelin-vector', + ), '1def2711' => array( 'javelin-install', 'javelin-dom', @@ -1129,17 +1139,6 @@ return array( 'javelin-dom', 'javelin-workflow', ), - '34e513e2' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - 'phabricator-diff-inline', - ), '3ab51e2c' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -1332,6 +1331,9 @@ return array( 'javelin-vector', 'javelin-dom', ), + '57c491b5' => array( + 'javelin-install', + ), '58dea2fa' => array( 'javelin-install', 'javelin-util', @@ -1406,6 +1408,17 @@ return array( 'javelin-dom', 'phabricator-notification', ), + '68758d99' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + 'phabricator-diff-inline', + ), '6882e80a' => array( 'javelin-dom', ), @@ -1910,9 +1923,6 @@ return array( 'javelin-dom', 'javelin-vector', ), - 'c2e9ff4c' => array( - 'javelin-dom', - ), 'c420b0b9' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -2036,13 +2046,6 @@ return array( 'javelin-dom', 'javelin-stratcom', ), - 'd59300f5' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - 'javelin-util', - 'javelin-vector', - ), 'd5a2d665' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2221,9 +2224,6 @@ return array( 'javelin-dom', 'phortune-credit-card-form', ), - 'fcbf80e9' => array( - 'javelin-install', - ), 'fe287620' => array( 'javelin-install', 'javelin-dom', diff --git a/src/applications/differential/view/DifferentialChangesetListView.php b/src/applications/differential/view/DifferentialChangesetListView.php index c69eef008f..1e40595bd0 100644 --- a/src/applications/differential/view/DifferentialChangesetListView.php +++ b/src/applications/differential/view/DifferentialChangesetListView.php @@ -244,15 +244,19 @@ final class DifferentialChangesetListView extends AphrontView { pht('Jump to previous inline comment.'), 'Jump to the table of contents.' => pht('Jump to the table of contents.'), - 'Reply to selected inline comment.' => - pht('Reply to selected inline comment.'), + 'Edit selected inline comment.' => pht('Edit selected inline comment.'), - 'You must select a comment to reply to.' => - pht('You must select a comment to reply to.'), 'You must select a comment to edit.' => pht('You must select a comment to edit.'), + 'Reply to selected inline comment or change.' => + pht('Reply to selected inline comment or change.'), + 'You must select a comment or change to reply to.' => + pht('You must select a comment or change to reply to.'), + 'Reply and quote selected inline comment.' => + pht('Reply and quote selected inline comment.'), + 'Mark or unmark selected inline comment as done.' => pht('Mark or unmark selected inline comment as done.'), 'You must select a comment to mark done.' => diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index 8176e4f12b..f6d1200c67 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -526,7 +526,37 @@ JX.install('DiffChangeset', { return data.inline; }, - newInlineForRange: function(data) { + newInlineForRange: function(origin, target) { + var list = this.getChangesetList(); + + var src = list.getLineNumberFromHeader(origin); + var dst = list.getLineNumberFromHeader(target); + + var changeset_id = null; + var side = list.getDisplaySideFromHeader(origin); + if (side == 'right') { + changeset_id = this.getRightChangesetID(); + } else { + changeset_id = this.getLeftChangesetID(); + } + + var is_new = false; + if (side == 'right') { + is_new = true; + } else if (this.getRightChangesetID() != this.getLeftChangesetID()) { + is_new = true; + } + + var data = { + origin: origin, + target: target, + number: src, + length: dst - src, + changesetID: changeset_id, + displaySide: side, + isNewFile: is_new + }; + var inline = new JX.DiffInline() .setChangeset(this) .bindToRange(data); @@ -538,14 +568,14 @@ JX.install('DiffChangeset', { return inline; }, - newInlineReply: function(original) { + newInlineReply: function(original, text) { var inline = new JX.DiffInline() .setChangeset(this) .bindToReply(original); this._inlines.push(inline); - inline.create(); + inline.create(text); return inline; }, diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index d9f1e35991..6067217419 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -136,8 +136,11 @@ JX.install('DiffChangesetList', { label = pht('Jump to the table of contents.'); this._installKey('t', label, this._ontoc); - label = pht('Reply to selected inline comment.'); - this._installKey('r', label, this._onkeyreply); + label = pht('Reply to selected inline comment or change.'); + this._installKey('r', label, JX.bind(this, this._onkeyreply, false)); + + label = pht('Reply and quote selected inline comment.'); + this._installKey('R', label, JX.bind(this, this._onkeyreply, true)); label = pht('Edit selected inline comment.'); this._installKey('e', label, this._onkeyedit); @@ -234,7 +237,7 @@ JX.install('DiffChangesetList', { manager.scrollTo(toc); }, - _onkeyreply: function() { + _onkeyreply: function(is_quote) { var cursor = this._cursorItem; if (cursor) { @@ -243,14 +246,77 @@ JX.install('DiffChangesetList', { if (inline.canReply()) { this.setFocus(null); - inline.reply(); + var text; + if (is_quote) { + text = inline.getRawText(); + text = '> ' + text.replace(/\n/g, '\n> ') + '\n\n'; + } else { + text = ''; + } + + inline.reply(text); return; } } + + // If the keyboard cursor is selecting a range of lines, we may have + // a mixture of old and new changes on the selected rows. It is not + // entirely unambiguous what the user means when they say they want + // to reply to this, but we use this logic: reply on the new file if + // there are any new lines. Otherwise (if there are only removed + // lines) reply on the old file. + + if (cursor.type == 'change') { + var origin = cursor.nodes.begin; + var target = cursor.nodes.end; + + // The "origin" and "target" are entire rows, but we need to find + // a range of "" nodes to actually create an inline, so go + // fishing. + + var old_list = []; + var new_list = []; + + var row = origin; + while (row) { + var header = row.firstChild; + while (header) { + if (JX.DOM.isType(header, 'th')) { + if (header.className.indexOf('old') !== -1) { + old_list.push(header); + } else if (header.className.indexOf('new') !== -1) { + new_list.push(header); + } + } + header = header.nextSibling; + } + + if (row == target) { + break; + } + + row = row.nextSibling; + } + + var use_list; + if (new_list.length) { + use_list = new_list; + } else { + use_list = old_list; + } + + var src = use_list[0]; + var dst = use_list[use_list.length - 1]; + + cursor.changeset.newInlineForRange(src, dst); + + this.setFocus(null); + return; + } } var pht = this.getTranslations(); - this._warnUser(pht('You must select a comment to reply to.')); + this._warnUser(pht('You must select a comment or change to reply to.')); }, _onkeyedit: function() { @@ -804,7 +870,7 @@ JX.install('DiffChangesetList', { // Clear the mouse hover reticle after a substantive edit: we don't get // a "mouseout" event if the row vanished because of row being removed // after an edit. - this.reseHover(); + this.resetHover(); }, setFocus: function(node, extended_node) { @@ -978,8 +1044,19 @@ JX.install('DiffChangesetList', { var inline_row = e.getNode('inline-row'); return changeset.getInlineForRow(inline_row); - } + }, + getLineNumberFromHeader: function(th) { + try { + return parseInt(th.id.match(/^C\d+[ON]L(\d+)$/)[1], 10); + } catch (x) { + return null; + } + }, + + getDisplaySideFromHeader: function(th) { + return (th.parentNode.firstChild != th) ? 'right' : 'left'; + } } }); diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js index 38c2d7c82b..edd31942f9 100644 --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -148,6 +148,10 @@ JX.install('DiffInline', { return true; }, + getRawText: function() { + return this._originalText; + }, + _hasAction: function(action) { var nodes = JX.DOM.scry(this._row, 'a', 'differential-inline-' + action); return (nodes.length > 0); @@ -247,9 +251,9 @@ JX.install('DiffInline', { .send(); }, - reply: function() { + reply: function(text) { var changeset = this.getChangeset(); - return changeset.newInlineReply(this); + return changeset.newInlineReply(this, text); }, edit: function(text) { @@ -365,13 +369,9 @@ JX.install('DiffInline', { }, _oncreateresponse: function(response) { - try { var rows = JX.$H(response).getNode(); this._drawEditRows(rows); - } catch (e) { - JX.log(e); - } }, _ondeleteresponse: function() { @@ -471,7 +471,11 @@ JX.install('DiffInline', { 'textarea', 'differential-inline-comment-edit-textarea'); if (textareas.length) { - textareas[0].focus(); + var area = textareas[0]; + area.focus(); + + var length = area.value.length; + JX.TextAreaUtils.setSelectionRange(area, length, length); } row = next_row; diff --git a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js index 4b7b59decb..08c09d6626 100644 --- a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js +++ b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js @@ -72,11 +72,6 @@ JX.behavior('differential-edit-inline-comments', function(config) { return node.parentNode.firstChild != node; } - function isNewFile(node) { - var data = JX.Stratcom.getData(root); - return isOnRight(node) || (data.left != data.right); - } - function getRowNumber(th_node) { try { return parseInt(th_node.id.match(/^C\d+[ON]L(\d+)$/)[1], 10); @@ -192,15 +187,7 @@ JX.behavior('differential-edit-inline-comments', function(config) { var view = JX.DiffChangeset.getForNode(root); - view.newInlineForRange({ - origin: origin, - target: target, - number: o, - length: len, - changesetID: changeset, - isNewFile: isNewFile(target), - displaySide: isOnRight(target) ? 'right' : 'left' - }); + view.newInlineForRange(origin, target); selecting = false; origin = null; From 422eb9db837dd4f6ae4de49acbea61554e653476 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 17 May 2017 05:52:45 -0700 Subject: [PATCH 50/81] Correct the generation of "" IDs on left-hand-side of image changesets Summary: Fixes T7682. The left-hand-side "" row did not generate with the correct ID. (I couldn't reproduce the exact issue described in T7682, but hovering comments on either side now works properly for me.) Test Plan: {F4962479} Reviewers: chad Reviewed By: chad Maniphest Tasks: T7682 Differential Revision: https://secure.phabricator.com/D17926 --- .../render/DifferentialChangesetTwoUpRenderer.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php b/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php index 688544513e..5d476f5136 100644 --- a/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php +++ b/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php @@ -323,6 +323,12 @@ final class DifferentialChangesetTwoUpRenderer $new = $this->renderImageStage($new_file); } + // If we don't have an explicit "vs" changeset, it's the left side of the + // "id" changeset. + if (!$vs) { + $vs = $id; + } + $html_old = array(); $html_new = array(); foreach ($this->getOldComments() as $on_line => $comment_group) { From 6ecd6980a1420633d5e02d09f67262d9dc876089 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 16 May 2017 20:13:34 -0700 Subject: [PATCH 51/81] Allow setting of button colors in headers Summary: We currently override button color for headers, since the default is blue, but if a developer sets a specific color, we should respect that. Test Plan: Set a button in the header to green and see green. See grey everywhere else. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17922 --- src/view/phui/PHUIButtonView.php | 4 ++++ src/view/phui/PHUIHeaderView.php | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/view/phui/PHUIButtonView.php b/src/view/phui/PHUIButtonView.php index c4ee7a190c..ee6975357c 100644 --- a/src/view/phui/PHUIButtonView.php +++ b/src/view/phui/PHUIButtonView.php @@ -64,6 +64,10 @@ final class PHUIButtonView extends AphrontTagView { return $this; } + public function getColor() { + return $this->color; + } + public function setDisabled($disabled) { $this->disabled = $disabled; return $this; diff --git a/src/view/phui/PHUIHeaderView.php b/src/view/phui/PHUIHeaderView.php index 0ae2c558d6..0cd8379b42 100644 --- a/src/view/phui/PHUIHeaderView.php +++ b/src/view/phui/PHUIHeaderView.php @@ -267,7 +267,9 @@ final class PHUIHeaderView extends AphrontTagView { if ($this->actionLinks) { $actions = array(); foreach ($this->actionLinks as $button) { - $button->setColor(PHUIButtonView::GREY); + if (!$button->getColor()) { + $button->setColor(PHUIButtonView::GREY); + } $button->addClass(PHUI::MARGIN_SMALL_LEFT); $button->addClass('phui-header-action-link'); $actions[] = $button; From 51df02821b86a7d48ddb708750328ad44279bdc6 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 17 May 2017 07:00:42 -0700 Subject: [PATCH 52/81] Move the "select a line range" inline code to DiffInline Summary: Ref T12616. This makes line range selection use the new code, and removes the remainder of the old "hover a line number" / "select a line range" code. Test Plan: Hovered line numbers; selected line ranges. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616 Differential Revision: https://secure.phabricator.com/D17927 --- resources/celerity/map.php | 42 ++-- resources/celerity/packages.php | 1 - .../view/DifferentialChangesetListView.php | 7 - webroot/rsrc/externals/javelin/lib/DOM.js | 4 +- .../js/application/diff/DiffChangesetList.js | 149 +++++++++++++ .../behavior-edit-inline-comments.js | 209 ------------------ 6 files changed, 167 insertions(+), 245 deletions(-) delete mode 100644 webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 125568ab64..794b117e82 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -10,10 +10,10 @@ return array( 'conpherence.pkg.css' => 'ff161f2d', 'conpherence.pkg.js' => 'b5b51108', 'core.pkg.css' => 'ee5f28cd', - 'core.pkg.js' => '8c5f913d', + 'core.pkg.js' => '0f87a6eb', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => 'ea471cb0', - 'differential.pkg.js' => 'ae6f5198', + 'differential.pkg.js' => '85c19957', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -237,7 +237,7 @@ return array( 'rsrc/externals/javelin/ext/view/__tests__/ViewInterpreter.js' => '7a94d6a5', 'rsrc/externals/javelin/ext/view/__tests__/ViewRenderer.js' => '6ea96ac9', 'rsrc/externals/javelin/lib/Cookie.js' => '62dfea03', - 'rsrc/externals/javelin/lib/DOM.js' => '805b806a', + 'rsrc/externals/javelin/lib/DOM.js' => '4976858c', 'rsrc/externals/javelin/lib/History.js' => 'd4505101', 'rsrc/externals/javelin/lib/JSON.js' => '69adf288', 'rsrc/externals/javelin/lib/Leader.js' => '7f243deb', @@ -391,12 +391,11 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', 'rsrc/js/application/diff/DiffChangeset.js' => '68758d99', - 'rsrc/js/application/diff/DiffChangesetList.js' => '57c491b5', + 'rsrc/js/application/diff/DiffChangesetList.js' => '842e2676', 'rsrc/js/application/diff/DiffInline.js' => '1afe9760', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', - 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '1c6bc8cf', 'rsrc/js/application/differential/behavior-populate.js' => '5e41c819', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', @@ -619,7 +618,6 @@ return array( 'javelin-behavior-device' => 'bb1dd507', 'javelin-behavior-diff-preview-link' => '051c7832', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', - 'javelin-behavior-differential-edit-inline-comments' => '1c6bc8cf', 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-populate' => '5e41c819', 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', @@ -715,7 +713,7 @@ return array( 'javelin-color' => '7e41274a', 'javelin-cookie' => '62dfea03', 'javelin-diffusion-locate-file-source' => 'c93358e3', - 'javelin-dom' => '805b806a', + 'javelin-dom' => '4976858c', 'javelin-dynval' => 'f6555212', 'javelin-event' => '2ee659ce', 'javelin-fx' => '54b612ba', @@ -780,7 +778,7 @@ return array( 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', 'phabricator-diff-changeset' => '68758d99', - 'phabricator-diff-changeset-list' => '57c491b5', + 'phabricator-diff-changeset-list' => '842e2676', 'phabricator-diff-inline' => '1afe9760', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', @@ -1040,13 +1038,6 @@ return array( 'javelin-request', 'javelin-uri', ), - '1c6bc8cf' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - 'javelin-util', - 'javelin-vector', - ), '1def2711' => array( 'javelin-install', 'javelin-dom', @@ -1250,6 +1241,13 @@ return array( 'javelin-uri', 'phabricator-notification', ), + '4976858c' => array( + 'javelin-magical-init', + 'javelin-install', + 'javelin-util', + 'javelin-vector', + 'javelin-stratcom', + ), '49ae8328' => array( 'javelin-behavior', 'javelin-dom', @@ -1331,9 +1329,6 @@ return array( 'javelin-vector', 'javelin-dom', ), - '57c491b5' => array( - 'javelin-install', - ), '58dea2fa' => array( 'javelin-install', 'javelin-util', @@ -1529,13 +1524,6 @@ return array( 'javelin-vector', 'javelin-stratcom', ), - '805b806a' => array( - 'javelin-magical-init', - 'javelin-install', - 'javelin-util', - 'javelin-vector', - 'javelin-stratcom', - ), '834a1173' => array( 'javelin-behavior', 'javelin-scrollbar', @@ -1544,6 +1532,9 @@ return array( 'javelin-install', 'javelin-dom', ), + '842e2676' => array( + 'javelin-install', + ), '8499b6ab' => array( 'javelin-behavior', 'javelin-dom', @@ -2421,7 +2412,6 @@ return array( 'phabricator-drag-and-drop-file-upload', 'phabricator-shaped-request', 'javelin-behavior-differential-feedback-preview', - 'javelin-behavior-differential-edit-inline-comments', 'javelin-behavior-differential-populate', 'javelin-behavior-differential-diff-radios', 'javelin-behavior-aphront-drag-and-drop-textarea', diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php index 9ea3fa6b35..619a5d6389 100644 --- a/resources/celerity/packages.php +++ b/resources/celerity/packages.php @@ -193,7 +193,6 @@ return array( 'phabricator-shaped-request', 'javelin-behavior-differential-feedback-preview', - 'javelin-behavior-differential-edit-inline-comments', 'javelin-behavior-differential-populate', 'javelin-behavior-differential-diff-radios', 'javelin-behavior-aphront-drag-and-drop-textarea', diff --git a/src/applications/differential/view/DifferentialChangesetListView.php b/src/applications/differential/view/DifferentialChangesetListView.php index 1e40595bd0..aa34b0415f 100644 --- a/src/applications/differential/view/DifferentialChangesetListView.php +++ b/src/applications/differential/view/DifferentialChangesetListView.php @@ -274,13 +274,6 @@ final class DifferentialChangesetListView extends AphrontView { ), )); - if ($this->inlineURI) { - Javelin::initBehavior('differential-edit-inline-comments', array( - 'uri' => $this->inlineURI, - 'stage' => 'differential-review-stage', - )); - } - if ($this->header) { $header = $this->header; } else { diff --git a/webroot/rsrc/externals/javelin/lib/DOM.js b/webroot/rsrc/externals/javelin/lib/DOM.js index 189d778ad5..e0e3d764e0 100644 --- a/webroot/rsrc/externals/javelin/lib/DOM.js +++ b/webroot/rsrc/externals/javelin/lib/DOM.js @@ -891,7 +891,7 @@ JX.install('DOM', { * it. * * @param Node Node to look above. - * @param string Tag name, like 'a' or 'textarea'. + * @param string Optional tag name, like 'a' or 'textarea'. * @param string Optionally, sigil which selected node must have. * @return Node Matching node. * @@ -911,7 +911,7 @@ JX.install('DOM', { if (!result) { break; } - if (JX.DOM.isType(result, tagname)) { + if (!tagname || JX.DOM.isType(result, tagname)) { if (!sigil || JX.Stratcom.hasSigil(result, sigil)) { break; } diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index 6067217419..8c2ff6e90f 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -62,6 +62,21 @@ JX.install('DiffChangesetList', { ['mouseover', 'mouseout'], 'differential-inline-comment', onhover); + + var onrangedown = JX.bind(this, this._ifawake, this._onrangedown); + JX.Stratcom.listen( + 'mousedown', + ['differential-changeset', 'tag:th'], + onrangedown); + + var onrangemove = JX.bind(this, this._ifawake, this._onrangemove); + JX.Stratcom.listen( + ['mouseover', 'mouseout'], + ['differential-changeset', 'tag:th'], + onrangemove); + + var onrangeup = JX.bind(this, this._ifawake, this._onrangeup); + JX.Stratcom.listen('mouseup', null, onrangeup); }, properties: { @@ -85,6 +100,10 @@ JX.install('DiffChangesetList', { _hoverOrigin: null, _hoverTarget: null, + _rangeActive: false, + _rangeOrigin: null, + _rangeTarget: null, + sleep: function() { this._asleep = true; @@ -953,8 +972,18 @@ JX.install('DiffChangesetList', { this._redrawHover(); }, + _setHoverRange: function(origin, target) { + this._hoverOrigin = origin; + this._hoverTarget = target; + + this._redrawHover(); + }, + resetHover: function() { this._setHoverInline(null); + + this._hoverOrigin = null; + this._hoverTarget = null; }, _redrawHover: function() { @@ -1056,6 +1085,126 @@ JX.install('DiffChangesetList', { getDisplaySideFromHeader: function(th) { return (th.parentNode.firstChild != th) ? 'right' : 'left'; + }, + + _onrangedown: function(e) { + if (!e.isNormalMouseEvent()) { + return; + } + + if (e.getIsTouchEvent()) { + return; + } + + if (this._rangeActive) { + return; + } + + var target = e.getTarget(); + var number = this.getLineNumberFromHeader(target); + if (!number) { + return; + } + + e.kill(); + this._rangeActive = true; + + this._rangeOrigin = target; + this._rangeTarget = target; + + this._setHoverRange(this._rangeOrigin, this._rangeTarget); + }, + + _onrangemove: function(e) { + if (e.getIsTouchEvent()) { + return; + } + + var target = e.getTarget(); + + // Don't update the range if this "" doesn't correspond to a line + // number. For instance, this may be a dead line number, like the empty + // line numbers on the left hand side of a newly added file. + var number = this.getLineNumberFromHeader(target); + if (!number) { + return; + } + + if (this._rangeActive) { + var origin = this._hoverOrigin; + + // Don't update the reticle if we're selecting a line range and the + // "" under the cursor is on the wrong side of the file. You can + // only leave inline comments on the left or right side of a file, not + // across lines on both sides. + var origin_side = this.getDisplaySideFromHeader(origin); + var target_side = this.getDisplaySideFromHeader(target); + if (origin_side != target_side) { + return; + } + + // Don't update the reticle if we're selecting a line range and the + // "" under the cursor corresponds to a different file. You can + // only leave inline comments on lines in a single file, not across + // multiple files. + var origin_table = JX.DOM.findAbove(origin, 'table'); + var target_table = JX.DOM.findAbove(target, 'table'); + if (origin_table != target_table) { + return; + } + } + + var is_out = (e.getType() == 'mouseout'); + if (is_out) { + if (this._rangeActive) { + // If we're dragging a range, just leave the state as it is. This + // allows you to drag over something invalid while selecting a + // range without the range flickering or getting lost. + } else { + // Otherwise, clear the current range. + this.resetHover(); + } + return; + } + + if (this._rangeActive) { + this._rangeTarget = target; + } else { + this._rangeOrigin = target; + this._rangeTarget = target; + } + + this._setHoverRange(this._rangeOrigin, this._rangeTarget); + }, + + _onrangeup: function(e) { + if (!this._rangeActive) { + return; + } + + e.kill(); + + var origin = this._rangeOrigin; + var target = this._rangeTarget; + + // If the user dragged a range from the bottom to the top, swap the node + // order around. + if (JX.$V(origin).y > JX.$V(target).y) { + var tmp = target; + target = origin; + origin = tmp; + } + + var node = JX.DOM.findAbove(origin, null, 'differential-changeset'); + var changeset = this.getChangesetForNode(node); + + changeset.newInlineForRange(origin, target); + + this._rangeActive = false; + this._rangeOrigin = null; + this._rangeTarget = null; + + this.resetHover(); } } diff --git a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js b/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js deleted file mode 100644 index 08c09d6626..0000000000 --- a/webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js +++ /dev/null @@ -1,209 +0,0 @@ -/** - * @provides javelin-behavior-differential-edit-inline-comments - * @requires javelin-behavior - * javelin-stratcom - * javelin-dom - * javelin-util - * javelin-vector - */ - -JX.behavior('differential-edit-inline-comments', function(config) { - - var selecting = false; - var reticle = JX.$N('div', {className: 'differential-reticle'}); - JX.DOM.hide(reticle); - - var origin = null; - var target = null; - var root = null; - var changeset = null; - - function updateReticle() { - JX.DOM.getContentFrame().appendChild(reticle); - - var top = origin; - var bot = target; - if (JX.$V(top).y > JX.$V(bot).y) { - var tmp = top; - top = bot; - bot = tmp; - } - - // Find the leftmost cell that we're going to highlight: this is the next - // in the row. In 2up views, it should be directly adjacent. In - // 1up views, we may have to skip over the other line number column. - var l = top; - while (JX.DOM.isType(l, 'th')) { - l = l.nextSibling; - } - - // Find the rightmost cell that we're going to highlight: this is the - // farthest consecutive, adjacent in the row. Sometimes the left - // and right nodes are the same (left side of 2up view); sometimes we're - // going to highlight several nodes (copy + code + coverage). - var r = l; - while (r.nextSibling && JX.DOM.isType(r.nextSibling, 'td')) { - r = r.nextSibling; - } - - var pos = JX.$V(l) - .add(JX.Vector.getAggregateScrollForNode(l)); - - var dim = JX.$V(r) - .add(JX.Vector.getAggregateScrollForNode(r)) - .add(-pos.x, -pos.y) - .add(JX.Vector.getDim(r)); - - var bpos = JX.$V(bot) - .add(JX.Vector.getAggregateScrollForNode(bot)); - dim.y = (bpos.y - pos.y) + JX.Vector.getDim(bot).y; - - pos.setPos(reticle); - dim.setDim(reticle); - - JX.DOM.show(reticle); - } - - function hideReticle() { - JX.DOM.hide(reticle); - } - - function isOnRight(node) { - return node.parentNode.firstChild != node; - } - - function getRowNumber(th_node) { - try { - return parseInt(th_node.id.match(/^C\d+[ON]L(\d+)$/)[1], 10); - } catch (x) { - return undefined; - } - } - - JX.Stratcom.listen( - 'mousedown', - ['differential-changeset', 'tag:th'], - function(e) { - if (e.isRightButton() || - getRowNumber(e.getTarget()) === undefined) { - return; - } - - if (selecting) { - return; - } - - selecting = true; - root = e.getNode('differential-changeset'); - - origin = target = e.getTarget(); - - var data = e.getNodeData('differential-changeset'); - if (isOnRight(target)) { - changeset = data.right; - } else { - changeset = data.left; - } - - updateReticle(); - - e.kill(); - }); - - JX.Stratcom.listen( - ['mouseover', 'mouseout'], - ['differential-changeset', 'tag:th'], - function(e) { - if (e.getIsTouchEvent()) { - return; - } - - if (getRowNumber(e.getTarget()) === undefined) { - // Don't update the reticle if this "" doesn't correspond to a - // line number. For instance, this may be a dead line number, like the - // empty line numbers on the left hand side of a newly added file. - return; - } - - if (selecting) { - if (isOnRight(e.getTarget()) != isOnRight(origin)) { - // Don't update the reticle if we're selecting a line range and the - // "" under the cursor is on the wrong side of the file. You - // can only leave inline comments on the left or right side of a - // file, not across lines on both sides. - return; - } - - if (e.getNode('differential-changeset') !== root) { - // Don't update the reticle if we're selecting a line range and - // the "" under the cursor corresponds to a different file. - // You can only leave inline comments on lines in a single file, - // not across multiple files. - return; - } - } - - if (e.getType() == 'mouseout') { - if (selecting) { - // Don't hide the reticle if we're selecting, since we want to - // keep showing the line range that will be used if the mouse is - // released. - return; - } - hideReticle(); - } else { - target = e.getTarget(); - if (!selecting) { - // If we're just hovering the mouse and not selecting a line range, - // set the origin to the current row so we highlight it. - origin = target; - } - - updateReticle(); - } - }); - - JX.Stratcom.listen( - 'mouseup', - null, - function(e) { - if (!selecting) { - return; - } - - var o = getRowNumber(origin); - var t = getRowNumber(target); - - var insert; - var len; - if (t < o) { - len = (o - t); - o = t; - insert = origin.parentNode; - } else { - len = (t - o); - insert = target.parentNode; - } - - var view = JX.DiffChangeset.getForNode(root); - - view.newInlineForRange(origin, target); - - selecting = false; - origin = null; - target = null; - - e.kill(); - }); - - JX.Stratcom.listen( - ['mouseover', 'mouseout'], - 'differential-inline-comment', - function(e) { - if (e.getIsTouchEvent()) { - return; - } - hideReticle(); - }); - -}); From 343f7cac72bbd9c64deeb71cdccd03444169a02b Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 17 May 2017 07:38:16 -0700 Subject: [PATCH 53/81] Improve mobile/device behaviors for inline comments Summary: Fixes T1026. Ref T12616. Allows drag-to-select on devices to add inlines on a range of lines, using dark magic that I copy/pasted from StackOverflow. Test Plan: Left a comment on a range of lines on iPhone simulator. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616, T1026 Differential Revision: https://secure.phabricator.com/D17928 --- resources/celerity/map.php | 12 ++--- .../js/application/diff/DiffChangesetList.js | 52 ++++++++++++++++--- 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 794b117e82..40b61fca95 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '0f87a6eb', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => 'ea471cb0', - 'differential.pkg.js' => '85c19957', + 'differential.pkg.js' => '58457c19', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -391,7 +391,7 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', 'rsrc/js/application/diff/DiffChangeset.js' => '68758d99', - 'rsrc/js/application/diff/DiffChangesetList.js' => '842e2676', + 'rsrc/js/application/diff/DiffChangesetList.js' => '204e4bfc', 'rsrc/js/application/diff/DiffInline.js' => '1afe9760', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', @@ -778,7 +778,7 @@ return array( 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', 'phabricator-diff-changeset' => '68758d99', - 'phabricator-diff-changeset-list' => '842e2676', + 'phabricator-diff-changeset-list' => '204e4bfc', 'phabricator-diff-inline' => '1afe9760', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', @@ -1066,6 +1066,9 @@ return array( 'javelin-install', 'javelin-dom', ), + '204e4bfc' => array( + 'javelin-install', + ), '21df4ff5' => array( 'javelin-install', 'javelin-workboard-card', @@ -1532,9 +1535,6 @@ return array( 'javelin-install', 'javelin-dom', ), - '842e2676' => array( - 'javelin-install', - ), '8499b6ab' => array( 'javelin-behavior', 'javelin-dom', diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index 8c2ff6e90f..e57ebc1674 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -65,7 +65,7 @@ JX.install('DiffChangesetList', { var onrangedown = JX.bind(this, this._ifawake, this._onrangedown); JX.Stratcom.listen( - 'mousedown', + ['touchstart', 'mousedown'], ['differential-changeset', 'tag:th'], onrangedown); @@ -75,8 +75,17 @@ JX.install('DiffChangesetList', { ['differential-changeset', 'tag:th'], onrangemove); + var onrangetouchmove = JX.bind(this, this._ifawake, this._onrangetouchmove); + JX.Stratcom.listen( + 'touchmove', + null, + onrangetouchmove); + var onrangeup = JX.bind(this, this._ifawake, this._onrangeup); - JX.Stratcom.listen('mouseup', null, onrangeup); + JX.Stratcom.listen( + ['touchend', 'mouseup'], + null, + onrangeup); }, properties: { @@ -1088,11 +1097,9 @@ JX.install('DiffChangesetList', { }, _onrangedown: function(e) { - if (!e.isNormalMouseEvent()) { - return; - } - - if (e.getIsTouchEvent()) { + // NOTE: We're allowing touch events through, including "touchstart". We + // need to kill the "touchstart" event so the page doesn't scroll. + if (e.isRightButton()) { return; } @@ -1120,8 +1127,13 @@ JX.install('DiffChangesetList', { return; } + var is_out = (e.getType() == 'mouseout'); var target = e.getTarget(); + this._updateRange(target, is_out); + }, + + _updateRange: function(target, is_out) { // Don't update the range if this "" doesn't correspond to a line // number. For instance, this may be a dead line number, like the empty // line numbers on the left hand side of a newly added file. @@ -1154,7 +1166,6 @@ JX.install('DiffChangesetList', { } } - var is_out = (e.getType() == 'mouseout'); if (is_out) { if (this._rangeActive) { // If we're dragging a range, just leave the state as it is. This @@ -1177,6 +1188,31 @@ JX.install('DiffChangesetList', { this._setHoverRange(this._rangeOrigin, this._rangeTarget); }, + _onrangetouchmove: function(e) { + if (!this._rangeActive) { + return; + } + + // NOTE: The target of a "touchmove" event is bogus. Use dark magic to + // identify the actual target. Some day, this might move into the core + // libraries. If this doesn't work, just bail. + + var target; + try { + var raw_event = e.getRawEvent(); + var touch = raw_event.touches[0]; + target = document.elementFromPoint(touch.clientX, touch.clientY); + } catch (ex) { + return; + } + + if (!JX.DOM.isType(target, 'th')) { + return; + } + + this._updateRange(target, false); + }, + _onrangeup: function(e) { if (!this._rangeActive) { return; From 9eb285f4aabb27b8e3cb144a9e020ee1ac0fa747 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 17 May 2017 08:25:39 -0700 Subject: [PATCH 54/81] Fix "left"/"right" changeset ID selection for synthetic deletions Summary: Fixes T8323. See that task for a description. We were using `nonempty()`, but that rule doesn't cover synthetic deletions (file present in an earlier diff, but no longer present in the later diff). Test Plan: Followed the steps in T8323, got a clean comment. Reviewers: chad Reviewed By: chad Maniphest Tasks: T8323 Differential Revision: https://secure.phabricator.com/D17929 --- .../view/DifferentialChangesetDetailView.php | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/applications/differential/view/DifferentialChangesetDetailView.php b/src/applications/differential/view/DifferentialChangesetDetailView.php index 219dfaaa1f..42be1c6444 100644 --- a/src/applications/differential/view/DifferentialChangesetDetailView.php +++ b/src/applications/differential/view/DifferentialChangesetDetailView.php @@ -150,15 +150,31 @@ final class DifferentialChangesetDetailView extends AphrontView { $renderer = DifferentialChangesetHTMLRenderer::getHTMLRendererByKey( $this->getRenderer()); + $changeset_id = $this->changeset->getID(); + + $vs_id = $this->getVsChangesetID(); + if (!$vs_id) { + // Showing a changeset normally. + $left_id = $changeset_id; + $right_id = $changeset_id; + } else if ($vs_id == -1) { + // Showing a synthetic "deleted" changeset for a file which was + // removed between changes. + $left_id = $changeset_id; + $right_id = null; + } else { + // Showing a diff-of-diffs. + $left_id = $vs_id; + $right_id = $changeset_id; + } + return javelin_tag( 'div', array( 'sigil' => 'differential-changeset', 'meta' => array( - 'left' => nonempty( - $this->getVsChangesetID(), - $this->changeset->getID()), - 'right' => $this->changeset->getID(), + 'left' => $left_id, + 'right' => $right_id, 'renderURI' => $this->getRenderURI(), 'whitespace' => $this->getWhitespace(), 'highlight' => null, From dde63af1cc86c5eedc1cbe492d8c721aa59a67b5 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 17 May 2017 08:35:13 -0700 Subject: [PATCH 55/81] Fix a JS console warning when hovering over replies to ghosts on lines which no longer exist Summary: Fixes T11662. In the very obscure situation described in that task, quiet a JS console warning. The actual edit operation appears to work correctly after changes elsewhere. There aren't really any legitimate lines for us to highlight in this case so I'm just giving up rather than trying to do something approximate. Test Plan: - Wrote `long.txt`. - Created revision. - Added an inline near the bottom. - Removed most of `long.txt`. - Updated revsion. - Replied to the ghost inline. - Edited the reply to the ghost inline, worked. - Hovered the reply to the ghost inline: no line highlight, but no errors. Reviewers: chad Reviewed By: chad Maniphest Tasks: T11662 Differential Revision: https://secure.phabricator.com/D17930 --- resources/celerity/map.php | 12 ++++++------ .../js/application/diff/DiffChangesetList.js | 19 +++++++++++++++---- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 40b61fca95..e82602aa0b 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '0f87a6eb', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => 'ea471cb0', - 'differential.pkg.js' => '58457c19', + 'differential.pkg.js' => '4a466790', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -391,7 +391,7 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', 'rsrc/js/application/diff/DiffChangeset.js' => '68758d99', - 'rsrc/js/application/diff/DiffChangesetList.js' => '204e4bfc', + 'rsrc/js/application/diff/DiffChangesetList.js' => '796922e0', 'rsrc/js/application/diff/DiffInline.js' => '1afe9760', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', @@ -778,7 +778,7 @@ return array( 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', 'phabricator-diff-changeset' => '68758d99', - 'phabricator-diff-changeset-list' => '204e4bfc', + 'phabricator-diff-changeset-list' => '796922e0', 'phabricator-diff-inline' => '1afe9760', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', @@ -1066,9 +1066,6 @@ return array( 'javelin-install', 'javelin-dom', ), - '204e4bfc' => array( - 'javelin-install', - ), '21df4ff5' => array( 'javelin-install', 'javelin-workboard-card', @@ -1496,6 +1493,9 @@ return array( 'javelin-behavior', 'javelin-quicksand', ), + '796922e0' => array( + 'javelin-install', + ), '7a68dda3' => array( 'owners-path-editor', 'javelin-behavior', diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index e57ebc1674..6e3853a9d1 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -968,11 +968,22 @@ JX.install('DiffChangesetList', { var number = inline.getLineNumber(); var length = inline.getLineLength(); - var origin = JX.$(prefix + number); - var target = JX.$(prefix + (number + length)); + try { + var origin = JX.$(prefix + number); + var target = JX.$(prefix + (number + length)); - this._hoverOrigin = origin; - this._hoverTarget = target; + this._hoverOrigin = origin; + this._hoverTarget = target; + } catch (error) { + // There may not be any nodes present in the document. A case where + // this occurs is when you reply to a ghost inline which was made + // on lines near the bottom of "long.txt" in an earlier diff, and + // the file was later shortened so those lines no longer exist. For + // more details, see T11662. + + this._hoverOrigin = null; + this._hoverTarget = null; + } } else { this._hoverOrigin = null; this._hoverTarget = null; From 75fb1a0327cb199cdff7d56ffe95217a9d227711 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Wed, 17 May 2017 09:06:07 -0700 Subject: [PATCH 56/81] Don't render an action list without actions Summary: Skips rendering of partial elements if no actions are present. Test Plan: Tested on profile menu item page, maniphest curtain, phriction dropdown, and instance backups page (no actions at all). Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17931 --- resources/celerity/map.php | 6 +++--- src/view/layout/PhabricatorActionListView.php | 4 ++++ webroot/rsrc/css/phui/phui-curtain-view.css | 7 ++++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index e82602aa0b..4e06cdc7f2 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ return array( 'names' => array( 'conpherence.pkg.css' => 'ff161f2d', 'conpherence.pkg.js' => 'b5b51108', - 'core.pkg.css' => 'ee5f28cd', + 'core.pkg.css' => 'a5a2d647', 'core.pkg.js' => '0f87a6eb', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => 'ea471cb0', @@ -145,7 +145,7 @@ return array( 'rsrc/css/phui/phui-comment-form.css' => '57af2e14', 'rsrc/css/phui/phui-comment-panel.css' => 'f50152ad', 'rsrc/css/phui/phui-crumbs-view.css' => '6ece3bbb', - 'rsrc/css/phui/phui-curtain-view.css' => '679743bb', + 'rsrc/css/phui/phui-curtain-view.css' => '55dd0e59', 'rsrc/css/phui/phui-document-pro.css' => '62c4dcbf', 'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf', 'rsrc/css/phui/phui-document.css' => 'c32e8dec', @@ -844,7 +844,7 @@ return array( 'phui-comment-form-css' => '57af2e14', 'phui-comment-panel-css' => 'f50152ad', 'phui-crumbs-view-css' => '6ece3bbb', - 'phui-curtain-view-css' => '679743bb', + 'phui-curtain-view-css' => '55dd0e59', 'phui-document-summary-view-css' => '9ca48bdf', 'phui-document-view-css' => 'c32e8dec', 'phui-document-view-pro-css' => '62c4dcbf', diff --git a/src/view/layout/PhabricatorActionListView.php b/src/view/layout/PhabricatorActionListView.php index faf30555eb..134c336735 100644 --- a/src/view/layout/PhabricatorActionListView.php +++ b/src/view/layout/PhabricatorActionListView.php @@ -16,6 +16,10 @@ final class PhabricatorActionListView extends AphrontTagView { } protected function getTagName() { + if (!$this->actions) { + return null; + } + return 'ul'; } diff --git a/webroot/rsrc/css/phui/phui-curtain-view.css b/webroot/rsrc/css/phui/phui-curtain-view.css index dbc706994a..0d7d4f942f 100644 --- a/webroot/rsrc/css/phui/phui-curtain-view.css +++ b/webroot/rsrc/css/phui/phui-curtain-view.css @@ -7,12 +7,17 @@ margin: 0 4px; } +.phui-two-column-properties > .phui-curtain-panel:first-child { + padding-top: 6px; +} + .device .phui-curtain-panel { padding: 8px 0; margin: 0; } -.device-desktop .phui-curtain-panel { +.device-desktop .phui-curtain-panel + .phui-curtain-panel, +.device-desktop .phabricator-action-list-view + .phui-curtain-panel { border-top: 1px solid {$greybackground}; } From d03a497616cf5be0ace7235e6d1c83558eea655f Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 17 May 2017 11:39:56 -0700 Subject: [PATCH 57/81] Allow any inline in the document to be queried by ID Summary: Ref T12616. When you "Delete" a comment from the preview, we try to delete the comment on screen too. It may or may not be present on screen: if you just added it it's usually visible. However, you might also have hidden the file it contains or it could be on an older diff in a file which is no longer present in the current diff. After updates in T12616, we could only find the comment if you'd previously interacted with it for some reason. Update this code to be able to find all inlines present in the document. Test Plan: - Write a draft comment. - Reload the page. - DO NOT INTERACT WITH THE COMMENT! - Delete it from the preview. - After patch: Comment is deleted from the document, too. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616 Differential Revision: https://secure.phabricator.com/D17937 --- .../rsrc/js/application/diff/DiffChangeset.js | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index f6d1200c67..f591eebb67 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -581,11 +581,19 @@ JX.install('DiffChangeset', { }, getInlineByID: function(id) { - // TODO: Currently, this will only find inlines which the user has - // already interacted with! Inlines are built lazily as events arrive. - // This can not yet find inlines which are passively present in the - // document. + // First, look for the inline in the objects we've already built. + var inline = this._findInlineByID(id); + if (inline) { + return inline; + } + // If we haven't found a matching inline yet, rebuild all the inlines + // present in the document, then look again. + this._rebuildAllInlines(); + return this._findInlineByID(id); + }, + + _findInlineByID: function(id) { for (var ii = 0; ii < this._inlines.length; ii++) { var inline = this._inlines[ii]; if (inline.getID() == id) { @@ -594,8 +602,21 @@ JX.install('DiffChangeset', { } return null; - } + }, + _rebuildAllInlines: function() { + var rows = JX.DOM.scry(this._node, 'tr'); + for (var ii = 0; ii < rows.length; ii++) { + var row = rows[ii]; + if (this._getRowType(row) != 'comment') { + continue; + } + + // As a side effect, this builds any missing inline objects and adds + // them to this Changeset's list of inlines. + this.getInlineForRow(row); + } + } }, From 80c329c94294445f2fbf6ac8f18ca4810fa2168d Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 17 May 2017 11:59:00 -0700 Subject: [PATCH 58/81] When replying to a threaded inline, put the new inline in the right place in the thread Summary: Fixes T10563. If you have a thread like this: ``` > A > B > C ``` ...and you reply to "B", we should put the new inline below "B". We currently do when you reload the page, but the initial edit goes at the bottom always (below "C"). Test Plan: {F4963015} Reviewers: chad Reviewed By: chad Maniphest Tasks: T10563 Differential Revision: https://secure.phabricator.com/D17938 --- resources/celerity/map.php | 38 ++++++++-------- .../rsrc/js/application/diff/DiffChangeset.js | 27 ++++++++++-- .../rsrc/js/application/diff/DiffInline.js | 44 ++++++++++++++++++- 3 files changed, 84 insertions(+), 25 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 4e06cdc7f2..10ff2b3e47 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => '0f87a6eb', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => 'ea471cb0', - 'differential.pkg.js' => '4a466790', + 'differential.pkg.js' => '31e1b646', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -390,9 +390,9 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => '68758d99', + 'rsrc/js/application/diff/DiffChangeset.js' => 'ec32b333', 'rsrc/js/application/diff/DiffChangesetList.js' => '796922e0', - 'rsrc/js/application/diff/DiffInline.js' => '1afe9760', + 'rsrc/js/application/diff/DiffInline.js' => '3337c065', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', @@ -777,9 +777,9 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => '68758d99', + 'phabricator-diff-changeset' => 'ec32b333', 'phabricator-diff-changeset-list' => '796922e0', - 'phabricator-diff-inline' => '1afe9760', + 'phabricator-diff-inline' => '3337c065', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -1028,9 +1028,6 @@ return array( 'javelin-util', 'phabricator-keyboard-shortcut-manager', ), - '1afe9760' => array( - 'javelin-dom', - ), '1bd28176' => array( 'javelin-install', 'javelin-dom', @@ -1130,6 +1127,9 @@ return array( 'javelin-dom', 'javelin-workflow', ), + '3337c065' => array( + 'javelin-dom', + ), '3ab51e2c' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -1403,17 +1403,6 @@ return array( 'javelin-dom', 'phabricator-notification', ), - '68758d99' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - 'phabricator-diff-inline', - ), '6882e80a' => array( 'javelin-dom', ), @@ -2142,6 +2131,17 @@ return array( 'javelin-dom', 'phabricator-draggable-list', ), + 'ec32b333' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + 'phabricator-diff-inline', + ), 'eded9ee8' => array( 'javelin-behavior', 'javelin-typeahead-ondemand-source', diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index f591eebb67..aca035ea0c 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -581,8 +581,16 @@ JX.install('DiffChangeset', { }, getInlineByID: function(id) { + return this._queryInline('id', id); + }, + + getInlineByPHID: function(phid) { + return this._queryInline('phid', phid); + }, + + _queryInline: function(field, value) { // First, look for the inline in the objects we've already built. - var inline = this._findInlineByID(id); + var inline = this._findInline(field, value); if (inline) { return inline; } @@ -590,13 +598,24 @@ JX.install('DiffChangeset', { // If we haven't found a matching inline yet, rebuild all the inlines // present in the document, then look again. this._rebuildAllInlines(); - return this._findInlineByID(id); + return this._findInline(field, value); }, - _findInlineByID: function(id) { + _findInline: function(field, value) { for (var ii = 0; ii < this._inlines.length; ii++) { var inline = this._inlines[ii]; - if (inline.getID() == id) { + + var target; + switch (field) { + case 'id': + target = inline.getID(); + break; + case 'phid': + target = inline.getPHID(); + break; + } + + if (target == value) { return inline; } } diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js index edd31942f9..b46bc00b4f 100644 --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -63,6 +63,8 @@ JX.install('DiffInline', { (this.getDisplaySide() == 'right') || (data.left != data.right); + this._replyToCommentPHID = data.replyToCommentPHID; + this.setInvisible(false); return this; @@ -100,11 +102,45 @@ JX.install('DiffInline', { this._replyToCommentPHID = inline._phid; - // TODO: This should insert correctly into the thread, not just at the - // bottom. + var changeset = this.getChangeset(); + + // We're going to figure out where in the document to position the new + // inline. Normally, it goes after any existing inline rows (so if + // several inlines reply to the same line, they appear in chronological + // order). + + // However: if inlines are threaded, we want to put the new inline in + // the right place in the thread. This might be somewhere in the middle, + // so we need to do a bit more work to figure it out. + + // To find the right place in the thread, we're going to look for any + // inline which is at or above the level of the comment we're replying + // to. This means we've reached a new fork of the thread, and should + // put our new inline before the comment we found. + var ancestor_map = {}; + var ancestor = inline; + var reply_phid; + while (ancestor) { + reply_phid = ancestor.getReplyToCommentPHID(); + if (!reply_phid) { + break; + } + ancestor_map[reply_phid] = true; + ancestor = changeset.getInlineByPHID(reply_phid); + } + var parent_row = inline._row; var target_row = parent_row.nextSibling; while (target_row && JX.Stratcom.hasSigil(target_row, 'inline-row')) { + var target = changeset.getInlineForRow(target_row); + reply_phid = target.getReplyToCommentPHID(); + + // If we found an inline which is replying directly to some ancestor + // of this new comment, this is where the new rows go. + if (ancestor_map.hasOwnProperty(reply_phid)) { + break; + } + target_row = target_row.nextSibling; } @@ -318,6 +354,10 @@ JX.install('DiffInline', { return this._id; }, + getPHID: function() { + return this._phid; + }, + getChangesetID: function() { return this._changesetID; }, From 0e8f72a0d992ee5b27f807b65e4ce83ef2ec5078 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Wed, 17 May 2017 14:48:15 -0700 Subject: [PATCH 59/81] Clarify Subscription page language Summary: This says "Edit Subscription" but the page only lets you update the payment method for autopay. I think this is confusing users. I tried to find the best language here, but suggest something else if you prefer. Test Plan: Review language on sample subscription page. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T12451 Differential Revision: https://secure.phabricator.com/D17944 --- .../subscription/PhortuneSubscriptionViewController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/phortune/controller/subscription/PhortuneSubscriptionViewController.php b/src/applications/phortune/controller/subscription/PhortuneSubscriptionViewController.php index daf3daa11d..2e78d37d5c 100644 --- a/src/applications/phortune/controller/subscription/PhortuneSubscriptionViewController.php +++ b/src/applications/phortune/controller/subscription/PhortuneSubscriptionViewController.php @@ -43,8 +43,8 @@ final class PhortuneSubscriptionViewController extends PhortuneController { $curtain->addAction( id(new PhabricatorActionView()) - ->setIcon('fa-pencil') - ->setName(pht('Edit Subscription')) + ->setIcon('fa-credit-card') + ->setName(pht('Manage Autopay')) ->setHref($edit_uri) ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); From d12be0fc2190c9442b9eec388b915cb4e37ed19e Mon Sep 17 00:00:00 2001 From: Austin McKinley Date: Wed, 17 May 2017 16:11:04 -0700 Subject: [PATCH 60/81] Change Pholio editor access modifier Summary: Used by `PholioImageFileTransaction::mergeTransactions()`. I forgot to test adding multiple images to a Mock at the same time after migrating `mergeTransactions` over to the modular framework. Test Plan: Added multiple images in a single transaction and didn't get an exception about accessing a protected function. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: epriestley Differential Revision: https://secure.phabricator.com/D17946 --- .../editor/PhabricatorApplicationTransactionEditor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index b5aa399521..3f639d8aba 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -1743,7 +1743,7 @@ abstract class PhabricatorApplicationTransactionEditor return array_values($result); } - protected function mergePHIDOrEdgeTransactions( + public function mergePHIDOrEdgeTransactions( PhabricatorApplicationTransaction $u, PhabricatorApplicationTransaction $v) { From 1e3c8df1c89db591891ecfba9945df6894fb98ca Mon Sep 17 00:00:00 2001 From: Austin McKinley Date: Tue, 16 May 2017 14:40:15 -0700 Subject: [PATCH 61/81] Migrate Project names to modular transactions Summary: Also changes access modifiers on `PhabricatorProjectTransactionEditor` and sets up `storage` for `applyExternalEffects`. Test Plan: Created new projects, attempted to create without name, with too long of a name, and with a name that conflicts with other projects and observed expected errors. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: epriestley Maniphest Tasks: T12673 Differential Revision: https://secure.phabricator.com/D17947 --- .../sql/patches/20131020.pxactionmig.php | 2 +- src/__phutil_library_map__.php | 6 +- .../PhabricatorProjectCoreTestCase.php | 13 +- .../conduit/ProjectCreateConduitAPIMethod.php | 2 +- .../PhabricatorProjectTransactionEditor.php | 80 +------------ .../engine/PhabricatorProjectEditEngine.php | 2 +- .../PhabricatorProjectTestDataGenerator.php | 2 +- .../storage/PhabricatorProjectTransaction.php | 35 +----- .../PhabricatorProjectNameTransaction.php | 112 ++++++++++++++++++ .../PhabricatorProjectTransactionType.php | 4 + ...habricatorApplicationTransactionEditor.php | 2 + 11 files changed, 142 insertions(+), 118 deletions(-) create mode 100644 src/applications/project/xaction/PhabricatorProjectNameTransaction.php create mode 100644 src/applications/project/xaction/PhabricatorProjectTransactionType.php diff --git a/resources/sql/patches/20131020.pxactionmig.php b/resources/sql/patches/20131020.pxactionmig.php index 3d593d6e34..a6d305b77a 100644 --- a/resources/sql/patches/20131020.pxactionmig.php +++ b/resources/sql/patches/20131020.pxactionmig.php @@ -32,7 +32,7 @@ foreach ($rows as $row) { $project_phid = $project_row['phid']; $type_map = array( - 'name' => PhabricatorProjectTransaction::TYPE_NAME, + 'name' => PhabricatorProjectNameTransaction::TRANSACTIONTYPE, 'members' => PhabricatorProjectTransaction::TYPE_MEMBERS, 'status' => PhabricatorProjectTransaction::TYPE_STATUS, 'canview' => PhabricatorTransactions::TYPE_VIEW_POLICY, diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index fbc972328c..cc4794d251 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3644,6 +3644,7 @@ phutil_register_library_map(array( 'PhabricatorProjectMenuItemController' => 'applications/project/controller/PhabricatorProjectMenuItemController.php', 'PhabricatorProjectMoveController' => 'applications/project/controller/PhabricatorProjectMoveController.php', 'PhabricatorProjectNameContextFreeGrammar' => 'applications/project/lipsum/PhabricatorProjectNameContextFreeGrammar.php', + 'PhabricatorProjectNameTransaction' => 'applications/project/xaction/PhabricatorProjectNameTransaction.php', 'PhabricatorProjectNoProjectsDatasource' => 'applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php', 'PhabricatorProjectObjectHasProjectEdgeType' => 'applications/project/edge/PhabricatorProjectObjectHasProjectEdgeType.php', 'PhabricatorProjectOrUserDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserDatasource.php', @@ -3674,6 +3675,7 @@ phutil_register_library_map(array( 'PhabricatorProjectTransaction' => 'applications/project/storage/PhabricatorProjectTransaction.php', 'PhabricatorProjectTransactionEditor' => 'applications/project/editor/PhabricatorProjectTransactionEditor.php', 'PhabricatorProjectTransactionQuery' => 'applications/project/query/PhabricatorProjectTransactionQuery.php', + 'PhabricatorProjectTransactionType' => 'applications/project/xaction/PhabricatorProjectTransactionType.php', 'PhabricatorProjectUIEventListener' => 'applications/project/events/PhabricatorProjectUIEventListener.php', 'PhabricatorProjectUpdateController' => 'applications/project/controller/PhabricatorProjectUpdateController.php', 'PhabricatorProjectUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php', @@ -9053,6 +9055,7 @@ phutil_register_library_map(array( 'PhabricatorProjectMenuItemController' => 'PhabricatorProjectController', 'PhabricatorProjectMoveController' => 'PhabricatorProjectController', 'PhabricatorProjectNameContextFreeGrammar' => 'PhutilContextFreeGrammar', + 'PhabricatorProjectNameTransaction' => 'PhabricatorProjectTransactionType', 'PhabricatorProjectNoProjectsDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorProjectObjectHasProjectEdgeType' => 'PhabricatorEdgeType', 'PhabricatorProjectOrUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource', @@ -9083,9 +9086,10 @@ phutil_register_library_map(array( 'PhabricatorProjectSubprojectsController' => 'PhabricatorProjectController', 'PhabricatorProjectSubprojectsProfileMenuItem' => 'PhabricatorProfileMenuItem', 'PhabricatorProjectTestDataGenerator' => 'PhabricatorTestDataGenerator', - 'PhabricatorProjectTransaction' => 'PhabricatorApplicationTransaction', + 'PhabricatorProjectTransaction' => 'PhabricatorModularTransaction', 'PhabricatorProjectTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorProjectTransactionQuery' => 'PhabricatorApplicationTransactionQuery', + 'PhabricatorProjectTransactionType' => 'PhabricatorModularTransactionType', 'PhabricatorProjectUIEventListener' => 'PhabricatorEventListener', 'PhabricatorProjectUpdateController' => 'PhabricatorProjectController', 'PhabricatorProjectUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', diff --git a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php index 3943a7c0c3..0f97d88f1e 100644 --- a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php +++ b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php @@ -356,7 +356,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME) + ->setTransactionType(PhabricatorProjectNameTransaction::TRANSACTIONTYPE) ->setNewValue($name); $this->applyTransactions($project, $user, $xactions); @@ -382,7 +382,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME) + ->setTransactionType(PhabricatorProjectNameTransaction::TRANSACTIONTYPE) ->setNewValue($name2); $xactions[] = id(new PhabricatorProjectTransaction()) @@ -503,7 +503,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME) + ->setTransactionType(PhabricatorProjectNameTransaction::TRANSACTIONTYPE) ->setNewValue($name); $xactions[] = id(new PhabricatorProjectTransaction()) @@ -601,7 +601,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME) + ->setTransactionType(PhabricatorProjectNameTransaction::TRANSACTIONTYPE) ->setNewValue($name); $xactions[] = id(new PhabricatorProjectTransaction()) @@ -1290,7 +1290,8 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $new_name = $proj->getName().' '.mt_rand(); $xaction = new PhabricatorProjectTransaction(); - $xaction->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME); + $xaction->setTransactionType( + PhabricatorProjectNameTransaction::TRANSACTIONTYPE); $xaction->setNewValue($new_name); $this->applyTransactions($proj, $user, array($xaction)); @@ -1440,7 +1441,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME) + ->setTransactionType(PhabricatorProjectNameTransaction::TRANSACTIONTYPE) ->setNewValue($name); if ($parent) { diff --git a/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php b/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php index 66075e7248..edd2606a77 100644 --- a/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php +++ b/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php @@ -42,7 +42,7 @@ final class ProjectCreateConduitAPIMethod extends ProjectConduitAPIMethod { $user); $project = PhabricatorProject::initializeNewProject($user); - $type_name = PhabricatorProjectTransaction::TYPE_NAME; + $type_name = PhabricatorProjectNameTransaction::TRANSACTIONTYPE; $members = $request->getValue('members'); $xactions = array(); diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php index aa8c0852ab..7a38c3bbdf 100644 --- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php @@ -10,7 +10,7 @@ final class PhabricatorProjectTransactionEditor return $this; } - private function getIsMilestone() { + public function getIsMilestone() { return $this->isMilestone; } @@ -30,7 +30,6 @@ final class PhabricatorProjectTransactionEditor $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; $types[] = PhabricatorTransactions::TYPE_JOIN_POLICY; - $types[] = PhabricatorProjectTransaction::TYPE_NAME; $types[] = PhabricatorProjectTransaction::TYPE_SLUGS; $types[] = PhabricatorProjectTransaction::TYPE_STATUS; $types[] = PhabricatorProjectTransaction::TYPE_IMAGE; @@ -52,8 +51,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_NAME: - return $object->getName(); case PhabricatorProjectTransaction::TYPE_SLUGS: $slugs = $object->getSlugs(); $slugs = mpull($slugs, 'getSlug', 'getSlug'); @@ -90,7 +87,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_NAME: case PhabricatorProjectTransaction::TYPE_STATUS: case PhabricatorProjectTransaction::TYPE_IMAGE: case PhabricatorProjectTransaction::TYPE_ICON: @@ -121,13 +117,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_NAME: - $name = $xaction->getNewValue(); - $object->setName($name); - if (!$this->getIsMilestone()) { - $object->setPrimarySlug(PhabricatorSlug::normalizeProjectSlug($name)); - } - return; case PhabricatorProjectTransaction::TYPE_SLUGS: return; case PhabricatorProjectTransaction::TYPE_STATUS: @@ -178,16 +167,6 @@ final class PhabricatorProjectTransactionEditor $new = $xaction->getNewValue(); switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_NAME: - // First, add the old name as a secondary slug; this is helpful - // for renames and generally a good thing to do. - if (!$this->getIsMilestone()) { - if ($old !== null) { - $this->addSlug($object, $old, false); - } - $this->addSlug($object, $new, false); - } - return; case PhabricatorProjectTransaction::TYPE_SLUGS: $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); @@ -299,59 +278,6 @@ final class PhabricatorProjectTransactionEditor $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { - case PhabricatorProjectTransaction::TYPE_NAME: - $missing = $this->validateIsEmptyTextField( - $object->getName(), - $xactions); - - if ($missing) { - $error = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Required'), - pht('Project name is required.'), - nonempty(last($xactions), null)); - - $error->setIsMissingFieldError(true); - $errors[] = $error; - } - - if (!$xactions) { - break; - } - - if ($this->getIsMilestone()) { - break; - } - - $name = last($xactions)->getNewValue(); - - if (!PhabricatorSlug::isValidProjectSlug($name)) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'Project names must contain at least one letter or number.'), - last($xactions)); - break; - } - - $slug = PhabricatorSlug::normalizeProjectSlug($name); - - $slug_used_already = id(new PhabricatorProjectSlug()) - ->loadOneWhere('slug = %s', $slug); - if ($slug_used_already && - $slug_used_already->getProjectPHID() != $object->getPHID()) { - $error = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Duplicate'), - pht( - 'Project name generates the same hashtag ("%s") as another '. - 'existing project. Choose a unique name.', - '#'.$slug), - nonempty(last($xactions), null)); - $errors[] = $error; - } - break; case PhabricatorProjectTransaction::TYPE_SLUGS: if (!$xactions) { break; @@ -497,7 +423,7 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_NAME: + case PhabricatorProjectNameTransaction::TRANSACTIONTYPE: case PhabricatorProjectTransaction::TYPE_STATUS: case PhabricatorProjectTransaction::TYPE_IMAGE: case PhabricatorProjectTransaction::TYPE_ICON: @@ -717,7 +643,7 @@ final class PhabricatorProjectTransactionEditor return parent::applyFinalEffects($object, $xactions); } - private function addSlug(PhabricatorProject $project, $slug, $force) { + public function addSlug(PhabricatorProject $project, $slug, $force) { $slug = PhabricatorSlug::normalizeProjectSlug($slug); $table = new PhabricatorProjectSlug(); $project_phid = $project->getPHID(); diff --git a/src/applications/project/engine/PhabricatorProjectEditEngine.php b/src/applications/project/engine/PhabricatorProjectEditEngine.php index 286e9bab0f..8f855074d6 100644 --- a/src/applications/project/engine/PhabricatorProjectEditEngine.php +++ b/src/applications/project/engine/PhabricatorProjectEditEngine.php @@ -235,7 +235,7 @@ final class PhabricatorProjectEditEngine id(new PhabricatorTextEditField()) ->setKey('name') ->setLabel(pht('Name')) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME) + ->setTransactionType(PhabricatorProjectNameTransaction::TRANSACTIONTYPE) ->setIsRequired(true) ->setDescription(pht('Project name.')) ->setConduitDescription(pht('Rename the project')) diff --git a/src/applications/project/lipsum/PhabricatorProjectTestDataGenerator.php b/src/applications/project/lipsum/PhabricatorProjectTestDataGenerator.php index 0fd5bd66e7..8032749f94 100644 --- a/src/applications/project/lipsum/PhabricatorProjectTestDataGenerator.php +++ b/src/applications/project/lipsum/PhabricatorProjectTestDataGenerator.php @@ -16,7 +16,7 @@ final class PhabricatorProjectTestDataGenerator $xactions = array(); $xactions[] = $this->newTransaction( - PhabricatorProjectTransaction::TYPE_NAME, + PhabricatorProjectNameTransaction::TRANSACTIONTYPE, $this->newProjectTitle()); $xactions[] = $this->newTransaction( diff --git a/src/applications/project/storage/PhabricatorProjectTransaction.php b/src/applications/project/storage/PhabricatorProjectTransaction.php index fb9a25eda6..0ad6230a5e 100644 --- a/src/applications/project/storage/PhabricatorProjectTransaction.php +++ b/src/applications/project/storage/PhabricatorProjectTransaction.php @@ -1,9 +1,8 @@ getOldValue(); $new = $this->getNewValue(); @@ -149,20 +152,6 @@ final class PhabricatorProjectTransaction '%s created this project.', $this->renderHandleLink($author_phid)); - case self::TYPE_NAME: - if ($old === null) { - return pht( - '%s created this project.', - $author_handle); - } else { - return pht( - '%s renamed this project from "%s" to "%s".', - $author_handle, - $old, - $new); - } - break; - case self::TYPE_STATUS: if ($old == 0) { return pht( @@ -329,20 +318,6 @@ final class PhabricatorProjectTransaction $new = $this->getNewValue(); switch ($this->getTransactionType()) { - case self::TYPE_NAME: - if ($old === null) { - return pht( - '%s created %s.', - $author_handle, - $object_handle); - } else { - return pht( - '%s renamed %s from "%s" to "%s".', - $author_handle, - $object_handle, - $old, - $new); - } case self::TYPE_STATUS: if ($old == 0) { return pht( diff --git a/src/applications/project/xaction/PhabricatorProjectNameTransaction.php b/src/applications/project/xaction/PhabricatorProjectNameTransaction.php new file mode 100644 index 0000000000..512d899f0b --- /dev/null +++ b/src/applications/project/xaction/PhabricatorProjectNameTransaction.php @@ -0,0 +1,112 @@ +getName(); + } + + public function applyInternalEffects($object, $value) { + $object->setName($value); + if (!$this->getEditor()->getIsMilestone()) { + $object->setPrimarySlug(PhabricatorSlug::normalizeProjectSlug($value)); + } + } + + public function applyExternalEffects($object, $value) { + $old = $this->getOldValue(); + + // First, add the old name as a secondary slug; this is helpful + // for renames and generally a good thing to do. + if (!$this->getEditor()->getIsMilestone()) { + if ($old !== null) { + $this->getEditor()->addSlug($object, $old, false); + } + $this->getEditor()->addSlug($object, $value, false); + } + return; + } + + public function getTitle() { + $old = $this->getOldValue(); + if ($old === null) { + return pht( + '%s created this project.', + $this->renderAuthor()); + } else { + return pht( + '%s renamed this project from %s to %s.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } + } + + public function getTitleForFeed() { + $old = $this->getOldValue(); + if ($old === null) { + return pht( + '%s created %s.', + $this->renderAuthor(), + $this->renderObject()); + } else { + return pht( + '%s renamed %s from %s to %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderOldValue(), + $this->renderNewValue()); + } + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + if ($this->isEmptyTextTransaction($object->getName(), $xactions)) { + $errors[] = $this->newRequiredError(pht('Projects must have a name.')); + } + + $max_length = $object->getColumnMaximumByteLength('name'); + foreach ($xactions as $xaction) { + $new_value = $xaction->getNewValue(); + $new_length = strlen($new_value); + if ($new_length > $max_length) { + $errors[] = $this->newInvalidError( + pht( + 'Project names must not be longer than %s character(s).', + new PhutilNumber($max_length))); + } + } + + if ($this->getEditor()->getIsMilestone() || !$xactions) { + return $errors; + } + + $name = last($xactions)->getNewValue(); + + if (!PhabricatorSlug::isValidProjectSlug($name)) { + $errors[] = $this->newInvalidError( + pht('Project names must contain at least one letter or number.')); + } + + $slug = PhabricatorSlug::normalizeProjectSlug($name); + + $slug_used_already = id(new PhabricatorProjectSlug()) + ->loadOneWhere('slug = %s', $slug); + if ($slug_used_already && + $slug_used_already->getProjectPHID() != $object->getPHID()) { + + $errors[] = $this->newInvalidError( + pht( + 'Project name generates the same hashtag ("%s") as another '. + 'existing project. Choose a unique name.', + '#'.$slug)); + } + + return $errors; + } + +} diff --git a/src/applications/project/xaction/PhabricatorProjectTransactionType.php b/src/applications/project/xaction/PhabricatorProjectTransactionType.php new file mode 100644 index 0000000000..9ce02dc450 --- /dev/null +++ b/src/applications/project/xaction/PhabricatorProjectTransactionType.php @@ -0,0 +1,4 @@ +getModularTransactionType($type); if ($xtype) { + $xtype = clone $xtype; + $xtype->setStorage($xaction); return $xtype->applyExternalEffects($object, $xaction->getNewValue()); } From f78ce156f1144fccd356803eb2dcb88ab200585c Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 17 May 2017 12:51:29 -0700 Subject: [PATCH 62/81] Restore "h" to hide or show files, and modernize file visibility toggling Summary: Ref T12616. This puts "h" back to collapse or expand the current file. This removes some very complicated/messy code around following links in the table of contents and getting files auto-expanded. I suspect no one will miss this, but we can restore it if ayone notices. Test Plan: Pressed "h" to collapse/expand a file. Also used the menu items. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12616 Differential Revision: https://secure.phabricator.com/D17940 --- resources/celerity/map.php | 59 ++++---- resources/celerity/packages.php | 1 - .../view/DifferentialChangesetListView.php | 16 ++- .../differential/changeset-view.css | 1 + .../rsrc/js/application/diff/DiffChangeset.js | 61 ++++++++- .../js/application/diff/DiffChangesetList.js | 25 +++- .../differential/behavior-toggle-files.js | 128 ------------------ 7 files changed, 114 insertions(+), 177 deletions(-) delete mode 100644 webroot/rsrc/js/application/differential/behavior-toggle-files.js diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 10ff2b3e47..2f9026785d 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -12,8 +12,8 @@ return array( 'core.pkg.css' => 'a5a2d647', 'core.pkg.js' => '0f87a6eb', 'darkconsole.pkg.js' => '1f9a31bc', - 'differential.pkg.css' => 'ea471cb0', - 'differential.pkg.js' => '31e1b646', + 'differential.pkg.css' => '697405d4', + 'differential.pkg.js' => '07c56ffc', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -64,7 +64,7 @@ return array( 'rsrc/css/application/dashboard/dashboard.css' => 'fe5b1869', 'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a', 'rsrc/css/application/differential/add-comment.css' => 'c47f8c40', - 'rsrc/css/application/differential/changeset-view.css' => '6a77323e', + 'rsrc/css/application/differential/changeset-view.css' => '15be1064', 'rsrc/css/application/differential/core.css' => '5b7b8ff4', 'rsrc/css/application/differential/phui-inline-comment.css' => 'ffd1a542', 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', @@ -390,14 +390,13 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => 'ec32b333', - 'rsrc/js/application/diff/DiffChangesetList.js' => '796922e0', + 'rsrc/js/application/diff/DiffChangeset.js' => '731125f3', + 'rsrc/js/application/diff/DiffChangesetList.js' => '59d1ceb1', 'rsrc/js/application/diff/DiffInline.js' => '3337c065', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', 'rsrc/js/application/differential/behavior-populate.js' => '5e41c819', - 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', 'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => 'c93358e3', 'rsrc/js/application/diffusion/behavior-audit-preview.js' => 'd835b03a', @@ -566,7 +565,7 @@ return array( 'conpherence-thread-manager' => '4d863052', 'conpherence-transaction-css' => '85129c68', 'd3' => 'a11a5ff2', - 'differential-changeset-view-css' => '6a77323e', + 'differential-changeset-view-css' => '15be1064', 'differential-core-view-css' => '5b7b8ff4', 'differential-revision-add-comment-css' => 'c47f8c40', 'differential-revision-comment-css' => '14b8565a', @@ -620,7 +619,6 @@ return array( 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-populate' => '5e41c819', - 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', 'javelin-behavior-differential-user-select' => 'a8d8459d', 'javelin-behavior-diffusion-browse-file' => '054a0f0b', 'javelin-behavior-diffusion-commit-branches' => 'bdaf4d04', @@ -777,8 +775,8 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => 'ec32b333', - 'phabricator-diff-changeset-list' => '796922e0', + 'phabricator-diff-changeset' => '731125f3', + 'phabricator-diff-changeset-list' => '59d1ceb1', 'phabricator-diff-inline' => '3337c065', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', @@ -1001,6 +999,9 @@ return array( 'javelin-dom', 'javelin-history', ), + '15be1064' => array( + 'phui-inline-comment-view-css', + ), '17bb8539' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1348,6 +1349,9 @@ return array( 'javelin-vector', 'javelin-dom', ), + '59d1ceb1' => array( + 'javelin-install', + ), '5c54cbf3' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1409,9 +1413,6 @@ return array( '69adf288' => array( 'javelin-install', ), - '6a77323e' => array( - 'phui-inline-comment-view-css', - ), '6ad39b6f' => array( 'javelin-install', 'javelin-event', @@ -1449,6 +1450,17 @@ return array( 'javelin-workflow', 'phabricator-draggable-list', ), + '731125f3' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + 'phabricator-diff-inline', + ), '7319e029' => array( 'javelin-behavior', 'javelin-dom', @@ -1482,9 +1494,6 @@ return array( 'javelin-behavior', 'javelin-quicksand', ), - '796922e0' => array( - 'javelin-install', - ), '7a68dda3' => array( 'owners-path-editor', 'javelin-behavior', @@ -1960,12 +1969,6 @@ return array( 'phabricator-shaped-request', 'conpherence-thread-manager', ), - 'ca3f91eb' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - 'phabricator-phtize', - ), 'caade6f2' => array( 'javelin-behavior', 'javelin-request', @@ -2131,17 +2134,6 @@ return array( 'javelin-dom', 'phabricator-draggable-list', ), - 'ec32b333' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - 'phabricator-diff-inline', - ), 'eded9ee8' => array( 'javelin-behavior', 'javelin-typeahead-ondemand-source', @@ -2418,7 +2410,6 @@ return array( 'javelin-behavior-phabricator-object-selector', 'javelin-behavior-repository-crossreference', 'javelin-behavior-load-blame', - 'javelin-behavior-differential-toggle-files', 'javelin-behavior-differential-user-select', 'javelin-behavior-aphront-more', 'phabricator-diff-inline', diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php index 619a5d6389..e756e696cb 100644 --- a/resources/celerity/packages.php +++ b/resources/celerity/packages.php @@ -200,7 +200,6 @@ return array( 'javelin-behavior-repository-crossreference', 'javelin-behavior-load-blame', - 'javelin-behavior-differential-toggle-files', 'javelin-behavior-differential-user-select', 'javelin-behavior-aphront-more', diff --git a/src/applications/differential/view/DifferentialChangesetListView.php b/src/applications/differential/view/DifferentialChangesetListView.php index aa34b0415f..4ac2612ecd 100644 --- a/src/applications/differential/view/DifferentialChangesetListView.php +++ b/src/applications/differential/view/DifferentialChangesetListView.php @@ -131,13 +131,6 @@ final class DifferentialChangesetListView extends AphrontView { $changesets = $this->changesets; - Javelin::initBehavior('differential-toggle-files', array( - 'pht' => array( - 'undo' => pht('Undo'), - 'collapsed' => pht('This file content has been collapsed.'), - ), - )); - $renderer = DifferentialChangesetParser::getDefaultRendererForViewer( $viewer); @@ -271,6 +264,15 @@ final class DifferentialChangesetListView extends AphrontView { pht('Jump to next inline comment, including hidden comments.'), 'Jump to previous inline comment, including hidden comments.' => pht('Jump to previous inline comment, including hidden comments.'), + + 'This file content has been collapsed.' => + pht('This file content has been collapsed.'), + 'Show Content' => pht('Show Content'), + + 'Hide or show the current file.' => + pht('Hide or show the current file.'), + 'You must select a file to hide or show.' => + pht('You must select a file to hide or show.'), ), )); diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index 92a544c24c..0cec38ffdc 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -325,6 +325,7 @@ td.cov-I { border: 1px solid {$blue}; text-align: center; background-color: {$lightblue}; + margin: 8px; } .differential-collapse-undo a { diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index aca035ea0c..c3359c2007 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -55,6 +55,9 @@ JX.install('DiffChangeset', { _rightID: null, _inlines: null, + _visible: true, + + _undoNode: null, getLeftChangesetID: function() { return this._leftID; @@ -345,6 +348,10 @@ JX.install('DiffChangeset', { } }); + if (!this._visible) { + return items; + } + var rows = JX.DOM.scry(this._node, 'tr'); var blocks = []; @@ -635,8 +642,60 @@ JX.install('DiffChangeset', { // them to this Changeset's list of inlines. this.getInlineForRow(row); } - } + }, + toggleVisibility: function() { + this._visible = !this._visible; + + var diff = JX.DOM.find(this._node, 'table', 'differential-diff'); + var undo = this._getUndoNode(); + + if (this._visible) { + JX.DOM.show(diff); + JX.DOM.remove(undo); + } else { + JX.DOM.hide(diff); + JX.DOM.appendContent(diff.parentNode, undo); + } + + JX.Stratcom.invoke('resize'); + }, + + _getUndoNode: function() { + if (!this._undoNode) { + var pht = this.getChangesetList().getTranslations(); + + var link_attributes = { + href: '#' + }; + + var undo_link = JX.$N('a', link_attributes, pht('Show Content')); + + var onundo = JX.bind(this, this._onundo); + JX.DOM.listen(undo_link, 'click', null, onundo); + + var node_attributes = { + className: 'differential-collapse-undo' + }; + + var node_content = [ + pht('This file content has been collapsed.'), + ' ', + undo_link + ]; + + var undo_node = JX.$N('div', node_attributes, node_content); + + this._undoNode = undo_node; + } + + return this._undoNode; + }, + + _onundo: function(e) { + e.kill(); + this.toggleVisibility(); + } }, statics: { diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index 6e3853a9d1..ff51cd4666 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -161,6 +161,9 @@ JX.install('DiffChangesetList', { 'Jump to previous inline comment, including hidden comments.'); this._installJumpKey('P', label, -1, 'comment', true); + label = pht('Hide or show the current file.'); + this._installKey('h', label, this._onkeytogglefile); + label = pht('Jump to the table of contents.'); this._installKey('t', label, this._ontoc); @@ -385,6 +388,20 @@ JX.install('DiffChangesetList', { this._warnUser(pht('You must select a comment to mark done.')); }, + _onkeytogglefile: function() { + var cursor = this._cursorItem; + + if (cursor) { + if (cursor.type == 'file') { + cursor.changeset.toggleVisibility(); + return; + } + } + + var pht = this.getTranslations(); + this._warnUser(pht('You must select a file to hide or show.')); + }, + _onkeyhide: function() { var cursor = this._cursorItem; @@ -616,14 +633,10 @@ JX.install('DiffChangesetList', { var visible_item = new JX.PHUIXActionView() .setHandler(function(e) { - var diff = JX.DOM.scry( - JX.$(data.containerID), - 'table', - 'differential-diff'); - - JX.Stratcom.invoke('differential-toggle-file', null, {diff: diff}); e.prevent(); menu.close(); + + changeset.toggleVisibility(); }); list.addItem(visible_item); diff --git a/webroot/rsrc/js/application/differential/behavior-toggle-files.js b/webroot/rsrc/js/application/differential/behavior-toggle-files.js deleted file mode 100644 index 296b6f91b2..0000000000 --- a/webroot/rsrc/js/application/differential/behavior-toggle-files.js +++ /dev/null @@ -1,128 +0,0 @@ -/** - * @provides javelin-behavior-differential-toggle-files - * @requires javelin-behavior - * javelin-dom - * javelin-stratcom - * phabricator-phtize - */ - -JX.behavior('differential-toggle-files', function(config) { - var pht = JX.phtize(config.pht); - - JX.Stratcom.listen( - 'differential-toggle-file', - null, - function(e) { - if (e.getData().diff.length != 1) { - return; - } - - var diff = e.getData().diff[0], - data = JX.Stratcom.getData(diff); - if (data.hidden) { - data.hidden = false; - JX.DOM.show(diff); - JX.DOM.remove(data.undo); - data.undo = null; - } else { - data.hidden = true; - data.undo = render_collapse_undo(); - JX.DOM.hide(diff); - JX.DOM.listen( - data.undo, - 'click', - 'differential-collapse-undo', - function(e) { - e.kill(); - data.hidden = false; - JX.DOM.show(diff); - JX.DOM.remove(data.undo); - data.undo = null; - }); - JX.DOM.appendContent(diff.parentNode, data.undo); - } - JX.Stratcom.invoke('differential-toggle-file-toggled'); - }); - - JX.Stratcom.listen( - 'differential-toggle-file-request', - null, - function(e) { - var elt = e.getData().element; - while (elt !== document.body) { - if (JX.Stratcom.hasSigil(elt, 'differential-changeset')) { - var diffs = JX.DOM.scry(elt, 'table', 'differential-diff'); - var invoked = false; - for (var i = 0; i < diffs.length; ++i) { - if (JX.Stratcom.getData(diffs[i]).hidden) { - JX.Stratcom.invoke('differential-toggle-file', null, { - diff: [ diffs[i] ] - }); - invoked = true; - } - } - if (!invoked) { - e.prevent(); - } - return; - } - elt = elt.parentNode; - } - e.prevent(); - }); - - JX.Stratcom.listen( - 'click', - 'tag:a', - function(e) { - var link = e.getNode('tag:a'); - var id = link.getAttribute('href'); - if (!id || !id.match(/^#.+/)) { - return; - } - var raw = e.getRawEvent(); - if (raw.altKey || raw.ctrlKey || raw.metaKey || raw.shiftKey) { - return; - } - // The target may have either a matching name or a matching id. - var target; - try { - target = JX.$(id.substr(1)); - } catch(err) { - var named = document.getElementsByName(id.substr(1)); - for (var i = 0; i < named.length; ++i) { - if (named[i].tagName.toLowerCase() == 'a') { - if (target) { - return; - } - target = named[i]; - } - } - if (!target) { - return; - } - } - var event = JX.Stratcom.invoke('differential-toggle-file-request', null, { - element: target - }); - if (!event.getPrevented()) { - // This event is processed after the hash has changed, so it doesn't - // automatically jump there like we want. - JX.DOM.scrollTo(target); - } - }); - - var render_collapse_undo = function() { - var link = JX.$N( - 'a', - {href: '#', sigil: 'differential-collapse-undo'}, - pht('undo')); - - return JX.$N( - 'div', - {className: 'differential-collapse-undo', - sigil: 'differential-collapse-undo-div'}, - [pht('collapsed'), ' ', link]); - }; - -}); From fb9f3cc0b4cae3decd115da2a773978d43b26e4c Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 17 May 2017 14:49:26 -0700 Subject: [PATCH 63/81] Restore the "buoyant" header in Differential Summary: Fixes T1591. This was removed long ago because it was a mess to implement and caused a bunch of weird issues, and also my tolerance for dealing with weird JS issues was much, much lower. I have now survived the fires of JX.Scrollbar and would love to address 200 small nitpicks about obscure browser behaviors on Linux, so open the floodgates again. A secondary goal here is to create room to add a global view state menu on the right, with 300 options like "hide all inlines", "hide done inlines", "hide collapsed inlines", "hide ghosts", "show ghosts", "enable filetree", "disable filetree", etc, etc. Not sure how much of this I'll actually do. I have one more experiment I want to try first. Test Plan: {F4963294} Reviewers: chad Reviewed By: chad Maniphest Tasks: T1591 Differential Revision: https://secure.phabricator.com/D17945 --- resources/celerity/map.php | 82 +++++++++---------- .../view/DifferentialChangesetDetailView.php | 1 + .../differential/changeset-view.css | 18 ++++ webroot/rsrc/css/core/z-index.css | 4 + .../rsrc/js/application/diff/DiffChangeset.js | 14 ++++ .../js/application/diff/DiffChangesetList.js | 82 +++++++++++++++++++ .../rsrc/js/core/behavior-phabricator-nav.js | 11 +++ 7 files changed, 171 insertions(+), 41 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 2f9026785d..d175534828 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,11 +9,11 @@ return array( 'names' => array( 'conpherence.pkg.css' => 'ff161f2d', 'conpherence.pkg.js' => 'b5b51108', - 'core.pkg.css' => 'a5a2d647', - 'core.pkg.js' => '0f87a6eb', + 'core.pkg.css' => '4937a7d7', + 'core.pkg.js' => 'a0c8fb20', 'darkconsole.pkg.js' => '1f9a31bc', - 'differential.pkg.css' => '697405d4', - 'differential.pkg.js' => '07c56ffc', + 'differential.pkg.css' => '52b014e7', + 'differential.pkg.js' => '1efe85bf', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -64,7 +64,7 @@ return array( 'rsrc/css/application/dashboard/dashboard.css' => 'fe5b1869', 'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a', 'rsrc/css/application/differential/add-comment.css' => 'c47f8c40', - 'rsrc/css/application/differential/changeset-view.css' => '15be1064', + 'rsrc/css/application/differential/changeset-view.css' => 'e7bd2a79', 'rsrc/css/application/differential/core.css' => '5b7b8ff4', 'rsrc/css/application/differential/phui-inline-comment.css' => 'ffd1a542', 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', @@ -116,7 +116,7 @@ return array( 'rsrc/css/core/core.css' => '9f4cb463', 'rsrc/css/core/remarkup.css' => 'd1a5e11e', 'rsrc/css/core/syntax.css' => 'cae95e89', - 'rsrc/css/core/z-index.css' => '0233d039', + 'rsrc/css/core/z-index.css' => '9d8f7c4b', 'rsrc/css/diviner/diviner-shared.css' => '896f1d43', 'rsrc/css/font/font-awesome.css' => 'e838e088', 'rsrc/css/font/font-lato.css' => 'c7ccd872', @@ -390,8 +390,8 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => '731125f3', - 'rsrc/js/application/diff/DiffChangesetList.js' => '59d1ceb1', + 'rsrc/js/application/diff/DiffChangeset.js' => '3268dd83', + 'rsrc/js/application/diff/DiffChangesetList.js' => '0a4f7809', 'rsrc/js/application/diff/DiffInline.js' => '3337c065', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', @@ -503,7 +503,7 @@ return array( 'rsrc/js/core/behavior-more.js' => 'a80d0378', 'rsrc/js/core/behavior-object-selector.js' => 'e0ec7f2f', 'rsrc/js/core/behavior-oncopy.js' => '2926fff2', - 'rsrc/js/core/behavior-phabricator-nav.js' => '08163386', + 'rsrc/js/core/behavior-phabricator-nav.js' => '947753e0', 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => 'acd29eee', 'rsrc/js/core/behavior-read-only-warning.js' => 'ba158207', 'rsrc/js/core/behavior-refresh-csrf.js' => 'ab2f381b', @@ -565,7 +565,7 @@ return array( 'conpherence-thread-manager' => '4d863052', 'conpherence-transaction-css' => '85129c68', 'd3' => 'a11a5ff2', - 'differential-changeset-view-css' => '15be1064', + 'differential-changeset-view-css' => 'e7bd2a79', 'differential-core-view-css' => '5b7b8ff4', 'differential-revision-add-comment-css' => 'c47f8c40', 'differential-revision-comment-css' => '14b8565a', @@ -658,7 +658,7 @@ return array( 'javelin-behavior-phabricator-keyboard-pager' => 'a8da01f0', 'javelin-behavior-phabricator-keyboard-shortcuts' => '01fca1f0', 'javelin-behavior-phabricator-line-linker' => '1499a8cb', - 'javelin-behavior-phabricator-nav' => '08163386', + 'javelin-behavior-phabricator-nav' => '947753e0', 'javelin-behavior-phabricator-notification-example' => '8ce821c5', 'javelin-behavior-phabricator-object-selector' => 'e0ec7f2f', 'javelin-behavior-phabricator-oncopy' => '2926fff2', @@ -775,8 +775,8 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => '731125f3', - 'phabricator-diff-changeset-list' => '59d1ceb1', + 'phabricator-diff-changeset' => '3268dd83', + 'phabricator-diff-changeset-list' => '0a4f7809', 'phabricator-diff-inline' => '3337c065', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', @@ -816,7 +816,7 @@ return array( 'phabricator-uiexample-reactor-select' => 'a155550f', 'phabricator-uiexample-reactor-sendclass' => '1def2711', 'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee', - 'phabricator-zindex-css' => '0233d039', + 'phabricator-zindex-css' => '9d8f7c4b', 'phame-css' => 'b3a0b3a3', 'pholio-css' => 'ca89d380', 'pholio-edit-css' => '07676f51', @@ -946,16 +946,6 @@ return array( 'javelin-stratcom', 'javelin-workflow', ), - '08163386' => array( - 'javelin-behavior', - 'javelin-behavior-device', - 'javelin-stratcom', - 'javelin-dom', - 'javelin-magical-init', - 'javelin-vector', - 'javelin-request', - 'javelin-util', - ), '0825c27a' => array( 'javelin-behavior', 'javelin-dom', @@ -983,6 +973,9 @@ return array( 'javelin-dom', 'javelin-router', ), + '0a4f7809' => array( + 'javelin-install', + ), '0f764c35' => array( 'javelin-install', 'javelin-util', @@ -999,9 +992,6 @@ return array( 'javelin-dom', 'javelin-history', ), - '15be1064' => array( - 'phui-inline-comment-view-css', - ), '17bb8539' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1122,6 +1112,17 @@ return array( 'javelin-dom', 'javelin-vector', ), + '3268dd83' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + 'phabricator-diff-inline', + ), '327a00d1' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1349,9 +1350,6 @@ return array( 'javelin-vector', 'javelin-dom', ), - '59d1ceb1' => array( - 'javelin-install', - ), '5c54cbf3' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1450,17 +1448,6 @@ return array( 'javelin-workflow', 'phabricator-draggable-list', ), - '731125f3' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - 'phabricator-diff-inline', - ), '7319e029' => array( 'javelin-behavior', 'javelin-dom', @@ -1619,6 +1606,16 @@ return array( 'javelin-workflow', 'javelin-dom', ), + '947753e0' => array( + 'javelin-behavior', + 'javelin-behavior-device', + 'javelin-stratcom', + 'javelin-dom', + 'javelin-magical-init', + 'javelin-vector', + 'javelin-request', + 'javelin-util', + ), '949c0fe5' => array( 'javelin-install', ), @@ -2127,6 +2124,9 @@ return array( 'javelin-workflow', 'javelin-magical-init', ), + 'e7bd2a79' => array( + 'phui-inline-comment-view-css', + ), 'e9581f08' => array( 'javelin-behavior', 'javelin-stratcom', diff --git a/src/applications/differential/view/DifferentialChangesetDetailView.php b/src/applications/differential/view/DifferentialChangesetDetailView.php index 42be1c6444..86fba06c40 100644 --- a/src/applications/differential/view/DifferentialChangesetDetailView.php +++ b/src/applications/differential/view/DifferentialChangesetDetailView.php @@ -183,6 +183,7 @@ final class DifferentialChangesetDetailView extends AphrontView { 'autoload' => $this->getAutoload(), 'loaded' => $this->getLoaded(), 'undoTemplates' => hsprintf('%s', $renderer->renderUndoTemplates()), + 'path' => $display_filename, ), 'class' => $class, 'id' => $id, diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index 0cec38ffdc..1931ce354b 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -386,3 +386,21 @@ tr.differential-inline-loading { .differential-review-stage { position: relative; } + +.diff-banner { + position: fixed; + top: 0; + left: 0; + right: 0; + background: rgba(255, 255, 255, 0.95); + box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1); + border-bottom: 1px solid {$lightgreyborder}; + padding: 12px 18px; + vertical-align: middle; + font-weight: bold; + font-size: {$biggerfontsize}; +} + +.diff-banner .phui-icon-view { + margin-right: 4px; +} diff --git a/webroot/rsrc/css/core/z-index.css b/webroot/rsrc/css/core/z-index.css index bdab2e97ed..75cd394988 100644 --- a/webroot/rsrc/css/core/z-index.css +++ b/webroot/rsrc/css/core/z-index.css @@ -93,6 +93,10 @@ div.phui-calendar-day-event { z-index: 6; } +.diff-banner { + z-index: 6; +} + .conpherence-durable-column { z-index: 7; } diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index c3359c2007..148be860b9 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -19,6 +19,7 @@ JX.install('DiffChangeset', { this._node = node; var data = this._getNodeData(); + this._renderURI = data.renderURI; this._ref = data.ref; this._whitespace = data.whitespace; @@ -30,6 +31,8 @@ JX.install('DiffChangeset', { this._leftID = data.left; this._rightID = data.right; + this._path = data.path; + this._inlines = []; }, @@ -58,6 +61,7 @@ JX.install('DiffChangeset', { _visible: true, _undoNode: null, + _path: null, getLeftChangesetID: function() { return this._leftID; @@ -227,6 +231,9 @@ JX.install('DiffChangeset', { JX.Router.getInstance().queue(routable); }, + getPath: function() { + return this._path; + }, /** * Receive a response to a context request. @@ -428,6 +435,13 @@ JX.install('DiffChangeset', { return JX.Stratcom.getData(this._node); }, + getVectors: function() { + return { + pos: JX.$V(this._node), + dim: JX.Vector.getDim(this._node) + }; + }, + _onresponse: function(sequence, response) { if (sequence != this._sequence) { // If this isn't the most recent request, ignore it. This normally diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index ff51cd4666..2798d0d1c6 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -51,6 +51,9 @@ JX.install('DiffChangesetList', { var onresize = JX.bind(this, this._ifawake, this._onresize); JX.Stratcom.listen('resize', null, onresize); + var onscroll = JX.bind(this, this._ifawake, this._onscroll); + JX.Stratcom.listen('scroll', null, onscroll); + var onselect = JX.bind(this, this._ifawake, this._onselect); JX.Stratcom.listen( 'mousedown', @@ -113,6 +116,8 @@ JX.install('DiffChangesetList', { _rangeOrigin: null, _rangeTarget: null, + _bannerNode: null, + sleep: function() { this._asleep = true; @@ -807,6 +812,12 @@ JX.install('DiffChangesetList', { this._redrawFocus(); this._redrawSelection(); this._redrawHover(); + + this._redrawBanner(); + }, + + _onscroll: function() { + this._redrawBanner(); }, _onselect: function(e) { @@ -1265,6 +1276,77 @@ JX.install('DiffChangesetList', { this._rangeTarget = null; this.resetHover(); + }, + + _redrawBanner: function() { + var node = this._getBannerNode(); + var changeset = this._getVisibleChangeset(); + + // Don't do anything if nothing has changed. This seems to avoid some + // flickering issues in Safari, at least. + if (this._bannerChangeset === changeset) { + return; + } + this._bannerChangeset = changeset; + + if (!changeset) { + JX.DOM.remove(node); + return; + } + + var icon = new JX.PHUIXIconView() + .setIcon('fa-file') + .getNode(); + JX.DOM.setContent(node, [icon, ' ', changeset.getPath()]); + + document.body.appendChild(node); + }, + + _getBannerNode: function() { + if (!this._bannerNode) { + var attributes = { + className: 'diff-banner', + id: 'diff-banner' + }; + + this._bannerNode = JX.$N('div', attributes); + } + + return this._bannerNode; + }, + + _getVisibleChangeset: function() { + if (this.isAsleep()) { + return null; + } + + if (JX.Device.getDevice() != 'desktop') { + return null; + } + + // Never show the banner if we're very near the top of the page. + var margin = 480; + var s = JX.Vector.getScroll(); + if (s.y < margin) { + return null; + } + + var v = JX.Vector.getViewport(); + for (var ii = 0; ii < this._changesets.length; ii++) { + var changeset = this._changesets[ii]; + var c = changeset.getVectors(); + + // If the changeset starts above the upper half of the screen... + if (c.pos.y < (s.y + (v.y / 2))) { + // ...and ends below the lower half of the screen, this is the + // current visible changeset. + if ((c.pos.y + c.dim.y) > (s.y + (v.y / 2))) { + return changeset; + } + } + } + + return null; } } diff --git a/webroot/rsrc/js/core/behavior-phabricator-nav.js b/webroot/rsrc/js/core/behavior-phabricator-nav.js index cd132550a8..e37680abf0 100644 --- a/webroot/rsrc/js/core/behavior-phabricator-nav.js +++ b/webroot/rsrc/js/core/behavior-phabricator-nav.js @@ -130,8 +130,19 @@ JX.behavior('phabricator-nav', function(config) { return; } + // When the buoyant header is visible, move the menu down below it. This + // is a bit of a hack. + var banner_height = 0; + try { + var banner = JX.$('diff-banner'); + banner_height = JX.Vector.getDim(banner).y; + } catch (error) { + // Ignore if there's no banner on the page. + } + local.style.top = Math.max( 0, + banner_height, JX.$V(content).y - Math.max(0, JX.Vector.getScroll().y)) + 'px'; } From 1599c56217a1262d0e16b52553939e248a7f3529 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Thu, 18 May 2017 10:32:10 -0700 Subject: [PATCH 64/81] Add Pinboard Items to Timeline Summary: This allows adding of pinboard items to a timeline. I'm hoping we can get this in for Maniphest (Pholio, Cover Image) and Macro (because, Macro), but unsure how to scalably do this. Anyways, here's the front end. Test Plan: Make some fake timeline items in UIExamples, test mobile, tablet, and desktop breakpoints. {F4965798} Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17950 --- resources/celerity/map.php | 6 +-- .../examples/PHUITimelineExample.php | 38 +++++++++++++------ src/view/phui/PHUITimelineEventView.php | 17 ++++++++- webroot/rsrc/css/phui/phui-timeline-view.css | 6 +++ 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index d175534828..614824d9c6 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ return array( 'names' => array( 'conpherence.pkg.css' => 'ff161f2d', 'conpherence.pkg.js' => 'b5b51108', - 'core.pkg.css' => '4937a7d7', + 'core.pkg.css' => '0ab752b2', 'core.pkg.js' => 'a0c8fb20', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '52b014e7', @@ -173,7 +173,7 @@ return array( 'rsrc/css/phui/phui-spacing.css' => '042804d6', 'rsrc/css/phui/phui-status.css' => 'd5263e49', 'rsrc/css/phui/phui-tag-view.css' => 'cc4fd402', - 'rsrc/css/phui/phui-timeline-view.css' => '1d7ef61d', + 'rsrc/css/phui/phui-timeline-view.css' => '313c7f22', 'rsrc/css/phui/phui-two-column-view.css' => 'ce9fa0b7', 'rsrc/css/phui/workboards/phui-workboard-color.css' => '783cdff5', 'rsrc/css/phui/workboards/phui-workboard.css' => '3bc85455', @@ -880,7 +880,7 @@ return array( 'phui-status-list-view-css' => 'd5263e49', 'phui-tag-view-css' => 'cc4fd402', 'phui-theme-css' => '9f261c6b', - 'phui-timeline-view-css' => '1d7ef61d', + 'phui-timeline-view-css' => '313c7f22', 'phui-two-column-view-css' => 'ce9fa0b7', 'phui-workboard-color-css' => '783cdff5', 'phui-workboard-view-css' => '3bc85455', diff --git a/src/applications/uiexample/examples/PHUITimelineExample.php b/src/applications/uiexample/examples/PHUITimelineExample.php index 9bedffd9eb..42c680ea7b 100644 --- a/src/applications/uiexample/examples/PHUITimelineExample.php +++ b/src/applications/uiexample/examples/PHUITimelineExample.php @@ -80,13 +80,35 @@ final class PHUITimelineExample extends PhabricatorUIExample { ->setTitle(pht('Minor Red Event')) ->setColor(PhabricatorTransactions::COLOR_RED); + // Pinboard!! + $pin1 = id(new PHUIPinboardItemView()) + ->setUser($user) + ->setHeader('user0.png') + ->setImageURI(celerity_get_resource_uri('/rsrc/image/people/user0.png')) + ->setURI(celerity_get_resource_uri('/rsrc/image/people/user0.png')) + ->setImageSize(280, 210); + + $pin2 = id(new PHUIPinboardItemView()) + ->setUser($user) + ->setHeader('user1.png') + ->setImageURI(celerity_get_resource_uri('/rsrc/image/people/user1.png')) + ->setURI(celerity_get_resource_uri('/rsrc/image/people/user1.png')) + ->setImageSize(280, 210); + + $pin3 = id(new PHUIPinboardItemView()) + ->setUser($user) + ->setHeader('user2.png') + ->setImageURI(celerity_get_resource_uri('/rsrc/image/people/user2.png')) + ->setURI(celerity_get_resource_uri('/rsrc/image/people/user1.png')) + ->setImageSize(280, 210); + $events[] = id(new PHUITimelineEventView()) ->setUserHandle($handle) - ->setIcon('fa-check') - ->setTitle(pht('Historically Important Action')) - ->setColor(PhabricatorTransactions::COLOR_BLACK) - ->setReallyMajorEvent(true); - + ->setIcon('fa-camera-retro') + ->setTitle(pht('Pinboard Image Event')) + ->addPinboardItem($pin1) + ->addPinboardItem($pin2) + ->addPinboardItem($pin3); $events[] = id(new PHUITimelineEventView()) ->setUserHandle($handle) @@ -102,12 +124,6 @@ final class PHUITimelineExample extends PhabricatorUIExample { ->appendChild(str_repeat(pht('Long Text Body').' ', 64)) ->setColor(PhabricatorTransactions::COLOR_ORANGE); - $events[] = id(new PHUITimelineEventView()) - ->setUserHandle($handle) - ->setTitle(str_repeat('LongTextEventNoSpaces', 1024)) - ->appendChild(str_repeat('LongTextNoSpaces', 1024)) - ->setColor(PhabricatorTransactions::COLOR_RED); - $colors = array( PhabricatorTransactions::COLOR_RED, PhabricatorTransactions::COLOR_ORANGE, diff --git a/src/view/phui/PHUITimelineEventView.php b/src/view/phui/PHUITimelineEventView.php index 142ea1e87d..51a0ea5aae 100644 --- a/src/view/phui/PHUITimelineEventView.php +++ b/src/view/phui/PHUITimelineEventView.php @@ -28,6 +28,7 @@ final class PHUITimelineEventView extends AphrontView { private $hideCommentOptions = false; private $authorPHID; private $badges = array(); + private $pinboardItems = array(); public function setAuthorPHID($author_phid) { $this->authorPHID = $author_phid; @@ -190,6 +191,11 @@ final class PHUITimelineEventView extends AphrontView { return $this->hideCommentOptions; } + public function addPinboardItem(PHUIPinboardItemView $item) { + $this->pinboardItems[] = $item; + return $this; + } + public function setToken($token, $removed = false) { $this->token = $token; $this->tokenRemoved = $removed; @@ -441,12 +447,21 @@ final class PHUITimelineEventView extends AphrontView { ), $content); + // Image Events + $pinboard = null; + if ($this->pinboardItems) { + $pinboard = new PHUIPinboardView(); + foreach ($this->pinboardItems as $item) { + $pinboard->addItem($item); + } + } + $content = phutil_tag( 'div', array( 'class' => implode(' ', $content_classes), ), - array($image, $badges, $wedge, $content)); + array($image, $badges, $wedge, $content, $pinboard)); $outer_classes = $this->classes; $outer_classes[] = 'phui-timeline-shell'; diff --git a/webroot/rsrc/css/phui/phui-timeline-view.css b/webroot/rsrc/css/phui/phui-timeline-view.css index c4f568411a..da6b5950c4 100644 --- a/webroot/rsrc/css/phui/phui-timeline-view.css +++ b/webroot/rsrc/css/phui/phui-timeline-view.css @@ -429,3 +429,9 @@ a.phui-timeline-menu .phui-icon-view { .phui-comment-preview-view { margin-bottom: 20px; } + +.phui-timeline-view .phui-pinboard-view { + margin: 8px 0 0 0; + padding: 0; + text-align: left; +} From 91eb22cb3a8ba3873a760b2036781e17190b7072 Mon Sep 17 00:00:00 2001 From: Austin McKinley Date: Thu, 18 May 2017 11:03:31 -0700 Subject: [PATCH 65/81] Migrate Project slugs to modular transactions Test Plan: Unit tests all pass. Added/removed/altered some project hashtags and observed expected transactions in timeline. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: epriestley Differential Revision: https://secure.phabricator.com/D17952 --- src/__phutil_library_map__.php | 2 + .../PhabricatorProjectCoreTestCase.php | 12 +- .../conduit/ProjectCreateConduitAPIMethod.php | 3 +- .../PhabricatorProjectTransactionEditor.php | 85 +-------- .../engine/PhabricatorProjectEditEngine.php | 3 +- .../storage/PhabricatorProjectTransaction.php | 68 +------- .../PhabricatorProjectSlugsTransaction.php | 165 ++++++++++++++++++ 7 files changed, 181 insertions(+), 157 deletions(-) create mode 100644 src/applications/project/xaction/PhabricatorProjectSlugsTransaction.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index cc4794d251..85120f9fe8 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3666,6 +3666,7 @@ phutil_register_library_map(array( 'PhabricatorProjectSilenceController' => 'applications/project/controller/PhabricatorProjectSilenceController.php', 'PhabricatorProjectSilencedEdgeType' => 'applications/project/edge/PhabricatorProjectSilencedEdgeType.php', 'PhabricatorProjectSlug' => 'applications/project/storage/PhabricatorProjectSlug.php', + 'PhabricatorProjectSlugsTransaction' => 'applications/project/xaction/PhabricatorProjectSlugsTransaction.php', 'PhabricatorProjectStandardCustomField' => 'applications/project/customfield/PhabricatorProjectStandardCustomField.php', 'PhabricatorProjectStatus' => 'applications/project/constants/PhabricatorProjectStatus.php', 'PhabricatorProjectSubprojectWarningController' => 'applications/project/controller/PhabricatorProjectSubprojectWarningController.php', @@ -9077,6 +9078,7 @@ phutil_register_library_map(array( 'PhabricatorProjectSilenceController' => 'PhabricatorProjectController', 'PhabricatorProjectSilencedEdgeType' => 'PhabricatorEdgeType', 'PhabricatorProjectSlug' => 'PhabricatorProjectDAO', + 'PhabricatorProjectSlugsTransaction' => 'PhabricatorProjectTransactionType', 'PhabricatorProjectStandardCustomField' => array( 'PhabricatorProjectCustomField', 'PhabricatorStandardCustomFieldInterface', diff --git a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php index 0f97d88f1e..0bec2fe53c 100644 --- a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php +++ b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php @@ -362,7 +362,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_SLUGS) + ->setTransactionType(PhabricatorProjectSlugsTransaction::TRANSACTIONTYPE) ->setNewValue(array($name)); $this->applyTransactions($project, $user, $xactions); @@ -386,7 +386,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { ->setNewValue($name2); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_SLUGS) + ->setTransactionType(PhabricatorProjectSlugsTransaction::TRANSACTIONTYPE) ->setNewValue(array($name2)); $this->applyTransactions($project, $user, $xactions); @@ -416,7 +416,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_SLUGS) + ->setTransactionType(PhabricatorProjectSlugsTransaction::TRANSACTIONTYPE) ->setNewValue(array($input, $input)); $this->applyTransactions($project, $user, $xactions); @@ -448,7 +448,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_SLUGS) + ->setTransactionType(PhabricatorProjectSlugsTransaction::TRANSACTIONTYPE) ->setNewValue(array($input)); $this->applyTransactions($project, $user, $xactions); @@ -474,7 +474,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_SLUGS) + ->setTransactionType(PhabricatorProjectSlugsTransaction::TRANSACTIONTYPE) ->setNewValue(array($input)); $caught = null; @@ -605,7 +605,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { ->setNewValue($name); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_SLUGS) + ->setTransactionType(PhabricatorProjectSlugsTransaction::TRANSACTIONTYPE) ->setNewValue(array($slug)); $this->applyTransactions($project, $user, $xactions); diff --git a/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php b/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php index edd2606a77..896cc9a07b 100644 --- a/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php +++ b/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php @@ -64,7 +64,8 @@ final class ProjectCreateConduitAPIMethod extends ProjectConduitAPIMethod { if ($request->getValue('tags')) { $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_SLUGS) + ->setTransactionType( + PhabricatorProjectSlugsTransaction::TRANSACTIONTYPE) ->setNewValue($request->getValue('tags')); } diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php index 7a38c3bbdf..f5c4f9fab2 100644 --- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php @@ -30,7 +30,6 @@ final class PhabricatorProjectTransactionEditor $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; $types[] = PhabricatorTransactions::TYPE_JOIN_POLICY; - $types[] = PhabricatorProjectTransaction::TYPE_SLUGS; $types[] = PhabricatorProjectTransaction::TYPE_STATUS; $types[] = PhabricatorProjectTransaction::TYPE_IMAGE; $types[] = PhabricatorProjectTransaction::TYPE_ICON; @@ -51,11 +50,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_SLUGS: - $slugs = $object->getSlugs(); - $slugs = mpull($slugs, 'getSlug', 'getSlug'); - unset($slugs[$object->getPrimarySlug()]); - return array_keys($slugs); case PhabricatorProjectTransaction::TYPE_STATUS: return $object->getStatus(); case PhabricatorProjectTransaction::TYPE_IMAGE: @@ -105,8 +99,6 @@ final class PhabricatorProjectTransactionEditor return null; } return $value; - case PhabricatorProjectTransaction::TYPE_SLUGS: - return $this->normalizeSlugs($xaction->getNewValue()); } return parent::getCustomTransactionNewValue($object, $xaction); @@ -117,8 +109,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_SLUGS: - return; case PhabricatorProjectTransaction::TYPE_STATUS: $object->setStatus($xaction->getNewValue()); return; @@ -167,18 +157,6 @@ final class PhabricatorProjectTransactionEditor $new = $xaction->getNewValue(); switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_SLUGS: - $old = $xaction->getOldValue(); - $new = $xaction->getNewValue(); - $add = array_diff($new, $old); - $rem = array_diff($old, $new); - - foreach ($add as $slug) { - $this->addSlug($object, $slug, true); - } - - $this->removeSlugs($object, $rem); - return; case PhabricatorProjectTransaction::TYPE_STATUS: case PhabricatorProjectTransaction::TYPE_IMAGE: case PhabricatorProjectTransaction::TYPE_ICON: @@ -278,65 +256,6 @@ final class PhabricatorProjectTransactionEditor $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { - case PhabricatorProjectTransaction::TYPE_SLUGS: - if (!$xactions) { - break; - } - - $slug_xaction = last($xactions); - - $new = $slug_xaction->getNewValue(); - - $invalid = array(); - foreach ($new as $slug) { - if (!PhabricatorSlug::isValidProjectSlug($slug)) { - $invalid[] = $slug; - } - } - - if ($invalid) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'Hashtags must contain at least one letter or number. %s '. - 'project hashtag(s) are invalid: %s.', - phutil_count($invalid), - implode(', ', $invalid)), - $slug_xaction); - break; - } - - $new = $this->normalizeSlugs($new); - - if ($new) { - $slugs_used_already = id(new PhabricatorProjectSlug()) - ->loadAllWhere('slug IN (%Ls)', $new); - } else { - // The project doesn't have any extra slugs. - $slugs_used_already = array(); - } - - $slugs_used_already = mgroup($slugs_used_already, 'getProjectPHID'); - foreach ($slugs_used_already as $project_phid => $used_slugs) { - if ($project_phid == $object->getPHID()) { - continue; - } - - $used_slug_strs = mpull($used_slugs, 'getSlug'); - - $error = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - '%s project hashtag(s) are already used by other projects: %s.', - phutil_count($used_slug_strs), - implode(', ', $used_slug_strs)), - $slug_xaction); - $errors[] = $error; - } - - break; case PhabricatorProjectTransaction::TYPE_PARENT: case PhabricatorProjectTransaction::TYPE_MILESTONE: if (!$xactions) { @@ -674,7 +593,7 @@ final class PhabricatorProjectTransactionEditor ->save(); } - private function removeSlugs(PhabricatorProject $project, array $slugs) { + public function removeSlugs(PhabricatorProject $project, array $slugs) { if (!$slugs) { return; } @@ -696,7 +615,7 @@ final class PhabricatorProjectTransactionEditor } } - private function normalizeSlugs(array $slugs) { + public function normalizeSlugs(array $slugs) { foreach ($slugs as $key => $slug) { $slugs[$key] = PhabricatorSlug::normalizeProjectSlug($slug); } diff --git a/src/applications/project/engine/PhabricatorProjectEditEngine.php b/src/applications/project/engine/PhabricatorProjectEditEngine.php index 8f855074d6..b0e3384d4a 100644 --- a/src/applications/project/engine/PhabricatorProjectEditEngine.php +++ b/src/applications/project/engine/PhabricatorProjectEditEngine.php @@ -262,7 +262,8 @@ final class PhabricatorProjectEditEngine id(new PhabricatorStringListEditField()) ->setKey('slugs') ->setLabel(pht('Additional Hashtags')) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_SLUGS) + ->setTransactionType( + PhabricatorProjectSlugsTransaction::TRANSACTIONTYPE) ->setDescription(pht('Additional project slugs.')) ->setConduitDescription(pht('Change project slugs.')) ->setConduitTypeDescription(pht('New list of slugs.')) diff --git a/src/applications/project/storage/PhabricatorProjectTransaction.php b/src/applications/project/storage/PhabricatorProjectTransaction.php index 0ad6230a5e..2abd7a19ed 100644 --- a/src/applications/project/storage/PhabricatorProjectTransaction.php +++ b/src/applications/project/storage/PhabricatorProjectTransaction.php @@ -3,7 +3,6 @@ final class PhabricatorProjectTransaction extends PhabricatorModularTransaction { - const TYPE_SLUGS = 'project:slugs'; const TYPE_STATUS = 'project:status'; const TYPE_IMAGE = 'project:image'; const TYPE_ICON = 'project:icon'; @@ -134,8 +133,6 @@ final class PhabricatorProjectTransaction return 'fa-photo'; case self::TYPE_MEMBERS: return 'fa-user'; - case self::TYPE_SLUGS: - return 'fa-tag'; } return parent::getIcon(); } @@ -212,33 +209,6 @@ final class PhabricatorProjectTransaction } break; - case self::TYPE_SLUGS: - $add = array_diff($new, $old); - $rem = array_diff($old, $new); - - if ($add && $rem) { - return pht( - '%s changed project hashtag(s), added %d: %s; removed %d: %s.', - $author_handle, - count($add), - $this->renderSlugList($add), - count($rem), - $this->renderSlugList($rem)); - } else if ($add) { - return pht( - '%s added %d project hashtag(s): %s.', - $author_handle, - count($add), - $this->renderSlugList($add)); - } else if ($rem) { - return pht( - '%s removed %d project hashtag(s): %s.', - $author_handle, - count($rem), - $this->renderSlugList($rem)); - } - break; - case self::TYPE_MEMBERS: $add = array_diff($new, $old); $rem = array_diff($old, $new); @@ -380,36 +350,6 @@ final class PhabricatorProjectTransaction $author_handle, $object_handle); } - - case self::TYPE_SLUGS: - $add = array_diff($new, $old); - $rem = array_diff($old, $new); - - if ($add && $rem) { - return pht( - '%s changed %s hashtag(s), added %d: %s; removed %d: %s.', - $author_handle, - $object_handle, - count($add), - $this->renderSlugList($add), - count($rem), - $this->renderSlugList($rem)); - } else if ($add) { - return pht( - '%s added %d %s hashtag(s): %s.', - $author_handle, - count($add), - $object_handle, - $this->renderSlugList($add)); - } else if ($rem) { - return pht( - '%s removed %d %s hashtag(s): %s.', - $author_handle, - count($rem), - $object_handle, - $this->renderSlugList($rem)); - } - } return parent::getTitleForFeed(); @@ -418,8 +358,8 @@ final class PhabricatorProjectTransaction public function getMailTags() { $tags = array(); switch ($this->getTransactionType()) { - case self::TYPE_NAME: - case self::TYPE_SLUGS: + case PhabricatorProjectNameTransaction::TRANSACTIONTYPE: + case PhabricatorProjectSlugsTransaction::TRANSACTIONTYPE: case self::TYPE_IMAGE: case self::TYPE_ICON: case self::TYPE_COLOR: @@ -447,8 +387,4 @@ final class PhabricatorProjectTransaction return $tags; } - private function renderSlugList($slugs) { - return implode(', ', $slugs); - } - } diff --git a/src/applications/project/xaction/PhabricatorProjectSlugsTransaction.php b/src/applications/project/xaction/PhabricatorProjectSlugsTransaction.php new file mode 100644 index 0000000000..a9105e5ba3 --- /dev/null +++ b/src/applications/project/xaction/PhabricatorProjectSlugsTransaction.php @@ -0,0 +1,165 @@ +getSlugs(); + $slugs = mpull($slugs, 'getSlug', 'getSlug'); + unset($slugs[$object->getPrimarySlug()]); + return array_keys($slugs); + } + + public function generateNewValue($object, $value) { + return $this->getEditor()->normalizeSlugs($value); + } + + public function applyInternalEffects($object, $value) { + return; + } + + public function applyExternalEffects($object, $value) { + $old = $this->getOldValue(); + $new = $value; + $add = array_diff($new, $old); + $rem = array_diff($old, $new); + + foreach ($add as $slug) { + $this->getEditor()->addSlug($object, $slug, true); + } + + $this->getEditor()->removeSlugs($object, $rem); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $add = array_diff($new, $old); + $rem = array_diff($old, $new); + + if ($add && $rem) { + return pht( + '%s changed project hashtag(s), added %d: %s; removed %d: %s.', + $this->renderAuthor(), + count($add), + $this->renderSlugList($add), + count($rem), + $this->renderSlugList($rem)); + } else if ($add) { + return pht( + '%s added %d project hashtag(s): %s.', + $this->renderAuthor(), + count($add), + $this->renderSlugList($add)); + } else if ($rem) { + return pht( + '%s removed %d project hashtag(s): %s.', + $this->renderAuthor(), + count($rem), + $this->renderSlugList($rem)); + } + break; + } + + public function getTitleForFeed() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $add = array_diff($new, $old); + $rem = array_diff($old, $new); + + if ($add && $rem) { + return pht( + '%s changed %s hashtag(s), added %d: %s; removed %d: %s.', + $this->renderAuthor(), + $this->renderObject(), + count($add), + $this->renderSlugList($add), + count($rem), + $this->renderSlugList($rem)); + } else if ($add) { + return pht( + '%s added %d %s hashtag(s): %s.', + $this->renderAuthor(), + count($add), + $this->renderObject(), + $this->renderSlugList($add)); + } else if ($rem) { + return pht( + '%s removed %d %s hashtag(s): %s.', + $this->renderAuthor(), + count($rem), + $this->renderObject(), + $this->renderSlugList($rem)); + } + } + + public function getIcon() { + return 'fa-tag'; + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + if (!$xactions) { + return $errors; + } + + $slug_xaction = last($xactions); + + $new = $slug_xaction->getNewValue(); + + $invalid = array(); + foreach ($new as $slug) { + if (!PhabricatorSlug::isValidProjectSlug($slug)) { + $invalid[] = $slug; + } + } + + if ($invalid) { + $errors[] = $this->newInvalidError( + pht( + 'Hashtags must contain at least one letter or number. %s '. + 'project hashtag(s) are invalid: %s.', + phutil_count($invalid), + implode(', ', $invalid))); + + return $errors; + } + + $new = $this->getEditor()->normalizeSlugs($new); + + if ($new) { + $slugs_used_already = id(new PhabricatorProjectSlug()) + ->loadAllWhere('slug IN (%Ls)', $new); + } else { + // The project doesn't have any extra slugs. + $slugs_used_already = array(); + } + + $slugs_used_already = mgroup($slugs_used_already, 'getProjectPHID'); + foreach ($slugs_used_already as $project_phid => $used_slugs) { + if ($project_phid == $object->getPHID()) { + continue; + } + + $used_slug_strs = mpull($used_slugs, 'getSlug'); + + $errors[] = $this->newInvalidError( + pht( + '%s project hashtag(s) are already used by other projects: %s.', + phutil_count($used_slug_strs), + implode(', ', $used_slug_strs))); + } + + return $errors; + } + + private function renderSlugList($slugs) { + return implode(', ', $slugs); + } + +} From f92059d84c6a37f4aed038f3fd237cb04ae10d8e Mon Sep 17 00:00:00 2001 From: Austin McKinley Date: Thu, 18 May 2017 11:24:59 -0700 Subject: [PATCH 66/81] Migrate Project status to modular transactions Test Plan: Unit tests pass. Archived/activated some projects a couple times; observed expected transactions on timeline. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: epriestley Differential Revision: https://secure.phabricator.com/D17953 --- .../sql/patches/20131020.pxactionmig.php | 2 +- src/__phutil_library_map__.php | 2 + .../PhabricatorProjectArchiveController.php | 3 +- ...PhabricatorProjectColumnHideController.php | 3 +- .../PhabricatorProjectTransactionEditor.php | 10 +-- .../PhabricatorProjectTestDataGenerator.php | 2 +- .../storage/PhabricatorProjectTransaction.php | 49 +------------- .../PhabricatorProjectStatusTransaction.php | 66 +++++++++++++++++++ 8 files changed, 76 insertions(+), 61 deletions(-) create mode 100644 src/applications/project/xaction/PhabricatorProjectStatusTransaction.php diff --git a/resources/sql/patches/20131020.pxactionmig.php b/resources/sql/patches/20131020.pxactionmig.php index a6d305b77a..9a7b82f3e8 100644 --- a/resources/sql/patches/20131020.pxactionmig.php +++ b/resources/sql/patches/20131020.pxactionmig.php @@ -34,7 +34,7 @@ foreach ($rows as $row) { $type_map = array( 'name' => PhabricatorProjectNameTransaction::TRANSACTIONTYPE, 'members' => PhabricatorProjectTransaction::TYPE_MEMBERS, - 'status' => PhabricatorProjectTransaction::TYPE_STATUS, + 'status' => PhabricatorProjectStatusTransaction::TRANSACTIONTYPE, 'canview' => PhabricatorTransactions::TYPE_VIEW_POLICY, 'canedit' => PhabricatorTransactions::TYPE_EDIT_POLICY, 'canjoin' => PhabricatorTransactions::TYPE_JOIN_POLICY, diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 85120f9fe8..c3427d2f0a 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3669,6 +3669,7 @@ phutil_register_library_map(array( 'PhabricatorProjectSlugsTransaction' => 'applications/project/xaction/PhabricatorProjectSlugsTransaction.php', 'PhabricatorProjectStandardCustomField' => 'applications/project/customfield/PhabricatorProjectStandardCustomField.php', 'PhabricatorProjectStatus' => 'applications/project/constants/PhabricatorProjectStatus.php', + 'PhabricatorProjectStatusTransaction' => 'applications/project/xaction/PhabricatorProjectStatusTransaction.php', 'PhabricatorProjectSubprojectWarningController' => 'applications/project/controller/PhabricatorProjectSubprojectWarningController.php', 'PhabricatorProjectSubprojectsController' => 'applications/project/controller/PhabricatorProjectSubprojectsController.php', 'PhabricatorProjectSubprojectsProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectSubprojectsProfileMenuItem.php', @@ -9084,6 +9085,7 @@ phutil_register_library_map(array( 'PhabricatorStandardCustomFieldInterface', ), 'PhabricatorProjectStatus' => 'Phobject', + 'PhabricatorProjectStatusTransaction' => 'PhabricatorProjectTransactionType', 'PhabricatorProjectSubprojectWarningController' => 'PhabricatorProjectController', 'PhabricatorProjectSubprojectsController' => 'PhabricatorProjectController', 'PhabricatorProjectSubprojectsProfileMenuItem' => 'PhabricatorProfileMenuItem', diff --git a/src/applications/project/controller/PhabricatorProjectArchiveController.php b/src/applications/project/controller/PhabricatorProjectArchiveController.php index ee974bf8e8..b97d5391ab 100644 --- a/src/applications/project/controller/PhabricatorProjectArchiveController.php +++ b/src/applications/project/controller/PhabricatorProjectArchiveController.php @@ -32,7 +32,8 @@ final class PhabricatorProjectArchiveController $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_STATUS) + ->setTransactionType( + PhabricatorProjectStatusTransaction::TRANSACTIONTYPE) ->setNewValue($new_status); id(new PhabricatorProjectTransactionEditor()) diff --git a/src/applications/project/controller/PhabricatorProjectColumnHideController.php b/src/applications/project/controller/PhabricatorProjectColumnHideController.php index 27dbb17c47..1dd5e47ecb 100644 --- a/src/applications/project/controller/PhabricatorProjectColumnHideController.php +++ b/src/applications/project/controller/PhabricatorProjectColumnHideController.php @@ -65,7 +65,8 @@ final class PhabricatorProjectColumnHideController $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_STATUS) + ->setTransactionType( + PhabricatorProjectStatusTransaction::TRANSACTIONTYPE) ->setNewValue($new_status); id(new PhabricatorProjectTransactionEditor()) diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php index f5c4f9fab2..0a38867d71 100644 --- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php @@ -30,7 +30,6 @@ final class PhabricatorProjectTransactionEditor $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; $types[] = PhabricatorTransactions::TYPE_JOIN_POLICY; - $types[] = PhabricatorProjectTransaction::TYPE_STATUS; $types[] = PhabricatorProjectTransaction::TYPE_IMAGE; $types[] = PhabricatorProjectTransaction::TYPE_ICON; $types[] = PhabricatorProjectTransaction::TYPE_COLOR; @@ -50,8 +49,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_STATUS: - return $object->getStatus(); case PhabricatorProjectTransaction::TYPE_IMAGE: return $object->getProfileImagePHID(); case PhabricatorProjectTransaction::TYPE_ICON: @@ -81,7 +78,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_STATUS: case PhabricatorProjectTransaction::TYPE_IMAGE: case PhabricatorProjectTransaction::TYPE_ICON: case PhabricatorProjectTransaction::TYPE_COLOR: @@ -109,9 +105,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_STATUS: - $object->setStatus($xaction->getNewValue()); - return; case PhabricatorProjectTransaction::TYPE_IMAGE: $object->setProfileImagePHID($xaction->getNewValue()); return; @@ -157,7 +150,6 @@ final class PhabricatorProjectTransactionEditor $new = $xaction->getNewValue(); switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_STATUS: case PhabricatorProjectTransaction::TYPE_IMAGE: case PhabricatorProjectTransaction::TYPE_ICON: case PhabricatorProjectTransaction::TYPE_COLOR: @@ -343,7 +335,7 @@ final class PhabricatorProjectTransactionEditor switch ($xaction->getTransactionType()) { case PhabricatorProjectNameTransaction::TRANSACTIONTYPE: - case PhabricatorProjectTransaction::TYPE_STATUS: + case PhabricatorProjectStatusTransaction::TRANSACTIONTYPE: case PhabricatorProjectTransaction::TYPE_IMAGE: case PhabricatorProjectTransaction::TYPE_ICON: case PhabricatorProjectTransaction::TYPE_COLOR: diff --git a/src/applications/project/lipsum/PhabricatorProjectTestDataGenerator.php b/src/applications/project/lipsum/PhabricatorProjectTestDataGenerator.php index 8032749f94..e29ee13f2d 100644 --- a/src/applications/project/lipsum/PhabricatorProjectTestDataGenerator.php +++ b/src/applications/project/lipsum/PhabricatorProjectTestDataGenerator.php @@ -20,7 +20,7 @@ final class PhabricatorProjectTestDataGenerator $this->newProjectTitle()); $xactions[] = $this->newTransaction( - PhabricatorProjectTransaction::TYPE_STATUS, + PhabricatorProjectStatusTransaction::TRANSACTIONTYPE, $this->newProjectStatus()); // Almost always make the author a member. diff --git a/src/applications/project/storage/PhabricatorProjectTransaction.php b/src/applications/project/storage/PhabricatorProjectTransaction.php index 2abd7a19ed..e61ff26277 100644 --- a/src/applications/project/storage/PhabricatorProjectTransaction.php +++ b/src/applications/project/storage/PhabricatorProjectTransaction.php @@ -3,7 +3,6 @@ final class PhabricatorProjectTransaction extends PhabricatorModularTransaction { - const TYPE_STATUS = 'project:status'; const TYPE_IMAGE = 'project:image'; const TYPE_ICON = 'project:icon'; const TYPE_COLOR = 'project:color'; @@ -55,22 +54,6 @@ final class PhabricatorProjectTransaction return array_merge($req_phids, parent::getRequiredHandlePHIDs()); } - public function getColor() { - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_STATUS: - if ($old == 0) { - return 'red'; - } else { - return 'green'; - } - } - return parent::getColor(); - } - public function shouldHide() { switch ($this->getTransactionType()) { case PhabricatorTransactions::TYPE_EDGE: @@ -115,12 +98,6 @@ final class PhabricatorProjectTransaction $new = $this->getNewValue(); switch ($this->getTransactionType()) { - case self::TYPE_STATUS: - if ($old == 0) { - return 'fa-ban'; - } else { - return 'fa-check'; - } case self::TYPE_LOCKED: if ($new) { return 'fa-lock'; @@ -149,18 +126,6 @@ final class PhabricatorProjectTransaction '%s created this project.', $this->renderHandleLink($author_phid)); - case self::TYPE_STATUS: - if ($old == 0) { - return pht( - '%s archived this project.', - $author_handle); - } else { - return pht( - '%s activated this project.', - $author_handle); - } - break; - case self::TYPE_IMAGE: // TODO: Some day, it would be nice to show the images. if (!$old) { @@ -288,18 +253,6 @@ final class PhabricatorProjectTransaction $new = $this->getNewValue(); switch ($this->getTransactionType()) { - case self::TYPE_STATUS: - if ($old == 0) { - return pht( - '%s archived %s.', - $author_handle, - $object_handle); - } else { - return pht( - '%s activated %s.', - $author_handle, - $object_handle); - } case self::TYPE_IMAGE: // TODO: Some day, it would be nice to show the images. if (!$old) { @@ -378,7 +331,7 @@ final class PhabricatorProjectTransaction $tags[] = self::MAILTAG_OTHER; } break; - case self::TYPE_STATUS: + case PhabricatorProjectStatusTransaction::TRANSACTIONTYPE: case self::TYPE_LOCKED: default: $tags[] = self::MAILTAG_OTHER; diff --git a/src/applications/project/xaction/PhabricatorProjectStatusTransaction.php b/src/applications/project/xaction/PhabricatorProjectStatusTransaction.php new file mode 100644 index 0000000000..e5c8ead4f4 --- /dev/null +++ b/src/applications/project/xaction/PhabricatorProjectStatusTransaction.php @@ -0,0 +1,66 @@ +getStatus(); + } + + public function applyInternalEffects($object, $value) { + $object->setStatus($value); + } + + public function getTitle() { + $old = $this->getOldValue(); + + if ($old == 0) { + return pht( + '%s archived this project.', + $this->renderAuthor()); + } else { + return pht( + '%s activated this project.', + $this->renderAuthor()); + } + } + + public function getTitleForFeed() { + $old = $this->getOldValue(); + + if ($old == 0) { + return pht( + '%s archived %s.', + $this->renderAuthor(), + $this->renderObject()); + } else { + return pht( + '%s activated %s.', + $this->renderAuthor(), + $this->renderObject()); + } + } + + public function getColor() { + $old = $this->getOldValue(); + + if ($old == 0) { + return 'red'; + } else { + return 'green'; + } + } + + public function getIcon() { + $old = $this->getOldValue(); + + if ($old == 0) { + return 'fa-ban'; + } else { + return 'fa-check'; + } + } + +} From eb84bf98a413cd578f987aba2ddd2f08630932c6 Mon Sep 17 00:00:00 2001 From: Austin McKinley Date: Thu, 18 May 2017 15:14:02 -0700 Subject: [PATCH 67/81] Migrate Project image to modular transactions Summary: I'm not sure you can actually remove a project's image (maybe via the API?), but I kept the code for rendering the relevant title/feed anyway. Test Plan: Unit tests + adding/changing project pictures. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin, epriestley Differential Revision: https://secure.phabricator.com/D17954 --- src/__phutil_library_map__.php | 2 + .../PhabricatorFileComposeController.php | 3 +- ...habricatorProjectEditPictureController.php | 3 +- .../PhabricatorProjectTransactionEditor.php | 26 +--- .../storage/PhabricatorProjectTransaction.php | 50 +------ .../PhabricatorProjectImageTransaction.php | 136 ++++++++++++++++++ .../PhabricatorProjectSlugsTransaction.php | 1 - 7 files changed, 144 insertions(+), 77 deletions(-) create mode 100644 src/applications/project/xaction/PhabricatorProjectImageTransaction.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index c3427d2f0a..d43d756426 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3621,6 +3621,7 @@ phutil_register_library_map(array( 'PhabricatorProjectHovercardEngineExtension' => 'applications/project/engineextension/PhabricatorProjectHovercardEngineExtension.php', 'PhabricatorProjectIconSet' => 'applications/project/icon/PhabricatorProjectIconSet.php', 'PhabricatorProjectIconsConfigOptionType' => 'applications/project/config/PhabricatorProjectIconsConfigOptionType.php', + 'PhabricatorProjectImageTransaction' => 'applications/project/xaction/PhabricatorProjectImageTransaction.php', 'PhabricatorProjectInterface' => 'applications/project/interface/PhabricatorProjectInterface.php', 'PhabricatorProjectListController' => 'applications/project/controller/PhabricatorProjectListController.php', 'PhabricatorProjectListView' => 'applications/project/view/PhabricatorProjectListView.php', @@ -9035,6 +9036,7 @@ phutil_register_library_map(array( 'PhabricatorProjectHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension', 'PhabricatorProjectIconSet' => 'PhabricatorIconSet', 'PhabricatorProjectIconsConfigOptionType' => 'PhabricatorConfigJSONOptionType', + 'PhabricatorProjectImageTransaction' => 'PhabricatorProjectTransactionType', 'PhabricatorProjectListController' => 'PhabricatorProjectController', 'PhabricatorProjectListView' => 'AphrontView', 'PhabricatorProjectLockController' => 'PhabricatorProjectController', diff --git a/src/applications/files/controller/PhabricatorFileComposeController.php b/src/applications/files/controller/PhabricatorFileComposeController.php index 930ec61483..7afd99330a 100644 --- a/src/applications/files/controller/PhabricatorFileComposeController.php +++ b/src/applications/files/controller/PhabricatorFileComposeController.php @@ -48,7 +48,8 @@ final class PhabricatorFileComposeController $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_IMAGE) + ->setTransactionType( + PhabricatorProjectImageTransaction::TRANSACTIONTYPE) ->setNewValue($file->getPHID()); $editor = id(new PhabricatorProjectTransactionEditor()) diff --git a/src/applications/project/controller/PhabricatorProjectEditPictureController.php b/src/applications/project/controller/PhabricatorProjectEditPictureController.php index 4352ec6ad8..9d0c16efb9 100644 --- a/src/applications/project/controller/PhabricatorProjectEditPictureController.php +++ b/src/applications/project/controller/PhabricatorProjectEditPictureController.php @@ -78,7 +78,8 @@ final class PhabricatorProjectEditPictureController $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_IMAGE) + ->setTransactionType( + PhabricatorProjectImageTransaction::TRANSACTIONTYPE) ->setNewValue($new_value); $editor = id(new PhabricatorProjectTransactionEditor()) diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php index 0a38867d71..101020970a 100644 --- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php @@ -30,7 +30,6 @@ final class PhabricatorProjectTransactionEditor $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; $types[] = PhabricatorTransactions::TYPE_JOIN_POLICY; - $types[] = PhabricatorProjectTransaction::TYPE_IMAGE; $types[] = PhabricatorProjectTransaction::TYPE_ICON; $types[] = PhabricatorProjectTransaction::TYPE_COLOR; $types[] = PhabricatorProjectTransaction::TYPE_LOCKED; @@ -49,8 +48,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_IMAGE: - return $object->getProfileImagePHID(); case PhabricatorProjectTransaction::TYPE_ICON: return $object->getIcon(); case PhabricatorProjectTransaction::TYPE_COLOR: @@ -78,7 +75,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_IMAGE: case PhabricatorProjectTransaction::TYPE_ICON: case PhabricatorProjectTransaction::TYPE_COLOR: case PhabricatorProjectTransaction::TYPE_LOCKED: @@ -105,9 +101,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_IMAGE: - $object->setProfileImagePHID($xaction->getNewValue()); - return; case PhabricatorProjectTransaction::TYPE_ICON: $object->setIcon($xaction->getNewValue()); return; @@ -150,7 +143,6 @@ final class PhabricatorProjectTransactionEditor $new = $xaction->getNewValue(); switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_IMAGE: case PhabricatorProjectTransaction::TYPE_ICON: case PhabricatorProjectTransaction::TYPE_COLOR: case PhabricatorProjectTransaction::TYPE_LOCKED: @@ -336,7 +328,7 @@ final class PhabricatorProjectTransactionEditor switch ($xaction->getTransactionType()) { case PhabricatorProjectNameTransaction::TRANSACTIONTYPE: case PhabricatorProjectStatusTransaction::TRANSACTIONTYPE: - case PhabricatorProjectTransaction::TYPE_IMAGE: + case PhabricatorProjectImageTransaction::TRANSACTIONTYPE: case PhabricatorProjectTransaction::TYPE_ICON: case PhabricatorProjectTransaction::TYPE_COLOR: PhabricatorPolicyFilter::requireCapability( @@ -475,22 +467,6 @@ final class PhabricatorProjectTransactionEditor return true; } - protected function extractFilePHIDsFromCustomTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_IMAGE: - $new = $xaction->getNewValue(); - if ($new) { - return array($new); - } - break; - } - - return parent::extractFilePHIDsFromCustomTransaction($object, $xaction); - } - protected function applyFinalEffects( PhabricatorLiskDAO $object, array $xactions) { diff --git a/src/applications/project/storage/PhabricatorProjectTransaction.php b/src/applications/project/storage/PhabricatorProjectTransaction.php index e61ff26277..d99cf4c5ce 100644 --- a/src/applications/project/storage/PhabricatorProjectTransaction.php +++ b/src/applications/project/storage/PhabricatorProjectTransaction.php @@ -3,7 +3,6 @@ final class PhabricatorProjectTransaction extends PhabricatorModularTransaction { - const TYPE_IMAGE = 'project:image'; const TYPE_ICON = 'project:icon'; const TYPE_COLOR = 'project:color'; const TYPE_LOCKED = 'project:locked'; @@ -45,10 +44,6 @@ final class PhabricatorProjectTransaction $rem = array_diff($old, $new); $req_phids = array_merge($add, $rem); break; - case self::TYPE_IMAGE: - $req_phids[] = $old; - $req_phids[] = $new; - break; } return array_merge($req_phids, parent::getRequiredHandlePHIDs()); @@ -106,8 +101,6 @@ final class PhabricatorProjectTransaction } case self::TYPE_ICON: return PhabricatorProjectIconSet::getIconIcon($new); - case self::TYPE_IMAGE: - return 'fa-photo'; case self::TYPE_MEMBERS: return 'fa-user'; } @@ -126,26 +119,6 @@ final class PhabricatorProjectTransaction '%s created this project.', $this->renderHandleLink($author_phid)); - case self::TYPE_IMAGE: - // TODO: Some day, it would be nice to show the images. - if (!$old) { - return pht( - "%s set this project's image to %s.", - $author_handle, - $this->renderHandleLink($new)); - } else if (!$new) { - return pht( - "%s removed this project's image.", - $author_handle); - } else { - return pht( - "%s updated this project's image from %s to %s.", - $author_handle, - $this->renderHandleLink($old), - $this->renderHandleLink($new)); - } - break; - case self::TYPE_ICON: $set = new PhabricatorProjectIconSet(); @@ -253,27 +226,6 @@ final class PhabricatorProjectTransaction $new = $this->getNewValue(); switch ($this->getTransactionType()) { - case self::TYPE_IMAGE: - // TODO: Some day, it would be nice to show the images. - if (!$old) { - return pht( - '%s set the image for %s to %s.', - $author_handle, - $object_handle, - $this->renderHandleLink($new)); - } else if (!$new) { - return pht( - '%s removed the image for %s.', - $author_handle, - $object_handle); - } else { - return pht( - '%s updated the image for %s from %s to %s.', - $author_handle, - $object_handle, - $this->renderHandleLink($old), - $this->renderHandleLink($new)); - } case self::TYPE_ICON: $set = new PhabricatorProjectIconSet(); @@ -313,7 +265,7 @@ final class PhabricatorProjectTransaction switch ($this->getTransactionType()) { case PhabricatorProjectNameTransaction::TRANSACTIONTYPE: case PhabricatorProjectSlugsTransaction::TRANSACTIONTYPE: - case self::TYPE_IMAGE: + case PhabricatorProjectImageTransaction::TRANSACTIONTYPE: case self::TYPE_ICON: case self::TYPE_COLOR: $tags[] = self::MAILTAG_METADATA; diff --git a/src/applications/project/xaction/PhabricatorProjectImageTransaction.php b/src/applications/project/xaction/PhabricatorProjectImageTransaction.php new file mode 100644 index 0000000000..f6d10f0961 --- /dev/null +++ b/src/applications/project/xaction/PhabricatorProjectImageTransaction.php @@ -0,0 +1,136 @@ +getProfileImagePHID(); + } + + public function applyInternalEffects($object, $value) { + $object->setProfileImagePHID($value); + } + + public function applyExternalEffects($object, $value) { + $old = $this->getOldValue(); + $new = $value; + $all = array(); + if ($old) { + $all[] = $old; + } + if ($new) { + $all[] = $new; + } + + $files = id(new PhabricatorFileQuery()) + ->setViewer($this->getActor()) + ->withPHIDs($all) + ->execute(); + $files = mpull($files, null, 'getPHID'); + + $old_file = idx($files, $old); + if ($old_file) { + $old_file->detachFromObject($object->getPHID()); + } + + $new_file = idx($files, $new); + if ($new_file) { + $new_file->attachToObject($object->getPHID()); + } + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + // TODO: Some day, it would be nice to show the images. + if (!$old) { + return pht( + "%s set this project's image to %s.", + $this->renderAuthor(), + $this->renderNewHandle()); + } else if (!$new) { + return pht( + "%s removed this project's image.", + $this->renderAuthor()); + } else { + return pht( + "%s updated this project's image from %s to %s.", + $this->renderAuthor(), + $this->renderOldHandle(), + $this->renderNewHandle()); + } + } + + public function getTitleForFeed() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + // TODO: Some day, it would be nice to show the images. + if (!$old) { + return pht( + '%s set the image for %s to %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderNewHandle()); + } else if (!$new) { + return pht( + '%s removed the image for %s.', + $this->renderAuthor(), + $this->renderObject()); + } else { + return pht( + '%s updated the image for %s from %s to %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderOldHandle(), + $this->renderNewHandle()); + } + } + + public function getIcon() { + return 'fa-photo'; + } + + public function extractFilePHIDs($object, $value) { + if ($value) { + return array($value); + } + return array(); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + $viewer = $this->getActor(); + + foreach ($xactions as $xaction) { + $file_phid = $xaction->getNewValue(); + + // Only validate if file was uploaded + if ($file_phid) { + $file = id(new PhabricatorFileQuery()) + ->setViewer($viewer) + ->withPHIDs(array($file_phid)) + ->executeOne(); + + if (!$file) { + $errors[] = $this->newInvalidError( + pht('"%s" is not a valid file PHID.', + $file_phid)); + } else { + if (!$file->isViewableImage()) { + $mime_type = $file->getMimeType(); + $errors[] = $this->newInvalidError( + pht('File mime type of "%s" is not a valid viewable image.', + $mime_type)); + } + } + } + } + + return $errors; + } + +} diff --git a/src/applications/project/xaction/PhabricatorProjectSlugsTransaction.php b/src/applications/project/xaction/PhabricatorProjectSlugsTransaction.php index a9105e5ba3..d442c39a76 100644 --- a/src/applications/project/xaction/PhabricatorProjectSlugsTransaction.php +++ b/src/applications/project/xaction/PhabricatorProjectSlugsTransaction.php @@ -61,7 +61,6 @@ final class PhabricatorProjectSlugsTransaction count($rem), $this->renderSlugList($rem)); } - break; } public function getTitleForFeed() { From 1bff5309e667a8cf4a60f52492e68aa844b293b9 Mon Sep 17 00:00:00 2001 From: Austin McKinley Date: Thu, 18 May 2017 16:15:24 -0700 Subject: [PATCH 68/81] Migrate Project icons to modular transactions Test Plan: Unit tests pass. Changed some icons, observed expected timeline entries. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: epriestley Differential Revision: https://secure.phabricator.com/D17956 --- src/__phutil_library_map__.php | 2 + .../conduit/ProjectCreateConduitAPIMethod.php | 2 +- .../PhabricatorProjectTransactionEditor.php | 10 +---- .../engine/PhabricatorProjectEditEngine.php | 4 +- .../storage/PhabricatorProjectTransaction.php | 24 +---------- .../PhabricatorProjectIconTransaction.php | 42 +++++++++++++++++++ 6 files changed, 49 insertions(+), 35 deletions(-) create mode 100644 src/applications/project/xaction/PhabricatorProjectIconTransaction.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index d43d756426..2516fd72c6 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3620,6 +3620,7 @@ phutil_register_library_map(array( 'PhabricatorProjectHeraldFieldGroup' => 'applications/project/herald/PhabricatorProjectHeraldFieldGroup.php', 'PhabricatorProjectHovercardEngineExtension' => 'applications/project/engineextension/PhabricatorProjectHovercardEngineExtension.php', 'PhabricatorProjectIconSet' => 'applications/project/icon/PhabricatorProjectIconSet.php', + 'PhabricatorProjectIconTransaction' => 'applications/project/xaction/PhabricatorProjectIconTransaction.php', 'PhabricatorProjectIconsConfigOptionType' => 'applications/project/config/PhabricatorProjectIconsConfigOptionType.php', 'PhabricatorProjectImageTransaction' => 'applications/project/xaction/PhabricatorProjectImageTransaction.php', 'PhabricatorProjectInterface' => 'applications/project/interface/PhabricatorProjectInterface.php', @@ -9035,6 +9036,7 @@ phutil_register_library_map(array( 'PhabricatorProjectHeraldFieldGroup' => 'HeraldFieldGroup', 'PhabricatorProjectHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension', 'PhabricatorProjectIconSet' => 'PhabricatorIconSet', + 'PhabricatorProjectIconTransaction' => 'PhabricatorProjectTransactionType', 'PhabricatorProjectIconsConfigOptionType' => 'PhabricatorConfigJSONOptionType', 'PhabricatorProjectImageTransaction' => 'PhabricatorProjectTransactionType', 'PhabricatorProjectListController' => 'PhabricatorProjectController', diff --git a/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php b/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php index 896cc9a07b..407512de9a 100644 --- a/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php +++ b/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php @@ -52,7 +52,7 @@ final class ProjectCreateConduitAPIMethod extends ProjectConduitAPIMethod { if ($request->getValue('icon')) { $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_ICON) + ->setTransactionType(PhabricatorProjectIconTransaction::TRANSACTIONTYPE) ->setNewValue($request->getValue('icon')); } diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php index 101020970a..915807445a 100644 --- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php @@ -30,7 +30,6 @@ final class PhabricatorProjectTransactionEditor $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; $types[] = PhabricatorTransactions::TYPE_JOIN_POLICY; - $types[] = PhabricatorProjectTransaction::TYPE_ICON; $types[] = PhabricatorProjectTransaction::TYPE_COLOR; $types[] = PhabricatorProjectTransaction::TYPE_LOCKED; $types[] = PhabricatorProjectTransaction::TYPE_PARENT; @@ -48,8 +47,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_ICON: - return $object->getIcon(); case PhabricatorProjectTransaction::TYPE_COLOR: return $object->getColor(); case PhabricatorProjectTransaction::TYPE_LOCKED: @@ -75,7 +72,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_ICON: case PhabricatorProjectTransaction::TYPE_COLOR: case PhabricatorProjectTransaction::TYPE_LOCKED: case PhabricatorProjectTransaction::TYPE_PARENT: @@ -101,9 +97,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_ICON: - $object->setIcon($xaction->getNewValue()); - return; case PhabricatorProjectTransaction::TYPE_COLOR: $object->setColor($xaction->getNewValue()); return; @@ -143,7 +136,6 @@ final class PhabricatorProjectTransactionEditor $new = $xaction->getNewValue(); switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_ICON: case PhabricatorProjectTransaction::TYPE_COLOR: case PhabricatorProjectTransaction::TYPE_LOCKED: case PhabricatorProjectTransaction::TYPE_PARENT: @@ -329,7 +321,7 @@ final class PhabricatorProjectTransactionEditor case PhabricatorProjectNameTransaction::TRANSACTIONTYPE: case PhabricatorProjectStatusTransaction::TRANSACTIONTYPE: case PhabricatorProjectImageTransaction::TRANSACTIONTYPE: - case PhabricatorProjectTransaction::TYPE_ICON: + case PhabricatorProjectIconTransaction::TRANSACTIONTYPE: case PhabricatorProjectTransaction::TYPE_COLOR: PhabricatorPolicyFilter::requireCapability( $this->requireActor(), diff --git a/src/applications/project/engine/PhabricatorProjectEditEngine.php b/src/applications/project/engine/PhabricatorProjectEditEngine.php index b0e3384d4a..a4a97654cd 100644 --- a/src/applications/project/engine/PhabricatorProjectEditEngine.php +++ b/src/applications/project/engine/PhabricatorProjectEditEngine.php @@ -112,7 +112,7 @@ final class PhabricatorProjectEditEngine PhabricatorTransactions::TYPE_VIEW_POLICY, PhabricatorTransactions::TYPE_EDIT_POLICY, PhabricatorTransactions::TYPE_JOIN_POLICY, - PhabricatorProjectTransaction::TYPE_ICON, + PhabricatorProjectIconTransaction::TRANSACTIONTYPE, PhabricatorProjectTransaction::TYPE_COLOR, ); $unavailable = array_fuse($unavailable); @@ -244,7 +244,7 @@ final class PhabricatorProjectEditEngine id(new PhabricatorIconSetEditField()) ->setKey('icon') ->setLabel(pht('Icon')) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_ICON) + ->setTransactionType(PhabricatorProjectIconTransaction::TRANSACTIONTYPE) ->setIconSet(new PhabricatorProjectIconSet()) ->setDescription(pht('Project icon.')) ->setConduitDescription(pht('Change the project icon.')) diff --git a/src/applications/project/storage/PhabricatorProjectTransaction.php b/src/applications/project/storage/PhabricatorProjectTransaction.php index d99cf4c5ce..9f707c3e94 100644 --- a/src/applications/project/storage/PhabricatorProjectTransaction.php +++ b/src/applications/project/storage/PhabricatorProjectTransaction.php @@ -3,7 +3,6 @@ final class PhabricatorProjectTransaction extends PhabricatorModularTransaction { - const TYPE_ICON = 'project:icon'; const TYPE_COLOR = 'project:color'; const TYPE_LOCKED = 'project:locked'; const TYPE_PARENT = 'project:parent'; @@ -99,8 +98,6 @@ final class PhabricatorProjectTransaction } else { return 'fa-unlock'; } - case self::TYPE_ICON: - return PhabricatorProjectIconSet::getIconIcon($new); case self::TYPE_MEMBERS: return 'fa-user'; } @@ -119,15 +116,6 @@ final class PhabricatorProjectTransaction '%s created this project.', $this->renderHandleLink($author_phid)); - case self::TYPE_ICON: - $set = new PhabricatorProjectIconSet(); - - return pht( - "%s set this project's icon to %s.", - $author_handle, - $set->getIconLabel($new)); - break; - case self::TYPE_COLOR: return pht( "%s set this project's color to %s.", @@ -226,16 +214,6 @@ final class PhabricatorProjectTransaction $new = $this->getNewValue(); switch ($this->getTransactionType()) { - - case self::TYPE_ICON: - $set = new PhabricatorProjectIconSet(); - - return pht( - '%s set the icon for %s to %s.', - $author_handle, - $object_handle, - $set->getIconLabel($new)); - case self::TYPE_COLOR: return pht( '%s set the color for %s to %s.', @@ -266,7 +244,7 @@ final class PhabricatorProjectTransaction case PhabricatorProjectNameTransaction::TRANSACTIONTYPE: case PhabricatorProjectSlugsTransaction::TRANSACTIONTYPE: case PhabricatorProjectImageTransaction::TRANSACTIONTYPE: - case self::TYPE_ICON: + case PhabricatorProjectIconTransaction::TRANSACTIONTYPE: case self::TYPE_COLOR: $tags[] = self::MAILTAG_METADATA; break; diff --git a/src/applications/project/xaction/PhabricatorProjectIconTransaction.php b/src/applications/project/xaction/PhabricatorProjectIconTransaction.php new file mode 100644 index 0000000000..932ce4bdc4 --- /dev/null +++ b/src/applications/project/xaction/PhabricatorProjectIconTransaction.php @@ -0,0 +1,42 @@ +getIcon(); + } + + public function applyInternalEffects($object, $value) { + $object->setIcon($value); + } + + public function getTitle() { + $set = new PhabricatorProjectIconSet(); + $new = $this->getNewValue(); + + return pht( + "%s set this project's icon to %s.", + $this->renderAuthor(), + $this->renderValue($set->getIconLabel($new))); + } + + public function getTitleForFeed() { + $set = new PhabricatorProjectIconSet(); + $new = $this->getNewValue(); + + return pht( + '%s set the icon for %s to %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderValue($set->getIconLabel($new))); + } + + public function getIcon() { + $new = $this->getNewValue(); + return PhabricatorProjectIconSet::getIconIcon($new); + } + +} From 7e46d7ab6a6be474b7dd90f9e71ee3bd6ae66fbc Mon Sep 17 00:00:00 2001 From: Austin McKinley Date: Thu, 18 May 2017 16:32:04 -0700 Subject: [PATCH 69/81] Migrate Project color to modular transactions Test Plan: Unit tests + changing project colors. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: epriestley Differential Revision: https://secure.phabricator.com/D17958 --- src/__phutil_library_map__.php | 2 ++ .../conduit/ProjectCreateConduitAPIMethod.php | 6 ++-- .../PhabricatorProjectTransactionEditor.php | 10 +----- .../engine/PhabricatorProjectEditEngine.php | 5 +-- .../storage/PhabricatorProjectTransaction.php | 17 +--------- .../PhabricatorProjectColorTransaction.php | 33 +++++++++++++++++++ 6 files changed, 44 insertions(+), 29 deletions(-) create mode 100644 src/applications/project/xaction/PhabricatorProjectColorTransaction.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 2516fd72c6..cbf324c847 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3584,6 +3584,7 @@ phutil_register_library_map(array( 'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php', 'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php', 'PhabricatorProjectCardView' => 'applications/project/view/PhabricatorProjectCardView.php', + 'PhabricatorProjectColorTransaction' => 'applications/project/xaction/PhabricatorProjectColorTransaction.php', 'PhabricatorProjectColorsConfigOptionType' => 'applications/project/config/PhabricatorProjectColorsConfigOptionType.php', 'PhabricatorProjectColumn' => 'applications/project/storage/PhabricatorProjectColumn.php', 'PhabricatorProjectColumnDetailController' => 'applications/project/controller/PhabricatorProjectColumnDetailController.php', @@ -8987,6 +8988,7 @@ phutil_register_library_map(array( 'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectCardView' => 'AphrontTagView', + 'PhabricatorProjectColorTransaction' => 'PhabricatorProjectTransactionType', 'PhabricatorProjectColorsConfigOptionType' => 'PhabricatorConfigJSONOptionType', 'PhabricatorProjectColumn' => array( 'PhabricatorProjectDAO', diff --git a/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php b/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php index 407512de9a..cbaa006951 100644 --- a/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php +++ b/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php @@ -52,13 +52,15 @@ final class ProjectCreateConduitAPIMethod extends ProjectConduitAPIMethod { if ($request->getValue('icon')) { $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectIconTransaction::TRANSACTIONTYPE) + ->setTransactionType( + PhabricatorProjectIconTransaction::TRANSACTIONTYPE) ->setNewValue($request->getValue('icon')); } if ($request->getValue('color')) { $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_COLOR) + ->setTransactionType( + PhabricatorProjectColorTransaction::TRANSACTIONTYPE) ->setNewValue($request->getValue('color')); } diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php index 915807445a..2f86afc1ac 100644 --- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php @@ -30,7 +30,6 @@ final class PhabricatorProjectTransactionEditor $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; $types[] = PhabricatorTransactions::TYPE_JOIN_POLICY; - $types[] = PhabricatorProjectTransaction::TYPE_COLOR; $types[] = PhabricatorProjectTransaction::TYPE_LOCKED; $types[] = PhabricatorProjectTransaction::TYPE_PARENT; $types[] = PhabricatorProjectTransaction::TYPE_MILESTONE; @@ -47,8 +46,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_COLOR: - return $object->getColor(); case PhabricatorProjectTransaction::TYPE_LOCKED: return (int)$object->getIsMembershipLocked(); case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: @@ -72,7 +69,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_COLOR: case PhabricatorProjectTransaction::TYPE_LOCKED: case PhabricatorProjectTransaction::TYPE_PARENT: case PhabricatorProjectTransaction::TYPE_MILESTONE: @@ -97,9 +93,6 @@ final class PhabricatorProjectTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_COLOR: - $object->setColor($xaction->getNewValue()); - return; case PhabricatorProjectTransaction::TYPE_LOCKED: $object->setIsMembershipLocked($xaction->getNewValue()); return; @@ -136,7 +129,6 @@ final class PhabricatorProjectTransactionEditor $new = $xaction->getNewValue(); switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_COLOR: case PhabricatorProjectTransaction::TYPE_LOCKED: case PhabricatorProjectTransaction::TYPE_PARENT: case PhabricatorProjectTransaction::TYPE_MILESTONE: @@ -322,7 +314,7 @@ final class PhabricatorProjectTransactionEditor case PhabricatorProjectStatusTransaction::TRANSACTIONTYPE: case PhabricatorProjectImageTransaction::TRANSACTIONTYPE: case PhabricatorProjectIconTransaction::TRANSACTIONTYPE: - case PhabricatorProjectTransaction::TYPE_COLOR: + case PhabricatorProjectColorTransaction::TRANSACTIONTYPE: PhabricatorPolicyFilter::requireCapability( $this->requireActor(), $object, diff --git a/src/applications/project/engine/PhabricatorProjectEditEngine.php b/src/applications/project/engine/PhabricatorProjectEditEngine.php index a4a97654cd..cc377bed44 100644 --- a/src/applications/project/engine/PhabricatorProjectEditEngine.php +++ b/src/applications/project/engine/PhabricatorProjectEditEngine.php @@ -113,7 +113,7 @@ final class PhabricatorProjectEditEngine PhabricatorTransactions::TYPE_EDIT_POLICY, PhabricatorTransactions::TYPE_JOIN_POLICY, PhabricatorProjectIconTransaction::TRANSACTIONTYPE, - PhabricatorProjectTransaction::TYPE_COLOR, + PhabricatorProjectColorTransaction::TRANSACTIONTYPE, ); $unavailable = array_fuse($unavailable); @@ -253,7 +253,8 @@ final class PhabricatorProjectEditEngine id(new PhabricatorSelectEditField()) ->setKey('color') ->setLabel(pht('Color')) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_COLOR) + ->setTransactionType( + PhabricatorProjectColorTransaction::TRANSACTIONTYPE) ->setOptions(PhabricatorProjectIconSet::getColorMap()) ->setDescription(pht('Project tag color.')) ->setConduitDescription(pht('Change the project tag color.')) diff --git a/src/applications/project/storage/PhabricatorProjectTransaction.php b/src/applications/project/storage/PhabricatorProjectTransaction.php index 9f707c3e94..99686a91d1 100644 --- a/src/applications/project/storage/PhabricatorProjectTransaction.php +++ b/src/applications/project/storage/PhabricatorProjectTransaction.php @@ -3,7 +3,6 @@ final class PhabricatorProjectTransaction extends PhabricatorModularTransaction { - const TYPE_COLOR = 'project:color'; const TYPE_LOCKED = 'project:locked'; const TYPE_PARENT = 'project:parent'; const TYPE_MILESTONE = 'project:milestone'; @@ -116,13 +115,6 @@ final class PhabricatorProjectTransaction '%s created this project.', $this->renderHandleLink($author_phid)); - case self::TYPE_COLOR: - return pht( - "%s set this project's color to %s.", - $author_handle, - PHUITagView::getShadeName($new)); - break; - case self::TYPE_LOCKED: if ($new) { return pht( @@ -214,13 +206,6 @@ final class PhabricatorProjectTransaction $new = $this->getNewValue(); switch ($this->getTransactionType()) { - case self::TYPE_COLOR: - return pht( - '%s set the color for %s to %s.', - $author_handle, - $object_handle, - PHUITagView::getShadeName($new)); - case self::TYPE_LOCKED: if ($new) { return pht( @@ -245,7 +230,7 @@ final class PhabricatorProjectTransaction case PhabricatorProjectSlugsTransaction::TRANSACTIONTYPE: case PhabricatorProjectImageTransaction::TRANSACTIONTYPE: case PhabricatorProjectIconTransaction::TRANSACTIONTYPE: - case self::TYPE_COLOR: + case PhabricatorProjectColorTransaction::TRANSACTIONTYPE: $tags[] = self::MAILTAG_METADATA; break; case PhabricatorTransactions::TYPE_EDGE: diff --git a/src/applications/project/xaction/PhabricatorProjectColorTransaction.php b/src/applications/project/xaction/PhabricatorProjectColorTransaction.php new file mode 100644 index 0000000000..9f36179b12 --- /dev/null +++ b/src/applications/project/xaction/PhabricatorProjectColorTransaction.php @@ -0,0 +1,33 @@ +getColor(); + } + + public function applyInternalEffects($object, $value) { + $object->setColor($value); + } + + public function getTitle() { + $new = $this->getNewValue(); + return pht( + "%s set this project's color to %s.", + $this->renderAuthor(), + $this->renderValue(PHUITagView::getShadeName($new))); + } + + public function getTitleForFeed() { + $new = $this->getNewValue(); + return pht( + '%s set the color for %s to %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderValue(PHUITagView::getShadeName($new))); + } + +} From 93e28da76eb2f9bdebb734e624545dc0a1b890ee Mon Sep 17 00:00:00 2001 From: Chad Little Date: Fri, 19 May 2017 17:54:53 +0000 Subject: [PATCH 70/81] Add more "disabled" UI to PHUIObjectItemView Summary: Brings more UI tweaks to disabled objects, like projects/people. Also fixes a missing icon in projects. Test Plan: Application search with people and projects that have disabled results. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T12732 Differential Revision: https://secure.phabricator.com/D17962 --- resources/celerity/map.php | 6 +++--- .../people/query/PhabricatorPeopleSearchEngine.php | 1 + .../project/view/PhabricatorProjectListView.php | 2 +- .../rsrc/css/phui/object-item/phui-oi-list-view.css | 11 +++++++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 614824d9c6..9cc404312d 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ return array( 'names' => array( 'conpherence.pkg.css' => 'ff161f2d', 'conpherence.pkg.js' => 'b5b51108', - 'core.pkg.css' => '0ab752b2', + 'core.pkg.css' => 'd6dc3994', 'core.pkg.js' => 'a0c8fb20', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '52b014e7', @@ -131,7 +131,7 @@ return array( 'rsrc/css/phui/object-item/phui-oi-color.css' => 'cd2b9b77', 'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => '08f4ccc3', 'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '9d9685d6', - 'rsrc/css/phui/object-item/phui-oi-list-view.css' => '7c8ec27a', + 'rsrc/css/phui/object-item/phui-oi-list-view.css' => '412bef1a', 'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => 'a8beebea', 'rsrc/css/phui/phui-action-list.css' => 'c01858f4', 'rsrc/css/phui/phui-action-panel.css' => '91c7b835', @@ -869,7 +869,7 @@ return array( 'phui-oi-color-css' => 'cd2b9b77', 'phui-oi-drag-ui-css' => '08f4ccc3', 'phui-oi-flush-ui-css' => '9d9685d6', - 'phui-oi-list-view-css' => '7c8ec27a', + 'phui-oi-list-view-css' => '412bef1a', 'phui-oi-simple-ui-css' => 'a8beebea', 'phui-pager-css' => '77d8a794', 'phui-pinboard-view-css' => '2495140e', diff --git a/src/applications/people/query/PhabricatorPeopleSearchEngine.php b/src/applications/people/query/PhabricatorPeopleSearchEngine.php index e800a8ee1a..fa363c4428 100644 --- a/src/applications/people/query/PhabricatorPeopleSearchEngine.php +++ b/src/applications/people/query/PhabricatorPeopleSearchEngine.php @@ -265,6 +265,7 @@ final class PhabricatorPeopleSearchEngine if ($user->getIsDisabled()) { $item->addIcon('fa-ban', pht('Disabled')); + $item->setDisabled(true); } if (!$is_approval) { diff --git a/src/applications/project/view/PhabricatorProjectListView.php b/src/applications/project/view/PhabricatorProjectListView.php index d1d0792855..7496a401bb 100644 --- a/src/applications/project/view/PhabricatorProjectListView.php +++ b/src/applications/project/view/PhabricatorProjectListView.php @@ -68,7 +68,7 @@ final class PhabricatorProjectListView extends AphrontView { )); if ($project->getStatus() == PhabricatorProjectStatus::STATUS_ARCHIVED) { - $item->addIcon('delete-grey', pht('Archived')); + $item->addIcon('fa-ban', pht('Archived')); $item->setDisabled(true); } diff --git a/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css b/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css index 2f29001f9a..26b0781e8d 100644 --- a/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css +++ b/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css @@ -390,6 +390,17 @@ ul.phui-oi-icons { text-decoration: line-through; } +.phui-oi.phui-oi-disabled .phui-oi-image { + opacity: .8; + -webkit-filter: grayscale(100%); + filter: grayscale(100%); +} + +.phui-oi.phui-oi-disabled .phui-oi-attribute, +.phui-oi.phui-oi-disabled .phui-oi-attribute .phui-icon-view { + color: {$lightgreytext}; +} + /* - Effects ------------------------------------------------------------------- From d783299a19c536d79bc4e73a5b1f6c1942ad2d05 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Fri, 19 May 2017 17:57:38 +0000 Subject: [PATCH 71/81] Fix Phriction status not set property on new document Summary: I deleted too many lines of code here and TYPE_MOVE was always being applied when CONTENT was set. This should fix on next document save, but should I write some migration tool anyways? Test Plan: Create a new document, see document with correct status. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17960 --- .../phriction/editor/PhrictionTransactionEditor.php | 4 +++- .../phriction/xaction/PhrictionDocumentTitleTransaction.php | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/applications/phriction/editor/PhrictionTransactionEditor.php b/src/applications/phriction/editor/PhrictionTransactionEditor.php index cbe6e426d0..1f97dd3c54 100644 --- a/src/applications/phriction/editor/PhrictionTransactionEditor.php +++ b/src/applications/phriction/editor/PhrictionTransactionEditor.php @@ -164,6 +164,8 @@ final class PhrictionTransactionEditor switch ($xaction->getTransactionType()) { case PhrictionTransaction::TYPE_CONTENT: + $object->setStatus(PhrictionDocumentStatus::STATUS_EXISTS); + return; case PhrictionTransaction::TYPE_MOVE_AWAY: $object->setStatus(PhrictionDocumentStatus::STATUS_MOVED); return; @@ -241,10 +243,10 @@ final class PhrictionTransactionEditor foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: + case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: case PhrictionTransaction::TYPE_CONTENT: case PhrictionTransaction::TYPE_DELETE: case PhrictionTransaction::TYPE_MOVE_AWAY: - case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: $save_content = true; break; default: diff --git a/src/applications/phriction/xaction/PhrictionDocumentTitleTransaction.php b/src/applications/phriction/xaction/PhrictionDocumentTitleTransaction.php index 730b062f34..4f1ba850a7 100644 --- a/src/applications/phriction/xaction/PhrictionDocumentTitleTransaction.php +++ b/src/applications/phriction/xaction/PhrictionDocumentTitleTransaction.php @@ -14,6 +14,9 @@ final class PhrictionDocumentTitleTransaction public function applyInternalEffects($object, $value) { $object->setStatus(PhrictionDocumentStatus::STATUS_EXISTS); + } + + public function applyExternalEffects($object, $value) { $this->getEditor()->getNewContent()->setTitle($value); } From 6c46f27d98c48e1f57109677acaea34dab243516 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 18 May 2017 11:54:58 -0700 Subject: [PATCH 72/81] Add quest objectives to the minimap Summary: Add important objectives (like waygates and quest markers) to the minimap. This also probably fixes @cspeckmim's bug with the {key @} keyboard shortcut. Test Plan: (This is probably easier to undestand if you `arc patch` + click around.) {F4966037} Reviewers: chad, amckinley Reviewed By: chad Subscribers: cspeckmim Differential Revision: https://secure.phabricator.com/D17955 --- resources/celerity/map.php | 116 +++++++++------ resources/celerity/packages.php | 3 + .../view/DifferentialChangesetDetailView.php | 2 + .../view/PHUIDiffInlineCommentDetailView.php | 11 ++ .../differential/changeset-view.css | 29 ++++ .../rsrc/js/application/diff/DiffChangeset.js | 68 ++++++++- .../js/application/diff/DiffChangesetList.js | 29 +++- .../rsrc/js/application/diff/DiffInline.js | 78 +++++++++- .../js/application/diff/ScrollObjective.js | 121 +++++++++++++++ .../application/diff/ScrollObjectiveList.js | 139 ++++++++++++++++++ .../differential/behavior-comment-preview.js | 2 + .../behavior-show-older-transactions.js | 1 + 12 files changed, 542 insertions(+), 57 deletions(-) create mode 100644 webroot/rsrc/js/application/diff/ScrollObjective.js create mode 100644 webroot/rsrc/js/application/diff/ScrollObjectiveList.js diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 9cc404312d..5c1839de41 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -10,10 +10,10 @@ return array( 'conpherence.pkg.css' => 'ff161f2d', 'conpherence.pkg.js' => 'b5b51108', 'core.pkg.css' => 'd6dc3994', - 'core.pkg.js' => 'a0c8fb20', + 'core.pkg.js' => 'e822b496', 'darkconsole.pkg.js' => '1f9a31bc', - 'differential.pkg.css' => '52b014e7', - 'differential.pkg.js' => '1efe85bf', + 'differential.pkg.css' => 'deae6388', + 'differential.pkg.js' => 'dedee9c8', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -64,7 +64,7 @@ return array( 'rsrc/css/application/dashboard/dashboard.css' => 'fe5b1869', 'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a', 'rsrc/css/application/differential/add-comment.css' => 'c47f8c40', - 'rsrc/css/application/differential/changeset-view.css' => 'e7bd2a79', + 'rsrc/css/application/differential/changeset-view.css' => '6b79bdf3', 'rsrc/css/application/differential/core.css' => '5b7b8ff4', 'rsrc/css/application/differential/phui-inline-comment.css' => 'ffd1a542', 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', @@ -390,11 +390,13 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => '3268dd83', - 'rsrc/js/application/diff/DiffChangesetList.js' => '0a4f7809', - 'rsrc/js/application/diff/DiffInline.js' => '3337c065', + 'rsrc/js/application/diff/DiffChangeset.js' => '20580ec0', + 'rsrc/js/application/diff/DiffChangesetList.js' => '61086d73', + 'rsrc/js/application/diff/DiffInline.js' => '77e14b60', + 'rsrc/js/application/diff/ScrollObjective.js' => '0eee7a00', + 'rsrc/js/application/diff/ScrollObjectiveList.js' => '1ca4d9db', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', - 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', + 'rsrc/js/application/differential/behavior-comment-preview.js' => '51c5ad07', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', 'rsrc/js/application/differential/behavior-populate.js' => '5e41c819', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', @@ -446,7 +448,7 @@ return array( 'rsrc/js/application/transactions/behavior-comment-actions.js' => '9a6dd75c', 'rsrc/js/application/transactions/behavior-reorder-configs.js' => 'd7a74243', 'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96', - 'rsrc/js/application/transactions/behavior-show-older-transactions.js' => '94c65b72', + 'rsrc/js/application/transactions/behavior-show-older-transactions.js' => 'ae95d984', 'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => 'b23b49e6', 'rsrc/js/application/transactions/behavior-transaction-list.js' => '1f6794f6', 'rsrc/js/application/typeahead/behavior-typeahead-browse.js' => '635de1ec', @@ -565,7 +567,7 @@ return array( 'conpherence-thread-manager' => '4d863052', 'conpherence-transaction-css' => '85129c68', 'd3' => 'a11a5ff2', - 'differential-changeset-view-css' => 'e7bd2a79', + 'differential-changeset-view-css' => '6b79bdf3', 'differential-core-view-css' => '5b7b8ff4', 'differential-revision-add-comment-css' => 'c47f8c40', 'differential-revision-comment-css' => '14b8565a', @@ -617,7 +619,7 @@ return array( 'javelin-behavior-device' => 'bb1dd507', 'javelin-behavior-diff-preview-link' => '051c7832', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', - 'javelin-behavior-differential-feedback-preview' => 'b064af76', + 'javelin-behavior-differential-feedback-preview' => '51c5ad07', 'javelin-behavior-differential-populate' => '5e41c819', 'javelin-behavior-differential-user-select' => 'a8d8459d', 'javelin-behavior-diffusion-browse-file' => '054a0f0b', @@ -665,7 +667,7 @@ return array( 'javelin-behavior-phabricator-remarkup-assist' => 'acd29eee', 'javelin-behavior-phabricator-reveal-content' => '60821bc7', 'javelin-behavior-phabricator-search-typeahead' => 'eded9ee8', - 'javelin-behavior-phabricator-show-older-transactions' => '94c65b72', + 'javelin-behavior-phabricator-show-older-transactions' => 'ae95d984', 'javelin-behavior-phabricator-tooltips' => 'c420b0b9', 'javelin-behavior-phabricator-transaction-comment-form' => 'b23b49e6', 'javelin-behavior-phabricator-transaction-list' => '1f6794f6', @@ -775,9 +777,9 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => '3268dd83', - 'phabricator-diff-changeset-list' => '0a4f7809', - 'phabricator-diff-inline' => '3337c065', + 'phabricator-diff-changeset' => '20580ec0', + 'phabricator-diff-changeset-list' => '61086d73', + 'phabricator-diff-inline' => '77e14b60', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -797,6 +799,8 @@ return array( 'phabricator-phtize' => 'd254d646', 'phabricator-prefab' => 'c5af80a2', 'phabricator-remarkup-css' => 'd1a5e11e', + 'phabricator-scroll-objective' => '0eee7a00', + 'phabricator-scroll-objective-list' => '1ca4d9db', 'phabricator-search-results-css' => 'f87d23ad', 'phabricator-shaped-request' => '7cbe244b', 'phabricator-slowvote-css' => 'a94b7230', @@ -973,8 +977,12 @@ return array( 'javelin-dom', 'javelin-router', ), - '0a4f7809' => array( + '0eee7a00' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', 'javelin-install', + 'javelin-workflow', ), '0f764c35' => array( 'javelin-install', @@ -1026,6 +1034,14 @@ return array( 'javelin-request', 'javelin-uri', ), + '1ca4d9db' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'phabricator-scroll-objective', + ), '1def2711' => array( 'javelin-install', 'javelin-dom', @@ -1054,6 +1070,17 @@ return array( 'javelin-install', 'javelin-dom', ), + '20580ec0' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + 'phabricator-diff-inline', + ), '21df4ff5' => array( 'javelin-install', 'javelin-workboard-card', @@ -1112,26 +1139,12 @@ return array( 'javelin-dom', 'javelin-vector', ), - '3268dd83' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - 'phabricator-diff-inline', - ), '327a00d1' => array( 'javelin-behavior', 'javelin-stratcom', 'javelin-dom', 'javelin-workflow', ), - '3337c065' => array( - 'javelin-dom', - ), '3ab51e2c' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -1295,6 +1308,14 @@ return array( 'javelin-dom', 'javelin-reactor-dom', ), + '51c5ad07' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + 'javelin-request', + 'javelin-util', + 'phabricator-shaped-request', + ), '522431f7' => array( 'javelin-behavior', 'javelin-util', @@ -1386,6 +1407,10 @@ return array( 'javelin-stratcom', 'javelin-dom', ), + '61086d73' => array( + 'javelin-install', + 'phabricator-scroll-objective-list', + ), '61cbc29a' => array( 'javelin-magical-init', 'javelin-util', @@ -1417,6 +1442,9 @@ return array( 'javelin-util', 'javelin-magical-init', ), + '6b79bdf3' => array( + 'phui-inline-comment-view-css', + ), '6b8ef10b' => array( 'javelin-install', ), @@ -1469,6 +1497,9 @@ return array( 'javelin-reactor', 'javelin-util', ), + '77e14b60' => array( + 'javelin-dom', + ), '782ab6e7' => array( 'javelin-behavior', 'javelin-dom', @@ -1629,12 +1660,6 @@ return array( 'javelin-resource', 'javelin-routable', ), - '94c65b72' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - 'phabricator-busy', - ), '960f6a39' => array( 'javelin-behavior', 'javelin-dom', @@ -1776,20 +1801,18 @@ return array( 'phuix-autocomplete', 'javelin-mask', ), + 'ae95d984' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + 'phabricator-busy', + ), 'b003d4fb' => array( 'javelin-behavior', 'javelin-stratcom', 'javelin-dom', 'phuix-dropdown-menu', ), - 'b064af76' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - 'javelin-request', - 'javelin-util', - 'phabricator-shaped-request', - ), 'b1f0ccee' => array( 'javelin-install', 'javelin-dom', @@ -2124,9 +2147,6 @@ return array( 'javelin-workflow', 'javelin-magical-init', ), - 'e7bd2a79' => array( - 'phui-inline-comment-view-css', - ), 'e9581f08' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2412,6 +2432,8 @@ return array( 'javelin-behavior-load-blame', 'javelin-behavior-differential-user-select', 'javelin-behavior-aphront-more', + 'phabricator-scroll-objective', + 'phabricator-scroll-objective-list', 'phabricator-diff-inline', 'phabricator-diff-changeset', 'phabricator-diff-changeset-list', diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php index e756e696cb..afa6c456a6 100644 --- a/resources/celerity/packages.php +++ b/resources/celerity/packages.php @@ -203,6 +203,9 @@ return array( 'javelin-behavior-differential-user-select', 'javelin-behavior-aphront-more', + 'phabricator-scroll-objective', + 'phabricator-scroll-objective-list', + 'phabricator-diff-inline', 'phabricator-diff-changeset', 'phabricator-diff-changeset-list', diff --git a/src/applications/differential/view/DifferentialChangesetDetailView.php b/src/applications/differential/view/DifferentialChangesetDetailView.php index 86fba06c40..123fd71f99 100644 --- a/src/applications/differential/view/DifferentialChangesetDetailView.php +++ b/src/applications/differential/view/DifferentialChangesetDetailView.php @@ -184,6 +184,8 @@ final class DifferentialChangesetDetailView extends AphrontView { 'loaded' => $this->getLoaded(), 'undoTemplates' => hsprintf('%s', $renderer->renderUndoTemplates()), 'path' => $display_filename, + 'objectiveName' => basename($display_filename), + 'icon' => 'fa-file-text-o', ), 'class' => $class, 'id' => $id, diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php index 91f27f0e2b..9fc1e1bded 100644 --- a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php +++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php @@ -99,6 +99,14 @@ final class PHUIDiffInlineCommentDetailView 'differential-inline-comment', ); + $is_fixed = false; + switch ($inline->getFixedState()) { + case PhabricatorInlineCommentInterface::STATE_DONE: + case PhabricatorInlineCommentInterface::STATE_DRAFT: + $is_fixed = true; + break; + } + $metadata = array( 'id' => $inline->getID(), 'phid' => $inline->getPHID(), @@ -109,6 +117,9 @@ final class PHUIDiffInlineCommentDetailView 'on_right' => $this->getIsOnRight(), 'original' => $inline->getContent(), 'replyToCommentPHID' => $inline->getReplyToCommentPHID(), + 'isDraft' => $inline->isDraft(), + 'isFixed' => $is_fixed, + 'isGhost' => $inline->getIsGhost(), ); $sigil = 'differential-inline-comment'; diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index 1931ce354b..3d4ff2eb48 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -404,3 +404,32 @@ tr.differential-inline-loading { .diff-banner .phui-icon-view { margin-right: 4px; } + +.scroll-objective-list { + position: fixed; + right: 0; + width: 24px; + top: 48px; + bottom: 48px; + z-index: 6; + background: rgba(255, 255, 255, 0.50); + border-style: solid; + border-color: rgba(255, 255, 255, 0.95); + border-width: 1px 0 1px 1px; + box-shadow: -1px 0 2px rgba(255, 255, 255, 0.25); + overflow: hidden; +} + +.scroll-objective { + display: block; + position: absolute; + box-sizing: border-box; + cursor: pointer; + left: 8px; +} + +.scroll-objective .phui-icon-view { + text-shadow: 1px 1px 4px rgba(0, 0, 0, 0.25); + display: block; + height: 14px; +} diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index 148be860b9..28e19b6276 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -32,14 +32,12 @@ JX.install('DiffChangeset', { this._rightID = data.right; this._path = data.path; + this._objectiveName = data.objectiveName; + this._icon = data.icon; this._inlines = []; }, - properties: { - changesetList: null - }, - members: { _node: null, _loaded: false, @@ -63,6 +61,11 @@ JX.install('DiffChangeset', { _undoNode: null, _path: null, + _changesetList: null, + _objective: null, + _objectiveName: null, + _icon: null, + getLeftChangesetID: function() { return this._leftID; }, @@ -71,6 +74,49 @@ JX.install('DiffChangeset', { return this._rightID; }, + setChangesetList: function(list) { + this._changesetList = list; + + var objectives = list.getObjectives(); + this._objective = objectives.newObjective() + .setAnchor(this._node); + + this._updateObjective(); + + return this; + }, + + _updateObjective: function() { + this._objective + .setIcon(this.getIcon()) + .setColor(this.getColor()) + .setTooltip(this.getObjectiveName()); + }, + + getIcon: function() { + if (!this._visible) { + return 'fa-file-o'; + } + + return this._icon; + }, + + getColor: function() { + if (!this._visible) { + return 'grey'; + } + + return 'blue'; + }, + + getObjectiveName: function() { + return this._objectiveName; + }, + + getChangesetList: function() { + return this._changesetList; + }, + /** * Has the content of this changeset been loaded? * @@ -523,6 +569,11 @@ JX.install('DiffChangeset', { } JX.Stratcom.invoke('differential-inline-comment-refresh'); + + this._objective.show(); + this._rebuildAllInlines(); + + JX.Stratcom.invoke('resize'); }, _getContentFrame: function() { @@ -672,9 +723,18 @@ JX.install('DiffChangeset', { JX.DOM.appendContent(diff.parentNode, undo); } + this._updateObjective(); + for (var ii = 0; ii < this._inlines.length; ii++) { + this._inlines[ii].updateObjective(); + } + JX.Stratcom.invoke('resize'); }, + isVisible: function() { + return this._visible; + }, + _getUndoNode: function() { if (!this._undoNode) { var pht = this.getChangesetList().getTranslations(); diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index 2798d0d1c6..f8ca3b9a56 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -1,6 +1,7 @@ /** * @provides phabricator-diff-changeset-list * @requires javelin-install + * phabricator-scroll-objective-list * @javelin */ @@ -8,6 +9,7 @@ JX.install('DiffChangesetList', { construct: function() { this._changesets = []; + this._objectives = new JX.ScrollObjectiveList(); var onload = JX.bind(this, this._ifawake, this._onload); JX.Stratcom.listen('click', 'differential-load', onload); @@ -100,6 +102,7 @@ JX.install('DiffChangesetList', { _initialized: false, _asleep: true, _changesets: null, + _objectives: null, _cursorItem: null, @@ -124,6 +127,8 @@ JX.install('DiffChangesetList', { this._redrawFocus(); this._redrawSelection(); this.resetHover(); + + this._objectives.hide(); }, wake: function() { @@ -132,6 +137,8 @@ JX.install('DiffChangesetList', { this._redrawFocus(); this._redrawSelection(); + this._objectives.show(); + if (this._initialized) { return; } @@ -192,6 +199,10 @@ JX.install('DiffChangesetList', { return this._asleep; }, + getObjectives: function() { + return this._objectives; + }, + newChangesetForNode: function(node) { var changeset = JX.DiffChangeset.getForNode(node); @@ -273,6 +284,18 @@ JX.install('DiffChangesetList', { manager.scrollTo(toc); }, + getSelectedInline: function() { + var cursor = this._cursorItem; + + if (cursor) { + if (cursor.type == 'comment') { + return cursor.target; + } + } + + return null; + }, + _onkeyreply: function(is_quote) { var cursor = this._cursorItem; @@ -772,7 +795,7 @@ JX.install('DiffChangesetList', { } else if (diffs.length == 1) { var diff = diffs[0]; visible_item.setDisabled(false); - if (JX.Stratcom.getData(diff).hidden) { + if (!changeset.isVisible()) { visible_item .setName(pht('Expand File')) .setIcon('fa-expand'); @@ -836,6 +859,10 @@ JX.install('DiffChangesetList', { // event. e.kill(); + this.selectInline(inline); + }, + + selectInline: function(inline) { var selection = this._getSelectionState(); var item; diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js index b46bc00b4f..e66fb544c5 100644 --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -9,10 +9,6 @@ JX.install('DiffInline', { construct : function() { }, - properties: { - changeset: null - }, - members: { _id: null, _phid: null, @@ -31,12 +27,18 @@ JX.install('DiffInline', { _isInvisible: false, _isLoading: false, + _changeset: null, + _objective: null, + + _isDraft: null, + _isFixed: null, + bindToRow: function(row) { this._row = row; + this._objective.setAnchor(this._row); var row_data = JX.Stratcom.getData(row); row_data.inline = this; - this._hidden = row_data.hidden || false; // TODO: Get smarter about this once we do more editing, this is pretty @@ -65,6 +67,13 @@ JX.install('DiffInline', { this._replyToCommentPHID = data.replyToCommentPHID; + this._isDraft = data.isDraft; + this._isFixed = data.isFixed; + this._isGhost = data.isGhost; + + this._changesetID = data.changesetID; + + this.updateObjective(); this.setInvisible(false); return this; @@ -152,6 +161,60 @@ JX.install('DiffInline', { return this; }, + setChangeset: function(changeset) { + this._changeset = changeset; + + var objectives = changeset.getChangesetList().getObjectives(); + this._objective = objectives.newObjective() + .setCallback(JX.bind(this, this._onobjective)); + this.updateObjective(); + + return this; + }, + + getChangeset: function() { + return this._changeset; + }, + + _onobjective: function() { + this.getChangeset().getChangesetList().selectInline(this); + }, + + updateObjective: function() { + var objective = this._objective; + + if (this.isHidden() || this._isDeleted) { + objective.hide(); + return; + } + + var changeset = this.getChangeset(); + if (!changeset.isVisible()) { + objective.hide(); + return; + } + + var icon = 'fa-comment'; + var color = 'bluegrey'; + + if (this._isDraft) { + // This inline is an unsubmitted draft. + icon = 'fa-pencil'; + } else if (this._isFixed) { + // This inline has been marked done. + icon = 'fa-check'; + color = 'grey'; + } else if (this._isGhost) { + icon = 'fa-comment-o'; + color = 'grey'; + } + + objective + .setIcon(icon) + .setColor(color) + .show(); + }, + canReply: function() { if (!this._hasAction('reply')) { return false; @@ -202,6 +265,7 @@ JX.install('DiffInline', { JX.Stratcom.getData(row).inline = this; this._row = row; + this._objective.setAnchor(this._row); this._id = null; this._phid = null; @@ -272,6 +336,8 @@ JX.install('DiffInline', { // top-level "draft" state of unsubmitted comments. JX.DOM.alterClass(comment, 'inline-state-is-draft', response.draftState); + this._isFixed = response.isChecked; + this._didUpdate(); }, @@ -633,6 +699,8 @@ JX.install('DiffInline', { this.getChangeset().getChangesetList().redrawPreview(); } + this.updateObjective(); + this.getChangeset().getChangesetList().redrawCursor(); this.getChangeset().getChangesetList().resetHover(); diff --git a/webroot/rsrc/js/application/diff/ScrollObjective.js b/webroot/rsrc/js/application/diff/ScrollObjective.js new file mode 100644 index 0000000000..4e6fa55d6a --- /dev/null +++ b/webroot/rsrc/js/application/diff/ScrollObjective.js @@ -0,0 +1,121 @@ +/** + * @provides phabricator-scroll-objective + * @requires javelin-dom + * javelin-util + * javelin-stratcom + * javelin-install + * javelin-workflow + * @javelin + */ + + +JX.install('ScrollObjective', { + + construct : function() { + var node = this.getNode(); + + var onclick = JX.bind(this, this._onclick); + JX.DOM.listen(node, 'click', null, onclick); + }, + + members: { + _list: null, + + _node: null, + _anchor: null, + + _visible: false, + _callback: false, + + getNode: function() { + if (!this._node) { + var attributes = { + className: 'scroll-objective' + }; + + var content = this._getIconObject().getNode(); + + var node = JX.$N('div', attributes, content); + + this._node = node; + } + + return this._node; + }, + + setCallback: function(callback) { + this._callback = callback; + return this; + }, + + setObjectiveList: function(list) { + this._list = list; + return this; + }, + + _getIconObject: function() { + if (!this._iconObject) { + this._iconObject = new JX.PHUIXIconView(); + } + return this._iconObject; + }, + + _onclick: function(e) { + (this._callback && this._callback(e)); + + if (e.getPrevented()) { + return; + } + + e.kill(); + + // This is magic to account for the banner, and should probably be made + // less hard-coded. + var buffer = 48; + + JX.DOM.scrollToPosition(null, JX.$V(this.getAnchor()).y - buffer); + }, + + setAnchor: function(node) { + this._anchor = node; + return this; + }, + + getAnchor: function() { + return this._anchor; + }, + + setIcon: function(icon) { + this._getIconObject().setIcon(icon); + return this; + }, + + setColor: function(color) { + this._getIconObject().setColor(color); + return this; + }, + + setTooltip: function(tip) { + var node = this._getIconObject().getNode(); + JX.Stratcom.addSigil(node, 'has-tooltip'); + JX.Stratcom.getData(node).tip = tip; + JX.Stratcom.getData(node).align = 'W'; + return this; + }, + + show: function() { + this._visible = true; + return this; + }, + + hide: function() { + this._visible = false; + }, + + isVisible: function() { + return this._visible; + } + + } + +}); diff --git a/webroot/rsrc/js/application/diff/ScrollObjectiveList.js b/webroot/rsrc/js/application/diff/ScrollObjectiveList.js new file mode 100644 index 0000000000..f5beb751e6 --- /dev/null +++ b/webroot/rsrc/js/application/diff/ScrollObjectiveList.js @@ -0,0 +1,139 @@ +/** + * @provides phabricator-scroll-objective-list + * @requires javelin-dom + * javelin-util + * javelin-stratcom + * javelin-install + * javelin-workflow + * phabricator-scroll-objective + * @javelin + */ + + +JX.install('ScrollObjectiveList', { + + construct : function() { + this._objectives = []; + + var onresize = JX.bind(this, this._dirty); + JX.Stratcom.listen('resize', null, onresize); + }, + + members: { + _objectives: null, + _visible: false, + _trigger: null, + + newObjective: function() { + var objective = new JX.ScrollObjective() + .setObjectiveList(this); + + this._objectives.push(objective); + this._getNode().appendChild(objective.getNode()); + + this._dirty(); + + return objective; + }, + + show: function() { + this._visible = true; + this._dirty(); + return this; + }, + + hide: function() { + this._visible = false; + this._dirty(); + return this; + }, + + _getNode: function() { + if (!this._node) { + var node = new JX.$N('div', {className: 'scroll-objective-list'}); + this._node = node; + } + return this._node; + }, + + _dirty: function() { + if (this._trigger !== null) { + return; + } + + this._trigger = setTimeout(JX.bind(this, this._redraw), 0); + }, + + _redraw: function() { + this._trigger = null; + + var node = this._getNode(); + + var is_visible = + (this._visible) && + (JX.Device.getDevice() == 'desktop') && + (this._objectives.length); + + if (!is_visible) { + JX.DOM.remove(node); + return; + } + + document.body.appendChild(node); + + var d = JX.Vector.getDocument(); + + var list_dimensions = JX.Vector.getDim(node); + var icon_height = 16; + var list_y = (list_dimensions.y - icon_height); + + var ii; + var offset; + + // First, build a list of all the items we're going to show. + var items = []; + for (ii = 0; ii < this._objectives.length; ii++) { + var objective = this._objectives[ii]; + var objective_node = objective.getNode(); + + var anchor = objective.getAnchor(); + if (!anchor || !objective.isVisible()) { + JX.DOM.remove(objective_node); + continue; + } + + offset = (JX.$V(anchor).y / d.y) * (list_y); + + items.push({ + offset: offset, + node: objective_node + }); + } + + // Now, sort it from top to bottom. + items.sort(function(u, v) { + return u.offset - v.offset; + }); + + // Lay out the items in the objective list, leaving a minimum amount + // of space between them so they do not overlap. + var min = null; + for (ii = 0; ii < items.length; ii++) { + var item = items[ii]; + + offset = item.offset; + + if (min !== null) { + offset = Math.max(offset, min); + } + min = offset + 15; + + item.node.style.top = offset + 'px'; + node.appendChild(item.node); + } + + } + + } + +}); diff --git a/webroot/rsrc/js/application/differential/behavior-comment-preview.js b/webroot/rsrc/js/application/differential/behavior-comment-preview.js index 283e6ea12c..beb9f9a5d9 100644 --- a/webroot/rsrc/js/application/differential/behavior-comment-preview.js +++ b/webroot/rsrc/js/application/differential/behavior-comment-preview.js @@ -74,6 +74,8 @@ JX.behavior('differential-feedback-preview', function(config) { }); updateLinks(); + + JX.Stratcom.invoke('resize'); }) .setTimeout(5000) .send(); diff --git a/webroot/rsrc/js/application/transactions/behavior-show-older-transactions.js b/webroot/rsrc/js/application/transactions/behavior-show-older-transactions.js index b4dcd2975b..ab4bf768d8 100644 --- a/webroot/rsrc/js/application/transactions/behavior-show-older-transactions.js +++ b/webroot/rsrc/js/application/transactions/behavior-show-older-transactions.js @@ -60,6 +60,7 @@ JX.behavior('phabricator-show-older-transactions', function(config) { var show_older = function(swap, r) { JX.DOM.replace(swap, JX.$H(r.timeline).getFragment()); + JX.Stratcom.invoke('resize'); }; var load_hidden_hash_callback = function(swap, r) { From fdf00f6df475ff33268ee245fc1517a138999df2 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 18 May 2017 16:31:16 -0700 Subject: [PATCH 73/81] Clean up some minor UI behaviors in Differential Summary: Minor UI tweaks: - Use the dynamic icon for each file (e.g., image, text), not a hard-coded icon. - Render the path (less important) in grey and the filename (more important) in black. Test Plan: {F4966176} Reviewers: chad Reviewed By: chad Differential Revision: https://secure.phabricator.com/D17957 --- resources/celerity/map.php | 58 +++++++++---------- .../view/DifferentialChangesetDetailView.php | 24 +++++++- .../differential/changeset-view.css | 5 +- webroot/rsrc/css/core/z-index.css | 4 ++ .../rsrc/js/application/diff/DiffChangeset.js | 8 +-- .../js/application/diff/DiffChangesetList.js | 4 +- 6 files changed, 65 insertions(+), 38 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 5c1839de41..e50d8bbd1a 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,11 +9,11 @@ return array( 'names' => array( 'conpherence.pkg.css' => 'ff161f2d', 'conpherence.pkg.js' => 'b5b51108', - 'core.pkg.css' => 'd6dc3994', + 'core.pkg.css' => '5ffe8b79', 'core.pkg.js' => 'e822b496', 'darkconsole.pkg.js' => '1f9a31bc', - 'differential.pkg.css' => 'deae6388', - 'differential.pkg.js' => 'dedee9c8', + 'differential.pkg.css' => '4d7dd14e', + 'differential.pkg.js' => '68a4fa60', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -64,7 +64,7 @@ return array( 'rsrc/css/application/dashboard/dashboard.css' => 'fe5b1869', 'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a', 'rsrc/css/application/differential/add-comment.css' => 'c47f8c40', - 'rsrc/css/application/differential/changeset-view.css' => '6b79bdf3', + 'rsrc/css/application/differential/changeset-view.css' => '54774a28', 'rsrc/css/application/differential/core.css' => '5b7b8ff4', 'rsrc/css/application/differential/phui-inline-comment.css' => 'ffd1a542', 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', @@ -116,7 +116,7 @@ return array( 'rsrc/css/core/core.css' => '9f4cb463', 'rsrc/css/core/remarkup.css' => 'd1a5e11e', 'rsrc/css/core/syntax.css' => 'cae95e89', - 'rsrc/css/core/z-index.css' => '9d8f7c4b', + 'rsrc/css/core/z-index.css' => '998f3ce1', 'rsrc/css/diviner/diviner-shared.css' => '896f1d43', 'rsrc/css/font/font-awesome.css' => 'e838e088', 'rsrc/css/font/font-lato.css' => 'c7ccd872', @@ -390,8 +390,8 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => '20580ec0', - 'rsrc/js/application/diff/DiffChangesetList.js' => '61086d73', + 'rsrc/js/application/diff/DiffChangeset.js' => 'cf4e2140', + 'rsrc/js/application/diff/DiffChangesetList.js' => '5c68c40c', 'rsrc/js/application/diff/DiffInline.js' => '77e14b60', 'rsrc/js/application/diff/ScrollObjective.js' => '0eee7a00', 'rsrc/js/application/diff/ScrollObjectiveList.js' => '1ca4d9db', @@ -567,7 +567,7 @@ return array( 'conpherence-thread-manager' => '4d863052', 'conpherence-transaction-css' => '85129c68', 'd3' => 'a11a5ff2', - 'differential-changeset-view-css' => '6b79bdf3', + 'differential-changeset-view-css' => '54774a28', 'differential-core-view-css' => '5b7b8ff4', 'differential-revision-add-comment-css' => 'c47f8c40', 'differential-revision-comment-css' => '14b8565a', @@ -777,8 +777,8 @@ return array( 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => '20580ec0', - 'phabricator-diff-changeset-list' => '61086d73', + 'phabricator-diff-changeset' => 'cf4e2140', + 'phabricator-diff-changeset-list' => '5c68c40c', 'phabricator-diff-inline' => '77e14b60', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', @@ -820,7 +820,7 @@ return array( 'phabricator-uiexample-reactor-select' => 'a155550f', 'phabricator-uiexample-reactor-sendclass' => '1def2711', 'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee', - 'phabricator-zindex-css' => '9d8f7c4b', + 'phabricator-zindex-css' => '998f3ce1', 'phame-css' => 'b3a0b3a3', 'pholio-css' => 'ca89d380', 'pholio-edit-css' => '07676f51', @@ -1070,17 +1070,6 @@ return array( 'javelin-install', 'javelin-dom', ), - '20580ec0' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - 'phabricator-diff-inline', - ), '21df4ff5' => array( 'javelin-install', 'javelin-workboard-card', @@ -1327,6 +1316,9 @@ return array( '5294060f' => array( 'phui-theme-css', ), + '54774a28' => array( + 'phui-inline-comment-view-css', + ), '54b612ba' => array( 'javelin-color', 'javelin-install', @@ -1376,6 +1368,10 @@ return array( 'javelin-stratcom', 'javelin-dom', ), + '5c68c40c' => array( + 'javelin-install', + 'phabricator-scroll-objective-list', + ), '5e2634b9' => array( 'javelin-behavior', 'javelin-aphlict', @@ -1407,10 +1403,6 @@ return array( 'javelin-stratcom', 'javelin-dom', ), - '61086d73' => array( - 'javelin-install', - 'phabricator-scroll-objective-list', - ), '61cbc29a' => array( 'javelin-magical-init', 'javelin-util', @@ -1442,9 +1434,6 @@ return array( 'javelin-util', 'javelin-magical-init', ), - '6b79bdf3' => array( - 'phui-inline-comment-view-css', - ), '6b8ef10b' => array( 'javelin-install', ), @@ -2013,6 +2002,17 @@ return array( 'cd2b9b77' => array( 'phui-oi-list-view-css', ), + 'cf4e2140' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + 'phabricator-diff-inline', + ), 'd0c516d5' => array( 'javelin-behavior', 'javelin-dom', diff --git a/src/applications/differential/view/DifferentialChangesetDetailView.php b/src/applications/differential/view/DifferentialChangesetDetailView.php index 123fd71f99..e80dc20ec7 100644 --- a/src/applications/differential/view/DifferentialChangesetDetailView.php +++ b/src/applications/differential/view/DifferentialChangesetDetailView.php @@ -168,6 +168,26 @@ final class DifferentialChangesetDetailView extends AphrontView { $right_id = $changeset_id; } + // In the persistent banner, emphasize the current filename. + $path_part = dirname($display_filename); + $file_part = basename($display_filename); + $display_parts = array(); + if (strlen($path_part)) { + $path_part = $path_part.'/'; + $display_parts[] = phutil_tag( + 'span', + array( + 'class' => 'diff-banner-path', + ), + $path_part); + } + $display_parts[] = phutil_tag( + 'span', + array( + 'class' => 'diff-banner-file', + ), + $file_part); + return javelin_tag( 'div', array( @@ -183,9 +203,9 @@ final class DifferentialChangesetDetailView extends AphrontView { 'autoload' => $this->getAutoload(), 'loaded' => $this->getLoaded(), 'undoTemplates' => hsprintf('%s', $renderer->renderUndoTemplates()), - 'path' => $display_filename, + 'displayPath' => hsprintf('%s', $display_parts), 'objectiveName' => basename($display_filename), - 'icon' => 'fa-file-text-o', + 'icon' => $display_icon, ), 'class' => $class, 'id' => $id, diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index 3d4ff2eb48..eee0e167f3 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -405,13 +405,16 @@ tr.differential-inline-loading { margin-right: 4px; } +.diff-banner-path { + color: {$greytext}; +} + .scroll-objective-list { position: fixed; right: 0; width: 24px; top: 48px; bottom: 48px; - z-index: 6; background: rgba(255, 255, 255, 0.50); border-style: solid; border-color: rgba(255, 255, 255, 0.95); diff --git a/webroot/rsrc/css/core/z-index.css b/webroot/rsrc/css/core/z-index.css index 75cd394988..04b013386d 100644 --- a/webroot/rsrc/css/core/z-index.css +++ b/webroot/rsrc/css/core/z-index.css @@ -97,6 +97,10 @@ div.phui-calendar-day-event { z-index: 6; } +.scroll-objective-list { + z-index: 6; +} + .conpherence-durable-column { z-index: 7; } diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js index 28e19b6276..cd7f888a40 100644 --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -31,7 +31,7 @@ JX.install('DiffChangeset', { this._leftID = data.left; this._rightID = data.right; - this._path = data.path; + this._displayPath = JX.$H(data.displayPath); this._objectiveName = data.objectiveName; this._icon = data.icon; @@ -59,7 +59,7 @@ JX.install('DiffChangeset', { _visible: true, _undoNode: null, - _path: null, + _displayPath: null, _changesetList: null, _objective: null, @@ -277,8 +277,8 @@ JX.install('DiffChangeset', { JX.Router.getInstance().queue(routable); }, - getPath: function() { - return this._path; + getDisplayPath: function() { + return this._displayPath; }, /** diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index f8ca3b9a56..e256372852 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -1322,9 +1322,9 @@ JX.install('DiffChangesetList', { } var icon = new JX.PHUIXIconView() - .setIcon('fa-file') + .setIcon(changeset.getIcon()) .getNode(); - JX.DOM.setContent(node, [icon, ' ', changeset.getPath()]); + JX.DOM.setContent(node, [icon, ' ', changeset.getDisplayPath()]); document.body.appendChild(node); }, From c9889e3d55b1ee5d5bf0cc401470fb5fa7771cc3 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 19 May 2017 12:41:46 -0700 Subject: [PATCH 74/81] Fix an issue in Phriction where moving a document just copied it instead Summary: Ref T12732. See D17918. With modular transactions, `getCustomTransactionNewValue()` isn't actually called. Test Plan: Moved document `/x/` to `/y/`, saw document gone at `/x/` instead of copied. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12732 Differential Revision: https://secure.phabricator.com/D17963 --- .../phriction/editor/PhrictionTransactionEditor.php | 10 +++++----- .../xaction/PhrictionDocumentMoveToTransaction.php | 5 ++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/applications/phriction/editor/PhrictionTransactionEditor.php b/src/applications/phriction/editor/PhrictionTransactionEditor.php index 1f97dd3c54..9ab91d5db9 100644 --- a/src/applications/phriction/editor/PhrictionTransactionEditor.php +++ b/src/applications/phriction/editor/PhrictionTransactionEditor.php @@ -69,6 +69,11 @@ final class PhrictionTransactionEditor return $this->processContentVersionError; } + public function setMoveAwayDocument(PhrictionDocument $document) { + $this->moveAwayDocument = $document; + return $this; + } + public function getEditorApplicationClass() { return 'PhabricatorPhrictionApplication'; } @@ -116,11 +121,6 @@ final class PhrictionTransactionEditor case PhrictionTransaction::TYPE_CONTENT: case PhrictionTransaction::TYPE_DELETE: return $xaction->getNewValue(); - case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: - $document = $xaction->getNewValue(); - // grab the real object now for the sub-editor to come - $this->moveAwayDocument = $document; - return; case PhrictionTransaction::TYPE_MOVE_AWAY: $document = $xaction->getNewValue(); $dict = array( diff --git a/src/applications/phriction/xaction/PhrictionDocumentMoveToTransaction.php b/src/applications/phriction/xaction/PhrictionDocumentMoveToTransaction.php index a513b8fe36..a95e70ebdb 100644 --- a/src/applications/phriction/xaction/PhrictionDocumentMoveToTransaction.php +++ b/src/applications/phriction/xaction/PhrictionDocumentMoveToTransaction.php @@ -17,12 +17,15 @@ final class PhrictionDocumentMoveToTransaction 'content' => $document->getContent()->getContent(), 'title' => $document->getContent()->getTitle(), ); + + $editor = $this->getEditor(); + $editor->setMoveAwayDocument($document); + return $dict; } public function applyInternalEffects($object, $value) { $object->setStatus(PhrictionDocumentStatus::STATUS_EXISTS); - $this->getEditor()->getNewContent()->setTitle($value); } public function applyExternalEffects($object, $value) { From 601622013d371d489f72de76117efc24fc570169 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Fri, 19 May 2017 13:29:54 -0700 Subject: [PATCH 75/81] Clarify milestone/subproject creation language Summary: Ref T12732 Test Plan: Read new language Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T12732 Differential Revision: https://secure.phabricator.com/D17961 --- .../controller/PhabricatorProjectSubprojectsController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php index f516f87e32..269e80a080 100644 --- a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php +++ b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php @@ -183,7 +183,7 @@ final class PhabricatorProjectSubprojectsController 'have their own milestones.'); } else { if (!$milestones) { - $note = pht('You can create milestones for this project.'); + $note = pht('Milestones can be created for this project.'); } else { $note = pht('This project has milestones.'); } @@ -199,7 +199,7 @@ final class PhabricatorProjectSubprojectsController 'subprojects.'); } else { if (!$subprojects) { - $note = pht('You can create subprojects for this project.'); + $note = pht('Subprojects can be created for this project.'); } else { $note = pht('This project has subprojects.'); } From 5a34b299e4133f8e68acb762b5a04e1271e1c301 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Fri, 19 May 2017 13:41:21 -0700 Subject: [PATCH 76/81] Update Maniphest title language Summary: Ref T12732 Test Plan: Read Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T12732 Differential Revision: https://secure.phabricator.com/D17964 --- .../maniphest/xaction/ManiphestTaskTitleTransaction.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php index dcaf959a57..5a4be8faf8 100644 --- a/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php +++ b/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php @@ -36,7 +36,7 @@ final class ManiphestTaskTitleTransaction } return pht( - '%s changed the title from %s to %s.', + '%s renamed the title from %s to %s.', $this->renderAuthor(), $this->renderOldValue(), $this->renderNewValue()); @@ -53,7 +53,7 @@ final class ManiphestTaskTitleTransaction } return pht( - '%s changed %s title from %s to %s.', + '%s renamed %s from %s to %s.', $this->renderAuthor(), $this->renderObject(), $this->renderOldValue(), From abd791889c3f62db7add11ebad9209179f5959ac Mon Sep 17 00:00:00 2001 From: Chad Little Date: Fri, 19 May 2017 13:43:45 -0700 Subject: [PATCH 77/81] Update Maniphest title transaction again Summary: Ref T12732, third time is the charm? Test Plan: Read Reviewers: epriestley Subscribers: Korvin Maniphest Tasks: T12732 Differential Revision: https://secure.phabricator.com/D17965 --- .../maniphest/xaction/ManiphestTaskTitleTransaction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php index 5a4be8faf8..5fade77d07 100644 --- a/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php +++ b/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php @@ -36,7 +36,7 @@ final class ManiphestTaskTitleTransaction } return pht( - '%s renamed the title from %s to %s.', + '%s renamed this task from %s to %s.', $this->renderAuthor(), $this->renderOldValue(), $this->renderNewValue()); From 10b38792320830e0b34f6fb59ad8054e2b154db1 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 19 May 2017 13:45:06 -0700 Subject: [PATCH 78/81] Make Project slug/hashtag transactions render a little more nicely Summary: Ref T12732. Use `renderValue()` to build `renderValueList()` so we get nice fancy text for these. Test Plan: {F4967410} Reviewers: chad, amckinley Reviewed By: amckinley Maniphest Tasks: T12732 Differential Revision: https://secure.phabricator.com/D17966 --- .../PhabricatorProjectSlugsTransaction.php | 30 ++++++++++++------- .../PhabricatorModularTransactionType.php | 13 ++++++++ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/applications/project/xaction/PhabricatorProjectSlugsTransaction.php b/src/applications/project/xaction/PhabricatorProjectSlugsTransaction.php index d442c39a76..e1f22b3746 100644 --- a/src/applications/project/xaction/PhabricatorProjectSlugsTransaction.php +++ b/src/applications/project/xaction/PhabricatorProjectSlugsTransaction.php @@ -40,26 +40,29 @@ final class PhabricatorProjectSlugsTransaction $add = array_diff($new, $old); $rem = array_diff($old, $new); + $add = $this->renderHashtags($add); + $rem = $this->renderHashtags($rem); + if ($add && $rem) { return pht( '%s changed project hashtag(s), added %d: %s; removed %d: %s.', $this->renderAuthor(), count($add), - $this->renderSlugList($add), + $this->renderValueList($add), count($rem), - $this->renderSlugList($rem)); + $this->renderValueList($rem)); } else if ($add) { return pht( '%s added %d project hashtag(s): %s.', $this->renderAuthor(), count($add), - $this->renderSlugList($add)); + $this->renderValueList($add)); } else if ($rem) { return pht( '%s removed %d project hashtag(s): %s.', $this->renderAuthor(), count($rem), - $this->renderSlugList($rem)); + $this->renderValueList($rem)); } } @@ -70,29 +73,32 @@ final class PhabricatorProjectSlugsTransaction $add = array_diff($new, $old); $rem = array_diff($old, $new); + $add = $this->renderHashtags($add); + $rem = $this->renderHashtags($rem); + if ($add && $rem) { return pht( '%s changed %s hashtag(s), added %d: %s; removed %d: %s.', $this->renderAuthor(), $this->renderObject(), count($add), - $this->renderSlugList($add), + $this->renderValueList($add), count($rem), - $this->renderSlugList($rem)); + $this->renderValueList($rem)); } else if ($add) { return pht( '%s added %d %s hashtag(s): %s.', $this->renderAuthor(), count($add), $this->renderObject(), - $this->renderSlugList($add)); + $this->renderValueList($add)); } else if ($rem) { return pht( '%s removed %d %s hashtag(s): %s.', $this->renderAuthor(), count($rem), $this->renderObject(), - $this->renderSlugList($rem)); + $this->renderValueList($rem)); } } @@ -157,8 +163,12 @@ final class PhabricatorProjectSlugsTransaction return $errors; } - private function renderSlugList($slugs) { - return implode(', ', $slugs); + private function renderHashtags(array $tags) { + $result = array(); + foreach ($tags as $tag) { + $result[] = '#'.$tag; + } + return $result; } } diff --git a/src/applications/transactions/storage/PhabricatorModularTransactionType.php b/src/applications/transactions/storage/PhabricatorModularTransactionType.php index 8a56e8e8ce..128b5c7c19 100644 --- a/src/applications/transactions/storage/PhabricatorModularTransactionType.php +++ b/src/applications/transactions/storage/PhabricatorModularTransactionType.php @@ -208,6 +208,19 @@ abstract class PhabricatorModularTransactionType $value); } + final protected function renderValueList(array $values) { + $result = array(); + foreach ($values as $value) { + $result[] = $this->renderValue($value); + } + + if ($this->isTextMode()) { + return implode(', ', $result); + } + + return phutil_implode_html(', ', $result); + } + final protected function renderOldValue() { return $this->renderValue($this->getOldValue()); } From 789d57522b039b1ca755af7e5dc4e591d92e7277 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 19 May 2017 13:52:26 -0700 Subject: [PATCH 79/81] Make editing project images redirect to "Manage" more consistently Summary: Ref T12732. Currently, different ways of setting a profile image can leave you in different places. Instead, always send the user back to the "Manage" page. Test Plan: Used "Current Picture", "use picture", "Build picture" and "upload picture", always ended up in the same spot. Reviewers: chad, amckinley Reviewed By: amckinley Maniphest Tasks: T12732 Differential Revision: https://secure.phabricator.com/D17967 --- .../controller/PhabricatorProjectEditPictureController.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/applications/project/controller/PhabricatorProjectEditPictureController.php b/src/applications/project/controller/PhabricatorProjectEditPictureController.php index 9d0c16efb9..95d3bbd855 100644 --- a/src/applications/project/controller/PhabricatorProjectEditPictureController.php +++ b/src/applications/project/controller/PhabricatorProjectEditPictureController.php @@ -23,8 +23,7 @@ final class PhabricatorProjectEditPictureController $this->setProject($project); - $edit_uri = $this->getApplicationURI('profile/'.$project->getID().'/'); - $view_uri = $this->getApplicationURI('profile/'.$project->getID().'/'); + $manage_uri = $this->getApplicationURI('manage/'.$project->getID().'/'); $supported_formats = PhabricatorFile::getTransformableImageFormats(); $e_file = true; @@ -90,7 +89,7 @@ final class PhabricatorProjectEditPictureController $editor->applyTransactions($project, $xactions); - return id(new AphrontRedirectResponse())->setURI($edit_uri); + return id(new AphrontRedirectResponse())->setURI($manage_uri); } } @@ -243,7 +242,7 @@ final class PhabricatorProjectEditPictureController pht('Supported formats: %s', implode(', ', $supported_formats)))) ->appendChild( id(new AphrontFormSubmitControl()) - ->addCancelButton($edit_uri) + ->addCancelButton($manage_uri) ->setValue(pht('Upload Picture'))); $form_box = id(new PHUIObjectBoxView()) From bbc5f792274ca98bc639cef662c45431c427eaa3 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 19 May 2017 14:08:55 -0700 Subject: [PATCH 80/81] Make membership lock/unlock feed stories read more naturally Summary: Ref T12732. This is pre-existing. Test Plan: {F4967438} Reviewers: chad, amckinley Reviewed By: chad Maniphest Tasks: T12732 Differential Revision: https://secure.phabricator.com/D17969 --- .../project/storage/PhabricatorProjectTransaction.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/project/storage/PhabricatorProjectTransaction.php b/src/applications/project/storage/PhabricatorProjectTransaction.php index 99686a91d1..b0d442ae57 100644 --- a/src/applications/project/storage/PhabricatorProjectTransaction.php +++ b/src/applications/project/storage/PhabricatorProjectTransaction.php @@ -209,12 +209,12 @@ final class PhabricatorProjectTransaction case self::TYPE_LOCKED: if ($new) { return pht( - '%s locked %s membership.', + '%s locked membership for %s.', $author_handle, $object_handle); } else { return pht( - '%s unlocked %s membership.', + '%s unlocked membership for %s.', $author_handle, $object_handle); } From c6a7bcfe89287c2be2baa290def7c2f4e365396d Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 19 May 2017 14:11:54 -0700 Subject: [PATCH 81/81] Make Pholio description behave as a remarkup field (e.g., subscribe mentioned users) Summary: Ref T12732. This is pre-existing but fix it since I caught it while banging around. Test Plan: {F4967442} Reviewers: chad, amckinley Reviewed By: chad Maniphest Tasks: T12732 Differential Revision: https://secure.phabricator.com/D17970 --- .../xaction/PholioMockDescriptionTransaction.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/applications/pholio/xaction/PholioMockDescriptionTransaction.php b/src/applications/pholio/xaction/PholioMockDescriptionTransaction.php index ed14d43c99..75293168e1 100644 --- a/src/applications/pholio/xaction/PholioMockDescriptionTransaction.php +++ b/src/applications/pholio/xaction/PholioMockDescriptionTransaction.php @@ -44,4 +44,14 @@ final class PholioMockDescriptionTransaction ->setNewText($this->getNewValue()); } + public function newRemarkupChanges() { + $changes = array(); + + $changes[] = $this->newRemarkupChange() + ->setOldValue($this->getOldValue()) + ->setNewValue($this->getNewValue()); + + return $changes; + } + }