Select Git revision
convert.php
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
convert.php 12.08 KiB
<?php
final class ConvertController extends TracToGitlab\Controller
{
public function before_filter(&$action, &$args)
{
parent::before_filter($action, $args);
Navigation::activateItem('/course/trac2gitlab');
$views = Sidebar::get()->addWidget(new ViewsWidget());
$views->addLink(
_('Trac-Ticket migrieren'),
$this->indexURL()
)->setActive(!in_array($action, ['history', 'config']));
$views->addLink(
_('Bereits migrierte Tickets'),
$this->historyURL()
)->setActive($action === 'history');
if ($GLOBALS['user']->perms === 'root') {
$views->addLink(
_('Konfiguration'),
$this->configURL()
)->setActive($action === 'config');
}
}
public function index_action()
{
}
public function convert_action()
{
$ticket_id = Request::int('trac-id');
$migrated = TracToGitlab\TicketIssueEntry::findOneByTrac_ticket_id($ticket_id);
if ($migrated) {
$issue = $this->gitlab->issues()->show(
$this->gitlabProjectId,
$migrated['gitlab_issue_id']
);
PageLayout::postSuccess(_('Das Ticket wurde bereits migriert'), [
sprintf(
'<a href="%s" target="_blank">gitlab Issue #%u</a>',
$issue['web_url'],
$migrated['gitlab_issue_id']
),
]);
$this->redirect($this->indexURL());
return;
}
// Transfer attachments
$path = "{$GLOBALS['TMP_PATH']}/trac-migration/";
if (!file_exists($path)) {
mkdir($path, 0777, true);
}
$attachments = $this->trac->listAttachments($ticket_id);
$attachment_mapping = [];
foreach ($attachments as $attachment) {
$content = $this->trac->getAttachment($ticket_id, $attachment);
$filename = "{$path}/{$attachment->getName()}";
file_put_contents($filename, $content);
$uploaded_file = $this->gitlab->projects()->uploadFile(
Config::get()->TRAC2GITLAB_GITLAB_PROJECT_ID,
$filename
);
unlink($filename);
// Returned object:
// {
// "alt": "dk",
// "url": "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png",
// "full_path": "/namespace1/project1/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png",
// "markdown": ""
//}
$attachment_mapping[$attachment->getName()] = $uploaded_file;
}
// Transfer ticket
$ticket = $this->trac->getTicket($ticket_id);
$ticket['changes'] = $this->trac->getChanges($ticket_id);
$description = $this->convertTracMarkup($ticket[3]['description'], $ticket_id, $attachment_mapping);
if ($description) {
$description .= "\n\n---\n\n";
}
// Add original reporter and other info
$description .= "- Reporter: {$ticket[3]['reporter']}\n";
if ($ticket[3]['studipversion']) {
$description .= "- Stud.IP Version: {$ticket[3]['studipversion']}\n";
}
if ($ticket[3]['milestone']) {
$description .= "- Meilenstein: {$ticket[3]['milestone']}\n";
}
if ($ticket[3]['version']) {
$description .= "- Version: {$ticket[3]['version']}\n";
}
if ($ticket[3]['cc']) {
$description .= "- CC: {$ticket[3]['cc']}\n";
}
$description .= sprintf(
"\n[⤷ Link zum ursprünglichen Ticket im trac](%s)",
$this->trac->getCleanURL('ticket', $ticket_id)
);
// Create issue
$attributes = [
'title' => $ticket[3]['summary'],
'description' => $description,
'created_at' => date('c', $ticket[1]),
'labels' => $this->extractLabels($ticket),
'confidential' => !empty($ticket[3]['sensitive']),
];
if ($assignee_id = $this->findGitlabUserId($ticket[3]['owner'])) {
$attributes['assignee_id'] = $assignee_id;
}
$result = $this->gitlab->issues()->create(Config::get()->TRAC2GITLAB_GITLAB_PROJECT_ID, $attributes);
if ($ticket[3]['status'] === 'closed') {
$this->gitlab->issues()->update(
Config::get()->TRAC2GITLAB_GITLAB_PROJECT_ID,
$result['iid'],
['state_event' => 'close']
);
}
foreach ($ticket['changes'] as $change) {
if (in_array($change[2], ['owner', 'status', 'description', 'summary'])) {
continue;
}
if ($change[2] === 'comment' && $change[4]) {
$this->gitlab->issues()->addNote(
Config::get()->TRAC2GITLAB_GITLAB_PROJECT_ID,
$result['iid'],
"{$change[1]}:\n\n" . $this->convertTracMarkup($change[4], $ticket_id, $attachment_mapping),
['created_at' => date('c', $change[0])]
);
} elseif ($change[2] === 'attachment' && $change[4]) {
$note = "{$change[1]} added attachment";
if (isset($attachment_mapping[$change[4]])) {
$note .= ":\n\n[{$change[4]}]({$attachment_mapping[$change[4]]['url']})";
} else {
$note .= " {$change[4]}";
}
$this->gitlab->issues()->addNote(
Config::Get()->TRAC2GITLAB_GITLAB_PROJECT_ID,
$result['iid'],
$note,
['created_at' => date('c', $change[0])]
);
}
}
// Close ticket and leave comment with link to gitlab
$this->trac->updateTicket(
$ticket_id,
"Ticket has been migrated to gitlab\n\n" . $result['web_url'],
!in_array($ticket[3]['status'], ['closed', 'migrated']) ? ['status' => 'migrated'] : []
);
// Store migration
TracToGitlab\TicketIssueEntry::create([
'trac_ticket_id' => $ticket_id,
'gitlab_issue_id' => $result['iid'],
'summary' => $ticket[3]['summary'],
'trac_url' => $this->trac->getCleanURL('ticket', $ticket_id),
'gitlab_url' => $result['web_url'],
'user_id' => $GLOBALS['user']->id,
]);
PageLayout::postSuccess(_('Das Ticket wurde erfolgreich migriert'), [
sprintf(
'<a href="%s" target="_blank">gitlab Issue #%u</a>',
$result['web_url'],
$result['iid']
),
]);
$this->redirect($this->indexURL());
}
public function history_action($page = 0)
{
$this->page = $page;
$this->limit = Config::get()->ENTRIES_PER_PAGE;
$this->entries = TracToGitlab\TicketIssueEntry::findBySQL(sprintf(
"1 ORDER BY mkdate DESC LIMIT %u, %u",
$this->page * $this->limit,
$this->limit
));
$this->total = TracToGitlab\TicketIssueEntry::countBySQL('1');
}
public function quicksearch_action()
{
$needle = trim(Request::get('term'));
$query = "description=~{$needle}&order=id&desc=1";
if (is_numeric($needle)) {
$query = "id={$needle}&or&{$query}";
}
$tickets = $this->trac->listTicketsForQuery($query, 20);
$tickets = array_map(function ($ticket) {
$ticket['migrated'] = TracToGitlab\TicketIssueEntry::countByTrac_ticket_id($ticket[0]) > 0;
return $ticket;
}, $tickets);
$this->render_json($tickets);
}
public function config_action()
{
$this->trac_url = Config::get()->TRAC2GITLAB_TRAC_URL;
$this->gitlab_url = Config::get()->TRAC2GITLAB_GITLAB_URL;
$this->gitlab_token = Config::get()->TRAC2GITLAB_GITLAB_TOKEN;
$this->gitlab_project_id = Config::get()->TRAC2GITLAB_GITLAB_PROJECT_ID;
if (Request::isPost()) {
Config::get()->store('TRAC2GITLAB_TRAC_URL', trim(Request::get('trac-url')));
Config::get()->store('TRAC2GITLAB_GITLAB_URL', trim(Request::get('gitlab-url')));
Config::get()->store('TRAC2GITLAB_GITLAB_TOKEN', trim(Request::get('gitlab-token')));
Config::get()->store('TRAC2GITLAB_GITLAB_PROJECT_ID', Request::int('gitlab-project-id'));
PageLayout::postSuccess(_('Einstellungen wurden gespeichert'));
$this->redirect($this->configURL());
}
}
private function convertTracMarkup(string $description, int $ticket_id, array $attachment_mapping): string
{
$description = trim($description);
if (!$description) {
return '';
}
return translateTracToMarkdown(
$description,
$this->trac->getCleanURL(),
$ticket_id,
$attachment_mapping
);
}
private function extractLabels(array $ticket): array
{
$review_mapping = [
'userdoc' => 'Anwendungs-Doku',
'iface' => 'Schnittstellen',
'review' => 'Code-Review',
'functest' => 'Funktionalität',
'devdoc' => 'Entwicklungs-Doku',
'gui' => 'GUI-Richtlinien',
'strings' => 'Textstrings',
];
$priority_mapping = [
'lowest' => 'sehr niedrig',
'low' => 'niedrig',
'high' => 'hoch',
'highest' => 'sehr hoch',
];
$labels = [];
// Ticket Typ
if ($ticket[3]['type'] === 'Lifters') {
$labels[] = 'LifTer';
} elseif (in_array($ticket[3]['type'], ['BIEST', 'StEP', 'TIC'])) {
$labels[] = $ticket[3]['type'];
}
// Ticket resolution
if ($ticket[3]['status'] === 'closed' && $ticket[3]['resolution'] !== 'fixed') {
$labels[] = $ticket[3]['resolution'];
}
// Ticket sensitivy
if ($ticket[3]['sensitive']) {
$labels[] = 'Sicherheitslücke';
}
// Component
if ($ticket[3]['component']) {
$labels[] = "Komponente: {$ticket[3]['component']}";
}
// Priority
if ($ticket[3]['priority'] && array_key_exists($ticket[3]['priority'], $priority_mapping)) {
$labels[] = "Priorität: {$priority_mapping[$ticket[3]['priority']]}";
}
// Severity
if ($ticket[3]['severity']) {
$labels[] = "Schweregrad: {$ticket[3]['severity']}";
}
// Review tags
if (in_array($ticket[3]['type'], ['TIC', 'StEP'])) {
foreach ($review_mapping as $trac => $gitlab) {
if (!isset($ticket[3][$trac])) {
continue;
}
$state = $ticket[3][$trac] ?: '?';
$labels[] = "QM::{$gitlab}::{$state}";
}
}
// Add keywords
foreach (preg_split('/[, ]/', $ticket[3]['keywords'], -1, PREG_SPLIT_NO_EMPTY) as $keyword) {
$labels[] = $keyword;
}
// Usability
if (!empty($ticket[3]['usability'])) {
$labels[] = 'Usability';
}
// Browsers
$browser = str_replace('Microsoft ', '', $ticket[3]['browser']);
if (in_array($browser, ['Chrome', 'Edge', 'Firefox', 'Safari'])) {
$labels[] = "Browser: {$browser}";
}
// Version
if (!empty($ticket[3]['studipversion'])) {
$labels[] = "Version::{$ticket[3]['studipversion']}";
}
return $labels;
}
private function findGitlabUserId(string $username)
{
$members = $this->gitlab->projects()->allMembers(
Config::get()->TRAC2GITLAB_GITLAB_PROJECT_ID,
['query' => $username]
);
foreach ($members as $member) {
if ($member['username'] === $username) {
return $member['id'];
}
}
return false;
}
}