diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 4275021cb1..ed79b7158b 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -2280,7 +2280,7 @@ celerity_register_resource_map(array( ), 'phabricator-action-list-view-css' => array( - 'uri' => '/res/1b4eef71/rsrc/css/layout/phabricator-action-list-view.css', + 'uri' => '/res/fa304592/rsrc/css/layout/phabricator-action-list-view.css', 'type' => 'css', 'requires' => array( @@ -2325,7 +2325,7 @@ celerity_register_resource_map(array( ), 'phabricator-core-buttons-css' => array( - 'uri' => '/res/a105abeb/rsrc/css/core/buttons.css', + 'uri' => '/res/427fac91/rsrc/css/core/buttons.css', 'type' => 'css', 'requires' => array( @@ -3003,7 +3003,7 @@ celerity_register_resource_map(array( ), array( 'packages' => array( - '495635fb' => + '19bef443' => array( 'name' => 'core.pkg.css', 'symbols' => @@ -3032,7 +3032,7 @@ celerity_register_resource_map(array( 21 => 'phabricator-flag-css', 22 => 'aphront-error-view-css', ), - 'uri' => '/res/pkg/495635fb/core.pkg.css', + 'uri' => '/res/pkg/19bef443/core.pkg.css', 'type' => 'css', ), '3a455e4f' => @@ -3199,20 +3199,20 @@ celerity_register_resource_map(array( 'reverse' => array( 'aphront-attached-file-view-css' => '7839ae2d', - 'aphront-crumbs-view-css' => '495635fb', - 'aphront-dialog-view-css' => '495635fb', - 'aphront-error-view-css' => '495635fb', - 'aphront-form-view-css' => '495635fb', + 'aphront-crumbs-view-css' => '19bef443', + 'aphront-dialog-view-css' => '19bef443', + 'aphront-error-view-css' => '19bef443', + 'aphront-form-view-css' => '19bef443', 'aphront-headsup-action-list-view-css' => '2ba14b3d', - 'aphront-headsup-view-css' => '495635fb', - 'aphront-list-filter-view-css' => '495635fb', - 'aphront-pager-view-css' => '495635fb', - 'aphront-panel-view-css' => '495635fb', - 'aphront-side-nav-view-css' => '495635fb', - 'aphront-table-view-css' => '495635fb', - 'aphront-tokenizer-control-css' => '495635fb', - 'aphront-tooltip-css' => '495635fb', - 'aphront-typeahead-control-css' => '495635fb', + 'aphront-headsup-view-css' => '19bef443', + 'aphront-list-filter-view-css' => '19bef443', + 'aphront-pager-view-css' => '19bef443', + 'aphront-panel-view-css' => '19bef443', + 'aphront-side-nav-view-css' => '19bef443', + 'aphront-table-view-css' => '19bef443', + 'aphront-tokenizer-control-css' => '19bef443', + 'aphront-tooltip-css' => '19bef443', + 'aphront-typeahead-control-css' => '19bef443', 'differential-changeset-view-css' => '2ba14b3d', 'differential-core-view-css' => '2ba14b3d', 'differential-inline-comment-editor' => 'd05e3c0f', @@ -3278,15 +3278,15 @@ celerity_register_resource_map(array( 'javelin-workflow' => '3a455e4f', 'maniphest-task-summary-css' => '7839ae2d', 'maniphest-transaction-detail-css' => '7839ae2d', - 'phabricator-app-buttons-css' => '495635fb', + 'phabricator-app-buttons-css' => '19bef443', 'phabricator-content-source-view-css' => '2ba14b3d', - 'phabricator-core-buttons-css' => '495635fb', - 'phabricator-core-css' => '495635fb', - 'phabricator-directory-css' => '495635fb', + 'phabricator-core-buttons-css' => '19bef443', + 'phabricator-core-css' => '19bef443', + 'phabricator-directory-css' => '19bef443', 'phabricator-drag-and-drop-file-upload' => 'd05e3c0f', 'phabricator-dropdown-menu' => '3a455e4f', - 'phabricator-flag-css' => '495635fb', - 'phabricator-jump-nav' => '495635fb', + 'phabricator-flag-css' => '19bef443', + 'phabricator-jump-nav' => '19bef443', 'phabricator-keyboard-shortcut' => '3a455e4f', 'phabricator-keyboard-shortcut-manager' => '3a455e4f', 'phabricator-menu-item' => '3a455e4f', @@ -3294,11 +3294,11 @@ celerity_register_resource_map(array( 'phabricator-paste-file-upload' => '3a455e4f', 'phabricator-prefab' => '3a455e4f', 'phabricator-project-tag-css' => '7839ae2d', - 'phabricator-remarkup-css' => '495635fb', + 'phabricator-remarkup-css' => '19bef443', 'phabricator-shaped-request' => 'd05e3c0f', - 'phabricator-standard-page-view' => '495635fb', + 'phabricator-standard-page-view' => '19bef443', 'phabricator-tooltip' => '3a455e4f', - 'phabricator-transaction-view-css' => '495635fb', - 'syntax-highlighting-css' => '495635fb', + 'phabricator-transaction-view-css' => '19bef443', + 'syntax-highlighting-css' => '19bef443', ), )); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 4133b07d8b..f5054a2095 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -545,6 +545,7 @@ phutil_register_library_map(array( 'PackageModifyMail' => 'applications/owners/mail/PackageModifyMail.php', 'Phabricator404Controller' => 'applications/base/controller/Phabricator404Controller.php', 'PhabricatorAccessLog' => 'infrastructure/PhabricatorAccessLog.php', + 'PhabricatorActionListExample' => 'applications/uiexample/examples/PhabricatorActionListExample.php', 'PhabricatorActionListView' => 'view/layout/PhabricatorActionListView.php', 'PhabricatorActionView' => 'view/layout/PhabricatorActionView.php', 'PhabricatorAnchorView' => 'view/layout/PhabricatorAnchorView.php', @@ -1710,6 +1711,7 @@ phutil_register_library_map(array( 'PackageDeleteMail' => 'PackageMail', 'PackageModifyMail' => 'PackageMail', 'Phabricator404Controller' => 'PhabricatorController', + 'PhabricatorActionListExample' => 'PhabricatorUIExample', 'PhabricatorActionListView' => 'AphrontView', 'PhabricatorActionView' => 'AphrontView', 'PhabricatorAnchorView' => 'AphrontView', diff --git a/src/applications/flag/events/PhabricatorFlagsUIEventListener.php b/src/applications/flag/events/PhabricatorFlagsUIEventListener.php index 9ed31c4b06..0b3082a97f 100644 --- a/src/applications/flag/events/PhabricatorFlagsUIEventListener.php +++ b/src/applications/flag/events/PhabricatorFlagsUIEventListener.php @@ -34,6 +34,12 @@ final class PhabricatorFlagsUIEventListener extends PhutilEventListener { $user = $event->getUser(); $object = $event->getValue('object'); + if (!$object || !$object->getPHID()) { + // If we have no object, or the object doesn't have a PHID yet, we can't + // flag it. + return; + } + $flag = PhabricatorFlagQuery::loadUserFlag($user, $object->getPHID()); if ($flag) { diff --git a/src/applications/uiexample/controller/PhabricatorUIExampleRenderController.php b/src/applications/uiexample/controller/PhabricatorUIExampleRenderController.php index 2fc92bfaf5..947cc12328 100644 --- a/src/applications/uiexample/controller/PhabricatorUIExampleRenderController.php +++ b/src/applications/uiexample/controller/PhabricatorUIExampleRenderController.php @@ -53,6 +53,12 @@ final class PhabricatorUIExampleRenderController extends PhabricatorController { $example = $classes[$selected]; $example->setRequest($this->getRequest()); + $result = $example->renderExample(); + if ($result instanceof AphrontResponse) { + // This allows examples to generate dialogs, etc., for demonstration. + return $result; + } + $nav->appendChild( '
'. '

'. @@ -64,7 +70,7 @@ final class PhabricatorUIExampleRenderController extends PhabricatorController { '

'. '

'); - $nav->appendChild($example->renderExample()); + $nav->appendChild($result); return $this->buildApplicationPage( $nav, diff --git a/src/applications/uiexample/examples/PhabricatorActionListExample.php b/src/applications/uiexample/examples/PhabricatorActionListExample.php new file mode 100644 index 0000000000..824ef935da --- /dev/null +++ b/src/applications/uiexample/examples/PhabricatorActionListExample.php @@ -0,0 +1,117 @@ +PhabricatorActionListView to render object actions.'; + } + + public function renderExample() { + $request = $this->getRequest(); + $user = $request->getUser(); + + $notices = array(); + if ($request->isFormPost()) { + $notices[] = 'You just submitted a valid form POST.'; + } + + if ($request->isJavelinWorkflow()) { + $notices[] = 'You just submitted a Workflow request.'; + } + + if ($notices) { + $notices = id(new AphrontErrorView()) + ->setSeverity(AphrontErrorView::SEVERITY_NOTICE) + ->setErrors($notices); + } else { + $notices = null; + } + + if ($request->isJavelinWorkflow()) { + $dialog = new AphrontDialogView(); + $dialog->setUser($user); + $dialog->setTitle('Request Information'); + $dialog->appendChild($notices); + $dialog->addCancelButton($request->getRequestURI(), 'Close'); + return id(new AphrontDialogResponse())->setDialog($dialog); + } + + $view = new PhabricatorActionListView(); + $view->setUser($user); + + $view->addAction( + id(new PhabricatorActionView()) + ->setUser($user) + ->setHref($request->getRequestURI()) + ->setName('Normal Action') + ->setIcon('file')); + + $view->addAction( + id(new PhabricatorActionView()) + ->setUser($user) + ->setHref($request->getRequestURI()) + ->setDisabled(true) + ->setName('Disabled Action') + ->setIcon('file')); + + $view->addAction( + id(new PhabricatorActionView()) + ->setUser($user) + ->setHref($request->getRequestURI()) + ->setRenderAsForm(true) + ->setName('Form Action') + ->setIcon('file')); + + $view->addAction( + id(new PhabricatorActionView()) + ->setUser($user) + ->setHref($request->getRequestURI()) + ->setRenderAsForm(true) + ->setDisabled(true) + ->setName('Disabled Form Action') + ->setIcon('file')); + + $view->addAction( + id(new PhabricatorActionView()) + ->setUser($user) + ->setHref($request->getRequestURI()) + ->setWorkflow(true) + ->setName('Workflow Action') + ->setIcon('file')); + + $view->addAction( + id(new PhabricatorActionView()) + ->setUser($user) + ->setHref($request->getRequestURI()) + ->setRenderAsForm(true) + ->setWorkflow(true) + ->setName('Form + Workflow Action') + ->setIcon('file')); + + return array( + $view, + '
', + $notices, + ); + } +} diff --git a/src/view/layout/PhabricatorActionListView.php b/src/view/layout/PhabricatorActionListView.php index cfc7671715..b80ba779ef 100644 --- a/src/view/layout/PhabricatorActionListView.php +++ b/src/view/layout/PhabricatorActionListView.php @@ -42,10 +42,6 @@ final class PhabricatorActionListView extends AphrontView { throw new Exception("Call setUser() before render()!"); } - if (!$this->object) { - throw new Exception("Call setObject() before render()!"); - } - $event = new PhabricatorEvent( PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS, array( diff --git a/src/view/layout/PhabricatorActionView.php b/src/view/layout/PhabricatorActionView.php index 73294baad3..f6b2a0c66b 100644 --- a/src/view/layout/PhabricatorActionView.php +++ b/src/view/layout/PhabricatorActionView.php @@ -19,10 +19,12 @@ final class PhabricatorActionView extends AphrontView { private $name; + private $user; private $icon; private $href; private $disabled; private $workflow; + private $renderAsForm; public function setHref($href) { $this->href = $href; @@ -49,6 +51,16 @@ final class PhabricatorActionView extends AphrontView { return $this; } + public function setRenderAsForm($form) { + $this->renderAsForm = $form; + return $this; + } + + public function setUser(PhabricatorUser $user) { + $this->user = $user; + return $this; + } + public function render() { $icon = null; @@ -63,14 +75,37 @@ final class PhabricatorActionView extends AphrontView { } if ($this->href) { - $item = javelin_render_tag( - 'a', - array( - 'href' => $this->href, - 'class' => 'phabricator-action-view-item', - 'sigil' => $this->workflow ? 'workflow' : null, - ), - phutil_escape_html($this->name)); + if ($this->renderAsForm) { + if (!$this->user) { + throw new Exception( + 'Call setUser() when rendering an action as a form.'); + } + + $item = javelin_render_tag( + 'button', + array( + 'class' => 'phabricator-action-view-item', + ), + phutil_escape_html($this->name)); + + $item = phabricator_render_form( + $this->user, + array( + 'action' => $this->href, + 'method' => 'POST', + 'sigil' => $this->workflow ? 'workflow' : null, + ), + $item); + } else { + $item = javelin_render_tag( + 'a', + array( + 'href' => $this->href, + 'class' => 'phabricator-action-view-item', + 'sigil' => $this->workflow ? 'workflow' : null, + ), + phutil_escape_html($this->name)); + } } else { $item = phutil_render_tag( 'span', diff --git a/webroot/rsrc/css/core/buttons.css b/webroot/rsrc/css/core/buttons.css index 9ec6a17864..103406049d 100644 --- a/webroot/rsrc/css/core/buttons.css +++ b/webroot/rsrc/css/core/buttons.css @@ -157,7 +157,6 @@ button.link:hover { text-decoration: underline; } - a.toggle { background: #d3d3d3; padding: 2px 6px 3px; diff --git a/webroot/rsrc/css/layout/phabricator-action-list-view.css b/webroot/rsrc/css/layout/phabricator-action-list-view.css index 1320d4c2c0..80083e448a 100644 --- a/webroot/rsrc/css/layout/phabricator-action-list-view.css +++ b/webroot/rsrc/css/layout/phabricator-action-list-view.css @@ -32,6 +32,21 @@ position: relative; } +.phabricator-action-view button.phabricator-action-view-item { + border: none; + background: transparent; + box-shadow: none; + outline: 0; + box-shadow: 0; + padding: 0; + margin: 0; + font-weight: normal; + color: #3b5998; + width: 100%; + text-align: left; +} + +.phabricator-action-view button.phabricator-action-view-item, .phabricator-action-view-item { line-height: 20px; padding-left: 34px; @@ -53,11 +68,13 @@ text-decoration: none; } -.phabricator-action-view-disabled .phabricator-action-view-item { +.phabricator-action-view-disabled .phabricator-action-view-item, +.phabricator-action-view-disabled button.phabricator-action-view-item { color: #888888; } -.phabricator-action-view-disabled .phabricator-action-view-item:hover { +.phabricator-action-view-disabled .phabricator-action-view-item:hover, +.phabricator-action-view-disabled button.phabricator-action-view-item:hover { background-color: #dfdfdf; color: #888888; }