diff --git a/TracToGitlabPlugin.php b/TracToGitlabPlugin.php index d01ba4972e6a71b5b9aceadc690a4247c08b7182..7ed3947e282443475f0a14c8db26277bc7d5b8ee 100644 --- a/TracToGitlabPlugin.php +++ b/TracToGitlabPlugin.php @@ -13,7 +13,6 @@ final class TracToGitlabPlugin extends StudIPPlugin implements StandardPlugin, S return; } - $this->buildNavigation(); } @@ -120,6 +119,10 @@ final class TracToGitlabPlugin extends StudIPPlugin implements StandardPlugin, S 'translations', new Navigation(_('Übersetzungen'), PluginEngine::getURL($this, [], 'translations')) ); + $navigation->addSubNavigation( + 'merge', + new Navigation(_('Zu portierende Bugfixes'), PluginEngine::getURL($this, [], 'merge')) + ); Navigation::addItem('/gitlab-dashboard', $navigation); } diff --git a/controllers/dashboard.php b/controllers/dashboard.php index a5d7ade8fe2956454813590f20fc171e062fb977..15ab26b56dbb7164fd192d254717a6126e25ffc4 100644 --- a/controllers/dashboard.php +++ b/controllers/dashboard.php @@ -17,6 +17,7 @@ final class DashboardController extends TracToGitlab\Controller Navigation::activateItem('/gitlab-dashboard/dashboard'); PageLayout::setTitle(_('Übersicht der Issues in gitlab')); + $this->activateNavigation('dashboard'); $this->setupSidebar(); } @@ -100,6 +101,7 @@ final class DashboardController extends TracToGitlab\Controller usort($issues, function ($a, $b) { return $a['id'] - $b['id']; }); + return array_map(function ($issue) { return new TracToGitlab\GitlabIssue($issue); }, $issues); diff --git a/controllers/merge.php b/controllers/merge.php new file mode 100644 index 0000000000000000000000000000000000000000..e997eed535eb8142de6f8d7d7ad5fbe9cad0c1d1 --- /dev/null +++ b/controllers/merge.php @@ -0,0 +1,106 @@ +<?php +final class MergeController extends TracToGitlab\GitlabController +{ + public function before_filter(&$action, &$args) + { + parent::before_filter($action, $args); + + PageLayout::setTitle(_('Übersicht noch zu portierender Bugfixes')); + $this->activateNavigation('merge'); + } + + public function index_action() + { + $data = $this->readFromCache('issues-to-merge', true); + if ($data === false) { + $this->issues = $this->fetchIssues(); + $this->writeToCache('issues-to-merge', $this->issues); + } else { + $this->issues = $data['data']; + + Sidebar::get()->addWidget(new TemplateWidget( + 'Aus dem Cache', + $this->get_template_factory()->open( + $this->get_default_template('sidebar'), + ), + ['time' => time()] + )); + } + } + + public function diff_action($mr_iid) + { +// $versions = $this->gitlab->mergeRequests()-> + } + + private function fetchIssues(): array + { + $issues = $this->gitlabPager->fetchAll( + $this->gitlab->issues(), + 'all', + [ + $this->gitlabProjectId, + [ + 'sort' => 'asc', + 'scope' => 'all', + 'milestone' => 'None', + 'labels' => 'BIEST', + 'state' => 'closed', + ] + ] + ); + + $issues = array_filter($issues, function ($issue) { + foreach (['worksforme', 'wontfix', 'Duplicate', 'invalid'] as $label) { + if (in_array($label, $issue['labels'])) { + return false; + } + } + + $has_version = array_reduce($issue['labels'], function ($has_version, $label) { + return $has_version || strpos($label, 'Version::') === 0; + }, false); + return $has_version; + }); + + $issues = array_map(function ($issue) { + $issue['studip_version'] = $this->extractVersion($issue['labels']); + $issue['mr'] = $this->fetchRelatedMergeRequest($issue['iid']); + return $issue; + }, $issues); + + usort($issues, function ($a, $b) { + return strcmp($b['studip_version'], $a['studip_version']); + }); + + return $issues; + } + + private function fetchRelatedMergeRequest(int $issue_id) + { + $mrs = $this->gitlab->issues()->closedByMergeRequests( + $this->gitlabProjectId, + $issue_id + ); + foreach ($mrs as $mr) { + if ($mr['state'] === 'merged') { + return $mr['iid']; + } + } + return null; + } + + private function extractVersion(array $labels): string + { + $version = ''; + foreach ($labels as $label) { + if (strpos($label, 'Version::') === 0) { + $v = substr($label, 9); + if (!$version || $v < $version) { + $version = $v; + } + } + } + return $version; + } +} diff --git a/lib/Controller.php b/lib/Controller.php index 18801cdee27fe958756f8b434befb8dd87a3db94..8e669c75727d049a2d017fe90a49bfa2eb029278 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -20,4 +20,9 @@ abstract class Controller extends \PluginController return $container[$offset]; } + + protected function activateNavigation(string ...$path): void + { + \Navigation::activateItem('/gitlab-dashboard/' . implode('/', $path)); + } } diff --git a/lib/GitlabController.php b/lib/GitlabController.php new file mode 100644 index 0000000000000000000000000000000000000000..e4ac7647a486b7f5a93a090159c9bd70a9bae6b8 --- /dev/null +++ b/lib/GitlabController.php @@ -0,0 +1,43 @@ +<?php +namespace TracToGitlab; + +use Gitlab\Api\AbstractApi; + +abstract class GitlabController extends Controller +{ + private $gitlabCache; + + public function before_filter(&$action, &$args) + { + parent::before_filter($action, $args); + + $this->gitlabCache = \StudipCacheFactory::getCache(); + } + + protected function readFromCache(string $key, bool $raw = false) + { + $cached = $this->gitlabCache->read($key); + if (!$cached) { + return false; + } + $data = json_decode($cached, true); + return $raw ? $data : $data['data']; + } + + protected function writeToCache(string $key, $data, $expire = 5 * 60) + { + $this->gitlabCache->write($key, json_encode([ + 'data' => $data, + 'time' => time(), + ]), $expire); + } + + protected function getCachedDate(string $key) + { + $data = $this->readFromCache($key, true); + if ($data === false) { + return false; + } + return $data['time'] ?? false; + } +} diff --git a/views/dashboard/index.php b/views/dashboard/index.php index b4680c23cf4bc87c95a97b35e25b27f07ee2cc19..34066f81cd8de68cf9bcbabdb02f7cf43c99f068 100644 --- a/views/dashboard/index.php +++ b/views/dashboard/index.php @@ -1,3 +1,9 @@ +<?php +/** + * @var array<string, string> $mapping + * @var array<int, TracToGitlab\GitlabIssue> $issues + */ +?> <table class="default"> <thead> <tr> diff --git a/views/merge/index.php b/views/merge/index.php new file mode 100644 index 0000000000000000000000000000000000000000..a335d64102e2e69f689670f7d179a6321e057d57 --- /dev/null +++ b/views/merge/index.php @@ -0,0 +1,44 @@ +<?php +/** + * @var array<int, array> $issues + */ +?> +<table class="default"> + <thead> + <tr> + <th><?= _('Issue') ?></th> + <th><?= _('Autor') ?></th> + <th><?= _('Bearbeiter') ?></th> + <th><?= _('Version') ?></th> +<!-- <th></th>--> + </tr> + </thead> + <tbody> + <? if (!$issues): ?> + <tr> + <td colspan="4" style="text-align: center"> + <?= _('Keine portierbaren Bugfixes vorhanden 🥳🍺') ?> + </td> + </tr> + <? endif; ?> + <? foreach ($issues as $issue): ?> + <tr> + <td> + <a href="<?= htmlReady($issue['web_url']) ?>" target="_blank"> + #<?= htmlReady($issue['iid']) ?>: + <?= htmlReady($issue['title']) ?> + </td> + <td><?= htmlReady($issue['author']['username']) ?></td> + <td><?= htmlReady(implode(',', array_column($issue['assignees'], 'username'))) ?></td> + <td><?= htmlReady($issue['studip_version']) ?></td> +<!-- <td>--> +<!-- --><?// if ($issue['mr']): ?> +<!-- <a href="--><?//= $controller->diff($issue['mr']) ?><!--" target="_blank">--> +<!-- --><?//= Icon::create('file-generic') ?> +<!-- </a>--> +<!-- --><?// endif; ?> +<!-- </td>--> + </tr> + <? endforeach; ?> + </tbody> +</table> diff --git a/views/merge/sidebar.php b/views/merge/sidebar.php new file mode 100644 index 0000000000000000000000000000000000000000..d8de1a3a16f952649f3a1edc52f86726ff438cb6 --- /dev/null +++ b/views/merge/sidebar.php @@ -0,0 +1 @@ +Zeitpunkt: <?= date('d.m.Y H:i:s', $time) ?>