diff --git a/src/applications/audit/controller/PhabricatorAuditListController.php b/src/applications/audit/controller/PhabricatorAuditListController.php index d858dec6bf..d776fe12fd 100644 --- a/src/applications/audit/controller/PhabricatorAuditListController.php +++ b/src/applications/audit/controller/PhabricatorAuditListController.php @@ -1,489 +1,489 @@ filter = idx($data, 'filter'); $this->name = idx($data, 'name'); } public function processRequest() { $request = $this->getRequest(); $nav = $this->buildNavAndSelectFilter(); if ($request->isFormPost()) { // If the list filter is POST'ed, redirect to GET so the page can be // bookmarked. $uri = $request->getRequestURI(); $phid = head($request->getArr('set_phid')); $user = id(new PhabricatorUser())->loadOneWhere( 'phid = %s', $phid); $uri = $request->getRequestURI(); if ($user) { $username = phutil_escape_uri($user->getUsername()); $uri = '/audit/view/'.$this->filter.'/'.$username.'/'; } else if ($phid) { $uri = $request->getRequestURI(); $uri = $uri->alter('phid', $phid); } return id(new AphrontRedirectResponse())->setURI($uri); } $this->filterStatus = $request->getStr('status', 'all'); $handle = $this->loadHandle(); $nav->appendChild($this->buildListFilters($handle)); $title = null; $message = null; if (!$handle) { switch ($this->filter) { case 'project': $title = pht('Choose A Project'); $message = pht('Choose a project to view audits for.'); break; case 'package': case 'packagecommits': $title = pht('Choose a Package'); $message = pht('Choose a package to view audits for.'); break; } } if (!$message) { $nav->appendChild($this->buildViews($handle)); } else { $panel = id(new AphrontErrorView()) ->setSeverity(AphrontErrorView::SEVERITY_NODATA) ->setTitle($title) ->appendChild($message); $nav->appendChild($panel); } return $this->buildStandardPageResponse( $nav, array( 'title' => pht('Audits'), )); } private function buildNavAndSelectFilter() { $nav = new AphrontSideNavFilterView(); $nav->setBaseURI(new PhutilURI('/audit/view/')); $nav->addLabel(pht('Active')); $nav->addFilter('active', pht('Need Attention')); $nav->addLabel(pht('Audits')); $nav->addFilter('audits', pht('All')); $nav->addFilter('user', pht('By User')); $nav->addFilter('project', pht('By Project')); $nav->addFilter('package', pht('By Package')); $nav->addLabel(pht('Commits')); $nav->addFilter('commits', pht('All')); $nav->addFilter('author', pht('By Author')); $nav->addFilter('packagecommits', pht('By Package')); $this->filter = $nav->selectFilter($this->filter, 'active'); return $nav; } private function buildListFilters(PhabricatorObjectHandle $handle = null) { $request = $this->getRequest(); $user = $request->getUser(); $form = new AphrontFormView(); $form->setUser($user); $show_status = false; $show_user = false; $show_project = false; $show_package = false; switch ($this->filter) { case 'audits': case 'commits': $show_status = true; break; case 'active': $show_user = true; break; case 'author': case 'user': $show_user = true; $show_status = true; break; case 'project': $show_project = true; $show_status = true; break; case 'package': case 'packagecommits': $show_package = true; $show_status = true; break; } if ($show_user || $show_project || $show_package) { if ($show_user) { $uri = '/typeahead/common/users/'; $label = pht('User'); } else if ($show_project) { $uri = '/typeahead/common/projects/'; $label = pht('Project'); } else if ($show_package) { $uri = '/typeahead/common/packages/'; $label = pht('Package'); } $tok_value = null; if ($handle) { $tok_value = array( $handle->getPHID() => $handle->getFullName(), ); } $form->appendChild( id(new AphrontFormTokenizerControl()) ->setName('set_phid') ->setLabel($label) ->setLimit(1) ->setDatasource($uri) ->setValue($tok_value)); } if ($show_status) { $form->appendChild( id(new AphrontFormToggleButtonsControl()) ->setName('status') ->setLabel(pht('Status')) ->setBaseURI($request->getRequestURI(), 'status') ->setValue($this->filterStatus) ->setButtons( array( 'all' => pht('All'), 'open' => pht('Open'), ))); } $form->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Filter Audits'))); $view = new AphrontListFilterView(); $view->appendChild($form); return $view; } private function loadHandle() { $request = $this->getRequest(); $default = null; switch ($this->filter) { case 'user': case 'active': case 'author': $default = $request->getUser()->getPHID(); if ($this->name) { $user = id(new PhabricatorUser())->loadOneWhere( 'username = %s', $this->name); if ($user) { $default = $user->getPHID(); } } break; } $phid = $request->getStr('phid', $default); if (!$phid) { return null; } $phids = array($phid); $handles = $this->loadViewerHandles($phids); $handle = $handles[$phid]; $this->validateHandle($handle); return $handle; } private function validateHandle(PhabricatorObjectHandle $handle) { switch ($this->filter) { case 'active': case 'user': case 'author': if ($handle->getType() !== PhabricatorPHIDConstants::PHID_TYPE_USER) { throw new Exception("PHID must be a user PHID!"); } break; case 'package': case 'packagecommits': if ($handle->getType() !== PhabricatorPHIDConstants::PHID_TYPE_OPKG) { throw new Exception("PHID must be a package PHID!"); } break; case 'project': if ($handle->getType() !== PhabricatorPHIDConstants::PHID_TYPE_PROJ) { throw new Exception("PHID must be a project PHID!"); } break; case 'audits': case 'commits': break; default: throw new Exception("Unknown filter '{$this->filter}'!"); } } private function buildViews(PhabricatorObjectHandle $handle = null) { $views = array(); switch ($this->filter) { case 'active': $views[] = $this->buildAuditView($handle); $views[] = $this->buildCommitView($handle); break; case 'audits': case 'user': case 'package': case 'project': $views[] = $this->buildAuditView($handle); break; case 'commits': case 'packagecommits': case 'author': $views[] = $this->buildCommitView($handle); break; } return $views; } private function buildAuditView(PhabricatorObjectHandle $handle = null) { $request = $this->getRequest(); $query = new PhabricatorAuditQuery(); $use_pager = ($this->filter != 'active'); if ($use_pager) { $pager = new AphrontPagerView(); $pager->setURI($request->getRequestURI(), 'offset'); $pager->setOffset($request->getInt('offset')); $query->setOffset($pager->getOffset()); $query->setLimit($pager->getPageSize() + 1); } $awaiting = null; $phids = null; switch ($this->filter) { case 'user': case 'active': $obj = id(new PhabricatorUser())->loadOneWhere( 'phid = %s', $handle->getPHID()); if (!$obj) { throw new Exception("Invalid user!"); } $phids = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($obj); $awaiting = $obj; break; case 'project': case 'package': $phids = array($handle->getPHID()); break; case 'audits'; break; default: throw new Exception("Unknown filter!"); } if ($phids) { $query->withAuditorPHIDs($phids); } if ($awaiting) { $query->withAwaitingUser($awaiting); } switch ($this->filter) { case 'audits': case 'user': case 'project': case 'package': switch ($this->filterStatus) { case 'open': $query->withStatus(PhabricatorAuditQuery::STATUS_OPEN); break; } break; case 'active': $query->withStatus(PhabricatorAuditQuery::STATUS_OPEN); break; } if ($handle) { - $handle_name = phutil_escape_html($handle->getName()); + $handle_name = $handle->getName(); } else { $handle_name = null; } switch ($this->filter) { case 'active': $header = pht('Required Audits'); $nodata = pht('No commits require your audit.'); break; case 'user': $header = pht("Audits for %s", $handle_name); $nodata = pht("No matching audits by %s.", $handle_name); break; case 'audits': $header = pht('Audits'); $nodata = pht('No matching audits.'); break; case 'project': $header = pht("Audits in Project %s", $handle_name); $nodata = pht("No matching audits in project %s.", $handle_name); break; case 'package': $header = pht("Audits for Package %s", $handle_name); $nodata = pht("No matching audits in package %s.", $handle_name); break; } $query->needCommitData(true); $audits = $query->execute(); if ($use_pager) { $audits = $pager->sliceResults($audits); } $view = new PhabricatorAuditListView(); $view->setAudits($audits); $view->setCommits($query->getCommits()); $view->setUser($request->getUser()); $view->setNoDataString($nodata); $phids = $view->getRequiredHandlePHIDs(); $handles = $this->loadViewerHandles($phids); $view->setHandles($handles); $panel = new AphrontPanelView(); $panel->setHeader($header); $panel->appendChild($view); $panel->setNoBackground(); if ($use_pager) { $panel->appendChild($pager); } return $panel; } private function buildCommitView(PhabricatorObjectHandle $handle = null) { $request = $this->getRequest(); $query = new PhabricatorAuditCommitQuery(); $query->needCommitData(true); $query->needAudits(true); $use_pager = ($this->filter != 'active'); if ($use_pager) { $pager = new AphrontPagerView(); $pager->setURI($request->getRequestURI(), 'offset'); $pager->setOffset($request->getInt('offset')); $query->setOffset($pager->getOffset()); $query->setLimit($pager->getPageSize() + 1); } switch ($this->filter) { case 'active': case 'author': $query->withAuthorPHIDs(array($handle->getPHID())); break; case 'packagecommits': $query->withPackagePHIDs(array($handle->getPHID())); break; } switch ($this->filter) { case 'active': $query->withStatus(PhabricatorAuditQuery::STATUS_OPEN); break; case 'author': case 'packagecommits': switch ($this->filterStatus) { case 'open': $query->withStatus(PhabricatorAuditQuery::STATUS_OPEN); break; } break; } if ($handle) { - $handle_name = phutil_escape_html($handle->getName()); + $handle_name = $handle->getName(); } else { $handle_name = null; } switch ($this->filter) { case 'active': $header = pht('Problem Commits'); $nodata = pht('None of your commits have open concerns.'); break; case 'author': $header = pht("Commits by %s", $handle_name); $nodata = pht("No matching commits by %s.", $handle_name); break; case 'commits': $header = pht("Commits"); $nodata = pht("No matching commits."); break; case 'packagecommits': $header = pht("Commits in Package %s", $handle_name); $nodata = pht("No matching commits in package %s.", $handle_name); break; } $commits = $query->execute(); if ($use_pager) { $commits = $pager->sliceResults($commits); } $view = new PhabricatorAuditCommitListView(); $view->setUser($request->getUser()); $view->setCommits($commits); $view->setNoDataString($nodata); $phids = $view->getRequiredHandlePHIDs(); $handles = $this->loadViewerHandles($phids); $view->setHandles($handles); $panel = new AphrontPanelView(); $panel->setHeader($header); $panel->appendChild($view); $panel->setNoBackground(); if ($use_pager) { $panel->appendChild($pager); } return $panel; } } diff --git a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php index 8e7f2bec39..68f4fb4935 100644 --- a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php +++ b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php @@ -1,135 +1,135 @@ method = $data['method']; } public function processRequest() { $request = $this->getRequest(); $methods = $this->getAllMethods(); if (empty($methods[$this->method])) { return new Aphront404Response(); } $this->setFilter('method/'.$this->method); $method_class = $methods[$this->method]; $method_object = newv($method_class, array()); $status = $method_object->getMethodStatus(); $reason = $method_object->getMethodStatusDescription(); $status_view = null; if ($status != ConduitAPIMethod::METHOD_STATUS_STABLE) { $status_view = new AphrontErrorView(); switch ($status) { case ConduitAPIMethod::METHOD_STATUS_DEPRECATED: $status_view->setTitle('Deprecated Method'); $status_view->appendChild( nonempty($reason, "This method is deprecated.")); break; case ConduitAPIMethod::METHOD_STATUS_UNSTABLE: $status_view->setSeverity(AphrontErrorView::SEVERITY_WARNING); $status_view->setTitle('Unstable Method'); $status_view->appendChild( nonempty( $reason, "This method is new and unstable. Its interface is subject ". "to change.")); break; } } $error_types = $method_object->defineErrorTypes(); if ($error_types) { $error_description = array(); foreach ($error_types as $error => $meaning) { $error_description[] = hsprintf( '
  • %s: %s
  • ', $error, $meaning); } $error_description = phutil_tag('ul', array(), $error_description); } else { $error_description = "This method does not raise any specific errors."; } $form = new AphrontFormView(); $form ->setUser($request->getUser()) ->setAction('/api/'.$this->method) ->addHiddenInput('allowEmptyParams', 1) ->appendChild( id(new AphrontFormStaticControl()) ->setLabel('Description') ->setValue($method_object->getMethodDescription())) ->appendChild( id(new AphrontFormStaticControl()) ->setLabel('Returns') ->setValue($method_object->defineReturnType())) ->appendChild( id(new AphrontFormMarkupControl()) ->setLabel('Errors') ->setValue($error_description)) ->appendChild(hsprintf( '

    Enter parameters using '. 'JSON. For instance, to enter a list, type: '. '["apple", "banana", "cherry"]')); $params = $method_object->defineParamTypes(); foreach ($params as $param => $desc) { $form->appendChild( id(new AphrontFormTextControl()) ->setLabel($param) ->setName("params[{$param}]") ->setCaption($desc)); } $form ->appendChild( id(new AphrontFormSelectControl()) ->setLabel('Output Format') ->setName('output') ->setOptions( array( 'human' => 'Human Readable', 'json' => 'JSON', ))) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue('Call Method')); $panel = new AphrontPanelView(); - $panel->setHeader('Conduit API: '.phutil_escape_html($this->method)); + $panel->setHeader('Conduit API: '.$this->method); $panel->appendChild($form); $panel->setWidth(AphrontPanelView::WIDTH_FULL); return $this->buildStandardPageResponse( array( $status_view, $panel, ), array( 'title' => 'Conduit Console - '.$this->method, )); } private function getAllMethods() { $classes = $this->getAllMethodImplementationClasses(); $methods = array(); foreach ($classes as $class) { $name = ConduitAPIMethod::getAPIMethodNameFromClassName($class); $methods[$name] = $class; } return $methods; } } diff --git a/src/applications/diffusion/controller/DiffusionBrowseController.php b/src/applications/diffusion/controller/DiffusionBrowseController.php index 7d3ac8ed7c..d24b9a209b 100644 --- a/src/applications/diffusion/controller/DiffusionBrowseController.php +++ b/src/applications/diffusion/controller/DiffusionBrowseController.php @@ -1,121 +1,121 @@ diffusionRequest; if ($this->getRequest()->getStr('before')) { $results = array(); $is_file = true; } else { $browse_query = DiffusionBrowseQuery::newFromDiffusionRequest($drequest); $results = $browse_query->loadPaths(); $reason = $browse_query->getReasonForEmptyResultSet(); $is_file = ($reason == DiffusionBrowseQuery::REASON_IS_FILE); } $content = array(); if ($drequest->getTagContent()) { $title = 'Tag: '.$drequest->getSymbolicCommit(); $tag_view = new AphrontPanelView(); - $tag_view->setHeader(phutil_escape_html($title)); + $tag_view->setHeader($title); $tag_view->appendChild( $this->markupText($drequest->getTagContent())); $content[] = $tag_view; } if (!$results) { if ($is_file) { $controller = new DiffusionBrowseFileController($this->getRequest()); $controller->setDiffusionRequest($drequest); $controller->setCurrentApplication($this->getCurrentApplication()); return $this->delegateToController($controller); } $empty_result = new DiffusionEmptyResultView(); $empty_result->setDiffusionRequest($drequest); $empty_result->setBrowseQuery($browse_query); $empty_result->setView($this->getRequest()->getStr('view')); $content[] = $empty_result; } else { $phids = array(); foreach ($results as $result) { $data = $result->getLastCommitData(); if ($data) { if ($data->getCommitDetail('authorPHID')) { $phids[$data->getCommitDetail('authorPHID')] = true; } } } $phids = array_keys($phids); $handles = $this->loadViewerHandles($phids); $browse_table = new DiffusionBrowseTableView(); $browse_table->setDiffusionRequest($drequest); $browse_table->setHandles($handles); $browse_table->setPaths($results); $browse_table->setUser($this->getRequest()->getUser()); $browse_panel = new AphrontPanelView(); $browse_panel->appendChild($browse_table); $browse_panel->setNoBackground(); $content[] = $browse_panel; } $content[] = $this->buildOpenRevisions(); $readme_content = $browse_query->renderReadme($results); if ($readme_content) { $readme_panel = new AphrontPanelView(); $readme_panel->setHeader('README'); $readme_panel->appendChild($readme_content); $content[] = $readme_panel; } $nav = $this->buildSideNav('browse', false); $nav->appendChild($content); $crumbs = $this->buildCrumbs( array( 'branch' => true, 'path' => true, 'view' => 'browse', )); $nav->setCrumbs($crumbs); return $this->buildApplicationPage( $nav, array( 'title' => array( nonempty(basename($drequest->getPath()), '/'), $drequest->getRepository()->getCallsign().' Repository', ), )); } private function markupText($text) { $engine = PhabricatorMarkupEngine::newDiffusionMarkupEngine(); $text = phutil_safe_html($engine->markupText($text)); $text = phutil_tag( 'div', array( 'class' => 'phabricator-remarkup', ), $text); return $text; } } diff --git a/src/applications/diffusion/controller/DiffusionLintDetailsController.php b/src/applications/diffusion/controller/DiffusionLintDetailsController.php index fb7e81fc35..877a8479d0 100644 --- a/src/applications/diffusion/controller/DiffusionLintDetailsController.php +++ b/src/applications/diffusion/controller/DiffusionLintDetailsController.php @@ -1,142 +1,142 @@ getRequest()->getInt('offset', 0); $drequest = $this->getDiffusionRequest(); $branch = $drequest->loadBranch(); $messages = $this->loadLintMessages($branch, $limit, $offset); $is_dir = (substr('/'.$drequest->getPath(), -1) == '/'); $rows = array(); foreach ($messages as $message) { $path = hsprintf( '%s', $drequest->generateURI(array( 'action' => 'lint', 'path' => $message['path'], )), substr($message['path'], strlen($drequest->getPath()) + 1)); $line = hsprintf( '%s', $drequest->generateURI(array( 'action' => 'browse', 'path' => $message['path'], 'line' => $message['line'], 'commit' => $branch->getLintCommit(), )), $message['line']); $rows[] = array( $path, $line, ArcanistLintSeverity::getStringForSeverity($message['severity']), $message['name'], $message['description'], ); } $table = id(new AphrontTableView($rows)) ->setHeaders(array( 'Path', 'Line', 'Severity', 'Name', 'Description', )) ->setColumnClasses(array('', 'n', '', '', '')) ->setColumnVisibility(array($is_dir)); $content = array(); $pager = id(new AphrontPagerView()) ->setPageSize($limit) ->setOffset($offset) ->setHasMorePages(count($messages) >= $limit) ->setURI($this->getRequest()->getRequestURI(), 'offset'); $lint = $drequest->getLint(); $link = hsprintf( '%s', $drequest->generateURI(array( 'action' => 'lint', 'lint' => null, )), pht('Switch to Grouped View')); $content[] = id(new AphrontPanelView()) ->setHeader( - ($lint != '' ? phutil_escape_html($lint)." \xC2\xB7 " : ''). + ($lint != '' ? $lint." \xC2\xB7 " : ''). pht('%d Lint Message(s)', count($messages))) ->setCaption($link) ->appendChild($table) ->appendChild($pager); $nav = $this->buildSideNav('lint', false); $nav->appendChild($content); $crumbs = $this->buildCrumbs( array( 'branch' => true, 'path' => true, 'view' => 'lint', )); $nav->setCrumbs($crumbs); return $this->buildApplicationPage( $nav, array('title' => array( 'Lint', $drequest->getRepository()->getCallsign(), ))); } private function loadLintMessages( PhabricatorRepositoryBranch $branch, $limit, $offset) { $drequest = $this->getDiffusionRequest(); if (!$branch) { return array(); } $conn = $branch->establishConnection('r'); $where = array( qsprintf( $conn, 'branchID = %d', $branch->getID()) ); if ($drequest->getPath() != '') { $is_dir = (substr($drequest->getPath(), -1) == '/'); $where[] = qsprintf( $conn, 'path '.($is_dir ? 'LIKE %>' : '= %s'), '/'.$drequest->getPath()); } if ($drequest->getLint() != '') { $where[] = qsprintf( $conn, 'code = %s', $drequest->getLint()); } return queryfx_all( $conn, 'SELECT * FROM %T WHERE %Q ORDER BY path, code, line LIMIT %d OFFSET %d', PhabricatorRepository::TABLE_LINTMESSAGE, implode(' AND ', $where), $limit, $offset); } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php index 2f271fe6be..273dbc9250 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php @@ -1,256 +1,256 @@ diffusionRequest; $content = array(); $crumbs = $this->buildCrumbs(); $content[] = $crumbs; $content[] = $this->buildPropertiesTable($drequest->getRepository()); $history_query = DiffusionHistoryQuery::newFromDiffusionRequest( $drequest); $history_query->setLimit(15); $history_query->needParents(true); $history = $history_query->loadHistory(); $browse_query = DiffusionBrowseQuery::newFromDiffusionRequest($drequest); $browse_results = $browse_query->loadPaths(); $phids = array(); foreach ($history as $item) { $data = $item->getCommitData(); if ($data) { if ($data->getCommitDetail('authorPHID')) { $phids[$data->getCommitDetail('authorPHID')] = true; } if ($data->getCommitDetail('committerPHID')) { $phids[$data->getCommitDetail('committerPHID')] = true; } } } foreach ($browse_results as $item) { $data = $item->getLastCommitData(); if ($data) { if ($data->getCommitDetail('authorPHID')) { $phids[$data->getCommitDetail('authorPHID')] = true; } if ($data->getCommitDetail('committerPHID')) { $phids[$data->getCommitDetail('committerPHID')] = true; } } } $phids = array_keys($phids); $handles = $this->loadViewerHandles($phids); $history_table = new DiffusionHistoryTableView(); $history_table->setUser($this->getRequest()->getUser()); $history_table->setDiffusionRequest($drequest); $history_table->setHandles($handles); $history_table->setHistory($history); $history_table->loadRevisions(); $history_table->setParents($history_query->getParents()); $history_table->setIsHead(true); $callsign = $drequest->getRepository()->getCallsign(); $all = phutil_tag( 'a', array( 'href' => "/diffusion/{$callsign}/history/", ), 'View Full Commit History'); $panel = new AphrontPanelView(); - $panel->setHeader("Recent Commits · {$all}"); + $panel->setHeader(hsprintf("Recent Commits · %s", $all)); $panel->appendChild($history_table); $panel->setNoBackground(); $content[] = $panel; $browse_table = new DiffusionBrowseTableView(); $browse_table->setDiffusionRequest($drequest); $browse_table->setHandles($handles); $browse_table->setPaths($browse_results); $browse_table->setUser($this->getRequest()->getUser()); $browse_panel = new AphrontPanelView(); $browse_panel->setHeader('Browse Repository'); $browse_panel->appendChild($browse_table); $browse_panel->setNoBackground(); $content[] = $browse_panel; $content[] = $this->buildTagListTable($drequest); $content[] = $this->buildBranchListTable($drequest); $readme = $browse_query->renderReadme($browse_results); if ($readme) { $panel = new AphrontPanelView(); $panel->setHeader('README'); $panel->appendChild($readme); $content[] = $panel; } return $this->buildStandardPageResponse( $content, array( 'title' => $drequest->getRepository()->getName(), )); } private function buildPropertiesTable(PhabricatorRepository $repository) { $properties = array(); $properties['Name'] = $repository->getName(); $properties['Callsign'] = $repository->getCallsign(); $properties['Description'] = $repository->getDetail('description'); switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $properties['Clone URI'] = $repository->getPublicRemoteURI(); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $properties['Repository Root'] = $repository->getPublicRemoteURI(); break; } $rows = array(); foreach ($properties as $key => $value) { $rows[] = array($key, $value); } $table = new AphrontTableView($rows); $table->setColumnClasses( array( 'header', 'wide', )); $panel = new AphrontPanelView(); $panel->setHeader('Repository Properties'); $panel->appendChild($table); $panel->setNoBackground(); return $panel; } private function buildBranchListTable(DiffusionRequest $drequest) { if ($drequest->getBranch() !== null) { $limit = 15; $branch_query = DiffusionBranchQuery::newFromDiffusionRequest($drequest); $branch_query->setLimit($limit + 1); $branches = $branch_query->loadBranches(); if (!$branches) { return null; } $more_branches = (count($branches) > $limit); $branches = array_slice($branches, 0, $limit); $commits = id(new PhabricatorAuditCommitQuery()) ->withIdentifiers( $drequest->getRepository()->getID(), mpull($branches, 'getHeadCommitIdentifier')) ->needCommitData(true) ->execute(); $table = new DiffusionBranchTableView(); $table->setDiffusionRequest($drequest); $table->setBranches($branches); $table->setCommits($commits); $table->setUser($this->getRequest()->getUser()); $panel = new AphrontPanelView(); $panel->setHeader('Branches'); $panel->setNoBackground(); if ($more_branches) { $panel->setCaption('Showing ' . $limit . ' branches.'); } $panel->addButton( phutil_tag( 'a', array( 'href' => $drequest->generateURI( array( 'action' => 'branches', )), 'class' => 'grey button', ), "Show All Branches \xC2\xBB")); $panel->appendChild($table); return $panel; } return null; } private function buildTagListTable(DiffusionRequest $drequest) { $tag_limit = 15; $query = DiffusionTagListQuery::newFromDiffusionRequest($drequest); $query->setLimit($tag_limit + 1); $tags = $query->loadTags(); if (!$tags) { return null; } $more_tags = (count($tags) > $tag_limit); $tags = array_slice($tags, 0, $tag_limit); $commits = id(new PhabricatorAuditCommitQuery()) ->withIdentifiers( $drequest->getRepository()->getID(), mpull($tags, 'getCommitIdentifier')) ->needCommitData(true) ->execute(); $view = new DiffusionTagListView(); $view->setDiffusionRequest($drequest); $view->setTags($tags); $view->setUser($this->getRequest()->getUser()); $view->setCommits($commits); $phids = $view->getRequiredHandlePHIDs(); $handles = $this->loadViewerHandles($phids); $view->setHandles($handles); $panel = new AphrontPanelView(); $panel->setHeader('Tags'); if ($more_tags) { $panel->setCaption('Showing the '.$tag_limit.' most recent tags.'); } $panel->addButton( phutil_tag( 'a', array( 'href' => $drequest->generateURI( array( 'action' => 'tags', )), 'class' => 'grey button', ), "Show All Tags \xC2\xBB")); $panel->appendChild($view); return $panel; } } diff --git a/src/applications/maniphest/controller/ManiphestReportController.php b/src/applications/maniphest/controller/ManiphestReportController.php index ca1d7a3cc7..229ba2e1f4 100644 --- a/src/applications/maniphest/controller/ManiphestReportController.php +++ b/src/applications/maniphest/controller/ManiphestReportController.php @@ -1,764 +1,764 @@ view = idx($data, 'view'); } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); if ($request->isFormPost()) { $uri = $request->getRequestURI(); $project = head($request->getArr('set_project')); $project = nonempty($project, null); $uri = $uri->alter('project', $project); $window = $request->getStr('set_window'); $uri = $uri->alter('window', $window); return id(new AphrontRedirectResponse())->setURI($uri); } $base_nav = $this->buildBaseSideNav(); $base_nav->selectFilter('report', 'report'); $nav = new AphrontSideNavFilterView(); $nav->setBaseURI(new PhutilURI('/maniphest/report/')); $nav->addLabel('Open Tasks'); $nav->addFilter('user', 'By User'); $nav->addFilter('project', 'By Project'); $nav->addLabel('Burnup'); $nav->addFilter('burn', 'Burnup Rate'); $this->view = $nav->selectFilter($this->view, 'user'); require_celerity_resource('maniphest-report-css'); switch ($this->view) { case 'burn': $core = $this->renderBurn(); break; case 'user': case 'project': $core = $this->renderOpenTasks(); break; default: return new Aphront404Response(); } $nav->appendChild($core); $base_nav->appendChild($nav); return $this->buildStandardPageResponse( $base_nav, array( 'title' => 'Maniphest Reports', )); } public function renderBurn() { $request = $this->getRequest(); $user = $request->getUser(); $handle = null; $project_phid = $request->getStr('project'); if ($project_phid) { $phids = array($project_phid); $handles = $this->loadViewerHandles($phids); $handle = $handles[$project_phid]; } $table = new ManiphestTransaction(); $conn = $table->establishConnection('r'); $joins = ''; if ($project_phid) { $joins = qsprintf( $conn, 'JOIN %T t ON x.taskID = t.id JOIN %T p ON p.taskPHID = t.phid AND p.projectPHID = %s', id(new ManiphestTask())->getTableName(), id(new ManiphestTaskProject())->getTableName(), $project_phid); } $data = queryfx_all( $conn, 'SELECT x.oldValue, x.newValue, x.dateCreated FROM %T x %Q WHERE transactionType = %s ORDER BY x.dateCreated ASC', $table->getTableName(), $joins, ManiphestTransactionType::TYPE_STATUS); $stats = array(); $day_buckets = array(); $open_tasks = array(); foreach ($data as $key => $row) { // NOTE: Hack to avoid json_decode(). $oldv = trim($row['oldValue'], '"'); $newv = trim($row['newValue'], '"'); $old_is_open = ($oldv === (string)ManiphestTaskStatus::STATUS_OPEN); $new_is_open = ($newv === (string)ManiphestTaskStatus::STATUS_OPEN); $is_open = ($new_is_open && !$old_is_open); $is_close = ($old_is_open && !$new_is_open); $data[$key]['_is_open'] = $is_open; $data[$key]['_is_close'] = $is_close; if (!$is_open && !$is_close) { // This is either some kind of bogus event, or a resolution change // (e.g., resolved -> invalid). Just skip it. continue; } $day_bucket = phabricator_format_local_time( $row['dateCreated'], $user, 'Yz'); $day_buckets[$day_bucket] = $row['dateCreated']; if (empty($stats[$day_bucket])) { $stats[$day_bucket] = array( 'open' => 0, 'close' => 0, ); } $stats[$day_bucket][$is_close ? 'close' : 'open']++; } $template = array( 'open' => 0, 'close' => 0, ); $rows = array(); $rowc = array(); $last_month = null; $last_month_epoch = null; $last_week = null; $last_week_epoch = null; $week = null; $month = null; $last = last_key($stats) - 1; $period = $template; foreach ($stats as $bucket => $info) { $epoch = $day_buckets[$bucket]; $week_bucket = phabricator_format_local_time( $epoch, $user, 'YW'); if ($week_bucket != $last_week) { if ($week) { $rows[] = $this->formatBurnRow( 'Week of '.phabricator_date($last_week_epoch, $user), $week); $rowc[] = 'week'; } $week = $template; $last_week = $week_bucket; $last_week_epoch = $epoch; } $month_bucket = phabricator_format_local_time( $epoch, $user, 'Ym'); if ($month_bucket != $last_month) { if ($month) { $rows[] = $this->formatBurnRow( phabricator_format_local_time($last_month_epoch, $user, 'F, Y'), $month); $rowc[] = 'month'; } $month = $template; $last_month = $month_bucket; $last_month_epoch = $epoch; } $rows[] = $this->formatBurnRow(phabricator_date($epoch, $user), $info); $rowc[] = null; $week['open'] += $info['open']; $week['close'] += $info['close']; $month['open'] += $info['open']; $month['close'] += $info['close']; $period['open'] += $info['open']; $period['close'] += $info['close']; } if ($week) { $rows[] = $this->formatBurnRow( 'Week To Date', $week); $rowc[] = 'week'; } if ($month) { $rows[] = $this->formatBurnRow( 'Month To Date', $month); $rowc[] = 'month'; } $rows[] = $this->formatBurnRow( 'All Time', $period); $rowc[] = 'aggregate'; $rows = array_reverse($rows); $rowc = array_reverse($rowc); $table = new AphrontTableView($rows); $table->setRowClasses($rowc); $table->setHeaders( array( 'Period', 'Opened', 'Closed', 'Change', )); $table->setColumnClasses( array( 'right wide', 'n', 'n', 'n', )); if ($handle) { - $header = "Task Burn Rate for Project ".$handle->renderLink(); + $header = pht("Task Burn Rate for Project %s", $handle->renderLink()); $caption = hsprintf( "

    NOTE: This table reflects tasks currently in ". "the project. If a task was opened in the past but added to ". "the project recently, it is counted on the day it was ". "opened, not the day it was categorized. If a task was part ". "of this project in the past but no longer is, it is not ". "counted at all.

    "); } else { $header = "Task Burn Rate for All Tasks"; $caption = null; } $panel = new AphrontPanelView(); $panel->setHeader($header); $panel->setCaption($caption); $panel->appendChild($table); $tokens = array(); if ($handle) { $tokens = array( $handle->getPHID() => $handle->getFullName(), ); } $filter = $this->renderReportFilters($tokens, $has_window = false); $id = celerity_generate_unique_node_id(); $chart = phutil_tag( 'div', array( 'id' => $id, 'style' => 'border: 1px solid #6f6f6f; '. 'margin: 1em 2em; '. 'height: 400px; ', ), ''); list($burn_x, $burn_y) = $this->buildSeries($data); require_celerity_resource('raphael-core'); require_celerity_resource('raphael-g'); require_celerity_resource('raphael-g-line'); Javelin::initBehavior('line-chart', array( 'hardpoint' => $id, 'x' => array( $burn_x, ), 'y' => array( $burn_y, ), 'xformat' => 'epoch', )); return array($filter, $chart, $panel); } private function renderReportFilters(array $tokens, $has_window) { $request = $this->getRequest(); $user = $request->getUser(); $form = id(new AphrontFormView()) ->setUser($user) ->appendChild( id(new AphrontFormTokenizerControl()) ->setDatasource('/typeahead/common/searchproject/') ->setLabel('Project') ->setLimit(1) ->setName('set_project') ->setValue($tokens)); if ($has_window) { list($window_str, $ignored, $window_error) = $this->getWindow(); $form ->appendChild( id(new AphrontFormTextControl()) ->setLabel('"Recently" Means') ->setName('set_window') ->setCaption( 'Configure the cutoff for the "Recently Closed" column.') ->setValue($window_str) ->setError($window_error)); } $form ->appendChild( id(new AphrontFormSubmitControl()) ->setValue('Filter By Project')); $filter = new AphrontListFilterView(); $filter->appendChild($form); return $filter; } private function buildSeries(array $data) { $out = array(); $counter = 0; foreach ($data as $row) { $t = (int)$row['dateCreated']; if ($row['_is_close']) { --$counter; $out[$t] = $counter; } else if ($row['_is_open']) { ++$counter; $out[$t] = $counter; } } return array(array_keys($out), array_values($out)); } private function formatBurnRow($label, $info) { $delta = $info['open'] - $info['close']; $fmt = number_format($delta); if ($delta > 0) { $fmt = '+'.$fmt; $fmt = hsprintf('%s', $fmt); } else { $fmt = hsprintf('%s', $fmt); } return array( $label, number_format($info['open']), number_format($info['close']), $fmt); } public function renderOpenTasks() { $request = $this->getRequest(); $user = $request->getUser(); $query = id(new ManiphestTaskQuery()) ->withStatus(ManiphestTaskQuery::STATUS_OPEN); $project_phid = $request->getStr('project'); $project_handle = null; if ($project_phid) { $phids = array($project_phid); $handles = $this->loadViewerHandles($phids); $project_handle = $handles[$project_phid]; $query->withAnyProjects($phids); } $tasks = $query->execute(); $recently_closed = $this->loadRecentlyClosedTasks(); $date = phabricator_date(time(), $user); switch ($this->view) { case 'user': $result = mgroup($tasks, 'getOwnerPHID'); $leftover = idx($result, '', array()); unset($result['']); $result_closed = mgroup($recently_closed, 'getOwnerPHID'); $leftover_closed = idx($result_closed, '', array()); unset($result_closed['']); $base_link = '/maniphest/?users='; $leftover_name = phutil_tag( 'a', array( 'href' => $base_link.ManiphestTaskOwner::OWNER_UP_FOR_GRABS, ), phutil_tag('em', array(), '(Up For Grabs)')); $col_header = 'User'; $header = 'Open Tasks by User and Priority ('.$date.')'; break; case 'project': $result = array(); $leftover = array(); foreach ($tasks as $task) { $phids = $task->getProjectPHIDs(); if ($phids) { foreach ($phids as $project_phid) { $result[$project_phid][] = $task; } } else { $leftover[] = $task; } } $result_closed = array(); $leftover_closed = array(); foreach ($recently_closed as $task) { $phids = $task->getProjectPHIDs(); if ($phids) { foreach ($phids as $project_phid) { $result_closed[$project_phid][] = $task; } } else { $leftover_closed[] = $task; } } $base_link = '/maniphest/view/all/?projects='; $leftover_name = phutil_tag( 'a', array( 'href' => $base_link.ManiphestTaskOwner::PROJECT_NO_PROJECT, ), phutil_tag('em', array(), '(No Project)')); $col_header = 'Project'; $header = 'Open Tasks by Project and Priority ('.$date.')'; break; } $phids = array_keys($result); $handles = $this->loadViewerHandles($phids); $handles = msort($handles, 'getName'); $order = $request->getStr('order', 'name'); list($order, $reverse) = AphrontTableView::parseSort($order); require_celerity_resource('aphront-tooltip-css'); Javelin::initBehavior('phabricator-tooltips', array()); $rows = array(); $pri_total = array(); foreach (array_merge($handles, array(null)) as $handle) { if ($handle) { if (($project_handle) && ($project_handle->getPHID() == $handle->getPHID())) { // If filtering by, e.g., "bugs", don't show a "bugs" group. continue; } $tasks = idx($result, $handle->getPHID(), array()); $name = phutil_tag( 'a', array( 'href' => $base_link.$handle->getPHID(), ), $handle->getName()); $closed = idx($result_closed, $handle->getPHID(), array()); } else { $tasks = $leftover; $name = $leftover_name; $closed = $leftover_closed; } $taskv = $tasks; $tasks = mgroup($tasks, 'getPriority'); $row = array(); $row[] = $name; $total = 0; foreach (ManiphestTaskPriority::getTaskPriorityMap() as $pri => $label) { $n = count(idx($tasks, $pri, array())); if ($n == 0) { $row[] = '-'; } else { $row[] = number_format($n); } $total += $n; } $row[] = number_format($total); list($link, $oldest_all) = $this->renderOldest($taskv); $row[] = $link; $normal_or_better = array(); foreach ($taskv as $id => $task) { if ($task->getPriority() < ManiphestTaskPriority::PRIORITY_NORMAL) { continue; } $normal_or_better[$id] = $task; } list($link, $oldest_pri) = $this->renderOldest($normal_or_better); $row[] = $link; if ($closed) { $task_ids = implode(',', mpull($closed, 'getID')); $row[] = phutil_tag( 'a', array( 'href' => '/maniphest/view/custom/?s=oc&tasks='.$task_ids, 'target' => '_blank', ), number_format(count($closed))); } else { $row[] = '-'; } switch ($order) { case 'total': $row['sort'] = $total; break; case 'oldest-all': $row['sort'] = $oldest_all; break; case 'oldest-pri': $row['sort'] = $oldest_pri; break; case 'closed': $row['sort'] = count($closed); break; case 'name': default: $row['sort'] = $handle ? $handle->getName() : '~'; break; } $rows[] = $row; } $rows = isort($rows, 'sort'); foreach ($rows as $k => $row) { unset($rows[$k]['sort']); } if ($reverse) { $rows = array_reverse($rows); } $cname = array($col_header); $cclass = array('pri right wide'); $pri_map = ManiphestTaskPriority::getTaskBriefPriorityMap(); foreach ($pri_map as $pri => $label) { $cname[] = $label; $cclass[] = 'n'; } $cname[] = 'Total'; $cclass[] = 'n'; $cname[] = javelin_tag( 'span', array( 'sigil' => 'has-tooltip', 'meta' => array( 'tip' => 'Oldest open task.', 'size' => 200, ), ), 'Oldest (All)'); $cclass[] = 'n'; $cname[] = javelin_tag( 'span', array( 'sigil' => 'has-tooltip', 'meta' => array( 'tip' => 'Oldest open task, excluding those with Low or Wishlist '. 'priority.', 'size' => 200, ), ), 'Oldest (Pri)'); $cclass[] = 'n'; list($ignored, $window_epoch) = $this->getWindow(); $cname[] = javelin_tag( 'span', array( 'sigil' => 'has-tooltip', 'meta' => array( 'tip' => 'Closed after '.phabricator_datetime($window_epoch, $user), 'size' => 260 ), ), 'Recently Closed'); $cclass[] = 'n'; $table = new AphrontTableView($rows); $table->setHeaders($cname); $table->setColumnClasses($cclass); $table->makeSortable( $request->getRequestURI(), 'order', $order, $reverse, array( 'name', null, null, null, null, null, null, 'total', 'oldest-all', 'oldest-pri', 'closed', )); $panel = new AphrontPanelView(); $panel->setHeader($header); $panel->appendChild($table); $tokens = array(); if ($project_handle) { $tokens = array( $project_handle->getPHID() => $project_handle->getFullName(), ); } $filter = $this->renderReportFilters($tokens, $has_window = true); return array($filter, $panel); } /** * Load all the tasks that have been recently closed. */ private function loadRecentlyClosedTasks() { list($ignored, $window_epoch) = $this->getWindow(); $table = new ManiphestTask(); $xtable = new ManiphestTransaction(); $conn_r = $table->establishConnection('r'); $tasks = queryfx_all( $conn_r, 'SELECT t.* FROM %T t JOIN %T x ON x.taskID = t.id WHERE t.status != 0 AND x.oldValue IN (null, %s, %s) AND x.newValue NOT IN (%s, %s) AND t.dateModified >= %d AND x.dateCreated >= %d', $table->getTableName(), $xtable->getTableName(), // TODO: Gross. This table is not meant to be queried like this. Build // real stats tables. json_encode((int)ManiphestTaskStatus::STATUS_OPEN), json_encode((string)ManiphestTaskStatus::STATUS_OPEN), json_encode((int)ManiphestTaskStatus::STATUS_OPEN), json_encode((string)ManiphestTaskStatus::STATUS_OPEN), $window_epoch, $window_epoch); return id(new ManiphestTask())->loadAllFromArray($tasks); } /** * Parse the "Recently Means" filter into: * * - A string representation, like "12 AM 7 days ago" (default); * - a locale-aware epoch representation; and * - a possible error. */ private function getWindow() { $request = $this->getRequest(); $user = $request->getUser(); $window_str = $this->getRequest()->getStr('window', '12 AM 7 days ago'); $error = null; $window_epoch = null; // Do locale-aware parsing so that the user's timezone is assumed for // time windows like "3 PM", rather than assuming the server timezone. $timezone = new DateTimeZone($user->getTimezoneIdentifier()); try { $date = new DateTime($window_str, $timezone); $window_epoch = $date->format('U'); } catch (Exception $e) { $error = 'Invalid'; $window_epoch = time() - (60 * 60 * 24 * 7); } // If the time ends up in the future, convert it to the corresponding time // and equal distance in the past. This is so users can type "6 days" (which // means "6 days from now") and get the behavior of "6 days ago", rather // than no results (because the window epoch is in the future). This might // be a little confusing because it casues "tomorrow" to mean "yesterday" // and "2022" (or whatever) to mean "ten years ago", but these inputs are // nonsense anyway. if ($window_epoch > time()) { $window_epoch = time() - ($window_epoch - time()); } return array($window_str, $window_epoch, $error); } private function renderOldest(array $tasks) { assert_instances_of($tasks, 'ManiphestTask'); $oldest = null; foreach ($tasks as $id => $task) { if (($oldest === null) || ($task->getDateCreated() < $tasks[$oldest]->getDateCreated())) { $oldest = $id; } } if ($oldest === null) { return array('-', 0); } $oldest = $tasks[$oldest]; $raw_age = (time() - $oldest->getDateCreated()); $age = number_format($raw_age / (24 * 60 * 60)).' d'; $link = javelin_tag( 'a', array( 'href' => '/T'.$oldest->getID(), 'sigil' => 'has-tooltip', 'meta' => array( 'tip' => 'T'.$oldest->getID().': '.$oldest->getTitle(), ), 'target' => '_blank', ), $age); return array($link, $raw_age); } } diff --git a/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php b/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php index a0b4e1d592..4eb21e5b80 100644 --- a/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php +++ b/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php @@ -1,207 +1,206 @@ getRequest(); $current_user = $request->getUser(); $server = new PhabricatorOAuthServer(); $client_phid = $request->getStr('client_id'); $scope = $request->getStr('scope', array()); $redirect_uri = $request->getStr('redirect_uri'); $state = $request->getStr('state'); $response_type = $request->getStr('response_type'); $response = new PhabricatorOAuthResponse(); // state is an opaque value the client sent us for their own purposes // we just need to send it right back to them in the response! if ($state) { $response->setState($state); } if (!$client_phid) { $response->setError('invalid_request'); $response->setErrorDescription( 'Required parameter client_id not specified.' ); return $response; } $server->setUser($current_user); // one giant try / catch around all the exciting database stuff so we // can return a 'server_error' response if something goes wrong! try { $client = id(new PhabricatorOAuthServerClient()) ->loadOneWhere('phid = %s', $client_phid); if (!$client) { $response->setError('invalid_request'); $response->setErrorDescription( 'Client with id '.$client_phid.' not found.' ); return $response; } $server->setClient($client); if ($redirect_uri) { $client_uri = new PhutilURI($client->getRedirectURI()); $redirect_uri = new PhutilURI($redirect_uri); if (!($server->validateSecondaryRedirectURI($redirect_uri, $client_uri))) { $response->setError('invalid_request'); $response->setErrorDescription( 'The specified redirect URI is invalid. The redirect URI '. 'must be a fully-qualified domain with no fragments and '. 'must have the same domain and at least the same query '. 'parameters as the redirect URI the client registered.' ); return $response; } $uri = $redirect_uri; } else { $uri = new PhutilURI($client->getRedirectURI()); } // we've now validated this request enough overall such that we // can safely redirect to the client with the response $response->setClientURI($uri); if (empty($response_type)) { $response->setError('invalid_request'); $response->setErrorDescription( 'Required parameter response_type not specified.' ); return $response; } if ($response_type != 'code') { $response->setError('unsupported_response_type'); $response->setErrorDescription( 'The authorization server does not support obtaining an '. 'authorization code using the specified response_type. '. 'You must specify the response_type as "code".' ); return $response; } if ($scope) { if (!PhabricatorOAuthServerScope::validateScopesList($scope)) { $response->setError('invalid_scope'); $response->setErrorDescription( 'The requested scope is invalid, unknown, or malformed.' ); return $response; } $scope = PhabricatorOAuthServerScope::scopesListToDict($scope); } list($is_authorized, $authorization) = $server->userHasAuthorizedClient($scope); if ($is_authorized) { $return_auth_code = true; $unguarded_write = AphrontWriteGuard::beginScopedUnguardedWrites(); } else if ($request->isFormPost()) { $scope = PhabricatorOAuthServerScope::getScopesFromRequest($request); if ($authorization) { $authorization->setScope($scope)->save(); } else { $authorization = $server->authorizeClient($scope); } $return_auth_code = true; $unguarded_write = null; } else { $return_auth_code = false; $unguarded_write = null; } if ($return_auth_code) { // step 1 -- generate authorization code $auth_code = $server->generateAuthorizationCode($uri); // step 2 return it $content = array( 'code' => $auth_code->getCode(), 'scope' => $authorization->getScopeString(), ); $response->setContent($content); return $response; } unset($unguarded_write); } catch (Exception $e) { // Note we could try harder to determine between a server_error // vs temporarily_unavailable. Good enough though. $response->setError('server_error'); $response->setErrorDescription( 'The authorization server encountered an unexpected condition '. 'which prevented it from fulfilling the request. ' ); return $response; } // display time -- make a nice form for the user to grant the client // access to the granularity specified by $scope - $name = phutil_escape_html($client->getName()); - $title = 'Authorize ' . $name . '?'; + $title = 'Authorize '.$client->getName().'?'; $panel = new AphrontPanelView(); $panel->setWidth(AphrontPanelView::WIDTH_FORM); $panel->setHeader($title); $description = "Do want to authorize {$name} to access your ". "Phabricator account data?"; if ($scope) { if ($authorization) { $desired_scopes = array_merge($scope, $authorization->getScope()); } else { $desired_scopes = $scope; } if (!PhabricatorOAuthServerScope::validateScopesDict($desired_scopes)) { $response->setError('invalid_scope'); $response->setErrorDescription( 'The requested scope is invalid, unknown, or malformed.' ); return $response; } } else { $desired_scopes = array( PhabricatorOAuthServerScope::SCOPE_WHOAMI => 1, PhabricatorOAuthServerScope::SCOPE_OFFLINE_ACCESS => 1 ); } $cancel_uri = clone $uri; $cancel_params = array( 'error' => 'access_denied', 'error_description' => 'The resource owner (aka the user) denied the request.' ); $cancel_uri->setQueryParams($cancel_params); $form = id(new AphrontFormView()) ->setUser($current_user) ->appendChild( id(new AphrontFormStaticControl()) ->setValue($description) ) ->appendChild( PhabricatorOAuthServerScope::getCheckboxControl($desired_scopes) ) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue('Authorize') ->addCancelButton($cancel_uri) ); $panel->appendChild($form); return $this->buildStandardPageResponse( $panel, array('title' => $title)); } } diff --git a/src/applications/owners/controller/PhabricatorOwnersDetailController.php b/src/applications/owners/controller/PhabricatorOwnersDetailController.php index 7381a20c1a..eae6ac2050 100644 --- a/src/applications/owners/controller/PhabricatorOwnersDetailController.php +++ b/src/applications/owners/controller/PhabricatorOwnersDetailController.php @@ -1,228 +1,227 @@ id = $data['id']; } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $package = id(new PhabricatorOwnersPackage())->load($this->id); if (!$package) { return new Aphront404Response(); } $this->package = $package; $paths = $package->loadPaths(); $owners = $package->loadOwners(); $repository_phids = array(); foreach ($paths as $path) { $repository_phids[$path->getRepositoryPHID()] = true; } if ($repository_phids) { $repositories = id(new PhabricatorRepository())->loadAllWhere( 'phid in (%Ls)', array_keys($repository_phids)); $repositories = mpull($repositories, null, 'getPHID'); } else { $repositories = array(); } $phids = array(); foreach ($owners as $owner) { $phids[$owner->getUserPHID()] = true; } $phids = array_keys($phids); $handles = $this->loadViewerHandles($phids); $rows = array(); $rows[] = array('Name', $package->getName()); $rows[] = array('Description', $package->getDescription()); $primary_owner = null; $primary_phid = $package->getPrimaryOwnerPHID(); if ($primary_phid && isset($handles[$primary_phid])) { $primary_owner = phutil_tag( 'strong', array(), $handles[$primary_phid]->renderLink()); } $rows[] = array('Primary Owner', $primary_owner); $owner_links = array(); foreach ($owners as $owner) { $owner_links[] = $handles[$owner->getUserPHID()]->renderLink(); } $owner_links = phutil_implode_html(phutil_tag('br'), $owner_links); $rows[] = array('Owners', $owner_links); $rows[] = array( 'Auditing', $package->getAuditingEnabled() ? 'Enabled' : 'Disabled', ); $path_links = array(); foreach ($paths as $path) { $repo = idx($repositories, $path->getRepositoryPHID()); if (!$repo) { continue; } $href = DiffusionRequest::generateDiffusionURI( array( 'callsign' => $repo->getCallsign(), 'branch' => $repo->getDefaultBranch(), 'path' => $path->getPath(), 'action' => 'browse' )); $repo_name = phutil_tag('strong', array(), $repo->getName()); $path_link = phutil_tag( 'a', array( 'href' => (string) $href, ), $path->getPath()); $path_links[] = hsprintf( '%s %s %s', ($path->getExcluded() ? "\xE2\x80\x93" : '+'), $repo_name, $path_link); } $path_links = phutil_implode_html(phutil_tag('br'), $path_links); $rows[] = array('Paths', $path_links); $table = new AphrontTableView($rows); $table->setColumnClasses( array( 'header', 'wide', )); $panel = new AphrontPanelView(); - $panel->setHeader( - 'Package Details for "'.phutil_escape_html($package->getName()).'"'); + $panel->setHeader('Package Details for "'.$package->getName().'"'); $panel->addButton( javelin_tag( 'a', array( 'href' => '/owners/delete/'.$package->getID().'/', 'class' => 'button grey', 'sigil' => 'workflow', ), 'Delete Package')); $panel->addButton( phutil_tag( 'a', array( 'href' => '/owners/edit/'.$package->getID().'/', 'class' => 'button', ), 'Edit Package')); $panel->appendChild($table); $key = 'package/'.$package->getID(); $this->setSideNavFilter($key); $commit_views = array(); $commit_uri = id(new PhutilURI('/audit/view/packagecommits/')) ->setQueryParams( array( 'phid' => $package->getPHID(), )); $attention_query = id(new PhabricatorAuditCommitQuery()) ->withPackagePHIDs(array($package->getPHID())) ->withStatus(PhabricatorAuditCommitQuery::STATUS_OPEN) ->needCommitData(true) ->needAudits(true) ->setLimit(10); $attention_commits = $attention_query->execute(); if ($attention_commits) { $view = new PhabricatorAuditCommitListView(); $view->setUser($user); $view->setCommits($attention_commits); $commit_views[] = array( 'view' => $view, 'header' => 'Commits in this Package that Need Attention', 'button' => phutil_tag( 'a', array( 'href' => $commit_uri->alter('status', 'open'), 'class' => 'button grey', ), 'View All Problem Commits'), ); } $all_query = id(new PhabricatorAuditCommitQuery()) ->withPackagePHIDs(array($package->getPHID())) ->needCommitData(true) ->needAudits(true) ->setLimit(100); $all_commits = $all_query->execute(); $view = new PhabricatorAuditCommitListView(); $view->setUser($user); $view->setCommits($all_commits); $view->setNoDataString('No commits in this package.'); $commit_views[] = array( 'view' => $view, 'header' => 'Recent Commits in Package', 'button' => phutil_tag( 'a', array( 'href' => $commit_uri, 'class' => 'button grey', ), 'View All Package Commits'), ); $phids = array(); foreach ($commit_views as $commit_view) { $phids[] = $commit_view['view']->getRequiredHandlePHIDs(); } $phids = array_mergev($phids); $handles = $this->loadViewerHandles($phids); $commit_panels = array(); foreach ($commit_views as $commit_view) { $commit_panel = new AphrontPanelView(); - $commit_panel->setHeader(phutil_escape_html($commit_view['header'])); + $commit_panel->setHeader($commit_view['header']); if (isset($commit_view['button'])) { $commit_panel->addButton($commit_view['button']); } $commit_view['view']->setHandles($handles); $commit_panel->appendChild($commit_view['view']); $commit_panels[] = $commit_panel; } return $this->buildStandardPageResponse( array( $panel, $commit_panels, ), array( 'title' => "Package '".$package->getName()."'", )); } protected function getExtraPackageViews(AphrontSideNavFilterView $view) { $package = $this->package; $view->addFilter('package/'.$package->getID(), 'Details'); } } diff --git a/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php b/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php index 44b3a06d5d..5285ff6061 100644 --- a/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php +++ b/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php @@ -1,459 +1,459 @@ id = $data['id']; } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $viewer_phid = $user->getPHID(); $poll = id(new PhabricatorSlowvotePoll())->load($this->id); if (!$poll) { return new Aphront404Response(); } $options = id(new PhabricatorSlowvoteOption())->loadAllWhere( 'pollID = %d', $poll->getID()); $choices = id(new PhabricatorSlowvoteChoice())->loadAllWhere( 'pollID = %d', $poll->getID()); $comments = id(new PhabricatorSlowvoteComment())->loadAllWhere( 'pollID = %d', $poll->getID()); $choices_by_option = mgroup($choices, 'getOptionID'); $comments_by_user = mpull($comments, null, 'getAuthorPHID'); $choices_by_user = mgroup($choices, 'getAuthorPHID'); $viewer_choices = idx($choices_by_user, $viewer_phid, array()); $viewer_comment = idx($comments_by_user, $viewer_phid, null); $comment_text = null; if ($viewer_comment) { $comment_text = $viewer_comment->getCommentText(); } if ($request->isFormPost()) { $comment = idx($comments_by_user, $viewer_phid, null); if ($comment) { $comment->delete(); } $comment_text = $request->getStr('comments'); if (strlen($comment_text)) { id(new PhabricatorSlowvoteComment()) ->setAuthorPHID($viewer_phid) ->setPollID($poll->getID()) ->setCommentText($comment_text) ->save(); } $votes = $request->getArr('vote'); switch ($poll->getMethod()) { case PhabricatorSlowvotePoll::METHOD_PLURALITY: // Enforce only one vote. $votes = array_slice($votes, 0, 1); break; case PhabricatorSlowvotePoll::METHOD_APPROVAL: // No filtering. break; default: throw new Exception("Unknown poll method!"); } foreach ($viewer_choices as $viewer_choice) { $viewer_choice->delete(); } foreach ($votes as $vote) { id(new PhabricatorSlowvoteChoice()) ->setAuthorPHID($viewer_phid) ->setPollID($poll->getID()) ->setOptionID($vote) ->save(); } return id(new AphrontRedirectResponse())->setURI('/V'.$poll->getID()); } require_celerity_resource('phabricator-slowvote-css'); $phids = array_merge( mpull($choices, 'getAuthorPHID'), mpull($comments, 'getAuthorPHID'), array( $poll->getAuthorPHID(), )); $query = new PhabricatorObjectHandleData($phids); $handles = $query->loadHandles(); $objects = $query->loadObjects(); if ($poll->getShuffle()) { shuffle($options); } $option_markup = array(); foreach ($options as $option) { $option_markup[] = $this->renderPollOption( $poll, $viewer_choices, $option); } $comments_by_option = array(); switch ($poll->getMethod()) { case PhabricatorSlowvotePoll::METHOD_PLURALITY: $choice_ids = array(); foreach ($choices_by_user as $user_phid => $user_choices) { $choice_ids[$user_phid] = head($user_choices)->getOptionID(); } foreach ($comments as $comment) { $choice = idx($choice_ids, $comment->getAuthorPHID()); if ($choice) { $comments_by_option[$choice][] = $comment; } } break; case PhabricatorSlowvotePoll::METHOD_APPROVAL: // All comments are grouped in approval voting. break; default: throw new Exception("Unknown poll method!"); } $result_markup = $this->renderResultMarkup( $poll, $options, $choices, $comments, $viewer_choices, $choices_by_option, $comments_by_option, $handles, $objects); if ($viewer_choices) { $instructions = 'Your vote has been recorded... but there is still ample time to '. 'rethink your position. Have you thoroughly considered all possible '. 'eventualities?'; } else { $instructions = 'This is a weighty matter indeed. Consider your choices with the '. 'greatest of care.'; } $form = id(new AphrontFormView()) ->setUser($user) ->appendChild(hsprintf( '

    %s

    ', $instructions)) ->appendChild( id(new AphrontFormMarkupControl()) ->setLabel('Vote') ->setValue($option_markup)) ->appendChild( id(new AphrontFormTextAreaControl()) ->setLabel('Comments') ->setHeight(AphrontFormTextAreaControl::HEIGHT_SHORT) ->setName('comments') ->setValue($comment_text)) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue('Cautiously Engage in Deliberations')); $panel = new AphrontPanelView(); - $panel->setHeader(phutil_escape_html($poll->getQuestion())); + $panel->setHeader($poll->getQuestion()); $panel->setWidth(AphrontPanelView::WIDTH_WIDE); $panel->appendChild($form); $panel->appendChild('

    '); $panel->appendChild($result_markup); return $this->buildStandardPageResponse( $panel, array( 'title' => 'V'.$poll->getID().' '.$poll->getQuestion(), )); } private function renderComments(array $comments, array $handles) { assert_instances_of($comments, 'PhabricatorSlowvoteComment'); assert_instances_of($handles, 'PhabricatorObjectHandle'); $viewer = $this->getRequest()->getUser(); $engine = PhabricatorMarkupEngine::newSlowvoteMarkupEngine(); $comment_markup = array(); foreach ($comments as $comment) { $handle = $handles[$comment->getAuthorPHID()]; $markup = phutil_safe_html( $engine->markupText($comment->getCommentText())); require_celerity_resource('phabricator-remarkup-css'); $comment_markup[] = hsprintf( ''. ''. '%s'. '
    %s
    '. ''. ''. '
    %s
    '. ''. '', $handle->renderLink(), phabricator_datetime($comment->getDateCreated(), $viewer), $markup); } if ($comment_markup) { $comment_markup = phutil_tag( 'table', array( 'class' => 'phabricator-slowvote-comments', ), $comment_markup); } else { $comment_markup = null; } return $comment_markup; } private function renderPollOption( PhabricatorSlowvotePoll $poll, array $viewer_choices, PhabricatorSlowvoteOption $option) { assert_instances_of($viewer_choices, 'PhabricatorSlowvoteChoice'); $id = $option->getID(); switch ($poll->getMethod()) { case PhabricatorSlowvotePoll::METHOD_PLURALITY: // Render a radio button. $selected_option = head($viewer_choices); if ($selected_option) { $selected = $selected_option->getOptionID(); } else { $selected = null; } if ($selected == $id) { $checked = "checked"; } else { $checked = null; } $input = phutil_tag( 'input', array( 'type' => 'radio', 'name' => 'vote[]', 'value' => $id, 'checked' => $checked, )); break; case PhabricatorSlowvotePoll::METHOD_APPROVAL: // Render a check box. $checked = null; foreach ($viewer_choices as $choice) { if ($choice->getOptionID() == $id) { $checked = 'checked'; break; } } $input = phutil_tag( 'input', array( 'type' => 'checkbox', 'name' => 'vote[]', 'checked' => $checked, 'value' => $id, )); break; default: throw new Exception("Unknown poll method!"); } if ($checked) { $checked_class = 'phabricator-slowvote-checked'; } else { $checked_class = null; } return phutil_tag( 'label', array( 'class' => 'phabricator-slowvote-label '.$checked_class, ), array($input, $option->getName())); } private function renderVoteCount( PhabricatorSlowvotePoll $poll, array $choices, array $chosen) { assert_instances_of($choices, 'PhabricatorSlowvoteChoice'); assert_instances_of($chosen, 'PhabricatorSlowvoteChoice'); switch ($poll->getMethod()) { case PhabricatorSlowvotePoll::METHOD_PLURALITY: $out_of_total = count($choices); break; case PhabricatorSlowvotePoll::METHOD_APPROVAL: // Count unique respondents for approval votes. $out_of_total = count(mpull($choices, null, 'getAuthorPHID')); break; default: throw new Exception("Unknown poll method!"); } return sprintf( '%d / %d (%d%%)', number_format(count($chosen)), number_format($out_of_total), $out_of_total ? round(100 * count($chosen) / $out_of_total) : 0); } private function renderResultMarkup( PhabricatorSlowvotePoll $poll, array $options, array $choices, array $comments, array $viewer_choices, array $choices_by_option, array $comments_by_option, array $handles, array $objects) { assert_instances_of($options, 'PhabricatorSlowvoteOption'); assert_instances_of($choices, 'PhabricatorSlowvoteChoice'); assert_instances_of($comments, 'PhabricatorSlowvoteComment'); assert_instances_of($viewer_choices, 'PhabricatorSlowvoteChoice'); assert_instances_of($handles, 'PhabricatorObjectHandle'); assert_instances_of($objects, 'PhabricatorLiskDAO'); $viewer_phid = $this->getRequest()->getUser()->getPHID(); $can_see_responses = false; $need_vote = false; switch ($poll->getResponseVisibility()) { case PhabricatorSlowvotePoll::RESPONSES_VISIBLE: $can_see_responses = true; break; case PhabricatorSlowvotePoll::RESPONSES_VOTERS: $can_see_responses = (bool)$viewer_choices; $need_vote = true; break; case PhabricatorSlowvotePoll::RESPONSES_OWNER: $can_see_responses = ($viewer_phid == $poll->getAuthorPHID()); break; } $result_markup = id(new AphrontFormLayoutView()) ->appendChild(phutil_tag('h1', array(), 'Ongoing Deliberation')); if (!$can_see_responses) { if ($need_vote) { $reason = "You must vote to see the results."; } else { $reason = "The results are not public."; } $result_markup ->appendChild(hsprintf( '

    %s

    ', $reason)); return $result_markup; } foreach ($options as $option) { $id = $option->getID(); $chosen = idx($choices_by_option, $id, array()); $users = array_select_keys($handles, mpull($chosen, 'getAuthorPHID')); if ($users) { $user_markup = array(); foreach ($users as $handle) { $object = idx($objects, $handle->getPHID()); if (!$object) { continue; } $profile_image = $handle->getImageURI(); $user_markup[] = phutil_tag( 'a', array( 'href' => $handle->getURI(), 'class' => 'phabricator-slowvote-facepile', ), phutil_tag( 'img', array( 'src' => $profile_image, ))); } } else { $user_markup = 'This option has failed to appeal to anyone.'; } $comment_markup = $this->renderComments( idx($comments_by_option, $id, array()), $handles); $vote_count = $this->renderVoteCount( $poll, $choices, $chosen); $result_markup->appendChild(hsprintf( '
    '. '
    %s
    '. '

    %s

    '. '
    '. '%s'. '
    '. '
    '. '%s'. '
    ', $vote_count, $option->getName(), phutil_tag('div', array(), $user_markup), $comment_markup)); } if ($poll->getMethod() == PhabricatorSlowvotePoll::METHOD_APPROVAL && $comments) { $comment_markup = $this->renderComments( $comments, $handles); $result_markup->appendChild( phutil_tag('h1', array(), 'Motions Proposed for Consideration')); $result_markup->appendChild($comment_markup); } return $result_markup; } } diff --git a/src/view/layout/AphrontPanelView.php b/src/view/layout/AphrontPanelView.php index 861ad07416..596af7c7c8 100644 --- a/src/view/layout/AphrontPanelView.php +++ b/src/view/layout/AphrontPanelView.php @@ -1,111 +1,112 @@ addButton( phutil_tag( 'a', array( 'href' => $href, 'class' => 'button green', ), $create_button)); return $this; } public function addClass($class) { $this->classes[] = $class; return $this; } public function addButton($button) { $this->buttons[] = $button; return $this; } public function setHeader($header) { $this->header = $header; return $this; } public function setWidth($width) { $this->width = $width; return $this; } public function setID($id) { $this->id = $id; return $this; } public function setCaption($caption) { $this->caption = $caption; return $this; } public function setNoBackground() { $this->classes[] = 'aphront-panel-plain'; return $this; } public function render() { if ($this->header !== null) { - $header = '

    '.$this->header.'

    '; + $header = phutil_tag('h1', array(), $this->header); } else { $header = null; } if ($this->caption !== null) { $caption = phutil_tag( 'div', array('class' => 'aphront-panel-view-caption'), $this->caption); } else { $caption = null; } $buttons = null; if ($this->buttons) { - $buttons = - '
    '. - implode(" ", $this->buttons). - '
    '; + $buttons = hsprintf( + '
    %s
    ', + phutil_implode_html(" ", $this->buttons)); } - $header_elements = - '
    '. - $buttons.$header.$caption. - '
    '; - $table = implode('', $this->renderChildren()); + $header_elements = hsprintf( + '
    %s%s%s
    ', + $buttons, + $header, + $caption); + + // TODO: [HTML] Make HTML safe. + $table = phutil_safe_html(implode('', $this->renderChildren())); require_celerity_resource('aphront-panel-view-css'); $classes = $this->classes; $classes[] = 'aphront-panel-view'; if ($this->width) { $classes[] = 'aphront-panel-width-'.$this->width; } return phutil_tag( 'div', array( 'class' => implode(' ', $classes), 'id' => $this->id, ), - // TODO: [HTML] Make HTML safe. - phutil_safe_html($header_elements.$table)); + array($header_elements, $table)); } }