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