Something went wrong. Try again.
@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
Something went wrong. Try again.
38 kB · 1153 lines
PHP
at commit dcd7a316
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154<?php
final class DiffusionCommitController extends DiffusionController {
const CHANGES_LIMIT = 100;
private $auditAuthorityPHIDs; private $highlightedAudits;
public function shouldAllowPublic() { return true; }
public function willProcessRequest(array $data) { // This controller doesn't use blob/path stuff, just pass the dictionary // in directly instead of using the AphrontRequest parsing mechanism. $data['user'] = $this->getRequest()->getUser(); $drequest = DiffusionRequest::newFromDictionary($data); $this->diffusionRequest = $drequest; }
public function processRequest() { $drequest = $this->getDiffusionRequest(); $request = $this->getRequest(); $user = $request->getUser();
if ($request->getStr('diff')) { return $this->buildRawDiffResponse($drequest); }
$callsign = $drequest->getRepository()->getCallsign();
$content = array(); $repository = $drequest->getRepository(); $commit = $drequest->loadCommit();
$crumbs = $this->buildCrumbs(array( 'commit' => true, ));
if (!$commit) { $exists = $this->callConduitWithDiffusionRequest( 'diffusion.existsquery', array('commit' => $drequest->getCommit())); if (!$exists) { return new Aphront404Response(); }
$error = id(new AphrontErrorView()) ->setTitle(pht('Commit Still Parsing')) ->appendChild( pht( 'Failed to load the commit because the commit has not been '. 'parsed yet.'));
return $this->buildApplicationPage( array( $crumbs, $error, ), array( 'title' => pht('Commit Still Parsing'), )); }
$commit_data = $drequest->loadCommitData(); $commit->attachCommitData($commit_data);
$top_anchor = id(new PhabricatorAnchorView()) ->setAnchorName('top') ->setNavigationMarker(true);
$audit_requests = id(new PhabricatorAuditQuery()) ->withCommitPHIDs(array($commit->getPHID())) ->execute(); $this->auditAuthorityPHIDs = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user);
$is_foreign = $commit_data->getCommitDetail('foreign-svn-stub'); $changesets = null; if ($is_foreign) { $subpath = $commit_data->getCommitDetail('svn-subpath');
$error_panel = new AphrontErrorView(); $error_panel->setTitle(pht('Commit Not Tracked')); $error_panel->setSeverity(AphrontErrorView::SEVERITY_WARNING); $error_panel->appendChild( pht("This Diffusion repository is configured to track only one ". "subdirectory of the entire Subversion repository, and this commit ". "didn't affect the tracked subdirectory ('%s'), so no ". "information is available.", $subpath)); $content[] = $error_panel; $content[] = $top_anchor; } else { $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); $engine->setConfig('viewer', $user);
require_celerity_resource('diffusion-commit-view-css'); require_celerity_resource('phabricator-remarkup-css');
$parents = $this->callConduitWithDiffusionRequest( 'diffusion.commitparentsquery', array('commit' => $drequest->getCommit()));
if ($parents) { $parents = id(new DiffusionCommitQuery()) ->setViewer($user) ->withRepository($repository) ->withIdentifiers($parents) ->execute(); }
$headsup_view = id(new PHUIHeaderView()) ->setHeader(nonempty($commit->getSummary(), pht('Commit Detail')));
$headsup_actions = $this->renderHeadsupActionList($commit, $repository);
$commit_properties = $this->loadCommitProperties( $commit, $commit_data, $parents, $audit_requests); $property_list = id(new PHUIPropertyListView()) ->setHasKeyboardShortcuts(true) ->setUser($user) ->setObject($commit); foreach ($commit_properties as $key => $value) { $property_list->addProperty($key, $value); }
$message = $commit_data->getCommitMessage();
$revision = $commit->getCommitIdentifier(); $message = $this->linkBugtraq($message);
$message = $engine->markupText($message);
$property_list->invokeWillRenderEvent(); $property_list->setActionList($headsup_actions);
$detail_list = new PHUIPropertyListView(); $detail_list->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); $detail_list->addTextContent( phutil_tag( 'div', array( 'class' => 'diffusion-commit-message phabricator-remarkup', ), $message)); $content[] = $top_anchor;
$object_box = id(new PHUIObjectBoxView()) ->setHeader($headsup_view) ->addPropertyList($property_list) ->addPropertyList($detail_list);
$content[] = $object_box; }
$content[] = $this->buildComments($commit);
$hard_limit = 1000;
if ($commit->isImported()) { $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest( $drequest); $change_query->setLimit($hard_limit + 1); $changes = $change_query->loadChanges(); } else { $changes = array(); }
$was_limited = (count($changes) > $hard_limit); if ($was_limited) { $changes = array_slice($changes, 0, $hard_limit); }
$content[] = $this->buildMergesTable($commit);
// TODO: This is silly, but the logic to figure out which audits are // highlighted currently lives in PhabricatorAuditListView. Refactor this // to be less goofy. $highlighted_audits = id(new PhabricatorAuditListView()) ->setAudits($audit_requests) ->setAuthorityPHIDs($this->auditAuthorityPHIDs) ->setUser($user) ->setCommits(array($commit->getPHID() => $commit)) ->getHighlightedAudits();
$owners_paths = array(); if ($highlighted_audits) { $packages = id(new PhabricatorOwnersPackage())->loadAllWhere( 'phid IN (%Ls)', mpull($highlighted_audits, 'getAuditorPHID')); if ($packages) { $owners_paths = id(new PhabricatorOwnersPath())->loadAllWhere( 'repositoryPHID = %s AND packageID IN (%Ld)', $repository->getPHID(), mpull($packages, 'getID')); } }
$change_table = new DiffusionCommitChangeTableView(); $change_table->setDiffusionRequest($drequest); $change_table->setPathChanges($changes); $change_table->setOwnersPaths($owners_paths);
$count = count($changes);
$bad_commit = null; if ($count == 0) { $bad_commit = queryfx_one( id(new PhabricatorRepository())->establishConnection('r'), 'SELECT * FROM %T WHERE fullCommitName = %s', PhabricatorRepository::TABLE_BADCOMMIT, 'r'.$callsign.$commit->getCommitIdentifier()); }
if ($bad_commit) { $content[] = $this->renderStatusMessage( pht('Bad Commit'), $bad_commit['description']); } else if ($is_foreign) { // Don't render anything else. } else if (!$commit->isImported()) { $content[] = $this->renderStatusMessage( pht('Still Importing...'), pht( 'This commit is still importing. Changes will be visible once '. 'the import finishes.')); } else if (!count($changes)) { $content[] = $this->renderStatusMessage( pht('Empty Commit'), pht( 'This commit is empty and does not affect any paths.')); } else if ($was_limited) { $content[] = $this->renderStatusMessage( pht('Enormous Commit'), pht( 'This commit is enormous, and affects more than %d files. '. 'Changes are not shown.', $hard_limit)); } else { // The user has clicked "Show All Changes", and we should show all the // changes inline even if there are more than the soft limit. $show_all_details = $request->getBool('show_all');
$change_panel = new PHUIObjectBoxView(); $header = new PHUIHeaderView(); $header->setHeader("Changes (".number_format($count).")"); $change_panel->setID('toc'); if ($count > self::CHANGES_LIMIT && !$show_all_details) {
$icon = id(new PHUIIconView()) ->setSpriteSheet(PHUIIconView::SPRITE_ICONS) ->setSpriteIcon('transcript');
$button = id(new PHUIButtonView()) ->setText(pht('Show All Changes')) ->setHref('?show_all=true') ->setTag('a') ->setIcon($icon);
$warning_view = id(new AphrontErrorView()) ->setSeverity(AphrontErrorView::SEVERITY_WARNING) ->setTitle('Very Large Commit') ->appendChild( pht("This commit is very large. Load each file individually."));
$change_panel->setErrorView($warning_view); $header->addActionLink($button); }
$change_panel->appendChild($change_table); $change_panel->setHeader($header);
$content[] = $change_panel;
$changesets = DiffusionPathChange::convertToDifferentialChangesets( $changes);
$vcs = $repository->getVersionControlSystem(); switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $vcs_supports_directory_changes = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $vcs_supports_directory_changes = false; break; default: throw new Exception("Unknown VCS."); }
$references = array(); foreach ($changesets as $key => $changeset) { $file_type = $changeset->getFileType(); if ($file_type == DifferentialChangeType::FILE_DIRECTORY) { if (!$vcs_supports_directory_changes) { unset($changesets[$key]); continue; } }
$references[$key] = $drequest->generateURI( array( 'action' => 'rendering-ref', 'path' => $changeset->getFilename(), )); }
// TODO: Some parts of the views still rely on properties of the // DifferentialChangeset. Make the objects ephemeral to make sure we don't // accidentally save them, and then set their ID to the appropriate ID for // this application (the path IDs). $path_ids = array_flip(mpull($changes, 'getPath')); foreach ($changesets as $changeset) { $changeset->makeEphemeral(); $changeset->setID($path_ids[$changeset->getFilename()]); }
if ($count <= self::CHANGES_LIMIT || $show_all_details) { $visible_changesets = $changesets; } else { $visible_changesets = array(); $inlines = id(new PhabricatorAuditInlineComment())->loadAllWhere( 'commitPHID = %s AND (auditCommentID IS NOT NULL OR authorPHID = %s)', $commit->getPHID(), $user->getPHID()); $path_ids = mpull($inlines, null, 'getPathID'); foreach ($changesets as $key => $changeset) { if (array_key_exists($changeset->getID(), $path_ids)) { $visible_changesets[$key] = $changeset; } } }
$change_list_title = DiffusionView::nameCommit( $repository, $commit->getCommitIdentifier()); $change_list = new DifferentialChangesetListView(); $change_list->setTitle($change_list_title); $change_list->setChangesets($changesets); $change_list->setVisibleChangesets($visible_changesets); $change_list->setRenderingReferences($references); $change_list->setRenderURI('/diffusion/'.$callsign.'/diff/'); $change_list->setRepository($repository); $change_list->setUser($user);
// TODO: Try to setBranch() to something reasonable here?
$change_list->setStandaloneURI( '/diffusion/'.$callsign.'/diff/'); $change_list->setRawFileURIs( // TODO: Implement this, somewhat tricky if there's an octopus merge // or whatever? null, '/diffusion/'.$callsign.'/diff/?view=r');
$change_list->setInlineCommentControllerURI( '/diffusion/inline/edit/'.phutil_escape_uri($commit->getPHID()).'/');
$change_references = array(); foreach ($changesets as $key => $changeset) { $change_references[$changeset->getID()] = $references[$key]; } $change_table->setRenderingReferences($change_references);
$content[] = $change_list->render(); }
$content[] = $this->renderAddCommentPanel($commit, $audit_requests);
$commit_id = 'r'.$callsign.$commit->getCommitIdentifier(); $short_name = DiffusionView::nameCommit( $repository, $commit->getCommitIdentifier());
$prefs = $user->loadPreferences(); $pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE; $pref_collapse = PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED; $show_filetree = $prefs->getPreference($pref_filetree); $collapsed = $prefs->getPreference($pref_collapse);
if ($changesets && $show_filetree) { $nav = id(new DifferentialChangesetFileTreeSideNavBuilder()) ->setAnchorName('top') ->setTitle($short_name) ->setBaseURI(new PhutilURI('/'.$commit_id)) ->build($changesets) ->setCrumbs($crumbs) ->setCollapsed((bool)$collapsed) ->appendChild($content); $content = $nav; } else { $content = array($crumbs, $content); }
return $this->buildApplicationPage( $content, array( 'title' => $commit_id, 'pageObjects' => array($commit->getPHID()), )); }
private function loadCommitProperties( PhabricatorRepositoryCommit $commit, PhabricatorRepositoryCommitData $data, array $parents, array $audit_requests) {
assert_instances_of($parents, 'PhabricatorRepositoryCommit'); $viewer = $this->getRequest()->getUser(); $commit_phid = $commit->getPHID(); $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository();
$edge_query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(array($commit_phid)) ->withEdgeTypes(array( PhabricatorEdgeConfig::TYPE_COMMIT_HAS_TASK, PhabricatorEdgeConfig::TYPE_COMMIT_HAS_PROJECT, PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV, ));
$edges = $edge_query->execute();
$task_phids = array_keys( $edges[$commit_phid][PhabricatorEdgeConfig::TYPE_COMMIT_HAS_TASK]); $proj_phids = array_keys( $edges[$commit_phid][PhabricatorEdgeConfig::TYPE_COMMIT_HAS_PROJECT]); $revision_phid = key( $edges[$commit_phid][PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV]);
$phids = $edge_query->getDestinationPHIDs(array($commit_phid));
if ($data->getCommitDetail('authorPHID')) { $phids[] = $data->getCommitDetail('authorPHID'); } if ($data->getCommitDetail('reviewerPHID')) { $phids[] = $data->getCommitDetail('reviewerPHID'); } if ($data->getCommitDetail('committerPHID')) { $phids[] = $data->getCommitDetail('committerPHID'); } if ($parents) { foreach ($parents as $parent) { $phids[] = $parent->getPHID(); } }
// NOTE: We should never normally have more than a single push log, but // it can occur naturally if a commit is pushed, then the branch it was // on is deleted, then the commit is pushed again (or through other similar // chains of events). This should be rare, but does not indicate a bug // or data issue.
// NOTE: We never query push logs in SVN because the commiter is always // the pusher and the commit time is always the push time; the push log // is redundant and we save a query by skipping it.
$push_logs = array(); if ($repository->isHosted() && !$repository->isSVN()) { $push_logs = id(new PhabricatorRepositoryPushLogQuery()) ->setViewer($viewer) ->withRepositoryPHIDs(array($repository->getPHID())) ->withNewRefs(array($commit->getCommitIdentifier())) ->withRefTypes(array(PhabricatorRepositoryPushLog::REFTYPE_COMMIT)) ->execute(); foreach ($push_logs as $log) { $phids[] = $log->getPusherPHID(); } }
$handles = array(); if ($phids) { $handles = $this->loadViewerHandles($phids); }
$props = array();
if ($commit->getAuditStatus()) { $status = PhabricatorAuditCommitStatusConstants::getStatusName( $commit->getAuditStatus()); $tag = id(new PHUITagView()) ->setType(PHUITagView::TYPE_STATE) ->setName($status);
switch ($commit->getAuditStatus()) { case PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT: $tag->setBackgroundColor(PHUITagView::COLOR_ORANGE); break; case PhabricatorAuditCommitStatusConstants::CONCERN_RAISED: $tag->setBackgroundColor(PHUITagView::COLOR_RED); break; case PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED: $tag->setBackgroundColor(PHUITagView::COLOR_BLUE); break; case PhabricatorAuditCommitStatusConstants::FULLY_AUDITED: $tag->setBackgroundColor(PHUITagView::COLOR_GREEN); break; }
$props['Status'] = $tag; }
if ($audit_requests) { $user_requests = array(); $other_requests = array(); foreach ($audit_requests as $audit_request) { if ($audit_request->isUser()) { $user_requests[] = $audit_request; } else { $other_requests[] = $audit_request; } }
if ($user_requests) { $props['Auditors'] = $this->renderAuditStatusView( $user_requests); }
if ($other_requests) { $props['Project/Package Auditors'] = $this->renderAuditStatusView( $other_requests); } }
$author_phid = $data->getCommitDetail('authorPHID'); $author_name = $data->getAuthorName();
if (!$repository->isSVN()) { $authored_info = id(new PHUIStatusItemView()); // TODO: In Git, a distinct authorship date is available. When present, // we should show it here.
if ($author_phid) { $authored_info->setTarget($handles[$author_phid]->renderLink()); } else if (strlen($author_name)) { $authored_info->setTarget($author_name); }
$props['Authored'] = id(new PHUIStatusListView()) ->addItem($authored_info); }
$committed_info = id(new PHUIStatusItemView()) ->setNote(phabricator_datetime($commit->getEpoch(), $viewer));
$committer_phid = $data->getCommitDetail('committerPHID'); $committer_name = $data->getCommitDetail('committer'); if ($committer_phid) { $committed_info->setTarget($handles[$committer_phid]->renderLink()); } else if (strlen($committer_name)) { $committed_info->setTarget($committer_name); } else if ($author_phid) { $committed_info->setTarget($handles[$author_phid]->renderLink()); } else if (strlen($author_name)) { $committed_info->setTarget($author_name); }
$props['Committed'] = id(new PHUIStatusListView()) ->addItem($committed_info);
if ($push_logs) { $pushed_list = new PHUIStatusListView();
foreach ($push_logs as $push_log) { $pushed_item = id(new PHUIStatusItemView()) ->setTarget($handles[$push_log->getPusherPHID()]->renderLink()) ->setNote(phabricator_datetime($push_log->getEpoch(), $viewer)); $pushed_list->addItem($pushed_item); }
$props['Pushed'] = $pushed_list; }
$reviewer_phid = $data->getCommitDetail('reviewerPHID'); if ($reviewer_phid) { $props['Reviewer'] = $handles[$reviewer_phid]->renderLink(); }
if ($revision_phid) { $props['Differential Revision'] = $handles[$revision_phid]->renderLink(); }
if ($parents) { $parent_links = array(); foreach ($parents as $parent) { $parent_links[] = $handles[$parent->getPHID()]->renderLink(); } $props['Parents'] = phutil_implode_html(" \xC2\xB7 ", $parent_links); }
$props['Branches'] = phutil_tag( 'span', array( 'id' => 'commit-branches', ), pht('Unknown')); $props['Tags'] = phutil_tag( 'span', array( 'id' => 'commit-tags', ), pht('Unknown'));
$callsign = $repository->getCallsign(); $root = '/diffusion/'.$callsign.'/commit/'.$commit->getCommitIdentifier(); Javelin::initBehavior( 'diffusion-commit-branches', array( $root.'/branches/' => 'commit-branches', $root.'/tags/' => 'commit-tags', ));
$refs = $this->buildRefs($drequest); if ($refs) { $props['References'] = $refs; }
if ($task_phids) { $task_list = array(); foreach ($task_phids as $phid) { $task_list[] = $handles[$phid]->renderLink(); } $task_list = phutil_implode_html(phutil_tag('br'), $task_list); $props['Tasks'] = $task_list; }
if ($proj_phids) { $proj_list = array(); foreach ($proj_phids as $phid) { $proj_list[] = $handles[$phid]->renderLink(); } $proj_list = phutil_implode_html(phutil_tag('br'), $proj_list); $props['Projects'] = $proj_list; }
return $props; }
private function buildComments(PhabricatorRepositoryCommit $commit) { $user = $this->getRequest()->getUser(); $comments = id(new PhabricatorAuditComment())->loadAllWhere( 'targetPHID = %s ORDER BY dateCreated ASC', $commit->getPHID());
$inlines = id(new PhabricatorAuditInlineComment())->loadAllWhere( 'commitPHID = %s AND auditCommentID IS NOT NULL', $commit->getPHID());
$path_ids = mpull($inlines, 'getPathID');
$path_map = array(); if ($path_ids) { $path_map = id(new DiffusionPathQuery()) ->withPathIDs($path_ids) ->execute(); $path_map = ipull($path_map, 'path', 'id'); }
$engine = new PhabricatorMarkupEngine(); $engine->setViewer($user);
foreach ($comments as $comment) { $engine->addObject( $comment, PhabricatorAuditComment::MARKUP_FIELD_BODY); }
foreach ($inlines as $inline) { $engine->addObject( $inline, PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY); }
$engine->process();
$view = new DiffusionCommentListView(); $view->setMarkupEngine($engine); $view->setUser($user); $view->setComments($comments); $view->setInlineComments($inlines); $view->setPathMap($path_map);
$phids = $view->getRequiredHandlePHIDs(); $handles = $this->loadViewerHandles($phids); $view->setHandles($handles);
return $view; }
private function renderAddCommentPanel( PhabricatorRepositoryCommit $commit, array $audit_requests) { assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
$request = $this->getRequest(); $user = $request->getUser();
if (!$user->isLoggedIn()) { return id(new PhabricatorApplicationTransactionCommentView()) ->setUser($user) ->setRequestURI($request->getRequestURI()); }
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$pane_id = celerity_generate_unique_node_id(); Javelin::initBehavior( 'differential-keyboard-navigation', array( 'haunt' => $pane_id, ));
$draft = id(new PhabricatorDraft())->loadOneWhere( 'authorPHID = %s AND draftKey = %s', $user->getPHID(), 'diffusion-audit-'.$commit->getID()); if ($draft) { $draft = $draft->getDraft(); } else { $draft = null; }
$actions = $this->getAuditActions($commit, $audit_requests);
$form = id(new AphrontFormView()) ->setUser($user) ->setAction('/audit/addcomment/') ->addHiddenInput('commit', $commit->getPHID()) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel(pht('Action')) ->setName('action') ->setID('audit-action') ->setOptions($actions)) ->appendChild( id(new AphrontFormTokenizerControl()) ->setLabel(pht('Add Auditors')) ->setName('auditors') ->setControlID('add-auditors') ->setControlStyle('display: none') ->setID('add-auditors-tokenizer') ->setDisableBehavior(true)) ->appendChild( id(new AphrontFormTokenizerControl()) ->setLabel(pht('Add CCs')) ->setName('ccs') ->setControlID('add-ccs') ->setControlStyle('display: none') ->setID('add-ccs-tokenizer') ->setDisableBehavior(true)) ->appendChild( id(new PhabricatorRemarkupControl()) ->setLabel(pht('Comments')) ->setName('content') ->setValue($draft) ->setID('audit-content') ->setUser($user)) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue($is_serious ? pht('Submit') : pht('Cook the Books')));
$header = new PHUIHeaderView(); $header->setHeader( $is_serious ? pht('Audit Commit') : pht('Creative Accounting'));
require_celerity_resource('phabricator-transaction-view-css');
Javelin::initBehavior( 'differential-add-reviewers-and-ccs', array( 'dynamic' => array( 'add-auditors-tokenizer' => array( 'actions' => array('add_auditors' => 1), 'src' => '/typeahead/common/users/', 'row' => 'add-auditors', 'placeholder' => pht('Type a user name...'), ), 'add-ccs-tokenizer' => array( 'actions' => array('add_ccs' => 1), 'src' => '/typeahead/common/mailable/', 'row' => 'add-ccs', 'placeholder' => pht('Type a user or mailing list...'), ), ), 'select' => 'audit-action', ));
Javelin::initBehavior('differential-feedback-preview', array( 'uri' => '/audit/preview/'.$commit->getID().'/', 'preview' => 'audit-preview', 'content' => 'audit-content', 'action' => 'audit-action', 'previewTokenizers' => array( 'auditors' => 'add-auditors-tokenizer', 'ccs' => 'add-ccs-tokenizer', ), 'inline' => 'inline-comment-preview', 'inlineuri' => '/diffusion/inline/preview/'.$commit->getPHID().'/', ));
$loading = phutil_tag_div( 'aphront-panel-preview-loading-text', pht('Loading preview...'));
$preview_panel = phutil_tag_div( 'aphront-panel-preview aphront-panel-flush', array( phutil_tag('div', array('id' => 'audit-preview'), $loading), phutil_tag('div', array('id' => 'inline-comment-preview')) ));
// TODO: This is pretty awkward, unify the CSS between Diffusion and // Differential better. require_celerity_resource('differential-core-view-css');
$anchor = id(new PhabricatorAnchorView()) ->setAnchorName('comment') ->setNavigationMarker(true) ->render();
$comment_box = id(new PHUIObjectBoxView()) ->setHeader($header) ->appendChild($form);
return phutil_tag( 'div', array( 'id' => $pane_id, ), phutil_tag_div( 'differential-add-comment-panel', array($anchor, $comment_box, $preview_panel))); }
/** * Return a map of available audit actions for rendering into a <select />. * This shows the user valid actions, and does not show nonsense/invalid * actions (like closing an already-closed commit, or resigning from a commit * you have no association with). */ private function getAuditActions( PhabricatorRepositoryCommit $commit, array $audit_requests) { assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest'); $user = $this->getRequest()->getUser();
$user_is_author = ($commit->getAuthorPHID() == $user->getPHID());
$user_request = null; foreach ($audit_requests as $audit_request) { if ($audit_request->getAuditorPHID() == $user->getPHID()) { $user_request = $audit_request; break; } }
$actions = array(); $actions[PhabricatorAuditActionConstants::COMMENT] = true; $actions[PhabricatorAuditActionConstants::ADD_CCS] = true; $actions[PhabricatorAuditActionConstants::ADD_AUDITORS] = true;
// We allow you to accept your own commits. A use case here is that you // notice an issue with your own commit and "Raise Concern" as an indicator // to other auditors that you're on top of the issue, then later resolve it // and "Accept". You can not accept on behalf of projects or packages, // however. $actions[PhabricatorAuditActionConstants::ACCEPT] = true; $actions[PhabricatorAuditActionConstants::CONCERN] = true;
// To resign, a user must have authority on some request and not be the // commit's author. if (!$user_is_author) { $may_resign = false;
$authority_map = array_fill_keys($this->auditAuthorityPHIDs, true); foreach ($audit_requests as $request) { if (empty($authority_map[$request->getAuditorPHID()])) { continue; } $may_resign = true; break; }
// If the user has already resigned, don't show "Resign...". $status_resigned = PhabricatorAuditStatusConstants::RESIGNED; if ($user_request) { if ($user_request->getAuditStatus() == $status_resigned) { $may_resign = false; } }
if ($may_resign) { $actions[PhabricatorAuditActionConstants::RESIGN] = true; } }
$status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED; $concern_raised = ($commit->getAuditStatus() == $status_concern); $can_close_option = PhabricatorEnv::getEnvConfig( 'audit.can-author-close-audit'); if ($can_close_option && $user_is_author && $concern_raised) { $actions[PhabricatorAuditActionConstants::CLOSE] = true; }
foreach ($actions as $constant => $ignored) { $actions[$constant] = PhabricatorAuditActionConstants::getActionName($constant); }
return $actions; }
private function buildMergesTable(PhabricatorRepositoryCommit $commit) { $drequest = $this->getDiffusionRequest(); $limit = 50;
$merges = array(); try { $merges = $this->callConduitWithDiffusionRequest( 'diffusion.mergedcommitsquery', array( 'commit' => $drequest->getCommit(), 'limit' => $limit + 1)); } catch (ConduitException $ex) { if ($ex->getMessage() != 'ERR-UNSUPPORTED-VCS') { throw $ex; } }
if (!$merges) { return null; }
$caption = null; if (count($merges) > $limit) { $merges = array_slice($merges, 0, $limit); $caption = "This commit merges more than {$limit} changes. Only the first ". "{$limit} are shown."; }
$history_table = new DiffusionHistoryTableView(); $history_table->setUser($this->getRequest()->getUser()); $history_table->setDiffusionRequest($drequest); $history_table->setHistory($merges); $history_table->loadRevisions();
$phids = $history_table->getRequiredHandlePHIDs(); $handles = $this->loadViewerHandles($phids); $history_table->setHandles($handles);
$panel = new AphrontPanelView(); $panel->setHeader(pht('Merged Changes')); $panel->setCaption($caption); $panel->appendChild($history_table); $panel->setNoBackground();
return $panel; }
private function renderHeadsupActionList( PhabricatorRepositoryCommit $commit, PhabricatorRepository $repository) {
$request = $this->getRequest(); $user = $request->getUser();
$actions = id(new PhabricatorActionListView()) ->setUser($user) ->setObject($commit) ->setObjectURI($request->getRequestURI());
$can_edit = PhabricatorPolicyFilter::hasCapability( $user, $commit, PhabricatorPolicyCapability::CAN_EDIT);
$uri = '/diffusion/'.$repository->getCallsign().'/commit/'. $commit->getCommitIdentifier().'/edit/';
$action = id(new PhabricatorActionView()) ->setName(pht('Edit Commit')) ->setHref($uri) ->setIcon('edit') ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit); $actions->addAction($action);
require_celerity_resource('phabricator-object-selector-css'); require_celerity_resource('javelin-behavior-phabricator-object-selector');
$maniphest = 'PhabricatorApplicationManiphest'; if (PhabricatorApplication::isClassInstalled($maniphest)) { $action = id(new PhabricatorActionView()) ->setName(pht('Edit Maniphest Tasks')) ->setIcon('attach') ->setHref('/search/attach/'.$commit->getPHID().'/TASK/edge/') ->setWorkflow(true) ->setDisabled(!$can_edit); $actions->addAction($action); }
$action = id(new PhabricatorActionView()) ->setName(pht('Download Raw Diff')) ->setHref($request->getRequestURI()->alter('diff', true)) ->setIcon('download'); $actions->addAction($action);
return $actions; }
private function buildRefs(DiffusionRequest $request) { // this is git-only, so save a conduit round trip and just get out of // here if the repository isn't git $type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT; $repository = $request->getRepository(); if ($repository->getVersionControlSystem() != $type_git) { return null; }
$results = $this->callConduitWithDiffusionRequest( 'diffusion.refsquery', array('commit' => $request->getCommit())); $ref_links = array(); foreach ($results as $ref_data) { $ref_links[] = phutil_tag('a', array('href' => $ref_data['href']), $ref_data['ref']); } return phutil_implode_html(', ', $ref_links); }
private function buildRawDiffResponse(DiffusionRequest $drequest) { $raw_diff = $this->callConduitWithDiffusionRequest( 'diffusion.rawdiffquery', array( 'commit' => $drequest->getCommit(), 'path' => $drequest->getPath()));
$file = PhabricatorFile::buildFromFileDataOrHash( $raw_diff, array( 'name' => $drequest->getCommit().'.diff', 'ttl' => (60 * 60 * 24), 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, ));
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $file->attachToObject( $this->getRequest()->getUser(), $drequest->getRepository()->getPHID()); unset($unguarded);
return id(new AphrontRedirectResponse())->setURI($file->getBestURI()); }
private function renderAuditStatusView(array $audit_requests) { assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
$phids = mpull($audit_requests, 'getAuditorPHID'); $this->loadHandles($phids);
$authority_map = array_fill_keys($this->auditAuthorityPHIDs, true);
$view = new PHUIStatusListView(); foreach ($audit_requests as $request) { $item = new PHUIStatusItemView();
switch ($request->getAuditStatus()) { case PhabricatorAuditStatusConstants::AUDIT_NOT_REQUIRED: $item->setIcon('open-blue', pht('Commented')); break; case PhabricatorAuditStatusConstants::AUDIT_REQUIRED: $item->setIcon('warning-blue', pht('Audit Required')); break; case PhabricatorAuditStatusConstants::CONCERNED: $item->setIcon('reject-red', pht('Concern Raised')); break; case PhabricatorAuditStatusConstants::ACCEPTED: $item->setIcon('accept-green', pht('Accepted')); break; case PhabricatorAuditStatusConstants::AUDIT_REQUESTED: $item->setIcon('warning-dark', pht('Audit Requested')); break; case PhabricatorAuditStatusConstants::RESIGNED: $item->setIcon('open-dark', pht('Resigned')); break; case PhabricatorAuditStatusConstants::CLOSED: $item->setIcon('accept-blue', pht('Closed')); break; case PhabricatorAuditStatusConstants::CC: $item->setIcon('info-dark', pht('Subscribed')); break; default: $item->setIcon( 'question-dark', pht('%s?', $request->getAuditStatus())); break; }
$note = array(); foreach ($request->getAuditReasons() as $reason) { $note[] = phutil_tag('div', array(), $reason); } $item->setNote($note);
$auditor_phid = $request->getAuditorPHID(); $target = $this->getHandle($auditor_phid)->renderLink(); $item->setTarget($target);
if (isset($authority_map[$auditor_phid])) { $item->setHighlighted(true); }
$view->addItem($item); }
return $view; }
private function linkBugtraq($corpus) { $url = PhabricatorEnv::getEnvConfig('bugtraq.url'); if (!strlen($url)) { return $corpus; }
$regexes = PhabricatorEnv::getEnvConfig('bugtraq.logregex'); if (!$regexes) { return $corpus; }
$parser = id(new PhutilBugtraqParser()) ->setBugtraqPattern("[[ {$url} | %BUGID% ]]") ->setBugtraqCaptureExpression(array_shift($regexes));
$select = array_shift($regexes); if ($select) { $parser->setBugtraqSelectExpression($select); }
return $parser->processCorpus($corpus); }
}