helper_plugin_issuelinks_data::importAllIssues()   B
last analyzed

Complexity

Conditions 8
Paths 30

Size

Total Lines 60
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 42
c 1
b 0
f 0
nc 30
nop 2
dl 0
loc 60
ccs 0
cts 53
cp 0
crap 72
rs 8.0035

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * DokuWiki Plugin issuelinks (Helper Component)
4
 *
5
 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6
 * @author  Andreas Gohr <[email protected]>
7
 */
8
9
use dokuwiki\plugin\issuelinks\classes\Issue;
10
use dokuwiki\plugin\issuelinks\classes\ServiceProvider;
11
12
class helper_plugin_issuelinks_data extends DokuWiki_Plugin
13
{
14
15
    /** @var helper_plugin_issuelinks_db */
16
    private $db = null;
17
18
    /**
19
     * constructor. loads helpers
20
     */
21
    public function __construct()
22
    {
23
        $this->db = $this->loadHelper('issuelinks_db');
24
    }
25
26
27
    /**
28
     * Import all Jira issues starting at given paging offset
29
     *
30
     * @param string $serviceName The name of the project management service
31
     * @param string $projectKey  The short-key of the project to be imported
32
     *
33
     * @throws Exception
34
     */
35
    public function importAllIssues($serviceName, $projectKey)
36
    {
37
        $lockfileKey = $this->getImportLockID($serviceName, $projectKey);
38
        if ($this->isImportLocked($lockfileKey)) {
39
            throw new RuntimeException('Import of Issues is already locked!');
40
        }
41
        dbglog('start import. $lockfileKey: ' . $lockfileKey);
42
        $this->lockImport($lockfileKey, json_encode(['user' => $_SERVER['REMOTE_USER'], 'status' => 'started']));
43
44
        $serviceProvider = ServiceProvider::getInstance();
45
        $services = $serviceProvider->getServices();
46
        dbglog($services);
47
        dbglog($serviceName);
48
        $serviceClass = $services[$serviceName];
49
        dbglog($serviceClass);
50
        $service = $serviceClass::getInstance();
51
52
        $total = 0;
53
        $counter = 0;
54
        $startAt = 0;
55
56
        try {
57
            while ($issues = $service->retrieveAllIssues($projectKey, $startAt)) {
58
                if (!$total) {
59
                    $total = $service->getTotalIssuesBeingImported();
60
                }
61
62
                if ($counter > $total) {
63
                    break;
64
                }
65
66
                if (!$this->isImportLockedByMe($lockfileKey)) {
67
                    throw new RuntimeException('Import of Issues aborted because lock removed');
68
                }
69
70
                $counter += count($issues);
71
                $this->lockImport($lockfileKey, json_encode([
72
                    'user' => $_SERVER['REMOTE_USER'],
73
                    'total' => $total,
74
                    'count' => $counter,
75
                    'status' => 'running',
76
                ]));
77
            }
78
        } catch (\Throwable $e) {
79
            dbglog(
80
                "Downloading all issues from $serviceName fpr project $projectKey failed ",
81
                __FILE__ . ': ' . __LINE__
82
            );
83
            if (is_a($e, \dokuwiki\plugin\issuelinks\classes\HTTPRequestException::class)) {
84
                /** @var \dokuwiki\plugin\issuelinks\classes\HTTPRequestException $e */
85
                dbglog($e->getUrl());
86
                dbglog($e->getHttpError());
87
                dbglog($e->getMessage());
88
                dbglog($e->getCode());
89
                dbglog($e->getResponseBody());
90
            }
91
            $this->lockImport($lockfileKey, json_encode(['status' => 'failed']));
92
            throw $e;
93
        }
94
        $this->unlockImport($lockfileKey);
95
    }
96
97
98
    public function getImportLockID($serviceName, $projectKey)
99
    {
100
        return "_plugin__issuelinks_import_$serviceName-$projectKey";
101
    }
102
103
    /**
104
     * This checks the lock for the import process it behaves differently from the dokuwiki-core checklock() function!
105
     *
106
     * It returns false if the lock does not exist. It returns **boolean true** if the lock exists and is mine.
107
     * It returns the username/ip if the lock exists and is not mine.
108
     * It is therefore important to use strict (===) checking for true!
109
     *
110
     * @param $id
111
     *
112
     * @return bool|string
113
     */
114
    public function isImportLocked($id)
115
    {
116
        global $conf;
117
        $lockFN = $conf['lockdir'] . '/' . md5('_' . $id) . '.lock';
118
        if (!file_exists($lockFN)) {
119
            return false;
120
        }
121
122
        clearstatcache(true, $lockFN);
123
        if ((time() - filemtime($lockFN)) > 120) {
124
            unlink($lockFN);
125
            dbglog('issuelinks: stale lock timeout');
126
            return false;
127
        }
128
129
        $lockData = json_decode(io_readFile($lockFN), true);
130
        if (!empty($lockData['status']) && $lockData['status'] === 'done') {
131
            return false;
132
        }
133
134
        return true;
135
    }
136
137
    /**
138
     * Generate lock file for import of issues/commits
139
     *
140
     * This is mostly a reimplementation of @see lock()
141
     * However we do not clean the id and prepent a underscore to avoid conflicts with locks of existing pages.
142
     *
143
     * @param $id
144
     * @param $jsonData
145
     */
146
    public function lockImport($id, $jsonData)
147
    {
148
        global $conf;
149
150
        $lock = $conf['lockdir'] . '/' . md5('_' . $id) . '.lock';
151
        dbglog('lock import: ' . $jsonData, __FILE__ . ': ' . __LINE__);
152
        io_saveFile($lock, $jsonData);
153
    }
154
155
    public function isImportLockedByMe($id)
156
    {
157
        if (!$this->isImportLocked($id)) {
158
            return false;
159
        }
160
161
        global $conf, $INPUT;
162
        $lockFN = $conf['lockdir'] . '/' . md5('_' . $id) . '.lock';
163
        $lockData = json_decode(io_readFile($lockFN), true);
164
        if ($lockData['user'] !== $INPUT->server->str('REMOTE_USER')) {
165
            return false;
166
        }
167
168
        touch($lockFN);
169
        return true;
170
    }
171
172
    /**
173
     * Marks the import as unlocked / done
174
     *
175
     * @param $id
176
     */
177
    public function unlockImport($id)
178
    {
179
        global $conf;
180
        $lockFN = $conf['lockdir'] . '/' . md5('_' . $id) . '.lock';
181
        $lockData = json_decode(io_readFile($lockFN), true);
182
        $lockData['status'] = 'done';
183
        $lockData['total'] = $lockData['count'];
184
        io_saveFile($lockFN, json_encode($lockData));
185
    }
186
187
    public function getLockContent($id)
188
    {
189
        global $conf;
190
        $lockFN = $conf['lockdir'] . '/' . md5('_' . $id) . '.lock';
191
        if (!file_exists($lockFN)) {
192
            return false;
193
        }
194
        return json_decode(io_readFile($lockFN), true);
195
    }
196
197
    public function removeLock($lockID)
198
    {
199
        global $conf;
200
        $lockFN = $conf['lockdir'] . '/' . md5('_' . $lockID) . '.lock';
201
        unlink($lockFN);
202
    }
203
204
    /**
205
     * Get an issue either from local DB or attempt to import it
206
     *
207
     * @param string $pmServiceName The name of the project management service
208
     * @param string $project
209
     * @param int    $issueid
210
     * @param bool   $isMergeRequest
211
     *
212
     * @return bool|Issue
213
     */
214
    public function getIssue($pmServiceName, $project, $issueid, $isMergeRequest)
215
    {
216
        $issue = Issue::getInstance($pmServiceName, $project, $issueid, $isMergeRequest);
217
        if (!$issue->isValid()) {
218
            try {
219
                $issue->getFromService();
220
                $issue->saveToDB();
221
            } catch (Exception $e) {
222
                // that's fine
223
            }
224
        }
225
        return $issue;
226
    }
227
228
    public function getMergeRequestsForIssue($serviceName, $projectKey, $issueId, $isMergeRequest)
229
    {
230
        /** @var helper_plugin_issuelinks_db $db */
231
        $db = plugin_load('helper', 'issuelinks_db');
232
        $issues = $db->getMergeRequestsReferencingIssue($serviceName, $projectKey, $issueId, $isMergeRequest);
233
        foreach ($issues as &$issueData) {
234
            $issue = Issue::getInstance(
235
                $issueData['service'],
236
                $issueData['project_id'],
237
                $issueData['issue_id'],
238
                $issueData['is_mergerequest']
239
            );
240
            $issue->getFromDB();
241
            $issueData['summary'] = $issue->getSummary();
242
            $issueData['status'] = $issue->getStatus();
243
            $issueData['url'] = $issue->getIssueURL();
244
        }
245
        unset($issueData);
246
247
        return $issues;
248
    }
249
250
    /**
251
     * Get Pages with links to issues
252
     *
253
     * @param string $pmServiceName The name of the project management service
254
     * @param string $projectKey
255
     * @param int    $issueId       the issue id
256
     * @param bool   $isMergeRequest
257
     *
258
     * @return array
259
     */
260
    public function getLinkingPages($pmServiceName, $projectKey, $issueId, $isMergeRequest)
261
    {
262
        $pages = $this->db->getAllPageLinkingToIssue($pmServiceName, $projectKey, $issueId, $isMergeRequest);
263
        $pages = $this->db->removeOldLinks($pmServiceName, $projectKey, $issueId, $isMergeRequest, $pages);
264
265
        if (empty($pages)) {
266
            return [];
267
        }
268
269
        $pages = $this->keepNewest($pages);
270
        $pages = $this->filterPagesForACL($pages);
271
        $pages = $this->addUserToPages($pages);
272
        return $pages;
273
    }
274
275
    /**
276
     * remove duplicate revisions of a page and keep only the newest
277
     *
278
     * @param array $pages Array of pages sorted(!) from newest to oldest
279
     *
280
     * @return array
281
     */
282
    public function keepNewest($pages)
283
    {
284
        $uniquePages = [];
285
        foreach ($pages as $page) {
286
            if (!array_key_exists($page['page'], $uniquePages) || $uniquePages[$page['page']]['rev'] < $page['rev']) {
287
                $uniquePages[$page['page']] = $page;
288
            }
289
        }
290
        return array_values($uniquePages);
291
    }
292
293
    /**
294
     * Filter the given pages for at least AUTH_READ
295
     *
296
     * @param array $pages
297
     *
298
     * @return array
299
     */
300
    private function filterPagesForACL($pages)
301
    {
302
        $allowedPagegs = [];
303
        foreach ($pages as $page) {
304
            if (auth_quickaclcheck($page['page']) >= AUTH_READ) {
305
                $allowedPagegs[] = $page;
306
            }
307
        }
308
        return $allowedPagegs;
309
    }
310
311
    /**
312
     * add the corresponding user to each revision
313
     *
314
     * @param array $pages
315
     *
316
     * @return array
317
     */
318
    public function addUserToPages($pages)
319
    {
320
        foreach ($pages as &$page) {
321
            $changelog = new PageChangelog($page['page']);
322
            $revision = $changelog->getRevisionInfo($page['rev']);
323
            $page['user'] = $revision['user'];
324
        }
325
        return $pages;
326
    }
327
}
328
329
// vim:ts=4:sw=4:et:
330