diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 0a31ae8c33..513fd28f62 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -1782,7 +1782,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-maniphest-list-editor' => array( - 'uri' => '/res/170f8457/rsrc/js/application/maniphest/behavior-list-edit.js', + 'uri' => '/res/a251e72f/rsrc/js/application/maniphest/behavior-list-edit.js', 'type' => 'js', 'requires' => array( @@ -1797,7 +1797,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-maniphest-subpriority-editor' => array( - 'uri' => '/res/994f0a9d/rsrc/js/application/maniphest/behavior-subpriorityeditor.js', + 'uri' => '/res/57540d92/rsrc/js/application/maniphest/behavior-subpriorityeditor.js', 'type' => 'js', 'requires' => array( @@ -2271,6 +2271,21 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/repository/repository-crossreference.js', ), + 'javelin-behavior-search-reorder-queries' => + array( + 'uri' => '/res/47148be5/rsrc/js/application/search/behavior-reorder-queries.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-vector', + 2 => 'javelin-stratcom', + 3 => 'javelin-workflow', + 4 => 'javelin-dom', + 5 => 'phabricator-draggable-list', + ), + 'disk' => '/rsrc/js/application/search/behavior-reorder-queries.js', + ), 'javelin-behavior-slowvote-embed' => array( 'uri' => '/res/8e85e20d/rsrc/js/application/slowvote/behavior-slowvote-embed.js', @@ -3206,7 +3221,7 @@ celerity_register_resource_map(array( ), 'phabricator-object-item-list-view-css' => array( - 'uri' => '/res/b114bc9a/rsrc/css/layout/phabricator-object-item-list-view.css', + 'uri' => '/res/90b606a1/rsrc/css/layout/phabricator-object-item-list-view.css', 'type' => 'css', 'requires' => array( @@ -3583,7 +3598,7 @@ celerity_register_resource_map(array( ), 'phabricator-zindex-css' => array( - 'uri' => '/res/d4048e34/rsrc/css/core/z-index.css', + 'uri' => '/res/f064ab0f/rsrc/css/core/z-index.css', 'type' => 'css', 'requires' => array( @@ -4012,7 +4027,7 @@ celerity_register_resource_map(array( ), array( 'packages' => array( - '21f9f13d' => + 'd5a2d8cd' => array( 'name' => 'core.pkg.css', 'symbols' => @@ -4060,7 +4075,7 @@ celerity_register_resource_map(array( 40 => 'phabricator-property-list-view-css', 41 => 'phabricator-tag-view-css', ), - 'uri' => '/res/pkg/21f9f13d/core.pkg.css', + 'uri' => '/res/pkg/d5a2d8cd/core.pkg.css', 'type' => 'css', ), 'f2ad0683' => @@ -4236,7 +4251,7 @@ celerity_register_resource_map(array( 'uri' => '/res/pkg/03ab92cf/maniphest.pkg.css', 'type' => 'css', ), - 'c0c9bc0b' => + '265bc3fa' => array( 'name' => 'maniphest.pkg.js', 'symbols' => @@ -4247,23 +4262,23 @@ celerity_register_resource_map(array( 3 => 'javelin-behavior-maniphest-transaction-expand', 4 => 'javelin-behavior-maniphest-subpriority-editor', ), - 'uri' => '/res/pkg/c0c9bc0b/maniphest.pkg.js', + 'uri' => '/res/pkg/265bc3fa/maniphest.pkg.js', 'type' => 'js', ), ), 'reverse' => array( 'aphront-attached-file-view-css' => '03ab92cf', - 'aphront-dialog-view-css' => '21f9f13d', - 'aphront-error-view-css' => '21f9f13d', - 'aphront-form-view-css' => '21f9f13d', - 'aphront-list-filter-view-css' => '21f9f13d', - 'aphront-pager-view-css' => '21f9f13d', - 'aphront-panel-view-css' => '21f9f13d', - 'aphront-table-view-css' => '21f9f13d', - 'aphront-tokenizer-control-css' => '21f9f13d', - 'aphront-tooltip-css' => '21f9f13d', - 'aphront-typeahead-control-css' => '21f9f13d', + 'aphront-dialog-view-css' => 'd5a2d8cd', + 'aphront-error-view-css' => 'd5a2d8cd', + 'aphront-form-view-css' => 'd5a2d8cd', + 'aphront-list-filter-view-css' => 'd5a2d8cd', + 'aphront-pager-view-css' => 'd5a2d8cd', + 'aphront-panel-view-css' => 'd5a2d8cd', + 'aphront-table-view-css' => 'd5a2d8cd', + 'aphront-tokenizer-control-css' => 'd5a2d8cd', + 'aphront-tooltip-css' => 'd5a2d8cd', + 'aphront-typeahead-control-css' => 'd5a2d8cd', 'differential-changeset-view-css' => 'dd27a69b', 'differential-core-view-css' => 'dd27a69b', 'differential-inline-comment-editor' => '9488bb69', @@ -4277,7 +4292,7 @@ celerity_register_resource_map(array( 'differential-table-of-contents-css' => 'dd27a69b', 'diffusion-commit-view-css' => 'c8ce2d88', 'diffusion-icons-css' => 'c8ce2d88', - 'global-drag-and-drop-css' => '21f9f13d', + 'global-drag-and-drop-css' => 'd5a2d8cd', 'inline-comment-summary-css' => 'dd27a69b', 'javelin-aphlict' => 'f2ad0683', 'javelin-behavior' => 'a9f14d76', @@ -4310,11 +4325,11 @@ celerity_register_resource_map(array( 'javelin-behavior-konami' => 'f2ad0683', 'javelin-behavior-lightbox-attachments' => 'f2ad0683', 'javelin-behavior-load-blame' => '9488bb69', - 'javelin-behavior-maniphest-batch-selector' => 'c0c9bc0b', - 'javelin-behavior-maniphest-subpriority-editor' => 'c0c9bc0b', - 'javelin-behavior-maniphest-transaction-controls' => 'c0c9bc0b', - 'javelin-behavior-maniphest-transaction-expand' => 'c0c9bc0b', - 'javelin-behavior-maniphest-transaction-preview' => 'c0c9bc0b', + 'javelin-behavior-maniphest-batch-selector' => '265bc3fa', + 'javelin-behavior-maniphest-subpriority-editor' => '265bc3fa', + 'javelin-behavior-maniphest-transaction-controls' => '265bc3fa', + 'javelin-behavior-maniphest-transaction-expand' => '265bc3fa', + 'javelin-behavior-maniphest-transaction-preview' => '265bc3fa', 'javelin-behavior-phabricator-active-nav' => 'f2ad0683', 'javelin-behavior-phabricator-autofocus' => 'f2ad0683', 'javelin-behavior-phabricator-gesture' => 'f2ad0683', @@ -4351,55 +4366,55 @@ celerity_register_resource_map(array( 'javelin-util' => 'a9f14d76', 'javelin-vector' => 'a9f14d76', 'javelin-workflow' => 'a9f14d76', - 'lightbox-attachment-css' => '21f9f13d', + 'lightbox-attachment-css' => 'd5a2d8cd', 'maniphest-task-summary-css' => '03ab92cf', 'maniphest-transaction-detail-css' => '03ab92cf', - 'phabricator-action-list-view-css' => '21f9f13d', - 'phabricator-application-launch-view-css' => '21f9f13d', + 'phabricator-action-list-view-css' => 'd5a2d8cd', + 'phabricator-application-launch-view-css' => 'd5a2d8cd', 'phabricator-busy' => 'f2ad0683', 'phabricator-content-source-view-css' => 'dd27a69b', - 'phabricator-core-buttons-css' => '21f9f13d', - 'phabricator-core-css' => '21f9f13d', - 'phabricator-crumbs-view-css' => '21f9f13d', + 'phabricator-core-buttons-css' => 'd5a2d8cd', + 'phabricator-core-css' => 'd5a2d8cd', + 'phabricator-crumbs-view-css' => 'd5a2d8cd', 'phabricator-drag-and-drop-file-upload' => '9488bb69', 'phabricator-dropdown-menu' => 'f2ad0683', 'phabricator-file-upload' => 'f2ad0683', - 'phabricator-filetree-view-css' => '21f9f13d', - 'phabricator-flag-css' => '21f9f13d', - 'phabricator-form-view-css' => '21f9f13d', - 'phabricator-header-view-css' => '21f9f13d', + 'phabricator-filetree-view-css' => 'd5a2d8cd', + 'phabricator-flag-css' => 'd5a2d8cd', + 'phabricator-form-view-css' => 'd5a2d8cd', + 'phabricator-header-view-css' => 'd5a2d8cd', 'phabricator-hovercard' => 'f2ad0683', - 'phabricator-jump-nav' => '21f9f13d', + 'phabricator-jump-nav' => 'd5a2d8cd', 'phabricator-keyboard-shortcut' => 'f2ad0683', 'phabricator-keyboard-shortcut-manager' => 'f2ad0683', - 'phabricator-main-menu-view' => '21f9f13d', + 'phabricator-main-menu-view' => 'd5a2d8cd', 'phabricator-menu-item' => 'f2ad0683', - 'phabricator-nav-view-css' => '21f9f13d', + 'phabricator-nav-view-css' => 'd5a2d8cd', 'phabricator-notification' => 'f2ad0683', - 'phabricator-notification-css' => '21f9f13d', - 'phabricator-notification-menu-css' => '21f9f13d', - 'phabricator-object-item-list-view-css' => '21f9f13d', + 'phabricator-notification-css' => 'd5a2d8cd', + 'phabricator-notification-menu-css' => 'd5a2d8cd', + 'phabricator-object-item-list-view-css' => 'd5a2d8cd', 'phabricator-object-selector-css' => 'dd27a69b', 'phabricator-phtize' => 'f2ad0683', 'phabricator-prefab' => 'f2ad0683', 'phabricator-project-tag-css' => '03ab92cf', - 'phabricator-property-list-view-css' => '21f9f13d', - 'phabricator-remarkup-css' => '21f9f13d', + 'phabricator-property-list-view-css' => 'd5a2d8cd', + 'phabricator-remarkup-css' => 'd5a2d8cd', 'phabricator-shaped-request' => '9488bb69', - 'phabricator-side-menu-view-css' => '21f9f13d', - 'phabricator-standard-page-view' => '21f9f13d', - 'phabricator-tag-view-css' => '21f9f13d', + 'phabricator-side-menu-view-css' => 'd5a2d8cd', + 'phabricator-standard-page-view' => 'd5a2d8cd', + 'phabricator-tag-view-css' => 'd5a2d8cd', 'phabricator-textareautils' => 'f2ad0683', 'phabricator-tooltip' => 'f2ad0683', - 'phabricator-transaction-view-css' => '21f9f13d', - 'phabricator-zindex-css' => '21f9f13d', - 'phui-form-css' => '21f9f13d', - 'phui-icon-view-css' => '21f9f13d', - 'spacing-css' => '21f9f13d', - 'sprite-apps-large-css' => '21f9f13d', - 'sprite-gradient-css' => '21f9f13d', - 'sprite-icons-css' => '21f9f13d', - 'sprite-menu-css' => '21f9f13d', - 'syntax-highlighting-css' => '21f9f13d', + 'phabricator-transaction-view-css' => 'd5a2d8cd', + 'phabricator-zindex-css' => 'd5a2d8cd', + 'phui-form-css' => 'd5a2d8cd', + 'phui-icon-view-css' => 'd5a2d8cd', + 'spacing-css' => 'd5a2d8cd', + 'sprite-apps-large-css' => 'd5a2d8cd', + 'sprite-gradient-css' => 'd5a2d8cd', + 'sprite-icons-css' => 'd5a2d8cd', + 'sprite-menu-css' => 'd5a2d8cd', + 'syntax-highlighting-css' => 'd5a2d8cd', ), )); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 90a2c73888..dbf888da4d 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1400,6 +1400,7 @@ phutil_register_library_map(array( 'PhabricatorSearchIndexer' => 'applications/search/index/PhabricatorSearchIndexer.php', 'PhabricatorSearchManagementIndexWorkflow' => 'applications/search/management/PhabricatorSearchManagementIndexWorkflow.php', 'PhabricatorSearchManagementWorkflow' => 'applications/search/management/PhabricatorSearchManagementWorkflow.php', + 'PhabricatorSearchOrderController' => 'applications/search/controller/PhabricatorSearchOrderController.php', 'PhabricatorSearchQuery' => 'applications/search/storage/PhabricatorSearchQuery.php', 'PhabricatorSearchRelationship' => 'applications/search/constants/PhabricatorSearchRelationship.php', 'PhabricatorSearchResultView' => 'applications/search/view/PhabricatorSearchResultView.php', @@ -3229,6 +3230,7 @@ phutil_register_library_map(array( 'PhabricatorSearchHovercardController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchManagementIndexWorkflow' => 'PhabricatorSearchManagementWorkflow', 'PhabricatorSearchManagementWorkflow' => 'PhutilArgumentWorkflow', + 'PhabricatorSearchOrderController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchQuery' => 'PhabricatorSearchDAO', 'PhabricatorSearchResultView' => 'AphrontView', 'PhabricatorSearchSelectController' => 'PhabricatorSearchBaseController', diff --git a/src/applications/search/application/PhabricatorApplicationSearch.php b/src/applications/search/application/PhabricatorApplicationSearch.php index c3723fe53d..5f3aa71ab9 100644 --- a/src/applications/search/application/PhabricatorApplicationSearch.php +++ b/src/applications/search/application/PhabricatorApplicationSearch.php @@ -37,6 +37,7 @@ final class PhabricatorApplicationSearch extends PhabricatorApplication { 'edit/(?P[^/]+)/' => 'PhabricatorSearchEditController', 'delete/(?P[^/]+)/(?P[^/]+)/' => 'PhabricatorSearchDeleteController', + 'order/(?P[^/]+)/' => 'PhabricatorSearchOrderController', ), ); } diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php index 9d7106f462..814595593c 100644 --- a/src/applications/search/controller/PhabricatorApplicationSearchController.php +++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php @@ -228,17 +228,23 @@ final class PhabricatorApplicationSearchController $named_queries = $engine->loadAllNamedQueries(); + $list_id = celerity_generate_unique_node_id(); + $list = new PhabricatorObjectItemListView(); $list->setUser($user); + $list->setID($list_id); + + Javelin::initBehavior( + 'search-reorder-queries', + array( + 'listID' => $list_id, + 'orderURI' => '/search/order/'.get_class($engine).'/', + )); foreach ($named_queries as $named_query) { $class = get_class($engine); $key = $named_query->getQueryKey(); - $date_created = phabricator_datetime( - $named_query->getDateCreated(), - $user); - $item = id(new PhabricatorObjectItemView()) ->setHeader($named_query->getQueryName()) ->setHref($engine->getQueryResultsPageURI($key)); @@ -263,13 +269,19 @@ final class PhabricatorApplicationSearchController } $item->setBarColor('grey'); } else { - $item->addIcon('none', $date_created); $item->addAction( id(new PHUIListItemView()) ->setIcon('edit') ->setHref('/search/edit/'.$key.'/')); } + $item->setGrippable(true); + $item->addSigil('named-query'); + $item->setMetadata( + array( + 'queryKey' => $named_query->getQueryKey(), + )); + $list->addItem($item); } diff --git a/src/applications/search/controller/PhabricatorSearchDeleteController.php b/src/applications/search/controller/PhabricatorSearchDeleteController.php index eff5c50202..2cd7e8845c 100644 --- a/src/applications/search/controller/PhabricatorSearchDeleteController.php +++ b/src/applications/search/controller/PhabricatorSearchDeleteController.php @@ -36,12 +36,7 @@ final class PhabricatorSearchDeleteController ->executeOne(); if (!$named_query && $engine->isBuiltinQuery($key)) { - $named_query = id(new PhabricatorNamedQuery()) - ->setUserPHID($user->getPHID()) - ->setQueryName('(BUILTIN)') - ->setQueryKey($key) - ->setEngineClassName($this->engineClass) - ->setIsBuiltin(true); + $named_query = $engine->getBuiltinQuery($key); } if (!$named_query) { diff --git a/src/applications/search/controller/PhabricatorSearchOrderController.php b/src/applications/search/controller/PhabricatorSearchOrderController.php new file mode 100644 index 0000000000..7093aed162 --- /dev/null +++ b/src/applications/search/controller/PhabricatorSearchOrderController.php @@ -0,0 +1,44 @@ +engineClass = idx($data, 'engine'); + } + + public function processRequest() { + $request = $this->getRequest(); + $user = $request->getUser(); + + $request->validateCSRF(); + + $base_class = 'PhabricatorApplicationSearchEngine'; + if (!is_subclass_of($this->engineClass, $base_class)) { + return new Aphront400Response(); + } + + $engine = newv($this->engineClass, array()); + $engine->setViewer($user); + + $queries = $engine->loadAllNamedQueries(); + $queries = mpull($queries, null, 'getQueryKey'); + + $order = $request->getStrList('order'); + $queries = array_select_keys($queries, $order) + $queries; + + $sequence = 1; + foreach ($queries as $query) { + $query->setSequence($sequence++); + $query->save(); + } + + return id(new AphrontAjaxResponse()); + } + +} diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php index 8a3b9753cc..6cd75dc419 100644 --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -157,6 +157,8 @@ abstract class PhabricatorApplicationSearchEngine { unset($builtin[$key]); } + $named_queries = msort($named_queries, 'getSortKey'); + return $named_queries + $builtin; } @@ -181,12 +183,15 @@ abstract class PhabricatorApplicationSearchEngine { $names = $this->getBuiltinQueryNames(); $queries = array(); + $sequence = 0; foreach ($names as $key => $name) { $queries[$key] = id(new PhabricatorNamedQuery()) + ->setUserPHID($this->requireViewer()->getPHID()) + ->setEngineClassName(get_class($this)) ->setQueryName($name) ->setQueryKey($key) - ->setIsBuiltin(true) - ->makeEphemeral(); + ->setSequence((1 << 24) + $sequence++) + ->setIsBuiltin(true); } return $queries; diff --git a/src/applications/search/storage/PhabricatorNamedQuery.php b/src/applications/search/storage/PhabricatorNamedQuery.php index 8bc11b6cce..cc1e199336 100644 --- a/src/applications/search/storage/PhabricatorNamedQuery.php +++ b/src/applications/search/storage/PhabricatorNamedQuery.php @@ -15,6 +15,10 @@ final class PhabricatorNamedQuery extends PhabricatorSearchDAO protected $isDisabled = 0; protected $sequence = 0; + public function getSortKey() { + return sprintf('~%010d%010d', $this->sequence, $this->getID()); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/webroot/rsrc/css/core/z-index.css b/webroot/rsrc/css/core/z-index.css index 139cc4cf2b..d6d243ff67 100644 --- a/webroot/rsrc/css/core/z-index.css +++ b/webroot/rsrc/css/core/z-index.css @@ -50,6 +50,10 @@ z-index: 5; } +.drag-dragging { + z-index: 5; +} + .phabricator-crumbs-view { z-index: 3; } diff --git a/webroot/rsrc/css/layout/phabricator-object-item-list-view.css b/webroot/rsrc/css/layout/phabricator-object-item-list-view.css index 378c1e438d..aec715142a 100644 --- a/webroot/rsrc/css/layout/phabricator-object-item-list-view.css +++ b/webroot/rsrc/css/layout/phabricator-object-item-list-view.css @@ -464,3 +464,20 @@ .phabricator-object-item-frame { border-color: #99ccff; } + + +.drag-ghost { + position: relative; + border: 1px dashed #aaaaaa; + background: #f9f9f9; + margin: 4px; +} + +.drag-dragging { + position: relative; + opacity: 0.8; +} + +.drag-sending { + opacity: 0.5; +} diff --git a/webroot/rsrc/js/application/search/behavior-reorder-queries.js b/webroot/rsrc/js/application/search/behavior-reorder-queries.js new file mode 100644 index 0000000000..6c2b6169a1 --- /dev/null +++ b/webroot/rsrc/js/application/search/behavior-reorder-queries.js @@ -0,0 +1,54 @@ +/** + * @provides javelin-behavior-search-reorder-queries + * @requires javelin-behavior + * javelin-vector + * javelin-stratcom + * javelin-workflow + * javelin-dom + * phabricator-draggable-list + */ + +JX.behavior('search-reorder-queries', function(config) { + + var ghost = JX.$N('div', {className: 'drag-ghost'}); + var root = JX.$(config.listID); + + var list = new JX.DraggableList('named-query', root) + .setGhostNode(ghost) + .setFindItemsHandler(function() { + return JX.DOM.scry(root, 'li', 'named-query'); + }); + + list.listen('didBeginDrag', function(node) { + list.getGhostNode().style.height = JX.Vector.getDim(node).y + 'px'; + JX.DOM.alterClass(node, 'drag-dragging', true); + }); + + list.listen('didEndDrag', function(node) { + JX.DOM.alterClass(node, 'drag-dragging', false); + }); + + list.listen('didDrop', function(node, after) { + var nodes = list.findItems(); + var order = []; + var key; + for (var ii = 0; ii < nodes.length; ii++) { + key = JX.Stratcom.getData(nodes[ii]).queryKey; + if (key) { + order.push(key); + } + } + + list.lock(); + JX.DOM.alterClass(node, 'drag-sending', true); + + new JX.Workflow(config.orderURI, {order: order.join()}) + .setHandler(function(e) { + JX.DOM.alterClass(node, 'drag-sending', false); + list.unlock(); + }) + .start(); + }); + +}); +