Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CodebergBridge] Add bridge #1951

Merged
merged 19 commits into from
Aug 13, 2021
Merged
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ab7343c
[CodebergBridge] Add bridge
VerifiedJoseph Jan 12, 2021
50a9c48
[CodebergBridge] Remove repo parameter from commits array
VerifiedJoseph Jan 22, 2021
5d214f4
[CodebergBridge] Fix formatting
VerifiedJoseph Jan 22, 2021
16944b8
[CodebergBridge] Add repo parameter title value
VerifiedJoseph Jan 22, 2021
edb2e9f
[CodebergBridge] Remove unused class property $feedName
VerifiedJoseph Jan 23, 2021
b82be07
[CodebergBridge] Add downloads to release & tag items
VerifiedJoseph Jan 23, 2021
4f28d76
[CodebergBridge] Format switch statements
VerifiedJoseph Jan 23, 2021
6930cb1
[CodebergBridge] Remove whitespace
VerifiedJoseph Jan 23, 2021
698a044
[CodebergBridge] Lower cache timeout to 1800 seconds (30 Minutes)
VerifiedJoseph Jan 25, 2021
49a8fca
[CodebergBridge] Fetch issue page in extractIssues()
VerifiedJoseph Feb 1, 2021
e9512b1
[CodebergBridge] Fetch pull request page in extractIssues()
VerifiedJoseph Feb 1, 2021
8b9f517
[CodebergBridge] Add Issue Comments option
VerifiedJoseph Feb 1, 2021
76b5ba6
[CodebergBridge] Add missing switch case
VerifiedJoseph Feb 1, 2021
dfd2c2c
[CodebergBridge] Add tag and commit to release items
VerifiedJoseph Feb 2, 2021
6b2461c
[CodebergBridge] Add global parameter 'username'
VerifiedJoseph Feb 2, 2021
6716f65
[CodebergBridge] Add method detectParameters()
VerifiedJoseph Feb 24, 2021
d7c2fa1
[CodebergBridge] Run phpcbf
VerifiedJoseph Feb 24, 2021
6641b9a
[OpenlyBridge] Add const TEST_DETECT_PARAMETERS
VerifiedJoseph Aug 10, 2021
f02d65d
[OpenlyBridge] Fix $urlRegex
VerifiedJoseph Aug 10, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
390 changes: 390 additions & 0 deletions bridges/CodebergBridge.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,390 @@
<?php
class CodebergBridge extends BridgeAbstract {
const NAME = 'Codeberg Bridge';
const URI = 'https://codeberg.org/';
const DESCRIPTION = 'Returns commits, issues, pull requests or releases for a repository.';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = array(
'Commits' => array(
'branch' => array(
'name' => 'branch',
'type' => 'text',
'exampleValue' => 'main',
'required' => false,
'title' => 'Optional, main branch is used by default.',
),
),
'Issues' => array(),
'Issue Comments' => array(
'issueId' => array(
'name' => 'Issue ID',
'type' => 'text',
'required' => true,
)
),
'Pull Requests' => array(),
'Releases' => array(),
'global' => array(
'username' => array(
'name' => 'Username',
'type' => 'text',
'exampleValue' => 'username',
'title' => 'Username of account that the repository belongs to.',
'required' => true,
),
'repo' => array(
'name' => 'Repository',
'type' => 'text',
'exampleValue' => 'repo',
'required' => true,
)
)
);

const CACHE_TIMEOUT = 1800;

const TEST_DETECT_PARAMETERS = array(
'https://codeberg.org/Codeberg/Community/issues/507' => array(
'context' => 'Issue Comments', 'username' => 'Codeberg', 'repo' => 'Community', 'issueId' => '507'
),
'https://codeberg.org/Codeberg/Community/issues' => array(
'context' => 'Issues', 'username' => 'Codeberg', 'repo' => 'Community'
),
'https://codeberg.org/Codeberg/Community/pulls' => array(
'context' => 'Pull Requests', 'username' => 'Codeberg', 'repo' => 'Community'
),
'https://codeberg.org/Codeberg/Community/releases' => array(
'context' => 'Releases', 'username' => 'Codeberg', 'repo' => 'Community'
),
'https://codeberg.org/Codeberg/Community/commits/branch/master' => array(
'context' => 'Commits', 'username' => 'Codeberg', 'repo' => 'Community', 'branch' => 'master'
),
'https://codeberg.org/Codeberg/Community/commits' => array(
'context' => 'Commits', 'username' => 'Codeberg', 'repo' => 'Community'
)
);

private $defaultBranch = 'main';
private $issueTitle = '';

private $urlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)(?:\/commits\/branch\/([\w]+))?/';
private $issuesUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/issues/';
private $pullsUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/pulls/';
private $releasesUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/releases/';
private $issueCommentsUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/issues\/([0-9]+)/';

