diff --git a/cache/.gitignore b/cache/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/mapg b/mapg index 6409a95..8acfda2 100755 --- a/mapg +++ b/mapg @@ -7,5 +7,6 @@ $app = new Symfony\Component\Console\Application('MapGuesser Console', ''); $app->add(new MapGuesser\Cli\DatabaseMigration()); $app->add(new MapGuesser\Cli\AddUserCommand()); +$app->add(new MapGuesser\Cli\LinkViewCommand()); $app->run(); diff --git a/public/index.php b/public/index.php index 24595aa..0a7084d 100644 --- a/public/index.php +++ b/public/index.php @@ -47,5 +47,6 @@ if ($match !== null) { } } +$content = new MapGuesser\Response\HtmlContent('error/404'); header('Content-Type: text/html; charset=UTF-8', true, 404); -require ROOT . '/views/error/404.php'; +echo $content->render(); diff --git a/public/static/css/mapguesser.css b/public/static/css/mapguesser.css index ed8b4f7..c00b5ab 100644 --- a/public/static/css/mapguesser.css +++ b/public/static/css/mapguesser.css @@ -17,6 +17,11 @@ button::-moz-focus-inner, input::-moz-focus-inner { border: 0; } +/* to be compatible with browsers that don't know
*/ +main { + display: block; +} + ::selection { background-color: #28a745; color: #ffffff; @@ -324,10 +329,11 @@ main { padding: 6px 12px; } -div.full { +main.full { position: relative; width: 100%; height: calc(100% - 40px); + padding: 0; } footer { diff --git a/scripts/install.sh b/scripts/install.sh index 4d6044d..d6191c5 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -21,6 +21,9 @@ echo "Migrating DB..." if [ -z "${DEV}" ] || [ "${DEV}" -eq "0" ]; then echo "Minifying JS, CSS and SVG files..." ${ROOT_DIR}/scripts/minify.sh + + echo "Linking view files..." + (cd ${ROOT_DIR} && ./mapg view:link) fi touch ${ROOT_DIR}/installed diff --git a/scripts/update.sh b/scripts/update.sh index 006ec90..8c44bc4 100755 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -20,4 +20,7 @@ echo "Migrating DB..." if [ -z "${DEV}" ] || [ "${DEV}" -eq "0" ]; then echo "Minifying JS, CSS and SVG files..." ${ROOT_DIR}/scripts/minify.sh + + echo "Linking view files..." + (cd ${ROOT_DIR} && ./mapg view:link) fi diff --git a/src/Cli/LinkViewCommand.php b/src/Cli/LinkViewCommand.php new file mode 100644 index 0000000..cccd74e --- /dev/null +++ b/src/Cli/LinkViewCommand.php @@ -0,0 +1,69 @@ +setName('view:link') + ->setDescription('Linking of views.') + ->addArgument('view', InputArgument::OPTIONAL, 'View file to be linked.'); + } + + public function execute(InputInterface $input, OutputInterface $output): int + { + $views = []; + + $view = $input->getArgument('view'); + + if ($view !== null) { + $views[] = $view; + } else { + $folder = ROOT . '/views'; + $folderLength = strlen($folder) + 1; + + $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($folder, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST); + + foreach ($iterator as $file) { + if ($file->isDir() || $file->getExtension() !== 'php') { + continue; + } + + $view = substr($file->getPath(), $folderLength) . '/' . $file->getBasename('.php'); + + if (strpos($view, 'templates') === 0) { + continue; + } + + $views[] = $view; + } + } + + try { + foreach ($views as $view) { + $generator = new Linker($view); + $generator->generate(); + } + } catch (\Exception $e) { + $output->writeln('Linking view(s) failed!'); + $output->writeln(''); + + $output->writeln((string) $e); + $output->writeln(''); + + return 1; + } + + $output->writeln('View(s) successfully linked!'); + + return 0; + } +} diff --git a/src/Response/HtmlContent.php b/src/Response/HtmlContent.php index 0ad813a..8664cf8 100644 --- a/src/Response/HtmlContent.php +++ b/src/Response/HtmlContent.php @@ -1,21 +1,28 @@ template = $template; + $this->view = $view; $this->data = &$data; } public function &render(): string { + if (!empty($_ENV['DEV'])) { + $generator = new Linker($this->view); + $generator->generate(); + } + extract($this->data); ob_start(); - require ROOT . '/views/' . $this->template . '.php'; + require ROOT . '/cache/views/' . $this->view . '.php'; $content = ob_get_contents(); ob_end_clean(); diff --git a/src/View/Linker.php b/src/View/Linker.php new file mode 100644 index 0000000..360d69c --- /dev/null +++ b/src/View/Linker.php @@ -0,0 +1,103 @@ +view = $view; + } + + public function generate(): void + { + $input = ROOT . '/views/' . $this->view . '.php'; + + $temporaryFiles = []; + $sections = []; + $extra = ['', '']; + + do { + $parser = new Parser($input); + $fragment = $parser->parse(); + + $extends = $fragment->getExtends(); + + $sections = array_merge($sections, $fragment->getSections()); //TODO: detect if section defined multiple times + $extra[0] = $fragment->getExtra()[0] . $extra[0]; + $extra[1] = $extra[1] . $fragment->getExtra()[1]; + + if ($extends === null) { + $this->writeFinal($extra, $input, ROOT . '/cache/views/' . $this->view . '.php'); + break; + } + + $tmpFile = tempnam(sys_get_temp_dir(), 'mapg-view-'); + $temporaryFiles[] = $tmpFile; + + $this->extendTemplate($sections, ROOT . '/views/' . $extends . '.php', $tmpFile); + + $input = $tmpFile; + } while (true); + + foreach ($temporaryFiles as $tmpFile) { + unlink($tmpFile); + } + } + + private function extendTemplate(array $sections, string $file, string $output): void + { + $inputFileHandle = fopen($file, 'r'); + if (!$inputFileHandle) { + throw new \Exception('Cannot open file ' . $file); + } + + $outputFileHandle = fopen($output, 'w'); + if (!$outputFileHandle) { + throw new \Exception('Cannot open file ' . $output . 'for writing.'); + } + + $lineNumber = 0; + while (($line = fgets($inputFileHandle)) !== false) { + ++$lineNumber; + + if (preg_match('/^\s*@yields\(\'([\w\/]+)\'\)\s*$/', $line, $matches)) { + if (isset($sections[$matches[1]])) { + fwrite($outputFileHandle, $sections[$matches[1]]); + } + } else { + fwrite($outputFileHandle, $line); + } + } + + fclose($inputFileHandle); + fclose($outputFileHandle); + } + + private function writeFinal(array $extra, string $file, string $output): void + { + $dirname = pathinfo($output, PATHINFO_DIRNAME); + if (!is_dir($dirname)) { + mkdir($dirname, 0755, true); + } + + $inputFileHandle = fopen($file, 'r'); + if (!$inputFileHandle) { + throw new \Exception('Cannot open file ' . $file); + } + + $outputFileHandle = fopen($output, 'w'); + if (!$outputFileHandle) { + throw new \Exception('Cannot open file ' . $output . 'for writing.'); + } + + fwrite($outputFileHandle, $extra[0]); + while (($line = fgets($inputFileHandle)) !== false) { + fwrite($outputFileHandle, $line); + } + fwrite($outputFileHandle, $extra[1]); + + fclose($inputFileHandle); + fclose($outputFileHandle); + } +} diff --git a/src/View/ParsedFragment.php b/src/View/ParsedFragment.php new file mode 100644 index 0000000..52bbb5b --- /dev/null +++ b/src/View/ParsedFragment.php @@ -0,0 +1,32 @@ +extends = $extends; + $this->sections = $sections; + $this->extra = $extra; + } + + public function getExtends(): ?string + { + return $this->extends; + } + + public function getSections(): array + { + return $this->sections; + } + + public function getExtra(): array + { + return $this->extra; + } +} diff --git a/src/View/Parser.php b/src/View/Parser.php new file mode 100644 index 0000000..6cbde82 --- /dev/null +++ b/src/View/Parser.php @@ -0,0 +1,128 @@ +file = $file; + } + + public function parse(): ParsedFragment + { + + $sectionOpen = null; + $extraOpen = false; + + $extends = null; + $sections = []; + $extra = ['', '']; + + $fileHandle = fopen($this->file, 'r'); + if (!$fileHandle) { + throw new \Exception('Cannot open file ' . $this->file); + } + + $lineNumber = 0; + while (($line = fgets($fileHandle)) !== false) { + ++$lineNumber; + + if (($extendsMatched = $this->matchExtends($line)) !== null) { + if ($extends !== null) { + throw new \Exception('Error in file ' . $this->file . ' in line ' . $lineNumber . ' - There is already an \'@extends\' declared.'); + } + + $extends = $extendsMatched; + + continue; + } + + if (($sectionMatched = $this->matchSection($line)) !== null) { + if ($extends === null) { + throw new \Exception('Error in file ' . $this->file . ' in line ' . $lineNumber . ' - \'@section\' has no meaning if view extends nothing.'); + } + if ($sectionOpen !== null) { + throw new \Exception('Parse error in file ' . $this->file . ' in line ' . $lineNumber . ' - A \'@section\' is already open (no \'@endsection\' found).'); + } + + $sectionOpen = $sectionMatched; + $sections[$sectionOpen] = ''; + + continue; + } + + if ($this->matchEndSection($line)) { + if ($sectionOpen === null) { + throw new \Exception('Parse error in file ' . $this->file . ' in line ' . $lineNumber . ' - Cannot end section until no \'@section\' is open.'); + } + + $sectionOpen = null; + } + + if ($this->matchExtra($line)) { + if ($extraOpen) { + throw new \Exception('Parse error in file ' . $this->file . ' in line ' . $lineNumber . ' - An \'@extra\' is already open (no \'@endextra\' found).'); + } + + $extraOpen = true; + + continue; + } + + if ($this->matchEndExtra($line)) { + if (!$extraOpen) { + throw new \Exception('Parse error in file ' . $this->file . ' in line ' . $lineNumber . ' - Cannot end extra until no \'@extra\' is open.'); + } + + $extraOpen = false; + } + + if ($sectionOpen !== null) { + $sections[$sectionOpen] .= $line; + } + + if ($extraOpen) { + $offset = $extends === null ? 0 : 1; + $extra[$offset] .= $line; + } + } + + fclose($fileHandle); + + return new ParsedFragment($extends, $sections, $extra); + } + + private function matchExtends(string $line): ?string + { + if (preg_match('/^\s*@extends\(\'([\w\/]+)\'\)\s*$/', $line, $matches)) { + return $matches[1]; + } + + return null; + } + + private function matchSection(string $line): ?string + { + if (preg_match('/^\s*@section\(\'(\w+)\'\)\s*$/', $line, $matches)) { + return $matches[1]; + } + + return null; + } + + private function matchEndSection(string $line): bool + { + return preg_match('/^\s*@endsection(?:\(\))?\s*$/', $line); + } + + private function matchExtra(string $line): bool + { + return preg_match('/^\s*@extra(?:\(\))?\s*$/', $line); + } + + private function matchEndExtra(string $line): bool + { + return preg_match('/^\s*@endextra(?:\(\))?\s*$/', $line); + } +} diff --git a/views/account/account.php b/views/account/account.php index dd3c698..f6186b6 100644 --- a/views/account/account.php +++ b/views/account/account.php @@ -1,5 +1,6 @@ - - +@extends('templates/layout_normal') + +@section('main')

Account

@@ -19,5 +20,4 @@
- - \ No newline at end of file +@endsection diff --git a/views/account/delete.php b/views/account/delete.php index 75deec5..1b190e7 100644 --- a/views/account/delete.php +++ b/views/account/delete.php @@ -1,5 +1,6 @@ - - +@extends('templates/layout_normal') + +@section('main')

Delete account

@@ -11,5 +12,4 @@
- - \ No newline at end of file +@endsection diff --git a/views/admin/map_editor.php b/views/admin/map_editor.php index 0e58851..1ca983f 100644 --- a/views/admin/map_editor.php +++ b/views/admin/map_editor.php @@ -1,3 +1,4 @@ +@extra - -
-

- - - -

-

- @@ -46,8 +43,9 @@ $jsFiles = [ 0 -

-
+@endsection + +@section('pagemodal') -
+@endsection + +@section('main')
@@ -73,7 +73,9 @@ $jsFiles = [
-
+@endsection + +@section('pagescript') - \ No newline at end of file +@endsection diff --git a/views/error/404.php b/views/error/404.php index f50146f..7564012 100644 --- a/views/error/404.php +++ b/views/error/404.php @@ -1,6 +1,6 @@ - - +@extends('templates/layout_normal') + +@section('main')

404 | Page not found

The requested URL was not found on this server. Back to start.

- - \ No newline at end of file +@endsection diff --git a/views/game.php b/views/game.php index 8d971fd..bc4c63a 100644 --- a/views/game.php +++ b/views/game.php @@ -1,3 +1,4 @@ +@extra - -
-

- - - -

-

+@endextra + +@extends('templates/layout_full') + +@section('subheader') Round Score -

-
-
+@endsection + +@section('main')
@@ -56,9 +53,11 @@ $jsFiles = [
- +@endsection + +@section('pagescript') - \ No newline at end of file +@endsection diff --git a/views/login/activate.php b/views/login/activate.php index 428ca72..e28c707 100644 --- a/views/login/activate.php +++ b/views/login/activate.php @@ -1,8 +1,8 @@ - - +@extends('templates/layout_normal') + +@section('main')

Account activation

Activation failed. Please check the link you entered or retry sign up!

- - \ No newline at end of file +@endsection diff --git a/views/login/cancel.php b/views/login/cancel.php index 2f37e59..980c125 100644 --- a/views/login/cancel.php +++ b/views/login/cancel.php @@ -1,5 +1,6 @@ - - +@extends('templates/layout_normal') + +@section('main')

Account cancellation

@@ -8,5 +9,4 @@

Cancellation failed. Please check the link you entered! Maybe the account was already deleted, in this case no further action is required.

- - \ No newline at end of file +@endsection diff --git a/views/login/google_login.php b/views/login/google_login.php index da33385..65020f0 100644 --- a/views/login/google_login.php +++ b/views/login/google_login.php @@ -1,8 +1,8 @@ - - +@extends('templates/layout_normal') + +@section('main')

Login up with Google

Authenticating with Google failed. Please retry!

- - \ No newline at end of file +@endsection diff --git a/views/login/google_signup.php b/views/login/google_signup.php index 8b741de..0f9fe9c 100644 --- a/views/login/google_signup.php +++ b/views/login/google_signup.php @@ -1,10 +1,14 @@ +@extra - - +@endextra + +@extends('templates/layout_normal') + +@section('main')

Sign up

@@ -26,5 +30,4 @@ $jsFiles = [
- - \ No newline at end of file +@endsection diff --git a/views/login/login.php b/views/login/login.php index 5170136..e2eef46 100644 --- a/views/login/login.php +++ b/views/login/login.php @@ -1,5 +1,6 @@ - - +@extends('templates/layout_normal') + +@section('main')

Login

@@ -15,5 +16,4 @@
- - \ No newline at end of file +@endsection diff --git a/views/login/signup.php b/views/login/signup.php index c0d11bd..6912a27 100644 --- a/views/login/signup.php +++ b/views/login/signup.php @@ -1,10 +1,14 @@ +@extra - - +@endextra + +@extends('templates/layout_normal') + +@section('main')

Sign up

@@ -30,5 +34,4 @@ $jsFiles = [
- - \ No newline at end of file +@endsection diff --git a/views/login/signup_success.php b/views/login/signup_success.php index e727ee1..f2a42f1 100644 --- a/views/login/signup_success.php +++ b/views/login/signup_success.php @@ -1,8 +1,8 @@ - - +@extends('templates/layout_normal') + +@section('main')

Sign up

Sign up was successful. Please check your email and click on the activation link to activate your account!

- - \ No newline at end of file +@endsection diff --git a/views/maps.php b/views/maps.php index 64ec1db..002d3bb 100644 --- a/views/maps.php +++ b/views/maps.php @@ -1,3 +1,4 @@ +@extra - - +@endextra + +@extends('templates/layout_normal') + +@section('main')
@@ -66,5 +70,4 @@ if ($isAdmin) {
- - \ No newline at end of file +@endsection diff --git a/views/templates/footer.php b/views/templates/footer.php deleted file mode 100644 index 8c3ff6f..0000000 --- a/views/templates/footer.php +++ /dev/null @@ -1,5 +0,0 @@ -
- \ No newline at end of file diff --git a/views/templates/layout_full.php b/views/templates/layout_full.php new file mode 100644 index 0000000..4a8fd24 --- /dev/null +++ b/views/templates/layout_full.php @@ -0,0 +1,18 @@ +@extends('templates/mapguesser') + +@section('content') +
+

+ + + +

+

+ @yields('subheader') +

+
+
+@yields('main') +
+@endsection diff --git a/views/templates/header.php b/views/templates/layout_normal.php similarity index 79% rename from views/templates/header.php rename to views/templates/layout_normal.php index c903207..1b5dd61 100644 --- a/views/templates/header.php +++ b/views/templates/layout_normal.php @@ -1,3 +1,5 @@ +@extends('templates/mapguesser') +@section('content')

@@ -21,4 +23,11 @@

-
\ No newline at end of file +
+@yields('main') +
+
+

© Pőcze Bence format('Y') ?>

+
+@endsection diff --git a/views/templates/main_footer.php b/views/templates/main_footer.php deleted file mode 100644 index 378e2ff..0000000 --- a/views/templates/main_footer.php +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/views/templates/main_header.php b/views/templates/mapguesser.php similarity index 50% rename from views/templates/main_header.php rename to views/templates/mapguesser.php index a88457d..083fbb7 100644 --- a/views/templates/main_header.php +++ b/views/templates/mapguesser.php @@ -38,4 +38,57 @@

- \ No newline at end of file + + @yields('pagemodal') + @yields('content') + + @yields('pagescript') + + + + + + + + + + + + + + + +