Passed
Push — master ( fd5a04...21cd55 )
by Nico
29:44
created

MakeEln::getAuthorId()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 3.004

Importance

Changes 0
Metric Value
cc 3
eloc 11
nc 4
nop 4
dl 0
loc 19
ccs 12
cts 13
cp 0.9231
crap 3.004
rs 9.9
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/**
3
 * @author Nicolas CARPi <[email protected]>
4
 * @copyright 2022 Nicolas CARPi
5
 * @see https://www.elabftw.net Official website
6
 * @license AGPL-3.0
7
 * @package elabftw
8
 */
9
10
namespace Elabftw\Make;
11
12
use DateTimeImmutable;
13
use Elabftw\Exceptions\IllegalActionException;
14
use Elabftw\Models\AbstractEntity;
15
use Elabftw\Models\Config;
16
use League\Flysystem\UnableToReadFile;
0 ignored issues
show
Bug introduced by
The type League\Flysystem\UnableToReadFile was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
use ZipStream\ZipStream;
0 ignored issues
show
Bug introduced by
The type ZipStream\ZipStream was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
19
/**
20
 * Make an ELN archive
21
 */
22
class MakeEln extends MakeStreamZip
23
{
24
    private const HASH_ALGO = 'sha256';
25
26
    protected string $extension = '.eln';
27
28
    private array $authors = array();
29
30
    // name of the folder containing everything
31
    private string $root;
32
33 6
    public function __construct(protected ZipStream $Zip, AbstractEntity $entity, protected array $idArr)
34
    {
35 6
        parent::__construct($Zip, $entity, $idArr);
36 6
        $now = new DateTimeImmutable();
37 6
        $this->root = $now->format('Y-m-d-His') . '-export';
38 6
        $this->jsonArr = array(
39 6
            '@context' => 'https://w3id.org/ro/crate/1.1/context',
40 6
            '@graph' => array(
41 6
                array(
42 6
                    '@id' => 'ro-crate-metadata.json',
43 6
                    '@type' => 'CreativeWork',
44 6
                    'about' => array('@id' => './'),
45 6
                    'conformsTo' => array('@id' => 'https://w3id.org/ro/crate/1.1'),
46 6
                    'dateCreated' => $now->format(DateTimeImmutable::ATOM),
47 6
                    'sdPublisher' => array(
48 6
                        '@type' => 'Organization',
49 6
                        'name' => 'eLabFTW',
50 6
                        'logo' => 'https://www.elabftw.net/img/elabftw-logo-only.svg',
51 6
                        'slogan' => 'A free and open source electronic lab notebook.',
52 6
                        'url' => 'https://www.elabftw.net',
53 6
                        'parentOrganization' => array(
54 6
                          '@type' => 'Organization',
55 6
                          'name' => 'Deltablot',
56 6
                          'logo' => 'https://www.deltablot.com/img/logos/deltablot.svg',
57 6
                          'slogan' => 'Open Source software for research labs.',
58 6
                          'url' => 'https://www.deltablot.com',
59 6
                        ),
60 6
                    ),
61 6
                    'version' => '1.0',
62 6
                ),
63 6
            ),
64 6
        );
65
    }
66
67 2
    public function getFileName(): string
68
    {
69 2
        return $this->root . $this->extension;
70
    }
71
72
    /**
73
     * Loop on each id and add it to our eln archive
74
     */
75 5
    public function getStreamZip(): void
76
    {
77 5
        $dataEntities = array();
78
        // an array of "@id" for main datasets
79 5
        $rootParts = array();
80 5
        foreach ($this->idArr as $id) {
81 5
            $hasPart = array();
82
            try {
83 5
                $this->Entity->setId((int) $id);
84
            } catch (IllegalActionException $e) {
85
                continue;
86
            }
87 5
            $e = $this->Entity->entityData;
88 5
            $currentDatasetFolder = $this->getBaseFileName();
89 5
            $this->folder = $this->root . '/' . $currentDatasetFolder;
90 5
            $rootParts[] = array('@id' => './' . $currentDatasetFolder);
91
92
            // LINKS
93 5
            $mentions = array();
94 5
            foreach ($e['items_links'] as $link) {
95 3
                $mentions[] = array(
96 3
                    '@id' => Config::fromEnv('SITE_URL') . '/database.php?mode=view&id=' . $link['itemid'],
97 3
                    '@type' => 'Dataset',
98 3
                    'name' => $link['category'] . ' - ' . $link['title'],
99 3
                    'identifier' => $link['elabid'],
100 3
                );
101
            }
102 5
            foreach ($e['experiments_links'] as $link) {
103
                $mentions[] = array(
104
                    '@id' => Config::fromEnv('SITE_URL') . '/experiments.php?mode=view&id=' . $link['itemid'],
105
                    '@type' => 'Dataset',
106
                    'name' => $link['category'] . ' - ' . $link['title'],
107
                    'identifier' => $link['elabid'],
108
                );
109
            }
110
111
            // JSON
112 5
            $MakeJson = new MakeJson($this->Entity, array((int) $e['id']));
113 5
            $json = $MakeJson->getFileContent();
114 5
            $this->Zip->addFile($this->folder . '/' . $MakeJson->getFileName(), $json);
115 5
            $jsonAtId = './' . $currentDatasetFolder . '/' . $MakeJson->getFileName();
116
            // add the json in the hasPart of the entry
117 5
            $hasPart[] = array('@id' => $jsonAtId);
118 5
            $dataEntities[] = array(
119 5
                '@id' => $jsonAtId,
120 5
                '@type' => 'File',
121 5
                'description' => 'JSON export',
122 5
                'name' => $MakeJson->getFileName(),
123 5
                'encodingFormat' => $MakeJson->getContentType(),
124 5
                'contentSize' => (string) $MakeJson->getContentSize(),
125 5
                'sha256' => hash('sha256', $json),
126 5
            );
127
128
            // COMMENTS
129 5
            $comments = array();
130 5
            foreach ($e['comments'] as $comment) {
131
                $firstname = $comment['firstname'] ?? '';
132
                $lastname = $comment['lastname'] ?? '';
133
                $dateCreated = (new DateTimeImmutable($e['created_at']))->format(DateTimeImmutable::ATOM);
134
                $comments[] = array(
135
                    '@id' => 'comment://' . urlencode($dateCreated),
136
                    'dateCreated' => $dateCreated,
137
                    'text' => $comment['comment'],
138
                    'author' => array('@id' => $this->getAuthorId($comment['userid'], $firstname, $lastname, $comment['orcid'])),
139
                );
140
            }
141
142
            // UPLOADS
143 5
            $uploadedFilesArr = $e['uploads'];
144 5
            if (!empty($uploadedFilesArr)) {
145
                try {
146
                    // this gets modified by the function so we have the correct real_names
147 4
                    $uploadedFilesArr = $this->addAttachedFiles($uploadedFilesArr);
148
                } catch (UnableToReadFile $e) {
149
                    continue;
150
                }
151 4
                foreach ($uploadedFilesArr as $file) {
152 4
                    $uploadAtId = './' . $currentDatasetFolder . '/' . $file['real_name'];
153 4
                    $hasPart[] = array('@id' => $uploadAtId);
154 4
                    $dataEntities[] = array(
155 4
                        '@id' => $uploadAtId,
156 4
                        '@type' => 'File',
157 4
                        'description' => $file['comment'] ?? '',
158 4
                        'name' => $file['real_name'],
159 4
                        'alternateName' => $file['long_name'],
160 4
                        'contentSize' => $file['filesize'],
161 4
                        'sha256' => $file['hash'] ?? hash_file('sha256', $uploadAtId),
162 4
                    );
163
                }
164
            }
165
166
            // TAGS
167 5
            $keywords = array();
168 5
            if ($this->Entity->entityData['tags']) {
169 5
                $keywords = explode('|', (string) $this->Entity->entityData['tags']);
170
            }
171
172
            // MAIN ENTRY
173 5
            $firstname = $e['firstname'] ?? '';
174 5
            $lastname = $e['lastname'] ?? '';
175 5
            $dataEntities[] = array(
176 5
                '@id' => './' . $currentDatasetFolder,
177 5
                '@type' => 'Dataset',
178 5
                'author' => array('@id' => $this->getAuthorId($e['userid'], $firstname, $lastname, $e['orcid'])),
179 5
                'dateCreated' => (new DateTimeImmutable($e['created_at']))->format(DateTimeImmutable::ATOM),
180 5
                'dateModified' => (new DateTimeImmutable($e['modified_at']))->format(DateTimeImmutable::ATOM),
181 5
                'identifier' => $e['elabid'] ?? '',
182 5
                'comment' => $comments,
183 5
                'keywords' => $keywords,
184 5
                'name' => $e['title'],
185 5
                'text' => $e['body'] ?? '',
186 5
                'url' => Config::fromEnv('SITE_URL') . '/' . $this->Entity->page . '.php?mode=view&id=' . $e['id'],
187 5
                'hasPart' => $hasPart,
188 5
                'mentions' => $mentions,
189 5
            );
190
        }
191
        // add the description of root with hasPart property
192 5
        $dataEntities[] = array(
193 5
            '@id' => './',
194 5
            '@type' => array('Dataset'),
195 5
            'hasPart' => $rootParts,
196 5
        );
197
198
        // merge all, including authors
199 5
        $this->jsonArr['@graph'] = array_merge($this->jsonArr['@graph'], $dataEntities, $this->authors);
200
201
        // add the metadata json file containing references to all the content of our crate
202 5
        $this->Zip->addFile($this->root . '/ro-crate-metadata.json', json_encode($this->jsonArr, JSON_THROW_ON_ERROR, 512));
203 5
        $this->Zip->finish();
204
    }
205
206
    /**
207
     * Generate an author node unless it exists already
208
     */
209 5
    private function getAuthorId(int $userid, string $firstname, string $lastname, ?string $orcid): string
210
    {
211
        // add firstname and lastname to the hash to get more entropy. Use the userid too so similar names won't collide.
212 5
        $id = sprintf('person://%s?hash_algo=%s', hash(self::HASH_ALGO, (string) $userid . $firstname . $lastname), self::HASH_ALGO);
213 5
        $node = array(
214 5
            '@id' => $id,
215 5
            '@type' => 'Person',
216 5
            'familyName' => $lastname,
217 5
            'givenName' => $firstname,
218 5
        );
219
        // only add an identifier property if there is an orcid
220 5
        if ($orcid !== null) {
221
            $node['identifier'] = 'https://orcid.org/' . $orcid;
222
        }
223
        // only add it if it doesn't exist yet in our list of authors
224 5
        if (!in_array($id, array_column($this->authors, '@id'), true)) {
225 5
            $this->authors[] = $node;
226
        }
227 5
        return $id;
228
    }
229
}
230