GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( 37b02e...ebbbe1 )
by James
08:59
created

app/Export/ExpandedProcessor.php (2 issues)

1
<?php
2
declare(strict_types=1);
3
/**
4
 * ExpandedProcessor.php
5
 * Copyright (c) 2017 [email protected]
6
 *
7
 * This file is part of Firefly III.
8
 *
9
 * Firefly III is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation, either version 3 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * Firefly III is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
namespace FireflyIII\Export;
24
25
use Crypt;
26
use DB;
27
use FireflyIII\Exceptions\FireflyException;
28
use FireflyIII\Export\Collector\AttachmentCollector;
29
use FireflyIII\Export\Collector\UploadCollector;
30
use FireflyIII\Export\Entry\Entry;
31
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
32
use FireflyIII\Helpers\Filter\InternalTransferFilter;
33
use FireflyIII\Models\AccountMeta;
34
use FireflyIII\Models\ExportJob;
35
use FireflyIII\Models\Note;
36
use FireflyIII\Models\Transaction;
37
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
38
use Illuminate\Support\Collection;
39
use Log;
40
use Storage;
41
use ZipArchive;
42
43
/**
44
 * Class ExpandedProcessor.
45
 *
46
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects) // its doing a lot.
47
 */
48
class ExpandedProcessor implements ProcessorInterface
49
{
50
    /** @var Collection */
51
    public $accounts;
52
    /** @var string */
53
    public $exportFormat;
54
    /** @var bool */
55
    public $includeAttachments;
56
    /** @var bool */
57
    public $includeOldUploads;
58
    /** @var ExportJob */
59
    public $job;
60
    /** @var array */
61
    public $settings;
62
    /** @var Collection */
63
    private $exportEntries;
64
    /** @var Collection */
65
    private $files;
66
    /** @var Collection */
67
    private $journals;
68
69
    /**
70
     * Processor constructor.
71
     */
72
    public function __construct()
73
    {
74
        $this->journals      = new Collection;
75
        $this->exportEntries = new Collection;
76
        $this->files         = new Collection;
77
    }
78
79
    /**
80
     * @return bool
81
     */
82
    public function collectAttachments(): bool
83
    {
84
        /** @var AttachmentCollector $attachmentCollector */
85
        $attachmentCollector = app(AttachmentCollector::class);
86
        $attachmentCollector->setJob($this->job);
87
        $attachmentCollector->setDates($this->settings['startDate'], $this->settings['endDate']);
88
        $attachmentCollector->run();
89
        $this->files = $this->files->merge($attachmentCollector->getEntries());
90
91
        return true;
92
    }
93
94
    /**
95
     * Collects all transaction journals.
96
     *
97
     * @return bool
98
     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
99
     */
100
    public function collectJournals(): bool
101
    {
102
        // use journal collector thing.
103
        /** @var JournalCollectorInterface $collector */
104
        $collector = app(JournalCollectorInterface::class);
105
        $collector->setUser($this->job->user);
106
        $collector->setAccounts($this->accounts)->setRange($this->settings['startDate'], $this->settings['endDate'])
107
                  ->withOpposingAccount()->withBudgetInformation()->withCategoryInformation()
108
                  ->removeFilter(InternalTransferFilter::class);
109
        $transactions = $collector->getJournals();
110
        // get some more meta data for each entry:
111
        $ids         = $transactions->pluck('journal_id')->toArray();
112
        $assetIds    = $transactions->pluck('account_id')->toArray();
113
        $opposingIds = $transactions->pluck('opposing_account_id')->toArray();
114
        $notes       = $this->getNotes($ids);
115
        $tags        = $this->getTags($ids);
116
        /** @var array $ibans */
117
        $ibans      = array_merge($this->getIbans($assetIds), $this->getIbans($opposingIds));
118
        $currencies = $this->getAccountCurrencies($ibans);
119
        $transactions->each(
120
            function (Transaction $transaction) use ($notes, $tags, $ibans, $currencies) {
121
                $journalId                            = (int)$transaction->journal_id;
122
                $accountId                            = (int)$transaction->account_id;
123
                $opposingId                           = (int)$transaction->opposing_account_id;
124
                $currencyId                           = (int)($ibans[$accountId]['currency_id'] ?? 0.0);
125
                $opposingCurrencyId                   = (int)($ibans[$opposingId]['currency_id'] ?? 0.0);
126
                $transaction->notes                   = $notes[$journalId] ?? '';
127
                $transaction->tags                    = implode(',', $tags[$journalId] ?? []);
128
                $transaction->account_number          = $ibans[$accountId]['accountNumber'] ?? '';
129
                $transaction->account_bic             = $ibans[$accountId]['BIC'] ?? '';
130
                $transaction->account_currency_code   = $currencies[$currencyId] ?? '';
131
                $transaction->opposing_account_number = $ibans[$opposingId]['accountNumber'] ?? '';
132
                $transaction->opposing_account_bic    = $ibans[$opposingId]['BIC'] ?? '';
133
                $transaction->opposing_currency_code  = $currencies[$opposingCurrencyId] ?? '';
134
            }
135
        );
136
137
        $this->journals = $transactions;
138
139
        return true;
140
    }
141
142
    /**
143
     * @return bool
144
     */
145
    public function collectOldUploads(): bool
146
    {
147
        /** @var UploadCollector $uploadCollector */
148
        $uploadCollector = app(UploadCollector::class);
149
        $uploadCollector->setJob($this->job);
150
        $uploadCollector->run();
151
152
        $this->files = $this->files->merge($uploadCollector->getEntries());
153
154
        return true;
155
    }
156
157
    /**
158
     * @return bool
159
     */
160
    public function convertJournals(): bool
161
    {
162
        $this->journals->each(
163
            function (Transaction $transaction) {
164
                $this->exportEntries->push(Entry::fromTransaction($transaction));
165
            }
166
        );
167
        Log::debug(sprintf('Count %d entries in exportEntries (convertJournals)', $this->exportEntries->count()));
168
169
        return true;
170
    }
171
172
    /**
173
     * @return bool
174
     *
175
     * @throws FireflyException
176
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
177
     */
178
    public function createZipFile(): bool
179
    {
180
        $zip      = new ZipArchive;
181
        $file     = $this->job->key . '.zip';
182
        $fullPath = storage_path('export') . '/' . $file;
183
184
        if (true !== $zip->open($fullPath, ZipArchive::CREATE)) {
185
            throw new FireflyException('Cannot store zip file.');
186
        }
187
        // for each file in the collection, add it to the zip file.
188
        $disk = Storage::disk('export');
0 ignored issues
show
Bug Best Practice introduced by
The method Illuminate\Support\Facades\Storage::disk() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

188
        /** @scrutinizer ignore-call */ 
189
        $disk = Storage::disk('export');
Loading history...
189
        foreach ($this->getFiles() as $entry) {
190
            // is part of this job?
191
            $zipFileName = str_replace($this->job->key . '-', '', $entry);
192
            $zip->addFromString($zipFileName, $disk->get($entry));
193
        }
194
195
        $zip->close();
196
197
        // delete the files:
198
        $this->deleteFiles();
199
200
        return true;
201
    }
202
203
    /**
204
     * @return bool
205
     */
206
    public function exportJournals(): bool
207
    {
208
        $exporterClass = config('firefly.export_formats.' . $this->exportFormat);
209
        $exporter      = app($exporterClass);
210
        $exporter->setJob($this->job);
211
        $exporter->setEntries($this->exportEntries);
212
        $exporter->run();
213
        $this->files->push($exporter->getFileName());
214
215
        return true;
216
    }
217
218
    /**
219
     * @return Collection
220
     */
221
    public function getFiles(): Collection
222
    {
223
        return $this->files;
224
    }
225
226
    /**
227
     * Save export job settings to class.
228
     *
229
     * @param array $settings
230
     */
231
    public function setSettings(array $settings)
232
    {
233
        // save settings
234
        $this->settings           = $settings;
235
        $this->accounts           = $settings['accounts'];
236
        $this->exportFormat       = $settings['exportFormat'];
237
        $this->includeAttachments = $settings['includeAttachments'];
238
        $this->includeOldUploads  = $settings['includeOldUploads'];
239
        $this->job                = $settings['job'];
240
    }
241
242
    /**
243
     *
244
     */
245
    private function deleteFiles()
246
    {
247
        $disk = Storage::disk('export');
0 ignored issues
show
Bug Best Practice introduced by
The method Illuminate\Support\Facades\Storage::disk() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

247
        /** @scrutinizer ignore-call */ 
248
        $disk = Storage::disk('export');
Loading history...
248
        foreach ($this->getFiles() as $file) {
249
            $disk->delete($file);
250
        }
251
    }
252
253
    /**
254
     * @param array $array
255
     *
256
     * @return array
257
     */
258
    private function getAccountCurrencies(array $array): array
259
    {
260
        /** @var CurrencyRepositoryInterface $repository */
261
        $repository = app(CurrencyRepositoryInterface::class);
262
        $return     = [];
263
        $ids        = [];
264
        $repository->setUser($this->job->user);
265
        foreach ($array as $value) {
266
            $ids[] = (int)($value['currency_id'] ?? 0.0);
267
        }
268
        $ids    = array_unique($ids);
269
        $result = $repository->getByIds($ids);
270
271
        foreach ($result as $currency) {
272
            $return[$currency->id] = $currency->code;
273
        }
274
275
        return $return;
276
    }
277
278
    /**
279
     * Get all IBAN / SWIFT / account numbers.
280
     *
281
     * @param array $array
282
     *
283
     * @return array
284
     */
285
    private function getIbans(array $array): array
286
    {
287
        $array  = array_unique($array);
288
        $return = [];
289
        $set    = AccountMeta::whereIn('account_id', $array)
290
                             ->leftJoin('accounts', 'accounts.id', 'account_meta.account_id')
291
                             ->where('accounts.user_id', $this->job->user_id)
292
                             ->whereIn('account_meta.name', ['accountNumber', 'BIC', 'currency_id'])
293
                             ->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data']);
294
        /** @var AccountMeta $meta */
295
        foreach ($set as $meta) {
296
            $id                       = (int)$meta->account_id;
297
            $return[$id][$meta->name] = $meta->data;
298
        }
299
300
        return $return;
301
    }
302
303
    /**
304
     * Returns, if present, for the given journal ID's the notes.
305
     *
306
     * @param array $array
307
     *
308
     * @return array
309
     */
310
    private function getNotes(array $array): array
311
    {
312
        $array  = array_unique($array);
313
        $notes  = Note::where('notes.noteable_type', 'FireflyIII\\Models\\TransactionJournal')
314
                      ->whereIn('notes.noteable_id', $array)
315
                      ->get(['notes.*']);
316
        $return = [];
317
        /** @var Note $note */
318
        foreach ($notes as $note) {
319
            if (strlen(trim((string)$note->text)) > 0) {
320
                $id          = (int)$note->noteable_id;
321
                $return[$id] = $note->text;
322
            }
323
        }
324
325
        return $return;
326
    }
327
328
    /**
329
     * Returns a comma joined list of all the users tags linked to these journals.
330
     *
331
     * @param array $array
332
     *
333
     * @return array
334
     * @throws \Illuminate\Contracts\Encryption\DecryptException
335
     */
336
    private function getTags(array $array): array
337
    {
338
        $set    = DB::table('tag_transaction_journal')
339
                    ->whereIn('tag_transaction_journal.transaction_journal_id', $array)
340
                    ->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id')
341
                    ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'tag_transaction_journal.transaction_journal_id')
342
                    ->where('transaction_journals.user_id', $this->job->user_id)
343
                    ->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag']);
344
        $result = [];
345
        foreach ($set as $entry) {
346
            $id            = (int)$entry->transaction_journal_id;
347
            $result[$id]   = $result[$id] ?? [];
348
            $result[$id][] = Crypt::decrypt($entry->tag);
349
        }
350
351
        return $result;
352
    }
353
}
354