public function detectParameters($url) {
$params = array();

// Issue Comments
if(preg_match($this->issueCommentsUrlRegex, $url, $matches)) {
$params['context'] = 'Issue Comments';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
$params['issueId'] = $matches[3];

return $params;
}

// Issues
if(preg_match($this->issuesUrlRegex, $url, $matches)) {
$params['context'] = 'Issues';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];

return $params;
}

// Pull Requests
if(preg_match($this->pullsUrlRegex, $url, $matches)) {
$params['context'] = 'Pull Requests';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];

return $params;
}

// Releases
if(preg_match($this->releasesUrlRegex, $url, $matches)) {
$params['context'] = 'Releases';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];

return $params;
}

// Commits
if(preg_match($this->urlRegex, $url, $matches)) {
$params['context'] = 'Commits';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];

if (isset($matches[3])) {
$params['branch'] = $matches[3];
}

return $params;
}

return null;
}

public function collectData() {
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not request: ' . $this->getURI());

$html = defaultLinkTo($html, $this->getURI());

switch($this->queriedContext) {
case 'Commits':
$this->extractCommits($html);
break;
case 'Issues':
$this->extractIssues($html);
break;
case 'Issue Comments':
$this->extractIssueComments($html);
break;
case 'Pull Requests':
$this->extractPulls($html);
break;
case 'Releases':
$this->extractReleases($html);
break;
default:
returnClientError('Invalid context: ' . $this->queriedContext);
}
}

public function getName() {
switch($this->queriedContext) {
case 'Commits':
if ($this->getBranch() === $this->defaultBranch) {
return $this->getRepo() . ' Commits';
}

return $this->getRepo() . ' Commits (' . $this->getBranch() . ' branch) - ' . self::NAME;
case 'Issues':
return $this->getRepo() . ' Issues - ' . self::NAME;
case 'Issue Comments':
return $this->issueTitle . ' - Issue Comments - ' . self::NAME;
case 'Pull Requests':
return $this->getRepo() . ' Pull Requests - ' . self::NAME;
case 'Releases':
return $this->getRepo() . ' Releases - ' . self::NAME;
default:
return parent::getName();
}
}

public function getURI() {
switch($this->queriedContext) {
case 'Commits':
return self::URI . $this->getRepo() . '/commits/branch/' . $this->getBranch();
case 'Issues':
return self::URI . $this->getRepo() . '/issues/';
case 'Issue Comments':
return self::URI . $this->getRepo() . '/issues/' . $this->getInput('issueId');
case 'Pull Requests':
return self::URI . $this->getRepo() . '/pulls';
case 'Releases':
return self::URI . $this->getRepo() . '/releases';
default:
return parent::getURI();
}
}

private function getBranch() {
if ($this->getInput('branch')) {
return $this->getInput('branch');
}

return $this->defaultBranch;
}

private function getRepo() {
return $this->getInput('username') . '/' . $this->getInput('repo');
}

private function extractCommits($html) {
$table = $html->find('table#commits-table', 0);
$tbody = $table->find('tbody.commit-list', 0);

foreach ($tbody->find('tr') as $tr) {
$item = array();

$message = $tr->find('td.message', 0);

$item['title'] = $message->find('span.message-wrapper', 0)->plaintext;
$item['uri'] = $tr->find('td.sha', 0)->find('a', 0)->href;
$item['author'] = $tr->find('td.author', 0)->plaintext;
$item['timestamp'] = $tr->find('td', 3)->find('span', 0)->title;

if ($message->find('pre.commit-body', 0)) {
$message->find('pre.commit-body', 0)->style = '';

$item['content'] = $message->find('pre.commit-body', 0);
} else {
$item['content'] = '<blockquote>' . $item['title'] . '</blockquote>';
}

$this->items[] = $item;
}
}

