Skip to content

Commit 0f4be08

Browse files
fprochazkadg
authored andcommitted
implemented blog
1 parent 8107bc8 commit 0f4be08

File tree

12 files changed

+350
-33
lines changed

12 files changed

+350
-33
lines changed

app/config/common.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,10 @@ session:
1111
expiration: 14 days
1212

1313

14+
security:
15+
users:
16+
admin: secret # user 'admin', password 'secret'
17+
18+
1419
services:
1520
router: App\RouterFactory::createRouter

app/presenters/HomepagePresenter.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,21 @@
99

1010
final class HomepagePresenter extends Nette\Application\UI\Presenter
1111
{
12+
/** @var Nette\Database\Context */
13+
private $database;
14+
15+
16+
public function __construct(Nette\Database\Context $database)
17+
{
18+
$this->database = $database;
19+
}
20+
21+
22+
public function renderDefault(int $page = 1): void
23+
{
24+
$this->template->page = $page;
25+
$this->template->posts = $this->database->table('posts')
26+
->order('created_at DESC')
27+
->page($page, 5);
28+
}
1229
}

app/presenters/PostPresenter.php

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Presenters;
6+
7+
use Nette;
8+
use Nette\Application\UI\Form;
9+
10+
11+
final class PostPresenter extends Nette\Application\UI\Presenter
12+
{
13+
/** @var Nette\Database\Context */
14+
private $database;
15+
16+
17+
public function __construct(Nette\Database\Context $database)
18+
{
19+
$this->database = $database;
20+
}
21+
22+
23+
public function renderShow(int $postId): void
24+
{
25+
$post = $this->database->table('posts')->get($postId);
26+
if (!$post) {
27+
$this->error('Post not found');
28+
}
29+
30+
$this->template->post = $post;
31+
$this->template->comments = $post->related('comment')->order('created_at');
32+
}
33+
34+
35+
protected function createComponentCommentForm(): Form
36+
{
37+
$form = new Form;
38+
$form->addText('name', 'Your name:')
39+
->setRequired();
40+
41+
$form->addEmail('email', 'Email:');
42+
43+
$form->addTextArea('content', 'Comment:')
44+
->setRequired();
45+
46+
$form->addSubmit('send', 'Publish comment');
47+
$form->onSuccess[] = [$this, 'commentFormSucceeded'];
48+
49+
return $form;
50+
}
51+
52+
53+
public function commentFormSucceeded(Form $form, \stdClass $values): void
54+
{
55+
$this->database->table('comments')->insert([
56+
'post_id' => $this->getParameter('postId'),
57+
'name' => $values->name,
58+
'email' => $values->email,
59+
'content' => $values->content,
60+
]);
61+
62+
$this->flashMessage('Thank you for your comment', 'success');
63+
$this->redirect('this');
64+
}
65+
66+
67+
public function actionCreate(): void
68+
{
69+
if (!$this->getUser()->isLoggedIn()) {
70+
$this->redirect('Sign:in');
71+
}
72+
}
73+
74+
75+
public function actionEdit(int $postId): void
76+
{
77+
if (!$this->getUser()->isLoggedIn()) {
78+
$this->redirect('Sign:in');
79+
}
80+
81+
$post = $this->database->table('posts')->get($postId);
82+
if (!$post) {
83+
$this->error('Post not found');
84+
}
85+
$this['postForm']->setDefaults($post->toArray());
86+
}
87+
88+
89+
protected function createComponentPostForm(): Form
90+
{
91+
if (!$this->getUser()->isLoggedIn()) {
92+
$this->error('You need to log in to create or edit posts');
93+
}
94+
95+
$form = new Form;
96+
$form->addText('title', 'Title:')
97+
->setRequired();
98+
$form->addTextArea('content', 'Content:')
99+
->setRequired();
100+
101+
$form->addSubmit('send', 'Save and publish');
102+
$form->onSuccess[] = [$this, 'postFormSucceeded'];
103+
104+
return $form;
105+
}
106+
107+
108+
public function postFormSucceeded(Form $form, \stdClass $values): void
109+
{
110+
$postId = $this->getParameter('postId');
111+
112+
if ($postId) {
113+
$post = $this->database->table('posts')->get($postId);
114+
$post->update($values);
115+
} else {
116+
$post = $this->database->table('posts')->insert($values);
117+
}
118+
119+
$this->flashMessage('Post was published', 'success');
120+
$this->redirect('show', $post->id);
121+
}
122+
}

