xref: /webtrees/app/Http/RequestHandlers/ExportGedcomClient.php (revision a04bb9a236e92915034f97b1229f5d47c9bc750f)
16d576906SGreg Roach<?php
26d576906SGreg Roach
36d576906SGreg Roach/**
46d576906SGreg Roach * webtrees: online genealogy
56d576906SGreg Roach * Copyright (C) 2019 webtrees development team
66d576906SGreg Roach * This program is free software: you can redistribute it and/or modify
76d576906SGreg Roach * it under the terms of the GNU General Public License as published by
86d576906SGreg Roach * the Free Software Foundation, either version 3 of the License, or
96d576906SGreg Roach * (at your option) any later version.
106d576906SGreg Roach * This program is distributed in the hope that it will be useful,
116d576906SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
126d576906SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
136d576906SGreg Roach * GNU General Public License for more details.
146d576906SGreg Roach * You should have received a copy of the GNU General Public License
156d576906SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>.
166d576906SGreg Roach */
176d576906SGreg Roach
186d576906SGreg Roachdeclare(strict_types=1);
196d576906SGreg Roach
206d576906SGreg Roachnamespace Fisharebest\Webtrees\Http\RequestHandlers;
216d576906SGreg Roach
226d576906SGreg Roachuse Fisharebest\Webtrees\Auth;
236d576906SGreg Roachuse Fisharebest\Webtrees\Functions\FunctionsExport;
246d576906SGreg Roachuse Fisharebest\Webtrees\GedcomRecord;
256d576906SGreg Roachuse Fisharebest\Webtrees\Http\ViewResponseTrait;
266d576906SGreg Roachuse Fisharebest\Webtrees\Media;
276d576906SGreg Roachuse Fisharebest\Webtrees\Tree;
286d576906SGreg Roachuse Illuminate\Database\Capsule\Manager as DB;
296d576906SGreg Roachuse League\Flysystem\Filesystem;
30*a04bb9a2SGreg Roachuse League\Flysystem\FilesystemInterface;
316d576906SGreg Roachuse League\Flysystem\MountManager;
326d576906SGreg Roachuse League\Flysystem\ZipArchive\ZipArchiveAdapter;
336d576906SGreg Roachuse Psr\Http\Message\ResponseFactoryInterface;
346d576906SGreg Roachuse Psr\Http\Message\ResponseInterface;
356d576906SGreg Roachuse Psr\Http\Message\ServerRequestInterface;
366d576906SGreg Roachuse Psr\Http\Message\StreamFactoryInterface;
376d576906SGreg Roachuse Psr\Http\Server\RequestHandlerInterface;
386d576906SGreg Roach
396d576906SGreg Roachuse function addcslashes;
406d576906SGreg Roachuse function app;
416d576906SGreg Roachuse function assert;
426d576906SGreg Roachuse function fclose;
436d576906SGreg Roachuse function fopen;
446d576906SGreg Roachuse function pathinfo;
456d576906SGreg Roachuse function rewind;
466d576906SGreg Roachuse function strtolower;
476d576906SGreg Roachuse function sys_get_temp_dir;
486d576906SGreg Roachuse function tempnam;
496d576906SGreg Roachuse function tmpfile;
506d576906SGreg Roach
516d576906SGreg Roachuse const PATHINFO_EXTENSION;
526d576906SGreg Roach
536d576906SGreg Roach/**
546d576906SGreg Roach * Download a GEDCOM file to the client.
556d576906SGreg Roach */
566d576906SGreg Roachclass ExportGedcomClient implements RequestHandlerInterface
576d576906SGreg Roach{
586d576906SGreg Roach    use ViewResponseTrait;
596d576906SGreg Roach
606d576906SGreg Roach    /**
616d576906SGreg Roach     * @param ServerRequestInterface $request
626d576906SGreg Roach     *
636d576906SGreg Roach     * @return ResponseInterface
646d576906SGreg Roach     */
656d576906SGreg Roach    public function handle(ServerRequestInterface $request): ResponseInterface
666d576906SGreg Roach    {
676d576906SGreg Roach        $tree = $request->getAttribute('tree');
686d576906SGreg Roach        assert($tree instanceof Tree);
696d576906SGreg Roach
70*a04bb9a2SGreg Roach        $data_filesystem = $request->getAttribute('filesystem.data');
71*a04bb9a2SGreg Roach        assert($data_filesystem instanceof FilesystemInterface);
72*a04bb9a2SGreg Roach
736d576906SGreg Roach        $convert          = (bool) ($request->getParsedBody()['convert'] ?? false);
746d576906SGreg Roach        $zip              = (bool) ($request->getParsedBody()['zip'] ?? false);
756d576906SGreg Roach        $media            = (bool) ($request->getParsedBody()['media'] ?? false);
766d576906SGreg Roach        $media_path       = $request->getParsedBody()['media-path'] ?? '';
776d576906SGreg Roach        $privatize_export = $request->getParsedBody()['privatize_export'];
786d576906SGreg Roach
796d576906SGreg Roach        $access_levels = [
806d576906SGreg Roach            'gedadmin' => Auth::PRIV_NONE,
816d576906SGreg Roach            'user'     => Auth::PRIV_USER,
826d576906SGreg Roach            'visitor'  => Auth::PRIV_PRIVATE,
836d576906SGreg Roach            'none'     => Auth::PRIV_HIDE,
846d576906SGreg Roach        ];
856d576906SGreg Roach
866d576906SGreg Roach        $access_level = $access_levels[$privatize_export];
876d576906SGreg Roach        $encoding     = $convert ? 'ANSI' : 'UTF-8';
886d576906SGreg Roach
896d576906SGreg Roach        // What to call the downloaded file
906d576906SGreg Roach        $download_filename = $tree->name();
916d576906SGreg Roach
926d576906SGreg Roach        // Force a ".ged" suffix
936d576906SGreg Roach        if (strtolower(pathinfo($download_filename, PATHINFO_EXTENSION)) !== 'ged') {
946d576906SGreg Roach            $download_filename .= '.ged';
956d576906SGreg Roach        }
966d576906SGreg Roach
976d576906SGreg Roach        if ($zip || $media) {
986d576906SGreg Roach            // Export the GEDCOM to an in-memory stream.
996d576906SGreg Roach            $tmp_stream = tmpfile();
1006d576906SGreg Roach            FunctionsExport::exportGedcom($tree, $tmp_stream, $access_level, $media_path, $encoding);
1016d576906SGreg Roach            rewind($tmp_stream);
1026d576906SGreg Roach
1036d576906SGreg Roach            $path = $tree->getPreference('MEDIA_DIRECTORY', 'media/');
1046d576906SGreg Roach
1056d576906SGreg Roach            // Create a new/empty .ZIP file
1066d576906SGreg Roach            $temp_zip_file  = tempnam(sys_get_temp_dir(), 'webtrees-zip-');
1076d576906SGreg Roach            $zip_adapter    = new ZipArchiveAdapter($temp_zip_file);
1086d576906SGreg Roach            $zip_filesystem = new Filesystem($zip_adapter);
1096d576906SGreg Roach            $zip_filesystem->writeStream($download_filename, $tmp_stream);
1106d576906SGreg Roach            fclose($tmp_stream);
1116d576906SGreg Roach
1126d576906SGreg Roach            if ($media) {
1136d576906SGreg Roach                $manager = new MountManager([
114*a04bb9a2SGreg Roach                    'media' => $tree->mediaFilesystem($data_filesystem),
1156d576906SGreg Roach                    'zip'   => $zip_filesystem,
1166d576906SGreg Roach                ]);
1176d576906SGreg Roach
1186d576906SGreg Roach                $records = DB::table('media')
1196d576906SGreg Roach                    ->where('m_file', '=', $tree->id())
1206d576906SGreg Roach                    ->get()
1216d576906SGreg Roach                    ->map(Media::rowMapper())
1226d576906SGreg Roach                    ->filter(GedcomRecord::accessFilter());
1236d576906SGreg Roach
1246d576906SGreg Roach                foreach ($records as $record) {
1256d576906SGreg Roach                    foreach ($record->mediaFiles() as $media_file) {
1266d576906SGreg Roach                        $from = 'media://' . $media_file->filename();
1276d576906SGreg Roach                        $to   = 'zip://' . $path . $media_file->filename();
1286d576906SGreg Roach                        if (!$media_file->isExternal() && $manager->has($from)) {
1296d576906SGreg Roach                            $manager->copy($from, $to);
1306d576906SGreg Roach                        }
1316d576906SGreg Roach                    }
1326d576906SGreg Roach                }
1336d576906SGreg Roach            }
1346d576906SGreg Roach
1356d576906SGreg Roach            // Need to force-close ZipArchive filesystems.
1366d576906SGreg Roach            $zip_adapter->getArchive()->close();
1376d576906SGreg Roach
1386d576906SGreg Roach            // Use a stream, so that we do not have to load the entire file into memory.
1396d576906SGreg Roach            $stream   = app(StreamFactoryInterface::class)->createStreamFromFile($temp_zip_file);
1406d576906SGreg Roach            $filename = addcslashes($download_filename, '"') . '.zip';
1416d576906SGreg Roach
1426d576906SGreg Roach            /** @var ResponseFactoryInterface $response_factory */
1436d576906SGreg Roach            $response_factory = app(ResponseFactoryInterface::class);
1446d576906SGreg Roach
1456d576906SGreg Roach            return $response_factory->createResponse()
1466d576906SGreg Roach                ->withBody($stream)
1476d576906SGreg Roach                ->withHeader('Content-Type', 'application/zip')
1486d576906SGreg Roach                ->withHeader('Content-Disposition', 'attachment; filename="' . $filename . '"');
1496d576906SGreg Roach        }
1506d576906SGreg Roach
1516d576906SGreg Roach        $resource = fopen('php://temp', 'wb+');
1526d576906SGreg Roach        FunctionsExport::exportGedcom($tree, $resource, $access_level, $media_path, $encoding);
1536d576906SGreg Roach        rewind($resource);
1546d576906SGreg Roach
1556d576906SGreg Roach        $charset = $convert ? 'ISO-8859-1' : 'UTF-8';
1566d576906SGreg Roach
1576d576906SGreg Roach        /** @var StreamFactoryInterface $response_factory */
1586d576906SGreg Roach        $stream_factory = app(StreamFactoryInterface::class);
1596d576906SGreg Roach
1606d576906SGreg Roach        $stream = $stream_factory->createStreamFromResource($resource);
1616d576906SGreg Roach
1626d576906SGreg Roach        /** @var ResponseFactoryInterface $response_factory */
1636d576906SGreg Roach        $response_factory = app(ResponseFactoryInterface::class);
1646d576906SGreg Roach
1656d576906SGreg Roach        return $response_factory->createResponse()
1666d576906SGreg Roach            ->withBody($stream)
1676d576906SGreg Roach            ->withHeader('Content-Type', 'text/x-gedcom; charset=' . $charset)
1686d576906SGreg Roach            ->withHeader('Content-Disposition', 'attachment; filename="' . addcslashes($download_filename, '"') . '"');
1696d576906SGreg Roach    }
1706d576906SGreg Roach}
171