private function extractIssues($html) {
$div = $html->find('div.repository', 0);

foreach ($div->find('li.item') as $li) {
$item = array();

$number = $li->find('div', 0)->plaintext;

$item['title'] = $li->find('a.title', 0)->plaintext . ' (' . $number . ')';
$item['uri'] = $li->find('a.title', 0)->href;
$item['timestamp'] = $li->find('p.desc', 0)->find('span', 0)->title;
$item['author'] = $li->find('p.desc', 0)->find('a', 0)->plaintext;

// Fetch issue page
$issuePage = getSimpleHTMLDOMCached($item['uri'], 3600)
or returnServerError('Could not request: ' . $item['uri']);

$issuePage = defaultLinkTo($issuePage, self::URI);

$item['content'] = $issuePage->find('ui.timeline', 0)->find('div.render-content.markdown', 0);

foreach ($li->find('a.ui.label') as $label) {
$item['categories'][] = $label->plaintext;
}

$this->items[] = $item;
}
}

private function extractIssueComments($html) {
$this->issueTitle = $html->find('span#issue-title', 0)->plaintext
. ' (' . $html->find('span.index', 0)->plaintext . ')';

foreach ($html->find('ui.timeline > div.timeline-item.comment') as $div) {
$item = array();

if ($div->class === 'timeline-item comment merge box') {
continue;
}

$item['title'] = $this->ellipsisTitle($div->find('div.render-content.markdown', 0)->plaintext);
$item['uri'] = $div->find('span.text.grey', 0)->find('a', 1)->href;
$item['content'] = $div->find('div.render-content.markdown', 0);

if ($div->find('div.dropzone-attachments', 0)) {
$item['content'] .= $div->find('div.dropzone-attachments', 0);
}

$item['author'] = $div->find('a.author', 0)->innertext;
$item['timestamp'] = $div->find('span.time-since', 0)->title;

$this->items[] = $item;
}
}

private function extractPulls($html) {
$div = $html->find('div.repository', 0);

foreach ($div->find('li.item') as $li) {
$item = array();

$number = $li->find('div', 0)->plaintext;

$item['title'] = $li->find('a.title', 0)->plaintext . ' (' . $number . ')';
$item['uri'] = $li->find('a.title', 0)->href;
$item['timestamp'] = $li->find('p.desc', 0)->find('span', 0)->title;
$item['author'] = $li->find('p.desc', 0)->find('a', 0)->plaintext;

// Fetch pull request page
$pullRequestPage = getSimpleHTMLDOMCached($item['uri'], 3600)
or returnServerError('Could not request: ' . $item['uri']);

$pullRequestPage = defaultLinkTo($pullRequestPage, self::URI);

$item['content'] = $pullRequestPage->find('ui.timeline', 0)->find('div.render-content.markdown', 0);

foreach ($li->find('a.ui.label') as $label) {
$item['categories'][] = $label->plaintext;
}

$this->items[] = $item;
}
}

private function extractReleases($html) {
$ul = $html->find('ul#release-list', 0);

foreach ($ul->find('li.ui.grid') as $li) {
$item = array();

if ($li->find('h3', 0)) { // Release
$item['title'] = $li->find('h3', 0)->plaintext;
$item['uri'] = $li->find('h3', 0)->find('a', 0)->href;

$tag = $li->find('span.tag', 0)->find('a', 0);
$commit = $li->find('span.commit', 0);
$downloads = $this->extractDownloads($li->find('div.download', 0));

$item['content'] = $li->find('div.markdown', 0);
$item['content'] .= <<<HTML
<strong>Tag</strong>
<p>{$tag}</p>
<strong>Commit</strong>
<p>{$commit}</p>
{$downloads}
HTML;
$item['timestamp'] = $li->find('span.time', 0)->find('span', 0)->title;
$item['author'] = $li->find('span.author', 0)->find('a', 0)->plaintext;
}

if ($li->find('h4', 0)) { // Tag
$item['title'] = $li->find('h4', 0)->plaintext;
$item['uri'] = $li->find('h4', 0)->find('a', 0)->href;

$item['content'] = <<<HTML
<strong>Commit</strong>
<p>{$li->find('div.download', 0)->find('a', 0)}</p>
HTML;

$item['content'] .= $this->extractDownloads($li->find('div.download', 0), true);
$item['timestamp'] = $li->find('span.time', 0)->find('span', 0)->title;
}

$this->items[] = $item;
}
}

private function extractDownloads($html, $skipFirst = false) {
$downloads = '';

foreach ($html->find('a') as $index => $a) {
if ($skipFirst === true && $index === 0) {
continue;
}

$downloads .= <<<HTML
{$a}<br>
HTML;
}

return <<<EOD
<strong>Downloads</strong>
<p>{$downloads}</p>
EOD;
}

private function ellipsisTitle($text) {
$length = 100;

if (strlen($text) > $length) {
$text = explode('<br>', wordwrap($text, $length, '<br>'));
return $text[0] . '...';
}
return $text;
}
}