##// END OF EJS Templates
Fixed medadata design when an image is present. Added a method to determine if the tag is empty (has no attached threads).
neko259 -
r33:45bad15a default
parent child Browse files
Show More
@@ -1,53 +1,57 b''
1 1 from django import forms
2 2
3 3
4 4 class PostForm(forms.Form):
5 5 MAX_TEXT_LENGTH = 10000
6 6 MAX_IMAGE_SIZE = 8 * 1024 * 1024
7 7
8 8 title = forms.CharField(max_length=50, required=False)
9 9 text = forms.CharField(widget=forms.Textarea, required=False)
10 10 image = forms.ImageField(required=False)
11 11
12 12 def clean_text(self):
13 13 text = self.cleaned_data['text']
14 14 if text:
15 15 if len(text) > self.MAX_TEXT_LENGTH:
16 16 raise forms.ValidationError('Too many text')
17 17 return text
18 18
19 19 def clean_image(self):
20 20 image = self.cleaned_data['image']
21 21 if image:
22 22 if image._size > self.MAX_IMAGE_SIZE:
23 23 raise forms.ValidationError('Too large image: more than ' +
24 24 str(self.MAX_IMAGE_SIZE) + ' bytes')
25 25 return image
26 26
27 27 def clean(self):
28 28 cleaned_data = super(PostForm, self).clean()
29 29
30 30 text = cleaned_data.get('text')
31 31 image = cleaned_data.get('image')
32 32
33 33 if (not text) and (not image):
34 34 raise forms.ValidationError('Enter either text or image')
35 35
36 36 return cleaned_data
37 37
38 38
39 39 class ThreadForm(PostForm):
40 INVALID_TAG_CHARACTERS = ['+', '/', '&', '=', '?']
41
40 42 tags = forms.CharField(max_length=100)
41 43
42 44 def clean_tags(self):
43 45 tags = self.cleaned_data['tags']
44 46 if tags:
45 if ('+' in tags) or ('/' in tags):
46 raise forms.ValidationError('Inappropriate characters in tags')
47 for character in tags:
48 if character in self.INVALID_TAG_CHARACTERS:
49 raise forms.ValidationError(
50 'Inappropriate characters in tags')
47 51
48 52 return tags
49 53
50 54 def clean(self):
51 55 cleaned_data = super(ThreadForm, self).clean()
52 56
53 57 return cleaned_data No newline at end of file
@@ -1,190 +1,208 b''
1 1 import os
2 2 import re
3 3 from django.db import models
4 4 from django.utils import timezone
5 5 import time
6 6
7 7 from neboard import settings
8 8
9 9 import thumbs
10 10
11 11 NO_PARENT = -1
12 12 NO_IP = '0.0.0.0'
13 13 UNKNOWN_UA = ''
14 14
15 15
16 16 def update_image_filename(instance, filename):
17 17 """Get unique image filename"""
18 18
19 19 path = 'images/'
20 20 new_name = str(int(time.mktime(time.gmtime()))) + '_' + filename
21 21 return os.path.join(path, new_name)
22 22
23 23
24 24 class PostManager(models.Manager):
25 25 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
26 26 ip=NO_IP, tags=None):
27 27 post = self.create(title=title,
28 28 text=text,
29 29 pub_time=timezone.now(),
30 30 parent=parent_id,
31 31 image=image,
32 32 poster_ip=ip,
33 33 poster_user_agent=UNKNOWN_UA,
34 34 last_edit_time=timezone.now())
35 35
36 36 if tags:
37 37 for tag in tags:
38 38 post.tags.add(tag)
39 39
40 40 if parent_id != NO_PARENT:
41 41 parent = self.get(id=parent_id)
42 42 parent.last_edit_time=timezone.now()
43 43 parent.save()
44 44 else:
45 45 self._delete_old_threads()
46 46
47 47 return post
48 48
49 49 def delete_post(self, post):
50 50 children = self.filter(parent=post.id)
51 51 for child in children:
52 52 self.delete_post(child)
53 53 post.delete()
54 54
55 55 def delete_posts_by_ip(self, ip):
56 56 posts = self.filter(poster_ip=ip)
57 57 for post in posts:
58 58 self.delete_post(post)
59 59
60 60 def get_threads(self, tag=None):
61 61 if tag:
62 62 threads = self.filter(parent=NO_PARENT, tags=tag)
63 63 else:
64 64 threads = self.filter(parent=NO_PARENT)
65 65 threads = list(threads.order_by('-last_edit_time'))
66 66
67 67 return threads
68 68
69 69 def get_thread(self, opening_post_id):
70 70 opening_post = self.get(id=opening_post_id)
71 replies = self.filter(parent=opening_post_id)
71
72 if opening_post.parent == NO_PARENT:
73 replies = self.filter(parent=opening_post_id)
72 74
73 thread = [opening_post]
74 thread.extend(replies)
75 thread = [opening_post]
76 thread.extend(replies)
75 77
76 return thread
78 return thread
77 79
78 80 def exists(self, post_id):
79 81 posts = self.filter(id=post_id)
80 82
81 83 return len(posts) > 0
82 84
83 85 def _delete_old_threads(self):
84 86 """
85 87 Preserves maximum thread count. If there are too many threads,
86 88 delete the old ones.
87 89 """
88 90
89 91 # TODO Try to find a better way to get the active thread count.
90 92
91 93 # TODO Move old threads to the archive instead of deleting them.
92 94 # Maybe make some 'old' field in the model to indicate the thread
93 95 # must not be shown and be able for replying.
94 96
95 97 threads = self.get_threads()
96 98 thread_count = len(threads)
97 99
98 100 if thread_count > settings.MAX_THREAD_COUNT:
99 101 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
100 102 old_threads = threads[-num_threads_to_delete:]
101 103
102 104 for thread in old_threads:
103 105 self.delete_post(thread)
104 106
105 107
108 class TagManager(models.Manager):
109 def get_not_empty_tags(self):
110 all_tags = self.all()
111 tags = []
112 for tag in all_tags:
113 if not tag.is_empty():
114 tags.append(tag)
115
116 return tags
117
118
106 119 class Tag(models.Model):
107 120 """
108 121 A tag is a text node assigned to the post. The tag serves as a board
109 122 section. There can be multiple tags for each message
110 123 """
111 124
125 objects = TagManager()
126
112 127 name = models.CharField(max_length=100)
113 128 # TODO Connect the tag to its posts to check the number of threads for
114 129 # the tag.
115 130
116 131 def __unicode__(self):
117 132 return self.name
118 133
119 134 def is_empty(self):
135 return self.get_post_count() == 0
136
137 def get_post_count(self):
120 138 posts_with_tag = Post.objects.get_threads(tag=self)
121 return len(posts_with_tag) == 0
139 return len(posts_with_tag)
122 140
123 141
124 142 class Post(models.Model):
125 143 """A post is a message."""
126 144
127 145 objects = PostManager()
128 146
129 147 title = models.CharField(max_length=50)
130 148 pub_time = models.DateTimeField()
131 149 text = models.TextField()
132 150 image = thumbs.ImageWithThumbsField(upload_to=update_image_filename,
133 151 blank=True, sizes=((200, 150),))
134 152 poster_ip = models.IPAddressField()
135 153 poster_user_agent = models.TextField()
136 154 parent = models.BigIntegerField()
137 155 tags = models.ManyToManyField(Tag)
138 156 last_edit_time = models.DateTimeField()
139 157
140 158 def __unicode__(self):
141 159 return self.title + ' (' + self.text + ')'
142 160
143 161 def _get_replies(self):
144 162 return Post.objects.filter(parent=self.id)
145 163
146 164 def get_reply_count(self):
147 165 return len(self._get_replies())
148 166
149 167 def get_images_count(self):
150 168 images_count = 1 if self.image else 0
151 169 for reply in self._get_replies():
152 170 if reply.image:
153 171 images_count += 1
154 172
155 173 return images_count
156 174
157 175 def get_gets_count(self):
158 176 gets_count = 1 if self.is_get() else 0
159 177 for reply in self._get_replies():
160 178 if reply.is_get():
161 179 gets_count += 1
162 180
163 181 return gets_count
164 182
165 183 def is_get(self):
166 184 """If the post has pretty id (1, 1000, 77777), than it is called GET"""
167 185
168 186 # TODO Make a better algorithm for describing gets
169 187 return self.id == 1 or self.id % 10 == 0
170 188
171 189 def get_parsed_text(self):
172 190 text = self.text
173 191
174 192 # TODO Implement parsing
175 193 # Make //text colored
176 194 # Make >>12 links to posts
177 195
178 196 return text
179 197
180 198
181 199 class Admin(models.Model):
182 200 """
183 201 Model for admin users
184 202 """
185 203 name = models.CharField(max_length=100)
186 204 password = models.CharField(max_length=100)
187 205
188 206 def __unicode__(self):
189 207 return self.name + '/' + '*' * len(self.password)
190 208
@@ -1,79 +1,86 b''
1 1 html {
2 2 background: #555;
3 3 color: #ffffff;
4 4 }
5 5
6 6 #admin_panel {
7 7 background: #FF0000;
8 8 color: #00FF00
9 9 }
10 10
11 11 .title {
12 12 font-weight: bold;
13 13 color: #ffcc00;
14 14 }
15 15
16 16 .post-form {
17 17 text-align: left;
18 18 display: table;
19 19 border-radius: 5px;
20 20 padding: 5px;
21 21 margin: 5px;
22 22 background: #334;
23 23 }
24 24
25 25 .form-row {
26 26 display: table-row;
27 27 }
28 28
29 29 .form-input {
30 30 display: table-cell;
31 31 }
32 32
33 33 .link {
34 34 color: #afdcec;
35 35 }
36 36
37 37 .link:hover {
38 38 color: #fff380;
39 39 }
40 40
41 41 .post_id {
42 42 color: #ffffff;
43 43 }
44 44
45 45 .block {
46 46 display: inline-block;
47 47 vertical-align: top;
48 48 }
49 49
50 50 .tag {
51 51 color: #b4cfec;
52 52 }
53 53
54 54 .tag:hover {
55 55 color: #d0edb4;
56 56 }
57 57
58 58 .post_id {
59 59 color: #fff380;
60 60 }
61 61
62 62 .post {
63 63 background: #333;
64 64 margin: 5px;
65 65 padding: 10px;
66 66 border-radius: 5px;
67 67 }
68 68
69 69 .form-title {
70 70 font-weight: bolder;
71 71 }
72 72
73 73 .metadata {
74 74 padding: 2px;
75 75 margin-top: 10px;
76 76 border: solid 1px #666;
77 77 font-size: 0.9em;
78 78 color: #ddd
79 }
80
81 #navigation_panel {
82 background: #444;
83 margin: 5px;
84 padding: 10px;
85 border-radius: 5px;
79 86 } No newline at end of file
@@ -1,133 +1,136 b''
1 1 from django.template import RequestContext
2 2 from boards import forms
3 3 import boards
4 4 from boards.forms import ThreadForm, PostForm
5 5 from boards.models import Post, Admin, Tag
6 6 from django.shortcuts import render, get_list_or_404, redirect
7 7 from django.http import HttpResponseRedirect, Http404
8 8
9 9
10 10 def index(request):
11 11 context = RequestContext(request)
12 12
13 13 if request.method == 'POST':
14 14 return new_post(request)
15 15 else:
16 16 threads = Post.objects.get_threads()
17 17
18 18 context['threads'] = None if len(threads) == 0 else threads
19 19 context['form'] = forms.ThreadForm()
20 context['tags'] = Tag.objects.get_not_empty_tags()
20 21
21 22 return render(request, 'posting_general.html',
22 23 context)
23 24
24 25
25 26 def new_post(request, thread_id=boards.models.NO_PARENT):
26 27 """Add a new post (in thread or as a reply)."""
27 28
28 29 if thread_id == boards.models.NO_PARENT:
29 30 form = ThreadForm(request.POST, request.FILES)
30 31 else:
31 32 form = PostForm(request.POST, request.FILES)
32 33
33 34 if form.is_valid():
34 35 data = form.cleaned_data
35 36 else:
36 37 return redirect(index)
37 38
38 39 title = data['title']
39 40 text = data['text']
40 41
41 42 if 'image' in data.keys():
42 43 image = data['image']
43 44 else:
44 45 image = None
45 46
46 47 ip = request.META['REMOTE_ADDR']
47 48
48 49 tags = []
49 50 if thread_id == boards.models.NO_PARENT:
50 51 tag_strings = data['tags']
51 52
52 53 if tag_strings:
53 tag_strings = tag_strings.split(' ')
54 tag_strings = tag_strings.split(',')
54 55 for tag_name in tag_strings:
55 56 tag_name = tag_name.strip()
56 57 if len(tag_name) > 0:
57 58 tag, created = Tag.objects.get_or_create(name=tag_name)
58 59 tags.append(tag)
59 60
60 61 # TODO Add a possibility to define a link image instead of an image file.
61 62 # If a link is given, download the image automatically.
62 63
63 64 post = Post.objects.create_post(title=title, text=text, ip=ip,
64 65 parent_id=thread_id, image=image,
65 66 tags=tags)
66 67
67 68 thread_to_show = (post.id if thread_id == boards.models.NO_PARENT else
68 69 thread_id)
69 70 return redirect(thread, post_id=thread_to_show)
70 71
71 72
72 73 def tag(request, tag_name):
73 74 """Get all tag threads (posts without a parent)."""
74 75
75 76 tag = Tag.objects.get(name=tag_name)
76 77 threads = Post.objects.get_threads(tag=tag)
77 78
78 79 if request.method == 'POST':
79 80 return new_post(request)
80 81 else:
81 82 context = RequestContext(request)
82 83 context['threads'] = None if len(threads) == 0 else threads
83 84 context['tag'] = tag_name
85 context['tags'] = Tag.objects.get_not_empty_tags()
84 86
85 87 context['form'] = forms.ThreadForm(initial={'tags': tag_name})
86 88
87 89 return render(request, 'posting_general.html',
88 90 context)
89 91
90 92
91 93 def thread(request, post_id):
92 94 """Get all thread posts"""
93 95
94 96 if request.method == 'POST':
95 97 return new_post(request, post_id)
96 98 else:
97 99 # TODO Show 404 if there is no such thread
98 100 posts = Post.objects.get_thread(post_id)
99 101
100 102 context = RequestContext(request)
103
101 104 context['posts'] = posts
102
103 105 context['form'] = forms.PostForm()
106 context['tags'] = Tag.objects.get_not_empty_tags()
104 107
105 108 return render(request, 'thread.html', context)
106 109
107 110
108 111 def login(request):
109 112 """Log in as admin"""
110 113
111 114 if 'name' in request.POST and 'password' in request.POST:
112 115 request.session['admin'] = False
113 116
114 117 isAdmin = len(Admin.objects.filter(name=request.POST['name'],
115 118 password=request.POST[
116 119 'password'])) > 0
117 120
118 121 if isAdmin:
119 122 request.session['admin'] = True
120 123
121 124 response = HttpResponseRedirect('/')
122 125
123 126 else:
124 127 response = render(request, 'login.html', {'error': 'Login error'})
125 128 else:
126 129 response = render(request, 'login.html', {})
127 130
128 131 return response
129 132
130 133
131 134 def logout(request):
132 135 request.session['admin'] = False
133 136 return HttpResponseRedirect('/') No newline at end of file
@@ -1,60 +1,72 b''
1 1 # SOME DESCRIPTIVE TITLE.
2 2 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 3 # This file is distributed under the same license as the PACKAGE package.
4 4 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5 5 #
6 6 msgid ""
7 7 msgstr ""
8 8 "Project-Id-Version: PACKAGE VERSION\n"
9 9 "Report-Msgid-Bugs-To: \n"
10 "POT-Creation-Date: 2013-04-11 21:26+0300\n"
10 "POT-Creation-Date: 2013-04-13 22:07+0300\n"
11 11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12 12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 13 "Language-Team: LANGUAGE <LL@li.org>\n"
14 14 "Language: ru\n"
15 15 "MIME-Version: 1.0\n"
16 16 "Content-Type: text/plain; charset=UTF-8\n"
17 17 "Content-Transfer-Encoding: 8bit\n"
18 18 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
19 19 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
20 20
21 #: templates/posting_general.html:22
21 #: templates/base.html:22
22 msgid "All threads"
23 msgstr "ВсС Π½ΠΈΠΊΠΈ"
24
25 #: templates/posting_general.html:26
22 26 msgid "View"
23 27 msgstr "ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€"
24 28
25 #: templates/posting_general.html:25
29 #: templates/posting_general.html:31
26 30 msgid "replies"
27 31 msgstr "ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ²"
28 32
29 #: templates/posting_general.html:26
33 #: templates/posting_general.html:32
30 34 msgid "images"
31 35 msgstr "ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ"
32 36
33 #: templates/posting_general.html:29 templates/posting_general.html.py:61
34 #: templates/thread.html:23
37 #: templates/posting_general.html:33
38 msgid "gets"
39 msgstr "Π³Π΅Ρ‚Ρ‹"
40
41 #: templates/posting_general.html:35 templates/posting_general.html.py:67
42 #: templates/thread.html:31
35 43 msgid "Tags"
36 44 msgstr "Π’Π΅Π³ΠΈ"
37 45
38 #: templates/posting_general.html:46
46 #: templates/posting_general.html:52
39 47 msgid "Create new thread"
40 msgstr "Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ Π½ΠΎΠ²Ρ‹ΠΉ Ρ‚Ρ€Π΅Π΄"
48 msgstr "Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ Π½ΠΎΠ²ΡƒΡŽ Π½ΠΈΡ‚ΡŒ"
41 49
42 #: templates/posting_general.html:49 templates/thread.html:42
50 #: templates/posting_general.html:55 templates/thread.html:50
43 51 msgid "Title"
44 52 msgstr "Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ"
45 53
46 #: templates/posting_general.html:53 templates/thread.html:46
54 #: templates/posting_general.html:59 templates/thread.html:54
47 55 msgid "Text"
48 56 msgstr "ВСкст"
49 57
50 #: templates/posting_general.html:57 templates/thread.html:50
58 #: templates/posting_general.html:63 templates/thread.html:58
51 59 msgid "Image"
52 60 msgstr "ΠšΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠ°"
53 61
54 #: templates/posting_general.html:64 templates/thread.html:53
62 #: templates/posting_general.html:70 templates/thread.html:61
55 63 msgid "Post"
56 64 msgstr "ΠžΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ"
57 65
58 #: templates/thread.html:39
66 #: templates/posting_general.html:72
67 msgid "Tags must be delimited by spaces. Text or image is required."
68 msgstr "Π’Π΅Π³ΠΈ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ Ρ€Π°Π·Π΄Π΅Π»Π΅Π½Ρ‹ ΠΏΡ€ΠΎΠ±Π΅Π»Π°ΠΌΠΈ. ВСкст ΠΈΠ»ΠΈ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹."
69
70 #: templates/thread.html:47
59 71 msgid "Reply to the thread"
60 msgstr "ΠžΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ Π² Ρ‚Ρ€Π΅Π΄"
72 msgstr "ΠžΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ Π² Π½ΠΈΡ‚ΡŒ"
@@ -1,23 +1,32 b''
1 1 {% load staticfiles %}
2 {% load i18n %}
2 3
3 4 <!DOCTYPE html>
4 5 <html>
5 6 <head>
6 7 <link rel="stylesheet" type="text/css"
7 8 href="{% static "css/base_page.css" %}" media="all"/>
8 9 {% block head %}{% endblock %}
9 10 </head>
10 11 <body>
11 12 <div id="admin_panel">
12 13
13 14 {% if request.session.admin == True %}
14 15 Admin panel TODO: Need to implement <BR />
15 16 {% endif %}
16 17
17 18
18 19 </div>
19 20
21 <div id="navigation_panel">
22 <a class="link" href="{% url 'index' %}">{% trans "All threads" %}</a>
23 {% for tag in tags %}
24 <a class="tag" href=" {% url 'tag' tag_name=tag.name %}">
25 {{ tag.name }}</a>({{ tag.get_post_count }})
26 {% endfor %}
27 </div>
28
20 29 {% block content %}{% endblock %}
21 30
22 31 </body>
23 32 </html> No newline at end of file
@@ -1,72 +1,76 b''
1 1 {% extends "base.html" %}
2 2
3 3 {% load i18n %}
4 4
5 {% block head %}
6 <title>Neboard</title>
7 {% endblock %}
8
5 9 {% block content %}
6 10
7 11 {% if threads %}
8 12 {% for thread in threads %}
9 13 <div class="post">
10 14 {% if thread.image %}
11 15 <div class="block">
12 16 <a href="{{ thread.image.url }}"><img
13 17 src="{{ thread.image.url_200x150 }}" />
14 18 </a>
15 19 </div>
16 20 {% endif %}
17 21 <div class="block">
18 22 <span class="title">{{ thread.title }}</span>
19 23 <span class="post_id">(#{{ thread.id }})</span>
20 24 [{{ thread.pub_time }}]
21 25 [<a class="link" href="{% url 'thread' thread.id %}"
22 26 >{% trans "View" %}</a>]<br />
23 27 {{ thread.get_parsed_text|linebreaksbr|truncatechars:300 }}<br
24 28 />
25 29 </div>
26 30 <div class="metadata">
27 31 {{ thread.get_reply_count }} {% trans 'replies' %},
28 32 {{ thread.get_images_count }} {% trans 'images' %},
29 33 {{ thread.get_gets_count }} {% trans 'gets' %}.
30 34 {% if thread.tags.all %}
31 35 <span class="tags">{% trans 'Tags' %}:
32 36 {% for tag in thread.tags.all %}
33 37 <a class="tag" href="
34 38 {% url 'tag' tag_name=tag.name %}">
35 39 {{ tag.name }}</a>
36 40 {% endfor %}
37 41 </span>
38 42 {% endif %}
39 43 </div>
40 44 </div>
41 45 {% endfor %}
42 46 {% else %}
43 47 No threads found.
44 48 <hr />
45 49 {% endif %}
46 50
47 51 <div class="post-form">
48 52 <span class="form-title">{% trans "Create new thread" %}</span>
49 53 <form enctype="multipart/form-data" method="post">{% csrf_token %}
50 54 <div class="form-row">
51 55 <div class="form-input">{% trans 'Title' %}</div>
52 56 <div class="form-input">{{ form.title }}</div>
53 57 </div>
54 58 <div class="form-row">
55 59 <div class="form-input">{% trans 'Text' %}</div>
56 60 <div class="form-input">{{ form.text }}</div>
57 61 </div>
58 62 <div class="form-row">
59 63 <div class="form-input">{% trans 'Image' %}</div>
60 64 <div class="form-input">{{ form.image }}</div>
61 65 </div>
62 66 <div class="form-row">
63 67 <div class="form-input">{% trans 'Tags' %}</div>
64 68 <div class="form-input">{{ form.tags }}</div>
65 69 </div>
66 70 <input type="submit" value="{% trans 'Post' %}" />
67 71 <hr />
68 72 {% trans "Tags must be delimited by spaces. Text or image is required." %}
69 73 </form>
70 74 </div>
71 75
72 76 {% endblock %} No newline at end of file
@@ -1,61 +1,65 b''
1 1 {% extends "base.html" %}
2 2
3 3 {% load i18n %}
4 4
5 {% block head %}
6 <title>Neboard</title>
7 {% endblock %}
8
5 9 {% block content %}
6 10
7 11 {% if posts %}
8 12 {% for post in posts %}
9 13 <a name="{{ post.id }}"></a>
10 14 <div class="post">
11 15 {% if post.image %}
12 16 <div class="block">
13 17 <a href="{{ post.image.url }}"><img
14 18 src="{{ post.image.url_200x150 }}" />
15 19 </a>
16 20 </div>
17 21 {% endif %}
18 22 <div class="block">
19 23 <span class="title">{{ post.title }}</span>
20 24 <a class="post_id" href="#{{ post.id }}">
21 25 (#{{ post.id }})</a>
22 26 [{{ post.pub_time }}]<br />
23 27 {{ post.get_parsed_text|linebreaksbr }}<br />
24 28 </div>
25 29 {% if post.tags.all %}
26 30 <div class="metadata">
27 31 <span class="tags">{% trans 'Tags' %}:
28 32 {% for tag in post.tags.all %}
29 33 <a class="tag" href="{% url 'tag' tag.name %}">
30 34 {{ tag.name }}</a>
31 35 {% endfor %}
32 36 </span>
33 37 </div>
34 38 {% endif %}
35 39 </div>
36 40 {% endfor %}
37 41 {% else %}
38 42 No threads found.
39 43 <hr />
40 44 {% endif %}
41 45
42 46 <div class="post-form">
43 47 <span class="form-title">{% trans "Reply to the thread" %}</span>
44 48 <form enctype="multipart/form-data" method="post">{% csrf_token %}
45 49 <div class="form-row">
46 50 <div class="form-input">{% trans 'Title' %}</div>
47 51 <div class="form-input">{{ form.title }}</div>
48 52 </div>
49 53 <div class="form-row">
50 54 <div class="form-input">{% trans 'Text' %}</div>
51 55 <div class="form-input">{{ form.text }}</div>
52 56 </div>
53 57 <div class="form-row">
54 58 <div class="form-input">{% trans 'Image' %}</div>
55 59 <div class="form-input">{{ form.image }}</div>
56 60 </div>
57 61 <input type="submit" value="{% trans 'Post' %}" />
58 62 </form>
59 63 </div>
60 64
61 65 {% endblock %} No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now