app/presenters/SignPresenter.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Presenters;
6+
7+
use Nette;
8+
use Nette\Application\UI\Form;
9+
10+
11+
final class SignPresenter extends Nette\Application\UI\Presenter
12+
{
13+
/**
14+
* Sign-in form factory.
15+
*/
16+
protected function createComponentSignInForm(): Form
17+
{
18+
$form = new Form;
19+
$form->addText('username', 'Username:')
20+
->setRequired('Please enter your username.');
21+
22+
$form->addPassword('password', 'Password:')
23+
->setRequired('Please enter your password.');
24+
25+
$form->addSubmit('send', 'Sign in');
26+
27+
// call method signInFormSucceeded() on success
28+
$form->onSuccess[] = [$this, 'signInFormSucceeded'];
29+
return $form;
30+
}
31+
32+
33+
public function signInFormSucceeded(Form $form, \stdClass $values): void
34+
{
35+
try {
36+
$this->getUser()->login($values->username, $values->password);
37+
$this->redirect('Homepage:');
38+
39+
} catch (Nette\Security\AuthenticationException $e) {
40+
$form->addError('Incorrect username or password.');
41+
}
42+
}
43+
44+
45+
public function actionOut(): void
46+
{
47+
$this->getUser()->logout();
48+
$this->flashMessage('You have been signed out.');
49+
$this->redirect('Homepage:');
50+
}
51+
}

app/presenters/templates/@layout.latte

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,28 @@
44
<meta charset="utf-8">
55
<meta name="viewport" content="width=device-width">
66

7-
<title>{ifset title}{include title|stripHtml} | {/ifset}Nette Web</title>
7+
<title>{ifset title}{include title|stripHtml} | {/ifset}Nette Framework Micro-blog</title>
8+
9+
<link rel="stylesheet" href="{$basePath}/css/style.css">
810
</head>
911

1012
<body>
13+
<ul class="navig">
14+
<li><a n:href="Homepage:">Homepage</a></li>
15+
{block navig}{/block}
16+
{if $user->loggedIn}
17+
<li><a n:href="Sign:out">Sign out</a></li>
18+
{else}
19+
<li><a n:href="Sign:in">Sign in</a></li>
20+
{/if}
21+
</ul>
22+
1123
<div n:foreach="$flashes as $flash" n:class="flash, $flash->type">{$flash->message}</div>
1224

1325
{include content}
1426

27+
<p class="footer">This is a <a href="http://doc.nette.org/quickstart">Nette Framework Quick Start</a>.</p>
28+
1529
{block scripts}
1630
<script src="https://nette.github.io/resources/js/3/netteForms.min.js"></script>
1731
{/block}
Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,19 @@
1-
{* This is the welcome page, you can delete it *}
1+
{block navig}
2+
<li n:if="$user->loggedIn"><a n:href="Post:create">Write new post</a></li>
3+
{/block}
24

35
{block content}
4-
<div id="banner">
5-
<h1 n:block=title>Congratulations!</h1>
6-
</div>
6+
<h1 n:block="title">My awesome blog</h1>
77

8-
<div id="content">
9-
<h2>You have successfully created your <a href="https://nette.org">Nette</a> Web project.</h2>
8+
<div n:foreach="$posts as $post" class="post">
9+
<div class="date">{$post->created_at|date:'F j, Y'}</div>
1010

