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