##// END OF EJS Templates
Added admin actions (showing IP, post removal). Added user ranks. This refs #61, #12
neko259 -
r112:1dfea0d0 1.1
parent child Browse files
Show More
@@ -1,6 +1,6 b''
1 from django.contrib import admin
1 from django.contrib import admin
2 from boards.models import Post, Tag, Admin
2 from boards.models import Post, Tag, User
3
3
4 admin.site.register(Post)
4 admin.site.register(Post)
5 admin.site.register(Tag)
5 admin.site.register(Tag)
6 admin.site.register(Admin)
6 admin.site.register(User)
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,168 +1,180 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-08-04 14:04+0300\n"
10 "POT-Creation-Date: 2013-08-21 20:49+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/boards/authors.html:6
21 #: templates/boards/authors.html:6
22 msgid "Authors"
22 msgid "Authors"
23 msgstr "Авторы"
23 msgstr "Авторы"
24
24
25 #: templates/boards/authors.html:24
25 #: templates/boards/authors.html:24
26 msgid "Distributed under the"
26 msgid "Distributed under the"
27 msgstr "РаспространяСтся ΠΏΠΎΠ΄"
27 msgstr "РаспространяСтся ΠΏΠΎΠ΄"
28
28
29 #: templates/boards/authors.html:26
29 #: templates/boards/authors.html:26
30 msgid "license"
30 msgid "license"
31 msgstr "Π»ΠΈΡ†Π΅Π½Π·ΠΈΠ΅ΠΉ"
31 msgstr "Π»ΠΈΡ†Π΅Π½Π·ΠΈΠ΅ΠΉ"
32
32
33 #: templates/boards/authors.html:28
33 #: templates/boards/authors.html:28
34 msgid "Repository"
34 msgid "Repository"
35 msgstr "Π Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ"
35 msgstr "Π Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ"
36
36
37 #: templates/boards/base.html:12
37 #: templates/boards/base.html:12
38 msgid "Feed"
38 msgid "Feed"
39 msgstr "Π›Π΅Π½Ρ‚Π°"
39 msgstr "Π›Π΅Π½Ρ‚Π°"
40
40
41 #: templates/boards/base.html:31
41 #: templates/boards/base.html:27
42 msgid "All threads"
42 msgid "All threads"
43 msgstr "ВсС Ρ‚Π΅ΠΌΡ‹"
43 msgstr "ВсС Ρ‚Π΅ΠΌΡ‹"
44
44
45 #: templates/boards/base.html:37
45 #: templates/boards/base.html:33
46 msgid "Settings"
46 msgid "Settings"
47 msgstr "Настройки"
47 msgstr "Настройки"
48
48
49 #: templates/boards/base.html:45
49 #: templates/boards/base.html:41
50 msgid "Up"
50 msgid "Up"
51 msgstr "Π’Π²Π΅Ρ€Ρ…"
51 msgstr "Π’Π²Π΅Ρ€Ρ…"
52
52
53 #: templates/boards/posting_general.html:18
53 #: templates/boards/posting_general.html:18
54 msgid "Tag: "
54 msgid "Tag: "
55 msgstr "Π’Π΅Π³: "
55 msgstr "Π’Π΅Π³: "
56
56
57 #: templates/boards/posting_general.html:35
57 #: templates/boards/posting_general.html:35
58 #: templates/boards/posting_general.html:79 templates/boards/thread.html:27
58 #: templates/boards/posting_general.html:87 templates/boards/thread.html:27
59 #: templates/boards/rss/post.html:5
59 #: templates/boards/rss/post.html:5
60 msgid "Post image"
60 msgid "Post image"
61 msgstr "Π˜Π·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ сообщСния"
61 msgstr "Π˜Π·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ сообщСния"
62
62
63 #: templates/boards/posting_general.html:46
63 #: templates/boards/posting_general.html:46
64 msgid "Reply"
64 msgid "Reply"
65 msgstr "ΠžΡ‚Π²Π΅Ρ‚"
65 msgstr "ΠžΡ‚Π²Π΅Ρ‚"
66
66
67 #: templates/boards/posting_general.html:53 templates/boards/thread.html:111
67 #: templates/boards/posting_general.html:52 templates/boards/thread.html:49
68 msgid "Delete"
69 msgstr "Π£Π΄Π°Π»ΠΈΡ‚ΡŒ"
70
71 #: templates/boards/posting_general.html:61 templates/boards/thread.html:119
68 msgid "replies"
72 msgid "replies"
69 msgstr "ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ²"
73 msgstr "ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ²"
70
74
71 #: templates/boards/posting_general.html:54 templates/boards/thread.html:112
75 #: templates/boards/posting_general.html:62 templates/boards/thread.html:120
72 msgid "images"
76 msgid "images"
73 msgstr "ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ"
77 msgstr "ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ"
74
78
75 #: templates/boards/posting_general.html:56
79 #: templates/boards/posting_general.html:64
76 #: templates/boards/posting_general.html:127 templates/boards/thread.html:51
80 #: templates/boards/posting_general.html:135 templates/boards/thread.html:59
77 #: templates/boards/rss/post.html:10
81 #: templates/boards/rss/post.html:10
78 msgid "Tags"
82 msgid "Tags"
79 msgstr "Π’Π΅Π³ΠΈ"
83 msgstr "Π’Π΅Π³ΠΈ"
80
84
81 #: templates/boards/posting_general.html:109
85 #: templates/boards/posting_general.html:117
82 msgid "Create new thread"
86 msgid "Create new thread"
83 msgstr "Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ Π½ΠΎΠ²ΡƒΡŽ Ρ‚Π΅ΠΌΡƒ"
87 msgstr "Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ Π½ΠΎΠ²ΡƒΡŽ Ρ‚Π΅ΠΌΡƒ"
84
88
85 #: templates/boards/posting_general.html:112 templates/boards/thread.html:73
89 #: templates/boards/posting_general.html:120 templates/boards/thread.html:81
86 msgid "Title"
90 msgid "Title"
87 msgstr "Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ"
91 msgstr "Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ"
88
92
89 #: templates/boards/posting_general.html:117 templates/boards/thread.html:78
93 #: templates/boards/posting_general.html:125 templates/boards/thread.html:86
90 msgid "Text"
94 msgid "Text"
91 msgstr "ВСкст"
95 msgstr "ВСкст"
92
96
93 #: templates/boards/posting_general.html:122 templates/boards/thread.html:83
97 #: templates/boards/posting_general.html:130 templates/boards/thread.html:91
94 msgid "Image"
98 msgid "Image"
95 msgstr "Π˜Π·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅"
99 msgstr "Π˜Π·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅"
96
100
97 #: templates/boards/posting_general.html:137 templates/boards/thread.html:94
101 #: templates/boards/posting_general.html:145 templates/boards/thread.html:102
98 msgid "Post"
102 msgid "Post"
99 msgstr "ΠžΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ"
103 msgstr "ΠžΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ"
100
104
101 #: templates/boards/posting_general.html:139
105 #: templates/boards/posting_general.html:147
102 msgid "Tags must be delimited by spaces. Text or image is required."
106 msgid "Tags must be delimited by spaces. Text or image is required."
103 msgstr ""
107 msgstr ""
104 "Π’Π΅Π³ΠΈ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ Ρ€Π°Π·Π΄Π΅Π»Π΅Π½Ρ‹ ΠΏΡ€ΠΎΠ±Π΅Π»Π°ΠΌΠΈ. ВСкст ΠΈΠ»ΠΈ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹."
108 "Π’Π΅Π³ΠΈ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ Ρ€Π°Π·Π΄Π΅Π»Π΅Π½Ρ‹ ΠΏΡ€ΠΎΠ±Π΅Π»Π°ΠΌΠΈ. ВСкст ΠΈΠ»ΠΈ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹."
105
109
106 #: templates/boards/posting_general.html:142 templates/boards/thread.html:96
110 #: templates/boards/posting_general.html:150 templates/boards/thread.html:104
107 msgid "Basic markdown syntax."
111 msgid "Basic markdown syntax."
108 msgstr "Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉ синтаксис markdown."
112 msgstr "Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉ синтаксис markdown."
109
113
110 #: templates/boards/posting_general.html:152
114 #: templates/boards/posting_general.html:160
111 msgid "Pages:"
115 msgid "Pages:"
112 msgstr "Π‘Ρ‚Ρ€Π°Π½ΠΈΡ†Ρ‹: "
116 msgstr "Π‘Ρ‚Ρ€Π°Π½ΠΈΡ†Ρ‹: "
113
117
114 #: templates/boards/settings.html:13
118 #: templates/boards/settings.html:12
119 msgid "User:"
120 msgstr "ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ:"
121
122 #: templates/boards/settings.html:14
123 msgid "You are moderator."
124 msgstr "Π’Ρ‹ ΠΌΠΎΠ΄Π΅Ρ€Π°Ρ‚ΠΎΡ€."
125
126 #: templates/boards/settings.html:20
115 msgid "Theme"
127 msgid "Theme"
116 msgstr "Π’Π΅ΠΌΠ°"
128 msgstr "Π’Π΅ΠΌΠ°"
117
129
118 #: templates/boards/settings.html:29
130 #: templates/boards/settings.html:36
119 msgid "Save"
131 msgid "Save"
120 msgstr "Π‘ΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ"
132 msgstr "Π‘ΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ"
121
133
122 #: templates/boards/tags.html:7
134 #: templates/boards/tags.html:7
123 msgid "tags"
135 msgid "tags"
124 msgstr "Ρ‚Π΅Π³ΠΎΠ²"
136 msgstr "Ρ‚Π΅Π³ΠΎΠ²"
125
137
126 #: templates/boards/thread.html:39
138 #: templates/boards/thread.html:39
127 msgid "Get!"
139 msgid "Get!"
128 msgstr "Π“Π΅Ρ‚!"
140 msgstr "Π“Π΅Ρ‚!"
129
141
130 #: templates/boards/thread.html:70
142 #: templates/boards/thread.html:78
131 msgid "Reply to thread"
143 msgid "Reply to thread"
132 msgstr "ΠžΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ Π² Ρ‚Π΅ΠΌΡƒ"
144 msgstr "ΠžΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ Π² Ρ‚Π΅ΠΌΡƒ"
133
145
134 #: templates/boards/thread.html:97
146 #: templates/boards/thread.html:105
135 msgid "Example: "
147 msgid "Example: "
136 msgstr "ΠŸΡ€ΠΈΠΌΠ΅Ρ€: "
148 msgstr "ΠŸΡ€ΠΈΠΌΠ΅Ρ€: "
137
149
138 #: templates/boards/thread.html:97
150 #: templates/boards/thread.html:105
139 msgid "italic"
151 msgid "italic"
140 msgstr "курсив"
152 msgstr "курсив"
141
153
142 #: templates/boards/thread.html:98
154 #: templates/boards/thread.html:106
143 msgid "bold"
155 msgid "bold"
144 msgstr "ΠΏΠΎΠ»ΡƒΠΆΠΈΡ€Π½Ρ‹ΠΉ"
156 msgstr "ΠΏΠΎΠ»ΡƒΠΆΠΈΡ€Π½Ρ‹ΠΉ"
145
157
146 #: templates/boards/thread.html:99
158 #: templates/boards/thread.html:107
147 msgid "Quotes can be inserted with"
159 msgid "Quotes can be inserted with"
148 msgstr "Π¦ΠΈΡ‚Π°Ρ‚Ρ‹ ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ вставлСны ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ"
160 msgstr "Π¦ΠΈΡ‚Π°Ρ‚Ρ‹ ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ вставлСны ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ"
149
161
150 #: templates/boards/thread.html:100
162 #: templates/boards/thread.html:108
151 msgid "Links to answers can be inserted with"
163 msgid "Links to answers can be inserted with"
152 msgstr "Бсылки Π½Π° ΠΎΡ‚Π²Π΅Ρ‚Ρ‹ ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ вставлСны с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ"
164 msgstr "Бсылки Π½Π° ΠΎΡ‚Π²Π΅Ρ‚Ρ‹ ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ вставлСны с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ"
153
165
154 #: templates/boards/thread.html:113
166 #: templates/boards/thread.html:121
155 msgid "Last update: "
167 msgid "Last update: "
156 msgstr "ПослСднСС обновлСниС: "
168 msgstr "ПослСднСС обновлСниС: "
157
169
158 #~ msgid "View"
170 #~ msgid "View"
159 #~ msgstr "ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€"
171 #~ msgstr "ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€"
160
172
161 #~ msgid "gets"
173 #~ msgid "gets"
162 #~ msgstr "Π³Π΅Ρ‚ΠΎΠ²"
174 #~ msgstr "Π³Π΅Ρ‚ΠΎΠ²"
163
175
164 msgid "author"
176 #~ msgid "author"
165 msgstr "Π°Π²Ρ‚ΠΎΡ€"
177 #~ msgstr "Π°Π²Ρ‚ΠΎΡ€"
166
178
167 msgid "developer"
179 #~ msgid "developer"
168 msgstr "Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ" No newline at end of file
180 #~ msgstr "Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ"
@@ -1,322 +1,333 b''
1 import os
1 import os
2 from random import random
2 from random import random
3 import re
3 import re
4 import time
4 import time
5 import math
5 import math
6
6
7 from django.db import models
7 from django.db import models
8 from django.http import Http404
8 from django.http import Http404
9 from django.utils import timezone
9 from django.utils import timezone
10 from markupfield.fields import MarkupField
10 from markupfield.fields import MarkupField
11 from threading import Thread
11 from threading import Thread
12
12
13 from neboard import settings
13 from neboard import settings
14 import thumbs
14 import thumbs
15
15
16 IMAGE_THUMB_SIZE = (200, 150)
16 IMAGE_THUMB_SIZE = (200, 150)
17
17
18 TITLE_MAX_LENGTH = 50
18 TITLE_MAX_LENGTH = 50
19
19
20 DEFAULT_MARKUP_TYPE = 'markdown'
20 DEFAULT_MARKUP_TYPE = 'markdown'
21
21
22 NO_PARENT = -1
22 NO_PARENT = -1
23 NO_IP = '0.0.0.0'
23 NO_IP = '0.0.0.0'
24 UNKNOWN_UA = ''
24 UNKNOWN_UA = ''
25 ALL_PAGES = -1
25 ALL_PAGES = -1
26 OPENING_POST_POPULARITY_WEIGHT = 2
26 OPENING_POST_POPULARITY_WEIGHT = 2
27 IMAGES_DIRECTORY = 'images/'
27 IMAGES_DIRECTORY = 'images/'
28 FILE_EXTENSION_DELIMITER = '.'
28 FILE_EXTENSION_DELIMITER = '.'
29
29
30 REGEX_PRETTY = re.compile(r'^\d(0)+$')
30 REGEX_PRETTY = re.compile(r'^\d(0)+$')
31 REGEX_SAME = re.compile(r'^(.)\1+$')
31 REGEX_SAME = re.compile(r'^(.)\1+$')
32
32
33 RANK_ADMIN = 0
34 RANK_MODERATOR = 10
35 RANK_USER = 100
36
33
37
34 class PostManager(models.Manager):
38 class PostManager(models.Manager):
35 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
39 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
36 ip=NO_IP, tags=None):
40 ip=NO_IP, tags=None):
37 post = self.create(title=title,
41 post = self.create(title=title,
38 text=text,
42 text=text,
39 pub_time=timezone.now(),
43 pub_time=timezone.now(),
40 parent=parent_id,
44 parent=parent_id,
41 image=image,
45 image=image,
42 poster_ip=ip,
46 poster_ip=ip,
43 poster_user_agent=UNKNOWN_UA,
47 poster_user_agent=UNKNOWN_UA,
44 last_edit_time=timezone.now())
48 last_edit_time=timezone.now())
45
49
46 if tags:
50 if tags:
47 map(post.tags.add, tags)
51 map(post.tags.add, tags)
48
52
49 if parent_id != NO_PARENT:
53 if parent_id != NO_PARENT:
50 self._bump_thread(parent_id)
54 self._bump_thread(parent_id)
51 else:
55 else:
52 self._delete_old_threads()
56 self._delete_old_threads()
53
57
54 return post
58 return post
55
59
56 def delete_post(self, post):
60 def delete_post(self, post):
57 children = self.filter(parent=post.id)
61 children = self.filter(parent=post.id)
58 for child in children:
62 for child in children:
59 self.delete_post(child)
63 self.delete_post(child)
60 post.delete()
64 post.delete()
61
65
62 def delete_posts_by_ip(self, ip):
66 def delete_posts_by_ip(self, ip):
63 posts = self.filter(poster_ip=ip)
67 posts = self.filter(poster_ip=ip)
64 for post in posts:
68 for post in posts:
65 self.delete_post(post)
69 self.delete_post(post)
66
70
67 def get_threads(self, tag=None, page=ALL_PAGES,
71 def get_threads(self, tag=None, page=ALL_PAGES,
68 order_by='-last_edit_time'):
72 order_by='-last_edit_time'):
69 if tag:
73 if tag:
70 threads = self.filter(parent=NO_PARENT, tags=tag)
74 threads = self.filter(parent=NO_PARENT, tags=tag)
71
75
72 # TODO Throw error 404 if no threads for tag found?
76 # TODO Throw error 404 if no threads for tag found?
73 else:
77 else:
74 threads = self.filter(parent=NO_PARENT)
78 threads = self.filter(parent=NO_PARENT)
75
79
76 threads = threads.order_by(order_by)
80 threads = threads.order_by(order_by)
77
81
78 if page != ALL_PAGES:
82 if page != ALL_PAGES:
79 thread_count = len(threads)
83 thread_count = len(threads)
80
84
81 if page < self.get_thread_page_count(tag=tag):
85 if page < self.get_thread_page_count(tag=tag):
82 start_thread = page * settings.THREADS_PER_PAGE
86 start_thread = page * settings.THREADS_PER_PAGE
83 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
87 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
84 thread_count)
88 thread_count)
85 threads = threads[start_thread:end_thread]
89 threads = threads[start_thread:end_thread]
86
90
87 return threads
91 return threads
88
92
89 def get_thread(self, opening_post_id):
93 def get_thread(self, opening_post_id):
90 try:
94 try:
91 opening_post = self.get(id=opening_post_id, parent=NO_PARENT)
95 opening_post = self.get(id=opening_post_id, parent=NO_PARENT)
92 except Post.DoesNotExist:
96 except Post.DoesNotExist:
93 raise Http404
97 raise Http404
94
98
95 if opening_post.parent == NO_PARENT:
99 if opening_post.parent == NO_PARENT:
96 replies = self.filter(parent=opening_post_id)
100 replies = self.filter(parent=opening_post_id)
97
101
98 thread = [opening_post]
102 thread = [opening_post]
99 thread.extend(replies)
103 thread.extend(replies)
100
104
101 return thread
105 return thread
102
106
103 def exists(self, post_id):
107 def exists(self, post_id):
104 posts = self.filter(id=post_id)
108 posts = self.filter(id=post_id)
105
109
106 return posts.count() > 0
110 return posts.count() > 0
107
111
108 def get_thread_page_count(self, tag=None):
112 def get_thread_page_count(self, tag=None):
109 if tag:
113 if tag:
110 threads = self.filter(parent=NO_PARENT, tags=tag)
114 threads = self.filter(parent=NO_PARENT, tags=tag)
111 else:
115 else:
112 threads = self.filter(parent=NO_PARENT)
116 threads = self.filter(parent=NO_PARENT)
113
117
114 return int(math.ceil(threads.count() / float(
118 return int(math.ceil(threads.count() / float(
115 settings.THREADS_PER_PAGE)))
119 settings.THREADS_PER_PAGE)))
116
120
117 def _delete_old_threads(self):
121 def _delete_old_threads(self):
118 """
122 """
119 Preserves maximum thread count. If there are too many threads,
123 Preserves maximum thread count. If there are too many threads,
120 delete the old ones.
124 delete the old ones.
121 """
125 """
122
126
123 # TODO Move old threads to the archive instead of deleting them.
127 # TODO Move old threads to the archive instead of deleting them.
124 # Maybe make some 'old' field in the model to indicate the thread
128 # Maybe make some 'old' field in the model to indicate the thread
125 # must not be shown and be able for replying.
129 # must not be shown and be able for replying.
126
130
127 threads = self.get_threads()
131 threads = self.get_threads()
128 thread_count = len(threads)
132 thread_count = len(threads)
129
133
130 if thread_count > settings.MAX_THREAD_COUNT:
134 if thread_count > settings.MAX_THREAD_COUNT:
131 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
135 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
132 old_threads = threads[thread_count - num_threads_to_delete:]
136 old_threads = threads[thread_count - num_threads_to_delete:]
133
137
134 for thread in old_threads:
138 for thread in old_threads:
135 self.delete_post(thread)
139 self.delete_post(thread)
136
140
137 def _bump_thread(self, thread_id):
141 def _bump_thread(self, thread_id):
138 thread = self.get(id=thread_id)
142 thread = self.get(id=thread_id)
139
143
140 if thread.can_bump():
144 if thread.can_bump():
141 thread.last_edit_time = timezone.now()
145 thread.last_edit_time = timezone.now()
142 thread.save()
146 thread.save()
143
147
144
148
145 class TagManager(models.Manager):
149 class TagManager(models.Manager):
146 def get_not_empty_tags(self):
150 def get_not_empty_tags(self):
147 all_tags = self.all().order_by('name')
151 all_tags = self.all().order_by('name')
148 tags = []
152 tags = []
149 for tag in all_tags:
153 for tag in all_tags:
150 if not tag.is_empty():
154 if not tag.is_empty():
151 tags.append(tag)
155 tags.append(tag)
152
156
153 return tags
157 return tags
154
158
155 def get_popular_tags(self):
159 def get_popular_tags(self):
156 all_tags = self.get_not_empty_tags()
160 all_tags = self.get_not_empty_tags()
157
161
158 sorted_tags = sorted(all_tags, key=lambda tag: tag.get_popularity(),
162 sorted_tags = sorted(all_tags, key=lambda tag: tag.get_popularity(),
159 reverse=True)
163 reverse=True)
160
164
161 return sorted_tags[:settings.POPULAR_TAGS]
165 return sorted_tags[:settings.POPULAR_TAGS]
162
166
163
167
164 class Tag(models.Model):
168 class Tag(models.Model):
165 """
169 """
166 A tag is a text node assigned to the post. The tag serves as a board
170 A tag is a text node assigned to the post. The tag serves as a board
167 section. There can be multiple tags for each message
171 section. There can be multiple tags for each message
168 """
172 """
169
173
170 objects = TagManager()
174 objects = TagManager()
171
175
172 name = models.CharField(max_length=100)
176 name = models.CharField(max_length=100)
173 # TODO Connect the tag to its posts to check the number of threads for
177 # TODO Connect the tag to its posts to check the number of threads for
174 # the tag.
178 # the tag.
175
179
176 def __unicode__(self):
180 def __unicode__(self):
177 return self.name
181 return self.name
178
182
179 def is_empty(self):
183 def is_empty(self):
180 return self.get_post_count() == 0
184 return self.get_post_count() == 0
181
185
182 def get_post_count(self):
186 def get_post_count(self):
183 posts_with_tag = Post.objects.get_threads(tag=self)
187 posts_with_tag = Post.objects.get_threads(tag=self)
184 return posts_with_tag.count()
188 return posts_with_tag.count()
185
189
186 def get_popularity(self):
190 def get_popularity(self):
187 posts_with_tag = Post.objects.get_threads(tag=self)
191 posts_with_tag = Post.objects.get_threads(tag=self)
188 reply_count = 0
192 reply_count = 0
189 for post in posts_with_tag:
193 for post in posts_with_tag:
190 reply_count += post.get_reply_count()
194 reply_count += post.get_reply_count()
191 reply_count += OPENING_POST_POPULARITY_WEIGHT
195 reply_count += OPENING_POST_POPULARITY_WEIGHT
192
196
193 return reply_count
197 return reply_count
194
198
195
199
196 class Post(models.Model):
200 class Post(models.Model):
197 """A post is a message."""
201 """A post is a message."""
198
202
199 objects = PostManager()
203 objects = PostManager()
200
204
201 def _update_image_filename(self, filename):
205 def _update_image_filename(self, filename):
202 """Get unique image filename"""
206 """Get unique image filename"""
203
207
204 path = IMAGES_DIRECTORY
208 path = IMAGES_DIRECTORY
205 new_name = str(int(time.mktime(time.gmtime())))
209 new_name = str(int(time.mktime(time.gmtime())))
206 new_name += str(int(random() * 1000))
210 new_name += str(int(random() * 1000))
207 new_name += FILE_EXTENSION_DELIMITER
211 new_name += FILE_EXTENSION_DELIMITER
208 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
212 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
209
213
210 return os.path.join(path, new_name)
214 return os.path.join(path, new_name)
211
215
212 title = models.CharField(max_length=TITLE_MAX_LENGTH)
216 title = models.CharField(max_length=TITLE_MAX_LENGTH)
213 pub_time = models.DateTimeField()
217 pub_time = models.DateTimeField()
214 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
218 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
215 escape_html=False)
219 escape_html=False)
216 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
220 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
217 blank=True, sizes=(IMAGE_THUMB_SIZE,))
221 blank=True, sizes=(IMAGE_THUMB_SIZE,))
218 poster_ip = models.IPAddressField()
222 poster_ip = models.IPAddressField()
219 poster_user_agent = models.TextField()
223 poster_user_agent = models.TextField()
220 parent = models.BigIntegerField()
224 parent = models.BigIntegerField()
221 tags = models.ManyToManyField(Tag)
225 tags = models.ManyToManyField(Tag)
222 last_edit_time = models.DateTimeField()
226 last_edit_time = models.DateTimeField()
223
227
224 def __unicode__(self):
228 def __unicode__(self):
225 return '#' + str(self.id) + ' ' + self.title + ' (' + self.text.raw + \
229 return '#' + str(self.id) + ' ' + self.title + ' (' + self.text.raw + \
226 ')'
230 ')'
227
231
228 def _get_replies(self):
232 def _get_replies(self):
229 return Post.objects.filter(parent=self.id)
233 return Post.objects.filter(parent=self.id)
230
234
231 def get_reply_count(self):
235 def get_reply_count(self):
232 return self._get_replies().count()
236 return self._get_replies().count()
233
237
234 def get_images_count(self):
238 def get_images_count(self):
235 images_count = 1 if self.image else 0
239 images_count = 1 if self.image else 0
236 for reply in self._get_replies():
240 for reply in self._get_replies():
237 if reply.image:
241 if reply.image:
238 images_count += 1
242 images_count += 1
239
243
240 return images_count
244 return images_count
241
245
242 def get_gets_count(self):
246 def get_gets_count(self):
243 gets_count = 1 if self.is_get() else 0
247 gets_count = 1 if self.is_get() else 0
244 for reply in self._get_replies():
248 for reply in self._get_replies():
245 if reply.is_get():
249 if reply.is_get():
246 gets_count += 1
250 gets_count += 1
247
251
248 return gets_count
252 return gets_count
249
253
250 def is_get(self):
254 def is_get(self):
251 """If the post has pretty id (1, 1000, 77777), than it is called GET"""
255 """If the post has pretty id (1, 1000, 77777), than it is called GET"""
252
256
253 first = self.id == 1
257 first = self.id == 1
254
258
255 id_str = str(self.id)
259 id_str = str(self.id)
256 pretty = REGEX_PRETTY.match(id_str)
260 pretty = REGEX_PRETTY.match(id_str)
257 same_digits = REGEX_SAME.match(id_str)
261 same_digits = REGEX_SAME.match(id_str)
258
262
259 return first or pretty or same_digits
263 return first or pretty or same_digits
260
264
261 def can_bump(self):
265 def can_bump(self):
262 """Check if the thread can be bumped by replying"""
266 """Check if the thread can be bumped by replying"""
263
267
264 replies_count = len(Post.objects.get_thread(self.id))
268 replies_count = len(Post.objects.get_thread(self.id))
265
269
266 return replies_count <= settings.MAX_POSTS_PER_THREAD
270 return replies_count <= settings.MAX_POSTS_PER_THREAD
267
271
268 def get_last_replies(self):
272 def get_last_replies(self):
269 if settings.LAST_REPLIES_COUNT > 0:
273 if settings.LAST_REPLIES_COUNT > 0:
270 reply_count = self.get_reply_count()
274 reply_count = self.get_reply_count()
271
275
272 if reply_count > 0:
276 if reply_count > 0:
273 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
277 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
274 reply_count)
278 reply_count)
275 last_replies = self._get_replies()[reply_count
279 last_replies = self._get_replies()[reply_count
276 - reply_count_to_show:]
280 - reply_count_to_show:]
277
281
278 return last_replies
282 return last_replies
279
283
280
284
281 class Admin(models.Model):
285 class Admin(models.Model):
282 """
286 """
283 Model for admin users
287 Model for admin users
284 """
288 """
285 name = models.CharField(max_length=100)
289 name = models.CharField(max_length=100)
286 password = models.CharField(max_length=100)
290 password = models.CharField(max_length=100)
287
291
288 def __unicode__(self):
292 def __unicode__(self):
289 return self.name + '/' + '*' * len(self.password)
293 return self.name + '/' + '*' * len(self.password)
290
294
291
295
292 class Setting(models.Model):
293
294 name = models.CharField(max_length=50)
295 value = models.CharField(max_length=50)
296
297
298 class User(models.Model):
296 class User(models.Model):
299
297
300 user_id = models.CharField(max_length=20)
298 user_id = models.CharField(max_length=50)
301 settings = models.ManyToManyField(Setting)
299 rank = models.IntegerField()
302
300
303 def save_setting(self, name, value):
301 def save_setting(self, name, value):
304 setting, created = self.settings.get_or_create(name=name)
302 setting, created = Setting.objects.get_or_create(name=name, user=self)
305 setting.value = value
303 setting.value = value
306 setting.save()
304 setting.save()
307
305
308 return setting
306 return setting
309
307
310 def get_setting(self, name):
308 def get_setting(self, name):
311 settings = self.settings.filter(name=name)
309 settings = Setting.objects.filter(name=name, user=self)
312 if len(settings) > 0:
310 if len(settings) > 0:
313 setting = settings[0]
311 setting = settings[0]
314 else:
312 else:
315 setting = None
313 setting = None
316
314
317 if setting:
315 if setting:
318 setting_value = setting.value
316 setting_value = setting.value
319 else:
317 else:
320 setting_value = None
318 setting_value = None
321
319
322 return setting_value
320 return setting_value
321
322 def is_moderator(self):
323 return RANK_MODERATOR >= self.rank
324
325 def __unicode__(self):
326 return self.user_id
327
328
329 class Setting(models.Model):
330
331 name = models.CharField(max_length=50)
332 value = models.CharField(max_length=50)
333 user = models.ForeignKey(User)
@@ -1,275 +1,280 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 .input_field {
11 .input_field {
12
12
13 }
13 }
14
14
15 .input_field_name {
15 .input_field_name {
16
16
17 }
17 }
18
18
19 .input_field_error {
19 .input_field_error {
20 color: #FF0000;
20 color: #FF0000;
21 }
21 }
22
22
23
23
24 .title {
24 .title {
25 font-weight: bold;
25 font-weight: bold;
26 color: #ffcc00;
26 color: #ffcc00;
27 }
27 }
28
28
29 .link, a {
29 .link, a {
30 color: #afdcec;
30 color: #afdcec;
31 }
31 }
32
32
33 .block {
33 .block {
34 display: inline-block;
34 display: inline-block;
35 vertical-align: top;
35 vertical-align: top;
36 }
36 }
37
37
38 .tag {
38 .tag {
39 color: #b4cfec;
39 color: #b4cfec;
40 }
40 }
41
41
42 .post_id {
42 .post_id {
43 color: #fff380;
43 color: #fff380;
44 }
44 }
45
45
46 .post, .dead_post {
46 .post, .dead_post {
47 background: #333;
47 background: #333;
48 margin: 5px;
48 margin: 5px;
49 padding: 10px;
49 padding: 10px;
50 border-radius: 5px;
50 border-radius: 5px;
51 clear: left;
51 clear: left;
52 word-wrap: break-word;
52 word-wrap: break-word;
53 }
53 }
54
54
55 .metadata {
55 .metadata {
56 padding: 5px;
56 padding: 5px;
57 margin-top: 10px;
57 margin-top: 10px;
58 border: solid 1px #666;
58 border: solid 1px #666;
59 font-size: 0.9em;
59 font-size: 0.9em;
60 color: #ddd;
60 color: #ddd;
61 display: table;
61 display: table;
62 }
62 }
63
63
64 .navigation_panel, .tag_info {
64 .navigation_panel, .tag_info {
65 background: #444;
65 background: #444;
66 margin: 5px;
66 margin: 5px;
67 padding: 10px;
67 padding: 10px;
68 border-radius: 5px;
68 border-radius: 5px;
69 color: #eee;
69 color: #eee;
70 }
70 }
71
71
72 .navigation_panel .link {
72 .navigation_panel .link {
73 border-right: 1px solid #fff;
73 border-right: 1px solid #fff;
74 font-weight: bold;
74 font-weight: bold;
75 margin-right: 1ex;
75 margin-right: 1ex;
76 padding-right: 1ex;
76 padding-right: 1ex;
77 }
77 }
78 .navigation_panel .link:last-child {
78 .navigation_panel .link:last-child {
79 border-left: 1px solid #fff;
79 border-left: 1px solid #fff;
80 border-right: none;
80 border-right: none;
81 float: right;
81 float: right;
82 margin-left: 1ex;
82 margin-left: 1ex;
83 margin-right: 0;
83 margin-right: 0;
84 padding-left: 1ex;
84 padding-left: 1ex;
85 padding-right: 0;
85 padding-right: 0;
86 }
86 }
87
87
88 .navigation_panel::after, .post::after {
88 .navigation_panel::after, .post::after {
89 clear: both;
89 clear: both;
90 content: ".";
90 content: ".";
91 display: block;
91 display: block;
92 height: 0;
92 height: 0;
93 line-height: 0;
93 line-height: 0;
94 visibility: hidden;
94 visibility: hidden;
95 }
95 }
96
96
97 p {
97 p {
98 margin-top: .5em;
98 margin-top: .5em;
99 margin-bottom: .5em;
99 margin-bottom: .5em;
100 }
100 }
101
101
102 .post-form-w {
102 .post-form-w {
103 display: table;
103 display: table;
104 background: #333344;
104 background: #333344;
105 border-radius: 5px;
105 border-radius: 5px;
106 color: #fff;
106 color: #fff;
107 padding: 10px;
107 padding: 10px;
108 margin: 5px
108 margin: 5px
109 }
109 }
110
110
111 .form-row {
111 .form-row {
112 display: table-row;
112 display: table-row;
113 }
113 }
114
114
115 .form-label, .form-input, .form-errors {
115 .form-label, .form-input, .form-errors {
116 display: table-cell;
116 display: table-cell;
117 }
117 }
118
118
119 .form-label {
119 .form-label {
120 padding: .25em 1ex .25em 0;
120 padding: .25em 1ex .25em 0;
121 vertical-align: top;
121 vertical-align: top;
122 }
122 }
123
123
124 .form-input {
124 .form-input {
125 padding: .25em 0;
125 padding: .25em 0;
126 }
126 }
127
127
128 .form-errors {
128 .form-errors {
129 padding-left: 1ex;
129 padding-left: 1ex;
130 font-weight: bold;
130 font-weight: bold;
131 vertical-align: top;
131 vertical-align: top;
132 }
132 }
133
133
134 .post-form input, .post-form textarea {
134 .post-form input, .post-form textarea {
135 background: #333;
135 background: #333;
136 color: #fff;
136 color: #fff;
137 border: solid 1px;
137 border: solid 1px;
138 padding: 0;
138 padding: 0;
139 width: 100%;
139 width: 100%;
140 }
140 }
141
141
142 .form-submit {
142 .form-submit {
143 border-bottom: 2px solid #ddd;
143 border-bottom: 2px solid #ddd;
144 margin-bottom: .5em;
144 margin-bottom: .5em;
145 padding-bottom: .5em;
145 padding-bottom: .5em;
146 }
146 }
147
147
148 .form-title {
148 .form-title {
149 font-weight: bold;
149 font-weight: bold;
150 }
150 }
151
151
152 input[type="submit"] {
152 input[type="submit"] {
153 background: #222;
153 background: #222;
154 border: solid 1px #fff;
154 border: solid 1px #fff;
155 color: #fff;
155 color: #fff;
156 }
156 }
157
157
158 blockquote {
158 blockquote {
159 border-left: solid 2px;
159 border-left: solid 2px;
160 padding-left: 5px;
160 padding-left: 5px;
161 color: #B1FB17;
161 color: #B1FB17;
162 margin: 0;
162 margin: 0;
163 }
163 }
164
164
165 .post > .image {
165 .post > .image {
166 float: left;
166 float: left;
167 margin: 0 1ex .5ex 0;
167 margin: 0 1ex .5ex 0;
168 min-width: 1px;
168 min-width: 1px;
169 height: 150px;
169 height: 150px;
170 text-align: center;
170 text-align: center;
171 display: table-row;
171 display: table-row;
172 }
172 }
173
173
174 .post > .metadata {
174 .post > .metadata {
175 clear: left;
175 clear: left;
176 }
176 }
177
177
178 .get {
178 .get {
179 font-weight: bold;
179 font-weight: bold;
180 color: #d55;
180 color: #d55;
181 }
181 }
182
182
183 * {
183 * {
184 text-decoration: none;
184 text-decoration: none;
185 }
185 }
186
186
187 .dead_post {
187 .dead_post {
188 background-color: #442222;
188 background-color: #442222;
189 }
189 }
190
190
191 .quote {
191 .quote {
192 color: #92cf38;
192 color: #92cf38;
193 }
193 }
194
194
195 .spoiler {
195 .spoiler {
196 background: white;
196 background: white;
197 color: white;
197 color: white;
198 }
198 }
199
199
200 .spoiler:hover {
200 .spoiler:hover {
201 background: black;
201 background: black;
202 }
202 }
203
203
204 .comment {
204 .comment {
205 color: #eb2;
205 color: #eb2;
206 font-style: italic;
206 font-style: italic;
207 }
207 }
208
208
209 a:hover {
209 a:hover {
210 text-decoration: underline;
210 text-decoration: underline;
211 }
211 }
212
212
213 .last-replies {
213 .last-replies {
214 margin-left: 3ex;
214 margin-left: 3ex;
215 }
215 }
216
216
217 .thread {
217 .thread {
218 margin-bottom: 3ex;
218 margin-bottom: 3ex;
219 }
219 }
220
220
221 .post:target {
221 .post:target {
222 border: solid 2px white;
222 border: solid 2px white;
223 }
223 }
224
224
225 pre{
225 pre{
226 white-space:pre-wrap
226 white-space:pre-wrap
227 }
227 }
228
228
229 li {
229 li {
230 list-style-position: inside;
230 list-style-position: inside;
231 }
231 }
232
232
233 .fancybox-skin {
233 .fancybox-skin {
234 position: relative;
234 position: relative;
235 background-color: #fff;
235 background-color: #fff;
236 color: #ddd;
236 color: #ddd;
237 text-shadow: none;
237 text-shadow: none;
238 }
238 }
239
239
240 .fancybox-image {
240 .fancybox-image {
241 border: 1px solid black;
241 border: 1px solid black;
242 }
242 }
243
243
244 .image-mode-tab {
244 .image-mode-tab {
245 background: #444;
245 background: #444;
246 color: #eee;
246 color: #eee;
247 display: table;
247 display: table;
248 margin: 5px;
248 margin: 5px;
249 padding: 5px;
249 padding: 5px;
250 border-radius: 5px;
250 border-radius: 5px;
251 }
251 }
252
252
253 .image-mode-tab > label {
253 .image-mode-tab > label {
254 margin: 0 1ex;
254 margin: 0 1ex;
255 }
255 }
256
256
257 .image-mode-tab > label > input {
257 .image-mode-tab > label > input {
258 margin-right: .5ex;
258 margin-right: .5ex;
259 }
259 }
260
260
261 #posts-table {
261 #posts-table {
262 margin: 5px;
262 margin: 5px;
263 }
263 }
264
264
265 .tag_info {
265 .tag_info {
266 display: table;
266 display: table;
267 }
267 }
268
268
269 .tag_info > h2 {
269 .tag_info > h2 {
270 margin: 0;
270 margin: 0;
271 }
271 }
272
272
273 .post-info {
273 .post-info {
274 color: #ddd;
274 color: #ddd;
275 }
275 }
276
277 .moderator_info {
278 color: #e99d41;
279 float: right;
280 } No newline at end of file
@@ -1,264 +1,269 b''
1 * {
1 * {
2 font-size: inherit;
2 font-size: inherit;
3 margin: 0;
3 margin: 0;
4 padding: 0;
4 padding: 0;
5 }
5 }
6 html {
6 html {
7 background: #fff;
7 background: #fff;
8 color: #000;
8 color: #000;
9 font: medium sans-serif;
9 font: medium sans-serif;
10 }
10 }
11 a {
11 a {
12 color: inherit;
12 color: inherit;
13 text-decoration: underline;
13 text-decoration: underline;
14 }
14 }
15 li {
15 li {
16 list-style-position: inside;
16 list-style-position: inside;
17 }
17 }
18
18
19 #admin_panel {
19 #admin_panel {
20 background: #182F6F;
20 background: #182F6F;
21 color: #fff;
21 color: #fff;
22 padding: .5ex 1ex .5ex 1ex;
22 padding: .5ex 1ex .5ex 1ex;
23 }
23 }
24
24
25 .navigation_panel {
25 .navigation_panel {
26 background: #182F6F;
26 background: #182F6F;
27 color: #B4CFEC;
27 color: #B4CFEC;
28 margin-bottom: 1em;
28 margin-bottom: 1em;
29 padding: .5ex 1ex 1ex 1ex;
29 padding: .5ex 1ex 1ex 1ex;
30 }
30 }
31 .navigation_panel::after {
31 .navigation_panel::after {
32 clear: both;
32 clear: both;
33 content: ".";
33 content: ".";
34 display: block;
34 display: block;
35 height: 0;
35 height: 0;
36 line-height: 0;
36 line-height: 0;
37 visibility: hidden;
37 visibility: hidden;
38 }
38 }
39
39
40 .navigation_panel a:link, .navigation_panel a:visited, .navigation_panel a:hover {
40 .navigation_panel a:link, .navigation_panel a:visited, .navigation_panel a:hover {
41 text-decoration: none;
41 text-decoration: none;
42 }
42 }
43
43
44 .navigation_panel .link {
44 .navigation_panel .link {
45 border-right: 1px solid #fff;
45 border-right: 1px solid #fff;
46 color: #fff;
46 color: #fff;
47 font-weight: bold;
47 font-weight: bold;
48 margin-right: 1ex;
48 margin-right: 1ex;
49 padding-right: 1ex;
49 padding-right: 1ex;
50 }
50 }
51 .navigation_panel .link:last-child {
51 .navigation_panel .link:last-child {
52 border-left: 1px solid #fff;
52 border-left: 1px solid #fff;
53 border-right: none;
53 border-right: none;
54 float: right;
54 float: right;
55 margin-left: 1ex;
55 margin-left: 1ex;
56 margin-right: 0;
56 margin-right: 0;
57 padding-left: 1ex;
57 padding-left: 1ex;
58 padding-right: 0;
58 padding-right: 0;
59 }
59 }
60
60
61 .navigation_panel .tag {
61 .navigation_panel .tag {
62 color: #fff;
62 color: #fff;
63 }
63 }
64
64
65 .input_field {
65 .input_field {
66
66
67 }
67 }
68
68
69 .input_field_name {
69 .input_field_name {
70
70
71 }
71 }
72
72
73 .input_field_error {
73 .input_field_error {
74 color: #FF0000;
74 color: #FF0000;
75 }
75 }
76
76
77
77
78 .title {
78 .title {
79 color: #182F6F;
79 color: #182F6F;
80 font-weight: bold;
80 font-weight: bold;
81 }
81 }
82
82
83 .post-form-w {
83 .post-form-w {
84 background: #182F6F;
84 background: #182F6F;
85 border-radius: 1ex;
85 border-radius: 1ex;
86 color: #fff;
86 color: #fff;
87 margin: 1em 1ex;
87 margin: 1em 1ex;
88 padding: 1ex;
88 padding: 1ex;
89 }
89 }
90 .post-form {
90 .post-form {
91 display: table;
91 display: table;
92 border-collapse: collapse;
92 border-collapse: collapse;
93 width: 100%;
93 width: 100%;
94
94
95 }
95 }
96 .form-row {
96 .form-row {
97 display: table-row;
97 display: table-row;
98 }
98 }
99 .form-label, .form-input {
99 .form-label, .form-input {
100 display: table-cell;
100 display: table-cell;
101 vertical-align: top;
101 vertical-align: top;
102 }
102 }
103 .form-label {
103 .form-label {
104 padding: .25em 1ex .25em 0;
104 padding: .25em 1ex .25em 0;
105 }
105 }
106 .form-input {
106 .form-input {
107 padding: .25em 0;
107 padding: .25em 0;
108 }
108 }
109 .form-input > * {
109 .form-input > * {
110 background: #fff;
110 background: #fff;
111 color: #000;
111 color: #000;
112 border: none;
112 border: none;
113 padding: 0;
113 padding: 0;
114 resize: vertical;
114 resize: vertical;
115 width: 100%;
115 width: 100%;
116 }
116 }
117 .form-submit {
117 .form-submit {
118 border-bottom: 1px solid #666;
118 border-bottom: 1px solid #666;
119 margin-bottom: .5em;
119 margin-bottom: .5em;
120 padding-bottom: .5em;
120 padding-bottom: .5em;
121 }
121 }
122 .form-title {
122 .form-title {
123 font-weight: bold;
123 font-weight: bold;
124 margin-bottom: .5em;
124 margin-bottom: .5em;
125 }
125 }
126 .post-form .settings_item {
126 .post-form .settings_item {
127 margin: .5em 0;
127 margin: .5em 0;
128 }
128 }
129 .form-submit input {
129 .form-submit input {
130 margin-top: .5em;
130 margin-top: .5em;
131 padding: .2em 1ex;
131 padding: .2em 1ex;
132 }
132 }
133 .form-label {
133 .form-label {
134 text-align: right;
134 text-align: right;
135 }
135 }
136
136
137 .block {
137 .block {
138 display: inline-block;
138 display: inline-block;
139 vertical-align: top;
139 vertical-align: top;
140 }
140 }
141
141
142 .post_id {
142 .post_id {
143 color: #a00;
143 color: #a00;
144 }
144 }
145
145
146 .post {
146 .post {
147 clear: left;
147 clear: left;
148 margin: 0 1ex 1em 1ex;
148 margin: 0 1ex 1em 1ex;
149 overflow-x: auto;
149 overflow-x: auto;
150 word-wrap: break-word;
150 word-wrap: break-word;
151 }
151 }
152 .last-replies > .post, #posts > .post {
152 .last-replies > .post, #posts > .post {
153 border-bottom: 1px solid #182F6F;
153 border-bottom: 1px solid #182F6F;
154 padding-bottom: 1em;
154 padding-bottom: 1em;
155 }
155 }
156 #posts > .post:last-child {
156 #posts > .post:last-child {
157 border-bottom: none;
157 border-bottom: none;
158 padding-bottom: 0;
158 padding-bottom: 0;
159 }
159 }
160
160
161 .metadata {
161 .metadata {
162 background: #C0E4E8;
162 background: #C0E4E8;
163 border: 1px solid #7F9699;
163 border: 1px solid #7F9699;
164 border-radius: .4ex;
164 border-radius: .4ex;
165 display: table;
165 display: table;
166 margin-top: .5em;
166 margin-top: .5em;
167 padding: .4em;
167 padding: .4em;
168 }
168 }
169
169
170 .post ul, .post ol {
170 .post ul, .post ol {
171 margin: .5em 0 .5em 3ex;
171 margin: .5em 0 .5em 3ex;
172 }
172 }
173 .post li {
173 .post li {
174 margin: .2em 0;
174 margin: .2em 0;
175 }
175 }
176 .post p {
176 .post p {
177 margin: .5em 0;
177 margin: .5em 0;
178 }
178 }
179 .post blockquote {
179 .post blockquote {
180 border-left: 3px solid #182F6F;
180 border-left: 3px solid #182F6F;
181 margin: .5em 0 .5em 3ex;
181 margin: .5em 0 .5em 3ex;
182 padding-left: 1ex;
182 padding-left: 1ex;
183 }
183 }
184 .post blockquote > blockquote {
184 .post blockquote > blockquote {
185 padding-top: .1em;
185 padding-top: .1em;
186 }
186 }
187
187
188 .post > .image {
188 .post > .image {
189 float: left;
189 float: left;
190 margin-right: 1ex;
190 margin-right: 1ex;
191 }
191 }
192 .post > .metadata {
192 .post > .metadata {
193 clear: left;
193 clear: left;
194 }
194 }
195
195
196 .post > .message .get {
196 .post > .message .get {
197 color: #182F6F; font-weight: bold;
197 color: #182F6F; font-weight: bold;
198 }
198 }
199
199
200 .dead_post > .metadata {
200 .dead_post > .metadata {
201 background: #eee;
201 background: #eee;
202 }
202 }
203
203
204 .quote {
204 .quote {
205 color: #182F6F;
205 color: #182F6F;
206 }
206 }
207
207
208 .spoiler {
208 .spoiler {
209 background: black;
209 background: black;
210 color: black;
210 color: black;
211 }
211 }
212
212
213 .spoiler:hover {
213 .spoiler:hover {
214 background: #ffffff;
214 background: #ffffff;
215 }
215 }
216
216
217 .comment {
217 .comment {
218 color: #557055;
218 color: #557055;
219 }
219 }
220
220
221 .last-replies {
221 .last-replies {
222 margin-left: 6ex;
222 margin-left: 6ex;
223 }
223 }
224
224
225 .thread > .post > .message > .post-info {
225 .thread > .post > .message > .post-info {
226 border-bottom: 2px solid #182F6F;
226 border-bottom: 2px solid #182F6F;
227 padding-bottom: .5em;
227 padding-bottom: .5em;
228 }
228 }
229
229
230 .last-replies > .post:last-child {
230 .last-replies > .post:last-child {
231 border-bottom: none;
231 border-bottom: none;
232 padding-bottom: 0;
232 padding-bottom: 0;
233 }
233 }
234
234
235 :target .post_id {
235 :target .post_id {
236 background: #182F6F;
236 background: #182F6F;
237 color: #FFF;
237 color: #FFF;
238 text-decoration: none;
238 text-decoration: none;
239 }
239 }
240
240
241 .image-mode-tab {
241 .image-mode-tab {
242 background: #182F6F;
242 background: #182F6F;
243 color: #FFF;
243 color: #FFF;
244 display: table;
244 display: table;
245 margin: 1em auto 1em 0;
245 margin: 1em auto 1em 0;
246 padding: .2em .5ex;
246 padding: .2em .5ex;
247 }
247 }
248
248
249 .image-mode-tab > label {
249 .image-mode-tab > label {
250 margin: 0 1ex;
250 margin: 0 1ex;
251 }
251 }
252
252
253 .image-mode-tab > label > input {
253 .image-mode-tab > label > input {
254 margin-right: .5ex;
254 margin-right: .5ex;
255 }
255 }
256
256
257 .tag_info {
257 .tag_info {
258 margin: 1em 0;
258 margin: 1em 0;
259 text-align: center;
259 text-align: center;
260 }
260 }
261
261
262 .form-errors {
262 .form-errors {
263 margin-left: 1ex;
263 margin-left: 1ex;
264 }
265
266 .moderator_info {
267 font-weight: bold;
268 float: right;
264 } No newline at end of file
269 }
@@ -1,52 +1,45 b''
1 {% load staticfiles %}
1 {% load staticfiles %}
2 {% load i18n %}
2 {% load i18n %}
3
3
4 <!DOCTYPE html>
4 <!DOCTYPE html>
5 <html>
5 <html>
6 <head>
6 <head>
7 <link rel="stylesheet" type="text/css"
7 <link rel="stylesheet" type="text/css"
8 href="{{ STATIC_URL }}css/jquery.fancybox.css" media="all"/>
8 href="{{ STATIC_URL }}css/jquery.fancybox.css" media="all"/>
9 <link rel="stylesheet" type="text/css"
9 <link rel="stylesheet" type="text/css"
10 href="{{ STATIC_URL }}css/{{ theme }}/base_page.css" media="all"/>
10 href="{{ STATIC_URL }}css/{{ theme }}/base_page.css" media="all"/>
11 <link rel="alternate" type="application/rss+xml" href="rss/" title="
11 <link rel="alternate" type="application/rss+xml" href="rss/" title="
12 {% trans 'Feed' %}"/>
12 {% trans 'Feed' %}"/>
13
13
14 <link rel="icon" type="image/png"
14 <link rel="icon" type="image/png"
15 href="{{ STATIC_URL }}favicon.png">
15 href="{{ STATIC_URL }}favicon.png">
16
16
17 <meta name="viewport" content="width=device-width, initial-scale=1"/>
17 <meta name="viewport" content="width=device-width, initial-scale=1"/>
18 <meta charset="utf-8"/>
18 <meta charset="utf-8"/>
19 {% block head %}{% endblock %}
19 {% block head %}{% endblock %}
20 </head>
20 </head>
21 <body>
21 <body>
22 <script src="{{ STATIC_URL }}js/jquery-2.0.1.min.js"></script>
22 <script src="{{ STATIC_URL }}js/jquery-2.0.1.min.js"></script>
23 <script src="{{ STATIC_URL }}js/jquery.fancybox.pack.js"></script>
23 <script src="{{ STATIC_URL }}js/jquery.fancybox.pack.js"></script>
24 <script src="{{ STATIC_URL }}js/main.js"></script>
24 <script src="{{ STATIC_URL }}js/main.js"></script>
25 <div id="admin_panel">
26
27 {% if request.session.admin == True %}
28 Admin panel TODO: Need to implement <BR />
29 {% endif %}
30
31 </div>
32
25
33 <div class="navigation_panel">
26 <div class="navigation_panel">
34 <a class="link" href="{% url 'index' %}">{% trans "All threads" %}</a>
27 <a class="link" href="{% url 'index' %}">{% trans "All threads" %}</a>
35 {% for tag in tags %}
28 {% for tag in tags %}
36 <a class="tag" href=" {% url 'tag' tag_name=tag.name %}">
29 <a class="tag" href="{% url 'tag' tag_name=tag.name %}">
37 {{ tag.name }}</a>({{ tag.get_post_count }})
30 {{ tag.name }}</a>({{ tag.get_post_count }})
38 {% endfor %}
31 {% endfor %}
39 <a class="tag" href="{% url 'tags' %}">[...]</a>
32 <a class="tag" href="{% url 'tags' %}">[...]</a>
40 <a class="link" href="{% url 'settings' %}">{% trans 'Settings' %}</a>
33 <a class="link" href="{% url 'settings' %}">{% trans 'Settings' %}</a>
41 </div>
34 </div>
42
35
43 {% block content %}{% endblock %}
36 {% block content %}{% endblock %}
44
37
45 <div class="navigation_panel">
38 <div class="navigation_panel">
46 {% block metapanel %}{% endblock %}
39 {% block metapanel %}{% endblock %}
47 [<a href="rss/">RSS</a>]
40 [<a href="rss/">RSS</a>]
48 <a class="link" href="#top">{% trans 'Up' %}</a>
41 <a class="link" href="#top">{% trans 'Up' %}</a>
49 </div>
42 </div>
50
43
51 </body>
44 </body>
52 </html> No newline at end of file
45 </html>
@@ -1,164 +1,172 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load markup %}
4 {% load markup %}
5
5
6 {% block head %}
6 {% block head %}
7 {% if tag %}
7 {% if tag %}
8 <title>Neboard - {{ tag }}</title>
8 <title>Neboard - {{ tag }}</title>
9 {% else %}
9 {% else %}
10 <title>Neboard</title>
10 <title>Neboard</title>
11 {% endif %}
11 {% endif %}
12 {% endblock %}
12 {% endblock %}
13
13
14 {% block content %}
14 {% block content %}
15
15
16 {% if tag %}
16 {% if tag %}
17 <div class="tag_info">
17 <div class="tag_info">
18 <h2>{% trans 'Tag: ' %}{{ tag }}</h2>
18 <h2>{% trans 'Tag: ' %}{{ tag }}</h2>
19 </div>
19 </div>
20 {% endif %}
20 {% endif %}
21
21
22 {% if threads %}
22 {% if threads %}
23 {% for thread in threads %}
23 {% for thread in threads %}
24 <div class="thread">
24 <div class="thread">
25 {% if thread.can_bump %}
25 {% if thread.can_bump %}
26 <div class="post">
26 <div class="post">
27 {% else %}
27 {% else %}
28 <div class="post dead_post">
28 <div class="post dead_post">
29 {% endif %}
29 {% endif %}
30 {% if thread.image %}
30 {% if thread.image %}
31 <div class="image">
31 <div class="image">
32 <a class="fancy"
32 <a class="fancy"
33 href="{{ thread.image.url }}"><img
33 href="{{ thread.image.url }}"><img
34 src="{{ thread.image.url_200x150 }}"
34 src="{{ thread.image.url_200x150 }}"
35 alt="{% trans 'Post image' %}" />
35 alt="{% trans 'Post image' %}" />
36 </a>
36 </a>
37 </div>
37 </div>
38 {% endif %}
38 {% endif %}
39 <div class="message">
39 <div class="message">
40 <div class="post-info">
40 <div class="post-info">
41 <span class="title">{{ thread.title }}</span>
41 <span class="title">{{ thread.title }}</span>
42 <a class="post_id" href="{% url 'thread' thread.id %}">
42 <a class="post_id" href="{% url 'thread' thread.id %}">
43 (#{{ thread.id }})</a>
43 (#{{ thread.id }})</a>
44 [{{ thread.pub_time }}]
44 [{{ thread.pub_time }}]
45 [<a class="link" href="{% url 'thread' thread.id %}#form"
45 [<a class="link" href="{% url 'thread' thread.id %}#form"
46 >{% trans "Reply" %}</a>]
46 >{% trans "Reply" %}</a>]
47
48 {% if user.is_moderator %}
49 <span class="moderator_info">
50 ({{ thread.poster_ip }})
51 [<a href="{% url 'delete' post_id=thread.id %}"
52 >{% trans 'Delete' %}</a>]
53 </span>
54 {% endif %}
47 </div>
55 </div>
48 {% autoescape off %}
56 {% autoescape off %}
49 {{ thread.text.rendered|truncatewords_html:50 }}
57 {{ thread.text.rendered|truncatewords_html:50 }}
50 {% endautoescape %}
58 {% endautoescape %}
51 </div>
59 </div>
52 <div class="metadata">
60 <div class="metadata">
53 {{ thread.get_reply_count }} {% trans 'replies' %},
61 {{ thread.get_reply_count }} {% trans 'replies' %},
54 {{ thread.get_images_count }} {% trans 'images' %}.
62 {{ thread.get_images_count }} {% trans 'images' %}.
55 {% if thread.tags.all %}
63 {% if thread.tags.all %}
56 <span class="tags">{% trans 'Tags' %}:
64 <span class="tags">{% trans 'Tags' %}:
57 {% for tag in thread.tags.all %}
65 {% for tag in thread.tags.all %}
58 <a class="tag" href="
66 <a class="tag" href="
59 {% url 'tag' tag_name=tag.name %}">
67 {% url 'tag' tag_name=tag.name %}">
60 {{ tag.name }}</a>
68 {{ tag.name }}</a>
61 {% endfor %}
69 {% endfor %}
62 </span>
70 </span>
63 {% endif %}
71 {% endif %}
64 </div>
72 </div>
65 </div>
73 </div>
66 {% if thread.get_last_replies %}
74 {% if thread.get_last_replies %}
67 <div class="last-replies">
75 <div class="last-replies">
68 {% for post in thread.get_last_replies %}
76 {% for post in thread.get_last_replies %}
69 {% if thread.can_bump %}
77 {% if thread.can_bump %}
70 <div class="post">
78 <div class="post">
71 {% else %}
79 {% else %}
72 <div class="post dead_post">
80 <div class="post dead_post">
73 {% endif %}
81 {% endif %}
74 {% if post.image %}
82 {% if post.image %}
75 <div class="image">
83 <div class="image">
76 <a class="fancy"
84 <a class="fancy"
77 href="{{ post.image.url }}"><img
85 href="{{ post.image.url }}"><img
78 src=" {{ post.image.url_200x150 }}"
86 src=" {{ post.image.url_200x150 }}"
79 alt="{% trans 'Post image' %}" />
87 alt="{% trans 'Post image' %}" />
80 </a>
88 </a>
81 </div>
89 </div>
82 {% endif %}
90 {% endif %}
83 <div class="message">
91 <div class="message">
84 <div class="post-info">
92 <div class="post-info">
85 <span class="title">{{ post.title }}</span>
93 <span class="title">{{ post.title }}</span>
86 <a class="post_id" href="
94 <a class="post_id" href="
87 {% url 'thread' thread.id %}#{{ post.id }}">
95 {% url 'thread' thread.id %}#{{ post.id }}">
88 (#{{ post.id }})</a>
96 (#{{ post.id }})</a>
89 [{{ post.pub_time }}]
97 [{{ post.pub_time }}]
90 </div>
98 </div>
91 {% autoescape off %}
99 {% autoescape off %}
92 {{ post.text.rendered|truncatewords_html:50 }}
100 {{ post.text.rendered|truncatewords_html:50 }}
93 {% endautoescape %}
101 {% endautoescape %}
94 </div>
102 </div>
95 </div>
103 </div>
96 {% endfor %}
104 {% endfor %}
97 </div>
105 </div>
98 {% endif %}
106 {% endif %}
99 </div>
107 </div>
100 {% endfor %}
108 {% endfor %}
101 {% else %}
109 {% else %}
102 No threads found.
110 No threads found.
103 <hr />
111 <hr />
104 {% endif %}
112 {% endif %}
105
113
106 <form enctype="multipart/form-data" method="post">{% csrf_token %}
114 <form enctype="multipart/form-data" method="post">{% csrf_token %}
107 <div class="post-form-w">
115 <div class="post-form-w">
108
116
109 <div class="form-title">{% trans "Create new thread" %}</div>
117 <div class="form-title">{% trans "Create new thread" %}</div>
110 <div class="post-form">
118 <div class="post-form">
111 <div class="form-row">
119 <div class="form-row">
112 <div class="form-label">{% trans 'Title' %}</div>
120 <div class="form-label">{% trans 'Title' %}</div>
113 <div class="form-input">{{ form.title }}</div>
121 <div class="form-input">{{ form.title }}</div>
114 <div class="form-errors">{{ form.title.errors }}</div>
122 <div class="form-errors">{{ form.title.errors }}</div>
115 </div>
123 </div>
116 <div class="form-row">
124 <div class="form-row">
117 <div class="form-label">{% trans 'Text' %}</div>
125 <div class="form-label">{% trans 'Text' %}</div>
118 <div class="form-input">{{ form.text }}</div>
126 <div class="form-input">{{ form.text }}</div>
119 <div class="form-errors">{{ form.text.errors }}</div>
127 <div class="form-errors">{{ form.text.errors }}</div>
120 </div>
128 </div>
121 <div class="form-row">
129 <div class="form-row">
122 <div class="form-label">{% trans 'Image' %}</div>
130 <div class="form-label">{% trans 'Image' %}</div>
123 <div class="form-input">{{ form.image }}</div>
131 <div class="form-input">{{ form.image }}</div>
124 <div class="form-errors">{{ form.image.errors }}</div>
132 <div class="form-errors">{{ form.image.errors }}</div>
125 </div>
133 </div>
126 <div class="form-row">
134 <div class="form-row">
127 <div class="form-label">{% trans 'Tags' %}</div>
135 <div class="form-label">{% trans 'Tags' %}</div>
128 <div class="form-input">{{ form.tags }}</div>
136 <div class="form-input">{{ form.tags }}</div>
129 <div class="form-errors">{{ form.tags.errors }}</div>
137 <div class="form-errors">{{ form.tags.errors }}</div>
130 </div>
138 </div>
131 <div class="form-row">
139 <div class="form-row">
132 {{ form.captcha }}
140 {{ form.captcha }}
133 <div class="form-errors">{{ form.captcha.errors }}</div>
141 <div class="form-errors">{{ form.captcha.errors }}</div>
134 </div>
142 </div>
135 </div>
143 </div>
136 <div class="form-submit">
144 <div class="form-submit">
137 <input type="submit" value="{% trans "Post" %}"/></div>
145 <input type="submit" value="{% trans "Post" %}"/></div>
138 <div>
146 <div>
139 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
147 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
140 </div>
148 </div>
141 <div><a href="http://daringfireball.net/projects/markdown/basics">
149 <div><a href="http://daringfireball.net/projects/markdown/basics">
142 {% trans 'Basic markdown syntax.' %}</a></div>
150 {% trans 'Basic markdown syntax.' %}</a></div>
143 </div>
151 </div>
144 </form>
152 </form>
145
153
146 {% endblock %}
154 {% endblock %}
147
155
148 {% block metapanel %}
156 {% block metapanel %}
149
157
150 <span class="metapanel">
158 <span class="metapanel">
151 <b><a href="{% url "authors" %}">Neboard</a> pre1.0</b>
159 <b><a href="{% url "authors" %}">Neboard</a> pre1.0</b>
152 {% trans "Pages:" %}
160 {% trans "Pages:" %}
153 {% for page in pages %}
161 {% for page in pages %}
154 [<a href="
162 [<a href="
155 {% if tag %}
163 {% if tag %}
156 {% url "tag" tag_name=tag page=page %}
164 {% url "tag" tag_name=tag page=page %}
157 {% else %}
165 {% else %}
158 {% url "index" page=page %}
166 {% url "index" page=page %}
159 {% endif %}
167 {% endif %}
160 ">{{ page }}</a>]
168 ">{{ page }}</a>]
161 {% endfor %}
169 {% endfor %}
162 </span>
170 </span>
163
171
164 {% endblock %}
172 {% endblock %}
@@ -1,38 +1,41 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4
4
5 {% block head %}
5 {% block head %}
6 <title>Neboard settings</title>
6 <title>Neboard settings</title>
7 {% endblock %}
7 {% endblock %}
8
8
9 {% block content %}
9 {% block content %}
10
10
11 <div class="post">
11 <div class="post">
12 {% trans 'User:' %} <b>{{ request.session.user.user_id }}</b>
12 {% trans 'User:' %} <b>{{ user.user_id }}</b>.
13 {% if user.is_moderator %}
14 {% trans 'You are moderator.' %}
15 {% endif %}
13 </div>
16 </div>
14
17
15 <div class="post-form-w">
18 <div class="post-form-w">
16 <div class="post-form">
19 <div class="post-form">
17 <span class="form-title">{% trans "Theme" %}</span>
20 <span class="form-title">{% trans "Theme" %}</span>
18 <form method="post">{% csrf_token %}
21 <form method="post">{% csrf_token %}
19 {% for choice in form.fields.theme.choices %}
22 {% for choice in form.fields.theme.choices %}
20 <div class="settings_item">
23 <div class="settings_item">
21 <label for="{{ choice.0 }}">
24 <label for="{{ choice.0 }}">
22 <input type="radio" name="theme"
25 <input type="radio" name="theme"
23 id="{{ choice.0 }}"
26 id="{{ choice.0 }}"
24 value="{{ choice.0 }}"
27 value="{{ choice.0 }}"
25 {% ifequal form.initial.theme choice.0 %}
28 {% ifequal form.initial.theme choice.0 %}
26 checked
29 checked
27 {% endifequal %}
30 {% endifequal %}
28 />
31 />
29 {{ choice.1 }}
32 {{ choice.1 }}
30 </label>
33 </label>
31 </div>
34 </div>
32 {% endfor %}
35 {% endfor %}
33 <input type="submit" value="{% trans "Save" %}" />
36 <input type="submit" value="{% trans "Save" %}" />
34 </form>
37 </form>
35 </div>
38 </div>
36 </div>
39 </div>
37
40
38 {% endblock %} No newline at end of file
41 {% endblock %}
@@ -1,116 +1,124 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load markup %}
4 {% load markup %}
5
5
6 {% block head %}
6 {% block head %}
7 <title>Neboard - {{ posts.0.title }}</title>
7 <title>Neboard - {{ posts.0.title }}</title>
8 {% endblock %}
8 {% endblock %}
9
9
10 {% block content %}
10 {% block content %}
11 <script src="{{ STATIC_URL }}js/thread.js"></script>
11 <script src="{{ STATIC_URL }}js/thread.js"></script>
12
12
13 {% if posts %}
13 {% if posts %}
14 <div id="posts">
14 <div id="posts">
15 {% for post in posts %}
15 {% for post in posts %}
16 {% if posts.0.can_bump %}
16 {% if posts.0.can_bump %}
17 <div class="post" id="{{ post.id }}">
17 <div class="post" id="{{ post.id }}">
18 {% else %}
18 {% else %}
19 <div class="post dead_post" id="{{ post.id }}">
19 <div class="post dead_post" id="{{ post.id }}">
20 {% endif %}
20 {% endif %}
21 {% if post.image %}
21 {% if post.image %}
22 <div class="image">
22 <div class="image">
23 <a
23 <a
24 class="fancy"
24 class="fancy"
25 href="{{ post.image.url }}"><img
25 href="{{ post.image.url }}"><img
26 src="{{ post.image.url_200x150 }}"
26 src="{{ post.image.url_200x150 }}"
27 alt="{% trans 'Post image' %}" />
27 alt="{% trans 'Post image' %}" />
28 </a>
28 </a>
29 </div>
29 </div>
30 {% endif %}
30 {% endif %}
31 <div class="message">
31 <div class="message">
32 <div class="post-info">
32 <div class="post-info">
33 <span class="title">{{ post.title }}</span>
33 <span class="title">{{ post.title }}</span>
34 <a class="post_id" href="#{{ post.id }}">
34 <a class="post_id" href="#{{ post.id }}">
35 (#{{ post.id }})</a>
35 (#{{ post.id }})</a>
36 [{{ post.pub_time }}]
36 [{{ post.pub_time }}]
37 {% if post.is_get %}
37 {% if post.is_get %}
38 <span class="get">
38 <span class="get">
39 {% trans "Get!" %}
39 {% trans "Get!" %}
40 </span>
40 </span>
41 {% endif %}
41 {% endif %}
42 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
42 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
43 ; return false;">&gt;&gt;</a>]
43 ; return false;">&gt;&gt;</a>]
44
45 {% if user.is_moderator %}
46 <span class="moderator_info">
47 ({{ post.poster_ip }})
48 [<a href="{% url 'delete' post_id=post.id %}"
49 >{% trans 'Delete' %}</a>]
50 </span>
51 {% endif %}
44 </div>
52 </div>
45 {% autoescape off %}
53 {% autoescape off %}
46 {{ post.text.rendered }}
54 {{ post.text.rendered }}
47 {% endautoescape %}
55 {% endautoescape %}
48 </div>
56 </div>
49 {% if post.tags.all %}
57 {% if post.tags.all %}
50 <div class="metadata">
58 <div class="metadata">
51 <span class="tags">{% trans 'Tags' %}:
59 <span class="tags">{% trans 'Tags' %}:
52 {% for tag in post.tags.all %}
60 {% for tag in post.tags.all %}
53 <a class="tag" href="{% url 'tag' tag.name %}">
61 <a class="tag" href="{% url 'tag' tag.name %}">
54 {{ tag.name }}</a>
62 {{ tag.name }}</a>
55 {% endfor %}
63 {% endfor %}
56 </span>
64 </span>
57 </div>
65 </div>
58 {% endif %}
66 {% endif %}
59 </div>
67 </div>
60 {% endfor %}
68 {% endfor %}
61 </div>
69 </div>
62 {% else %}
70 {% else %}
63 No thread found.
71 No thread found.
64 <hr />
72 <hr />
65 {% endif %}
73 {% endif %}
66
74
67 <form id="form" enctype="multipart/form-data" method="post"
75 <form id="form" enctype="multipart/form-data" method="post"
68 >{% csrf_token %}
76 >{% csrf_token %}
69 <div class="post-form-w">
77 <div class="post-form-w">
70 <div class="form-title">{% trans "Reply to thread" %}</div>
78 <div class="form-title">{% trans "Reply to thread" %}</div>
71 <div class="post-form">
79 <div class="post-form">
72 <div class="form-row">
80 <div class="form-row">
73 <div class="form-label">{% trans 'Title' %}</div>
81 <div class="form-label">{% trans 'Title' %}</div>
74 <div class="form-input">{{ form.title }}</div>
82 <div class="form-input">{{ form.title }}</div>
75 <div class="form-errors">{{ form.title.errors }}</div>
83 <div class="form-errors">{{ form.title.errors }}</div>
76 </div>
84 </div>
77 <div class="form-row">
85 <div class="form-row">
78 <div class="form-label">{% trans 'Text' %}</div>
86 <div class="form-label">{% trans 'Text' %}</div>
79 <div class="form-input">{{ form.text }}</div>
87 <div class="form-input">{{ form.text }}</div>
80 <div class="form-errors">{{ form.text.errors }}</div>
88 <div class="form-errors">{{ form.text.errors }}</div>
81 </div>
89 </div>
82 <div class="form-row">
90 <div class="form-row">
83 <div class="form-label">{% trans 'Image' %}</div>
91 <div class="form-label">{% trans 'Image' %}</div>
84 <div class="form-input">{{ form.image }}</div>
92 <div class="form-input">{{ form.image }}</div>
85 <div class="form-errors">{{ form.image.errors }}</div>
93 <div class="form-errors">{{ form.image.errors }}</div>
86 </div>
94 </div>
87 <div class="form-row">
95 <div class="form-row">
88 {{ form.captcha }}
96 {{ form.captcha }}
89 <div class="form-errors">{{ form.captcha.errors }}</div>
97 <div class="form-errors">{{ form.captcha.errors }}</div>
90 </div>
98 </div>
91 </div>
99 </div>
92
100
93 <div class="form-submit"><input type="submit"
101 <div class="form-submit"><input type="submit"
94 value="{% trans "Post" %}"/></div>
102 value="{% trans "Post" %}"/></div>
95 <div><a href="http://daringfireball.net/projects/markdown/basics">
103 <div><a href="http://daringfireball.net/projects/markdown/basics">
96 {% trans 'Basic markdown syntax.' %}</a></div>
104 {% trans 'Basic markdown syntax.' %}</a></div>
97 <div>{% trans 'Example: ' %}*<i>{% trans 'italic' %}</i>*,
105 <div>{% trans 'Example: ' %}*<i>{% trans 'italic' %}</i>*,
98 **<b>{% trans 'bold' %}</b>**</div>
106 **<b>{% trans 'bold' %}</b>**</div>
99 <div>{% trans 'Quotes can be inserted with' %} "&gt;"</div>
107 <div>{% trans 'Quotes can be inserted with' %} "&gt;"</div>
100 <div>{% trans 'Links to answers can be inserted with' %}
108 <div>{% trans 'Links to answers can be inserted with' %}
101 "&gt;&gt;123"
109 "&gt;&gt;123"
102 </div>
110 </div>
103 </div>
111 </div>
104 </form>
112 </form>
105
113
106 {% endblock %}
114 {% endblock %}
107
115
108 {% block metapanel %}
116 {% block metapanel %}
109
117
110 <span class="metapanel">
118 <span class="metapanel">
111 {{ posts.0.get_reply_count }} {% trans 'replies' %},
119 {{ posts.0.get_reply_count }} {% trans 'replies' %},
112 {{ posts.0.get_images_count }} {% trans 'images' %}.
120 {{ posts.0.get_images_count }} {% trans 'images' %}.
113 {% trans 'Last update: ' %}{{ posts.0.last_edit_time }}
121 {% trans 'Last update: ' %}{{ posts.0.last_edit_time }}
114 </span>
122 </span>
115
123
116 {% endblock %} No newline at end of file
124 {% endblock %}
@@ -1,36 +1,37 b''
1 from django.conf.urls import patterns, url, include
1 from django.conf.urls import patterns, url, include
2 from boards import views
2 from boards import views
3 from boards.rss import AllThreadsFeed, TagThreadsFeed, ThreadPostsFeed
3 from boards.rss import AllThreadsFeed, TagThreadsFeed, ThreadPostsFeed
4
4
5 urlpatterns = patterns('',
5 urlpatterns = patterns('',
6
6
7 # /boards/
7 # /boards/
8 url(r'^$', views.index, name='index'),
8 url(r'^$', views.index, name='index'),
9 # /boards/page/
9 # /boards/page/
10 url(r'^page/(?P<page>\w+)/$', views.index, name='index'),
10 url(r'^page/(?P<page>\w+)/$', views.index, name='index'),
11
11
12 # login page
12 # login page
13 url(r'^login$', views.login, name='login'),
13 url(r'^login$', views.login, name='login'),
14 # logout page
14 # logout page
15 url(r'^logout$', views.logout, name='logout'),
15 url(r'^logout$', views.logout, name='logout'),
16
16
17 # /boards/tag/tag_name/
17 # /boards/tag/tag_name/
18 url(r'^tag/(?P<tag_name>\w+)/$', views.tag, name='tag'),
18 url(r'^tag/(?P<tag_name>\w+)/$', views.tag, name='tag'),
19 # /boards/tag/tag_id/page/
19 # /boards/tag/tag_id/page/
20 url(r'^tag/(?P<tag_name>\w+)/page/(?P<page>\w+)/$', views.tag, name='tag'),
20 url(r'^tag/(?P<tag_name>\w+)/page/(?P<page>\w+)/$', views.tag, name='tag'),
21 # /boards/thread/
21 # /boards/thread/
22 url(r'^thread/(?P<post_id>\w+)/$', views.thread, name='thread'),
22 url(r'^thread/(?P<post_id>\w+)/$', views.thread, name='thread'),
23 # /boards/theme/theme_name/
23 # /boards/theme/theme_name/
24 url(r'^settings$', views.settings, name='settings'),
24 url(r'^settings$', views.settings, name='settings'),
25 url(r'^tags$', views.all_tags, name='tags'),
25 url(r'^tags$', views.all_tags, name='tags'),
26 url(r'^captcha/', include('captcha.urls')),
26 url(r'^captcha/', include('captcha.urls')),
27 url(r'^jump/(?P<post_id>\w+)/$', views.jump_to_post, name='jumper'),
27 url(r'^jump/(?P<post_id>\w+)/$', views.jump_to_post, name='jumper'),
28 url(r'^authors/$', views.authors, name='authors'),
28 url(r'^authors/$', views.authors, name='authors'),
29 url(r'^delete/(?P<post_id>\w+)/$', views.delete, name='delete'),
29
30
30 # RSS feeds
31 # RSS feeds
31 url(r'^rss/$', AllThreadsFeed()),
32 url(r'^rss/$', AllThreadsFeed()),
32 url(r'^page/(?P<page>\w+)/rss/$', AllThreadsFeed()),
33 url(r'^page/(?P<page>\w+)/rss/$', AllThreadsFeed()),
33 url(r'^tag/(?P<tag_name>\w+)/rss/$', TagThreadsFeed()),
34 url(r'^tag/(?P<tag_name>\w+)/rss/$', TagThreadsFeed()),
34 url(r'^tag/(?P<tag_name>\w+)/page/(?P<page>\w+)/rss/$', TagThreadsFeed()),
35 url(r'^tag/(?P<tag_name>\w+)/page/(?P<page>\w+)/rss/$', TagThreadsFeed()),
35 url(r'^thread/(?P<post_id>\w+)/rss/$', ThreadPostsFeed()),
36 url(r'^thread/(?P<post_id>\w+)/rss/$', ThreadPostsFeed()),
36 ) No newline at end of file
37 )
@@ -1,274 +1,287 b''
1 import hashlib
1 import hashlib
2 from django.core.urlresolvers import reverse
2 from django.core.urlresolvers import reverse
3 from django.template import RequestContext
3 from django.template import RequestContext
4 from django.shortcuts import render, redirect, get_object_or_404
4 from django.shortcuts import render, redirect, get_object_or_404
5 from django.http import HttpResponseRedirect
5 from django.http import HttpResponseRedirect
6
6
7 from boards import forms
7 from boards import forms
8 import boards
8 import boards
9 from boards import utils
9 from boards import utils
10 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
10 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
11 ThreadCaptchaForm, PostCaptchaForm
11 ThreadCaptchaForm, PostCaptchaForm
12
12
13 from boards.models import Post, Admin, Tag, User
13 from boards.models import Post, Admin, Tag, User, RANK_USER, RANK_MODERATOR, NO_PARENT
14 from boards import authors
14 from boards import authors
15 import neboard
15 import neboard
16
16
17
17
18 def index(request, page=0):
18 def index(request, page=0):
19 context = _init_default_context(request)
19 context = _init_default_context(request)
20
20
21 if utils.need_include_captcha(request):
21 if utils.need_include_captcha(request):
22 threadFormClass = ThreadCaptchaForm
22 threadFormClass = ThreadCaptchaForm
23 kwargs = {'request': request}
23 kwargs = {'request': request}
24 else:
24 else:
25 threadFormClass = ThreadForm
25 threadFormClass = ThreadForm
26 kwargs = {}
26 kwargs = {}
27
27
28 if request.method == 'POST':
28 if request.method == 'POST':
29 form = threadFormClass(request.POST, request.FILES,
29 form = threadFormClass(request.POST, request.FILES,
30 error_class=PlainErrorList, **kwargs)
30 error_class=PlainErrorList, **kwargs)
31
31
32 if form.is_valid():
32 if form.is_valid():
33 return _new_post(request, form)
33 return _new_post(request, form)
34 else:
34 else:
35 form = threadFormClass(error_class=PlainErrorList, **kwargs)
35 form = threadFormClass(error_class=PlainErrorList, **kwargs)
36
36
37 threads = Post.objects.get_threads(page=int(page))
37 threads = Post.objects.get_threads(page=int(page))
38
38
39 context['threads'] = None if len(threads) == 0 else threads
39 context['threads'] = None if len(threads) == 0 else threads
40 context['form'] = form
40 context['form'] = form
41 context['pages'] = range(Post.objects.get_thread_page_count())
41 context['pages'] = range(Post.objects.get_thread_page_count())
42
42
43 return render(request, 'boards/posting_general.html',
43 return render(request, 'boards/posting_general.html',
44 context)
44 context)
45
45
46
46
47 def _new_post(request, form, thread_id=boards.models.NO_PARENT):
47 def _new_post(request, form, thread_id=boards.models.NO_PARENT):
48 """Add a new post (in thread or as a reply)."""
48 """Add a new post (in thread or as a reply)."""
49
49
50 data = form.cleaned_data
50 data = form.cleaned_data
51
51
52 title = data['title']
52 title = data['title']
53 text = data['text']
53 text = data['text']
54
54
55 if 'image' in data.keys():
55 if 'image' in data.keys():
56 image = data['image']
56 image = data['image']
57 else:
57 else:
58 image = None
58 image = None
59
59
60 ip = _get_client_ip(request)
60 ip = _get_client_ip(request)
61
61
62 tags = []
62 tags = []
63
63
64 new_thread = thread_id == boards.models.NO_PARENT
64 new_thread = thread_id == boards.models.NO_PARENT
65 if new_thread:
65 if new_thread:
66 tag_strings = data['tags']
66 tag_strings = data['tags']
67
67
68 if tag_strings:
68 if tag_strings:
69 tag_strings = tag_strings.split(' ')
69 tag_strings = tag_strings.split(' ')
70 for tag_name in tag_strings:
70 for tag_name in tag_strings:
71 tag_name = tag_name.strip()
71 tag_name = tag_name.strip()
72 if len(tag_name) > 0:
72 if len(tag_name) > 0:
73 tag, created = Tag.objects.get_or_create(name=tag_name)
73 tag, created = Tag.objects.get_or_create(name=tag_name)
74 tags.append(tag)
74 tags.append(tag)
75
75
76 # TODO Add a possibility to define a link image instead of an image file.
76 # TODO Add a possibility to define a link image instead of an image file.
77 # If a link is given, download the image automatically.
77 # If a link is given, download the image automatically.
78
78
79 post = Post.objects.create_post(title=title, text=text, ip=ip,
79 post = Post.objects.create_post(title=title, text=text, ip=ip,
80 parent_id=thread_id, image=image,
80 parent_id=thread_id, image=image,
81 tags=tags)
81 tags=tags)
82
82
83 thread_to_show = (post.id if new_thread else thread_id)
83 thread_to_show = (post.id if new_thread else thread_id)
84
84
85 if new_thread:
85 if new_thread:
86 return redirect(thread, post_id=thread_to_show)
86 return redirect(thread, post_id=thread_to_show)
87 else:
87 else:
88 return redirect(reverse(thread,
88 return redirect(reverse(thread,
89 kwargs={'post_id': thread_to_show}) + '#'
89 kwargs={'post_id': thread_to_show}) + '#'
90 + str(post.id))
90 + str(post.id))
91
91
92
92
93 def tag(request, tag_name, page=0):
93 def tag(request, tag_name, page=0):
94 """Get all tag threads (posts without a parent)."""
94 """Get all tag threads (posts without a parent)."""
95
95
96 tag = get_object_or_404(Tag, name=tag_name)
96 tag = get_object_or_404(Tag, name=tag_name)
97 threads = Post.objects.get_threads(tag=tag, page=int(page))
97 threads = Post.objects.get_threads(tag=tag, page=int(page))
98
98
99 if request.method == 'POST':
99 if request.method == 'POST':
100 form = ThreadForm(request.POST, request.FILES,
100 form = ThreadForm(request.POST, request.FILES,
101 error_class=PlainErrorList)
101 error_class=PlainErrorList)
102 if form.is_valid():
102 if form.is_valid():
103 return _new_post(request, form)
103 return _new_post(request, form)
104 else:
104 else:
105 form = forms.ThreadForm(initial={'tags': tag_name},
105 form = forms.ThreadForm(initial={'tags': tag_name},
106 error_class=PlainErrorList)
106 error_class=PlainErrorList)
107
107
108 context = _init_default_context(request)
108 context = _init_default_context(request)
109 context['threads'] = None if len(threads) == 0 else threads
109 context['threads'] = None if len(threads) == 0 else threads
110 context['tag'] = tag_name
110 context['tag'] = tag_name
111 context['pages'] = range(Post.objects.get_thread_page_count(tag=tag))
111 context['pages'] = range(Post.objects.get_thread_page_count(tag=tag))
112
112
113 context['form'] = form
113 context['form'] = form
114
114
115 return render(request, 'boards/posting_general.html',
115 return render(request, 'boards/posting_general.html',
116 context)
116 context)
117
117
118
118
119 def thread(request, post_id):
119 def thread(request, post_id):
120 """Get all thread posts"""
120 """Get all thread posts"""
121
121
122 if utils.need_include_captcha(request):
122 if utils.need_include_captcha(request):
123 postFormClass = PostCaptchaForm
123 postFormClass = PostCaptchaForm
124 kwargs = {'request': request}
124 kwargs = {'request': request}
125 else:
125 else:
126 postFormClass = PostForm
126 postFormClass = PostForm
127 kwargs = {}
127 kwargs = {}
128
128
129 if request.method == 'POST':
129 if request.method == 'POST':
130 form = postFormClass(request.POST, request.FILES,
130 form = postFormClass(request.POST, request.FILES,
131 error_class=PlainErrorList, **kwargs)
131 error_class=PlainErrorList, **kwargs)
132 if form.is_valid():
132 if form.is_valid():
133 return _new_post(request, form, post_id)
133 return _new_post(request, form, post_id)
134 else:
134 else:
135 form = postFormClass(error_class=PlainErrorList, **kwargs)
135 form = postFormClass(error_class=PlainErrorList, **kwargs)
136
136
137 posts = Post.objects.get_thread(post_id)
137 posts = Post.objects.get_thread(post_id)
138
138
139 context = _init_default_context(request)
139 context = _init_default_context(request)
140
140
141 context['posts'] = posts
141 context['posts'] = posts
142 context['form'] = form
142 context['form'] = form
143
143
144 return render(request, 'boards/thread.html', context)
144 return render(request, 'boards/thread.html', context)
145
145
146
146
147 def login(request):
147 def login(request):
148 """Log in as admin"""
148 """Log in as admin"""
149
149
150 if 'name' in request.POST and 'password' in request.POST:
150 if 'name' in request.POST and 'password' in request.POST:
151 request.session['admin'] = False
151 request.session['admin'] = False
152
152
153 isAdmin = len(Admin.objects.filter(name=request.POST['name'],
153 isAdmin = len(Admin.objects.filter(name=request.POST['name'],
154 password=request.POST[
154 password=request.POST[
155 'password'])) > 0
155 'password'])) > 0
156
156
157 if isAdmin:
157 if isAdmin:
158 request.session['admin'] = True
158 request.session['admin'] = True
159
159
160 response = HttpResponseRedirect('/')
160 response = HttpResponseRedirect('/')
161
161
162 else:
162 else:
163 response = render(request, 'boards/login.html', {'error': 'Login error'})
163 response = render(request, 'boards/login.html', {'error': 'Login error'})
164 else:
164 else:
165 response = render(request, 'boards/login.html', {})
165 response = render(request, 'boards/login.html', {})
166
166
167 return response
167 return response
168
168
169
169
170 def logout(request):
170 def logout(request):
171 request.session['admin'] = False
171 request.session['admin'] = False
172 return HttpResponseRedirect('/')
172 return HttpResponseRedirect('/')
173
173
174
174
175 def settings(request):
175 def settings(request):
176 """User's settings"""
176 """User's settings"""
177
177
178 context = _init_default_context(request)
178 context = _init_default_context(request)
179
179
180 if request.method == 'POST':
180 if request.method == 'POST':
181 form = SettingsForm(request.POST)
181 form = SettingsForm(request.POST)
182 if form.is_valid():
182 if form.is_valid():
183 selected_theme = form.cleaned_data['theme']
183 selected_theme = form.cleaned_data['theme']
184
184
185 user = _get_user(request)
185 user = _get_user(request)
186 user.save_setting('theme', selected_theme)
186 user.save_setting('theme', selected_theme)
187
187
188 return redirect(settings)
188 return redirect(settings)
189 else:
189 else:
190 selected_theme = _get_theme(request)
190 selected_theme = _get_theme(request)
191 form = SettingsForm(initial={'theme': selected_theme})
191 form = SettingsForm(initial={'theme': selected_theme})
192 context['form'] = form
192 context['form'] = form
193 _get_user(request)
194
193
195 return render(request, 'boards/settings.html', context)
194 return render(request, 'boards/settings.html', context)
196
195
197
196
198 def all_tags(request):
197 def all_tags(request):
199 """All tags list"""
198 """All tags list"""
200
199
201 context = _init_default_context(request)
200 context = _init_default_context(request)
202 context['all_tags'] = Tag.objects.get_not_empty_tags()
201 context['all_tags'] = Tag.objects.get_not_empty_tags()
203
202
204 return render(request, 'boards/tags.html', context)
203 return render(request, 'boards/tags.html', context)
205
204
206
205
207 def jump_to_post(request, post_id):
206 def jump_to_post(request, post_id):
208 """Determine thread in which the requested post is and open it's page"""
207 """Determine thread in which the requested post is and open it's page"""
209
208
210 post = get_object_or_404(Post, id=post_id)
209 post = get_object_or_404(Post, id=post_id)
211
210
212 if boards.models.NO_PARENT == post.parent:
211 if boards.models.NO_PARENT == post.parent:
213 return redirect(thread, post_id=post.id)
212 return redirect(thread, post_id=post.id)
214 else:
213 else:
215 parent_thread = get_object_or_404(Post, id=post.parent)
214 parent_thread = get_object_or_404(Post, id=post.parent)
216 return redirect(reverse(thread, kwargs={'post_id': parent_thread.id})
215 return redirect(reverse(thread, kwargs={'post_id': parent_thread.id})
217 + '#' + str(post.id))
216 + '#' + str(post.id))
218
217
219
218
220 def authors(request):
219 def authors(request):
221 context = _init_default_context(request)
220 context = _init_default_context(request)
222 context['authors'] = boards.authors.authors
221 context['authors'] = boards.authors.authors
223
222
224 return render(request, 'boards/authors.html', context)
223 return render(request, 'boards/authors.html', context)
225
224
226
225
226 def delete(request, post_id):
227 user = _get_user(request)
228 post = get_object_or_404(Post, id=post_id)
229
230 if user.is_moderator():
231 Post.objects.delete_post(post)
232
233 if NO_PARENT == post.parent:
234 return redirect(index)
235 else:
236 return redirect(thread, post_id=post.parent)
237
238
227 def _get_theme(request):
239 def _get_theme(request):
228 """Get user's CSS theme"""
240 """Get user's CSS theme"""
229
241
230 user = _get_user(request)
242 user = _get_user(request)
231 theme = user.get_setting('theme')
243 theme = user.get_setting('theme')
232 if not theme:
244 if not theme:
233 theme = neboard.settings.DEFAULT_THEME
245 theme = neboard.settings.DEFAULT_THEME
234
246
235 return theme
247 return theme
236
248
237
249
238 def _get_client_ip(request):
250 def _get_client_ip(request):
239 x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
251 x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
240 if x_forwarded_for:
252 if x_forwarded_for:
241 ip = x_forwarded_for.split(',')[-1].strip()
253 ip = x_forwarded_for.split(',')[-1].strip()
242 else:
254 else:
243 ip = request.META.get('REMOTE_ADDR')
255 ip = request.META.get('REMOTE_ADDR')
244 return ip
256 return ip
245
257
246
258
247 def _init_default_context(request):
259 def _init_default_context(request):
248 """Create context with default values that are used in most views"""
260 """Create context with default values that are used in most views"""
249
261
250 context = RequestContext(request)
262 context = RequestContext(request)
251 context['tags'] = Tag.objects.get_popular_tags()
263 context['tags'] = Tag.objects.get_popular_tags()
252 context['theme'] = _get_theme(request)
264 context['theme'] = _get_theme(request)
265 context['user'] = _get_user(request)
253
266
254 return context
267 return context
255
268
256
269
257 def _get_user(request):
270 def _get_user(request):
258 """Get current user from the session"""
271 """Get current user from the session"""
259
272
260 session = request.session
273 session = request.session
261 if not 'user' in session:
274 if not 'user_id' in session:
262 request.session.save()
275 request.session.save()
263
276
264 md5 = hashlib.md5()
277 md5 = hashlib.md5()
265 md5.update(session.session_key)
278 md5.update(session.session_key)
266 new_id = md5.hexdigest()
279 new_id = md5.hexdigest()
267
280
268 user = User.objects.create(user_id=new_id)
281 user = User.objects.create(user_id=new_id, rank=RANK_USER)
269
282
270 session['user'] = user
283 session['user_id'] = user.id
271 else:
284 else:
272 user = session['user']
285 user = User.objects.get(id=session['user_id'])
273
286
274 return user No newline at end of file
287 return user
General Comments 0
You need to be logged in to leave comments. Login now