private function checkIfRepositoryIsFullyImported(PhabricatorRepository $repository) { // Check if the repository has the "Importing" flag set. We want to clear // the flag if we can. $importing = $repository->getDetail('importing'); if (!$importing) { // This repository isn't marked as "Importing", so we're done. return; } // Look for any commit which hasn't imported. $unparsed_commit = queryfx_one($repository->establishConnection('r'), 'SELECT * FROM %T WHERE repositoryID = %d AND (importStatus & %d) != %d LIMIT 1', id(new PhabricatorRepositoryCommit())->getTableName(), $repository->getID(), PhabricatorRepositoryCommit::IMPORTED_ALL, PhabricatorRepositoryCommit::IMPORTED_ALL); if ($unparsed_commit) { // We found a commit which still needs to import, so we can't clear the // flag. return; } // Clear the "importing" flag. $repository->openTransaction(); $repository->beginReadLocking(); $repository = $repository->reload(); $repository->setDetail('importing', false); $repository->save(); $repository->endReadLocking(); $repository->saveTransaction(); }
public function handleProtocols(PhabricatorRepository $repository) { $request = $this->getRequest(); $user = $request->getUser(); $type = $repository->getVersionControlSystem(); $is_svn = $type == PhabricatorRepositoryType::REPOSITORY_TYPE_SVN; $v_http_mode = $repository->getDetail('serve-over-http', PhabricatorRepository::SERVE_OFF); $v_ssh_mode = $repository->getDetail('serve-over-ssh', PhabricatorRepository::SERVE_OFF); $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); $prev_uri = $this->getRepositoryControllerURI($repository, 'edit/hosting/'); if ($request->isFormPost()) { $v_http_mode = $request->getStr('http'); $v_ssh_mode = $request->getStr('ssh'); $xactions = array(); $template = id(new PhabricatorRepositoryTransaction()); $type_http = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP; $type_ssh = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH; if (!$is_svn) { $xactions[] = id(clone $template)->setTransactionType($type_http)->setNewValue($v_http_mode); } $xactions[] = id(clone $template)->setTransactionType($type_ssh)->setNewValue($v_ssh_mode); id(new PhabricatorRepositoryEditor())->setContinueOnNoEffect(true)->setContentSourceFromRequest($request)->setActor($user)->applyTransactions($repository, $xactions); return id(new AphrontRedirectResponse())->setURI($edit_uri); } $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Protocols')); $title = pht('Edit Protocols (%s)', $repository->getName()); $rw_message = pht('Phabricator will serve a read-write copy of this repository.'); if (!$repository->isHosted()) { $rw_message = array($rw_message, phutil_tag('br'), phutil_tag('br'), pht('%s: This repository is hosted elsewhere, so Phabricator can not ' . 'perform writes. This mode will act like "Read Only" for ' . 'repositories hosted elsewhere.', phutil_tag('strong', array(), 'WARNING'))); } $ssh_control = id(new AphrontFormRadioButtonControl())->setName('ssh')->setLabel(pht('SSH'))->setValue($v_ssh_mode)->addButton(PhabricatorRepository::SERVE_OFF, PhabricatorRepository::getProtocolAvailabilityName(PhabricatorRepository::SERVE_OFF), pht('Phabricator will not serve this repository over SSH.'))->addButton(PhabricatorRepository::SERVE_READONLY, PhabricatorRepository::getProtocolAvailabilityName(PhabricatorRepository::SERVE_READONLY), pht('Phabricator will serve a read-only copy of this repository ' . 'over SSH.'))->addButton(PhabricatorRepository::SERVE_READWRITE, PhabricatorRepository::getProtocolAvailabilityName(PhabricatorRepository::SERVE_READWRITE), $rw_message); $http_control = id(new AphrontFormRadioButtonControl())->setName('http')->setLabel(pht('HTTP'))->setValue($v_http_mode)->addButton(PhabricatorRepository::SERVE_OFF, PhabricatorRepository::getProtocolAvailabilityName(PhabricatorRepository::SERVE_OFF), pht('Phabricator will not serve this repository over HTTP.'))->addButton(PhabricatorRepository::SERVE_READONLY, PhabricatorRepository::getProtocolAvailabilityName(PhabricatorRepository::SERVE_READONLY), pht('Phabricator will serve a read-only copy of this repository ' . 'over HTTP.'))->addButton(PhabricatorRepository::SERVE_READWRITE, PhabricatorRepository::getProtocolAvailabilityName(PhabricatorRepository::SERVE_READWRITE), $rw_message); if ($is_svn) { $http_control = id(new AphrontFormMarkupControl())->setLabel(pht('HTTP'))->setValue(phutil_tag('em', array(), pht('Phabricator does not currently support HTTP access to ' . 'Subversion repositories.'))); } $form = id(new AphrontFormView())->setUser($user)->appendRemarkupInstructions(pht('Phabricator can serve repositories over various protocols. You can ' . 'configure server protocols here.'))->appendChild($ssh_control); if (!PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth')) { $form->appendRemarkupInstructions(pht('NOTE: The configuration setting [[ %s | %s ]] is currently ' . 'disabled. You must enable it to activate authenticated access ' . 'to repositories over HTTP.', '/config/edit/diffusion.allow-http-auth/', 'diffusion.allow-http-auth')); } $form->appendChild($http_control)->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Save Changes'))->addCancelButton($prev_uri, pht('Back'))); $object_box = id(new PHUIObjectBoxView())->setHeaderText($title)->setForm($form); return $this->buildApplicationPage(array($crumbs, $object_box), array('title' => $title)); }
public function parseCommit(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit) { $uri = $repository->getDetail('remote-uri'); $log = $this->getSVNLogXMLObject($uri, $commit->getCommitIdentifier(), $verbose = false); $entry = $log->logentry[0]; $author = (string) $entry->author; $message = (string) $entry->msg; $this->updateCommitData($author, $message); if ($this->shouldQueueFollowupTasks()) { $task = new PhabricatorWorkerTask(); $task->setTaskClass('PhabricatorRepositorySvnCommitChangeParserWorker'); $task->setData(array('commitID' => $commit->getID())); $task->save(); } }
public function parseCommit(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit) { $local_path = $repository->getDetail('local-path'); // NOTE: %B was introduced somewhat recently in git's history, so pull // commit message information with %s and %b instead. list($info) = execx('(cd %s && git log -n 1 --pretty=format:%%an%%x00%%s%%n%%n%%b %s)', $local_path, $commit->getCommitIdentifier()); list($author, $message) = explode("", $info); // Make sure these are valid UTF-8. $author = phutil_utf8ize($author); $message = phutil_utf8ize($message); $message = trim($message); $this->updateCommitData($author, $message); if ($this->shouldQueueFollowupTasks()) { $task = new PhabricatorWorkerTask(); $task->setTaskClass('PhabricatorRepositoryGitCommitChangeParserWorker'); $task->setData(array('commitID' => $commit->getID())); $task->save(); } }
private function triggerOwnerAudits(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit) { if ($repository->getDetail('herald-disabled')) { return; } $affected_paths = PhabricatorOwnerPathQuery::loadAffectedPaths($repository, $commit, PhabricatorUser::getOmnipotentUser()); $affected_packages = PhabricatorOwnersPackage::loadAffectedPackages($repository, $affected_paths); if ($affected_packages) { $requests = id(new PhabricatorRepositoryAuditRequest())->loadAllWhere('commitPHID = %s', $commit->getPHID()); $requests = mpull($requests, null, 'getAuditorPHID'); foreach ($affected_packages as $package) { $request = idx($requests, $package->getPHID()); if ($request) { // Don't update request if it exists already. continue; } if ($package->getAuditingEnabled()) { $reasons = $this->checkAuditReasons($commit, $package); if ($reasons) { $audit_status = PhabricatorAuditStatusConstants::AUDIT_REQUIRED; } else { $audit_status = PhabricatorAuditStatusConstants::AUDIT_NOT_REQUIRED; } } else { $reasons = array(); $audit_status = PhabricatorAuditStatusConstants::NONE; } $relationship = new PhabricatorRepositoryAuditRequest(); $relationship->setAuditorPHID($package->getPHID()); $relationship->setCommitPHID($commit->getPHID()); $relationship->setAuditReasons($reasons); $relationship->setAuditStatus($audit_status); $relationship->save(); $requests[$package->getPHID()] = $relationship; } $commit->updateAuditStatus($requests); $commit->save(); } }
public function parseCommit(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit) { if ($repository->getDetail('herald-disabled')) { return; } $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere('commitID = %d', $commit->getID()); $rules = HeraldRule::loadAllByContentTypeWithFullData(HeraldContentTypeConfig::CONTENT_TYPE_COMMIT); $adapter = new HeraldCommitAdapter($repository, $commit, $data); $engine = new HeraldEngine(); $effects = $engine->applyRules($rules, $adapter); $engine->applyEffects($effects, $adapter); $email_phids = $adapter->getEmailPHIDs(); if (!$email_phids) { return; } $xscript = $engine->getTranscript(); $commit_name = $adapter->getHeraldName(); $revision = $adapter->loadDifferentialRevision(); $name = null; if ($revision) { $name = ' ' . $revision->getTitle(); } $author_phid = $data->getCommitDetail('authorPHID'); $reviewer_phid = $data->getCommitDetail('reviewerPHID'); $phids = array_filter(array($author_phid, $reviewer_phid)); $handles = array(); if ($phids) { $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles(); } if ($author_phid) { $author_name = $handles[$author_phid]->getName(); } else { $author_name = $data->getAuthorName(); } if ($reviewer_phid) { $reviewer_name = $handles[$reviewer_phid]->getName(); } else { $reviewer_name = null; } $who = implode(', ', array_filter(array($author_name, $reviewer_name))); $description = $data->getCommitMessage(); $details = PhabricatorEnv::getProductionURI('/' . $commit_name); $differential = $revision ? PhabricatorEnv::getProductionURI('/D' . $revision->getID()) : 'No revision.'; $files = $adapter->loadAffectedPaths(); sort($files); $files = implode("\n ", $files); $xscript_id = $xscript->getID(); $manage_uri = PhabricatorEnv::getProductionURI('/herald/view/commits/'); $why_uri = PhabricatorEnv::getProductionURI('/herald/transcript/' . $xscript_id . '/'); $body = <<<EOBODY DESCRIPTION {$description} DETAILS {$details} DIFFERENTIAL REVISION {$differential} AFFECTED FILES {$files} MANAGE HERALD COMMIT RULES {$manage_uri} WHY DID I GET THIS EMAIL? {$why_uri} EOBODY; $subject = "[Herald/Commit] {$commit_name} ({$who}){$name}"; $mailer = new PhabricatorMetaMTAMail(); $mailer->setRelatedPHID($commit->getPHID()); $mailer->addTos($email_phids); $mailer->setSubject($subject); $mailer->setBody($body); $mailer->addHeader('X-Herald-Rules', $xscript->getXHeraldRulesHeader()); if ($author_phid) { $mailer->setFrom($author_phid); } $mailer->saveAndSend(); }
private function lookupSvnCommits(PhabricatorRepository $repository, array $commits) { if (!$commits) { return array(); } $commit_table = new PhabricatorRepositoryCommit(); $commit_data = queryfx_all($commit_table->establishConnection('w'), 'SELECT id, commitIdentifier FROM %T WHERE repositoryID = %d AND commitIdentifier in (%Ls)', $commit_table->getTableName(), $repository->getID(), $commits); $commit_map = ipull($commit_data, 'id', 'commitIdentifier'); $need = array(); foreach ($commits as $commit) { if (empty($commit_map[$commit])) { $need[] = $commit; } } // If we are parsing a Subversion repository and have been configured to // import only some subdirectory of it, we may find commits which reference // other foreign commits outside of the directory (for instance, because of // a move or copy). Rather than trying to execute full parses on them, just // create stub commits and identify the stubs as foreign commits. if ($need) { $subpath = $repository->getDetail('svn-subpath'); if (!$subpath) { $commits = implode(', ', $need); throw new Exception("Missing commits ({$need}) in a SVN repository which is not " . "configured for subdirectory-only parsing!"); } foreach ($need as $foreign_commit) { $commit = new PhabricatorRepositoryCommit(); $commit->setRepositoryID($repository->getID()); $commit->setCommitIdentifier($foreign_commit); $commit->setEpoch(0); // Mark this commit as imported so it doesn't prevent the repository // from transitioning into the "Imported" state. $commit->setImportStatus(PhabricatorRepositoryCommit::IMPORTED_ALL); $commit->save(); $data = new PhabricatorRepositoryCommitData(); $data->setCommitID($commit->getID()); $data->setAuthorName(''); $data->setCommitMessage(''); $data->setCommitDetails(array('foreign-svn-stub' => true, 'svn-subpath' => $subpath)); $data->save(); $commit_map[$foreign_commit] = $commit->getID(); } } return $commit_map; }
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(phutil_escape_html($key), phutil_escape_html($value)); } $table = new AphrontTableView($rows); $table->setColumnClasses(array('header', 'wide')); $panel = new AphrontPanelView(); $panel->setHeader('Repository Properties'); $panel->appendChild($table); return $panel; }
private function buildDescriptionView(PhabricatorRepository $repository) { $viewer = $this->getViewer(); $view = id(new PHUIPropertyListView())->setUser($viewer); $description = $repository->getDetail('description'); if (strlen($description)) { $description = new PHUIRemarkupView($viewer, $description); $view->addTextContent($description); return id(new PHUIObjectBoxView())->setHeaderText(pht('Description'))->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)->appendChild($view); } return null; }
private function lookupRecursiveFileList(PhabricatorRepository $repository, array $info) { $path = $info['rawPath']; $rev = $info['rawCommit']; $path = $this->encodeSVNPath($path); $hashkey = md5($repository->getDetail('remote-uri') . $path . '@' . $rev); // This method is quite horrible. The underlying challenge is that some // commits in the Facebook repository are enormous, taking multiple hours // to 'ls -R' out of the repository and producing XML files >1GB in size. // If we try to SimpleXML them, the object exhausts available memory on a // 64G machine. Instead, cache the XML output and then parse it line by line // to limit space requirements. $cache_loc = sys_get_temp_dir() . '/diffusion.' . $hashkey . '.svnls'; if (!Filesystem::pathExists($cache_loc)) { $tmp = new TempFile(); execx('svn --non-interactive --xml ls -R %s%s@%d > %s', $repository->getDetail('remote-uri'), $path, $rev, $tmp); execx('mv %s %s', $tmp, $cache_loc); } $map = $this->parseRecursiveListFileData($cache_loc); Filesystem::remove($cache_loc); return $map; }
protected function parseCommit(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit) { $full_name = 'r' . $repository->getCallsign() . $commit->getCommitIdentifier(); echo "Parsing {$full_name}...\n"; if ($this->isBadCommit($full_name)) { echo "This commit is marked bad!\n"; return; } $local_path = $repository->getDetail('local-path'); list($raw) = execx('(cd %s && git log -n1 -M -C -B --find-copies-harder --raw -t ' . '--abbrev=40 --pretty=format: %s)', $local_path, $commit->getCommitIdentifier()); $changes = array(); $move_away = array(); $copy_away = array(); $lines = explode("\n", $raw); foreach ($lines as $line) { if (!strlen(trim($line))) { continue; } list($old_mode, $new_mode, $old_hash, $new_hash, $more_stuff) = preg_split('/ +/', $line); // We may only have two pieces here. list($action, $src_path, $dst_path) = array_merge(explode("\t", $more_stuff), array(null)); // Normalize the paths for consistency with the SVN workflow. $src_path = '/' . $src_path; if ($dst_path) { $dst_path = '/' . $dst_path; } $old_mode = intval($old_mode, 8); $new_mode = intval($new_mode, 8); $file_type = DifferentialChangeType::FILE_NORMAL; if ($new_mode & 040000) { $file_type = DifferentialChangeType::FILE_DIRECTORY; } else { if ($new_mode & 0120000) { $file_type = DifferentialChangeType::FILE_SYMLINK; } } // TODO: We can detect binary changes as git does, through a combination // of running 'git check-attr' for stuff like 'binary', 'merge' or 'diff', // and by falling back to inspecting the first 8,000 characters of the // buffer for null bytes (this is seriously git's algorithm, see // buffer_is_binary() in xdiff-interface.c). $change_type = null; $change_path = $src_path; $change_target = null; $is_direct = true; switch ($action[0]) { case 'A': $change_type = DifferentialChangeType::TYPE_ADD; break; case 'D': $change_type = DifferentialChangeType::TYPE_DELETE; break; case 'C': $change_type = DifferentialChangeType::TYPE_COPY_HERE; $change_path = $dst_path; $change_target = $src_path; $copy_away[$change_target][] = $change_path; break; case 'R': $change_type = DifferentialChangeType::TYPE_MOVE_HERE; $change_path = $dst_path; $change_target = $src_path; $move_away[$change_target][] = $change_path; break; case 'T': // Type of the file changed, fall through and treat it as a // modification. Not 100% sure this is the right thing to do but it // seems reasonable. // Type of the file changed, fall through and treat it as a // modification. Not 100% sure this is the right thing to do but it // seems reasonable. case 'M': if ($file_type == DifferentialChangeType::FILE_DIRECTORY) { $change_type = DifferentialChangeType::TYPE_CHILD; $is_direct = false; } else { $change_type = DifferentialChangeType::TYPE_CHANGE; } break; // NOTE: "U" (unmerged) and "X" (unknown) statuses are also possible // in theory but shouldn't appear here. // NOTE: "U" (unmerged) and "X" (unknown) statuses are also possible // in theory but shouldn't appear here. default: throw new Exception("Failed to parse line '{$line}'."); } $changes[$change_path] = array('repositoryID' => $repository->getID(), 'commitID' => $commit->getID(), 'path' => $change_path, 'changeType' => $change_type, 'fileType' => $file_type, 'isDirect' => $is_direct, 'commitSequence' => $commit->getEpoch(), 'targetPath' => $change_target, 'targetCommitID' => $change_target ? $commit->getID() : null); } // Add a change to '/' since git doesn't mention it. $changes['/'] = array('repositoryID' => $repository->getID(), 'commitID' => $commit->getID(), 'path' => '/', 'changeType' => DifferentialChangeType::TYPE_CHILD, 'fileType' => DifferentialChangeType::FILE_DIRECTORY, 'isDirect' => false, 'commitSequence' => $commit->getEpoch(), 'targetPath' => null, 'targetCommitID' => null); foreach ($copy_away as $change_path => $destinations) { if (isset($move_away[$change_path])) { $change_type = DifferentialChangeType::TYPE_MULTICOPY; $is_direct = true; unset($move_away[$change_path]); } else { $change_type = DifferentialChangeType::TYPE_COPY_AWAY; $is_direct = false; } $reference = $changes[reset($destinations)]; $changes[$change_path] = array('repositoryID' => $repository->getID(), 'commitID' => $commit->getID(), 'path' => $change_path, 'changeType' => $change_type, 'fileType' => $reference['fileType'], 'isDirect' => $is_direct, 'commitSequence' => $commit->getEpoch(), 'targetPath' => null, 'targetCommitID' => null); } foreach ($move_away as $change_path => $destinations) { $reference = $changes[reset($destinations)]; $changes[$change_path] = array('repositoryID' => $repository->getID(), 'commitID' => $commit->getID(), 'path' => $change_path, 'changeType' => DifferentialChangeType::TYPE_MOVE_AWAY, 'fileType' => $reference['fileType'], 'isDirect' => true, 'commitSequence' => $commit->getEpoch(), 'targetPath' => null, 'targetCommitID' => null); } $paths = array(); foreach ($changes as $change) { $paths[$change['path']] = true; if ($change['targetPath']) { $paths[$change['targetPath']] = true; } } $path_map = $this->lookupOrCreatePaths(array_keys($paths)); foreach ($changes as $key => $change) { $changes[$key]['pathID'] = $path_map[$change['path']]; if ($change['targetPath']) { $changes[$key]['targetPathID'] = $path_map[$change['targetPath']]; } else { $changes[$key]['targetPathID'] = null; } } $conn_w = $repository->establishConnection('w'); $changes_sql = array(); foreach ($changes as $change) { $values = array((int) $change['repositoryID'], (int) $change['pathID'], (int) $change['commitID'], $change['targetPathID'] ? (int) $change['targetPathID'] : 'null', $change['targetCommitID'] ? (int) $change['targetCommitID'] : 'null', (int) $change['changeType'], (int) $change['fileType'], (int) $change['isDirect'], (int) $change['commitSequence']); $changes_sql[] = '(' . implode(', ', $values) . ')'; } queryfx($conn_w, 'DELETE FROM %T WHERE commitID = %d', PhabricatorRepository::TABLE_PATHCHANGE, $commit->getID()); foreach (array_chunk($changes_sql, 256) as $sql_chunk) { queryfx($conn_w, 'INSERT INTO %T (repositoryID, pathID, commitID, targetPathID, targetCommitID, changeType, fileType, isDirect, commitSequence) VALUES %Q', PhabricatorRepository::TABLE_PATHCHANGE, implode(', ', $sql_chunk)); } $this->finishParse(); }
protected function buildDictForRepository(PhabricatorRepository $repository) { return array('name' => $repository->getName(), 'phid' => $repository->getPHID(), 'callsign' => $repository->getCallsign(), 'vcs' => $repository->getVersionControlSystem(), 'uri' => PhabricatorEnv::getProductionURI($repository->getURI()), 'remoteURI' => (string) $repository->getPublicRemoteURI(), 'tracking' => $repository->getDetail('tracking-enabled'), 'description' => $repository->getDetail('description')); }
private function executeSvnGetBaseSVNLogURI(PhabricatorRepository $repository) { $uri = $repository->getDetail('remote-uri'); $subpath = $repository->getDetail('svn-subpath'); return $uri . $subpath; }
/** * @task pull */ private function resolveUpdateFuture(PhabricatorRepository $repository, ExecFuture $future, $min_sleep) { $monogram = $repository->getMonogram(); $this->log(pht('Resolving update for "%s".', $monogram)); try { list($stdout, $stderr) = $future->resolvex(); } catch (Exception $ex) { $proxy = new PhutilProxyException(pht('Error while updating the "%s" repository.', $repository->getMonogram()), $ex); phlog($proxy); return time() + $min_sleep; } if (strlen($stderr)) { $stderr_msg = pht('Unexpected output while updating repository "%s": %s', $monogram, $stderr); phlog($stderr_msg); } // For now, continue respecting this deprecated setting for raising the // minimum pull frequency. // TODO: Remove this some day once this code has been completely stable // for a while. $sleep_for = (int) $repository->getDetail('pull-frequency'); $min_sleep = max($sleep_for, $min_sleep); $smart_wait = $repository->loadUpdateInterval($min_sleep); $this->log(pht('Based on activity in repository "%s", considering a wait of %s ' . 'seconds before update.', $repository->getMonogram(), new PhutilNumber($smart_wait))); return time() + $smart_wait; }
public function getAbsoluteRepositoryPath(PhabricatorRepository $repository, DifferentialDiff $diff = null) { $base = '/'; if ($diff && $diff->getSourceControlPath()) { $base = id(new PhutilURI($diff->getSourceControlPath()))->getPath(); } $path = $this->getFilename(); $path = rtrim($base, '/') . '/' . ltrim($path, '/'); $vcs = $repository->getVersionControlSystem(); if ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_SVN) { $prefix = $repository->getDetail('remote-uri'); $prefix = id(new PhutilURI($prefix))->getPath(); if (!strncmp($path, $prefix, strlen($prefix))) { $path = substr($path, strlen($prefix)); } $path = '/' . ltrim($path, '/'); } return $path; }
public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $shortcuts = id(new PhabricatorRepositoryShortcut())->loadAll(); if ($shortcuts) { $shortcuts = msort($shortcuts, 'getSequence'); $rows = array(); foreach ($shortcuts as $shortcut) { $rows[] = array(phutil_render_tag('a', array('href' => $shortcut->getHref()), phutil_escape_html($shortcut->getName())), phutil_escape_html($shortcut->getDescription())); } $shortcut_table = new AphrontTableView($rows); $shortcut_table->setHeaders(array('Link', '')); $shortcut_table->setColumnClasses(array('pri', 'wide')); $shortcut_panel = new AphrontPanelView(); $shortcut_panel->setHeader('Shortcuts'); $shortcut_panel->appendChild($shortcut_table); } else { $shortcut_panel = null; } $repository = new PhabricatorRepository(); $repositories = $repository->loadAll(); foreach ($repositories as $key => $repo) { if (!$repo->isTracked()) { unset($repositories[$key]); } } $repository_ids = mpull($repositories, 'getID'); $summaries = array(); $commits = array(); if ($repository_ids) { $summaries = queryfx_all($repository->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'); } } $rows = array(); foreach ($repositories as $repository) { $id = $repository->getID(); $commit = idx($commits, $id); $size = idx(idx($summaries, $id, array()), 'size', 0); $date = '-'; $time = '-'; if ($commit) { $date = phabricator_date($commit->getEpoch(), $user); $time = phabricator_time($commit->getEpoch(), $user); } $rows[] = array(phutil_render_tag('a', array('href' => '/diffusion/' . $repository->getCallsign() . '/'), phutil_escape_html($repository->getName())), phutil_escape_html($repository->getDetail('description')), PhabricatorRepositoryType::getNameForRepositoryType($repository->getVersionControlSystem()), $size ? number_format($size) : '-', $commit ? DiffusionView::linkCommit($repository, $commit->getCommitIdentifier()) : '-', $date, $time); } $repository_tool_uri = PhabricatorEnv::getProductionURI('/repository/'); $repository_tool = phutil_render_tag('a', array('href' => $repository_tool_uri), 'repository tool'); $no_repositories_txt = 'This instance of Phabricator does not have any ' . 'configured repositories. '; if ($user->getIsAdmin()) { $no_repositories_txt .= 'To setup one or more repositories, visit the ' . $repository_tool . '.'; } else { $no_repositories_txt .= 'Ask an administrator to setup one or more ' . 'repositories via the ' . $repository_tool . '.'; } $table = new AphrontTableView($rows); $table->setNoDataString($no_repositories_txt); $table->setHeaders(array('Repository', 'Description', 'VCS', 'Commits', 'Last', 'Date', 'Time')); $table->setColumnClasses(array('pri', 'wide', '', 'n', 'n', '', 'right')); $panel = new AphrontPanelView(); $panel->setHeader('Browse Repositories'); $panel->appendChild($table); $crumbs = $this->buildCrumbs(); return $this->buildStandardPageResponse(array($crumbs, $shortcut_panel, $panel), array('title' => 'Diffusion')); }
private function buildPatch(PhabricatorMetaMTAMail $template, PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit) { $attach_key = 'metamta.diffusion.attach-patches'; $inline_key = 'metamta.diffusion.inline-patches'; $attach_patches = PhabricatorEnv::getEnvConfig($attach_key); $inline_patches = PhabricatorEnv::getEnvConfig($inline_key); if (!$attach_patches && !$inline_patches) { return; } $encoding = $repository->getDetail('encoding', 'UTF-8'); $result = null; $patch_error = null; try { $raw_patch = $this->loadRawPatchText($repository, $commit); if ($attach_patches) { $commit_name = $repository->formatCommitName($commit->getCommitIdentifier()); $template->addAttachment(new PhabricatorMetaMTAAttachment($raw_patch, $commit_name . '.patch', 'text/x-patch; charset=' . $encoding)); } } catch (Exception $ex) { phlog($ex); $patch_error = 'Unable to generate: ' . $ex->getMessage(); } if ($patch_error) { $result = $patch_error; } else { if ($inline_patches) { $len = substr_count($raw_patch, "\n"); if ($len <= $inline_patches) { // We send email as utf8, so we need to convert the text to utf8 if // we can. if ($encoding) { $raw_patch = phutil_utf8_convert($raw_patch, 'UTF-8', $encoding); } $result = phutil_utf8ize($raw_patch); } } } if ($result) { $result = "PATCH\n\n{$result}\n"; } return $result; }
private function buildActionsProperties(PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView())->setUser($viewer)->setActionList($actions); $notify = $repository->getDetail('herald-disabled') ? pht('Off') : pht('On'); $notify = phutil_tag('em', array(), $notify); $view->addProperty(pht('Publish/Notify'), $notify); $autoclose = $repository->getDetail('disable-autoclose') ? pht('Off') : pht('On'); $autoclose = phutil_tag('em', array(), $autoclose); $view->addProperty(pht('Autoclose'), $autoclose); return $view; }
public function parseCommit(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit) { $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere('commitID = %d', $commit->getID()); if (!$data) { // TODO: Permanent failure. return; } $rules = HeraldRule::loadAllByContentTypeWithFullData(HeraldContentTypeConfig::CONTENT_TYPE_COMMIT, $commit->getPHID()); $adapter = new HeraldCommitAdapter($repository, $commit, $data); $engine = new HeraldEngine(); $effects = $engine->applyRules($rules, $adapter); $engine->applyEffects($effects, $adapter, $rules); $audit_phids = $adapter->getAuditMap(); if ($audit_phids) { $this->createAudits($commit, $audit_phids, $rules); } $this->createAuditsFromCommitMessage($commit, $data); $email_phids = $adapter->getEmailPHIDs(); if (!$email_phids) { return; } if ($repository->getDetail('herald-disabled')) { // This just means "disable email"; audits are (mostly) idempotent. return; } $xscript = $engine->getTranscript(); $revision = $adapter->loadDifferentialRevision(); if ($revision) { $name = $revision->getTitle(); } else { $name = $data->getSummary(); } $author_phid = $data->getCommitDetail('authorPHID'); $reviewer_phid = $data->getCommitDetail('reviewerPHID'); $phids = array_filter(array($author_phid, $reviewer_phid, $commit->getPHID())); $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles(); $commit_handle = $handles[$commit->getPHID()]; $commit_name = $commit_handle->getName(); if ($author_phid) { $author_name = $handles[$author_phid]->getName(); } else { $author_name = $data->getAuthorName(); } if ($reviewer_phid) { $reviewer_name = $handles[$reviewer_phid]->getName(); } else { $reviewer_name = null; } $who = implode(', ', array_filter(array($author_name, $reviewer_name))); $description = $data->getCommitMessage(); $commit_uri = PhabricatorEnv::getProductionURI($commit_handle->getURI()); $differential = $revision ? PhabricatorEnv::getProductionURI('/D' . $revision->getID()) : 'No revision.'; $files = $adapter->loadAffectedPaths(); sort($files); $files = implode("\n ", $files); $xscript_id = $xscript->getID(); $manage_uri = PhabricatorEnv::getProductionURI('/herald/view/commits/'); $why_uri = PhabricatorEnv::getProductionURI('/herald/transcript/' . $xscript_id . '/'); $reply_handler = PhabricatorAuditCommentEditor::newReplyHandlerForCommit($commit); $reply_instructions = $reply_handler->getReplyHandlerInstructions(); if ($reply_instructions) { $reply_instructions = "\n" . "REPLY HANDLER ACTIONS\n" . " " . $reply_instructions . "\n"; } $body = <<<EOBODY DESCRIPTION {$description} DETAILS {$commit_uri} DIFFERENTIAL REVISION {$differential} AFFECTED FILES {$files} {$reply_instructions} MANAGE HERALD COMMIT RULES {$manage_uri} WHY DID I GET THIS EMAIL? {$why_uri} EOBODY; $prefix = PhabricatorEnv::getEnvConfig('metamta.diffusion.subject-prefix'); $subject = trim("{$prefix} {$commit_name}: {$name}"); $vary_subject = trim("{$prefix} [Commit] {$commit_name}: {$name}"); $threading = PhabricatorAuditCommentEditor::getMailThreading($commit->getPHID()); list($thread_id, $thread_topic) = $threading; $template = new PhabricatorMetaMTAMail(); $template->setRelatedPHID($commit->getPHID()); $template->setSubject($subject); $template->setVarySubject($subject); $template->setBody($body); $template->setThreadID($thread_id, $is_new = true); $template->addHeader('Thread-Topic', $thread_topic); $template->setIsBulk(true); $template->addHeader('X-Herald-Rules', $xscript->getXHeraldRulesHeader()); if ($author_phid) { $template->setFrom($author_phid); } $mails = $reply_handler->multiplexMail($template, id(new PhabricatorObjectHandleData($email_phids))->loadHandles(), array()); foreach ($mails as $mail) { $mail->saveAndSend(); } }