diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 2433beba80..e3854c740c 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ return array( 'names' => array( 'conpherence.pkg.css' => 'e68cf1fa', 'conpherence.pkg.js' => 'b5b51108', - 'core.pkg.css' => '291cbd98', + 'core.pkg.css' => '4ac857bf', 'core.pkg.js' => '6c085267', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '45951e9e', @@ -29,10 +29,10 @@ return array( 'rsrc/css/aphront/dialog-view.css' => '6bfc244b', 'rsrc/css/aphront/list-filter-view.css' => '5d6f0526', 'rsrc/css/aphront/multi-column.css' => '84cc6640', - 'rsrc/css/aphront/notification.css' => '3f6c89c9', + 'rsrc/css/aphront/notification.css' => '457861ec', 'rsrc/css/aphront/panel-view.css' => '8427b78d', 'rsrc/css/aphront/phabricator-nav-view.css' => 'faf6a6fc', - 'rsrc/css/aphront/table-view.css' => 'a3aa6910', + 'rsrc/css/aphront/table-view.css' => '8c9bbafe', 'rsrc/css/aphront/tokenizer.css' => '15d5ff71', 'rsrc/css/aphront/tooltip.css' => '173b9431', 'rsrc/css/aphront/typeahead-browse.css' => 'f2818435', @@ -40,7 +40,7 @@ return array( 'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af', 'rsrc/css/application/auth/auth.css' => '0877ed6e', 'rsrc/css/application/base/main-menu-view.css' => '1802a242', - 'rsrc/css/application/base/notification-menu.css' => '73fefdfa', + 'rsrc/css/application/base/notification-menu.css' => '10685bd4', 'rsrc/css/application/base/phui-theme.css' => '9f261c6b', 'rsrc/css/application/base/standard-page-view.css' => '34ee718b', 'rsrc/css/application/chatlog/chatlog.css' => 'd295b020', @@ -74,8 +74,8 @@ return array( 'rsrc/css/application/diffusion/diffusion-icons.css' => '0c15255e', 'rsrc/css/application/diffusion/diffusion-readme.css' => '419dd5b6', 'rsrc/css/application/diffusion/diffusion-repository.css' => 'ee6f20ec', - 'rsrc/css/application/diffusion/diffusion-source.css' => '47db8a7c', - 'rsrc/css/application/diffusion/diffusion.css' => 'ceacf994', + 'rsrc/css/application/diffusion/diffusion-source.css' => '69ac9399', + 'rsrc/css/application/diffusion/diffusion.css' => '45727264', 'rsrc/css/application/feed/feed.css' => 'ecd4ec57', 'rsrc/css/application/files/global-drag-and-drop.css' => 'b556a948', 'rsrc/css/application/flag/flag.css' => 'bba8f811', @@ -110,7 +110,7 @@ return array( 'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5', 'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd', 'rsrc/css/application/releeph/releeph-request-typeahead.css' => '667a48ae', - 'rsrc/css/application/search/application-search-view.css' => '66ee5d46', + 'rsrc/css/application/search/application-search-view.css' => '787f5b76', 'rsrc/css/application/search/search-results.css' => '505dd8cf', 'rsrc/css/application/slowvote/slowvote.css' => 'a94b7230', 'rsrc/css/application/tokens/tokens.css' => '3d0f239e', @@ -127,7 +127,7 @@ return array( 'rsrc/css/layout/phabricator-source-code-view.css' => 'aea41829', 'rsrc/css/phui/button/phui-button-bar.css' => 'f1ff5494', 'rsrc/css/phui/button/phui-button-simple.css' => '8e1baf68', - 'rsrc/css/phui/button/phui-button.css' => 'a37aa3a8', + 'rsrc/css/phui/button/phui-button.css' => '1863cc6e', 'rsrc/css/phui/calendar/phui-calendar-day.css' => '572b1893', 'rsrc/css/phui/calendar/phui-calendar-list.css' => '576be600', 'rsrc/css/phui/calendar/phui-calendar-month.css' => '21154caf', @@ -141,7 +141,7 @@ return array( 'rsrc/css/phui/phui-action-list.css' => 'e7eba156', 'rsrc/css/phui/phui-action-panel.css' => 'b4798122', 'rsrc/css/phui/phui-badge.css' => '22c0cf4f', - 'rsrc/css/phui/phui-basic-nav-view.css' => 'a0705f53', + 'rsrc/css/phui/phui-basic-nav-view.css' => '98c11ab3', 'rsrc/css/phui/phui-big-info-view.css' => 'acc3492c', 'rsrc/css/phui/phui-box.css' => '745e881d', 'rsrc/css/phui/phui-chart.css' => '6bf6f78e', @@ -178,7 +178,7 @@ return array( 'rsrc/css/phui/phui-status.css' => 'd5263e49', 'rsrc/css/phui/phui-tag-view.css' => 'b4719c50', 'rsrc/css/phui/phui-timeline-view.css' => 'f21db7ca', - 'rsrc/css/phui/phui-two-column-view.css' => '76dcd3d4', + 'rsrc/css/phui/phui-two-column-view.css' => 'bf86c483', 'rsrc/css/phui/workboards/phui-workboard-color.css' => '783cdff5', 'rsrc/css/phui/workboards/phui-workboard.css' => '3bc85455', 'rsrc/css/phui/workboards/phui-workcard.css' => 'cca5fa92', @@ -529,7 +529,7 @@ return array( 'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8', 'rsrc/js/phuix/PHUIXActionView.js' => '442efd08', 'rsrc/js/phuix/PHUIXAutocomplete.js' => '4b7430ab', - 'rsrc/js/phuix/PHUIXButtonView.js' => 'a37126bd', + 'rsrc/js/phuix/PHUIXButtonView.js' => '8a91e1ac', 'rsrc/js/phuix/PHUIXDropdownMenu.js' => '8018ee50', 'rsrc/js/phuix/PHUIXExample.js' => '68af71ca', 'rsrc/js/phuix/PHUIXFormControl.js' => '83e03671', @@ -543,11 +543,11 @@ return array( 'aphront-list-filter-view-css' => '5d6f0526', 'aphront-multi-column-view-css' => '84cc6640', 'aphront-panel-view-css' => '8427b78d', - 'aphront-table-view-css' => 'a3aa6910', + 'aphront-table-view-css' => '8c9bbafe', 'aphront-tokenizer-control-css' => '15d5ff71', 'aphront-tooltip-css' => '173b9431', 'aphront-typeahead-control-css' => 'a4a21016', - 'application-search-view-css' => '66ee5d46', + 'application-search-view-css' => '787f5b76', 'auth-css' => '0877ed6e', 'bulk-job-css' => 'df9c1d4a', 'conduit-api-css' => '7bc725c4', @@ -570,11 +570,11 @@ return array( 'differential-revision-history-css' => '0e8eb855', 'differential-revision-list-css' => 'f3c47d33', 'differential-table-of-contents-css' => 'ae4b7a55', - 'diffusion-css' => 'ceacf994', + 'diffusion-css' => '45727264', 'diffusion-icons-css' => '0c15255e', 'diffusion-readme-css' => '419dd5b6', 'diffusion-repository-css' => 'ee6f20ec', - 'diffusion-source-css' => '47db8a7c', + 'diffusion-source-css' => '69ac9399', 'diviner-shared-css' => '896f1d43', 'font-fontawesome' => 'e838e088', 'font-lato' => 'c7ccd872', @@ -792,8 +792,8 @@ return array( 'phabricator-main-menu-view' => '1802a242', 'phabricator-nav-view-css' => 'faf6a6fc', 'phabricator-notification' => '5c3349b2', - 'phabricator-notification-css' => '3f6c89c9', - 'phabricator-notification-menu-css' => '73fefdfa', + 'phabricator-notification-css' => '457861ec', + 'phabricator-notification-menu-css' => '10685bd4', 'phabricator-object-selector-css' => '85ee8ce6', 'phabricator-phtize' => 'd254d646', 'phabricator-prefab' => 'c5af80a2', @@ -820,11 +820,11 @@ return array( 'phriction-document-css' => '4282e4ad', 'phui-action-panel-css' => 'b4798122', 'phui-badge-view-css' => '22c0cf4f', - 'phui-basic-nav-view-css' => 'a0705f53', + 'phui-basic-nav-view-css' => '98c11ab3', 'phui-big-info-view-css' => 'acc3492c', 'phui-box-css' => '745e881d', 'phui-button-bar-css' => 'f1ff5494', - 'phui-button-css' => 'a37aa3a8', + 'phui-button-css' => '1863cc6e', 'phui-button-simple-css' => '8e1baf68', 'phui-calendar-css' => 'f1ddf11c', 'phui-calendar-day-css' => '572b1893', @@ -874,7 +874,7 @@ return array( 'phui-tag-view-css' => 'b4719c50', 'phui-theme-css' => '9f261c6b', 'phui-timeline-view-css' => 'f21db7ca', - 'phui-two-column-view-css' => '76dcd3d4', + 'phui-two-column-view-css' => 'bf86c483', 'phui-workboard-color-css' => '783cdff5', 'phui-workboard-view-css' => '3bc85455', 'phui-workcard-view-css' => 'cca5fa92', @@ -882,7 +882,7 @@ return array( 'phuix-action-list-view' => 'b5c256b8', 'phuix-action-view' => '442efd08', 'phuix-autocomplete' => '4b7430ab', - 'phuix-button-view' => 'a37126bd', + 'phuix-button-view' => '8a91e1ac', 'phuix-dropdown-menu' => '8018ee50', 'phuix-form-control-view' => '83e03671', 'phuix-icon-view' => 'bff6884b', @@ -1557,6 +1557,10 @@ return array( 'javelin-install', 'javelin-dom', ), + '8a91e1ac' => array( + 'javelin-install', + 'javelin-dom', + ), '8ce821c5' => array( 'phabricator-notification', 'javelin-stratcom', @@ -1696,10 +1700,6 @@ return array( 'javelin-sound', 'phabricator-notification', ), - 'a37126bd' => array( - 'javelin-install', - 'javelin-dom', - ), 'a3a63478' => array( 'phui-workcard-view-css', ), diff --git a/resources/sql/autopatches/20170828.ferret.01.taskdoc.sql b/resources/sql/autopatches/20170828.ferret.01.taskdoc.sql new file mode 100644 index 0000000000..8cb6835602 --- /dev/null +++ b/resources/sql/autopatches/20170828.ferret.01.taskdoc.sql @@ -0,0 +1,9 @@ +CREATE TABLE {$NAMESPACE}_maniphest.maniphest_task_fdocument ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + objectPHID VARBINARY(64) NOT NULL, + isClosed BOOL NOT NULL, + authorPHID VARBINARY(64), + ownerPHID VARBINARY(64), + epochCreated INT UNSIGNED NOT NULL, + epochModified INT UNSIGNED NOT NULL +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20170828.ferret.02.taskfield.sql b/resources/sql/autopatches/20170828.ferret.02.taskfield.sql new file mode 100644 index 0000000000..5528feec8f --- /dev/null +++ b/resources/sql/autopatches/20170828.ferret.02.taskfield.sql @@ -0,0 +1,7 @@ +CREATE TABLE {$NAMESPACE}_maniphest.maniphest_task_ffield ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + documentID INT UNSIGNED NOT NULL, + fieldKey VARCHAR(4) NOT NULL COLLATE {$COLLATE_TEXT}, + rawCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT}, + normalCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT} +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20170828.ferret.03.taskngrams.sql b/resources/sql/autopatches/20170828.ferret.03.taskngrams.sql new file mode 100644 index 0000000000..a7b5180642 --- /dev/null +++ b/resources/sql/autopatches/20170828.ferret.03.taskngrams.sql @@ -0,0 +1,5 @@ +CREATE TABLE {$NAMESPACE}_maniphest.maniphest_task_fngrams ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + documentID INT UNSIGNED NOT NULL, + ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT} +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20170830.ferret.01.unique.sql b/resources/sql/autopatches/20170830.ferret.01.unique.sql new file mode 100644 index 0000000000..f76c5050e8 --- /dev/null +++ b/resources/sql/autopatches/20170830.ferret.01.unique.sql @@ -0,0 +1,4 @@ +TRUNCATE TABLE {$NAMESPACE}_maniphest.maniphest_task_ffield; + +ALTER TABLE {$NAMESPACE}_maniphest.maniphest_task_ffield + ADD UNIQUE KEY `key_documentfield` (documentID, fieldKey); diff --git a/resources/sql/autopatches/20170830.ferret.02.term.sql b/resources/sql/autopatches/20170830.ferret.02.term.sql new file mode 100644 index 0000000000..81a619d85d --- /dev/null +++ b/resources/sql/autopatches/20170830.ferret.02.term.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_maniphest.maniphest_task_ffield + ADD termCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT}; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 4575908ef8..ff30ddae98 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1533,6 +1533,10 @@ phutil_register_library_map(array( 'ManiphestTaskEditBulkJobType' => 'applications/maniphest/bulk/ManiphestTaskEditBulkJobType.php', 'ManiphestTaskEditController' => 'applications/maniphest/controller/ManiphestTaskEditController.php', 'ManiphestTaskEditEngineLock' => 'applications/maniphest/editor/ManiphestTaskEditEngineLock.php', + 'ManiphestTaskFerretDocument' => 'applications/maniphest/storage/ManiphestTaskFerretDocument.php', + 'ManiphestTaskFerretEngine' => 'applications/maniphest/search/ManiphestTaskFerretEngine.php', + 'ManiphestTaskFerretField' => 'applications/maniphest/storage/ManiphestTaskFerretField.php', + 'ManiphestTaskFerretNgrams' => 'applications/maniphest/storage/ManiphestTaskFerretNgrams.php', 'ManiphestTaskFulltextEngine' => 'applications/maniphest/search/ManiphestTaskFulltextEngine.php', 'ManiphestTaskGraph' => 'infrastructure/graph/ManiphestTaskGraph.php', 'ManiphestTaskHasCommitEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasCommitEdgeType.php', @@ -2828,6 +2832,12 @@ phutil_register_library_map(array( 'PhabricatorFeedStoryNotification' => 'applications/notification/storage/PhabricatorFeedStoryNotification.php', 'PhabricatorFeedStoryPublisher' => 'applications/feed/PhabricatorFeedStoryPublisher.php', 'PhabricatorFeedStoryReference' => 'applications/feed/storage/PhabricatorFeedStoryReference.php', + 'PhabricatorFerretDocument' => 'applications/search/ferret/PhabricatorFerretDocument.php', + 'PhabricatorFerretEngine' => 'applications/search/ferret/PhabricatorFerretEngine.php', + 'PhabricatorFerretField' => 'applications/search/ferret/PhabricatorFerretField.php', + 'PhabricatorFerretFulltextEngineExtension' => 'applications/search/engineextension/PhabricatorFerretFulltextEngineExtension.php', + 'PhabricatorFerretInterface' => 'applications/search/ferret/PhabricatorFerretInterface.php', + 'PhabricatorFerretNgrams' => 'applications/search/ferret/PhabricatorFerretNgrams.php', 'PhabricatorFile' => 'applications/files/storage/PhabricatorFile.php', 'PhabricatorFileAES256StorageFormat' => 'applications/files/format/PhabricatorFileAES256StorageFormat.php', 'PhabricatorFileBundleLoader' => 'applications/files/query/PhabricatorFileBundleLoader.php', @@ -3195,6 +3205,8 @@ phutil_register_library_map(array( 'PhabricatorNamedQueryQuery' => 'applications/search/query/PhabricatorNamedQueryQuery.php', 'PhabricatorNavigationRemarkupRule' => 'infrastructure/markup/rule/PhabricatorNavigationRemarkupRule.php', 'PhabricatorNeverTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorNeverTriggerClock.php', + 'PhabricatorNgramEngine' => 'applications/search/ngrams/PhabricatorNgramEngine.php', + 'PhabricatorNgramEngineTestCase' => 'applications/search/ngrams/__tests__/PhabricatorNgramEngineTestCase.php', 'PhabricatorNgramsIndexEngineExtension' => 'applications/search/engineextension/PhabricatorNgramsIndexEngineExtension.php', 'PhabricatorNgramsInterface' => 'applications/search/interface/PhabricatorNgramsInterface.php', 'PhabricatorNotificationBuilder' => 'applications/notification/builder/PhabricatorNotificationBuilder.php', @@ -6659,6 +6671,7 @@ phutil_register_library_map(array( 'PhabricatorSpacesInterface', 'PhabricatorConduitResultInterface', 'PhabricatorFulltextInterface', + 'PhabricatorFerretInterface', 'DoorkeeperBridgedObjectInterface', 'PhabricatorEditEngineSubtypeInterface', 'PhabricatorEditEngineLockableInterface', @@ -6682,6 +6695,10 @@ phutil_register_library_map(array( 'ManiphestTaskEditBulkJobType' => 'PhabricatorWorkerBulkJobType', 'ManiphestTaskEditController' => 'ManiphestController', 'ManiphestTaskEditEngineLock' => 'PhabricatorEditEngineLock', + 'ManiphestTaskFerretDocument' => 'PhabricatorFerretDocument', + 'ManiphestTaskFerretEngine' => 'PhabricatorFerretEngine', + 'ManiphestTaskFerretField' => 'PhabricatorFerretField', + 'ManiphestTaskFerretNgrams' => 'PhabricatorFerretNgrams', 'ManiphestTaskFulltextEngine' => 'PhabricatorFulltextEngine', 'ManiphestTaskGraph' => 'PhabricatorObjectGraph', 'ManiphestTaskHasCommitEdgeType' => 'PhabricatorEdgeType', @@ -8147,6 +8164,11 @@ phutil_register_library_map(array( 'PhabricatorFeedStoryNotification' => 'PhabricatorFeedDAO', 'PhabricatorFeedStoryPublisher' => 'Phobject', 'PhabricatorFeedStoryReference' => 'PhabricatorFeedDAO', + 'PhabricatorFerretDocument' => 'PhabricatorSearchDAO', + 'PhabricatorFerretEngine' => 'Phobject', + 'PhabricatorFerretField' => 'PhabricatorSearchDAO', + 'PhabricatorFerretFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension', + 'PhabricatorFerretNgrams' => 'PhabricatorSearchDAO', 'PhabricatorFile' => array( 'PhabricatorFileDAO', 'PhabricatorApplicationTransactionInterface', @@ -8565,6 +8587,8 @@ phutil_register_library_map(array( 'PhabricatorNamedQueryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorNavigationRemarkupRule' => 'PhutilRemarkupRule', 'PhabricatorNeverTriggerClock' => 'PhabricatorTriggerClock', + 'PhabricatorNgramEngine' => 'Phobject', + 'PhabricatorNgramEngineTestCase' => 'PhabricatorTestCase', 'PhabricatorNgramsIndexEngineExtension' => 'PhabricatorIndexEngineExtension', 'PhabricatorNgramsInterface' => 'PhabricatorIndexableInterface', 'PhabricatorNotificationBuilder' => 'Phobject', diff --git a/src/applications/diffusion/controller/DiffusionBranchTableController.php b/src/applications/diffusion/controller/DiffusionBranchTableController.php index 801b45a450..441421ba3f 100644 --- a/src/applications/diffusion/controller/DiffusionBranchTableController.php +++ b/src/applications/diffusion/controller/DiffusionBranchTableController.php @@ -57,6 +57,7 @@ final class DiffusionBranchTableController extends DiffusionController { $content = id(new PHUIObjectBoxView()) ->setHeaderText($repository->getName()) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->addClass('diffusion-mobile-view') ->setTable($list) ->setPager($pager); } diff --git a/src/applications/diffusion/controller/DiffusionBrowseController.php b/src/applications/diffusion/controller/DiffusionBrowseController.php index b9d63b2f85..076e667dc4 100644 --- a/src/applications/diffusion/controller/DiffusionBrowseController.php +++ b/src/applications/diffusion/controller/DiffusionBrowseController.php @@ -371,6 +371,7 @@ final class DiffusionBrowseController extends DiffusionController { ->setHeader($browse_header) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setTable($browse_table) + ->addClass('diffusion-mobile-view') ->setPager($pager); $path = $drequest->getPath(); @@ -595,6 +596,8 @@ final class DiffusionBrowseController extends DiffusionController { ), $rows); + $corpus_table = phutil_tag_div('diffusion-source-wrap', $corpus_table); + if ($this->getRequest()->isAjax()) { return $corpus_table; } @@ -654,6 +657,7 @@ final class DiffusionBrowseController extends DiffusionController { ->setHeader($header) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($corpus) + ->addClass('diffusion-mobile-view') ->setCollapsed(true); $messages = array(); @@ -730,6 +734,7 @@ final class DiffusionBrowseController extends DiffusionController { ->setText($blame_text) ->setIcon($blame_icon) ->setUser($viewer) + ->setSelected(!$blame_value) ->setColor(PHUIButtonView::GREY); if ($viewer->isLoggedIn()) { @@ -859,6 +864,7 @@ final class DiffusionBrowseController extends DiffusionController { $view = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Owner Packages')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->addClass('diffusion-mobile-view') ->setObjectList($ownership); } @@ -1134,7 +1140,7 @@ final class DiffusionBrowseController extends DiffusionController { $before_link = null; $commit_date = null; - $style = 'border-right: 2px solid '.$line['color'].';'; + $style = 'border-right: 3px solid '.$line['color'].';'; if ($identifier && !$line['duplicate']) { if (isset($commit_links[$identifier])) { @@ -1340,6 +1346,7 @@ final class DiffusionBrowseController extends DiffusionController { return id(new PHUIObjectBoxView()) ->setHeader($header) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->addClass('diffusion-mobile-view') ->addPropertyList($properties); } @@ -1360,6 +1367,7 @@ final class DiffusionBrowseController extends DiffusionController { $box = id(new PHUIObjectBoxView()) ->setHeader($header) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->addClass('diffusion-mobile-view') ->appendChild($text); return $box; @@ -1692,15 +1700,20 @@ final class DiffusionBrowseController extends DiffusionController { $header = id(new PHUIHeaderView()) ->setHeader(pht('Recently Open Revisions')); - $view = id(new DifferentialRevisionListView()) + $list = id(new DifferentialRevisionListView()) + ->setRevisions($revisions) + ->setUser($viewer) + ->setNoBox(true); + + $phids = $list->getRequiredHandlePHIDs(); + $handles = $this->loadViewerHandles($phids); + $list->setHandles($handles); + + $view = id(new PHUIObjectBoxView()) ->setHeader($header) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->setRevisions($revisions) - ->setUser($viewer); - - $phids = $view->getRequiredHandlePHIDs(); - $handles = $this->loadViewerHandles($phids); - $view->setHandles($handles); + ->addClass('diffusion-mobile-view') + ->appendChild($list); return $view; } @@ -1837,6 +1850,7 @@ final class DiffusionBrowseController extends DiffusionController { $corpus = id(new PHUIObjectBoxView()) ->setHeader($header) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->addClass('diffusion-mobile-view') ->setCollapsed(true); if ($messages) { @@ -1921,6 +1935,7 @@ final class DiffusionBrowseController extends DiffusionController { return id(new PHUIObjectBoxView()) ->setHeader($header) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->addClass('diffusion-mobile-view') ->setTable($history_table); } diff --git a/src/applications/diffusion/controller/DiffusionCompareController.php b/src/applications/diffusion/controller/DiffusionCompareController.php index a3104e6da4..2361c066dd 100644 --- a/src/applications/diffusion/controller/DiffusionCompareController.php +++ b/src/applications/diffusion/controller/DiffusionCompareController.php @@ -15,6 +15,7 @@ final class DiffusionCompareController extends DiffusionController { $viewer = $this->getViewer(); $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); + require_celerity_resource('diffusion-css'); if (!$repository->supportsBranchComparison()) { return $this->newDialog() @@ -315,6 +316,7 @@ final class DiffusionCompareController extends DiffusionController { ->setHeader($header) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setTable($history_table) + ->addClass('diffusion-mobile-view') ->setPager($pager); } diff --git a/src/applications/diffusion/controller/DiffusionGraphController.php b/src/applications/diffusion/controller/DiffusionGraphController.php index 5062fe9765..096204ac6d 100644 --- a/src/applications/diffusion/controller/DiffusionGraphController.php +++ b/src/applications/diffusion/controller/DiffusionGraphController.php @@ -67,6 +67,7 @@ final class DiffusionGraphController extends DiffusionController { ->setHeaderText(pht('History Graph')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setTable($graph) + ->addClass('diffusion-mobile-view') ->setPager($pager); $tabs = $this->buildTabsView('graph'); diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php index 120dd0d344..f64b72e1f8 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php @@ -420,7 +420,8 @@ final class DiffusionRepositoryController extends DiffusionController { $history_table->setIsHead(true); $panel = id(new PHUIObjectBoxView()) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY); + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->addClass('diffusion-mobile-view'); $header = id(new PHUIHeaderView()) ->setHeader(pht('Recent Commits')); $panel->setHeader($header); @@ -583,6 +584,7 @@ final class DiffusionRepositoryController extends DiffusionController { ->setHeaderText($header) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setTable($browse_table) + ->addClass('diffusion-mobile-view') ->setPager($pager); } diff --git a/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php index 17ee506b16..90b646c3f9 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php @@ -86,8 +86,11 @@ final class DiffusionRepositoryBranchesManagementPanel $repository->getHumanReadableDetail('close-commits-filter', array()), phutil_tag('em', array(), pht('Autoclose On All Branches'))); + $autoclose_disabled = false; if ($repository->getDetail('disable-autoclose')) { - $autoclose_only = phutil_tag('em', array(), pht('Disabled')); + $autoclose_disabled = true; + $autoclose_only = + phutil_tag('em', array(), pht('Autoclose has been disabled')); } $view->addProperty(pht('Autoclose Only'), $autoclose_only); @@ -133,12 +136,18 @@ final class DiffusionRepositoryBranchesManagementPanel $status = pht('Open'); } + if ($autoclose_disabled) { + $autoclose_status = pht('Disabled (Repository)'); + } else { + $autoclose_status = pht('Off'); + } + $rows[] = array( $icon, $branch_name, $status, $tracking ? pht('Tracking') : pht('Off'), - $autoclosing ? pht('Autoclose On') : pht('Off'), + $autoclosing ? pht('Autoclose On') : $autoclose_status, ); } $branch_table = new AphrontTableView($rows); diff --git a/src/applications/diffusion/view/DiffusionBrowseTableView.php b/src/applications/diffusion/view/DiffusionBrowseTableView.php index 48729d0669..ffbfe8986f 100644 --- a/src/applications/diffusion/view/DiffusionBrowseTableView.php +++ b/src/applications/diffusion/view/DiffusionBrowseTableView.php @@ -134,7 +134,7 @@ final class DiffusionBrowseTableView extends DiffusionView { array( true, false, - true, + false, false, false, )); diff --git a/src/applications/diffusion/view/DiffusionHistoryListView.php b/src/applications/diffusion/view/DiffusionHistoryListView.php index 8f9301c53a..62ba7b70c2 100644 --- a/src/applications/diffusion/view/DiffusionHistoryListView.php +++ b/src/applications/diffusion/view/DiffusionHistoryListView.php @@ -40,6 +40,7 @@ final class DiffusionHistoryListView extends DiffusionHistoryView { $view[] = id(new PHUIObjectBoxView()) ->setHeader($header) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->addClass('diffusion-mobile-view') ->setObjectList($list); } diff --git a/src/applications/diffusion/view/DiffusionReadmeView.php b/src/applications/diffusion/view/DiffusionReadmeView.php index 88e2e84115..693333249d 100644 --- a/src/applications/diffusion/view/DiffusionReadmeView.php +++ b/src/applications/diffusion/view/DiffusionReadmeView.php @@ -105,6 +105,7 @@ final class DiffusionReadmeView extends DiffusionView { return id(new PHUIObjectBoxView()) ->setHeader($header) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->addClass('diffusion-mobile-view') ->appendChild($document) ->addClass('diffusion-readme-view'); } diff --git a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php index 150ec81def..0e3e0db69f 100644 --- a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php +++ b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php @@ -49,6 +49,8 @@ final class ManiphestTaskSearchEngine $subtype_map = id(new ManiphestTask())->newEditEngineSubtypeMap(); $hide_subtypes = (count($subtype_map) == 1); + $hide_ferret = !PhabricatorEnv::getEnvConfig('phabricator.show-prototypes'); + return array( id(new PhabricatorOwnersSearchField()) ->setLabel(pht('Assigned To')) @@ -89,6 +91,10 @@ final class ManiphestTaskSearchEngine id(new PhabricatorSearchTextField()) ->setLabel(pht('Contains Words')) ->setKey('fulltext'), + id(new PhabricatorSearchTextField()) + ->setLabel(pht('Query (Prototype)')) + ->setKey('query') + ->setIsHidden($hide_ferret), id(new PhabricatorSearchThreeStateField()) ->setLabel(pht('Open Parents')) ->setKey('hasParents') @@ -144,6 +150,7 @@ final class ManiphestTaskSearchEngine 'statuses', 'priorities', 'subtypes', + 'query', 'fulltext', 'hasParents', 'hasSubtasks', @@ -224,6 +231,27 @@ final class ManiphestTaskSearchEngine $query->withFullTextSearch($map['fulltext']); } + if (strlen($map['query'])) { + $raw_query = $map['query']; + + $compiler = id(new PhutilSearchQueryCompiler()) + ->setEnableFunctions(true); + + $raw_tokens = $compiler->newTokens($raw_query); + + $fulltext_tokens = array(); + foreach ($raw_tokens as $raw_token) { + $fulltext_token = id(new PhabricatorFulltextToken()) + ->setToken($raw_token); + + $fulltext_tokens[] = $fulltext_token; + } + + $query->withFerretConstraint( + id(new ManiphestTask())->newFerretEngine(), + $fulltext_tokens); + } + if ($map['parentIDs']) { $query->withParentTaskIDs($map['parentIDs']); } diff --git a/src/applications/maniphest/search/ManiphestTaskFerretEngine.php b/src/applications/maniphest/search/ManiphestTaskFerretEngine.php new file mode 100644 index 0000000000..7232d0251f --- /dev/null +++ b/src/applications/maniphest/search/ManiphestTaskFerretEngine.php @@ -0,0 +1,18 @@ +getStr('chronoKey'); if ($request->isDialogFormPost()) { + $should_clear = true; + } else { + try { + $request->validateCSRF(); + $should_clear = true; + } catch (AphrontMalformedRequestException $ex) { + $should_clear = false; + } + } + + if ($should_clear) { $table = new PhabricatorFeedStoryNotification(); queryfx( diff --git a/src/applications/notification/controller/PhabricatorNotificationPanelController.php b/src/applications/notification/controller/PhabricatorNotificationPanelController.php index be7eeb914e..3e30ead9e7 100644 --- a/src/applications/notification/controller/PhabricatorNotificationPanelController.php +++ b/src/applications/notification/controller/PhabricatorNotificationPanelController.php @@ -9,7 +9,7 @@ final class PhabricatorNotificationPanelController $query = id(new PhabricatorNotificationQuery()) ->setViewer($viewer) ->withUserPHIDs(array($viewer->getPHID())) - ->setLimit(15); + ->setLimit(10); $stories = $query->execute(); diff --git a/src/applications/project/engineextension/PhabricatorProjectsFulltextEngineExtension.php b/src/applications/project/engineextension/PhabricatorProjectsFulltextEngineExtension.php index 857783f811..79f395ebaa 100644 --- a/src/applications/project/engineextension/PhabricatorProjectsFulltextEngineExtension.php +++ b/src/applications/project/engineextension/PhabricatorProjectsFulltextEngineExtension.php @@ -9,11 +9,11 @@ final class PhabricatorProjectsFulltextEngineExtension return pht('Projects'); } - public function shouldIndexFulltextObject($object) { + public function shouldEnrichFulltextObject($object) { return ($object instanceof PhabricatorProjectInterface); } - public function indexFulltextObject( + public function enrichFulltextObject( $object, PhabricatorSearchAbstractDocument $document) { diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php index 780f072678..955eba9972 100644 --- a/src/applications/project/query/PhabricatorProjectQuery.php +++ b/src/applications/project/query/PhabricatorProjectQuery.php @@ -609,7 +609,8 @@ final class PhabricatorProjectQuery } if ($this->nameTokens !== null) { - foreach ($this->nameTokens as $key => $token) { + $name_tokens = $this->getNameTokensForQuery($this->nameTokens); + foreach ($name_tokens as $key => $token) { $token_table = 'token_'.$key; $joins[] = qsprintf( $conn, @@ -797,4 +798,22 @@ final class PhabricatorProjectQuery } } + private function getNameTokensForQuery(array $tokens) { + // When querying for projects by name, only actually search for the five + // longest tokens. MySQL can get grumpy with a large number of JOINs + // with LIKEs and queries for more than 5 tokens are essentially never + // legitimate searches for projects, but users copy/pasting nonsense. + // See also PHI47. + + $length_map = array(); + foreach ($tokens as $token) { + $length_map[$token] = strlen($token); + } + arsort($length_map); + + $length_map = array_slice($length_map, 0, 5, true); + + return array_keys($length_map); + } + } diff --git a/src/applications/search/constants/PhabricatorSearchDocumentFieldType.php b/src/applications/search/constants/PhabricatorSearchDocumentFieldType.php index 10dbf0ca65..42fc2738f3 100644 --- a/src/applications/search/constants/PhabricatorSearchDocumentFieldType.php +++ b/src/applications/search/constants/PhabricatorSearchDocumentFieldType.php @@ -5,5 +5,7 @@ final class PhabricatorSearchDocumentFieldType extends Phobject { const FIELD_TITLE = 'titl'; const FIELD_BODY = 'body'; const FIELD_COMMENT = 'cmnt'; + const FIELD_ALL = 'full'; + const FIELD_CORE = 'core'; } diff --git a/src/applications/search/engineextension/PhabricatorFerretFulltextEngineExtension.php b/src/applications/search/engineextension/PhabricatorFerretFulltextEngineExtension.php new file mode 100644 index 0000000000..1c9efeba9d --- /dev/null +++ b/src/applications/search/engineextension/PhabricatorFerretFulltextEngineExtension.php @@ -0,0 +1,197 @@ +getPHID(); + $engine = $object->newFerretEngine(); + + $ferret_document = $engine->newDocumentObject() + ->setObjectPHID($phid) + ->setIsClosed(0) + ->setEpochCreated(0) + ->setEpochModified(0); + + $stemmer = new PhutilSearchStemmer(); + $ngram_engine = id(new PhabricatorNgramEngine()); + + // Copy all of the "title" and "body" fields to create new "core" fields. + // This allows users to search "in title or body" with the "core:" prefix. + $document_fields = $document->getFieldData(); + $virtual_fields = array(); + foreach ($document_fields as $field) { + $virtual_fields[] = $field; + + list($key, $raw_corpus) = $field; + switch ($key) { + case PhabricatorSearchDocumentFieldType::FIELD_TITLE: + case PhabricatorSearchDocumentFieldType::FIELD_BODY: + $virtual_fields[] = array( + PhabricatorSearchDocumentFieldType::FIELD_CORE, + $raw_corpus, + ); + break; + } + } + + $key_all = PhabricatorSearchDocumentFieldType::FIELD_ALL; + + $empty_template = array( + 'raw' => array(), + 'term' => array(), + 'normal' => array(), + ); + + $ferret_corpus_map = array( + $key_all => $empty_template, + ); + + foreach ($virtual_fields as $field) { + list($key, $raw_corpus) = $field; + if (!strlen($raw_corpus)) { + continue; + } + + $term_corpus = $ngram_engine->newTermsCorpus($raw_corpus); + + $normal_corpus = $stemmer->stemCorpus($raw_corpus); + $normal_coprus = $ngram_engine->newTermsCorpus($normal_corpus); + + if (!isset($ferret_corpus_map[$key])) { + $ferret_corpus_map[$key] = $empty_template; + } + + $ferret_corpus_map[$key]['raw'][] = $raw_corpus; + $ferret_corpus_map[$key]['term'][] = $term_corpus; + $ferret_corpus_map[$key]['normal'][] = $normal_corpus; + + $ferret_corpus_map[$key_all]['raw'][] = $raw_corpus; + $ferret_corpus_map[$key_all]['term'][] = $term_corpus; + $ferret_corpus_map[$key_all]['normal'][] = $normal_corpus; + } + + $ferret_fields = array(); + $ngrams_source = array(); + foreach ($ferret_corpus_map as $key => $fields) { + $raw_corpus = $fields['raw']; + $raw_corpus = implode("\n", $raw_corpus); + $ngrams_source[] = $raw_corpus; + + $normal_corpus = $fields['normal']; + $normal_corpus = implode(' ', $normal_corpus); + if (strlen($normal_corpus)) { + $ngrams_source[] = $normal_corpus; + $normal_corpus = ' '.$normal_corpus.' '; + } + + $term_corpus = $fields['term']; + $term_corpus = implode(' ', $term_corpus); + if (strlen($term_corpus)) { + $ngrams_source[] = $term_corpus; + $term_corpus = ' '.$term_corpus.' '; + } + + $ferret_fields[] = $engine->newFieldObject() + ->setFieldKey($key) + ->setRawCorpus($raw_corpus) + ->setTermCorpus($term_corpus) + ->setNormalCorpus($normal_corpus); + } + $ngrams_source = implode(' ', $ngrams_source); + + $ngrams = $ngram_engine->getNgramsFromString($ngrams_source, 'index'); + + $ferret_document->openTransaction(); + + try { + $this->deleteOldDocument($engine, $object, $document); + + $ferret_document->save(); + + $document_id = $ferret_document->getID(); + foreach ($ferret_fields as $ferret_field) { + $ferret_field + ->setDocumentID($document_id) + ->save(); + } + + $ferret_ngrams = $engine->newNgramsObject(); + $conn = $ferret_ngrams->establishConnection('w'); + + $sql = array(); + foreach ($ngrams as $ngram) { + $sql[] = qsprintf( + $conn, + '(%d, %s)', + $document_id, + $ngram); + } + + foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { + queryfx( + $conn, + 'INSERT INTO %T (documentID, ngram) VALUES %Q', + $ferret_ngrams->getTableName(), + $chunk); + } + } catch (Exception $ex) { + $ferret_document->killTransaction(); + throw $ex; + } + + $ferret_document->saveTransaction(); + } + + + private function deleteOldDocument( + PhabricatorFerretEngine $engine, + $object, + PhabricatorSearchAbstractDocument $document) { + + $old_document = $engine->newDocumentObject()->loadOneWhere( + 'objectPHID = %s', + $document->getPHID()); + if (!$old_document) { + return; + } + + $conn = $old_document->establishConnection('w'); + $old_id = $old_document->getID(); + + queryfx( + $conn, + 'DELETE FROM %T WHERE id = %d', + $engine->newDocumentObject()->getTableName(), + $old_id); + + queryfx( + $conn, + 'DELETE FROM %T WHERE documentID = %d', + $engine->newFieldObject()->getTableName(), + $old_id); + + queryfx( + $conn, + 'DELETE FROM %T WHERE documentID = %d', + $engine->newNgramsObject()->getTableName(), + $old_id); + } + +} diff --git a/src/applications/search/engineextension/PhabricatorLiskFulltextEngineExtension.php b/src/applications/search/engineextension/PhabricatorLiskFulltextEngineExtension.php index 9cbe384a10..1626d248da 100644 --- a/src/applications/search/engineextension/PhabricatorLiskFulltextEngineExtension.php +++ b/src/applications/search/engineextension/PhabricatorLiskFulltextEngineExtension.php @@ -9,7 +9,7 @@ final class PhabricatorLiskFulltextEngineExtension return pht('Lisk Builtin Properties'); } - public function shouldIndexFulltextObject($object) { + public function shouldEnrichFulltextObject($object) { if (!($object instanceof PhabricatorLiskDAO)) { return false; } @@ -21,7 +21,7 @@ final class PhabricatorLiskFulltextEngineExtension return true; } - public function indexFulltextObject( + public function enrichFulltextObject( $object, PhabricatorSearchAbstractDocument $document) { diff --git a/src/applications/search/ferret/PhabricatorFerretDocument.php b/src/applications/search/ferret/PhabricatorFerretDocument.php new file mode 100644 index 0000000000..fa816c8d17 --- /dev/null +++ b/src/applications/search/ferret/PhabricatorFerretDocument.php @@ -0,0 +1,40 @@ + false, + self::CONFIG_COLUMN_SCHEMA => array( + 'isClosed' => 'bool', + 'authorPHID' => 'phid?', + 'ownerPHID' => 'phid?', + 'epochCreated' => 'epoch', + 'epochModified' => 'epoch', + ), + self::CONFIG_KEY_SCHEMA => array( + 'key_object' => array( + 'columns' => array('objectPHID'), + 'unique' => true, + ), + ), + ) + parent::getConfiguration(); + } + + public function getTableName() { + $application = $this->getApplicationName(); + $key = $this->getIndexKey(); + return "{$application}_{$key}_fdocument"; + } + +} diff --git a/src/applications/search/ferret/PhabricatorFerretEngine.php b/src/applications/search/ferret/PhabricatorFerretEngine.php new file mode 100644 index 0000000000..e816ef3f59 --- /dev/null +++ b/src/applications/search/ferret/PhabricatorFerretEngine.php @@ -0,0 +1,9 @@ + false, + self::CONFIG_COLUMN_SCHEMA => array( + 'documentID' => 'uint32', + 'fieldKey' => 'text4', + 'rawCorpus' => 'sort', + 'termCorpus' => 'sort', + 'normalCorpus' => 'sort', + ), + self::CONFIG_KEY_SCHEMA => array( + 'key_documentfield' => array( + 'columns' => array('documentID', 'fieldKey'), + 'unique' => true, + ), + ), + ) + parent::getConfiguration(); + } + + public function getTableName() { + $application = $this->getApplicationName(); + $key = $this->getIndexKey(); + return "{$application}_{$key}_ffield"; + } + +} diff --git a/src/applications/search/ferret/PhabricatorFerretInterface.php b/src/applications/search/ferret/PhabricatorFerretInterface.php new file mode 100644 index 0000000000..cdb651b6cf --- /dev/null +++ b/src/applications/search/ferret/PhabricatorFerretInterface.php @@ -0,0 +1,7 @@ + false, + self::CONFIG_COLUMN_SCHEMA => array( + 'documentID' => 'uint32', + 'ngram' => 'char3', + ), + self::CONFIG_KEY_SCHEMA => array( + 'key_ngram' => array( + 'columns' => array('ngram', 'documentID'), + ), + 'key_object' => array( + 'columns' => array('documentID'), + ), + ), + ) + parent::getConfiguration(); + } + + public function getTableName() { + $application = $this->getApplicationName(); + $key = $this->getIndexKey(); + return "{$application}_{$key}_fngrams"; + } + +} diff --git a/src/applications/search/index/PhabricatorFulltextEngine.php b/src/applications/search/index/PhabricatorFulltextEngine.php index 9f20917b3f..e1b2f4471d 100644 --- a/src/applications/search/index/PhabricatorFulltextEngine.php +++ b/src/applications/search/index/PhabricatorFulltextEngine.php @@ -26,9 +26,16 @@ abstract class PhabricatorFulltextEngine $object = $this->getObject(); $extensions = PhabricatorFulltextEngineExtension::getAllExtensions(); + + $enrich_extensions = array(); + $index_extensions = array(); foreach ($extensions as $key => $extension) { - if (!$extension->shouldIndexFulltextObject($object)) { - unset($extensions[$key]); + if ($extension->shouldEnrichFulltextObject($object)) { + $enrich_extensions[] = $extension; + } + + if ($extension->shouldIndexFulltextObject($object)) { + $index_extensions[] = $extension; } } @@ -36,7 +43,11 @@ abstract class PhabricatorFulltextEngine $this->buildAbstractDocument($document, $object); - foreach ($extensions as $extension) { + foreach ($enrich_extensions as $extension) { + $extension->enrichFulltextObject($object, $document); + } + + foreach ($index_extensions as $extension) { $extension->indexFulltextObject($object, $document); } diff --git a/src/applications/search/index/PhabricatorFulltextEngineExtension.php b/src/applications/search/index/PhabricatorFulltextEngineExtension.php index c52b35a58a..52aabe7c8b 100644 --- a/src/applications/search/index/PhabricatorFulltextEngineExtension.php +++ b/src/applications/search/index/PhabricatorFulltextEngineExtension.php @@ -12,11 +12,25 @@ abstract class PhabricatorFulltextEngineExtension extends Phobject { abstract public function getExtensionName(); - abstract public function shouldIndexFulltextObject($object); + public function shouldEnrichFulltextObject($object) { + return false; + } - abstract public function indexFulltextObject( + public function enrichFulltextObject( $object, - PhabricatorSearchAbstractDocument $document); + PhabricatorSearchAbstractDocument $document) { + return; + } + + public function shouldIndexFulltextObject($object) { + return false; + } + + public function indexFulltextObject( + $object, + PhabricatorSearchAbstractDocument $document) { + return; + } final public static function getAllExtensions() { return id(new PhutilClassMapQuery()) diff --git a/src/applications/search/ngrams/PhabricatorNgramEngine.php b/src/applications/search/ngrams/PhabricatorNgramEngine.php new file mode 100644 index 0000000000..f8f55d8757 --- /dev/null +++ b/src/applications/search/ngrams/PhabricatorNgramEngine.php @@ -0,0 +1,95 @@ +tokenizeString($value); + + $ngrams = array(); + foreach ($tokens as $token) { + $token = phutil_utf8_strtolower($token); + + switch ($mode) { + case 'query': + break; + case 'index': + $token = ' '.$token.' '; + break; + case 'prefix': + $token = ' '.$token; + break; + } + + $token_v = phutil_utf8v($token); + $len = (count($token_v) - 2); + for ($ii = 0; $ii < $len; $ii++) { + $ngram = array_slice($token_v, $ii, 3); + $ngram = implode('', $ngram); + $ngrams[$ngram] = $ngram; + } + } + + ksort($ngrams); + + return array_keys($ngrams); + } + + public function newTermsCorpus($raw_corpus) { + $term_corpus = strtr( + $raw_corpus, + array( + '!' => ' ', + '"' => ' ', + '#' => ' ', + '$' => ' ', + '%' => ' ', + '&' => ' ', + '(' => ' ', + ')' => ' ', + '*' => ' ', + '+' => ' ', + ',' => ' ', + '-' => ' ', + '/' => ' ', + ':' => ' ', + ';' => ' ', + '<' => ' ', + '=' => ' ', + '>' => ' ', + '?' => ' ', + '@' => ' ', + '[' => ' ', + '\\' => ' ', + ']' => ' ', + '^' => ' ', + '`' => ' ', + '{' => ' ', + '|' => ' ', + '}' => ' ', + '~' => ' ', + '.' => ' ', + '_' => ' ', + "\n" => ' ', + "\r" => ' ', + "\t" => ' ', + )); + + // NOTE: Single quotes divide terms only if they're at a word boundary. + // In contractions, like "whom'st've", the entire word is a single term. + $term_corpus = preg_replace('/(^| )[\']+/', ' ', $term_corpus); + $term_corpus = preg_replace('/[\']+( |$)/', ' ', $term_corpus); + + $term_corpus = preg_replace('/\s+/u', ' ', $term_corpus); + $term_corpus = trim($term_corpus, ' '); + + return $term_corpus; + } + + +} diff --git a/src/applications/search/ngrams/__tests__/PhabricatorNgramEngineTestCase.php b/src/applications/search/ngrams/__tests__/PhabricatorNgramEngineTestCase.php new file mode 100644 index 0000000000..fccb6fb324 --- /dev/null +++ b/src/applications/search/ngrams/__tests__/PhabricatorNgramEngineTestCase.php @@ -0,0 +1,26 @@ + 'Hear ye hear ye', + "Thou whom'st've art worthy." => "Thou whom'st've art worthy", + 'Guaranteed to contain "food".' => 'Guaranteed to contain food', + 'http://example.org/path/to/file.jpg' => + 'http example org path to file jpg', + ); + + $engine = new PhabricatorNgramEngine(); + foreach ($map as $input => $expect) { + $actual = $engine->newTermsCorpus($input); + + $this->assertEqual( + $expect, + $actual, + pht('Terms corpus for: %s', $input)); + } + } + +} diff --git a/src/applications/settings/controller/PhabricatorSettingsMainController.php b/src/applications/settings/controller/PhabricatorSettingsMainController.php index 1e4d4a0b70..9dc84a9bd0 100644 --- a/src/applications/settings/controller/PhabricatorSettingsMainController.php +++ b/src/applications/settings/controller/PhabricatorSettingsMainController.php @@ -205,7 +205,10 @@ final class PhabricatorSettingsMainController if ($panel->getPanelGroupKey() != $group_key) { $group_key = $panel->getPanelGroupKey(); $group = $panel->getPanelGroup(); - $nav->addLabel($group->getPanelGroupName()); + $panel_name = $group->getPanelGroupName(); + if ($panel_name) { + $nav->addLabel($panel_name); + } } $nav->addFilter($panel->getPanelKey(), $panel->getPanelName()); diff --git a/src/applications/settings/panelgroup/PhabricatorSettingsAccountPanelGroup.php b/src/applications/settings/panelgroup/PhabricatorSettingsAccountPanelGroup.php index f81d5d487a..826118b881 100644 --- a/src/applications/settings/panelgroup/PhabricatorSettingsAccountPanelGroup.php +++ b/src/applications/settings/panelgroup/PhabricatorSettingsAccountPanelGroup.php @@ -6,7 +6,7 @@ final class PhabricatorSettingsAccountPanelGroup const PANELGROUPKEY = 'account'; public function getPanelGroupName() { - return pht('Account'); + return null; } protected function getPanelGroupOrder() { diff --git a/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsFulltextEngineExtension.php b/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsFulltextEngineExtension.php index 3c25618ebe..e7876136a1 100644 --- a/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsFulltextEngineExtension.php +++ b/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsFulltextEngineExtension.php @@ -9,11 +9,11 @@ final class PhabricatorSubscriptionsFulltextEngineExtension return pht('Subscribers'); } - public function shouldIndexFulltextObject($object) { + public function shouldEnrichFulltextObject($object) { return ($object instanceof PhabricatorSubscribableInterface); } - public function indexFulltextObject( + public function enrichFulltextObject( $object, PhabricatorSearchAbstractDocument $document) { diff --git a/src/applications/transactions/engineextension/PhabricatorTransactionsFulltextEngineExtension.php b/src/applications/transactions/engineextension/PhabricatorTransactionsFulltextEngineExtension.php index b95fc0167a..701ad34ca7 100644 --- a/src/applications/transactions/engineextension/PhabricatorTransactionsFulltextEngineExtension.php +++ b/src/applications/transactions/engineextension/PhabricatorTransactionsFulltextEngineExtension.php @@ -9,11 +9,11 @@ final class PhabricatorTransactionsFulltextEngineExtension return pht('Comments'); } - public function shouldIndexFulltextObject($object) { + public function shouldEnrichFulltextObject($object) { return ($object instanceof PhabricatorApplicationTransactionInterface); } - public function indexFulltextObject( + public function enrichFulltextObject( $object, PhabricatorSearchAbstractDocument $document) { diff --git a/src/applications/uiexample/examples/PHUIButtonExample.php b/src/applications/uiexample/examples/PHUIButtonExample.php index f6e8fb526c..89f8e15fe8 100644 --- a/src/applications/uiexample/examples/PHUIButtonExample.php +++ b/src/applications/uiexample/examples/PHUIButtonExample.php @@ -83,9 +83,11 @@ final class PHUIButtonExample extends PhabricatorUIExample { ), array( 'icon' => 'fa-upload', + 'disabled' => true, ), array( 'icon' => 'fa-street-view', + 'selected' => true, ), array( 'text' => pht('Copy "Quack" to Clipboard'), @@ -99,6 +101,8 @@ final class PHUIButtonExample extends PhabricatorUIExample { ->setColor(PHUIButtonView::GREY) ->setIcon(idx($spec, 'icon')) ->setText(idx($spec, 'text')) + ->setSelected(idx($spec, 'selected')) + ->setDisabled(idx($spec, 'disabled')) ->addClass(PHUI::MARGIN_SMALL_RIGHT) ->setDropdown(idx($spec, 'dropdown')); diff --git a/src/infrastructure/customfield/engineextension/PhabricatorCustomFieldFulltextEngineExtension.php b/src/infrastructure/customfield/engineextension/PhabricatorCustomFieldFulltextEngineExtension.php index 8186ff5311..9e0684cacc 100644 --- a/src/infrastructure/customfield/engineextension/PhabricatorCustomFieldFulltextEngineExtension.php +++ b/src/infrastructure/customfield/engineextension/PhabricatorCustomFieldFulltextEngineExtension.php @@ -9,11 +9,11 @@ final class PhabricatorCustomFieldFulltextEngineExtension return pht('Custom Fields'); } - public function shouldIndexFulltextObject($object) { + public function shouldEnrichFulltextObject($object) { return ($object instanceof PhabricatorCustomFieldInterface); } - public function indexFulltextObject( + public function enrichFulltextObject( $object, PhabricatorSearchAbstractDocument $document) { diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php index 3ef2a72e6f..03d56bc1d2 100644 --- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php @@ -27,6 +27,9 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery private $spacePHIDs; private $spaceIsArchived; private $ngrams = array(); + private $ferretEngine; + private $ferretTokens; + private $ferretTables; protected function getPageCursors(array $page) { return array( @@ -270,6 +273,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery $joins[] = $this->buildEdgeLogicJoinClause($conn); $joins[] = $this->buildApplicationSearchJoinClause($conn); $joins[] = $this->buildNgramsJoinClause($conn); + $joins[] = $this->buildFerretJoinClause($conn); return $joins; } @@ -292,6 +296,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery $where[] = $this->buildEdgeLogicWhereClause($conn); $where[] = $this->buildSpacesWhereClause($conn); $where[] = $this->buildNgramsWhereClause($conn); + $where[] = $this->buildFerretWhereClause($conn); return $where; } @@ -346,6 +351,10 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery return true; } + if ($this->shouldGroupFerretResultRows()) { + return true; + } + return false; } @@ -1373,6 +1382,332 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery } +/* -( Ferret )------------------------------------------------------------- */ + + + public function withFerretConstraint( + PhabricatorFerretEngine $engine, + array $fulltext_tokens) { + + if ($this->ferretEngine) { + throw new Exception( + pht( + 'Query may not have multiple fulltext constraints.')); + } + + if (!$fulltext_tokens) { + return $this; + } + + $this->ferretEngine = $engine; + $this->ferretTokens = $fulltext_tokens; + + + $function_map = array( + 'all' => PhabricatorSearchDocumentFieldType::FIELD_ALL, + 'title' => PhabricatorSearchDocumentFieldType::FIELD_TITLE, + 'body' => PhabricatorSearchDocumentFieldType::FIELD_BODY, + 'core' => PhabricatorSearchDocumentFieldType::FIELD_CORE, + ); + + $current_function = 'all'; + $table_map = array(); + $idx = 1; + foreach ($this->ferretTokens as $fulltext_token) { + $raw_token = $fulltext_token->getToken(); + $function = $raw_token->getFunction(); + + if ($function === null) { + $function = $current_function; + } + + if (!isset($function_map[$function])) { + throw new PhutilSearchQueryCompilerSyntaxException( + pht( + 'Unknown search function "%s".', + $function)); + } + + if (!isset($table_map[$function])) { + $alias = 'ftfield'.$idx++; + $table_map[$function] = array( + 'alias' => $alias, + 'key' => $function_map[$function], + ); + } + + $current_function = $function; + } + + $this->ferretTables = $table_map; + + return $this; + } + + protected function buildFerretJoinClause(AphrontDatabaseConnection $conn) { + if (!$this->ferretEngine) { + return array(); + } + + $op_sub = PhutilSearchQueryCompiler::OPERATOR_SUBSTRING; + $op_not = PhutilSearchQueryCompiler::OPERATOR_NOT; + + $engine = $this->ferretEngine; + $ngram_engine = new PhabricatorNgramEngine(); + $stemmer = new PhutilSearchStemmer(); + + $ngram_table = $engine->newNgramsObject(); + $ngram_table_name = $ngram_table->getTableName(); + + $flat = array(); + foreach ($this->ferretTokens as $fulltext_token) { + $raw_token = $fulltext_token->getToken(); + + // If this is a negated term like "-pomegranate", don't join the ngram + // table since we aren't looking for documents with this term. (We could + // LEFT JOIN the table and require a NULL row, but this is probably more + // trouble than it's worth.) + if ($raw_token->getOperator() == $op_not) { + continue; + } + + $value = $raw_token->getValue(); + + $length = count(phutil_utf8v($value)); + + if ($raw_token->getOperator() == $op_sub) { + $is_substring = true; + } else { + $is_substring = false; + } + + // If the user specified a substring query for a substring which is + // shorter than the ngram length, we can't use the ngram index, so + // don't do a join. We'll fall back to just doing LIKE on the full + // corpus. + if ($is_substring) { + if ($length < 3) { + continue; + } + } + + if ($raw_token->isQuoted()) { + $is_stemmed = false; + } else { + $is_stemmed = true; + } + + if ($is_substring) { + $ngrams = $ngram_engine->getNgramsFromString($value, 'query'); + } else { + $ngrams = $ngram_engine->getNgramsFromString($value, 'index'); + + // If this is a stemmed term, only look for ngrams present in both the + // unstemmed and stemmed variations. + if ($is_stemmed) { + $stem_value = $stemmer->stemToken($value); + $stem_ngrams = $ngram_engine->getNgramsFromString( + $stem_value, + 'index'); + + $ngrams = array_intersect($ngrams, $stem_ngrams); + } + } + + foreach ($ngrams as $ngram) { + $flat[] = array( + 'table' => $ngram_table_name, + 'ngram' => $ngram, + ); + } + } + + // MySQL only allows us to join a maximum of 61 tables per query. Each + // ngram is going to cost us a join toward that limit, so if the user + // specified a very long query string, just pick 16 of the ngrams + // at random. + if (count($flat) > 16) { + shuffle($flat); + $flat = array_slice($flat, 0, 16); + } + + $alias = $this->getPrimaryTableAlias(); + if ($alias) { + $phid_column = qsprintf($conn, '%T.%T', $alias, 'phid'); + } else { + $phid_column = qsprintf($conn, '%T', 'phid'); + } + + $document_table = $engine->newDocumentObject(); + $field_table = $engine->newFieldObject(); + + $joins = array(); + $joins[] = qsprintf( + $conn, + 'JOIN %T ftdoc ON ftdoc.objectPHID = %Q', + $document_table->getTableName(), + $phid_column); + + $idx = 1; + foreach ($flat as $spec) { + $table = $spec['table']; + $ngram = $spec['ngram']; + + $alias = 'ft'.$idx++; + + $joins[] = qsprintf( + $conn, + 'JOIN %T %T ON %T.documentID = ftdoc.id AND %T.ngram = %s', + $table, + $alias, + $alias, + $alias, + $ngram); + } + + foreach ($this->ferretTables as $table) { + $alias = $table['alias']; + + $joins[] = qsprintf( + $conn, + 'JOIN %T %T ON ftdoc.id = %T.documentID + AND %T.fieldKey = %s', + $field_table->getTableName(), + $alias, + $alias, + $alias, + $table['key']); + } + + return $joins; + } + + protected function buildFerretWhereClause(AphrontDatabaseConnection $conn) { + if (!$this->ferretEngine) { + return array(); + } + + $ngram_engine = new PhabricatorNgramEngine(); + $stemmer = new PhutilSearchStemmer(); + $table_map = $this->ferretTables; + + $op_sub = PhutilSearchQueryCompiler::OPERATOR_SUBSTRING; + $op_not = PhutilSearchQueryCompiler::OPERATOR_NOT; + + $where = array(); + $current_function = 'all'; + foreach ($this->ferretTokens as $fulltext_token) { + $raw_token = $fulltext_token->getToken(); + $value = $raw_token->getValue(); + + $function = $raw_token->getFunction(); + if ($function === null) { + $function = $current_function; + } + $current_function = $function; + + $table_alias = $table_map[$function]['alias']; + + $is_not = ($raw_token->getOperator() == $op_not); + + if ($raw_token->getOperator() == $op_sub) { + $is_substring = true; + } else { + $is_substring = false; + } + + // If we're doing substring search, we just match against the raw corpus + // and we're done. + if ($is_substring) { + if ($is_not) { + $where[] = qsprintf( + $conn, + '(%T.rawCorpus NOT LIKE %~)', + $table_alias, + $value); + } else { + $where[] = qsprintf( + $conn, + '(%T.rawCorpus LIKE %~)', + $table_alias, + $value); + } + continue; + } + + // Otherwise, we need to match against the term corpus and the normal + // corpus, so that searching for "raw" does not find "strawberry". + if ($raw_token->isQuoted()) { + $is_quoted = true; + $is_stemmed = false; + } else { + $is_quoted = false; + $is_stemmed = true; + } + + // Never stem negated queries, since this can exclude results users + // did not mean to exclude and generally confuse things. + if ($is_not) { + $is_stemmed = false; + } + + $term_constraints = array(); + + $term_value = ' '.$ngram_engine->newTermsCorpus($value).' '; + if ($is_not) { + $term_constraints[] = qsprintf( + $conn, + '(%T.termCorpus NOT LIKE %~)', + $table_alias, + $term_value); + } else { + $term_constraints[] = qsprintf( + $conn, + '(%T.termCorpus LIKE %~)', + $table_alias, + $term_value); + } + + if ($is_stemmed) { + $stem_value = $stemmer->stemToken($value); + $stem_value = $ngram_engine->newTermsCorpus($stem_value); + $stem_value = ' '.$stem_value.' '; + + $term_constraints[] = qsprintf( + $conn, + '(%T.normalCorpus LIKE %~)', + $table_alias, + $stem_value); + } + + if ($is_not) { + $where[] = qsprintf( + $conn, + '(%Q)', + implode(' AND ', $term_constraints)); + } else if ($is_quoted) { + $where[] = qsprintf( + $conn, + '(%T.rawCorpus LIKE %~ AND (%Q))', + $table_alias, + $value, + implode(' OR ', $term_constraints)); + } else { + $where[] = qsprintf( + $conn, + '(%Q)', + implode(' OR ', $term_constraints)); + } + } + + return $where; + } + + protected function shouldGroupFerretResultRows() { + return (bool)$this->ferretTokens; + } + + /* -( Ngrams )------------------------------------------------------------- */ diff --git a/src/view/phui/PHUIButtonView.php b/src/view/phui/PHUIButtonView.php index 8d6eca9ec7..91c336eaaf 100644 --- a/src/view/phui/PHUIButtonView.php +++ b/src/view/phui/PHUIButtonView.php @@ -25,6 +25,7 @@ final class PHUIButtonView extends AphrontTagView { private $href = null; private $title = null; private $disabled; + private $selected; private $name; private $tooltip; private $noCSS; @@ -74,6 +75,11 @@ final class PHUIButtonView extends AphrontTagView { return $this; } + public function setSelected($selected) { + $this->selected = $selected; + return $this; + } + public function setTag($tag) { $this->tag = $tag; return $this; @@ -189,6 +195,10 @@ final class PHUIButtonView extends AphrontTagView { $classes[] = 'disabled'; } + if ($this->selected) { + $classes[] = 'selected'; + } + switch ($this->getButtonType()) { case self::BUTTONTYPE_DEFAULT: $classes[] = 'phui-button-default'; diff --git a/src/view/phui/PHUIFeedStoryView.php b/src/view/phui/PHUIFeedStoryView.php index fbba4a3f46..f0acd48201 100644 --- a/src/view/phui/PHUIFeedStoryView.php +++ b/src/view/phui/PHUIFeedStoryView.php @@ -150,13 +150,25 @@ final class PHUIFeedStoryView extends AphrontView { if ($this->getShowTimestamp()) { if ($this->epoch) { if ($user) { - $foot = phabricator_datetime($this->epoch, $user); + $marker = id(new PHUIIconView()) + ->setIcon('fa-circle') + ->addClass('phabricator-notification-status'); + $date = phabricator_datetime($this->epoch, $user); $foot = phutil_tag( 'span', array( 'class' => 'phabricator-notification-date', ), - $foot); + $date); + $foot = phutil_tag( + 'div', + array( + 'class' => 'phabricator-notification-foot', + ), + array( + $marker, + $date, + )); } else { $foot = null; } diff --git a/webroot/rsrc/css/aphront/notification.css b/webroot/rsrc/css/aphront/notification.css index ee835bba76..306075b582 100644 --- a/webroot/rsrc/css/aphront/notification.css +++ b/webroot/rsrc/css/aphront/notification.css @@ -11,7 +11,7 @@ .jx-notification { width: 240px; - padding: 8px 16px; + padding: 8px 12px; font-size: 11px; overflow: hidden; diff --git a/webroot/rsrc/css/aphront/table-view.css b/webroot/rsrc/css/aphront/table-view.css index a02d4dcfbe..1a7d2eb215 100644 --- a/webroot/rsrc/css/aphront/table-view.css +++ b/webroot/rsrc/css/aphront/table-view.css @@ -4,6 +4,7 @@ .aphront-table-wrap { overflow-x: auto; + -webkit-overflow-scrolling: touch; } .aphront-table-view { @@ -128,14 +129,8 @@ th.aphront-table-view-sortable-selected { padding: 8px 10px; } -.device-tablet .aphront-table-view td, -.device-phone .aphront-table-view td { - padding: 6px; -} - .device-tablet .aphront-table-view th, .device-phone .aphront-table-view th { - padding: 6px; overflow: hidden; } diff --git a/webroot/rsrc/css/application/base/notification-menu.css b/webroot/rsrc/css/application/base/notification-menu.css index 1a834b11fb..8e2391d57c 100644 --- a/webroot/rsrc/css/application/base/notification-menu.css +++ b/webroot/rsrc/css/application/base/notification-menu.css @@ -4,7 +4,8 @@ .phabricator-notification-menu { background: {$page.content}; - font-size: {$smallestfontsize}; + font-size: {$smallerfontsize}; + line-height: 18px; word-wrap: break-word; overflow-y: auto; box-shadow: {$dropshadow}; @@ -12,9 +13,8 @@ border-radius: 3px; } -.phabricator-notification .phabricator-notification-date { - margin-left: 8px; - color: {$lightgreytext}; +.phabricator-notification { + padding: 8px 12px; } .phabricator-notification-menu-loading { @@ -44,11 +44,7 @@ } .phabricator-notification-list .phabricator-notification { - padding: 10px 4px; -} - -.phabricator-notification { - padding: 6px 8px; + padding: 8px; } .phabricator-notification-menu .phabricator-notification { @@ -56,7 +52,7 @@ } .device-desktop .phabricator-notification-menu .phabricator-notification:hover { - background: {$hoverblue}; + background: {$lightgreybackground}; } .device-desktop .phabricator-notification-menu @@ -81,9 +77,34 @@ color: {$lightgreytext}; } +.phabricator-notification-foot { + color: {$lightgreytext}; + font-size: {$smallestfontsize}; + line-height: 18px; + position: relative; +} + +.phabricator-notification-unread .phabricator-notification-foot { + padding-left: 10px; +} + +.phabricator-notification-foot .phabricator-notification-status { + display: none; +} + +.phabricator-notification-unread .phabricator-notification-foot + .phabricator-notification-status { + font-size: 7px; + color: {$lightgreytext}; + position: absolute; + display: inline-block; + top: 6px; + left: 0; +} + .phabricator-notification-header { font-weight: bold; - padding: 10px 8px; + padding: 10px 12px; font-size: {$smallerfontsize}; border-bottom: 1px solid {$thinblueborder}; } diff --git a/webroot/rsrc/css/application/diffusion/diffusion-source.css b/webroot/rsrc/css/application/diffusion/diffusion-source.css index ffbed2589e..3b8b9a8a16 100644 --- a/webroot/rsrc/css/application/diffusion/diffusion-source.css +++ b/webroot/rsrc/css/application/diffusion/diffusion-source.css @@ -5,6 +5,12 @@ .diffusion-source { width: 100%; background: {$page.content}; + overflow: hidden; +} + +.device-phone .diffusion-source-wrap { + overflow: scroll; + -webkit-overflow-scrolling: touch; } .diffusion-source tr.phabricator-source-highlight th, @@ -27,6 +33,11 @@ word-break: break-all; } +.device .diffusion-source td { + word-break: normal; + white-space: nowrap; +} + .diffusion-browse-type-form { float: right; } diff --git a/webroot/rsrc/css/application/diffusion/diffusion.css b/webroot/rsrc/css/application/diffusion/diffusion.css index 2f7754f9f8..20f269dd3d 100644 --- a/webroot/rsrc/css/application/diffusion/diffusion.css +++ b/webroot/rsrc/css/application/diffusion/diffusion.css @@ -207,16 +207,6 @@ border-color: {$thinblueborder}; } -.device-phone.diffusion-history-view .phui-two-column-view - .phui-two-column-footer .phui-header-view { - text-align: center; -} - -.device-phone.diffusion-history-view .phui-two-column-content { - padding: 0; - margin: 0 -4px; -} - .device-phone.diffusion-history-view .phui-oi-attribute-spacer { display: none; } @@ -240,12 +230,10 @@ padding-bottom: 10px; } -.device-phone.diffusion-history-view .diffusion-history-list .button.has-icon - .phui-button-text { - margin: 0; -} - -.device-phone.diffusion-history-view .diffusion-history-list .button.has-icon - .phui-icon-view { - display: none; +.device-phone .phui-two-column-view .phui-two-column-content + .phui-object-box.diffusion-mobile-view { + margin: 0 -12px 20px; + border-left: none; + border-right: none; + border-color: {$thinblueborder}; } diff --git a/webroot/rsrc/css/application/search/application-search-view.css b/webroot/rsrc/css/application/search/application-search-view.css index 4036124547..afc59a7ba8 100644 --- a/webroot/rsrc/css/application/search/application-search-view.css +++ b/webroot/rsrc/css/application/search/application-search-view.css @@ -40,7 +40,7 @@ padding: 12px 0; } -.device-phone .application-search-results +.device-phone.application-search-view .application-search-results .phui-profile-header.phui-header-shell { padding: 12px 0 12px 4px; } diff --git a/webroot/rsrc/css/phui/button/phui-button.css b/webroot/rsrc/css/phui/button/phui-button.css index 99fef3707c..14bbdcf46b 100644 --- a/webroot/rsrc/css/phui/button/phui-button.css +++ b/webroot/rsrc/css/phui/button/phui-button.css @@ -105,6 +105,19 @@ button[disabled] { opacity: 0.5; } +button.button-grey.selected, +a.button.button-grey.selected, +button.button-grey.selected:hover, +a.button.button-grey.selected:hover { + border-color: {$sh-orangetext}; + color: {$sh-orangetext}; +} + +button.button-grey.selected .phui-icon-view, +a.button-grey.selected .phui-icon-view { + color: {$sh-orangetext}; +} + a.phuix-dropdown-open { color: {$greytext}; } diff --git a/webroot/rsrc/css/phui/phui-basic-nav-view.css b/webroot/rsrc/css/phui/phui-basic-nav-view.css index 70882b0afd..a545ad35ce 100644 --- a/webroot/rsrc/css/phui/phui-basic-nav-view.css +++ b/webroot/rsrc/css/phui/phui-basic-nav-view.css @@ -64,7 +64,7 @@ .phui-basic-nav .phabricator-side-menu .phui-list-item-href { display: block; - padding: 6px 8px 6px 20px; + padding: 6px 8px 6px 12px; color: {$darkbluetext}; border-top-right-radius: 3px; border-bottom-right-radius: 3px; @@ -72,13 +72,8 @@ text-overflow: ellipsis } -.phui-basic-nav .phabricator-side-menu .phui-list-item-has-icon - .phui-list-item-href { - padding-left: 12px; - } - .phui-basic-nav .phabricator-side-menu .phui-list-item-icon { - margin-left: -4px; + margin-left: -8px; text-align: center; width: 30px; } @@ -100,7 +95,6 @@ .phui-basic-nav .phabricator-side-menu .phui-list-item-selected { background-color: rgba({$alphablack},.05); - border-left: 4px solid {$sky}; border-top-right-radius: 3px; border-bottom-right-radius: 3px; font-weight: bold; @@ -109,12 +103,7 @@ .device-desktop .phui-basic-nav .phabricator-side-menu .phui-list-item-selected a.phui-list-item-href:hover { - background-color: rgba({$alphablack},.05); -} - -.phui-basic-nav .phabricator-side-menu .phui-list-item-selected - .phui-list-item-href { - margin-left: -4px; + background-color: rgba({$alphablack},.05); } .phui-basic-nav .phabricator-side-menu .phui-list-item-type-label { @@ -124,6 +113,7 @@ font-size: 12px; font-weight: bold; border-style: solid; + letter-spacing: 0.02em; } .device-desktop .phui-basic-nav .phabricator-side-menu diff --git a/webroot/rsrc/css/phui/phui-two-column-view.css b/webroot/rsrc/css/phui/phui-two-column-view.css index dba1519597..73926be92a 100644 --- a/webroot/rsrc/css/phui/phui-two-column-view.css +++ b/webroot/rsrc/css/phui/phui-two-column-view.css @@ -281,9 +281,7 @@ .phui-two-column-fixed.phui-two-column-view .phui-basic-nav .phabricator-side-menu .phui-list-item-selected { border-radius: 3px; - background-color: #f5f9ff; - border: 1px solid {$sky}; - padding-left: 3px; + background-color: {$sky}; } .phui-two-column-fixed.phui-two-column-view .phui-basic-nav @@ -291,6 +289,16 @@ border-radius: 3px; } +.phui-two-column-fixed.phui-two-column-view .phui-basic-nav + .phabricator-side-menu .phui-list-item-selected a { + color: #fff; +} + +.phui-two-column-fixed.phui-two-column-view .phui-basic-nav + .phabricator-side-menu .phui-list-item-selected a .phui-icon-view { + color: #fff; +} + .phui-two-column-fixed.phui-two-column-view .phui-header-action-links .phui-mobile-menu { display: block; diff --git a/webroot/rsrc/js/phuix/PHUIXButtonView.js b/webroot/rsrc/js/phuix/PHUIXButtonView.js index e87db88fe4..b7ca44a677 100644 --- a/webroot/rsrc/js/phuix/PHUIXButtonView.js +++ b/webroot/rsrc/js/phuix/PHUIXButtonView.js @@ -16,6 +16,7 @@ JX.install('PHUIXButtonView', { _iconView: null, _color: null, + _selected: null, _buttonType: null, setIcon: function(icon) { @@ -43,6 +44,13 @@ JX.install('PHUIXButtonView', { return this; }, + setSelected: function(selected) { + var node = this.getNode(); + this._selected = selected; + JX.DOM.alterClass(node, 'selected', this._selected); + return this; + }, + setButtonType: function(button_type) { var self = JX.PHUIXButtonView;