##// END OF EJS Templates
Fixed post display in admin page. Fixed css in Mystic Dark.
neko259 -
r40:0f09185f default
parent child Browse files
Show More
@@ -1,212 +1,212 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 from markupfield.fields import MarkupField
8 from markupfield.fields import MarkupField
9
9
10 import thumbs
10 import thumbs
11
11
12 NO_PARENT = -1
12 NO_PARENT = -1
13 NO_IP = '0.0.0.0'
13 NO_IP = '0.0.0.0'
14 UNKNOWN_UA = ''
14 UNKNOWN_UA = ''
15
15
16
16
17 def update_image_filename(instance, filename):
17 def update_image_filename(instance, filename):
18 """Get unique image filename"""
18 """Get unique image filename"""
19
19
20 path = 'images/'
20 path = 'images/'
21 new_name = str(int(time.mktime(time.gmtime()))) + '_' + filename
21 new_name = str(int(time.mktime(time.gmtime()))) + '_' + filename
22 return os.path.join(path, new_name)
22 return os.path.join(path, new_name)
23
23
24
24
25 class PostManager(models.Manager):
25 class PostManager(models.Manager):
26 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
26 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
27 ip=NO_IP, tags=None):
27 ip=NO_IP, tags=None):
28 post = self.create(title=title,
28 post = self.create(title=title,
29 text=text,
29 text=text,
30 pub_time=timezone.now(),
30 pub_time=timezone.now(),
31 parent=parent_id,
31 parent=parent_id,
32 image=image,
32 image=image,
33 poster_ip=ip,
33 poster_ip=ip,
34 poster_user_agent=UNKNOWN_UA,
34 poster_user_agent=UNKNOWN_UA,
35 last_edit_time=timezone.now())
35 last_edit_time=timezone.now())
36
36
37 if tags:
37 if tags:
38 for tag in tags:
38 for tag in tags:
39 post.tags.add(tag)
39 post.tags.add(tag)
40
40
41 if parent_id != NO_PARENT:
41 if parent_id != NO_PARENT:
42 self._bump_thread(parent_id)
42 self._bump_thread(parent_id)
43 else:
43 else:
44 self._delete_old_threads()
44 self._delete_old_threads()
45
45
46 return post
46 return post
47
47
48 def delete_post(self, post):
48 def delete_post(self, post):
49 children = self.filter(parent=post.id)
49 children = self.filter(parent=post.id)
50 for child in children:
50 for child in children:
51 self.delete_post(child)
51 self.delete_post(child)
52 post.delete()
52 post.delete()
53
53
54 def delete_posts_by_ip(self, ip):
54 def delete_posts_by_ip(self, ip):
55 posts = self.filter(poster_ip=ip)
55 posts = self.filter(poster_ip=ip)
56 for post in posts:
56 for post in posts:
57 self.delete_post(post)
57 self.delete_post(post)
58
58
59 def get_threads(self, tag=None):
59 def get_threads(self, tag=None):
60 if tag:
60 if tag:
61 threads = self.filter(parent=NO_PARENT, tags=tag)
61 threads = self.filter(parent=NO_PARENT, tags=tag)
62 else:
62 else:
63 threads = self.filter(parent=NO_PARENT)
63 threads = self.filter(parent=NO_PARENT)
64 threads = list(threads.order_by('-last_edit_time'))
64 threads = list(threads.order_by('-last_edit_time'))
65
65
66 return threads
66 return threads
67
67
68 def get_thread(self, opening_post_id):
68 def get_thread(self, opening_post_id):
69 opening_post = self.get(id=opening_post_id)
69 opening_post = self.get(id=opening_post_id)
70
70
71 if opening_post.parent == NO_PARENT:
71 if opening_post.parent == NO_PARENT:
72 replies = self.filter(parent=opening_post_id)
72 replies = self.filter(parent=opening_post_id)
73
73
74 thread = [opening_post]
74 thread = [opening_post]
75 thread.extend(replies)
75 thread.extend(replies)
76
76
77 return thread
77 return thread
78
78
79 def exists(self, post_id):
79 def exists(self, post_id):
80 posts = self.filter(id=post_id)
80 posts = self.filter(id=post_id)
81
81
82 return len(posts) > 0
82 return len(posts) > 0
83
83
84 def _delete_old_threads(self):
84 def _delete_old_threads(self):
85 """
85 """
86 Preserves maximum thread count. If there are too many threads,
86 Preserves maximum thread count. If there are too many threads,
87 delete the old ones.
87 delete the old ones.
88 """
88 """
89
89
90 # TODO Try to find a better way to get the active thread count.
90 # TODO Try to find a better way to get the active thread count.
91
91
92 # TODO Move old threads to the archive instead of deleting them.
92 # TODO Move old threads to the archive instead of deleting them.
93 # Maybe make some 'old' field in the model to indicate the thread
93 # Maybe make some 'old' field in the model to indicate the thread
94 # must not be shown and be able for replying.
94 # must not be shown and be able for replying.
95
95
96 threads = self.get_threads()
96 threads = self.get_threads()
97 thread_count = len(threads)
97 thread_count = len(threads)
98
98
99 if thread_count > settings.MAX_THREAD_COUNT:
99 if thread_count > settings.MAX_THREAD_COUNT:
100 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
100 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
101 old_threads = threads[-num_threads_to_delete:]
101 old_threads = threads[-num_threads_to_delete:]
102
102
103 for thread in old_threads:
103 for thread in old_threads:
104 self.delete_post(thread)
104 self.delete_post(thread)
105
105
106 def _bump_thread(self, thread_id):
106 def _bump_thread(self, thread_id):
107 thread = self.get(id=thread_id)
107 thread = self.get(id=thread_id)
108
108
109 replies_count = len(self.get_thread(thread_id))
109 replies_count = len(self.get_thread(thread_id))
110 if replies_count <= settings.MAX_POSTS_PER_THREAD:
110 if replies_count <= settings.MAX_POSTS_PER_THREAD:
111 thread.last_edit_time = timezone.now()
111 thread.last_edit_time = timezone.now()
112 thread.save()
112 thread.save()
113
113
114
114
115 class TagManager(models.Manager):
115 class TagManager(models.Manager):
116 def get_not_empty_tags(self):
116 def get_not_empty_tags(self):
117 all_tags = self.all().order_by('name')
117 all_tags = self.all().order_by('name')
118 tags = []
118 tags = []
119 for tag in all_tags:
119 for tag in all_tags:
120 if not tag.is_empty():
120 if not tag.is_empty():
121 tags.append(tag)
121 tags.append(tag)
122
122
123 return tags
123 return tags
124
124
125
125
126 class Tag(models.Model):
126 class Tag(models.Model):
127 """
127 """
128 A tag is a text node assigned to the post. The tag serves as a board
128 A tag is a text node assigned to the post. The tag serves as a board
129 section. There can be multiple tags for each message
129 section. There can be multiple tags for each message
130 """
130 """
131
131
132 objects = TagManager()
132 objects = TagManager()
133
133
134 name = models.CharField(max_length=100)
134 name = models.CharField(max_length=100)
135 # TODO Connect the tag to its posts to check the number of threads for
135 # TODO Connect the tag to its posts to check the number of threads for
136 # the tag.
136 # the tag.
137
137
138 def __unicode__(self):
138 def __unicode__(self):
139 return self.name
139 return self.name
140
140
141 def is_empty(self):
141 def is_empty(self):
142 return self.get_post_count() == 0
142 return self.get_post_count() == 0
143
143
144 def get_post_count(self):
144 def get_post_count(self):
145 posts_with_tag = Post.objects.get_threads(tag=self)
145 posts_with_tag = Post.objects.get_threads(tag=self)
146 return len(posts_with_tag)
146 return len(posts_with_tag)
147
147
148
148
149 class Post(models.Model):
149 class Post(models.Model):
150 """A post is a message."""
150 """A post is a message."""
151
151
152 objects = PostManager()
152 objects = PostManager()
153
153
154 title = models.CharField(max_length=50)
154 title = models.CharField(max_length=50)
155 pub_time = models.DateTimeField()
155 pub_time = models.DateTimeField()
156 text = MarkupField(default_markup_type='markdown', escape_html=True)
156 text = MarkupField(default_markup_type='markdown', escape_html=True)
157 image = thumbs.ImageWithThumbsField(upload_to=update_image_filename,
157 image = thumbs.ImageWithThumbsField(upload_to=update_image_filename,
158 blank=True, sizes=((200, 150),))
158 blank=True, sizes=((200, 150),))
159 poster_ip = models.IPAddressField()
159 poster_ip = models.IPAddressField()
160 poster_user_agent = models.TextField()
160 poster_user_agent = models.TextField()
161 parent = models.BigIntegerField()
161 parent = models.BigIntegerField()
162 tags = models.ManyToManyField(Tag)
162 tags = models.ManyToManyField(Tag)
163 last_edit_time = models.DateTimeField()
163 last_edit_time = models.DateTimeField()
164
164
165 def __unicode__(self):
165 def __unicode__(self):
166 return self.title + ' (' + self.text + ')'
166 return self.title + ' (' + self.text.raw + ')'
167
167
168 def _get_replies(self):
168 def _get_replies(self):
169 return Post.objects.filter(parent=self.id)
169 return Post.objects.filter(parent=self.id)
170
170
171 def get_reply_count(self):
171 def get_reply_count(self):
172 return len(self._get_replies())
172 return len(self._get_replies())
173
173
174 def get_images_count(self):
174 def get_images_count(self):
175 images_count = 1 if self.image else 0
175 images_count = 1 if self.image else 0
176 for reply in self._get_replies():
176 for reply in self._get_replies():
177 if reply.image:
177 if reply.image:
178 images_count += 1
178 images_count += 1
179
179
180 return images_count
180 return images_count
181
181
182 def get_gets_count(self):
182 def get_gets_count(self):
183 gets_count = 1 if self.is_get() else 0
183 gets_count = 1 if self.is_get() else 0
184 for reply in self._get_replies():
184 for reply in self._get_replies():
185 if reply.is_get():
185 if reply.is_get():
186 gets_count += 1
186 gets_count += 1
187
187
188 return gets_count
188 return gets_count
189
189
190 def is_get(self):
190 def is_get(self):
191 """If the post has pretty id (1, 1000, 77777), than it is called GET"""
191 """If the post has pretty id (1, 1000, 77777), than it is called GET"""
192
192
193 first = self.id == 1
193 first = self.id == 1
194
194
195 # TODO Compile regexes
195 # TODO Compile regexes
196
196
197 id_str = str(self.id)
197 id_str = str(self.id)
198 pretty = re.match(r'\d0+', id_str)
198 pretty = re.match(r'\d0+', id_str)
199 same_digits = re.match(r'^(.)\1{1,}$', id_str)
199 same_digits = re.match(r'^(.)\1{1,}$', id_str)
200 return first or pretty or same_digits
200 return first or pretty or same_digits
201
201
202
202
203 class Admin(models.Model):
203 class Admin(models.Model):
204 """
204 """
205 Model for admin users
205 Model for admin users
206 """
206 """
207 name = models.CharField(max_length=100)
207 name = models.CharField(max_length=100)
208 password = models.CharField(max_length=100)
208 password = models.CharField(max_length=100)
209
209
210 def __unicode__(self):
210 def __unicode__(self):
211 return self.name + '/' + '*' * len(self.password)
211 return self.name + '/' + '*' * len(self.password)
212
212
@@ -1,149 +1,162 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 .link, a {
16 .link, a {
17 color: #afdcec;
17 color: #afdcec;
18 }
18 }
19
19
20 .link:hover {
20 .link:hover {
21 color: #fff380;
21 color: #fff380;
22 }
22 }
23
23
24 .post_id {
24 .post_id {
25 color: #ffffff;
25 color: #ffffff;
26 }
26 }
27
27
28 .block {
29 display: inline-block;
30 vertical-align: top;
31 }
32
28 .tag {
33 .tag {
29 color: #b4cfec;
34 color: #b4cfec;
30 }
35 }
31
36
32 .tag:hover {
37 .tag:hover {
33 color: #d0edb4;
38 color: #d0edb4;
34 }
39 }
35
40
36 .post_id {
41 .post_id {
37 color: #fff380;
42 color: #fff380;
38 }
43 }
39
44
40 .post {
45 .post {
41 background: #333;
46 background: #333;
42 margin: 5px;
47 margin: 5px;
43 padding: 10px;
48 padding: 10px;
44 border-radius: 5px;
49 border-radius: 5px;
45 clear: left;
50 clear: left;
46 }
51 }
47
52
48 .metadata {
53 .metadata {
49 padding: 2px;
54 padding: 2px;
50 margin-top: 10px;
55 margin-top: 10px;
51 border: solid 1px #666;
56 border: solid 1px #666;
52 font-size: 0.9em;
57 font-size: 0.9em;
53 color: #ddd;
58 color: #ddd;
54 }
59 }
55
60
56 #navigation_panel {
61 #navigation_panel {
57 background: #444;
62 background: #444;
58 margin: 5px;
63 margin: 5px;
59 padding: 10px;
64 padding: 10px;
60 border-radius: 5px;
65 border-radius: 5px;
61 color: #eee;
66 color: #eee;
62 }
67 }
63
68
64 #navigation_panel .link {
69 #navigation_panel .link {
65 border-right: 1px solid #fff;
70 border-right: 1px solid #fff;
66 font-weight: bold;
71 font-weight: bold;
67 margin-right: 1ex;
72 margin-right: 1ex;
68 padding-right: 1ex;
73 padding-right: 1ex;
69 }
74 }
70 #navigation_panel .link:last-child {
75 #navigation_panel .link:last-child {
71 border-left: 1px solid #fff;
76 border-left: 1px solid #fff;
72 border-right: none;
77 border-right: none;
73 float: right;
78 float: right;
74 margin-left: 1ex;
79 margin-left: 1ex;
75 margin-right: 0;
80 margin-right: 0;
76 padding-left: 1ex;
81 padding-left: 1ex;
77 padding-right: 0;
82 padding-right: 0;
78 }
83 }
79
84
80 #navigation_panel::after, .post::after {
85 #navigation_panel::after, .post::after {
81 clear: both;
86 clear: both;
82 content: ".";
87 content: ".";
83 display: block;
88 display: block;
84 height: 0;
89 height: 0;
85 line-height: 0;
90 line-height: 0;
86 visibility: hidden;
91 visibility: hidden;
87 }
92 }
88
93
89 p {
94 p {
90 margin-top: .5em;
95 margin-top: .5em;
91 margin-bottom: .5em;
96 margin-bottom: .5em;
92 }
97 }
93
98
94 .post-form-w {
99 .post-form-w {
95 display: table;
100 display: table;
96 background: #333344;
101 background: #333344;
97 border-radius: 5px;
102 border-radius: 5px;
98 color: #fff;
103 color: #fff;
99 padding: 10px;
104 padding: 10px;
100 margin: 5px
105 margin: 5px
101 }
106 }
102
107
103 .form-row {
108 .form-row {
104 display: table-row;
109 display: table-row;
105 }
110 }
106
111
107 .form-label, .form-input {
112 .form-label, .form-input {
108 display: table-cell;
113 display: table-cell;
109 }
114 }
110
115
111 .form-label {
116 .form-label {
112 padding: .25em 1ex .25em 0;
117 padding: .25em 1ex .25em 0;
113 vertical-align: top;
118 vertical-align: top;
114 }
119 }
115
120
116 .form-input {
121 .form-input {
117 padding: .25em 0;
122 padding: .25em 0;
118 }
123 }
119
124
120 .post-form input, .post-form textarea {
125 .post-form input, .post-form textarea {
121 background: #333;
126 background: #333;
122 color: #fff;
127 color: #fff;
123 border: solid 1px;
128 border: solid 1px;
124 padding: 0;
129 padding: 0;
125 width: 100%;
130 width: 100%;
126 }
131 }
127
132
128 .form-submit {
133 .form-submit {
129 border-bottom: 2px solid #ddd;
134 border-bottom: 2px solid #ddd;
130 margin-bottom: .5em;
135 margin-bottom: .5em;
131 padding-bottom: .5em;
136 padding-bottom: .5em;
132 }
137 }
133
138
134 .form-title {
139 .form-title {
135 font-weight: bold;
140 font-weight: bold;
136 }
141 }
137
142
138 input[type="submit"] {
143 input[type="submit"] {
139 background: #222;
144 background: #222;
140 border: solid 1px #fff;
145 border: solid 1px #fff;
141 color: #fff;
146 color: #fff;
142 }
147 }
143
148
144 blockquote {
149 blockquote {
145 border-left: solid 2px;
150 border-left: solid 2px;
146 padding-left: 5px;
151 padding-left: 5px;
147 color: #B1FB17;
152 color: #B1FB17;
148 margin: 0;
153 margin: 0;
154 }
155
156 .post > .image {
157 float: left; margin: 0 1ex .5ex 0;
158 }
159
160 .post > .metadata {
161 clear: left;
149 } No newline at end of file
162 }
General Comments 0
You need to be logged in to leave comments. Login now