@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'master' into redesign-2015

+724 -310
+12 -12
resources/celerity/map.php
··· 8 8 return array( 9 9 'names' => array( 10 10 'core.pkg.css' => '9cbee819', 11 - 'core.pkg.js' => '41f5edc5', 11 + 'core.pkg.js' => 'f1e8abd7', 12 12 'darkconsole.pkg.js' => 'e7393ebb', 13 13 'differential.pkg.css' => 'fe951924', 14 14 'differential.pkg.js' => 'ebef29b1', ··· 459 459 'rsrc/js/core/behavior-object-selector.js' => '49b73b36', 460 460 'rsrc/js/core/behavior-oncopy.js' => '2926fff2', 461 461 'rsrc/js/core/behavior-phabricator-nav.js' => '56a1ca03', 462 - 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '095ed313', 462 + 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => 'eeaa9e5a', 463 463 'rsrc/js/core/behavior-refresh-csrf.js' => '7814b593', 464 464 'rsrc/js/core/behavior-remarkup-preview.js' => 'f7379f45', 465 465 'rsrc/js/core/behavior-reorder-applications.js' => '76b9fc3e', ··· 609 609 'javelin-behavior-phabricator-notification-example' => '8ce821c5', 610 610 'javelin-behavior-phabricator-object-selector' => '49b73b36', 611 611 'javelin-behavior-phabricator-oncopy' => '2926fff2', 612 - 'javelin-behavior-phabricator-remarkup-assist' => '095ed313', 612 + 'javelin-behavior-phabricator-remarkup-assist' => 'eeaa9e5a', 613 613 'javelin-behavior-phabricator-reveal-content' => '60821bc7', 614 614 'javelin-behavior-phabricator-search-typeahead' => '048330fa', 615 615 'javelin-behavior-phabricator-show-older-transactions' => 'dbbf48b6', ··· 894 894 'javelin-install', 895 895 'javelin-dom', 896 896 'javelin-stratcom', 897 - 'javelin-vector', 898 - ), 899 - '095ed313' => array( 900 - 'javelin-behavior', 901 - 'javelin-stratcom', 902 - 'javelin-dom', 903 - 'phabricator-phtize', 904 - 'phabricator-textareautils', 905 - 'javelin-workflow', 906 897 'javelin-vector', 907 898 ), 908 899 '0a3f3021' => array( ··· 1950 1941 'javelin-aphlict', 1951 1942 'phabricator-phtize', 1952 1943 'javelin-dom', 1944 + ), 1945 + 'eeaa9e5a' => array( 1946 + 'javelin-behavior', 1947 + 'javelin-stratcom', 1948 + 'javelin-dom', 1949 + 'phabricator-phtize', 1950 + 'phabricator-textareautils', 1951 + 'javelin-workflow', 1952 + 'javelin-vector', 1953 1953 ), 1954 1954 'efe49472' => array( 1955 1955 'javelin-install',
+5
resources/sql/autopatches/20150616.divinerrepository.sql
··· 1 + ALTER TABLE {$NAMESPACE}_diviner.diviner_livebook 2 + ADD COLUMN repositoryPHID VARBINARY(64) AFTER name; 3 + 4 + ALTER TABLE {$NAMESPACE}_diviner.diviner_livesymbol 5 + ADD COLUMN repositoryPHID VARBINARY(64) AFTER bookPHID;
-1
src/__phutil_library_map__.php
··· 3288 3288 'phabricator_format_local_time' => 'view/viewutils.php', 3289 3289 'phabricator_relative_date' => 'view/viewutils.php', 3290 3290 'phabricator_time' => 'view/viewutils.php', 3291 - 'phabricator_time_format' => 'view/viewutils.php', 3292 3291 'phid_get_subtype' => 'applications/phid/utils.php', 3293 3292 'phid_get_type' => 'applications/phid/utils.php', 3294 3293 'phid_group_by_type' => 'applications/phid/utils.php',
+10 -6
src/aphront/AphrontController.php
··· 35 35 36 36 throw new PhutilMethodNotImplementedException( 37 37 pht( 38 - 'Controllers must implement either handleRequest() (recommended) '. 39 - 'or processRequest() (deprecated).')); 38 + 'Controllers must implement either %s (recommended) '. 39 + 'or %s (deprecated).', 40 + 'handleRequest()', 41 + 'processRequest()')); 40 42 } 41 43 42 44 final public function setRequest(AphrontRequest $request) { ··· 46 48 47 49 final public function getRequest() { 48 50 if (!$this->request) { 49 - throw new Exception(pht('Call setRequest() before getRequest()!')); 51 + throw new PhutilInvalidStateException('setRequest'); 50 52 } 51 53 return $this->request; 52 54 } ··· 81 83 } 82 84 83 85 public function getDefaultResourceSource() { 84 - throw new Exception( 86 + throw new PhutilMethodNotImplementedException( 85 87 pht( 86 - 'A Controller must implement getDefaultResourceSource() before you '. 87 - 'can invoke requireResource() or initBehavior().')); 88 + 'A Controller must implement %s before you can invoke %s or %s.', 89 + 'getDefaultResourceSource()', 90 + 'requireResource()', 91 + 'initBehavior()')); 88 92 } 89 93 90 94 public function requireResource($symbol) {
+1 -1
src/applications/auth/engine/PhabricatorAuthInviteEngine.php
··· 18 18 19 19 public function getViewer() { 20 20 if (!$this->viewer) { 21 - throw new Exception(pht('Call setViewer() before getViewer()!')); 21 + throw new PhutilInvalidStateException('setViewer'); 22 22 } 23 23 return $this->viewer; 24 24 }
+3 -2
src/applications/calendar/controller/PhabricatorCalendarEventEditController.php
··· 437 437 ->setValue($end_disabled); 438 438 } 439 439 440 - $description = id(new AphrontFormTextAreaControl()) 440 + $description = id(new PhabricatorRemarkupControl()) 441 441 ->setLabel(pht('Description')) 442 442 ->setName('description') 443 - ->setValue($description); 443 + ->setValue($description) 444 + ->setUser($viewer); 444 445 445 446 $view_policies = id(new AphrontFormPolicyControl()) 446 447 ->setUser($viewer)
+13 -4
src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
··· 362 362 pht('Icon'), 363 363 $icon_display); 364 364 365 - $properties->addSectionHeader( 366 - pht('Description'), 367 - PHUIPropertyListView::ICON_SUMMARY); 368 - $properties->addTextContent($event->getDescription()); 365 + if (strlen($event->getDescription())) { 366 + 367 + $description = PhabricatorMarkupEngine::renderOneObject( 368 + id(new PhabricatorMarkupOneOff())->setContent($event->getDescription()), 369 + 'default', 370 + $viewer); 371 + 372 + $properties->addSectionHeader( 373 + pht('Description'), 374 + PHUIPropertyListView::ICON_SUMMARY); 375 + 376 + $properties->addTextContent($description); 377 + } 369 378 370 379 return $properties; 371 380 }
+26 -12
src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php
··· 126 126 $query->withDateRange($min_range, $max_range); 127 127 } 128 128 129 - $invited_phids = $saved->getParameter('invitedPHIDs'); 129 + $invited_phids = $saved->getParameter('invitedPHIDs', array()); 130 + $invited_phids = $user_datasource->evaluateTokens($invited_phids); 130 131 if ($invited_phids) { 131 - $invited_phids = $user_datasource->evaluateTokens($invited_phids); 132 132 $query->withInvitedPHIDs($invited_phids); 133 133 } 134 134 135 - $creator_phids = $saved->getParameter('creatorPHIDs'); 135 + $creator_phids = $saved->getParameter('creatorPHIDs', array()); 136 + $creator_phids = $user_datasource->evaluateTokens($creator_phids); 136 137 if ($creator_phids) { 137 - $creator_phids = $user_datasource->evaluateTokens($creator_phids); 138 138 $query->withCreatorPHIDs($creator_phids); 139 139 } 140 140 ··· 313 313 $list = new PHUIObjectItemListView(); 314 314 foreach ($events as $event) { 315 315 $from = phabricator_datetime($event->getDateFrom(), $viewer); 316 - $to = phabricator_datetime($event->getDateTo(), $viewer); 316 + $duration = ''; 317 317 $creator_handle = $handles[$event->getUserPHID()]; 318 318 319 + $attendees = array(); 320 + foreach ($event->getInvitees() as $invitee) { 321 + $attendees[] = $invitee->getInviteePHID(); 322 + } 323 + 324 + $attendees = pht( 325 + 'Attending: %s', 326 + $viewer->renderHandleList($attendees) 327 + ->setAsInline(1) 328 + ->render()); 329 + 330 + if (strlen($event->getDuration()) > 0) { 331 + $duration = pht( 332 + 'Duration: %s', 333 + $event->getDuration()); 334 + } 335 + 319 336 $item = id(new PHUIObjectItemView()) 320 - ->setHeader($event->getName()) 321 - ->setHref($event->getURI()) 322 - ->addByline(pht('Creator: %s', $creator_handle->renderLink())) 323 - ->addAttribute(pht('From %s to %s', $from, $to)) 324 - ->addAttribute(id(new PhutilUTF8StringTruncator()) 325 - ->setMaximumGlyphs(64) 326 - ->truncateString($event->getDescription())); 337 + ->setHeader($viewer->renderHandle($event->getPHID())->render()) 338 + ->addAttribute($attendees) 339 + ->addIcon('none', $from) 340 + ->addIcon('none', $duration); 327 341 328 342 $list->addItem($item); 329 343 }
+23
src/applications/calendar/storage/PhabricatorCalendarEvent.php
··· 373 373 return false; 374 374 } 375 375 376 + public function getDuration() { 377 + $seconds = $this->dateTo - $this->dateFrom; 378 + $minutes = round($seconds / 60, 1); 379 + $hours = round($minutes / 60, 3); 380 + $days = round($hours / 24, 2); 381 + 382 + $duration = ''; 383 + 384 + if ($days >= 1) { 385 + return pht( 386 + '%s day(s)', 387 + round($days, 1)); 388 + } else if ($hours >= 1) { 389 + return pht( 390 + '%s hour(s)', 391 + round($hours, 1)); 392 + } else if ($minutes >= 1) { 393 + return pht( 394 + '%s minute(s)', 395 + round($minutes, 0)); 396 + } 397 + } 398 + 376 399 /* -( Markup Interface )--------------------------------------------------- */ 377 400 378 401
+1 -1
src/applications/conduit/method/ConduitAPIMethod.php
··· 30 30 31 31 $query = $this->newQueryObject(); 32 32 if ($query) { 33 - $types['order'] = 'order'; 33 + $types['order'] = 'optional order'; 34 34 $types += $this->getPagerParamTypes(); 35 35 } 36 36
+14 -3
src/applications/config/option/PhabricatorSyntaxHighlightingConfigOptions.php
··· 20 20 } 21 21 22 22 public function getOptions() { 23 - 24 23 $caches_href = PhabricatorEnv::getDocLink('Managing Caches'); 25 24 26 25 return array( ··· 74 73 'c' => 'C', 75 74 'coffee-script' => 'CoffeeScript', 76 75 'cpp' => 'C++', 76 + 'csharp' => 'C#', 77 77 'css' => 'CSS', 78 78 'd' => 'D', 79 79 'diff' => 'Diff', 80 80 'django' => 'Django Templating', 81 + 'docker' => 'Docker', 81 82 'erb' => 'Embedded Ruby/ERB', 82 83 'erlang' => 'Erlang', 83 84 'go' => 'Golang', 84 85 'groovy' => 'Groovy', 85 86 'haskell' => 'Haskell', 86 87 'html' => 'HTML', 88 + 'http' => 'HTTP', 87 89 'invisible' => 'Invisible', 88 90 'java' => 'Java', 89 91 'js' => 'Javascript', 90 92 'json' => 'JSON', 93 + 'make' => 'Makefile', 91 94 'mysql' => 'MySQL', 95 + 'nginx' => 'Nginx Configuration', 92 96 'objc' => 'Objective-C', 93 97 'perl' => 'Perl', 94 98 'php' => 'PHP', 99 + 'postgresql' => 'PostgreSQL', 100 + 'pot' => 'Gettext Catalog', 95 101 'puppet' => 'Puppet', 96 - 'rest' => 'reStructuredText', 97 - 'text' => 'Plain Text', 98 102 'python' => 'Python', 99 103 'rainbow' => 'Rainbow', 100 104 'remarkup' => 'Remarkup', 105 + 'rest' => 'reStructuredText', 106 + 'robotframework' => 'RobotFramework', 107 + 'rst' => 'reStructuredText', 101 108 'ruby' => 'Ruby', 109 + 'sql' => 'SQL', 110 + 'tex' => 'LaTeX', 111 + 'text' => 'Plain Text', 112 + 'twig' => 'Twig', 102 113 'xml' => 'XML', 103 114 'yaml' => 'YAML', 104 115 ))
+1 -1
src/applications/conpherence/view/ConpherenceTransactionView.php
··· 64 64 public function render() { 65 65 $viewer = $this->getUser(); 66 66 if (!$viewer) { 67 - throw new Exception(pht('Call setUser() before render()!')); 67 + throw new PhutilInvalidStateException('setUser'); 68 68 } 69 69 70 70 require_celerity_resource('conpherence-transaction-css');
+7
src/applications/console/core/DarkConsoleCore.php
··· 127 127 } 128 128 return $data; 129 129 } else { 130 + // Truncate huge strings. Since the data doesn't really matter much, 131 + // just truncate bytes to avoid PhutilUTF8StringTruncator overhead. 132 + $length = strlen($data); 133 + $max = 4096; 134 + if ($length > $max) { 135 + $data = substr($data, 0, $max).'...<'.$length.' bytes>...'; 136 + } 130 137 return phutil_utf8ize($data); 131 138 } 132 139 }
+1 -3
src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php
··· 283 283 */ 284 284 private function detectRenderingCycle(PhabricatorDashboardPanel $panel) { 285 285 if ($this->parentPanelPHIDs === null) { 286 - throw new Exception( 287 - pht( 288 - 'You must call setParentPanelPHIDs() before rendering panels.')); 286 + throw new PhutilInvalidStateException('setParentPanelPHIDs'); 289 287 } 290 288 291 289 $max_depth = 4;
+16
src/applications/diffusion/conduit/DiffusionFileContentQueryConduitAPIMethod.php
··· 20 20 'path' => 'required string', 21 21 'commit' => 'required string', 22 22 'needsBlame' => 'optional bool', 23 + 'timeout' => 'optional int', 24 + 'byteLimit' => 'optional int', 23 25 ); 24 26 } 25 27 ··· 31 33 $file_query 32 34 ->setViewer($request->getUser()) 33 35 ->setNeedsBlame($needs_blame); 36 + 37 + $timeout = $request->getValue('timeout'); 38 + if ($timeout) { 39 + $file_query->setTimeout($timeout); 40 + } 41 + 42 + $byte_limit = $request->getValue('byteLimit'); 43 + if ($byte_limit) { 44 + $file_query->setByteLimit($byte_limit); 45 + } 46 + 34 47 $file_content = $file_query->loadFileContent(); 48 + 35 49 if ($needs_blame) { 36 50 list($text_list, $rev_list, $blame_dict) = $file_query->getBlameData(); 37 51 } else { 38 52 $text_list = $rev_list = $blame_dict = array(); 39 53 } 54 + 40 55 $file_content 41 56 ->setBlameDict($blame_dict) 42 57 ->setRevList($rev_list) 43 58 ->setTextList($text_list); 59 + 44 60 return $file_content->toDictionary(); 45 61 } 46 62
+40 -8
src/applications/diffusion/controller/DiffusionBrowseFileController.php
··· 54 54 $needs_blame = ($show_blame && !$show_color) || 55 55 ($show_blame && $request->isAjax()); 56 56 57 + $params = array( 58 + 'commit' => $drequest->getCommit(), 59 + 'path' => $drequest->getPath(), 60 + 'needsBlame' => $needs_blame, 61 + ); 62 + 63 + $byte_limit = null; 64 + if ($view !== 'raw') { 65 + $byte_limit = PhabricatorFileStorageEngine::getChunkThreshold(); 66 + $time_limit = 10; 67 + 68 + $params += array( 69 + 'timeout' => $time_limit, 70 + 'byteLimit' => $byte_limit, 71 + ); 72 + } 73 + 57 74 $file_content = DiffusionFileContent::newFromConduit( 58 75 $this->callConduitWithDiffusionRequest( 59 76 'diffusion.filecontentquery', 60 - array( 61 - 'commit' => $drequest->getCommit(), 62 - 'path' => $drequest->getPath(), 63 - 'needsBlame' => $needs_blame, 64 - ))); 77 + $params)); 65 78 $data = $file_content->getCorpus(); 66 79 67 80 if ($view === 'raw') { ··· 71 84 $this->loadLintMessages(); 72 85 $this->coverage = $drequest->loadCoverage(); 73 86 74 - $binary_uri = null; 75 - if (ArcanistDiffUtils::isHeuristicBinaryFile($data)) { 87 + if ($byte_limit && (strlen($data) == $byte_limit)) { 88 + $corpus = $this->buildErrorCorpus( 89 + pht( 90 + 'This file is larger than %s byte(s), and too large to display '. 91 + 'in the web UI.', 92 + $byte_limit)); 93 + } else if (ArcanistDiffUtils::isHeuristicBinaryFile($data)) { 76 94 $file = $this->loadFileForData($path, $data); 77 95 $file_uri = $file->getBestURI(); 78 96 ··· 80 98 $corpus = $this->buildImageCorpus($file_uri); 81 99 } else { 82 100 $corpus = $this->buildBinaryCorpus($file_uri, $data); 83 - $binary_uri = $file_uri; 84 101 } 85 102 } else { 86 103 // Build the content of the file. ··· 932 949 $header = id(new PHUIHeaderView()) 933 950 ->setHeader(pht('Details')) 934 951 ->addActionLink($file); 952 + 953 + $box = id(new PHUIObjectBoxView()) 954 + ->setHeader($header) 955 + ->appendChild($text); 956 + 957 + return $box; 958 + } 959 + 960 + private function buildErrorCorpus($message) { 961 + $text = id(new PHUIBoxView()) 962 + ->addPadding(PHUI::PADDING_LARGE) 963 + ->appendChild($message); 964 + 965 + $header = id(new PHUIHeaderView()) 966 + ->setHeader(pht('Details')); 935 967 936 968 $box = id(new PHUIObjectBoxView()) 937 969 ->setHeader($header)
+45 -1
src/applications/diffusion/query/filecontent/DiffusionFileContentQuery.php
··· 11 11 private $needsBlame; 12 12 private $fileContent; 13 13 private $viewer; 14 + private $timeout; 15 + private $byteLimit; 16 + 17 + public function setTimeout($timeout) { 18 + $this->timeout = $timeout; 19 + return $this; 20 + } 21 + 22 + public function getTimeout() { 23 + return $this->timeout; 24 + } 25 + 26 + public function setByteLimit($byte_limit) { 27 + $this->byteLimit = $byte_limit; 28 + return $this; 29 + } 30 + 31 + public function getByteLimit() { 32 + return $this->byteLimit; 33 + } 14 34 15 35 final public static function newFromDiffusionRequest( 16 36 DiffusionRequest $request) { ··· 21 41 abstract protected function executeQueryFromFuture(Future $future); 22 42 23 43 final public function loadFileContentFromFuture(Future $future) { 24 - $this->fileContent = $this->executeQueryFromFuture($future); 44 + 45 + if ($this->timeout) { 46 + $future->setTimeout($this->timeout); 47 + } 48 + 49 + if ($this->getByteLimit()) { 50 + $future->setStdoutSizeLimit($this->getByteLimit()); 51 + } 52 + 53 + try { 54 + $file_content = $this->executeQueryFromFuture($future); 55 + } catch (CommandException $ex) { 56 + if (!$future->getWasKilledByTimeout()) { 57 + throw $ex; 58 + } 59 + 60 + $message = pht( 61 + '<Attempt to load this file was terminated after %s second(s).>', 62 + $this->timeout); 63 + 64 + $file_content = new DiffusionFileContent(); 65 + $file_content->setCorpus($message); 66 + } 67 + 68 + $this->fileContent = $file_content; 25 69 26 70 $repository = $this->getRequest()->getRepository(); 27 71 $try_encoding = $repository->getDetail('encoding');
+10
src/applications/diviner/controller/DivinerAtomController.php
··· 84 84 if ($atom) { 85 85 $this->buildDefined($properties, $symbol); 86 86 $this->buildExtendsAndImplements($properties, $symbol); 87 + $this->buildRepository($properties, $symbol); 87 88 88 89 $warnings = $atom->getWarnings(); 89 90 if ($warnings) { ··· 292 293 pht('Implements'), 293 294 phutil_implode_html(phutil_tag('br'), $items)); 294 295 } 296 + } 297 + 298 + private function buildRepository( 299 + PHUIPropertyListView $view, 300 + DivinerLiveSymbol $symbol) { 301 + 302 + $view->addProperty( 303 + pht('Repository'), 304 + $this->getViewer()->renderHandle($symbol->getRepositoryPHID())); 295 305 } 296 306 297 307 private function renderAtomTag(DivinerLiveSymbol $symbol) {
+10
src/applications/diviner/controller/DivinerBookController.php
··· 14 14 $book = id(new DivinerBookQuery()) 15 15 ->setViewer($viewer) 16 16 ->withNames(array($book_name)) 17 + ->needRepositories(true) 17 18 ->executeOne(); 18 19 19 20 if (!$book) { ··· 42 43 ->setPolicyObject($book) 43 44 ->setEpoch($book->getDateModified()) 44 45 ->addActionLink($action_button); 46 + 47 + // TODO: This could probably look better. 48 + if ($book->getRepositoryPHID()) { 49 + $header->addTag( 50 + id(new PHUITagView()) 51 + ->setType(PHUITagView::TYPE_STATE) 52 + ->setBackgroundColor(PHUITagView::COLOR_BLUE) 53 + ->setName($book->getRepository()->getMonogram())); 54 + } 45 55 46 56 $document = new PHUIDocumentView(); 47 57 $document->setHeader($header);
+10
src/applications/diviner/controller/DivinerBookEditController.php
··· 75 75 ->setName('projectPHIDs') 76 76 ->setLabel(pht('Projects')) 77 77 ->setValue($book->getProjectPHIDs())) 78 + ->appendControl( 79 + id(new AphrontFormTokenizerControl()) 80 + ->setDatasource(new DiffusionRepositoryDatasource()) 81 + ->setName('repositoryPHIDs') 82 + ->setLabel(pht('Repository')) 83 + ->setDisableBehavior(true) 84 + ->setLimit(1) 85 + ->setValue($book->getRepositoryPHID() 86 + ? array($book->getRepositoryPHID()) 87 + : null)) 78 88 ->appendChild( 79 89 id(new AphrontFormPolicyControl()) 80 90 ->setName('viewPolicy')
+23 -5
src/applications/diviner/publisher/DivinerLivePublisher.php
··· 4 4 5 5 private $book; 6 6 7 - private function loadBook() { 7 + protected function getBook() { 8 8 if (!$this->book) { 9 9 $book_name = $this->getConfig('name'); 10 10 ··· 20 20 ->save(); 21 21 } 22 22 23 - $book->setConfigurationData($this->getConfigurationData())->save(); 23 + $conn_w = $book->establishConnection('w'); 24 + $conn_w->openTransaction(); 25 + 26 + $book 27 + ->setRepositoryPHID($this->getRepositoryPHID()) 28 + ->setConfigurationData($this->getConfigurationData()) 29 + ->save(); 30 + 31 + // TODO: This is gross. Without this, the repository won't be updated for 32 + // atoms which have already been published. 33 + queryfx( 34 + $conn_w, 35 + 'UPDATE %T SET repositoryPHID = %s WHERE bookPHID = %s', 36 + id(new DivinerLiveSymbol())->getTableName(), 37 + $this->getRepositoryPHID(), 38 + $book->getPHID()); 39 + 40 + $conn_w->saveTransaction(); 24 41 $this->book = $book; 25 42 26 43 id(new PhabricatorSearchIndexer()) ··· 33 50 private function loadSymbolForAtom(DivinerAtom $atom) { 34 51 $symbol = id(new DivinerAtomQuery()) 35 52 ->setViewer(PhabricatorUser::getOmnipotentUser()) 36 - ->withBookPHIDs(array($this->loadBook()->getPHID())) 53 + ->withBookPHIDs(array($atom->getBook())) 37 54 ->withTypes(array($atom->getType())) 38 55 ->withNames(array($atom->getName())) 39 56 ->withContexts(array($atom->getContext())) ··· 45 62 } 46 63 47 64 return id(new DivinerLiveSymbol()) 48 - ->setBookPHID($this->loadBook()->getPHID()) 65 + ->setBookPHID($this->getBook()->getPHID()) 49 66 ->setType($atom->getType()) 50 67 ->setName($atom->getName()) 51 68 ->setContext($atom->getContext()) ··· 68 85 protected function loadAllPublishedHashes() { 69 86 $symbols = id(new DivinerAtomQuery()) 70 87 ->setViewer(PhabricatorUser::getOmnipotentUser()) 71 - ->withBookPHIDs(array($this->loadBook()->getPHID())) 88 + ->withBookPHIDs(array($this->getBook()->getPHID())) 72 89 ->withGhosts(false) 73 90 ->execute(); 74 91 ··· 113 130 $is_documentable = $this->shouldGenerateDocumentForAtom($atom); 114 131 115 132 $symbol 133 + ->setRepositoryPHID($this->getRepositoryPHID()) 116 134 ->setGraphHash($hash) 117 135 ->setIsDocumentable((int)$is_documentable) 118 136 ->setTitle($ref->getTitle())
+10
src/applications/diviner/publisher/DivinerPublisher.php
··· 9 9 private $config; 10 10 private $symbolReverseMap; 11 11 private $dropCaches; 12 + private $repositoryPHID; 12 13 13 14 final public function setDropCaches($drop_caches) { 14 15 $this->dropCaches = $drop_caches; ··· 161 162 } 162 163 163 164 return true; 165 + } 166 + 167 + final public function getRepositoryPHID() { 168 + return $this->repositoryPHID; 169 + } 170 + 171 + final public function setRepositoryPHID($repository_phid) { 172 + $this->repositoryPHID = $repository_phid; 173 + return $this; 164 174 } 165 175 166 176 }
+46
src/applications/diviner/query/DivinerAtomQuery.php
··· 14 14 private $nodeHashes; 15 15 private $titles; 16 16 private $nameContains; 17 + private $repositoryPHIDs; 17 18 18 19 private $needAtoms; 19 20 private $needExtends; 20 21 private $needChildren; 22 + private $needRepositories; 21 23 22 24 public function withIDs(array $ids) { 23 25 $this->ids = $ids; ··· 106 108 107 109 public function withIsDocumentable($documentable) { 108 110 $this->isDocumentable = $documentable; 111 + return $this; 112 + } 113 + 114 + public function withRepositoryPHIDs(array $repository_phids) { 115 + $this->repositoryPHIDs = $repository_phids; 116 + return $this; 117 + } 118 + 119 + public function needRepositories($need_repositories) { 120 + $this->needRepositories = $need_repositories; 109 121 return $this; 110 122 } 111 123 ··· 125 137 } 126 138 127 139 protected function willFilterPage(array $atoms) { 140 + assert_instances_of($atoms, 'DivinerLiveSymbol'); 141 + 128 142 $books = array_unique(mpull($atoms, 'getBookPHID')); 129 143 130 144 $books = id(new DivinerBookQuery()) ··· 257 271 $this->attachAllChildren($atoms, $children, $this->needExtends); 258 272 } 259 273 274 + if ($this->needRepositories) { 275 + $repositories = id(new PhabricatorRepositoryQuery()) 276 + ->setViewer($this->getViewer()) 277 + ->withPHIDs(mpull($atoms, 'getRepositoryPHID')) 278 + ->execute(); 279 + $repositories = mpull($repositories, null, 'getPHID'); 280 + 281 + foreach ($atoms as $key => $atom) { 282 + if ($atom->getRepositoryPHID() === null) { 283 + $atom->attachRepository(null); 284 + continue; 285 + } 286 + 287 + $repository = idx($repositories, $atom->getRepositoryPHID()); 288 + 289 + if (!$repository) { 290 + $this->didRejectResult($atom); 291 + unset($atom[$key]); 292 + continue; 293 + } 294 + 295 + $atom->attachRepository($repository); 296 + } 297 + } 298 + 260 299 return $atoms; 261 300 } 262 301 ··· 379 418 $conn_r, 380 419 'CONVERT(name USING utf8) LIKE %~', 381 420 $this->nameContains); 421 + } 422 + 423 + if ($this->repositoryPHIDs) { 424 + $where[] = qsprintf( 425 + $conn_r, 426 + 'repositoryPHID IN (%Ls)', 427 + $this->repositoryPHIDs); 382 428 } 383 429 384 430 $where[] = $this->buildPagingClause($conn_r);
+25 -12
src/applications/diviner/query/DivinerAtomSearchEngine.php
··· 14 14 $saved = new PhabricatorSavedQuery(); 15 15 16 16 $saved->setParameter( 17 + 'repositoryPHIDs', 18 + $this->readPHIDsFromRequest($request, 'repositoryPHIDs')); 19 + $saved->setParameter('name', $request->getStr('name')); 20 + $saved->setParameter( 17 21 'types', 18 22 $this->readListFromRequest($request, 'types')); 19 23 20 - $saved->setParameter('name', $request->getStr('name')); 21 - 22 24 return $saved; 23 25 } 24 26 25 27 public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 26 28 $query = id(new DivinerAtomQuery()); 27 29 28 - $types = $saved->getParameter('types'); 29 - if ($types) { 30 - $query->withTypes($types); 30 + $repository_phids = $saved->getParameter('repositoryPHIDs'); 31 + if ($repository_phids) { 32 + $query->withRepositoryPHIDs($repository_phids); 31 33 } 32 34 33 35 $name = $saved->getParameter('name'); ··· 35 37 $query->withNameContains($name); 36 38 } 37 39 40 + $types = $saved->getParameter('types'); 41 + if ($types) { 42 + $query->withTypes($types); 43 + } 44 + 38 45 return $query; 39 46 } 40 47 ··· 42 49 AphrontFormView $form, 43 50 PhabricatorSavedQuery $saved) { 44 51 52 + $form->appendChild( 53 + id(new AphrontFormTextControl()) 54 + ->setLabel(pht('Name Contains')) 55 + ->setName('name') 56 + ->setValue($saved->getParameter('name'))); 57 + 45 58 $all_types = array(); 46 59 foreach (DivinerAtom::getAllTypes() as $type) { 47 60 $all_types[$type] = DivinerAtom::getAtomTypeNameString($type); ··· 59 72 $name, 60 73 isset($types[$type])); 61 74 } 75 + $form->appendChild($type_control); 62 76 63 - $form 64 - ->appendChild( 65 - id(new AphrontFormTextControl()) 66 - ->setLabel(pht('Name Contains')) 67 - ->setName('name') 68 - ->setValue($saved->getParameter('name'))) 69 - ->appendChild($type_control); 77 + $form->appendControl( 78 + id(new AphrontFormTokenizerControl()) 79 + ->setLabel(pht('Repositories')) 80 + ->setName('repositoryPHIDs') 81 + ->setDatasource(new DiffusionRepositoryDatasource()) 82 + ->setValue($saved->getParameter('repositoryPHIDs'))); 70 83 } 71 84 72 85 protected function getURI($path) {
+44
src/applications/diviner/query/DivinerBookQuery.php
··· 5 5 private $ids; 6 6 private $phids; 7 7 private $names; 8 + private $repositoryPHIDs; 8 9 9 10 private $needProjectPHIDs; 11 + private $needRepositories; 10 12 11 13 public function withIDs(array $ids) { 12 14 $this->ids = $ids; ··· 23 25 return $this; 24 26 } 25 27 28 + public function withRepositoryPHIDs(array $repository_phids) { 29 + $this->repositoryPHIDs = $repository_phids; 30 + return $this; 31 + } 32 + 26 33 public function needProjectPHIDs($need_phids) { 27 34 $this->needProjectPHIDs = $need_phids; 28 35 return $this; 29 36 } 30 37 38 + public function needRepositories($need_repositories) { 39 + $this->needRepositories = $need_repositories; 40 + return $this; 41 + } 42 + 31 43 protected function loadPage() { 32 44 $table = new DivinerLiveBook(); 33 45 $conn_r = $table->establishConnection('r'); ··· 46 58 protected function didFilterPage(array $books) { 47 59 assert_instances_of($books, 'DivinerLiveBook'); 48 60 61 + if ($this->needRepositories) { 62 + $repositories = id(new PhabricatorRepositoryQuery()) 63 + ->setViewer($this->getViewer()) 64 + ->withPHIDs(mpull($books, 'getRepositoryPHID')) 65 + ->execute(); 66 + $repositories = mpull($repositories, null, 'getPHID'); 67 + 68 + foreach ($books as $key => $book) { 69 + if ($book->getRepositoryPHID() === null) { 70 + $book->attachRepository(null); 71 + continue; 72 + } 73 + 74 + $repository = idx($repositories, $book->getRepositoryPHID()); 75 + 76 + if (!$repository) { 77 + $this->didRejectResult($book); 78 + unset($books[$key]); 79 + continue; 80 + } 81 + 82 + $book->attachRepository($repository); 83 + } 84 + } 85 + 49 86 if ($this->needProjectPHIDs) { 50 87 $edge_query = id(new PhabricatorEdgeQuery()) 51 88 ->withSourcePHIDs(mpull($books, 'getPHID')) ··· 89 126 $conn_r, 90 127 'name IN (%Ls)', 91 128 $this->names); 129 + } 130 + 131 + if ($this->repositoryPHIDs !== null) { 132 + $where[] = qsprintf( 133 + $conn_r, 134 + 'repositoryPHID IN (%Ls)', 135 + $this->repositoryPHIDs); 92 136 } 93 137 94 138 $where[] = $this->buildPagingClause($conn_r);
+6
src/applications/diviner/search/DivinerAtomSearchIndexer.php
··· 30 30 PhabricatorTime::getNow()); 31 31 32 32 $doc->addRelationship( 33 + PhabricatorSearchRelationship::RELATIONSHIP_REPOSITORY, 34 + $atom->getRepositoryPHID(), 35 + PhabricatorRepositoryRepositoryPHIDType::TYPECONST, 36 + PhabricatorTime::getNow()); 37 + 38 + $doc->addRelationship( 33 39 $atom->getGraphHash() 34 40 ? PhabricatorSearchRelationship::RELATIONSHIP_CLOSED 35 41 : PhabricatorSearchRelationship::RELATIONSHIP_OPEN,
+6
src/applications/diviner/search/DivinerBookSearchIndexer.php
··· 18 18 PhabricatorSearchDocumentFieldType::FIELD_BODY, 19 19 $book->getPreface()); 20 20 21 + $doc->addRelationship( 22 + PhabricatorSearchRelationship::RELATIONSHIP_REPOSITORY, 23 + $book->getRepositoryPHID(), 24 + PhabricatorRepositoryRepositoryPHIDType::TYPECONST, 25 + $book->getDateCreated()); 26 + 21 27 $this->indexTransactions( 22 28 $doc, 23 29 new DivinerLiveBookTransactionQuery(),
+13 -1
src/applications/diviner/storage/DivinerLiveBook.php
··· 8 8 PhabricatorApplicationTransactionInterface { 9 9 10 10 protected $name; 11 + protected $repositoryPHID; 11 12 protected $viewPolicy; 12 13 protected $editPolicy; 13 14 protected $configurationData = array(); 14 15 15 16 private $projectPHIDs = self::ATTACHABLE; 17 + private $repository = self::ATTACHABLE; 16 18 17 19 protected function getConfiguration() { 18 20 return array( ··· 22 24 ), 23 25 self::CONFIG_COLUMN_SCHEMA => array( 24 26 'name' => 'text64', 27 + 'repositoryPHID' => 'phid?', 25 28 ), 26 29 self::CONFIG_KEY_SCHEMA => array( 27 30 'key_phid' => null, ··· 68 71 return idx($spec, 'name', $group); 69 72 } 70 73 74 + public function attachRepository(PhabricatorRepository $repository = null) { 75 + $this->repository = $repository; 76 + return $this; 77 + } 78 + 79 + public function getRepository() { 80 + return $this->assertAttached($this->repository); 81 + } 82 + 71 83 public function attachProjectPHIDs(array $project_phids) { 72 84 $this->projectPHIDs = $project_phids; 73 85 return $this; ··· 98 110 } 99 111 100 112 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 101 - return false; 113 + return false; 102 114 } 103 115 104 116 public function describeAutomaticCapability($capability) {
+12
src/applications/diviner/storage/DivinerLiveSymbol.php
··· 7 7 PhabricatorDestructibleInterface { 8 8 9 9 protected $bookPHID; 10 + protected $repositoryPHID; 10 11 protected $context; 11 12 protected $type; 12 13 protected $name; ··· 22 23 protected $isDocumentable = 0; 23 24 24 25 private $book = self::ATTACHABLE; 26 + private $repository = self::ATTACHABLE; 25 27 private $atom = self::ATTACHABLE; 26 28 private $extends = self::ATTACHABLE; 27 29 private $children = self::ATTACHABLE; ··· 43 45 'summary' => 'text?', 44 46 'isDocumentable' => 'bool', 45 47 'nodeHash' => 'text64?', 48 + 'repositoryPHID' => 'phid?', 46 49 ), 47 50 self::CONFIG_KEY_SCHEMA => array( 48 51 'key_phid' => null, ··· 91 94 92 95 public function attachBook(DivinerLiveBook $book) { 93 96 $this->book = $book; 97 + return $this; 98 + } 99 + 100 + public function getRepository() { 101 + return $this->assertAttached($this->repository); 102 + } 103 + 104 + public function attachRepository(PhabricatorRepository $repository = null) { 105 + $this->repository = $repository; 94 106 return $this; 95 107 } 96 108
+23
src/applications/diviner/workflow/DivinerGenerateWorkflow.php
··· 25 25 'help' => pht('Specify a subclass of %s.', 'DivinerPublisher'), 26 26 'default' => 'DivinerLivePublisher', 27 27 ), 28 + array( 29 + 'name' => 'repository', 30 + 'param' => 'callsign', 31 + 'help' => pht('Repository that the documentation belongs to.'), 32 + ), 28 33 )); 29 34 } 30 35 ··· 186 191 'DivinerPublisher')); 187 192 } 188 193 $publisher = newv($publisher_class, array()); 194 + 195 + $callsign = $args->getArg('repository'); 196 + $repository = null; 197 + if ($callsign) { 198 + $repository = id(new PhabricatorRepositoryQuery()) 199 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 200 + ->withCallsigns(array($callsign)) 201 + ->executeOne(); 202 + 203 + if (!$repository) { 204 + throw new PhutilArgumentUsageException( 205 + pht( 206 + "Repository '%s' does not exist.", 207 + $callsign)); 208 + } 209 + 210 + $publisher->setRepositoryPHID($repository->getPHID()); 211 + } 189 212 190 213 $this->publishDocumentation($args->getArg('clean'), $publisher); 191 214 }
+1 -14
src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php
··· 77 77 78 78 $cmd = $lease->getInterface('command'); 79 79 80 - if ($v_platform !== 'windows') { 81 - $cmd->execx('mkdir %s', $full_path); 82 - } else { 83 - // Windows is terrible. The mkdir command doesn't even support putting 84 - // the path in quotes. IN QUOTES. ARGUHRGHUGHHGG!! Do some terribly 85 - // inaccurate sanity checking since we can't safely escape the path. 86 - if (preg_match('/^[A-Z]\\:\\\\[a-zA-Z0-9\\\\\\ ]/', $full_path) === 0) { 87 - throw new Exception( 88 - pht( 89 - 'Unsafe path detected for Windows platform: "%s".', 90 - $full_path)); 91 - } 92 - $cmd->execx('mkdir %C', $full_path); 93 - } 80 + $cmd->execx('mkdir %s', $full_path); 94 81 95 82 $lease->setAttribute('path', $full_path); 96 83 }
+2 -40
src/applications/drydock/interface/command/DrydockSSHCommandInterface.php
··· 42 42 $this->openCredentialsIfNotOpen(); 43 43 44 44 $argv = func_get_args(); 45 - 46 - if ($this->getConfig('platform') === 'windows') { 47 - // Handle Windows by executing the command under PowerShell. 48 - $command = id(new PhutilCommandString($argv)) 49 - ->setEscapingMode(PhutilCommandString::MODE_POWERSHELL); 50 - 51 - $change_directory = ''; 52 - if ($this->getWorkingDirectory() !== null) { 53 - $change_directory .= 'cd '.$this->getWorkingDirectory(); 54 - } 55 - 56 - $script = <<<EOF 57 - $change_directory 58 - $command 59 - if (\$LastExitCode -ne 0) { 60 - exit \$LastExitCode 61 - } 62 - EOF; 63 - 64 - // When Microsoft says "Unicode" they don't mean UTF-8. 65 - $script = mb_convert_encoding($script, 'UTF-16LE'); 66 - 67 - $script = base64_encode($script); 68 - 69 - $powershell = 70 - 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'; 71 - $powershell .= 72 - ' -ExecutionPolicy Bypass'. 73 - ' -NonInteractive'. 74 - ' -InputFormat Text'. 75 - ' -OutputFormat Text'. 76 - ' -EncodedCommand '.$script; 77 - 78 - $full_command = $powershell; 79 - } else { 80 - // Handle UNIX by executing under the native shell. 81 - $argv = $this->applyWorkingDirectoryToArgv($argv); 82 - 83 - $full_command = call_user_func_array('csprintf', $argv); 84 - } 45 + $argv = $this->applyWorkingDirectoryToArgv($argv); 46 + $full_command = call_user_func_array('csprintf', $argv); 85 47 86 48 $command_timeout = ''; 87 49 if ($this->connectTimeout !== null) {
+80 -29
src/applications/owners/query/PhabricatorOwnersPackageQuery.php
··· 7 7 private $phids; 8 8 private $ownerPHIDs; 9 9 private $repositoryPHIDs; 10 + private $namePrefix; 10 11 11 12 /** 12 13 * Owners are direct owners, and members of owning projects. ··· 31 32 return $this; 32 33 } 33 34 34 - protected function loadPage() { 35 - $table = new PhabricatorOwnersPackage(); 36 - $conn_r = $table->establishConnection('r'); 35 + public function withNamePrefix($prefix) { 36 + $this->namePrefix = $prefix; 37 + return $this; 38 + } 37 39 38 - $data = queryfx_all( 39 - $conn_r, 40 - 'SELECT p.* FROM %T p %Q %Q %Q %Q', 41 - $table->getTableName(), 42 - $this->buildJoinClause($conn_r), 43 - $this->buildWhereClause($conn_r), 44 - $this->buildOrderClause($conn_r), 45 - $this->buildLimitClause($conn_r)); 40 + public function newResultObject() { 41 + return new PhabricatorOwnersPackage(); 42 + } 46 43 47 - return $table->loadAllFromArray($data); 44 + protected function loadPage() { 45 + return $this->loadStandardPage(new PhabricatorOwnersPackage()); 48 46 } 49 47 50 - protected function buildJoinClause(AphrontDatabaseConnection $conn_r) { 51 - $joins = array(); 48 + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { 49 + $joins = parent::buildJoinClauseParts($conn); 52 50 53 51 if ($this->ownerPHIDs !== null) { 54 52 $joins[] = qsprintf( 55 - $conn_r, 53 + $conn, 56 54 'JOIN %T o ON o.packageID = p.id', 57 55 id(new PhabricatorOwnersOwner())->getTableName()); 58 56 } 59 57 60 58 if ($this->repositoryPHIDs !== null) { 61 59 $joins[] = qsprintf( 62 - $conn_r, 60 + $conn, 63 61 'JOIN %T rpath ON rpath.packageID = p.id', 64 62 id(new PhabricatorOwnersPath())->getTableName()); 65 63 } 66 64 67 - return implode(' ', $joins); 65 + return $joins; 68 66 } 69 67 70 - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { 71 - $where = array(); 68 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 69 + $where = parent::buildWhereClauseParts($conn); 72 70 73 71 if ($this->phids !== null) { 74 72 $where[] = qsprintf( 75 - $conn_r, 73 + $conn, 76 74 'p.phid IN (%Ls)', 77 75 $this->phids); 78 76 } 79 77 80 78 if ($this->ids !== null) { 81 79 $where[] = qsprintf( 82 - $conn_r, 80 + $conn, 83 81 'p.id IN (%Ld)', 84 82 $this->ids); 85 83 } 86 84 87 85 if ($this->repositoryPHIDs !== null) { 88 86 $where[] = qsprintf( 89 - $conn_r, 87 + $conn, 90 88 'rpath.repositoryPHID IN (%Ls)', 91 89 $this->repositoryPHIDs); 92 90 } ··· 94 92 if ($this->ownerPHIDs !== null) { 95 93 $base_phids = $this->ownerPHIDs; 96 94 97 - $query = new PhabricatorProjectQuery(); 98 - $query->setViewer($this->getViewer()); 99 - $query->withMemberPHIDs($base_phids); 100 - $projects = $query->execute(); 95 + $projects = id(new PhabricatorProjectQuery()) 96 + ->setViewer($this->getViewer()) 97 + ->withMemberPHIDs($base_phids) 98 + ->execute(); 101 99 $project_phids = mpull($projects, 'getPHID'); 102 100 103 101 $all_phids = array_merge($base_phids, $project_phids); 104 102 105 103 $where[] = qsprintf( 106 - $conn_r, 104 + $conn, 107 105 'o.userPHID IN (%Ls)', 108 106 $all_phids); 109 107 } 110 108 111 - $where[] = $this->buildPagingClause($conn_r); 112 - return $this->formatWhereClause($where); 109 + if (strlen($this->namePrefix)) { 110 + // NOTE: This is a hacky mess, but this column is currently case 111 + // sensitive and unique. 112 + $where[] = qsprintf( 113 + $conn, 114 + 'LOWER(p.name) LIKE %>', 115 + phutil_utf8_strtolower($this->namePrefix)); 116 + } 117 + 118 + return $where; 119 + } 120 + 121 + protected function shouldGroupQueryResultRows() { 122 + if ($this->repositoryPHIDs) { 123 + return true; 124 + } 125 + 126 + if ($this->ownerPHIDs) { 127 + return true; 128 + } 129 + 130 + return parent::shouldGroupQueryResultRows(); 131 + } 132 + 133 + public function getBuiltinOrders() { 134 + return array( 135 + 'name' => array( 136 + 'vector' => array('name'), 137 + 'name' => pht('Name'), 138 + ), 139 + ) + parent::getBuiltinOrders(); 140 + } 141 + 142 + public function getOrderableColumns() { 143 + return parent::getOrderableColumns() + array( 144 + 'name' => array( 145 + 'table' => $this->getPrimaryTableAlias(), 146 + 'column' => 'name', 147 + 'type' => 'string', 148 + 'unique' => true, 149 + 'reverse' => true, 150 + ), 151 + ); 152 + } 153 + 154 + protected function getPagingValueMap($cursor, array $keys) { 155 + $package = $this->loadCursorObject($cursor); 156 + return array( 157 + 'id' => $package->getID(), 158 + 'name' => $package->getName(), 159 + ); 113 160 } 114 161 115 162 public function getQueryApplicationClass() { 116 163 return 'PhabricatorOwnersApplication'; 164 + } 165 + 166 + protected function getPrimaryTableAlias() { 167 + return 'p'; 117 168 } 118 169 119 170 }
+22 -51
src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php
··· 11 11 return 'PhabricatorOwnersApplication'; 12 12 } 13 13 14 - public function buildSavedQueryFromRequest(AphrontRequest $request) { 15 - $saved = new PhabricatorSavedQuery(); 14 + public function newQuery() { 15 + return new PhabricatorOwnersPackageQuery(); 16 + } 16 17 17 - $saved->setParameter( 18 - 'ownerPHIDs', 19 - $this->readUsersFromRequest( 20 - $request, 21 - 'owners', 22 - array( 23 - PhabricatorProjectProjectPHIDType::TYPECONST, 24 - ))); 25 - 26 - $saved->setParameter( 27 - 'repositoryPHIDs', 28 - $this->readPHIDsFromRequest( 29 - $request, 30 - 'repositories', 31 - array( 32 - PhabricatorRepositoryRepositoryPHIDType::TYPECONST, 33 - ))); 34 - 35 - return $saved; 18 + protected function buildCustomSearchFields() { 19 + return array( 20 + id(new PhabricatorSearchDatasourceField()) 21 + ->setLabel(pht('Owners')) 22 + ->setKey('ownerPHIDs') 23 + ->setAliases(array('owner', 'owners')) 24 + ->setDatasource(new PhabricatorProjectOrUserDatasource()), 25 + id(new PhabricatorSearchDatasourceField()) 26 + ->setLabel(pht('Repositories')) 27 + ->setKey('repositoryPHIDs') 28 + ->setAliases(array('repository', 'repositories')) 29 + ->setDatasource(new DiffusionRepositoryDatasource()), 30 + ); 36 31 } 37 32 38 - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 39 - $query = id(new PhabricatorOwnersPackageQuery()); 33 + protected function buildQueryFromParameters(array $map) { 34 + $query = $this->newQuery(); 40 35 41 - $owner_phids = $saved->getParameter('ownerPHIDs', array()); 42 - if ($owner_phids) { 43 - $query->withOwnerPHIDs($owner_phids); 36 + if ($map['ownerPHIDs']) { 37 + $query->withOwnerPHIDs($map['ownerPHIDs']); 44 38 } 45 39 46 - $repository_phids = $saved->getParameter('repositoryPHIDs', array()); 47 - if ($repository_phids) { 48 - $query->withRepositoryPHIDs($repository_phids); 40 + if ($map['repositoryPHIDs']) { 41 + $query->withRepositoryPHIDs($map['repositoryPHIDs']); 49 42 } 50 43 51 44 return $query; 52 - } 53 - 54 - public function buildSearchForm( 55 - AphrontFormView $form, 56 - PhabricatorSavedQuery $saved) { 57 - 58 - $owner_phids = $saved->getParameter('ownerPHIDs', array()); 59 - $repository_phids = $saved->getParameter('repositoryPHIDs', array()); 60 - 61 - $form 62 - ->appendControl( 63 - id(new AphrontFormTokenizerControl()) 64 - ->setDatasource(new PhabricatorProjectOrUserDatasource()) 65 - ->setName('owners') 66 - ->setLabel(pht('Owners')) 67 - ->setValue($owner_phids)) 68 - ->appendControl( 69 - id(new AphrontFormTokenizerControl()) 70 - ->setDatasource(new DiffusionRepositoryDatasource()) 71 - ->setName('repositories') 72 - ->setLabel(pht('Repositories')) 73 - ->setValue($repository_phids)); 74 45 } 75 46 76 47 protected function getURI($path) {
+5 -9
src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php
··· 3 3 final class PhabricatorOwnersPackageDatasource 4 4 extends PhabricatorTypeaheadDatasource { 5 5 6 - public function isBrowsable() { 7 - // TODO: Make this browsable. 8 - return false; 9 - } 10 - 11 6 public function getBrowseTitle() { 12 7 return pht('Browse Packages'); 13 8 } ··· 26 21 27 22 $results = array(); 28 23 29 - $packages = id(new PhabricatorOwnersPackageQuery()) 30 - ->setViewer($viewer) 31 - ->execute(); 24 + $query = id(new PhabricatorOwnersPackageQuery()) 25 + ->withNamePrefix($raw_query) 26 + ->setOrder('name'); 32 27 28 + $packages = $this->executeQuery($query); 33 29 foreach ($packages as $package) { 34 30 $results[] = id(new PhabricatorTypeaheadResult()) 35 31 ->setName($package->getName()) ··· 37 33 ->setPHID($package->getPHID()); 38 34 } 39 35 40 - return $results; 36 + return $this->filterResultsAgainstTokens($results); 41 37 } 42 38 43 39 }
+35
src/applications/people/storage/PhabricatorUser.php
··· 738 738 return new DateTimeZone($this->getTimezoneIdentifier()); 739 739 } 740 740 741 + public function getPreference($key) { 742 + $preferences = $this->loadPreferences(); 743 + 744 + // TODO: After T4103 and T7707 this should eventually be pushed down the 745 + // stack into modular preference definitions and role profiles. This is 746 + // just fixing T8601 and mildly anticipating those changes. 747 + $value = $preferences->getPreference($key); 748 + 749 + $allowed_values = null; 750 + switch ($key) { 751 + case PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT: 752 + $allowed_values = array( 753 + 'g:i A', 754 + 'H:i', 755 + ); 756 + break; 757 + case PhabricatorUserPreferences::PREFERENCE_DATE_FORMAT: 758 + $allowed_values = array( 759 + 'Y-m-d', 760 + 'n/j/Y', 761 + 'd-m-Y', 762 + ); 763 + break; 764 + } 765 + 766 + if ($allowed_values !== null) { 767 + $allowed_values = array_fuse($allowed_values); 768 + if (empty($allowed_values[$value])) { 769 + $value = head($allowed_values); 770 + } 771 + } 772 + 773 + return $value; 774 + } 775 + 741 776 public function __toString() { 742 777 return $this->getUsername(); 743 778 }
+19 -1
src/applications/repository/storage/PhabricatorRepository.php
··· 1913 1913 PhabricatorDestructionEngine $engine) { 1914 1914 1915 1915 $this->openTransaction(); 1916 - $this->delete(); 1916 + 1917 + $this->delete(); 1918 + 1919 + $books = id(new DivinerBookQuery()) 1920 + ->setViewer($engine->getViewer()) 1921 + ->withRepositoryPHIDs(array($this->getPHID())) 1922 + ->execute(); 1923 + foreach ($books as $book) { 1924 + $engine->destroyObject($book); 1925 + } 1926 + 1927 + $atoms = id(new DivinerAtomQuery()) 1928 + ->setViewer($engine->getViewer()) 1929 + ->withRepositoryPHIDs(array($this->getPHID())) 1930 + ->execute(); 1931 + foreach ($atoms as $atom) { 1932 + $engine->destroyObject($atom); 1933 + } 1934 + 1917 1935 $this->saveTransaction(); 1918 1936 } 1919 1937
+2 -1
src/infrastructure/daemon/workers/PhabricatorWorker.php
··· 199 199 } 200 200 201 201 $tasks = id(new PhabricatorWorkerArchiveTaskQuery()) 202 - ->withIDs($task_ids); 202 + ->withIDs($task_ids) 203 + ->execute(); 203 204 204 205 foreach ($tasks as $task) { 205 206 if ($task->getResult() != PhabricatorWorkerArchiveTask::RESULT_SUCCESS) {
+1 -4
src/infrastructure/markup/PhabricatorMarkupEngine.php
··· 198 198 } 199 199 200 200 if (!isset($this->objects[$key]['output'])) { 201 - throw new Exception( 202 - pht( 203 - 'Call %s before using results.', 204 - 'process()')); 201 + throw new PhutilInvalidStateException('process'); 205 202 } 206 203 } 207 204
+41 -18
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementProbeWorkflow.php
··· 45 45 } 46 46 } 47 47 48 - $console->writeOut("%s\n", pht('APPROXIMATE TABLE SIZES')); 49 48 asort($totals); 49 + 50 + $table = id(new PhutilConsoleTable()) 51 + ->setShowHeader(false) 52 + ->setPadding(2) 53 + ->addColumn('name', array('title' => pht('Database / Table'))) 54 + ->addColumn('size', array('title' => pht('Size'))) 55 + ->addColumn('percentage', array('title' => pht('Percentage'))); 56 + 50 57 foreach ($totals as $db => $size) { 51 - $database_size = $this->formatSize($totals[$db], $overall); 52 - $console->writeOut( 53 - "**%s**\n", 54 - sprintf('%-32.32s %18s', $db, $database_size)); 58 + list($database_size, $database_percentage) = $this->formatSize( 59 + $totals[$db], 60 + $overall); 61 + 62 + $table->addRow(array( 63 + 'name' => phutil_console_format('**%s**', $db), 64 + 'size' => phutil_console_format('**%s**', $database_size), 65 + 'percentage' => phutil_console_format('**%s**', $database_percentage), 66 + )); 55 67 $data[$db] = isort($data[$db], '_totalSize'); 56 - foreach ($data[$db] as $table => $info) { 57 - $table_size = $this->formatSize($info['_totalSize'], $overall); 58 - $console->writeOut( 59 - "%s\n", 60 - sprintf(' %-28.28s %18s', $table, $table_size)); 68 + foreach ($data[$db] as $table_name => $info) { 69 + list($table_size, $table_percentage) = $this->formatSize( 70 + $info['_totalSize'], 71 + $overall); 72 + 73 + $table->addRow(array( 74 + 'name' => ' '.$table_name, 75 + 'size' => $table_size, 76 + 'percentage' => $table_percentage, 77 + )); 61 78 } 62 79 } 63 - $overall_size = $this->formatSize($overall, $overall); 64 - $console->writeOut( 65 - "**%s**\n", 66 - sprintf('%-32.32s %18s', pht('TOTAL'), $overall_size)); 80 + 81 + list($overall_size, $overall_percentage) = $this->formatSize( 82 + $overall, 83 + $overall); 84 + $table->addRow(array( 85 + 'name' => phutil_console_format('**%s**', pht('TOTAL')), 86 + 'size' => phutil_console_format('**%s**', $overall_size), 87 + 'percentage' => phutil_console_format('**%s**', $overall_percentage), 88 + )); 67 89 90 + $table->draw(); 68 91 return 0; 69 92 } 70 93 71 94 private function formatSize($n, $o) { 72 - return sprintf( 73 - '%8.8s MB %5.5s%%', 74 - number_format($n / (1024 * 1024), 1), 75 - sprintf('%3.1f', 100 * ($n / $o))); 95 + return array( 96 + sprintf('%8.8s MB', number_format($n / (1024 * 1024), 1)), 97 + sprintf('%3.1f%%', 100 * ($n / $o)), 98 + ); 76 99 } 77 100 78 101 }
+4 -10
src/view/form/control/AphrontFormDateControl.php
··· 137 137 } 138 138 139 139 private function getTimeFormat() { 140 - $viewer = $this->getUser(); 141 - $preferences = $viewer->loadPreferences(); 142 - $pref_time_format = PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT; 143 - 144 - return $preferences->getPreference($pref_time_format, 'g:i A'); 140 + return $this->getUser() 141 + ->getPreference(PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT); 145 142 } 146 143 147 144 private function getDateFormat() { 148 - $viewer = $this->getUser(); 149 - $preferences = $viewer->loadPreferences(); 150 - $pref_date_format = PhabricatorUserPreferences::PREFERENCE_DATE_FORMAT; 151 - 152 - return $preferences->getPreference($pref_date_format, 'Y-m-d'); 145 + return $this->getUser() 146 + ->getPreference(PhabricatorUserPreferences::PREFERENCE_DATE_FORMAT); 153 147 } 154 148 155 149 private function getTimeInputValue() {
+23 -28
src/view/form/control/AphrontFormDateControlValue.php
··· 10 10 private $zone; 11 11 private $optional; 12 12 13 - 14 13 public function getValueDate() { 15 14 return $this->valueDate; 16 15 } ··· 56 55 return $this->optional; 57 56 } 58 57 58 + public function getViewer() { 59 + return $this->viewer; 60 + } 61 + 59 62 public static function newFromParts( 60 63 PhabricatorUser $viewer, 61 64 $year, ··· 71 74 $year, 72 75 $month, 73 76 $day, 74 - coalesce($time, '12:00 AM'), 75 - $value); 77 + coalesce($time, '12:00 AM')); 76 78 $value->valueEnabled = $enabled; 77 79 78 80 return $value; ··· 85 87 list($value->valueDate, $value->valueTime) = 86 88 $value->getFormattedDateFromDate( 87 89 $request->getStr($key.'_d'), 88 - $request->getStr($key.'_t'), 89 - $value); 90 + $request->getStr($key.'_t')); 90 91 91 92 $value->valueEnabled = $request->getStr($key.'_e'); 92 93 return $value; ··· 108 109 $year, 109 110 $month, 110 111 $day, 111 - $time, 112 - $value); 112 + $time); 113 113 114 114 return $value; 115 115 } ··· 123 123 list($value->valueDate, $value->valueTime) = 124 124 $value->getFormattedDateFromDate( 125 125 idx($dictionary, 'd'), 126 - idx($dictionary, 't'), 127 - $value); 126 + idx($dictionary, 't')); 128 127 129 128 $value->valueEnabled = idx($dictionary, 'e'); 130 129 ··· 205 204 } 206 205 207 206 private function getTimeFormat() { 208 - $preferences = $this->viewer->loadPreferences(); 209 - $pref_time_format = PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT; 210 - 211 - return $preferences->getPreference($pref_time_format, 'g:i A'); 207 + return $this->getViewer() 208 + ->getPreference(PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT); 212 209 } 213 210 214 211 private function getDateFormat() { 215 - $preferences = $this->viewer->loadPreferences(); 216 - $pref_date_format = PhabricatorUserPreferences::PREFERENCE_DATE_FORMAT; 217 - 218 - return $preferences->getPreference($pref_date_format, 'Y-m-d'); 212 + return $this->getViewer() 213 + ->getPreference(PhabricatorUserPreferences::PREFERENCE_DATE_FORMAT); 219 214 } 220 215 221 - private function getFormattedDateFromDate($date, $time, $value) { 216 + private function getFormattedDateFromDate($date, $time) { 222 217 $original_input = $date; 223 - $zone = $value->getTimezone(); 224 - $separator = $value->getFormatSeparator(); 218 + $zone = $this->getTimezone(); 219 + $separator = $this->getFormatSeparator(); 225 220 $parts = preg_split('@[,./:-]@', $date); 226 221 $date = implode($separator, $parts); 227 222 $date = id(new DateTime($date, $zone)); 228 223 229 224 if ($date) { 230 - $date = $date->format($value->getDateFormat()); 225 + $date = $date->format($this->getDateFormat()); 231 226 } else { 232 227 $date = $original_input; 233 228 } ··· 235 230 $date = id(new DateTime("{$date} {$time}", $zone)); 236 231 237 232 return array( 238 - $date->format($value->getDateFormat()), 239 - $date->format($value->getTimeFormat()), 233 + $date->format($this->getDateFormat()), 234 + $date->format($this->getTimeFormat()), 240 235 ); 241 236 } 242 237 ··· 244 239 $year, 245 240 $month, 246 241 $day, 247 - $time, 248 - $value) { 249 - $zone = $value->getTimezone(); 242 + $time) { 243 + 244 + $zone = $this->getTimezone(); 250 245 $date_time = id(new DateTime("{$year}-{$month}-{$day} {$time}", $zone)); 251 246 252 247 return array( 253 - $date_time->format($value->getDateFormat()), 254 - $date_time->format($value->getTimeFormat()), 248 + $date_time->format($this->getDateFormat()), 249 + $date_time->format($this->getTimeFormat()), 255 250 ); 256 251 } 257 252
+2 -2
src/view/form/control/AphrontFormPolicyControl.php
··· 162 162 163 163 protected function renderInput() { 164 164 if (!$this->object) { 165 - throw new Exception(pht('Call setPolicyObject() before rendering!')); 165 + throw new PhutilInvalidStateException('setPolicyObject'); 166 166 } 167 167 if (!$this->capability) { 168 - throw new Exception(pht('Call setCapability() before rendering!')); 168 + throw new PhutilInvalidStateException('setCapability'); 169 169 } 170 170 171 171 $policy = $this->object->getPolicy($this->capability);
+5 -2
src/view/form/control/AphrontFormTokenizerControl.php
··· 109 109 if (!$viewer) { 110 110 throw new Exception( 111 111 pht( 112 - 'Call setUser() before rendering tokenizers. Use appendControl() '. 113 - 'on AphrontFormView to do this easily.')); 112 + 'Call %s before rendering tokenizers. '. 113 + 'Use %s on %s to do this easily.', 114 + 'setUser()', 115 + 'appendControl()', 116 + 'AphrontFormView')); 114 117 } 115 118 116 119 $values = nonempty($this->getValue(), array());
+2 -2
src/view/layout/AphrontSideNavFilterView.php
··· 187 187 public function render() { 188 188 if ($this->menu->getItems()) { 189 189 if (!$this->baseURI) { 190 - throw new Exception(pht('Call setBaseURI() before render()!')); 190 + throw new PhutilInvalidStateException('setBaseURI'); 191 191 } 192 192 if ($this->selectedFilter === false) { 193 - throw new Exception(pht('Call selectFilter() before render()!')); 193 + throw new PhutilInvalidStateException('selectFilter'); 194 194 } 195 195 } 196 196
+1 -1
src/view/layout/PhabricatorActionListView.php
··· 29 29 30 30 public function render() { 31 31 if (!$this->user) { 32 - throw new Exception(pht('Call setUser() before render()!')); 32 + throw new PhutilInvalidStateException('setUser'); 33 33 } 34 34 35 35 $event = new PhabricatorEvent(
+1 -1
src/view/phui/PHUITagView.php
··· 144 144 145 145 protected function getTagContent() { 146 146 if (!$this->type) { 147 - throw new Exception(pht('You must call setType() before render()!')); 147 + throw new PhutilInvalidStateException('setType', 'render'); 148 148 } 149 149 150 150 $color = null;
+2 -5
src/view/phui/calendar/PHUICalendarListView.php
··· 141 141 } 142 142 143 143 private function getEventTooltip(AphrontCalendarEventView $event) { 144 - $viewer = $this->getUser(); 145 - $preferences = $viewer->loadPreferences(); 146 - $time_pref = $preferences->getPreference( 147 - PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT, 148 - 'g:i A'); 144 + $time_pref = $this->getUser() 145 + ->getPreference(PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT); 149 146 150 147 Javelin::initBehavior('phabricator-tooltips'); 151 148
+4 -15
src/view/viewutils.php
··· 31 31 } 32 32 33 33 function phabricator_time($epoch, $user) { 34 + $time_key = PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT; 34 35 return phabricator_format_local_time( 35 36 $epoch, 36 37 $user, 37 - phabricator_time_format($user)); 38 + $user->getPreference($time_key)); 38 39 } 39 40 40 41 function phabricator_datetime($epoch, $user) { 42 + $time_key = PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT; 41 43 return phabricator_format_local_time( 42 44 $epoch, 43 45 $user, 44 46 pht('%s, %s', 45 47 phutil_date_format($epoch), 46 - phabricator_time_format($user))); 47 - } 48 - 49 - function phabricator_time_format($user) { 50 - $prefs = $user->loadPreferences(); 51 - 52 - $pref = $prefs->getPreference( 53 - PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT); 54 - 55 - if (strlen($pref)) { 56 - return $pref; 57 - } 58 - 59 - return pht('g:i A'); 48 + $user->getPreference($time_key))); 60 49 } 61 50 62 51 /**
+16 -4
webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js
··· 99 99 } else { 100 100 sel = [def]; 101 101 } 102 - sel = sel.join('\n' + ch); 103 - return sel; 102 + 103 + if (ch === '>') { 104 + for(var i=0; i < sel.length; i++) { 105 + if (sel[i][0] === '>') { 106 + ch = '>'; 107 + } else { 108 + ch = '> '; 109 + } 110 + sel[i] = ch + sel[i]; 111 + } 112 + return sel.join('\n'); 113 + } 114 + 115 + return sel.join('\n' + ch); 104 116 } 105 117 106 118 function assist(area, action, root) { ··· 141 153 update(area, code_prefix + '```\n', sel, '\n```'); 142 154 break; 143 155 case 'fa-quote-right': 144 - ch = '> '; 156 + ch = '>'; 145 157 sel = prepend_char_to_lines(ch, sel, pht('Quoted Text')); 146 - update(area, ((r.start === 0) ? '' : '\n\n') + ch, sel, '\n\n'); 158 + update(area, ((r.start === 0) ? '' : '\n\n'), sel, '\n\n'); 147 159 break; 148 160 case 'fa-table': 149 161 var table_prefix = (r.start === 0 ? '' : '\n\n');