Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 661
0.00% covered (danger)
0.00%
0 / 25
CRAP
0.00% covered (danger)
0.00%
0 / 1
DevDataUtils
0.00% covered (danger)
0.00%
0 / 661
0.00% covered (danger)
0.00%
0 / 25
7656
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 / 40
0.00% covered (danger)
0.00%
0 / 1
110
 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 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getDbStructureSqlFile
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
6
 getDbContentSql
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getDbContentSqlFile
0.00% covered (danger)
0.00%
0 / 66
0.00% covered (danger)
0.00%
0 / 1
110
 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
 tmpdir
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 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 / 55
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
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 = $this->tmpdir();
203        $plain_path = "{$tmp_dir}backup.plain.sql";
204        $plain_fp = fopen($plain_path, 'w+');
205        assert((bool) $plain_fp);
206
207        $structure_path = $this->getDbStructureSqlFile();
208        $structure_fp = fopen($structure_path, 'r');
209        assert((bool) $structure_fp);
210        while ($chunk = fread($structure_fp, 1024)) {
211            fwrite($plain_fp, $chunk);
212        }
213        fclose($structure_fp);
214
215        fwrite($plain_fp, "\n\n----------\n\n\n");
216
217        $content_path = $this->getDbContentSqlFile();
218        $content_fp = fopen($content_path, 'r');
219        assert((bool) $content_fp);
220        while ($chunk = fread($content_fp, 1024)) {
221            fwrite($plain_fp, $chunk);
222        }
223        fclose($content_fp);
224
225        fclose($plain_fp);
226        unlink($structure_path);
227        unlink($content_path);
228
229        $cipher_path = "{$tmp_dir}backup.cipher.sql";
230        $cipher_fp = fopen($cipher_path, 'w+');
231        assert((bool) $cipher_fp);
232        $algo = 'aes-256-gcm';
233        $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($algo) ?: 0);
234        fwrite($cipher_fp, openssl_encrypt(file_get_contents($plain_path) ?: '', $algo, $key, OPENSSL_RAW_DATA, $iv, $tag) ?: '');
235        fclose($cipher_fp);
236
237        unlink($plain_path);
238
239        echo json_encode([
240            'algo' => $algo,
241            'iv' => base64_encode($iv),
242            'tag' => base64_encode($tag),
243        ]);
244        echo "\n\n";
245
246        $cipher_fp = fopen($cipher_path, 'r');
247        assert((bool) $cipher_fp);
248        while (!feof($cipher_fp)) {
249            $plain = fread($cipher_fp, 57 * 143);
250            $encoded = base64_encode($plain ?: '');
251            $encoded = chunk_split($encoded, 76, "\r\n");
252            echo $encoded;
253        }
254        fclose($cipher_fp);
255
256        unlink($cipher_path);
257    }
258
259    public function dumpDb(): void {
260        $dev_data_dir = __DIR__.'/data/';
261
262        $sql_structure = $this->getDbStructureSql();
263        $sql_content = $this->getDbContentSql();
264        $sql_structure_path = "{$dev_data_dir}db_structure.sql";
265        $sql_content_path = "{$dev_data_dir}db_content.sql";
266        file_put_contents($sql_structure_path, $sql_structure);
267        file_put_contents($sql_content_path, $sql_content);
268    }
269
270    public function getDbStructureSql(): string {
271        $path = $this->getDbStructureSqlFile();
272        $content = file_get_contents($path) ?: '';
273        unlink($path);
274        return $content;
275    }
276
277    public function getDbStructureSqlFile(): string {
278        $tmp_dir = $this->tmpdir();
279        $env_utils = $this->envUtils();
280
281        $path_tmp = "{$tmp_dir}structure_tmp.sql";
282        $mysql_server = $env_utils->getMysqlServer();
283        $mysql_schema = $env_utils->getMysqlSchema();
284        $dump = new Mysqldump(
285            "mysql:host={$mysql_server};dbname={$mysql_schema}",
286            $env_utils->getMysqlUsername(),
287            $env_utils->getMysqlPassword(),
288            [
289                'skip-comments' => true,
290                'no-data' => true,
291                // This is the only way to exclude all views:
292                'include-views' => [''], // include only a view which does not exist.
293            ],
294        );
295        $dump->start($path_tmp);
296
297        $path = "{$tmp_dir}structure.sql";
298        $fp = fopen($path, 'w+');
299        assert((bool) $fp);
300
301        $current_migration = $this->getCurrentMigration();
302        fwrite(
303            $fp,
304            "-- Die Struktur der Datenbank der Webseite der OL Zimmerberg\n"
305            ."-- MIGRATION: {$current_migration}\n"
306            ."\n"
307        );
308        $fp_tmp = fopen($path_tmp, 'r');
309        assert((bool) $fp_tmp);
310        while ($chunk = fread($fp_tmp, 1024)) {
311            fwrite($fp, $chunk);
312        }
313        fclose($fp_tmp);
314        fclose($fp);
315        unlink($path_tmp);
316
317        return $path;
318    }
319
320    public function getDbContentSql(): string {
321        $path = $this->getDbContentSqlFile();
322        $content = file_get_contents($path) ?: '';
323        unlink($path);
324        return $content;
325    }
326
327    public function getDbContentSqlFile(): string {
328        $ignored_tables = [
329            'counter' => true,
330            'auth_requests' => true,
331            'messenger_messages' => true,
332        ];
333        $batch_size = 100;
334
335        $tmp_dir = $this->tmpdir();
336        $db = $this->dbUtils()->getDb();
337        $current_migration = $this->getCurrentMigration();
338
339        $path = "{$tmp_dir}content.sql";
340        $fp = fopen($path, 'w+');
341        assert((bool) $fp);
342
343        fwrite(
344            $fp,
345            "-- Der Test-Inhalt der Datenbank der Webseite der OL Zimmerberg\n"
346            ."-- MIGRATION: {$current_migration}\n"
347            ."\n"
348            ."SET SQL_MODE = \"NO_AUTO_VALUE_ON_ZERO\";\n"
349            ."SET AUTOCOMMIT = 0;\n"
350            ."START TRANSACTION;\n"
351            ."SET time_zone = \"+00:00\";\n"
352        );
353
354        $res_tables = $db->query('SHOW TABLES');
355        assert(!is_bool($res_tables));
356        while ($row_tables = $res_tables->fetch_row()) {
357            $table_name = $row_tables[0];
358            fwrite($fp, "\n");
359            fwrite($fp, "-- Table {$table_name}\n");
360            if ($ignored_tables[$table_name] ?? false) {
361                fwrite($fp, "-- ({$table_name} omitted)\n");
362                continue;
363            }
364            $offset = 0;
365            while (true) {
366                $res_contents = $db->query("SELECT * FROM `{$table_name}` LIMIT {$offset},{$batch_size}");
367                assert(!is_bool($res_contents));
368                if ($res_contents->num_rows === 0) {
369                    unset($res_contents);
370                    gc_collect_cycles();
371                    break;
372                }
373                fwrite($fp, "INSERT INTO {$table_name}\n");
374                $content_fields = $res_contents->fetch_fields();
375                $field_names = [];
376                foreach ($content_fields as $field) {
377                    $field_names[] = $field->name;
378                }
379                $field_names_sql = implode('`, `', $field_names);
380                fwrite($fp, "    (`{$field_names_sql}`)\n");
381                fwrite($fp, "VALUES\n");
382                $first = true;
383                while ($row_contents = $res_contents->fetch_assoc()) {
384                    if ($first) {
385                        $first = false;
386                    } else {
387                        fwrite($fp, ",\n");
388                    }
389                    $field_values = [];
390                    foreach ($field_names as $name) {
391                        $content = $row_contents[$name] ?? null;
392                        if ($content === null) {
393                            $field_values[] = 'NULL';
394                        } else {
395                            $sane_content = $db->escape_string("{$content}");
396                            $field_values[] = "'{$sane_content}'";
397                        }
398                    }
399                    $field_values_sql = implode(', ', $field_values);
400                    fwrite($fp, "    ({$field_values_sql})");
401                }
402                fwrite($fp, ";\n");
403                $offset += $batch_size;
404                unset($res_contents);
405                gc_collect_cycles();
406            }
407        }
408        fwrite($fp, "\n");
409        fwrite($fp, "COMMIT;\n");
410        fclose($fp);
411        return $path;
412    }
413
414    /** DO NOT CALL THIS FUNCTION ON PROD! */
415    public function clearFiles(): void {
416        if ($this->envUtils()->getAppEnv() === 'prod') {
417            throw new \Exception("Are you fucking insane? You're on prod!");
418        }
419        $data_path = $this->envUtils()->getDataPath();
420        $general_utils = $this->generalUtils();
421
422        // Remove existing data.
423        $general_utils->removeRecursive("{$data_path}downloads");
424        $general_utils->removeRecursive("{$data_path}files");
425        $general_utils->removeRecursive("{$data_path}img");
426        $general_utils->removeRecursive("{$data_path}movies");
427        $general_utils->removeRecursive("{$data_path}olz_mitglieder");
428        $general_utils->removeRecursive("{$data_path}OLZimmerbergAblage");
429        $general_utils->removeRecursive("{$data_path}panini_data");
430        $general_utils->removeRecursive("{$data_path}pdf");
431        $general_utils->removeRecursive("{$data_path}results");
432        $general_utils->removeRecursive("{$data_path}temp");
433    }
434
435    /** DO NOT CALL THIS FUNCTION ON PROD! */
436    public function addFiles(): void {
437        if ($this->envUtils()->getAppEnv() === 'prod') {
438            throw new \Exception("Are you fucking insane? You're on prod!");
439        }
440        $private_path = $this->envUtils()->getPrivatePath();
441        $data_path = $this->envUtils()->getDataPath();
442
443        $sample_path = __DIR__.'/data/sample-data/';
444
445        $this->enqueuedForTouch = [];
446
447        // Build downloads/
448        $this->mkdir("{$data_path}downloads");
449
450        // Build files/
451        $this->mkdir("{$data_path}files");
452        $this->mkdir("{$data_path}files/downloads");
453        $this->mkdir("{$data_path}files/downloads/1");
454        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/downloads/1/MIGRATED0000000000010001.pdf");
455        $this->mkdir("{$data_path}files/downloads/3");
456        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/downloads/3/XV4x94BJaf2JCPWvB8DDqTyt.pdf");
457
458        $this->mkdir("{$data_path}files/news");
459        $this->mkdir("{$data_path}files/news/3");
460        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/news/3/MIGRATED0000000000030001.pdf");
461        $this->mkdir("{$data_path}files/news/4");
462        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/news/4/xMpu3ExjfBKa8Cp35bcmsDgq.pdf");
463        $this->mkdir("{$data_path}files/news/10");
464        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/news/10/gAQa_kYXqXTP1_DKKU1s1pGr.csv");
465        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/news/10/8kCalo9sQtu2mrgrmMjoGLUW.pdf");
466        $this->mkdir("{$data_path}files/news/6403");
467        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/news/6403/MIGRATED0000000064030001.pdf");
468
469        $this->mkdir("{$data_path}files/questions");
470        $this->mkdir("{$data_path}files/questions/1");
471        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/questions/1/4a7J72vVQFrqkboyD358S4cf.pdf");
472
473        $this->mkdir("{$data_path}files/roles");
474        $this->mkdir("{$data_path}files/roles/5");
475        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/roles/5/c44s3s8QjwZd2WYTEVg3iW9k.pdf");
476
477        $this->mkdir("{$data_path}files/snippets");
478        $this->mkdir("{$data_path}files/snippets/24");
479        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/snippets/24/AXfZYP3eyLKTWJmfBRGTua7H.pdf");
480
481        $this->mkdir("{$data_path}files/termine");
482        $this->mkdir("{$data_path}files/termine/2");
483        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/termine/2/MIGRATED0000000000020001.pdf");
484        $this->mkdir("{$data_path}files/termine/5");
485        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/termine/5/MIGRATED0000000000050001.pdf");
486        $this->mkdir("{$data_path}files/termine/7");
487        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/termine/7/Kzt5p5g6cjM5k9CXdVaSsGFx.pdf");
488
489        $this->mkdir("{$data_path}files/termin_labels");
490        $this->mkdir("{$data_path}files/termin_labels/3");
491        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/termin_labels/3/6f6novQPv2fjHGzzguXE6nzi.pdf");
492        $this->mkdir("{$data_path}files/termin_labels/4");
493        $this->copy("{$sample_path}sample-icon_20.svg", "{$data_path}files/termin_labels/4/EM8hA6vye74doeon2RWzZyRf.svg");
494
495        $this->mkdir("{$data_path}files/termin_templates");
496        $this->mkdir("{$data_path}files/termin_templates/2");
497        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}files/termin_templates/2/qjhUey6Lc6svXsmUcSaguWkJ.pdf");
498
499        // Build img/
500        $this->mkdir("{$data_path}img");
501        $this->mkdir("{$data_path}img/weekly_picture");
502        $this->mkdir("{$data_path}img/weekly_picture/1");
503        $this->mkdir("{$data_path}img/weekly_picture/1/img");
504        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/weekly_picture/1/img/ed48ksmyjVgRsaKXUXmmcbRN.jpg", 800, 600);
505        $this->mkdir("{$data_path}img/weekly_picture/2");
506        $this->mkdir("{$data_path}img/weekly_picture/2/img");
507        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/weekly_picture/2/img/C8k84ncvWyVptk6kjtMJxTUu.jpg", 800, 600);
508        $this->mkdir("{$data_path}img/fuer_einsteiger");
509        $this->mkdir("{$data_path}img/fuer_einsteiger/img");
510        $this->mkdir("{$data_path}img/fuer_einsteiger/thumb");
511        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/orientierungslauf_001.jpg", 800, 600);
512        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/orientierungslauf_002.jpg", 800, 600);
513        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/orientierungslauf_003.jpg", 800, 600);
514        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/orientierungslauf_004.jpg", 800, 600);
515        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/was_ist_ol_001.jpg", 800, 600);
516        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_001.jpg", 800, 600);
517        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_002.jpg", 800, 600);
518        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_003.jpg", 800, 600);
519        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_004.jpg", 800, 600);
520        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_005.jpg", 800, 600);
521        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_006.jpg", 800, 600);
522        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_007.jpg", 800, 600);
523        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_008.jpg", 800, 600);
524        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_009.jpg", 800, 600);
525        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_010.jpg", 800, 600);
526        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_011.jpg", 800, 600);
527        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_012.jpg", 800, 600);
528        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_013.jpg", 800, 600);
529        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_014.jpg", 800, 600);
530        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_015.jpg", 800, 600);
531        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ol_zimmerberg_016.jpg", 800, 600);
532        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/wie_anfangen_001.jpg", 800, 600);
533        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/wie_anfangen_002.jpg", 800, 600);
534        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/wie_anfangen_003.jpg", 800, 600);
535        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/wie_anfangen_004.jpg", 800, 600);
536        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_001.jpg", 800, 600);
537        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_002.jpg", 800, 600);
538        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_003.jpg", 800, 600);
539        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_004.jpg", 800, 600);
540        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_005.jpg", 800, 600);
541        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_006.jpg", 800, 600);
542        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_007.jpg", 800, 600);
543        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_008.jpg", 800, 600);
544        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_009.jpg", 800, 600);
545        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_010.jpg", 800, 600);
546        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_011.jpg", 800, 600);
547        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_012.jpg", 800, 600);
548        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_013.jpg", 800, 600);
549        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_014.jpg", 800, 600);
550        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_015.jpg", 800, 600);
551        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/trainings_016.jpg", 800, 600);
552        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/pack_die_chance_001.jpg", 800, 600);
553        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ansprechperson_001.jpg", 800, 600);
554        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ansprechperson_002.jpg", 800, 600);
555        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ansprechperson_003.jpg", 800, 600);
556        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/fuer_einsteiger/img/ansprechperson_004.jpg", 800, 600);
557
558        // Generate thumbs
559        if (function_exists('shell_exec')) {
560            $thumbize_src = __DIR__."/../../tools/fuer_einsteiger/thumbize.sh";
561            $thumbize_dest = "{$data_path}img/fuer_einsteiger/thumbize.sh";
562            if (is_file($thumbize_dest)) {
563                unlink($thumbize_dest);
564            }
565            $this->copy($thumbize_src, $thumbize_dest);
566            $pwd = getcwd();
567            chdir("{$data_path}img/fuer_einsteiger");
568            shell_exec("sh ./thumbize.sh");
569            if ($pwd) {
570                chdir($pwd);
571            }
572        }
573
574        $this->mkdir("{$data_path}img/karten");
575        $this->mkdir("{$data_path}img/karten/1");
576        $this->mkdir("{$data_path}img/karten/1/img");
577        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/karten/1/img/MIGRATED0000000000010001.jpg", 800, 600);
578        $this->mkdir("{$data_path}img/karten/3");
579        $this->mkdir("{$data_path}img/karten/3/img");
580        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/karten/3/img/6R3bpgwcCU3SfUF8vCpepzRJ.jpg", 800, 600);
581
582        $this->mkdir("{$data_path}img/news");
583        $this->mkdir("{$data_path}img/news/3");
584        $this->mkdir("{$data_path}img/news/3/img");
585        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/3/img/MIGRATED0000000000030001.jpg", 800, 600);
586        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/3/img/MIGRATED0000000000030002.jpg", 800, 600);
587        $this->mkdir("{$data_path}img/news/4");
588        $this->mkdir("{$data_path}img/news/4/img");
589        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/4/img/xkbGJQgO5LFXpTSz2dCnvJzu.jpg", 800, 600);
590        $this->mkdir("{$data_path}img/news/6");
591        $this->mkdir("{$data_path}img/news/6/img");
592        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/6/img/eGbiJQgOyLF5p6S92kC3vTzE.jpg", 600, 800);
593        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/6/img/Frw83uTOyLF5p6S92kC7zpEW.jpg", 800, 600);
594        $this->mkdir("{$data_path}img/news/7");
595        $this->mkdir("{$data_path}img/news/7/img");
596        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/7/img/aRJIflbxtkF5p6S92k470912.jpg", 800, 600);
597        $this->mkdir("{$data_path}img/news/8");
598        $this->mkdir("{$data_path}img/news/8/img");
599        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/8/img/9GjbtlsSu96AWZ-oH0rHjxup.jpg", 800, 600);
600        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/8/img/zUXE3aKfbK3edmqS35FhaF8g.jpg", 800, 600);
601        $this->mkdir("{$data_path}img/news/10");
602        $this->mkdir("{$data_path}img/news/10/img");
603        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/10/img/DvDB8QkHcGuxQ4lAFwyvHnVd.jpg", 800, 600);
604        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/10/img/OOVJIqrWlitR_iTZuIIhztKC.jpg", 800, 600);
605        $this->mkdir("{$data_path}img/news/1201");
606        $this->mkdir("{$data_path}img/news/1201/img");
607        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1201/img/MIGRATED0000000012010001.jpg", 800, 600);
608        $this->mkdir("{$data_path}img/news/1202");
609        $this->mkdir("{$data_path}img/news/1202/img");
610        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1202/img/MIGRATED0000000012020001.jpg", 800, 600);
611        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1202/img/MIGRATED0000000012020002.jpg", 800, 600);
612        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1202/img/MIGRATED0000000012020003.jpg", 600, 800);
613        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1202/img/MIGRATED0000000012020004.jpg", 800, 600);
614        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1202/img/MIGRATED0000000012020005.jpg", 800, 300);
615        $this->mkdir("{$data_path}img/news/1203");
616        $this->mkdir("{$data_path}img/news/1203/img");
617        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1203/img/MIGRATED0000000012030001.jpg", 800, 600);
618        $this->mkdir("{$data_path}img/news/1203/thumb");
619        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1203/thumb/MIGRATED0000000012030001.jpg\$128.jpg", 128, 96);
620        $this->mkdir("{$data_path}img/news/1206");
621        $this->mkdir("{$data_path}img/news/1206/img");
622        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1206/img/MIGRATED0000000012060001.jpg", 800, 600);
623        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1206/img/MIGRATED0000000012060002.jpg", 800, 600);
624        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1206/img/MIGRATED0000000012060003.jpg", 800, 600);
625        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1206/img/MIGRATED0000000012060004.jpg", 800, 600);
626        $this->mkdir("{$data_path}img/news/1206/thumb");
627        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1206/thumb/MIGRATED0000000012060001.jpg\$128.jpg", 128, 96);
628        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1206/thumb/MIGRATED0000000012060002.jpg\$128.jpg", 128, 96);
629        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1206/thumb/MIGRATED0000000012060003.jpg\$128.jpg", 128, 96);
630        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/1206/thumb/MIGRATED0000000012060004.jpg\$128.jpg", 128, 96);
631        $this->mkdir("{$data_path}img/news/6401");
632        $this->mkdir("{$data_path}img/news/6401/img");
633        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/6401/img/MIGRATED0000000064010001.jpg", 800, 600);
634        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/6401/img/MIGRATED0000000064010002.jpg", 800, 600);
635        $this->mkdir("{$data_path}img/news/6403");
636        $this->mkdir("{$data_path}img/news/6403/img");
637        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/news/6403/img/MIGRATED0000000064030001.jpg", 800, 600);
638
639        $this->mkdir("{$data_path}img/questions");
640        $this->mkdir("{$data_path}img/questions/1");
641        $this->mkdir("{$data_path}img/questions/1/img");
642        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/questions/1/img/bKfDuE4hocQhbw9FGMD7WCW3.jpg", 800, 600);
643
644        $this->mkdir("{$data_path}img/roles");
645        $this->mkdir("{$data_path}img/roles/5");
646        $this->mkdir("{$data_path}img/roles/5/img");
647        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/roles/5/img/ZntVatFCHj3h8KZh7LyiB9x5.jpg", 800, 600);
648
649        $this->mkdir("{$data_path}img/snippets");
650        $this->mkdir("{$data_path}img/snippets/24");
651        $this->mkdir("{$data_path}img/snippets/24/img");
652        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/snippets/24/img/oCGvpb96V6bZNLoQNe8djJgw.jpg", 800, 600);
653
654        $this->mkdir("{$data_path}img/termine");
655        $this->mkdir("{$data_path}img/termine/5");
656        $this->mkdir("{$data_path}img/termine/5/img");
657        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/termine/5/img/Ffpi3PK5wBjKfN4etpvGK3ti.jpg", 800, 600);
658        $this->mkdir("{$data_path}img/termine/10");
659        $this->mkdir("{$data_path}img/termine/10/img");
660        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/termine/10/img/659gCbqzigX8D37XgWMbedB3.jpg", 800, 600);
661        $this->mkdir("{$data_path}img/termine/13");
662        $this->mkdir("{$data_path}img/termine/13/img");
663        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/termine/13/img/8EKYh2n8DZWShYMWo9ZRnor5.jpg", 800, 600);
664
665        $this->mkdir("{$data_path}img/termin_labels");
666        $this->mkdir("{$data_path}img/termin_labels/3");
667        $this->mkdir("{$data_path}img/termin_labels/3/img");
668        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/termin_labels/3/img/QQ8ZApZjsNSBM2wKrkRQxXZG.jpg", 800, 600);
669
670        $this->mkdir("{$data_path}img/termin_locations");
671        $this->mkdir("{$data_path}img/termin_locations/1");
672        $this->mkdir("{$data_path}img/termin_locations/1/img");
673        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/termin_locations/1/img/2ZiW6T9biPNjEERzj5xjLRDz.jpg", 800, 600);
674
675        $this->mkdir("{$data_path}img/termin_templates");
676        $this->mkdir("{$data_path}img/termin_templates/2");
677        $this->mkdir("{$data_path}img/termin_templates/2/img");
678        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/termin_templates/2/img/bv3KeYVKDJNg3MTyjhSQsDRx.jpg", 800, 600);
679
680        $this->mkdir("{$data_path}img/users");
681        $this->mkdir("{$data_path}img/users/1");
682        $this->mkdir("{$data_path}img/users/1/img");
683        $this->mkdir("{$data_path}img/users/1/thumb");
684        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/users/1/img/8sVwnV3aAEtQUUxmQYFmojMs.jpg", 300, 300);
685        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/users/1/thumb/8sVwnV3aAEtQUUxmQYFmojMs.jpg\$256.jpg", 256, 256);
686        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/users/1/thumb/8sVwnV3aAEtQUUxmQYFmojMs.jpg\$128.jpg", 128, 128);
687        $this->mkdir("{$data_path}img/users/3");
688        $this->mkdir("{$data_path}img/users/3/img");
689        $this->mkdir("{$data_path}img/users/3/thumb");
690        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/users/3/img/oyLeyPTaCfmadcm5ShEJ236e.jpg", 150, 150);
691        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "img/users/3/thumb/oyLeyPTaCfmadcm5ShEJ236e.jpg\$128.jpg", 128, 128);
692
693        // Build movies/
694        $this->mkdir("{$data_path}movies");
695
696        // Build olz_mitglieder/
697        $this->mkdir("{$data_path}olz_mitglieder");
698        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "olz_mitglieder/max_muster.jpg", 84, 120);
699
700        // Build OLZimmerbergAblage/
701        $this->mkdir("{$data_path}OLZimmerbergAblage");
702        $dokumente_path = "{$data_path}OLZimmerbergAblage/OLZ Dokumente";
703        $this->mkdir("{$dokumente_path}");
704        $this->mkdir("{$dokumente_path}/vorstand");
705        $this->copy("{$sample_path}sample-document.pdf", "{$dokumente_path}/vorstand/mitgliederliste.pdf");
706        $this->mkdir("{$dokumente_path}/vorstand/protokolle");
707        $this->copy("{$sample_path}sample-document.pdf", "{$dokumente_path}/vorstand/protokolle/protokoll.pdf");
708        $this->mkdir("{$dokumente_path}/karten");
709        $this->copy("{$sample_path}sample-document.pdf", "{$dokumente_path}/karten/uebersicht.pdf");
710        $this->mkdir("{$dokumente_path}/karten/wald");
711        $this->copy("{$sample_path}sample-document.pdf", "{$dokumente_path}/karten/wald/buchstabenwald.pdf");
712
713        // Build panini_data/
714        $this->mkdir("{$data_path}panini_data");
715        $this->mkdir("{$data_path}panini_data/cache");
716        $this->mkdir("{$data_path}panini_data/fonts");
717        $this->mkdir("{$data_path}panini_data/fonts/OpenSans");
718        $this->copy("{$sample_path}sample-font.ttf", "{$data_path}panini_data/fonts/OpenSans/OpenSans-SemiBold.ttf");
719        $this->copy("{$sample_path}sample-font.php", "{$data_path}panini_data/fonts/OpenSans/OpenSans-SemiBold.php");
720        $this->copy("{$sample_path}sample-font.z", "{$data_path}panini_data/fonts/OpenSans/OpenSans-SemiBold.z");
721        $this->mkdir("{$data_path}panini_data/masks");
722        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/topP_1517x2091.png", 1517, 2091);
723        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/bottomP_1517x2091.png", 1517, 2091);
724        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/associationP_1517x2091.png", 1517, 2091);
725        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/masks/associationStencilP_1517x2091.png", 1517, 2091);
726        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/topP_1594x2303.png", 1594, 2303);
727        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/topL_2303x1594.png", 2303, 1594);
728        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/bottomP_1594x2303.png", 1594, 2303);
729        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/bottomL_2303x1594.png", 2303, 1594);
730        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/associationP_1594x2303.png", 1594, 2303);
731        $this->mkimg("{$sample_path}sample-mask.png", $data_path, "panini_data/masks/associationL_2303x1594.png", 2303, 1594);
732        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/masks/associationStencilP_1594x2303.png", 1594, 2303);
733        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/masks/associationStencilL_2303x1594.png", 2303, 1594);
734        $this->mkdir("{$data_path}panini_data/wappen");
735        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/wappen/thalwil.jpg", 100, 100);
736        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/wappen/other.jpg", 100, 100);
737        $this->mkdir("{$data_path}panini_data/portraits");
738        $this->mkdir("{$data_path}panini_data/portraits/1001");
739        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/portraits/1001/vptD8fzvXIhv_6X32Zkw2s5s.jpg", 800, 600);
740        $this->mkdir("{$data_path}panini_data/portraits/1002");
741        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/portraits/1002/LkGdXukqgYEdnWpuFHfrJkr7.jpg", 800, 600);
742        $this->mkdir("{$data_path}panini_data/other");
743        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/other/portrait.jpg", 600, 800);
744        $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/other/landscape.jpg", 800, 600);
745        for ($i = 1003; $i <= 1012; $i++) {
746            $this->mkdir("{$data_path}panini_data/portraits/{$i}");
747            $this->mkimg("{$sample_path}sample-picture.jpg", $data_path, "panini_data/portraits/{$i}/LkGdXukqgYEdnWpuFHfrJkr7.jpg", 800, 600);
748        }
749
750        // Build pdf/
751        $this->mkdir("{$data_path}pdf");
752        $this->copy("{$sample_path}sample-document.pdf", "{$data_path}pdf/trainingsprogramm.pdf");
753
754        // Build results/
755        $this->mkdir("{$data_path}results");
756        $this->copy("{$sample_path}sample-results.xml", "{$data_path}results/results.xml");
757        $this->copy("{$sample_path}sample-results.xml", "{$data_path}results/2020-termine-7.xml");
758
759        // Build temp/
760        $this->mkdir("{$data_path}temp");
761
762        // Build logs/
763        $this->mkdir("{$private_path}logs");
764        $this->mklog("{$private_path}logs/merged-2020-08-13.log", "2020-08-13");
765        $this->mklog("{$private_path}logs/merged-2020-08-14.log", "2020-08-14");
766        $this->mklog("{$private_path}logs/merged-2020-08-15.log", "2020-08-15");
767        $this->mkdir("{$private_path}logs/server");
768        $this->mklog("{$private_path}logs/server/access_ssl_log", "2020-08-15");
769        $this->mklog("{$private_path}logs/server/access_ssl_log.processed", "2020-08-14");
770        $this->mklog("{$private_path}logs/server/access_ssl_log.processed.1", "2020-08-13");
771        $this->mklog("{$private_path}logs/server/access_ssl_log.processed.2", "2020-08-12");
772
773        // Build sessions
774        $this->mkdir("{$private_path}sessions");
775
776        $this->touchEnqueued(1584118800);
777    }
778
779    protected function tmpdir(): string {
780        $tmp_dir = __DIR__.'/data/tmp/';
781        if (!is_dir($tmp_dir)) {
782            mkdir($tmp_dir);
783        }
784        return $tmp_dir;
785    }
786
787    protected function mkdir(string $path, int $mode = 0o777, bool $recursive = false): void {
788        if (!is_dir($path)) {
789            mkdir($path, $mode, $recursive);
790        }
791        $this->enqueueForTouch($path);
792    }
793
794    protected function copy(string $source, string $dest): void {
795        if (!is_file($dest)) {
796            copy($source, $dest);
797        }
798        $this->enqueueForTouch($dest);
799    }
800
801    /**
802     * @param int<1, max> $width
803     * @param int<1, max> $height
804     */
805    protected function mkimg(
806        string $source_path,
807        string $data_path,
808        string $destination_relative_path,
809        int $width,
810        int $height,
811    ): void {
812        $destination_path = "{$data_path}{$destination_relative_path}";
813        if (is_file($destination_path)) {
814            return;
815        }
816        $tmp_dir = __DIR__.'/data/tmp/';
817        if (!is_dir($tmp_dir)) {
818            mkdir($tmp_dir);
819        }
820        $flat_destination_relative_path = str_replace('/', '___', $destination_relative_path);
821        $extension_pos = strrpos($flat_destination_relative_path, '.') ?: 0;
822        $ident = substr($flat_destination_relative_path, 0, $extension_pos);
823        $extension = substr($flat_destination_relative_path, $extension_pos);
824        $tmp_basename = "{$ident}___{$width}x{$height}{$extension}";
825        $tmp_path = "{$tmp_dir}{$tmp_basename}";
826        if (!is_file($tmp_path)) {
827            $info = getimagesize($source_path);
828            $source_width = $info[0] ?? 0;
829            $source_height = $info[1] ?? 0;
830            $image_type = $info[2] ?? 0;
831            $source = null;
832            if ($image_type === 2) {
833                $source = imagecreatefromjpeg($source_path);
834            } elseif ($image_type === 3) {
835                $source = imagecreatefrompng($source_path);
836            }
837            if (!$source) {
838                throw new \Exception("mkimg: Image must be JPEG or PNG, was: {$image_type}");
839            }
840            $destination = imagecreatetruecolor($width, $height);
841            imagealphablending($destination, false);
842            imagesavealpha($destination, true);
843            imagecopyresampled(
844                $destination,
845                $source,
846                0,
847                0,
848                0,
849                0,
850                $width,
851                $height,
852                $source_width,
853                $source_height,
854            );
855            $red = imagecolorallocate($destination, 255, 0, 0);
856            assert($red !== false);
857            $hash = intval(substr(md5($destination_relative_path), 0, 1), 16);
858            $x = floor($hash / 4) * $width / 4;
859            $y = floor($hash % 4) * $height / 4;
860            imagefilledrectangle(
861                $destination,
862                intval(round($x)),
863                intval(round($y)),
864                intval(round($x + $width / 4)),
865                intval(round($y + $height / 4)),
866                $red
867            );
868            if (preg_match('/\.jpg$/', $destination_relative_path)) {
869                imagejpeg($destination, $tmp_path, 90);
870            } else {
871                imagepng($destination, $tmp_path);
872            }
873        }
874        $this->copy($tmp_path, $destination_path);
875    }
876
877    protected function mklog(string $file_path, string $iso_date): void {
878        $log_levels = [
879            'DEBUG',
880            'INFO',
881            'NOTICE',
882            'WARNING',
883            'ERROR',
884            'CRITICAL',
885            'ALERT',
886            'EMERGENCY',
887        ];
888        $num_log_levels = count($log_levels);
889        $fp = fopen($file_path, 'w+');
890        assert($fp !== false);
891        $long_line = 'Wow,';
892        for ($i = 0; $i < 1000; $i++) {
893            $long_line .= ' so much content';
894        }
895        for ($i = 0; $i < 1440; $i++) {
896            $time = str_pad(strval(floor($i / 60)), 2, '0', STR_PAD_LEFT).':'.
897                str_pad(strval(floor($i % 60)), 2, '0', STR_PAD_LEFT).':'.
898                str_pad(strval(random_int(0, 59)), 2, '0', STR_PAD_LEFT).'.'.
899                str_pad(strval(random_int(0, 999999)), 6, '0', STR_PAD_LEFT);
900            $level = $log_levels[$i % $num_log_levels];
901            $fill_up = ($i % ($num_log_levels + 1)) === 0 ? $long_line : '';
902            $line = "[{$iso_date}T{$time}+01:00] Command:ProcessEmail.{$level}: Something happened... {$fill_up} [] []\n";
903            fwrite($fp, $line);
904        }
905        fclose($fp);
906    }
907
908    protected function enqueueForTouch(string $path): void {
909        $this->enqueuedForTouch[] = $path;
910    }
911
912    protected function touchEnqueued(?int $timestamp): void {
913        foreach ($this->enqueuedForTouch as $path) {
914            touch($path, $timestamp, $timestamp);
915        }
916    }
917}