Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.16% covered (danger)
0.16%
1 / 620
4.35% covered (danger)
4.35%
1 / 23
CRAP
0.00% covered (danger)
0.00%
0 / 1
DevDataUtils
0.16% covered (danger)
0.16%
1 / 620
4.35% covered (danger)
4.35%
1 / 23
6289.85
0.00% covered (danger)
0.00%
0 / 1
 fullResetDb
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 resetDbStructure
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 resetDbContent
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 dropDbTables
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
20
 truncateDbTables
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
20
 addDbStructure
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
42
 addDbContent
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
42
 getCurrentMigration
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 generateMigration
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 migrateTo
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
2
 printDbBackup
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
90
 dumpDb
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 getDbStructureSql
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
2
 getDbContentSql
0.00% covered (danger)
0.00%
0 / 51
0.00% covered (danger)
0.00%
0 / 1
90
 clearFiles
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
6
 addFiles
0.00% covered (danger)
0.00%
0 / 289
0.00% covered (danger)
0.00%
0 / 1
42
 mkdir
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 copy
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 mkimg
0.00% covered (danger)
0.00%
0 / 56
0.00% covered (danger)
0.00%
0 / 1
90
 mklog
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
20
 enqueueForTouch
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 touchEnqueued
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 fromEnv
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace Olz\Utils;
4
5use Ifsnop\Mysqldump\Mysqldump;
6use Symfony\Component\Console\Input\ArrayInput;
7use Symfony\Component\Console\Output\BufferedOutput;
8
9class DevDataUtils {
10    use WithUtilsTrait;
11
12    /** @var array<string> */
13    private array $enqueuedForTouch = [];
14
15    /** DO NOT CALL THIS FUNCTION ON PROD! */
16    public function fullResetDb(): void {
17        if ($this->envUtils()->getAppEnv() === 'prod') {
18            throw new \Exception("Are you fucking insane? You're on prod!");
19        }
20        // Overwrite database with dev content.
21        $this->dropDbTables();
22        $this->addDbStructure();
23        $this->addDbContent();
24        $this->migrateTo('latest');
25
26        // Initialize the non-code data file system at $data_path
27        $this->clearFiles();
28        $this->addFiles();
29    }
30
31    /** DO NOT CALL THIS FUNCTION ON PROD! */
32    public function resetDbStructure(): void {
33        if ($this->envUtils()->getAppEnv() === 'prod') {
34            throw new \Exception("Are you fucking insane? You're on prod!");
35        }
36        // Overwrite database with dev content.
37        $this->dropDbTables();
38        $this->addDbStructure();
39        $this->addDbContent();
40        $this->migrateTo('latest');
41
42        // Initialize the non-code data file system at $data_path
43        $this->addFiles();
44    }
45
46    /** DO NOT CALL THIS FUNCTION ON PROD! */
47    public function resetDbContent(): void {
48        if ($this->envUtils()->getAppEnv() === 'prod') {
49            throw new \Exception("Are you fucking insane? You're on prod!");
50        }
51        $this->truncateDbTables();
52        $this->addDbContent();
53    }
54
55    /** DO NOT CALL THIS FUNCTION ON PROD! */
56    public function dropDbTables(): void {
57        if ($this->envUtils()->getAppEnv() === 'prod') {
58            throw new \Exception("Are you fucking insane? You're on prod!");
59        }
60        $db = $this->dbUtils()->getDb();
61
62        // Remove all database tables.
63        $beg = microtime(true);
64        $result = $db->query("SHOW TABLES");
65        assert(!is_bool($result));
66        $table_names = [];
67        while ($row = $result->fetch_array()) {
68            $table_name = $row[0];
69            $table_names[] = $table_name;
70        }
71        $db->query('SET foreign_key_checks = 0');
72        foreach ($table_names as $table_name) {
73            $sql = "DROP TABLE `{$table_name}`";
74            $db->query($sql);
75        }
76        $db->query('SET foreign_key_checks = 1');
77        $duration = round(microtime(true) - $beg, 3);
78        $this->log()->debug("Dropping took {$duration}s");
79    }
80
81    /** DO NOT CALL THIS FUNCTION ON PROD! */
82    public function truncateDbTables(): void {
83        if ($this->envUtils()->getAppEnv() === 'prod') {
84            throw new \Exception("Are you fucking insane? You're on prod!");
85        }
86        $db = $this->dbUtils()->getDb();
87
88        // Remove all database tables.
89        $beg = microtime(true);
90        $result = $db->query("SHOW TABLES");
91        assert(!is_bool($result));
92        $table_names = [];
93        while ($row = $result->fetch_array()) {
94            $table_name = $row[0];
95            $table_names[] = $table_name;
96        }
97        $db->query('SET foreign_key_checks = 0');
98        foreach ($table_names as $table_name) {
99            $sql = "TRUNCATE TABLE `{$table_name}`";
100            $db->query($sql);
101        }
102        $db->query('SET foreign_key_checks = 1');
103        $duration = round(microtime(true) - $beg, 3);
104        $this->log()->debug("Truncating took {$duration}s");
105    }
106
107    /** DO NOT CALL THIS FUNCTION ON PROD! */
108    public function addDbStructure(): void {
109        if ($this->envUtils()->getAppEnv() === 'prod') {
110            throw new \Exception("Are you fucking insane? You're on prod!");
111        }
112        $db = $this->dbUtils()->getDb();
113        $dev_data_dir = __DIR__.'/data/';
114
115        // Overwrite database structure with dev content.
116        $beg = microtime(true);
117        $sql_content = file_get_contents("{$dev_data_dir}db_structure.sql") ?: '';
118        if ($db->multi_query($sql_content)) {
119            while ($db->next_result()) {
120                $result = $db->store_result();
121                if ($result) {
122                    $result->free();
123                }
124            }
125        }
126        $duration = round(microtime(true) - $beg, 3);
127        $this->log()->debug("Adding structure took {$duration}s");
128    }
129
130    /** DO NOT CALL THIS FUNCTION ON PROD! */
131    public function addDbContent(): void {
132        if ($this->envUtils()->getAppEnv() === 'prod') {
133            throw new \Exception("Are you fucking insane? You're on prod!");
134        }
135        $db = $this->dbUtils()->getDb();
136        $dev_data_dir = __DIR__.'/data/';
137
138        // Insert dev content into database.
139        $beg = microtime(true);
140        $db->query('SET foreign_key_checks = 0');
141        $sql_content = file_get_contents("{$dev_data_dir}db_content.sql") ?: '';
142        if ($db->multi_query($sql_content)) {
143            while ($db->next_result()) {
144                $result = $db->store_result();
145                if ($result) {
146                    $result->free();
147                }
148            }
149        }
150        $db->query('SET foreign_key_checks = 1');
151        $duration = round(microtime(true) - $beg, 3);
152        $this->log()->debug("Adding content took {$duration}s");
153    }
154
155    public function getCurrentMigration(): ?string {
156        $input = new ArrayInput(['--no-interaction' => true]);
157        $input->setInteractive(false);
158        $output = new BufferedOutput();
159        $this->symfonyUtils()->callCommand(
160            'doctrine:migrations:current',
161            $input,
162            $output
163        );
164        $is_match = preg_match('/^\s*([a-zA-Z0-9\\\]+)(\s|$)/', $output->fetch(), $matches);
165        return $is_match ? $matches[1] : null;
166    }
167
168    public function generateMigration(): string {
169        $input = new ArrayInput([
170            '--no-interaction' => true,
171        ]);
172        $input->setInteractive(false);
173        $output = new BufferedOutput();
174        $this->symfonyUtils()->callCommand(
175            'doctrine:migrations:diff',
176            $input,
177            $output
178        );
179        return $output->fetch();
180    }
181
182    public function migrateTo(string $version = 'latest'): string {
183        $input = new ArrayInput([
184            'version' => $version,
185            '--no-interaction' => true,
186        ]);
187        $input->setInteractive(false);
188        $output = new BufferedOutput();
189        $this->symfonyUtils()->callCommand(
190            'doctrine:migrations:migrate',
191            $input,
192            $output
193        );
194        return $output->fetch();
195    }
196
197    public function printDbBackup(string $key): void {
198        if (!$key || strlen($key) < 10) {
199            throw new \Exception("No valid key");
200        }
201
202        $tmp_dir = __DIR__.'/data/tmp/';
203        if (!is_dir($tmp_dir)) {
204            mkdir($tmp_dir);
205        }
206
207        $plain_path = "{$tmp_dir}backup.plain.sql";
208        $plain_fp = fopen($plain_path, 'w+');
209        assert((bool) $plain_fp);
210        fwrite($plain_fp, $this->getDbStructureSql());
211        fwrite($plain_fp, "\n\n----------\n\n\n");
212        fwrite($plain_fp, $this->getDbContentSql());
213        fclose($plain_fp);
214
215        $cipher_path = "{$tmp_dir}backup.cipher.sql";
216        $cipher_fp = fopen($cipher_path, 'w+');
217        assert((bool) $cipher_fp);
218        $algo = 'aes-256-gcm';
219        $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($algo) ?: 0);
220        fwrite($cipher_fp, openssl_encrypt(file_get_contents($plain_path) ?: '', $algo, $key, OPENSSL_RAW_DATA, $iv, $tag) ?: '');
221        fclose($cipher_fp);
222
223        unlink($plain_path);
224
225        echo json_encode([
226            'algo' => $algo,
227            'iv' => base64_encode($iv),
228            'tag' => base64_encode($tag),
229        ]);
230        echo "\n\n";
231
232        $cipher_fp = fopen($cipher_path, 'r');
233        assert((bool) $cipher_fp);
234        while (!feof($cipher_fp)) {
235            $plain = fread($cipher_fp, 57 * 143);
236            $encoded = base64_encode($plain ?: '');
237            $encoded = chunk_split($encoded, 76, "\r\n");
238            echo $encoded;
239        }
240        fclose($cipher_fp);
241
242        unlink($cipher_path);
243    }
244
245    public function dumpDb(): void {
246        $dev_data_dir = __DIR__.'/data/';
247
248        $sql_structure = $this->getDbStructureSql();
249        $sql_content = $this->getDbContentSql();
250        $sql_structure_path = "{$dev_data_dir}db_structure.sql";
251        $sql_content_path = "{$dev_data_dir}db_content.sql";
252        file_put_contents($sql_structure_path, $sql_structure);
253        file_put_contents($sql_content_path, $sql_content);
254    }
255
256    public function getDbStructureSql(): string {
257        $env_utils = $this->envUtils();
258
259        $current_migration = $this->getCurrentMigration();
260        $sql_content = (
261            "-- Die Struktur der Datenbank der Webseite der OL Zimmerberg\n"
262            ."-- MIGRATION: {$current_migration}\n"
263            ."\n"
264        );
265        $dump_filename = tempnam(__DIR__.'/tmp', 'OLZ');
266        $mysql_server = $env_utils->getMysqlServer();
267        $mysql_schema = $env_utils->getMysqlSchema();
268        $dump = new Mysqldump(
269            "mysql:host={$mysql_server};dbname={$mysql_schema}",
270            $env_utils->getMysqlUsername(),
271            $env_utils->getMysqlPassword(),
272            [
273                'skip-comments' => true,
274                'no-data' => true,
275                // This is the only way to exclude all views:
276                'include-views' => [''], // include only a view which does not exist.
277            ],
278        );
279        $dump->start($dump_filename);
280        $sql_content .= file_get_contents($dump_filename);
281        unlink($dump_filename);
282
283        return $sql_content;
284    }
285
286    public function getDbContentSql(): string {
287        $ignored_tables = [
288            'counter' => true,
289            'auth_requests' => true,
290            'messenger_messages' => true,
291        ];
292
293        $db = $this->dbUtils()->getDb();
294        $current_migration = $this->getCurrentMigration();
295        $sql_content = (
296            "-- Der Test-Inhalt der Datenbank der Webseite der OL Zimmerberg\n"
297            ."-- MIGRATION: {$current_migration}\n"
298            ."\n"
299            ."SET SQL_MODE = \"NO_AUTO_VALUE_ON_ZERO\";\n"
300            ."SET AUTOCOMMIT = 0;\n"
301            ."START TRANSACTION;\n"
302            ."SET time_zone = \"+00:00\";\n"
303        );
304
305        $res_tables = $db->query('SHOW TABLES');
306        assert(!is_bool($res_tables));
307        while ($row_tables = $res_tables->fetch_row()) {
308            $table_name = $row_tables[0];
309            $sql_content .= "\n";
310            $sql_content .= "-- Table {$table_name}\n";
311            $res_contents = $db->query("SELECT * FROM `{$table_name}`");
312            assert(!is_bool($res_contents));
313            if ($ignored_tables[$table_name] ?? false) {
314                $sql_content .= "-- ({$table_name} omitted)\n";
315            } elseif ($res_contents->num_rows > 0) {
316                $sql_content .= "INSERT INTO {$table_name}\n";
317                $content_fields = $res_contents->fetch_fields();
318                $field_names = [];
319                foreach ($content_fields as $field) {
320                    $field_names[] = $field->name;
321                }
322                $field_names_sql = implode('`, `', $field_names);
323                $sql_content .= "    (`{$field_names_sql}`)\n";
324                $sql_content .= "VALUES\n";
325                $first = true;
326                while ($row_contents = $res_contents->fetch_assoc()) {
327                    if ($first) {
328                        $first = false;
329                    } else {
330                        $sql_content .= ",\n";
331                    }
332                    $field_values = [];
333                    foreach ($field_names as $name) {
334                        $content = $row_contents[$name] ?? null;
335                        if ($content === null) {
336                            $field_values[] = 'NULL';
337                        } else {
338                            $sane_content = $db->escape_string("{$content}");
339                            $field_values[] = "'{$sane_content}'";
340                        }
341                    }
342                    $field_values_sql = implode(', ', $field_values);
343                    $sql_content .= "    ({$field_values_sql})";
344                }
345                $sql_content .= ";\n";
346            }
347        }
348        $sql_content .= "\n";
349        $sql_content .= "COMMIT;\n";
350        return $sql_content;
351    }
352
353    /** DO NOT CALL THIS FUNCTION ON PROD! */
354    public function clearFiles(): void {
355        if ($this->envUtils()->getAppEnv() === 'prod') {
356            throw new \Exception("Are you fucking insane? You're on prod!");
357        }
358        $data_path = $this->envUtils()->getDataPath();
359        $general_utils = $this->generalUtils();
360
361        // Remove existing data.
362        $general_utils->removeRecursive("{$data_path}downloads");
363        $general_utils->removeRecursive("{$data_path}files");
364        $general_utils->removeRecursive("{$data_path}img");
365        $general_utils->removeRecursive("{$data_path}movies");
366        $general_utils->removeRecursive("{$data_path}olz_mitglieder");
367        $general_utils->removeRecursive("{$data_path}OLZimmerbergAblage");
368        $general_utils->removeRecursive("{$data_path}panini_data");
369        $general_utils->removeRecursive("{$data_path}pdf");
370        $general_utils->removeRecursive("{$data_path}results");
371        $general_utils->removeRecursive("{$data_path}temp");
372    }
373
374    /** DO NOT CALL THIS FUNCTION ON PROD! */
375    public function addFiles(): void {
376        if ($this->envUtils()->getAppEnv() === 'prod') {
377            throw new \Exception("Are you fucking insane? You're on prod!");
378        }
379        $private_path = $this->envUtils()->getPrivatePath();
380        $data_path = $this->envUtils()->getDataPath();
381
382        $sample_path = __DIR__.'/data/sample-data/';
383
384        $this->enqueuedForTouch = [];
385
386        // Build downloads/
387        $this->mkdir("{$data_path}downloads");
388
389        // Build files/
390        $this->mkdir("{$data_path}files");
391        $this->mkdir("{$data_path}files/downloads");
392        $this->mkdir("{$data_path}files/downloads/1");
393        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/downloads/1/MIGRATED0000000000010001.pdf");
394        $this->mkdir("{$data_path}files/downloads/3");
395        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/downloads/3/XV4x94BJaf2JCPWvB8DDqTyt.pdf");
396
397        $this->mkdir("{$data_path}files/news");
398        $this->mkdir("{$data_path}files/news/3");
399        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/news/3/MIGRATED0000000000030001.pdf");
400        $this->mkdir("{$data_path}files/news/4");
401        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/news/4/xMpu3ExjfBKa8Cp35bcmsDgq.pdf");
402        $this->mkdir("{$data_path}files/news/10");
403        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/news/10/gAQa_kYXqXTP1_DKKU1s1pGr.csv");
404        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/news/10/8kCalo9sQtu2mrgrmMjoGLUW.pdf");
405        $this->mkdir("{$data_path}files/news/6403");
406        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/news/6403/MIGRATED0000000064030001.pdf");
407
408        $this->mkdir("{$data_path}files/questions");
409        $this->mkdir("{$data_path}files/questions/1");
410        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/questions/1/4a7J72vVQFrqkboyD358S4cf.pdf");
411
412        $this->mkdir("{$data_path}files/roles");
413        $this->mkdir("{$data_path}files/roles/5");
414        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/roles/5/c44s3s8QjwZd2WYTEVg3iW9k.pdf");
415
416        $this->mkdir("{$data_path}files/snippets");
417        $this->mkdir("{$data_path}files/snippets/24");
418        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/snippets/24/AXfZYP3eyLKTWJmfBRGTua7H.pdf");
419
420        $this->mkdir("{$data_path}files/termine");
421        $this->mkdir("{$data_path}files/termine/2");
422        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/termine/2/MIGRATED0000000000020001.pdf");
423        $this->mkdir("{$data_path}files/termine/5");
424        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/termine/5/MIGRATED0000000000050001.pdf");
425        $this->mkdir("{$data_path}files/termine/7");
426        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/termine/7/Kzt5p5g6cjM5k9CXdVaSsGFx.pdf");
427
428        $this->mkdir("{$data_path}files/termin_labels");
429        $this->mkdir("{$data_path}files/termin_labels/3");
430        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/termin_labels/3/6f6novQPv2fjHGzzguXE6nzi.pdf");
431        $this->mkdir("{$data_path}files/termin_labels/4");
432        $this->copy("{$sample_path}sample-icon_20.svg", "{$data_path}files/termin_labels/4/EM8hA6vye74doeon2RWzZyRf.svg");
433
434        $this->mkdir("{$data_path}files/termin_templates");
435        $this->mkdir("{$data_path}files/termin_templates/2");
436        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/termin_templates/2/qjhUey6Lc6svXsmUcSaguWkJ.pdf");
437
438        // Build img/
439        $this->mkdir("{$data_path}img");
440        $this->mkdir("{$data_path}img/weekly_picture");
441        $this->mkdir("{$data_path}img/weekly_picture/1");
442        $this->mkdir("{$data_path}img/weekly_picture/1/img");
443        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/weekly_picture/1/img/ed48ksmyjVgRsaKXUXmmcbRN.jpg", 800, 600);
444        $this->mkdir("{$data_path}img/weekly_picture/2");
445        $this->mkdir("{$data_path}img/weekly_picture/2/img");
446        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/weekly_picture/2/img/C8k84ncvWyVptk6kjtMJxTUu.jpg", 800, 600);
447        $this->mkdir("{$data_path}img/fuer_einsteiger");
448        $this->mkdir("{$data_path}img/fuer_einsteiger/img");
449        $this->mkdir("{$data_path}img/fuer_einsteiger/thumb");
450        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/orientierungslauf_001.jpg", 800, 600);
451        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/orientierungslauf_002.jpg", 800, 600);
452        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/orientierungslauf_003.jpg", 800, 600);
453        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/orientierungslauf_004.jpg", 800, 600);
454        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/was_ist_ol_001.jpg", 800, 600);
455        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_001.jpg", 800, 600);
456        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_002.jpg", 800, 600);
457        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_003.jpg", 800, 600);
458        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_004.jpg", 800, 600);
459        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_005.jpg", 800, 600);
460        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_006.jpg", 800, 600);
461        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_007.jpg", 800, 600);
462        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_008.jpg", 800, 600);
463        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_009.jpg", 800, 600);
464        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_010.jpg", 800, 600);
465        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_011.jpg", 800, 600);
466        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_012.jpg", 800, 600);
467        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_013.jpg", 800, 600);
468        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_014.jpg", 800, 600);
469        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_015.jpg", 800, 600);
470        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_016.jpg", 800, 600);
471        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/wie_anfangen_001.jpg", 800, 600);
472        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/wie_anfangen_002.jpg", 800, 600);
473        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/wie_anfangen_003.jpg", 800, 600);
474        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/wie_anfangen_004.jpg", 800, 600);
475        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_001.jpg", 800, 600);
476        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_002.jpg", 800, 600);
477        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_003.jpg", 800, 600);
478        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_004.jpg", 800, 600);
479        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_005.jpg", 800, 600);
480        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_006.jpg", 800, 600);
481        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_007.jpg", 800, 600);
482        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_008.jpg", 800, 600);
483        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_009.jpg", 800, 600);
484        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_010.jpg", 800, 600);
485        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_011.jpg", 800, 600);
486        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_012.jpg", 800, 600);
487        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_013.jpg", 800, 600);
488        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_014.jpg", 800, 600);
489        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_015.jpg", 800, 600);
490        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_016.jpg", 800, 600);
491        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/pack_die_chance_001.jpg", 800, 600);
492        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ansprechperson_001.jpg", 800, 600);
493        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ansprechperson_002.jpg", 800, 600);
494        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ansprechperson_003.jpg", 800, 600);
495        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ansprechperson_004.jpg", 800, 600);
496
497        // Generate thumbs
498        if (function_exists('shell_exec')) {
499            $thumbize_src = __DIR__."/../../tools/fuer_einsteiger/thumbize.sh";
500            $thumbize_dest = "{$data_path}img/fuer_einsteiger/thumbize.sh";
501            if (is_file($thumbize_dest)) {
502                unlink($thumbize_dest);
503            }
504            $this->copy($thumbize_src, $thumbize_dest);
505            $pwd = getcwd();
506            chdir("{$data_path}img/fuer_einsteiger");
507            shell_exec("sh ./thumbize.sh");
508            if ($pwd) {
509                chdir($pwd);
510            }
511        }
512
513        $this->mkdir("{$data_path}img/karten");
514        $this->mkdir("{$data_path}img/karten/1");
515        $this->mkdir("{$data_path}img/karten/1/img");
516        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/karten/1/img/MIGRATED0000000000010001.jpg", 800, 600);
517        $this->mkdir("{$data_path}img/karten/3");
518        $this->mkdir("{$data_path}img/karten/3/img");
519        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/karten/3/img/6R3bpgwcCU3SfUF8vCpepzRJ.jpg", 800, 600);
520
521        $this->mkdir("{$data_path}img/news");
522        $this->mkdir("{$data_path}img/news/3");
523        $this->mkdir("{$data_path}img/news/3/img");
524        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/3/img/MIGRATED0000000000030001.jpg", 800, 600);
525        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/3/img/MIGRATED0000000000030002.jpg", 800, 600);
526        $this->mkdir("{$data_path}img/news/4");
527        $this->mkdir("{$data_path}img/news/4/img");
528        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/4/img/xkbGJQgO5LFXpTSz2dCnvJzu.jpg", 800, 600);
529        $this->mkdir("{$data_path}img/news/6");
530        $this->mkdir("{$data_path}img/news/6/img");
531        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/6/img/eGbiJQgOyLF5p6S92kC3vTzE.jpg", 600, 800);
532        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/6/img/Frw83uTOyLF5p6S92kC7zpEW.jpg", 800, 600);
533        $this->mkdir("{$data_path}img/news/7");
534        $this->mkdir("{$data_path}img/news/7/img");
535        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/7/img/aRJIflbxtkF5p6S92k470912.jpg", 800, 600);
536        $this->mkdir("{$data_path}img/news/8");
537        $this->mkdir("{$data_path}img/news/8/img");
538        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/8/img/9GjbtlsSu96AWZ-oH0rHjxup.jpg", 800, 600);
539        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/8/img/zUXE3aKfbK3edmqS35FhaF8g.jpg", 800, 600);
540        $this->mkdir("{$data_path}img/news/10");
541        $this->mkdir("{$data_path}img/news/10/img");
542        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/10/img/DvDB8QkHcGuxQ4lAFwyvHnVd.jpg", 800, 600);
543        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/10/img/OOVJIqrWlitR_iTZuIIhztKC.jpg", 800, 600);
544        $this->mkdir("{$data_path}img/news/1201");
545        $this->mkdir("{$data_path}img/news/1201/img");
546        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1201/img/MIGRATED0000000012010001.jpg", 800, 600);
547        $this->mkdir("{$data_path}img/news/1202");
548        $this->mkdir("{$data_path}img/news/1202/img");
549        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1202/img/MIGRATED0000000012020001.jpg", 800, 600);
550        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1202/img/MIGRATED0000000012020002.jpg", 800, 600);
551        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1202/img/MIGRATED0000000012020003.jpg", 600, 800);
552        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1202/img/MIGRATED0000000012020004.jpg", 800, 600);
553        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1202/img/MIGRATED0000000012020005.jpg", 800, 300);
554        $this->mkdir("{$data_path}img/news/1203");
555        $this->mkdir("{$data_path}img/news/1203/img");
556        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1203/img/MIGRATED0000000012030001.jpg", 800, 600);
557        $this->mkdir("{$data_path}img/news/1203/thumb");
558        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1203/thumb/MIGRATED0000000012030001.jpg\$128.jpg", 128, 96);
559        $this->mkdir("{$data_path}img/news/1206");
560        $this->mkdir("{$data_path}img/news/1206/img");
561        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1206/img/MIGRATED0000000012060001.jpg", 800, 600);
562        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1206/img/MIGRATED0000000012060002.jpg", 800, 600);
563        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1206/img/MIGRATED0000000012060003.jpg", 800, 600);
564        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1206/img/MIGRATED0000000012060004.jpg", 800, 600);
565        $this->mkdir("{$data_path}img/news/1206/thumb");
566        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1206/thumb/MIGRATED0000000012060001.jpg\$128.jpg", 128, 96);
567        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1206/thumb/MIGRATED0000000012060002.jpg\$128.jpg", 128, 96);
568        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1206/thumb/MIGRATED0000000012060003.jpg\$128.jpg", 128, 96);
569        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1206/thumb/MIGRATED0000000012060004.jpg\$128.jpg", 128, 96);
570        $this->mkdir("{$data_path}img/news/6401");
571        $this->mkdir("{$data_path}img/news/6401/img");
572        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/6401/img/MIGRATED0000000064010001.jpg", 800, 600);
573        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/6401/img/MIGRATED0000000064010002.jpg", 800, 600);
574        $this->mkdir("{$data_path}img/news/6403");
575        $this->mkdir("{$data_path}img/news/6403/img");
576        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/6403/img/MIGRATED0000000064030001.jpg", 800, 600);
577
578        $this->mkdir("{$data_path}img/questions");
579        $this->mkdir("{$data_path}img/questions/1");
580        $this->mkdir("{$data_path}img/questions/1/img");
581        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/questions/1/img/bKfDuE4hocQhbw9FGMD7WCW3.jpg", 800, 600);
582
583        $this->mkdir("{$data_path}img/roles");
584        $this->mkdir("{$data_path}img/roles/5");
585        $this->mkdir("{$data_path}img/roles/5/img");
586        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/roles/5/img/ZntVatFCHj3h8KZh7LyiB9x5.jpg", 800, 600);
587
588        $this->mkdir("{$data_path}img/snippets");
589        $this->mkdir("{$data_path}img/snippets/24");
590        $this->mkdir("{$data_path}img/snippets/24/img");
591        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/snippets/24/img/oCGvpb96V6bZNLoQNe8djJgw.jpg", 800, 600);
592
593        $this->mkdir("{$data_path}img/termine");
594        $this->mkdir("{$data_path}img/termine/5");
595        $this->mkdir("{$data_path}img/termine/5/img");
596        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/termine/5/img/Ffpi3PK5wBjKfN4etpvGK3ti.jpg", 800, 600);
597        $this->mkdir("{$data_path}img/termine/10");
598        $this->mkdir("{$data_path}img/termine/10/img");
599        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/termine/10/img/659gCbqzigX8D37XgWMbedB3.jpg", 800, 600);
600        $this->mkdir("{$data_path}img/termine/13");
601        $this->mkdir("{$data_path}img/termine/13/img");
602        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/termine/13/img/8EKYh2n8DZWShYMWo9ZRnor5.jpg", 800, 600);
603
604        $this->mkdir("{$data_path}img/termin_labels");
605        $this->mkdir("{$data_path}img/termin_labels/3");
606        $this->mkdir("{$data_path}img/termin_labels/3/img");
607        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/termin_labels/3/img/QQ8ZApZjsNSBM2wKrkRQxXZG.jpg", 800, 600);
608
609        $this->mkdir("{$data_path}img/termin_locations");
610        $this->mkdir("{$data_path}img/termin_locations/1");
611        $this->mkdir("{$data_path}img/termin_locations/1/img");
612        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/termin_locations/1/img/2ZiW6T9biPNjEERzj5xjLRDz.jpg", 800, 600);
613
614        $this->mkdir("{$data_path}img/termin_templates");
615        $this->mkdir("{$data_path}img/termin_templates/2");
616        $this->mkdir("{$data_path}img/termin_templates/2/img");
617        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/termin_templates/2/img/bv3KeYVKDJNg3MTyjhSQsDRx.jpg", 800, 600);
618
619        $this->mkdir("{$data_path}img/users");
620        $this->mkdir("{$data_path}img/users/1");
621        $this->mkdir("{$data_path}img/users/1/img");
622        $this->mkdir("{$data_path}img/users/1/thumb");
623        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/users/1/img/8sVwnV3aAEtQUUxmQYFmojMs.jpg", 300, 300);
624        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/users/1/thumb/8sVwnV3aAEtQUUxmQYFmojMs.jpg\$256.jpg", 256, 256);
625        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/users/1/thumb/8sVwnV3aAEtQUUxmQYFmojMs.jpg\$128.jpg", 128, 128);
626        $this->mkdir("{$data_path}img/users/3");
627        $this->mkdir("{$data_path}img/users/3/img");
628        $this->mkdir("{$data_path}img/users/3/thumb");
629        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/users/3/img/oyLeyPTaCfmadcm5ShEJ236e.jpg", 150, 150);
630        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/users/3/thumb/oyLeyPTaCfmadcm5ShEJ236e.jpg\$128.jpg", 128, 128);
631
632        // Build movies/
633        $this->mkdir("{$data_path}movies");
634
635        // Build olz_mitglieder/
636        $this->mkdir("{$data_path}olz_mitglieder");
637        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "olz_mitglieder/max_muster.jpg", 84, 120);
638
639        // Build OLZimmerbergAblage/
640        $this->mkdir("{$data_path}OLZimmerbergAblage");
641        $dokumente_path = "{$data_path}OLZimmerbergAblage/OLZ Dokumente";
642        $this->mkdir("{$dokumente_path}");
643        $this->mkdir("{$dokumente_path}/vorstand");
644        $this->copy("{$sample_path}sample-document.pdf", "{$dokumente_path}/vorstand/mitgliederliste.pdf");
645        $this->mkdir("{$dokumente_path}/vorstand/protokolle");
646        $this->copy("{$sample_path}sample-document.pdf", "{$dokumente_path}/vorstand/protokolle/protokoll.pdf");
647        $this->mkdir("{$dokumente_path}/karten");
648        $this->copy("{$sample_path}sample-document.pdf", "{$dokumente_path}/karten/uebersicht.pdf");
649        $this->mkdir("{$dokumente_path}/karten/wald");
650        $this->copy("{$sample_path}sample-document.pdf", "{$dokumente_path}/karten/wald/buchstabenwald.pdf");
651
652        // Build panini_data/
653        $this->mkdir("{$data_path}panini_data");
654        $this->mkdir("{$data_path}panini_data/cache");
655        $this->mkdir("{$data_path}panini_data/fonts");
656        $this->mkdir("{$data_path}panini_data/fonts/OpenSans");
657        $this->copy("{$sample_path}sample-font.ttf", "{$data_path}panini_data/fonts/OpenSans/OpenSans-SemiBold.ttf");
658        $this->copy("{$sample_path}sample-font.php", "{$data_path}panini_data/fonts/OpenSans/OpenSans-SemiBold.php");
659        $this->copy("{$sample_path}sample-font.z", "{$data_path}panini_data/fonts/OpenSans/OpenSans-SemiBold.z");
660        $this->mkdir("{$data_path}panini_data/masks");
661        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/topP_1517x2091.png", 1517, 2091);
662        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/bottomP_1517x2091.png", 1517, 2091);
663        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/associationP_1517x2091.png", 1517, 2091);
664        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/masks/associationStencilP_1517x2091.png", 1517, 2091);
665        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/topP_1594x2303.png", 1594, 2303);
666        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/topL_2303x1594.png", 2303, 1594);
667        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/bottomP_1594x2303.png", 1594, 2303);
668        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/bottomL_2303x1594.png", 2303, 1594);
669        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/associationP_1594x2303.png", 1594, 2303);
670        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/associationL_2303x1594.png", 2303, 1594);
671        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/masks/associationStencilP_1594x2303.png", 1594, 2303);
672        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/masks/associationStencilL_2303x1594.png", 2303, 1594);
673        $this->mkdir("{$data_path}panini_data/wappen");
674        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/wappen/thalwil.jpg", 100, 100);
675        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/wappen/other.jpg", 100, 100);
676        $this->mkdir("{$data_path}panini_data/portraits");
677        $this->mkdir("{$data_path}panini_data/portraits/1001");
678        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/portraits/1001/vptD8fzvXIhv_6X32Zkw2s5s.jpg", 800, 600);
679        $this->mkdir("{$data_path}panini_data/portraits/1002");
680        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/portraits/1002/LkGdXukqgYEdnWpuFHfrJkr7.jpg", 800, 600);
681        $this->mkdir("{$data_path}panini_data/other");
682        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/other/portrait.jpg", 600, 800);
683        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/other/landscape.jpg", 800, 600);
684        for ($i = 1003; $i <= 1012; $i++) {
685            $this->mkdir("{$data_path}panini_data/portraits/{$i}");
686            $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/portraits/{$i}/LkGdXukqgYEdnWpuFHfrJkr7.jpg", 800, 600);
687        }
688
689        // Build pdf/
690        $this->mkdir("{$data_path}pdf");
691        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}pdf/trainingsprogramm.pdf");
692
693        // Build results/
694        $this->mkdir("{$data_path}results");
695        $this->copy("{$sample_path}sample-results.xml", "{$data_path}results/results.xml");
696        $this->copy("{$sample_path}sample-results.xml", "{$data_path}results/2020-termine-7.xml");
697
698        // Build temp/
699        $this->mkdir("{$data_path}temp");
700
701        // Build logs/
702        $this->mkdir("{$private_path}logs");
703        $this->mklog("{$private_path}logs/merged-2020-08-13.log", "2020-08-13");
704        $this->mklog("{$private_path}logs/merged-2020-08-14.log", "2020-08-14");
705        $this->mklog("{$private_path}logs/merged-2020-08-15.log", "2020-08-15");
706        $this->mkdir("{$private_path}logs/server");
707        $this->mklog("{$private_path}logs/server/access_ssl_log", "2020-08-15");
708        $this->mklog("{$private_path}logs/server/access_ssl_log.processed", "2020-08-14");
709        $this->mklog("{$private_path}logs/server/access_ssl_log.processed.1", "2020-08-13");
710        $this->mklog("{$private_path}logs/server/access_ssl_log.processed.2", "2020-08-12");
711
712        // Build sessions
713        $this->mkdir("{$private_path}sessions");
714
715        $this->touchEnqueued(1584118800);
716    }
717
718    protected function mkdir(string $path, int $mode = 0o777, bool $recursive = false): void {
719        if (!is_dir($path)) {
720            mkdir($path, $mode, $recursive);
721        }
722        $this->enqueueForTouch($path);
723    }
724
725    protected function copy(string $source, string $dest): void {
726        if (!is_file($dest)) {
727            copy($source, $dest);
728        }
729        $this->enqueueForTouch($dest);
730    }
731
732    /**
733     * @param int<1, max> $width
734     * @param int<1, max> $height
735     */
736    protected function mkimg(
737        string $source_path,
738        string $data_path,
739        string $destination_relative_path,
740        int $width,
741        int $height,
742    ): void {
743        $destination_path = "{$data_path}{$destination_relative_path}";
744        if (is_file($destination_path)) {
745            return;
746        }
747        $tmp_dir = __DIR__.'/data/tmp/';
748        if (!is_dir($tmp_dir)) {
749            mkdir($tmp_dir);
750        }
751        $flat_destination_relative_path = str_replace('/', '___', $destination_relative_path);
752        $extension_pos = strrpos($flat_destination_relative_path, '.') ?: 0;
753        $ident = substr($flat_destination_relative_path, 0, $extension_pos);
754        $extension = substr($flat_destination_relative_path, $extension_pos);
755        $tmp_basename = "{$ident}___{$width}x{$height}{$extension}";
756        $tmp_path = "{$tmp_dir}{$tmp_basename}";
757        if (!is_file($tmp_path)) {
758            $info = getimagesize($source_path);
759            $source_width = $info[0] ?? 0;
760            $source_height = $info[1] ?? 0;
761            $image_type = $info[2] ?? 0;
762            $source = null;
763            if ($image_type === 2) {
764                $source = imagecreatefromjpeg($source_path);
765            } elseif ($image_type === 3) {
766                $source = imagecreatefrompng($source_path);
767            }
768            if (!$source) {
769                throw new \Exception("mkimg: Image must be JPEG or PNG, was: {$image_type}");
770            }
771            $destination = imagecreatetruecolor($width, $height);
772            imagealphablending($destination, false);
773            imagesavealpha($destination, true);
774            imagecopyresampled(
775                $destination,
776                $source,
777                0,
778                0,
779                0,
780                0,
781                $width,
782                $height,
783                $source_width,
784                $source_height,
785            );
786            $red = imagecolorallocate($destination, 255, 0, 0);
787            assert($red !== false);
788            $hash = intval(substr(md5($destination_relative_path), 0, 1), 16);
789            $x = floor($hash / 4) * $width / 4;
790            $y = floor($hash % 4) * $height / 4;
791            imagefilledrectangle(
792                $destination,
793                intval(round($x)),
794                intval(round($y)),
795                intval(round($x + $width / 4)),
796                intval(round($y + $height / 4)),
797                $red
798            );
799            if (preg_match('/\.jpg$/', $destination_relative_path)) {
800                imagejpeg($destination, $tmp_path, 90);
801            } else {
802                imagepng($destination, $tmp_path);
803            }
804            imagedestroy($destination);
805        }
806        $this->copy($tmp_path, $destination_path);
807    }
808
809    protected function mklog(string $file_path, string $iso_date): void {
810        $log_levels = [
811            'DEBUG',
812            'INFO',
813            'NOTICE',
814            'WARNING',
815            'ERROR',
816            'CRITICAL',
817            'ALERT',
818            'EMERGENCY',
819        ];
820        $num_log_levels = count($log_levels);
821        $fp = fopen($file_path, 'w+');
822        assert($fp !== false);
823        $long_line = 'Wow,';
824        for ($i = 0; $i < 1000; $i++) {
825            $long_line .= ' so much content';
826        }
827        for ($i = 0; $i < 1440; $i++) {
828            $time = str_pad(strval(floor($i / 60)), 2, '0', STR_PAD_LEFT).':'.
829                str_pad(strval(floor($i % 60)), 2, '0', STR_PAD_LEFT).':'.
830                str_pad(strval(random_int(0, 59)), 2, '0', STR_PAD_LEFT).'.'.
831                str_pad(strval(random_int(0, 999999)), 6, '0', STR_PAD_LEFT);
832            $level = $log_levels[$i % $num_log_levels];
833            $fill_up = ($i % ($num_log_levels + 1)) === 0 ? $long_line : '';
834            $line = "[{$iso_date}T{$time}+01:00] Command:ProcessEmail.{$level}: Something happened... {$fill_up} [] []\n";
835            fwrite($fp, $line);
836        }
837        fclose($fp);
838    }
839
840    protected function enqueueForTouch(string $path): void {
841        $this->enqueuedForTouch[] = $path;
842    }
843
844    protected function touchEnqueued(?int $timestamp): void {
845        foreach ($this->enqueuedForTouch as $path) {
846            touch($path, $timestamp, $timestamp);
847        }
848    }
849
850    public static function fromEnv(): self {
851        return new self();
852    }
853}