Passed
Push — dev ( 824cd4...d410ef )
by Greg
12:51
created

ExportGedcomClient::handle()   B

Complexity

Conditions 8
Paths 10

Size

Total Lines 77
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 50
c 0
b 0
f 0
nc 10
nop 1
dl 0
loc 77
rs 7.8464

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
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2021 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Http\RequestHandlers;
21
22
use Fisharebest\Webtrees\Auth;
23
use Fisharebest\Webtrees\Encodings\ANSEL;
24
use Fisharebest\Webtrees\Encodings\ASCII;
25
use Fisharebest\Webtrees\Encodings\UTF16BE;
26
use Fisharebest\Webtrees\Encodings\UTF8;
27
use Fisharebest\Webtrees\Encodings\Windows1252;
28
use Fisharebest\Webtrees\GedcomRecord;
29
use Fisharebest\Webtrees\Http\ViewResponseTrait;
30
use Fisharebest\Webtrees\Registry;
31
use Fisharebest\Webtrees\Services\GedcomExportService;
32
use Fisharebest\Webtrees\Tree;
33
use Fisharebest\Webtrees\Validator;
34
use Illuminate\Database\Capsule\Manager as DB;
35
use League\Flysystem\Filesystem;
36
use League\Flysystem\FilesystemException;
37
use League\Flysystem\ZipArchive\FilesystemZipArchiveProvider;
38
use League\Flysystem\ZipArchive\ZipArchiveAdapter;
39
use Psr\Http\Message\ResponseFactoryInterface;
40
use Psr\Http\Message\ResponseInterface;
41
use Psr\Http\Message\ServerRequestInterface;
42
use Psr\Http\Message\StreamFactoryInterface;
43
use Psr\Http\Server\RequestHandlerInterface;
44
45
use function addcslashes;
46
use function assert;
47
use function fclose;
48
use function pathinfo;
49
use function strtolower;
50
use function tmpfile;
51
52
use const PATHINFO_EXTENSION;
53
54
/**
55
 * Download a GEDCOM file to the client.
56
 */
57
class ExportGedcomClient implements RequestHandlerInterface
58
{
59
    use ViewResponseTrait;
60
61
    private GedcomExportService $gedcom_export_service;
62
63
    private ResponseFactoryInterface $response_factory;
64
65
    private StreamFactoryInterface $stream_factory;
66
67
    /**
68
     * ExportGedcomServer constructor.
69
     *
70
     * @param GedcomExportService      $gedcom_export_service
71
     * @param ResponseFactoryInterface $response_factory
72
     * @param StreamFactoryInterface   $stream_factory
73
     */
74
    public function __construct(
75
        GedcomExportService $gedcom_export_service,
76
        ResponseFactoryInterface $response_factory,
77
        StreamFactoryInterface $stream_factory
78
    ) {
79
        $this->gedcom_export_service = $gedcom_export_service;
80
        $this->response_factory = $response_factory;
81
        $this->stream_factory = $stream_factory;
82
    }
83
84
    /**
85
     * @param ServerRequestInterface $request
86
     *
87
     * @return ResponseInterface
88
     * @throws FilesystemException
89
     */
90
    public function handle(ServerRequestInterface $request): ResponseInterface
91
    {
92
        $tree = $request->getAttribute('tree');
93
        assert($tree instanceof Tree);
94
95
        $data_filesystem = Registry::filesystem()->data();
96
97
        $format       = Validator::parsedBody($request)->isInArray(['gedcom', 'zip'])->requiredString('format');
98
        $privacy      = Validator::parsedBody($request)->isInArray(['none', 'gedadmin', 'user', 'visitor'])->requiredString('privacy');
99
        $encoding     = Validator::parsedBody($request)->isInArray([UTF8::NAME, UTF16BE::NAME, ANSEL::NAME, ASCII::NAME, Windows1252::NAME])->requiredString('encoding');
100
        $line_endings = Validator::parsedBody($request)->isInArray(['CRLF', 'LF'])->requiredString('line_endings');
101
        $media_path   = Validator::parsedBody($request)->string('media_path') ?? '';
102
103
        $access_levels = [
104
            'gedadmin' => Auth::PRIV_NONE,
105
            'user'     => Auth::PRIV_USER,
106
            'visitor'  => Auth::PRIV_PRIVATE,
107
            'none'     => Auth::PRIV_HIDE,
108
        ];
109
110
        $access_level = $access_levels[$privacy];
111
112
        // What to call the downloaded file
113
        $download_filename = $tree->name();
114
115
        // Force a ".ged" suffix
116
        if (strtolower(pathinfo($download_filename, PATHINFO_EXTENSION)) !== 'ged') {
1 ignored issue
show
Bug introduced by
It seems like pathinfo($download_filename, PATHINFO_EXTENSION) can also be of type array; however, parameter $string of strtolower() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

116
        if (strtolower(/** @scrutinizer ignore-type */ pathinfo($download_filename, PATHINFO_EXTENSION)) !== 'ged') {
Loading history...
117
            $download_filename .= '.ged';
118
        }
119
120
        if ($format === 'zip') {
121
            $resource = $this->gedcom_export_service->export($tree, true, $encoding, $access_level, $media_path, $line_endings);
122
123
            $path = $tree->getPreference('MEDIA_DIRECTORY');
124
125
            // Create a new/empty .ZIP file
126
            $temp_zip_file  = stream_get_meta_data(tmpfile())['uri'];
127
            $zip_provider   = new FilesystemZipArchiveProvider($temp_zip_file, 0755);
128
            $zip_adapter    = new ZipArchiveAdapter($zip_provider);
129
            $zip_filesystem = new Filesystem($zip_adapter);
130
            $zip_filesystem->writeStream($download_filename, $resource);
131
            fclose($resource);
132
133
            $media_filesystem = $tree->mediaFilesystem($data_filesystem);
134
135
            $records = DB::table('media')
136
                ->where('m_file', '=', $tree->id())
137
                ->get()
138
                ->map(Registry::mediaFactory()->mapper($tree))
139
                ->filter(GedcomRecord::accessFilter());
140
141
            foreach ($records as $record) {
142
                foreach ($record->mediaFiles() as $media_file) {
143
                    $from = $media_file->filename();
144
                    $to   = $path . $media_file->filename();
145
                    if (!$media_file->isExternal() && $media_filesystem->fileExists($from) && !$zip_filesystem->fileExists($to)) {
146
                        $zip_filesystem->writeStream($to, $media_filesystem->readStream($from));
147
                    }
148
                }
149
            }
150
151
            $stream   = $this->stream_factory->createStreamFromFile($temp_zip_file);
152
            $filename = addcslashes($download_filename, '"') . '.zip';
153
154
            return $this->response_factory->createResponse()
155
                ->withBody($stream)
156
                ->withHeader('Content-Type', 'application/zip')
157
                ->withHeader('Content-Disposition', 'attachment; filename="' . $filename . '"');
158
        }
159
160
        $resource = $this->gedcom_export_service->export($tree, true, $encoding, $access_level, $media_path);
161
        $stream   = $this->stream_factory->createStreamFromResource($resource);
162
163
        return $this->response_factory->createResponse()
164
            ->withBody($stream)
165
            ->withHeader('Content-Type', 'text/x-gedcom; charset=' . UTF8::NAME)
166
            ->withHeader('Content-Disposition', 'attachment; filename="' . addcslashes($download_filename, '"') . '"');
167
    }
168
}
169