Skip to content
Snippets Groups Projects
Select Git revision
  • f60319f836037c7775f192a9e19b385fe2e2ec7e
  • main default protected
  • issue-56
  • testing-systems
4 results

convert.php

Blame
  • 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": "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)"
                //}
                $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;
        }
    }