11-
<p><img src="data:image/png;base64,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" alt="">
12-
If you are exploring Nette for the first time, you should read the
13-
<a href="https://doc.nette.org/quickstart">Quick Start</a>, <a href="https://doc.nette.org">documentation</a>,
14-
<a href="https://pla.nette.org">tutorials</a> and <a href="https://forum.nette.org">forum</a>.</p>
11+
<h2><a n:href="Post:show $post->id">{$post->title}</a></h2>
1512

16-
<h2>We hope you enjoy Nette!</h2>
17-
</div>
13+
<div>{$post->content}</div>
14+
</div>
1815

19-
<style>
20-
html { font: normal 18px/1.3 Georgia, "New York CE", utopia, serif; color: #666; -webkit-text-stroke: 1px rgba(0,0,0,0); overflow-y: scroll; }
21-
body { background: #3484d2; color: #333; margin: 2em auto; padding: 0 .5em; max-width: 600px; min-width: 320px; }
22-
23-
a { color: #006aeb; padding: 3px 1px; }
24-
a:hover, a:active, a:focus { background-color: #006aeb; text-decoration: none; color: white; }
25-
26-
#banner { border-radius: 12px 12px 0 0; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAB5CAMAAADPursXAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAGBQTFRFD1CRDkqFDTlmDkF1D06NDT1tDTNZDk2KEFWaDTZgDkiCDTtpDT5wDkZ/DTBVEFacEFOWD1KUDTRcDTFWDkV9DkR7DkN4DkByDTVeDC9TDThjDTxrDkeADkuIDTRbDC9SbsUaggAAAEdJREFUeNqkwYURgAAQA7DH3d3335LSKyxAYpf9vWCpnYbf01qcOdFVXc14w4BznNTjkQfsscAdU3b4wIh9fDVYc4zV8xZgAAYaCMI6vPgLAAAAAElFTkSuQmCC); }
27-
28-
h1 { font: inherit; color: white; font-size: 50px; line-height: 121px; margin: 0; padding-left: 4%; background: url(https://files.nette.org/images/[email protected]) no-repeat 95%; background-size: 130px auto; text-shadow: 1px 1px 0 rgba(0, 0, 0, .9); }
29-
@media (max-width: 600px) {
30-
h1 { background: none; font-size: 40px; }
31-
}
32-
33-
#content { background: white; border: 1px solid #eff4f7; border-radius: 0 0 12px 12px; padding: 10px 4%; overflow: hidden; }
34-
35-
h2 { font: inherit; padding: 1.2em 0; margin: 0; }
36-
37-
img { border: none; float: right; margin: 0 0 1em 3em; }
38-
</style>
16+
<p><a n:href="this, page => $page-1" n:if="$page > 1">‹ back</a>
17+
&nbsp;
18+
<a n:href="this, page => $page+1" n:if="$iterations">next ›</a></p>
19+
{/block}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{block content}
2+
<h1>New post</h1>
3+
4+
{control postForm}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{block content}
2+
<h1>Edit post</h1>
3+
4+
{control postForm}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{block navig}
2+
<li n:if="$user->loggedIn"><a n:href="edit $post->id">Edit this post</a></li>
3+
{/block}
4+
5+
{block content}
6+
<div class="date">{$post->created_at|date:'F j, Y'}</div>
7+
8+
<h1 n:block=title>{$post->title}</h1>
9+
10+
<div class="post">{$post->content}</div>
11+
12+
<h2>Comments</h2>
13+
14+
<div class="comments">
15+
{foreach $comments as $comment}
16+
<p><b><a href="mailto:{$comment->email}" n:tag-if="$comment->email">{$comment->name}</a></b> said:</p>
17+
<div>{$comment->content}</div>
18+
{/foreach}
19+
</div>
20+
21+
<h2>Post new comment</h2>
22+
23+
{control commentForm}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{block content}
2+
<h1 n:block=title>Sign in</h1>
3+
4+
{control signInForm}

0 commit comments

Comments
 (0)