Path: blob/master/src/applications/diffusion/controller/DiffusionBrowseController.php
12242 views
<?php12final class DiffusionBrowseController extends DiffusionController {34private $lintCommit;5private $lintMessages;6private $corpusButtons = array();78public function shouldAllowPublic() {9return true;10}1112public function handleRequest(AphrontRequest $request) {13$response = $this->loadDiffusionContext();14if ($response) {15return $response;16}1718$drequest = $this->getDiffusionRequest();1920// Figure out if we're browsing a directory, a file, or a search result21// list.2223$grep = $request->getStr('grep');24if (phutil_nonempty_string($grep)) {25return $this->browseSearch();26}2728$pager = id(new PHUIPagerView())29->readFromRequest($request);3031$results = DiffusionBrowseResultSet::newFromConduit(32$this->callConduitWithDiffusionRequest(33'diffusion.browsequery',34array(35'path' => $drequest->getPath(),36'commit' => $drequest->getStableCommit(),37'offset' => $pager->getOffset(),38'limit' => $pager->getPageSize() + 1,39)));4041$reason = $results->getReasonForEmptyResultSet();42$is_file = ($reason == DiffusionBrowseResultSet::REASON_IS_FILE);4344if ($is_file) {45return $this->browseFile();46}4748$paths = $results->getPaths();49$paths = $pager->sliceResults($paths);50$results->setPaths($paths);5152return $this->browseDirectory($results, $pager);53}5455private function browseSearch() {56$drequest = $this->getDiffusionRequest();57$header = $this->buildHeaderView($drequest);58$path = nonempty(basename($drequest->getPath()), '/');5960$search_results = $this->renderSearchResults();61$search_form = $this->renderSearchForm($path);6263$search_form = phutil_tag(64'div',65array(66'class' => 'diffusion-mobile-search-form',67),68$search_form);6970$crumbs = $this->buildCrumbs(71array(72'branch' => true,73'path' => true,74'view' => 'browse',75));76$crumbs->setBorder(true);7778$tabs = $this->buildTabsView('code');7980$view = id(new PHUITwoColumnView())81->setHeader($header)82->setTabs($tabs)83->setFooter(84array(85$search_form,86$search_results,87));8889return $this->newPage()90->setTitle(91array(92nonempty(basename($drequest->getPath()), '/'),93$drequest->getRepository()->getDisplayName(),94))95->setCrumbs($crumbs)96->appendChild($view);97}9899private function browseFile() {100$viewer = $this->getViewer();101$request = $this->getRequest();102$drequest = $this->getDiffusionRequest();103$repository = $drequest->getRepository();104105$before = $request->getStr('before');106if ($before) {107return $this->buildBeforeResponse($before);108}109110$path = $drequest->getPath();111$params = array(112'commit' => $drequest->getCommit(),113'path' => $drequest->getPath(),114);115116$view = $request->getStr('view');117118$byte_limit = null;119if ($view !== 'raw') {120$byte_limit = PhabricatorFileStorageEngine::getChunkThreshold();121$time_limit = 10;122123$params += array(124'timeout' => $time_limit,125'byteLimit' => $byte_limit,126);127}128129$response = $this->callConduitWithDiffusionRequest(130'diffusion.filecontentquery',131$params);132133$hit_byte_limit = $response['tooHuge'];134$hit_time_limit = $response['tooSlow'];135136$file_phid = $response['filePHID'];137$show_editor = false;138if ($hit_byte_limit) {139$corpus = $this->buildErrorCorpus(140pht(141'This file is larger than %s byte(s), and too large to display '.142'in the web UI.',143phutil_format_bytes($byte_limit)));144} else if ($hit_time_limit) {145$corpus = $this->buildErrorCorpus(146pht(147'This file took too long to load from the repository (more than '.148'%s second(s)).',149new PhutilNumber($time_limit)));150} else {151$file = id(new PhabricatorFileQuery())152->setViewer($viewer)153->withPHIDs(array($file_phid))154->executeOne();155if (!$file) {156throw new Exception(pht('Failed to load content file!'));157}158159if ($view === 'raw') {160return $file->getRedirectResponse();161}162163$data = $file->loadFileData();164165$lfs_ref = $this->getGitLFSRef($repository, $data);166if ($lfs_ref) {167if ($view == 'git-lfs') {168$file = $this->loadGitLFSFile($lfs_ref);169170// Rename the file locally so we generate a better vanity URI for171// it. In storage, it just has a name like "lfs-13f9a94c0923...",172// since we don't get any hints about possible human-readable names173// at upload time.174$basename = basename($drequest->getPath());175$file->makeEphemeral();176$file->setName($basename);177178return $file->getRedirectResponse();179}180181$corpus = $this->buildGitLFSCorpus($lfs_ref);182} else {183$show_editor = true;184185$ref = id(new PhabricatorDocumentRef())186->setFile($file);187188$engine = id(new DiffusionDocumentRenderingEngine())189->setRequest($request)190->setDiffusionRequest($drequest);191192$corpus = $engine->newDocumentView($ref);193194$this->corpusButtons[] = $this->renderFileButton();195}196}197198$bar = $this->buildButtonBar($drequest, $show_editor);199$header = $this->buildHeaderView($drequest);200$header->setHeaderIcon('fa-file-code-o');201202$follow = $request->getStr('follow');203$follow_notice = null;204if ($follow) {205$follow_notice = id(new PHUIInfoView())206->setSeverity(PHUIInfoView::SEVERITY_WARNING)207->setTitle(pht('Unable to Continue'));208switch ($follow) {209case 'first':210$follow_notice->appendChild(211pht(212'Unable to continue tracing the history of this file because '.213'this commit is the first commit in the repository.'));214break;215case 'created':216$follow_notice->appendChild(217pht(218'Unable to continue tracing the history of this file because '.219'this commit created the file.'));220break;221}222}223224$renamed = $request->getStr('renamed');225$renamed_notice = null;226if ($renamed) {227$renamed_notice = id(new PHUIInfoView())228->setSeverity(PHUIInfoView::SEVERITY_NOTICE)229->setTitle(pht('File Renamed'))230->appendChild(231pht(232'File history passes through a rename from "%s" to "%s".',233$drequest->getPath(),234$renamed));235}236237$open_revisions = $this->buildOpenRevisions();238$owners_list = $this->buildOwnersList($drequest);239240$crumbs = $this->buildCrumbs(241array(242'branch' => true,243'path' => true,244'view' => 'browse',245));246$crumbs->setBorder(true);247248$basename = basename($this->getDiffusionRequest()->getPath());249$tabs = $this->buildTabsView('code');250$bar->setRight($this->corpusButtons);251252$view = id(new PHUITwoColumnView())253->setHeader($header)254->setTabs($tabs)255->setFooter(array(256$bar,257$follow_notice,258$renamed_notice,259$corpus,260$open_revisions,261$owners_list,262));263264$title = array($basename, $repository->getDisplayName());265266return $this->newPage()267->setTitle($title)268->setCrumbs($crumbs)269->appendChild(270array(271$view,272));273274}275276public function browseDirectory(277DiffusionBrowseResultSet $results,278PHUIPagerView $pager) {279280$request = $this->getRequest();281$drequest = $this->getDiffusionRequest();282$repository = $drequest->getRepository();283284$reason = $results->getReasonForEmptyResultSet();285286$this->buildActionButtons($drequest, true);287$details = $this->buildPropertyView($drequest);288289$header = $this->buildHeaderView($drequest);290$header->setHeaderIcon('fa-folder-open');291292$title = '/';293if ($drequest->getPath() !== null) {294$title = nonempty(basename($drequest->getPath()), '/');295}296297$empty_result = null;298$browse_panel = null;299if (!$results->isValidResults()) {300$empty_result = new DiffusionEmptyResultView();301$empty_result->setDiffusionRequest($drequest);302$empty_result->setDiffusionBrowseResultSet($results);303$empty_result->setView($request->getStr('view'));304} else {305$browse_table = id(new DiffusionBrowseTableView())306->setDiffusionRequest($drequest)307->setPaths($results->getPaths())308->setUser($request->getUser());309310$icon = 'fa-folder-open';311$browse_header = $this->buildPanelHeaderView($title, $icon);312313$browse_panel = id(new PHUIObjectBoxView())314->setHeader($browse_header)315->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)316->setTable($browse_table)317->addClass('diffusion-mobile-view')318->setPager($pager);319}320321$open_revisions = $this->buildOpenRevisions();322$readme = $this->renderDirectoryReadme($results);323324$crumbs = $this->buildCrumbs(325array(326'branch' => true,327'path' => true,328'view' => 'browse',329));330331$crumbs->setBorder(true);332$tabs = $this->buildTabsView('code');333$owners_list = $this->buildOwnersList($drequest);334$bar = id(new PHUILeftRightView())335->setRight($this->corpusButtons)336->addClass('diffusion-action-bar');337338$view = id(new PHUITwoColumnView())339->setHeader($header)340->setTabs($tabs)341->setFooter(342array(343$bar,344$empty_result,345$browse_panel,346$open_revisions,347$owners_list,348$readme,349));350351if ($details) {352$view->addPropertySection(pht('Details'), $details);353}354355return $this->newPage()356->setTitle(array(357$title,358$repository->getDisplayName(),359))360->setCrumbs($crumbs)361->appendChild(362array(363$view,364));365}366367private function renderSearchResults() {368$request = $this->getRequest();369370$drequest = $this->getDiffusionRequest();371$repository = $drequest->getRepository();372$results = array();373374$pager = id(new PHUIPagerView())375->readFromRequest($request);376377$search_mode = null;378switch ($repository->getVersionControlSystem()) {379case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:380$results = array();381break;382default:383if (strlen($this->getRequest()->getStr('grep'))) {384$search_mode = 'grep';385$query_string = $request->getStr('grep');386$results = $this->callConduitWithDiffusionRequest(387'diffusion.searchquery',388array(389'grep' => $query_string,390'commit' => $drequest->getStableCommit(),391'path' => $drequest->getPath(),392'limit' => $pager->getPageSize() + 1,393'offset' => $pager->getOffset(),394));395}396break;397}398$results = $pager->sliceResults($results);399400$table = null;401$header = null;402if ($search_mode == 'grep') {403$table = $this->renderGrepResults($results, $query_string);404$title = pht(405'File content matching "%s" under "%s"',406$query_string,407nonempty($drequest->getPath(), '/'));408$header = id(new PHUIHeaderView())409->setHeader($title)410->addClass('diffusion-search-result-header');411}412413return array($header, $table, $pager);414415}416417private function renderGrepResults(array $results, $pattern) {418$drequest = $this->getDiffusionRequest();419require_celerity_resource('phabricator-search-results-css');420421if (!$results) {422return id(new PHUIInfoView())423->setSeverity(PHUIInfoView::SEVERITY_NODATA)424->appendChild(425pht(426'The pattern you searched for was not found in the content of any '.427'files.'));428}429430$grouped = array();431foreach ($results as $file) {432list($path, $line, $string) = $file;433$grouped[$path][] = array($line, $string);434}435436$view = array();437foreach ($grouped as $path => $matches) {438$view[] = id(new DiffusionPatternSearchView())439->setPath($path)440->setMatches($matches)441->setPattern($pattern)442->setDiffusionRequest($drequest)443->render();444}445446return $view;447}448449private function buildButtonBar(450DiffusionRequest $drequest,451$show_editor) {452453$viewer = $this->getViewer();454$base_uri = $this->getRequest()->getRequestURI();455456$repository = $drequest->getRepository();457$path = $drequest->getPath();458$line = nonempty((int)$drequest->getLine(), 1);459$buttons = array();460461$editor_uri = null;462$editor_template = null;463464$link_engine = PhabricatorEditorURIEngine::newForViewer($viewer);465if ($link_engine) {466$link_engine->setRepository($repository);467468$editor_uri = $link_engine->getURIForPath($path, $line);469$editor_template = $link_engine->getURITokensForPath($path);470}471472$buttons[] =473id(new PHUIButtonView())474->setTag('a')475->setText(pht('Last Change'))476->setColor(PHUIButtonView::GREY)477->setHref(478$drequest->generateURI(479array(480'action' => 'change',481)))482->setIcon('fa-backward');483484if ($editor_uri) {485$buttons[] =486id(new PHUIButtonView())487->setTag('a')488->setText(pht('Open File'))489->setHref($editor_uri)490->setIcon('fa-pencil')491->setID('editor_link')492->setMetadata(array('template' => $editor_template))493->setDisabled(!$editor_uri)494->setColor(PHUIButtonView::GREY);495}496497$bar = id(new PHUILeftRightView())498->setLeft($buttons)499->addClass('diffusion-action-bar full-mobile-buttons');500return $bar;501}502503private function buildOwnersList(DiffusionRequest $drequest) {504$viewer = $this->getViewer();505506$have_owners = PhabricatorApplication::isClassInstalledForViewer(507'PhabricatorOwnersApplication',508$viewer);509if (!$have_owners) {510return null;511}512513$repository = $drequest->getRepository();514515$package_query = id(new PhabricatorOwnersPackageQuery())516->setViewer($viewer)517->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))518->withControl(519$repository->getPHID(),520array(521$drequest->getPath(),522));523524$package_query->execute();525526$packages = $package_query->getControllingPackagesForPath(527$repository->getPHID(),528$drequest->getPath());529530$ownership = id(new PHUIObjectItemListView())531->setUser($viewer)532->setNoDataString(pht('No Owners'));533534if ($packages) {535foreach ($packages as $package) {536$item = id(new PHUIObjectItemView())537->setObject($package)538->setObjectName($package->getMonogram())539->setHeader($package->getName())540->setHref($package->getURI());541542$owners = $package->getOwners();543if ($owners) {544$owner_list = $viewer->renderHandleList(545mpull($owners, 'getUserPHID'));546} else {547$owner_list = phutil_tag('em', array(), pht('None'));548}549$item->addAttribute(pht('Owners: %s', $owner_list));550551$auto = $package->getAutoReview();552$autoreview_map = PhabricatorOwnersPackage::getAutoreviewOptionsMap();553$spec = idx($autoreview_map, $auto, array());554$name = idx($spec, 'name', $auto);555$item->addIcon('fa-code', $name);556557$rule = $package->newAuditingRule();558$item->addIcon($rule->getIconIcon(), $rule->getDisplayName());559560if ($package->isArchived()) {561$item->setDisabled(true);562}563564$ownership->addItem($item);565}566}567568$view = id(new PHUIObjectBoxView())569->setHeaderText(pht('Owner Packages'))570->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)571->addClass('diffusion-mobile-view')572->setObjectList($ownership);573574return $view;575}576577private function renderFileButton($file_uri = null, $label = null) {578579$base_uri = $this->getRequest()->getRequestURI();580581if ($file_uri) {582$text = pht('Download File');583$href = $file_uri;584$icon = 'fa-download';585} else {586$text = pht('Raw File');587$href = $base_uri->alter('view', 'raw');588$icon = 'fa-file-text';589}590591if ($label !== null) {592$text = $label;593}594595$button = id(new PHUIButtonView())596->setTag('a')597->setText($text)598->setHref($href)599->setIcon($icon)600->setColor(PHUIButtonView::GREY);601602return $button;603}604605private function renderGitLFSButton() {606$viewer = $this->getViewer();607608$uri = $this->getRequest()->getRequestURI();609$href = $uri->alter('view', 'git-lfs');610611$text = pht('Download from Git LFS');612$icon = 'fa-download';613614return id(new PHUIButtonView())615->setTag('a')616->setText($text)617->setHref($href)618->setIcon($icon)619->setColor(PHUIButtonView::GREY);620}621622private function buildErrorCorpus($message) {623$text = id(new PHUIBoxView())624->addPadding(PHUI::PADDING_LARGE)625->appendChild($message);626627$header = id(new PHUIHeaderView())628->setHeader(pht('Details'));629630$box = id(new PHUIObjectBoxView())631->setHeader($header)632->appendChild($text);633634return $box;635}636637private function buildBeforeResponse($before) {638$request = $this->getRequest();639$drequest = $this->getDiffusionRequest();640641// NOTE: We need to get the grandparent so we can capture filename changes642// in the parent.643644$parent = $this->loadParentCommitOf($before);645$old_filename = null;646$was_created = false;647if ($parent) {648$grandparent = $this->loadParentCommitOf($parent);649650if ($grandparent) {651$rename_query = new DiffusionRenameHistoryQuery();652$rename_query->setRequest($drequest);653$rename_query->setOldCommit($grandparent);654$rename_query->setViewer($request->getUser());655$old_filename = $rename_query->loadOldFilename();656$was_created = $rename_query->getWasCreated();657}658}659660$follow = null;661if ($was_created) {662// If the file was created in history, that means older commits won't663// have it. Since we know it existed at 'before', it must have been664// created then; jump there.665$target_commit = $before;666$follow = 'created';667} else if ($parent) {668// If we found a parent, jump to it. This is the normal case.669$target_commit = $parent;670} else {671// If there's no parent, this was probably created in the initial commit?672// And the "was_created" check will fail because we can't identify the673// grandparent. Keep the user at 'before'.674$target_commit = $before;675$follow = 'first';676}677678$path = $drequest->getPath();679$renamed = null;680if ($old_filename !== null &&681$old_filename !== '/'.$path) {682$renamed = $path;683$path = $old_filename;684}685686$line = null;687// If there's a follow error, drop the line so the user sees the message.688if (!$follow) {689$line = $this->getBeforeLineNumber($target_commit);690}691692$before_uri = $drequest->generateURI(693array(694'action' => 'browse',695'commit' => $target_commit,696'line' => $line,697'path' => $path,698));699700if ($renamed === null) {701$before_uri->removeQueryParam('renamed');702} else {703$before_uri->replaceQueryParam('renamed', $renamed);704}705706if ($follow === null) {707$before_uri->removeQueryParam('follow');708} else {709$before_uri->replaceQueryParam('follow', $follow);710}711712return id(new AphrontRedirectResponse())->setURI($before_uri);713}714715private function getBeforeLineNumber($target_commit) {716$drequest = $this->getDiffusionRequest();717$viewer = $this->getViewer();718719$line = $drequest->getLine();720if (!$line) {721return null;722}723724$diff_info = $this->callConduitWithDiffusionRequest(725'diffusion.rawdiffquery',726array(727'commit' => $drequest->getCommit(),728'path' => $drequest->getPath(),729'againstCommit' => $target_commit,730));731732$file_phid = $diff_info['filePHID'];733$file = id(new PhabricatorFileQuery())734->setViewer($viewer)735->withPHIDs(array($file_phid))736->executeOne();737if (!$file) {738throw new Exception(739pht(740'Failed to load file ("%s") returned by "%s".',741$file_phid,742'diffusion.rawdiffquery.'));743}744745$raw_diff = $file->loadFileData();746747$old_line = 0;748$new_line = 0;749750foreach (explode("\n", $raw_diff) as $text) {751if ($text[0] == '-' || $text[0] == ' ') {752$old_line++;753}754if ($text[0] == '+' || $text[0] == ' ') {755$new_line++;756}757if ($new_line == $line) {758return $old_line;759}760}761762// We didn't find the target line.763return $line;764}765766private function loadParentCommitOf($commit) {767$drequest = $this->getDiffusionRequest();768$user = $this->getRequest()->getUser();769770$before_req = DiffusionRequest::newFromDictionary(771array(772'user' => $user,773'repository' => $drequest->getRepository(),774'commit' => $commit,775));776777$parents = DiffusionQuery::callConduitWithDiffusionRequest(778$user,779$before_req,780'diffusion.commitparentsquery',781array(782'commit' => $commit,783));784785return head($parents);786}787788protected function markupText($text) {789$engine = PhabricatorMarkupEngine::newDiffusionMarkupEngine();790$engine->setConfig('viewer', $this->getRequest()->getUser());791$text = $engine->markupText($text);792793$text = phutil_tag(794'div',795array(796'class' => 'phabricator-remarkup',797),798$text);799800return $text;801}802803protected function buildHeaderView(DiffusionRequest $drequest) {804$viewer = $this->getViewer();805$repository = $drequest->getRepository();806807$commit_tag = $this->renderCommitHashTag($drequest);808809$path = nonempty($drequest->getPath(), '/');810811$search = $this->renderSearchForm($path);812813$header = id(new PHUIHeaderView())814->setUser($viewer)815->setHeader($this->renderPathLinks($drequest, $mode = 'browse'))816->addActionItem($search)817->addTag($commit_tag)818->addClass('diffusion-browse-header');819820if (!$repository->isSVN()) {821$branch_tag = $this->renderBranchTag($drequest);822$header->addTag($branch_tag);823}824825return $header;826}827828protected function buildPanelHeaderView($title, $icon) {829830$header = id(new PHUIHeaderView())831->setHeader($title)832->setHeaderIcon($icon)833->addClass('diffusion-panel-header-view');834835return $header;836837}838839protected function buildActionButtons(840DiffusionRequest $drequest,841$is_directory = false) {842843$viewer = $this->getViewer();844$repository = $drequest->getRepository();845$history_uri = $drequest->generateURI(array('action' => 'history'));846$behind_head = $drequest->getSymbolicCommit();847$compare = null;848$head_uri = $drequest->generateURI(849array(850'commit' => '',851'action' => 'browse',852));853854if ($repository->supportsBranchComparison() && $is_directory) {855$compare_uri = $drequest->generateURI(array('action' => 'compare'));856$compare = id(new PHUIButtonView())857->setText(pht('Compare'))858->setIcon('fa-code-fork')859->setWorkflow(true)860->setTag('a')861->setHref($compare_uri)862->setColor(PHUIButtonView::GREY);863$this->corpusButtons[] = $compare;864}865866$head = null;867if ($behind_head) {868$head = id(new PHUIButtonView())869->setTag('a')870->setText(pht('Back to HEAD'))871->setHref($head_uri)872->setIcon('fa-home')873->setColor(PHUIButtonView::GREY);874$this->corpusButtons[] = $head;875}876877$history = id(new PHUIButtonView())878->setText(pht('History'))879->setHref($history_uri)880->setTag('a')881->setIcon('fa-history')882->setColor(PHUIButtonView::GREY);883$this->corpusButtons[] = $history;884885}886887protected function buildPropertyView(888DiffusionRequest $drequest) {889890$viewer = $this->getViewer();891$view = id(new PHUIPropertyListView())892->setUser($viewer);893894if ($drequest->getSymbolicType() == 'tag') {895$symbolic = $drequest->getSymbolicCommit();896$view->addProperty(pht('Tag'), $symbolic);897898$tags = $this->callConduitWithDiffusionRequest(899'diffusion.tagsquery',900array(901'names' => array($symbolic),902'needMessages' => true,903));904$tags = DiffusionRepositoryTag::newFromConduit($tags);905906$tags = mpull($tags, null, 'getName');907$tag = idx($tags, $symbolic);908909if ($tag && strlen($tag->getMessage())) {910$view->addSectionHeader(911pht('Tag Content'), 'fa-tag');912$view->addTextContent($this->markupText($tag->getMessage()));913}914}915916if ($view->hasAnyProperties()) {917return $view;918}919920return null;921}922923private function buildOpenRevisions() {924$viewer = $this->getViewer();925926$drequest = $this->getDiffusionRequest();927$repository = $drequest->getRepository();928$path = $drequest->getPath();929930$recent = (PhabricatorTime::getNow() - phutil_units('30 days in seconds'));931932$revisions = id(new DifferentialRevisionQuery())933->setViewer($viewer)934->withPaths(array($path))935->withRepositoryPHIDs(array($repository->getPHID()))936->withIsOpen(true)937->withUpdatedEpochBetween($recent, null)938->setOrder(DifferentialRevisionQuery::ORDER_MODIFIED)939->setLimit(10)940->needReviewers(true)941->needFlags(true)942->needDrafts(true)943->execute();944945if (!$revisions) {946return null;947}948949$header = id(new PHUIHeaderView())950->setHeader(pht('Recent Open Revisions'));951952$list = id(new DifferentialRevisionListView())953->setViewer($viewer)954->setRevisions($revisions)955->setNoBox(true);956957$view = id(new PHUIObjectBoxView())958->setHeader($header)959->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)960->addClass('diffusion-mobile-view')961->appendChild($list);962963return $view;964}965966private function getGitLFSRef(PhabricatorRepository $repository, $data) {967if (!$repository->canUseGitLFS()) {968return null;969}970971$lfs_pattern = '(^version https://git-lfs\\.github\\.com/spec/v1[\r\n])';972if (!preg_match($lfs_pattern, $data)) {973return null;974}975976$matches = null;977if (!preg_match('(^oid sha256:(.*)$)m', $data, $matches)) {978return null;979}980981$hash = $matches[1];982$hash = trim($hash);983984return id(new PhabricatorRepositoryGitLFSRefQuery())985->setViewer($this->getViewer())986->withRepositoryPHIDs(array($repository->getPHID()))987->withObjectHashes(array($hash))988->executeOne();989}990991private function buildGitLFSCorpus(PhabricatorRepositoryGitLFSRef $ref) {992// TODO: We should probably test if we can load the file PHID here and993// show the user an error if we can't, rather than making them click994// through to hit an error.995996$title = basename($this->getDiffusionRequest()->getPath());997$icon = 'fa-archive';998$drequest = $this->getDiffusionRequest();999$this->buildActionButtons($drequest);1000$header = $this->buildPanelHeaderView($title, $icon);10011002$severity = PHUIInfoView::SEVERITY_NOTICE;10031004$messages = array();1005$messages[] = pht(1006'This %s file is stored in Git Large File Storage.',1007phutil_format_bytes($ref->getByteSize()));10081009try {1010$file = $this->loadGitLFSFile($ref);1011$this->corpusButtons[] = $this->renderGitLFSButton();1012} catch (Exception $ex) {1013$severity = PHUIInfoView::SEVERITY_ERROR;1014$messages[] = pht('The data for this file could not be loaded.');1015}10161017$this->corpusButtons[] = $this->renderFileButton(1018null, pht('View Raw LFS Pointer'));10191020$corpus = id(new PHUIObjectBoxView())1021->setHeader($header)1022->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)1023->addClass('diffusion-mobile-view')1024->setCollapsed(true);10251026if ($messages) {1027$corpus->setInfoView(1028id(new PHUIInfoView())1029->setSeverity($severity)1030->setErrors($messages));1031}10321033return $corpus;1034}10351036private function loadGitLFSFile(PhabricatorRepositoryGitLFSRef $ref) {1037$viewer = $this->getViewer();10381039$file = id(new PhabricatorFileQuery())1040->setViewer($viewer)1041->withPHIDs(array($ref->getFilePHID()))1042->executeOne();1043if (!$file) {1044throw new Exception(1045pht(1046'Failed to load file object for Git LFS ref "%s"!',1047$ref->getObjectHash()));1048}10491050return $file;1051}10521053}105410551056