diff --git a/src/applications/diffusion/controller/DiffusionHomeController.php b/src/applications/diffusion/controller/DiffusionHomeController.php index e954c72087..51ee245e90 100644 --- a/src/applications/diffusion/controller/DiffusionHomeController.php +++ b/src/applications/diffusion/controller/DiffusionHomeController.php @@ -1,206 +1,189 @@ getRequest(); $user = $request->getUser(); $shortcuts = id(new PhabricatorRepositoryShortcut())->loadAll(); if ($shortcuts) { $shortcuts = msort($shortcuts, 'getSequence'); $rows = array(); foreach ($shortcuts as $shortcut) { $rows[] = array( $shortcut->getName(), $shortcut->getHref(), $shortcut->getDescription(), ); } $list = new PHUIObjectItemListView(); $list->setCards(true); $list->setFlush(true); foreach ($rows as $row) { $item = id(new PHUIObjectItemView()) ->setHeader($row[0]) ->setHref($row[1]) ->setSubhead(($row[2] ? $row[2] : pht('No Description'))); $list->addItem($item); } $shortcut_panel = id(new AphrontPanelView()) ->setNoBackground(true) ->setHeader(pht('Shortcuts')) ->appendChild($list); } else { $shortcut_panel = null; } $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($user) + ->needCommitCounts(true) + ->needMostRecentCommits(true) ->execute(); foreach ($repositories as $key => $repo) { if (!$repo->isTracked()) { unset($repositories[$key]); } } $repositories = msort($repositories, 'getName'); - $repository_ids = mpull($repositories, 'getID'); - $summaries = array(); - $commits = array(); - if ($repository_ids) { - $summaries = queryfx_all( - id(new PhabricatorRepository())->establishConnection('r'), - 'SELECT * FROM %T WHERE repositoryID IN (%Ld)', - PhabricatorRepository::TABLE_SUMMARY, - $repository_ids); - $summaries = ipull($summaries, null, 'repositoryID'); - - $commit_ids = array_filter(ipull($summaries, 'lastCommitID')); - if ($commit_ids) { - $commit = new PhabricatorRepositoryCommit(); - $commits = $commit->loadAllWhere('id IN (%Ld)', $commit_ids); - $commits = mpull($commits, null, 'getRepositoryID'); - } - } - $branch = new PhabricatorRepositoryBranch(); $lint_messages = queryfx_all( $branch->establishConnection('r'), 'SELECT b.repositoryID, b.name, COUNT(lm.id) AS n FROM %T b LEFT JOIN %T lm ON b.id = lm.branchID GROUP BY b.id', $branch->getTableName(), PhabricatorRepository::TABLE_LINTMESSAGE); $lint_messages = igroup($lint_messages, 'repositoryID'); $rows = array(); $show_lint = false; foreach ($repositories as $repository) { $id = $repository->getID(); - $commit = idx($commits, $id); - $size = idx(idx($summaries, $id, array()), 'size', '-'); - if ($size != '-') { + $size = $repository->getCommitCount(); + if ($size) { $size = hsprintf( '%s', DiffusionRequest::generateDiffusionURI(array( 'callsign' => $repository->getCallsign(), 'action' => 'history', )), pht('%s Commits', new PhutilNumber($size))); } $lint_count = ''; $lint_branches = ipull(idx($lint_messages, $id, array()), 'n', 'name'); $branch = $repository->getDefaultArcanistBranch(); if (isset($lint_branches[$branch])) { $show_lint = true; $lint_count = phutil_tag( 'a', array( 'href' => DiffusionRequest::generateDiffusionURI(array( 'callsign' => $repository->getCallsign(), 'action' => 'lint', )), ), pht('%s Lint Messages', new PhutilNumber($lint_branches[$branch]))); } $datetime = ''; - if ($commit) { - $date = phabricator_date($commit->getEpoch(), $user); - $time = phabricator_time($commit->getEpoch(), $user); + $most_recent_commit = $repository->getMostRecentCommit(); + if ($most_recent_commit) { + $date = phabricator_date($most_recent_commit->getEpoch(), $user); + $time = phabricator_time($most_recent_commit->getEpoch(), $user); $datetime = $date.' '.$time; } $rows[] = array( $repository->getName(), ('/diffusion/'.$repository->getCallsign().'/'), PhabricatorRepositoryType::getNameForRepositoryType( $repository->getVersionControlSystem()), - $size, + $size ? $size : null, $lint_count, - $commit + $most_recent_commit ? DiffusionView::linkCommit( $repository, - $commit->getCommitIdentifier(), - $commit->getSummary()) + $most_recent_commit->getCommitIdentifier(), + $most_recent_commit->getSummary()) : pht('No Commits'), $datetime ); } $repository_tool_uri = PhabricatorEnv::getProductionURI('/repository/'); $repository_tool = phutil_tag('a', array( 'href' => $repository_tool_uri, ), 'repository tool'); $preface = pht('This instance of Phabricator does not have any '. 'configured repositories.'); if ($user->getIsAdmin()) { $no_repositories_txt = hsprintf( '%s %s', $preface, pht( 'To setup one or more repositories, visit the %s.', $repository_tool)); } else { $no_repositories_txt = hsprintf( '%s %s', $preface, pht( 'Ask an administrator to setup one or more repositories '. 'via the %s.', $repository_tool)); } $list = new PHUIObjectItemListView(); $list->setCards(true); $list->setFlush(true); foreach ($rows as $row) { $item = id(new PHUIObjectItemView()) ->setHeader($row[0]) ->setSubHead($row[5]) ->setHref($row[1]) ->addAttribute(($row[2] ? $row[2] : pht('No Information'))) ->addAttribute(($row[3] ? $row[3] : pht('0 Commits'))) ->addIcon('none', $row[6]); if ($show_lint) { $item->addAttribute($row[4]); } $list->addItem($item); } $list = id(new AphrontPanelView()) ->setNoBackground(true) ->setHeader(pht('Repositories')) ->appendChild($list); $crumbs = $this->buildCrumbs(); $crumbs->addCrumb( id(new PhabricatorCrumbView()) ->setName(pht('All Repositories')) ->setHref($this->getApplicationURI())); return $this->buildApplicationPage( array( $crumbs, $shortcut_panel, $list, ), array( 'title' => pht('Diffusion'), 'device' => true, )); } } diff --git a/src/applications/repository/query/PhabricatorRepositoryQuery.php b/src/applications/repository/query/PhabricatorRepositoryQuery.php index 58401e6316..8b5af5fc19 100644 --- a/src/applications/repository/query/PhabricatorRepositoryQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryQuery.php @@ -1,73 +1,133 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withCallsigns(array $callsigns) { $this->callsigns = $callsigns; return $this; } protected function getReversePaging() { return true; } + public function needCommitCounts($need_counts) { + $this->needCommitCounts = $need_counts; + return $this; + } + + public function needMostRecentCommits($need_commits) { + $this->needMostRecentCommits = $need_commits; + return $this; + } + protected function loadPage() { $table = new PhabricatorRepository(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, - 'SELECT * FROM %T %Q %Q %Q', + 'SELECT * FROM %T r %Q %Q %Q %Q', $table->getTableName(), + $this->buildJoinsClause($conn_r), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); - return $table->loadAllFromArray($data); + $repositories = $table->loadAllFromArray($data); + + if ($this->needCommitCounts) { + $sizes = ipull($data, 'size', 'id'); + foreach ($repositories as $id => $repository) { + $repository->attachCommitCount(nonempty($sizes[$id], 0)); + } + } + + if ($this->needMostRecentCommits) { + $commit_ids = ipull($data, 'lastCommitID', 'id'); + $commit_ids = array_filter($commit_ids); + if ($commit_ids) { + $commits = id(new DiffusionCommitQuery()) + ->setViewer($this->getViewer()) + ->withIDs($commit_ids) + ->execute(); + } else { + $commits = array(); + } + foreach ($repositories as $id => $repository) { + $commit = null; + if (idx($commit_ids, $id)) { + $commit = idx($commits, $commit_ids[$id]); + } + $repository->attachMostRecentCommit($commit); + } + } + + + return $repositories; + } + + private function buildJoinsClause(AphrontDatabaseConnection $conn_r) { + $joins = array(); + + $join_summary_table = $this->needCommitCounts || + $this->needMostRecentCommits; + + if ($join_summary_table) { + $joins[] = qsprintf( + $conn_r, + 'LEFT JOIN %T summary ON r.id = summary.repositoryID', + PhabricatorRepository::TABLE_SUMMARY); + } + + return implode(' ', $joins); } private function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, - 'id IN (%Ld)', + 'r.id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, - 'phid IN (%Ls)', + 'r.phid IN (%Ls)', $this->phids); } if ($this->callsigns) { $where[] = qsprintf( $conn_r, - 'callsign IN (%Ls)', + 'r.callsign IN (%Ls)', $this->callsigns); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } } diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index d8e80d4c0b..96acf334e6 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -1,782 +1,804 @@ true, self::CONFIG_SERIALIZATION => array( 'details' => self::SERIALIZATION_JSON, ), ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID( PhabricatorRepositoryPHIDTypeRepository::TYPECONST); } public function toDictionary() { return array( 'name' => $this->getName(), 'phid' => $this->getPHID(), 'callsign' => $this->getCallsign(), 'vcs' => $this->getVersionControlSystem(), 'uri' => PhabricatorEnv::getProductionURI($this->getURI()), 'remoteURI' => (string)$this->getPublicRemoteURI(), 'tracking' => $this->getDetail('tracking-enabled'), 'description' => $this->getDetail('description'), ); } public function getDetail($key, $default = null) { return idx($this->details, $key, $default); } public function setDetail($key, $value) { $this->details[$key] = $value; return $this; } + public function attachCommitCount($count) { + $this->commitCount = $count; + return $this; + } + + public function getCommitCount() { + return $this->assertAttached($this->commitCount); + } + + public function attachMostRecentCommit( + PhabricatorRepositoryCommit $commit = null) { + $this->mostRecentCommit = $commit; + return $this; + } + + public function getMostRecentCommit() { + return $this->assertAttached($this->mostRecentCommit); + } + public function getDiffusionBrowseURIForPath( PhabricatorUser $user, $path, $line = null, $branch = null) { $drequest = DiffusionRequest::newFromDictionary( array( 'user' => $user, 'repository' => $this, 'path' => $path, 'branch' => $branch, )); return $drequest->generateURI( array( 'action' => 'browse', 'line' => $line, )); } public function getLocalPath() { return $this->getDetail('local-path'); } public function getSubversionBaseURI() { $vcs = $this->getVersionControlSystem(); if ($vcs != PhabricatorRepositoryType::REPOSITORY_TYPE_SVN) { throw new Exception("Not a subversion repository!"); } $uri = $this->getDetail('remote-uri'); $subpath = $this->getDetail('svn-subpath'); return $uri.$subpath; } public function execRemoteCommand($pattern /* , $arg, ... */) { $args = func_get_args(); $args = $this->formatRemoteCommand($args); return call_user_func_array('exec_manual', $args); } public function execxRemoteCommand($pattern /* , $arg, ... */) { $args = func_get_args(); $args = $this->formatRemoteCommand($args); return call_user_func_array('execx', $args); } public function getRemoteCommandFuture($pattern /* , $arg, ... */) { $args = func_get_args(); $args = $this->formatRemoteCommand($args); return newv('ExecFuture', $args); } public function passthruRemoteCommand($pattern /* , $arg, ... */) { $args = func_get_args(); $args = $this->formatRemoteCommand($args); return call_user_func_array('phutil_passthru', $args); } public function execLocalCommand($pattern /* , $arg, ... */) { $args = func_get_args(); $args = $this->formatLocalCommand($args); return call_user_func_array('exec_manual', $args); } public function execxLocalCommand($pattern /* , $arg, ... */) { $args = func_get_args(); $args = $this->formatLocalCommand($args); return call_user_func_array('execx', $args); } public function getLocalCommandFuture($pattern /* , $arg, ... */) { $args = func_get_args(); $args = $this->formatLocalCommand($args); return newv('ExecFuture', $args); } public function passthruLocalCommand($pattern /* , $arg, ... */) { $args = func_get_args(); $args = $this->formatLocalCommand($args); return call_user_func_array('phutil_passthru', $args); } private function formatRemoteCommand(array $args) { $pattern = $args[0]; $args = array_slice($args, 1); $empty = $this->getEmptyReadableDirectoryPath(); if ($this->shouldUseSSH()) { switch ($this->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $pattern = "SVN_SSH=%s svn --non-interactive {$pattern}"; array_unshift( $args, csprintf( 'ssh -l %P -i %P', new PhutilOpaqueEnvelope($this->getSSHLogin()), new PhutilOpaqueEnvelope($this->getSSHKeyfile()))); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $command = call_user_func_array( 'csprintf', array_merge( array( "(ssh-add %P && HOME=%s git {$pattern})", new PhutilOpaqueEnvelope($this->getSSHKeyfile()), $empty, ), $args)); $pattern = "ssh-agent sh -c %s"; $args = array($command); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $pattern = "hg --config ui.ssh=%s {$pattern}"; array_unshift( $args, csprintf( 'ssh -l %P -i %P', new PhutilOpaqueEnvelope($this->getSSHLogin()), new PhutilOpaqueEnvelope($this->getSSHKeyfile()))); break; default: throw new Exception("Unrecognized version control system."); } } else if ($this->shouldUseHTTP()) { switch ($this->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $pattern = "svn ". "--non-interactive ". "--no-auth-cache ". "--trust-server-cert ". "--username %P ". "--password %P ". $pattern; array_unshift( $args, new PhutilOpaqueEnvelope($this->getDetail('http-login')), new PhutilOpaqueEnvelope($this->getDetail('http-pass'))); break; default: throw new Exception( "No support for HTTP Basic Auth in this version control system."); } } else if ($this->shouldUseSVNProtocol()) { switch ($this->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $pattern = "svn ". "--non-interactive ". "--no-auth-cache ". "--username %P ". "--password %P ". $pattern; array_unshift( $args, new PhutilOpaqueEnvelope($this->getDetail('http-login')), new PhutilOpaqueEnvelope($this->getDetail('http-pass'))); break; default: throw new Exception( "SVN protocol is SVN only."); } } else { switch ($this->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $pattern = "svn --non-interactive {$pattern}"; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $pattern = "HOME=%s git {$pattern}"; array_unshift($args, $empty); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $pattern = "hg {$pattern}"; break; default: throw new Exception("Unrecognized version control system."); } } array_unshift($args, $pattern); return $args; } private function formatLocalCommand(array $args) { $pattern = $args[0]; $args = array_slice($args, 1); $empty = $this->getEmptyReadableDirectoryPath(); switch ($this->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $pattern = "(cd %s && svn --non-interactive {$pattern})"; array_unshift($args, $this->getLocalPath()); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $pattern = "(cd %s && HOME=%s git {$pattern})"; array_unshift($args, $this->getLocalPath(), $empty); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $hgplain = (phutil_is_windows() ? "set HGPLAIN=1 &&" : "HGPLAIN=1"); $pattern = "(cd %s && {$hgplain} hg {$pattern})"; array_unshift($args, $this->getLocalPath()); break; default: throw new Exception("Unrecognized version control system."); } array_unshift($args, $pattern); return $args; } private function getEmptyReadableDirectoryPath() { // See T2965. Some time after Git 1.7.5.4, Git started fataling if it can // not read $HOME. For many users, $HOME points at /root (this seems to be // a default result of Apache setup). Instead, explicitly point $HOME at a // readable, empty directory so that Git looks for the config file it's // after, fails to locate it, and moves on. This is really silly, but seems // like the least damaging approach to mitigating the issue. $root = dirname(phutil_get_library_root('phabricator')); return $root.'/support/empty/'; } private function getSSHLogin() { return $this->getDetail('ssh-login'); } private function getSSHKeyfile() { if ($this->sshKeyfile === null) { $key = $this->getDetail('ssh-key'); $keyfile = $this->getDetail('ssh-keyfile'); if ($keyfile) { // Make sure we can read the file, that it exists, etc. Filesystem::readFile($keyfile); $this->sshKeyfile = $keyfile; } else if ($key) { $keyfile = new TempFile('phabricator-repository-ssh-key'); chmod($keyfile, 0600); Filesystem::writeFile($keyfile, $key); $this->sshKeyfile = $keyfile; } else { $this->sshKeyfile = ''; } } return (string)$this->sshKeyfile; } public function getURI() { return '/diffusion/'.$this->getCallsign().'/'; } public function isTracked() { return $this->getDetail('tracking-enabled', false); } public function getDefaultBranch() { $default = $this->getDetail('default-branch'); if (strlen($default)) { return $default; } $default_branches = array( PhabricatorRepositoryType::REPOSITORY_TYPE_GIT => 'master', PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL => 'default', ); return idx($default_branches, $this->getVersionControlSystem()); } public function getDefaultArcanistBranch() { return coalesce($this->getDefaultBranch(), 'svn'); } private function isBranchInFilter($branch, $filter_key) { $vcs = $this->getVersionControlSystem(); $is_git = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT); $use_filter = ($is_git); if ($use_filter) { $filter = $this->getDetail($filter_key, array()); if ($filter && empty($filter[$branch])) { return false; } } // By default, all branches pass. return true; } public function shouldTrackBranch($branch) { return $this->isBranchInFilter($branch, 'branch-filter'); } public function shouldAutocloseBranch($branch) { if ($this->getDetail('disable-autoclose', false)) { return false; } return $this->isBranchInFilter($branch, 'close-commits-filter'); } public function shouldAutocloseCommit( PhabricatorRepositoryCommit $commit, PhabricatorRepositoryCommitData $data) { if ($this->getDetail('disable-autoclose', false)) { return false; } switch ($this->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: return true; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: return true; default: throw new Exception("Unrecognized version control system."); } $branches = $data->getCommitDetail('seenOnBranches', array()); foreach ($branches as $branch) { if ($this->shouldAutocloseBranch($branch)) { return true; } } return false; } public function formatCommitName($commit_identifier) { $vcs = $this->getVersionControlSystem(); $type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT; $type_hg = PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL; $is_git = ($vcs == $type_git); $is_hg = ($vcs == $type_hg); if ($is_git || $is_hg) { $short_identifier = substr($commit_identifier, 0, 12); } else { $short_identifier = $commit_identifier; } return 'r'.$this->getCallsign().$short_identifier; } public static function loadAllByPHIDOrCallsign(array $names) { $repositories = array(); foreach ($names as $name) { $repo = id(new PhabricatorRepository())->loadOneWhere( 'phid = %s OR callsign = %s', $name, $name); if (!$repo) { throw new Exception( "No repository with PHID or callsign '{$name}' exists!"); } $repositories[$repo->getID()] = $repo; } return $repositories; } /* -( Repository URI Management )------------------------------------------ */ /** * Get the remote URI for this repository. * * @return string * @task uri */ public function getRemoteURI() { return (string)$this->getRemoteURIObject(); } /** * Get the remote URI for this repository, without authentication information. * * @return string Repository URI. * @task uri */ public function getPublicRemoteURI() { $uri = $this->getRemoteURIObject(); // Make sure we don't leak anything if this repo is using HTTP Basic Auth // with the credentials in the URI or something zany like that. if ($uri instanceof PhutilGitURI) { if (!$this->getDetail('show-user', false)) { $uri->setUser(null); } } else { if (!$this->getDetail('show-user', false)) { $uri->setUser(null); } $uri->setPass(null); } return (string)$uri; } /** * Get the protocol for the repository's remote. * * @return string Protocol, like "ssh" or "git". * @task uri */ public function getRemoteProtocol() { $uri = $this->getRemoteURIObject(); if ($uri instanceof PhutilGitURI) { return 'ssh'; } else { return $uri->getProtocol(); } } /** * Get a parsed object representation of the repository's remote URI. This * may be a normal URI (returned as a @{class@libphutil:PhutilURI}) or a git * URI (returned as a @{class@libphutil:PhutilGitURI}). * * @return wild A @{class@libphutil:PhutilURI} or * @{class@libphutil:PhutilGitURI}. * @task uri */ private function getRemoteURIObject() { $raw_uri = $this->getDetail('remote-uri'); if (!$raw_uri) { return new PhutilURI(''); } if (!strncmp($raw_uri, '/', 1)) { return new PhutilURI('file://'.$raw_uri); } $uri = new PhutilURI($raw_uri); if ($uri->getProtocol()) { if ($this->isSSHProtocol($uri->getProtocol())) { if ($this->getSSHLogin()) { $uri->setUser($this->getSSHLogin()); } } return $uri; } $uri = new PhutilGitURI($raw_uri); if ($uri->getDomain()) { if ($this->getSSHLogin()) { $uri->setUser($this->getSSHLogin()); } return $uri; } throw new Exception("Remote URI '{$raw_uri}' could not be parsed!"); } /** * Determine if we should connect to the remote using SSH flags and * credentials. * * @return bool True to use the SSH protocol. * @task uri */ private function shouldUseSSH() { $protocol = $this->getRemoteProtocol(); if ($this->isSSHProtocol($protocol)) { return (bool)$this->getSSHKeyfile(); } else { return false; } } /** * Determine if we should connect to the remote using HTTP flags and * credentials. * * @return bool True to use the HTTP protocol. * @task uri */ private function shouldUseHTTP() { $protocol = $this->getRemoteProtocol(); if ($protocol == 'http' || $protocol == 'https') { return (bool)$this->getDetail('http-login'); } else { return false; } } /** * Determine if we should connect to the remote using SVN flags and * credentials. * * @return bool True to use the SVN protocol. * @task uri */ private function shouldUseSVNProtocol() { $protocol = $this->getRemoteProtocol(); if ($protocol == 'svn') { return (bool)$this->getDetail('http-login'); } else { return false; } } /** * Determine if a protocol is SSH or SSH-like. * * @param string A protocol string, like "http" or "ssh". * @return bool True if the protocol is SSH-like. * @task uri */ private function isSSHProtocol($protocol) { return ($protocol == 'ssh' || $protocol == 'svn+ssh'); } public function delete() { $this->openTransaction(); $paths = id(new PhabricatorOwnersPath()) ->loadAllWhere('repositoryPHID = %s', $this->getPHID()); foreach ($paths as $path) { $path->delete(); } $projects = id(new PhabricatorRepositoryArcanistProject()) ->loadAllWhere('repositoryID = %d', $this->getID()); foreach ($projects as $project) { // note each project deletes its PhabricatorRepositorySymbols $project->delete(); } $commits = id(new PhabricatorRepositoryCommit()) ->loadAllWhere('repositoryID = %d', $this->getID()); foreach ($commits as $commit) { // note PhabricatorRepositoryAuditRequests and // PhabricatorRepositoryCommitData are deleted here too. $commit->delete(); } $conn_w = $this->establishConnection('w'); queryfx( $conn_w, 'DELETE FROM %T WHERE repositoryID = %d', self::TABLE_FILESYSTEM, $this->getID()); queryfx( $conn_w, 'DELETE FROM %T WHERE repositoryID = %d', self::TABLE_PATHCHANGE, $this->getID()); queryfx( $conn_w, 'DELETE FROM %T WHERE repositoryID = %d', self::TABLE_SUMMARY, $this->getID()); $result = parent::delete(); $this->saveTransaction(); return $result; } public function isGit() { $vcs = $this->getVersionControlSystem(); return ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT); } public function isSVN() { $vcs = $this->getVersionControlSystem(); return ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_SVN); } public function isHg() { $vcs = $this->getVersionControlSystem(); return ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL); } /** * Link external bug tracking system if defined. * * @param string Plain text. * @param string Commit identifier. * @return string Remarkup */ public function linkBugtraq($message, $revision = null) { $bugtraq_url = PhabricatorEnv::getEnvConfig('bugtraq.url'); list($bugtraq_re, $id_re) = PhabricatorEnv::getEnvConfig('bugtraq.logregex') + array('', ''); switch ($this->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: // TODO: Get bugtraq:logregex and bugtraq:url from SVN properties. break; } if (!$bugtraq_url || $bugtraq_re == '') { return $message; } $matches = null; $flags = PREG_SET_ORDER | PREG_OFFSET_CAPTURE; preg_match_all('('.$bugtraq_re.')', $message, $matches, $flags); foreach ($matches as $match) { list($all, $all_offset) = array_shift($match); if ($id_re != '') { // Match substrings with bug IDs preg_match_all('('.$id_re.')', $all, $match, PREG_OFFSET_CAPTURE); list(, $match) = $match; } else { $all_offset = 0; } $match = array_reverse($match); foreach ($match as $val) { list($s, $offset) = $val; $message = substr_replace( $message, '[[ '.str_replace('%BUGID%', $s, $bugtraq_url).' | '.$s.' ]]', $offset + $all_offset, strlen($s)); } } return $message; } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return PhabricatorPolicies::POLICY_USER; case PhabricatorPolicyCapability::CAN_EDIT: return PhabricatorPolicies::POLICY_ADMIN; } } public function hasAutomaticCapability($capability, PhabricatorUser $user) { return false; } /* -( PhabricatorMarkupInterface )----------------------------------------- */ public function getMarkupFieldKey($field) { $hash = PhabricatorHash::digestForIndex($this->getMarkupText($field)); return "repo:{$hash}"; } public function newMarkupEngine($field) { return PhabricatorMarkupEngine::newMarkupEngine(array()); } public function getMarkupText($field) { return $this->getDetail('description'); } public function didMarkupText( $field, $output, PhutilMarkupEngine $engine) { require_celerity_resource('phabricator-remarkup-css'); return phutil_tag( 'div', array( 'class' => 'phabricator-remarkup', ), $output); } public function shouldUseMarkupCache($field) { return true; } }