##// END OF EJS Templates
Added thread metadata (replies, images, gets) to the threads list.
neko259 -
r30:0d6932e0 default
parent child Browse files
Show More
@@ -1,145 +1,173 b''
1 import os
1 import os
2 from django.db import models
2 from django.db import models
3 from django.utils import timezone
3 from django.utils import timezone
4 import time
4 import time
5
5
6 from neboard import settings
6 from neboard import settings
7
7
8 import thumbs
8 import thumbs
9
9
10 NO_PARENT = -1
10 NO_PARENT = -1
11 NO_IP = '0.0.0.0'
11 NO_IP = '0.0.0.0'
12 UNKNOWN_UA = ''
12 UNKNOWN_UA = ''
13
13
14
14
15 def update_image_filename(instance, filename):
15 def update_image_filename(instance, filename):
16 """Get unique image filename"""
16 """Get unique image filename"""
17
17
18 path = 'images/'
18 path = 'images/'
19 new_name = str(int(time.mktime(time.gmtime()))) + '_' + filename
19 new_name = str(int(time.mktime(time.gmtime()))) + '_' + filename
20 return os.path.join(path, new_name)
20 return os.path.join(path, new_name)
21
21
22
22
23 class PostManager(models.Manager):
23 class PostManager(models.Manager):
24 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
24 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
25 ip=NO_IP, tags=None):
25 ip=NO_IP, tags=None):
26 post = self.create(title=title,
26 post = self.create(title=title,
27 text=text,
27 text=text,
28 pub_time=timezone.now(),
28 pub_time=timezone.now(),
29 parent=parent_id,
29 parent=parent_id,
30 image=image,
30 image=image,
31 poster_ip=ip,
31 poster_ip=ip,
32 poster_user_agent=UNKNOWN_UA,
32 poster_user_agent=UNKNOWN_UA,
33 last_edit_time=timezone.now())
33 last_edit_time=timezone.now())
34
34
35 if tags:
35 if tags:
36 for tag in tags:
36 for tag in tags:
37 post.tags.add(tag)
37 post.tags.add(tag)
38
38
39 if parent_id != NO_PARENT:
39 if parent_id != NO_PARENT:
40 parent = self.get(id=parent_id)
40 parent = self.get(id=parent_id)
41 parent.last_edit_time=timezone.now()
41 parent.last_edit_time=timezone.now()
42 parent.save()
42 parent.save()
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 replies = self.filter(parent=opening_post_id)
70 replies = self.filter(parent=opening_post_id)
71
71
72 thread = [opening_post]
72 thread = [opening_post]
73 thread.extend(replies)
73 thread.extend(replies)
74
74
75 return thread
75 return thread
76
76
77 def exists(self, post_id):
77 def exists(self, post_id):
78 posts = self.filter(id=post_id)
78 posts = self.filter(id=post_id)
79
79
80 return len(posts) > 0
80 return len(posts) > 0
81
81
82 def _delete_old_threads(self):
82 def _delete_old_threads(self):
83 """
83 """
84 Preserves maximum thread count. If there are too many threads,
84 Preserves maximum thread count. If there are too many threads,
85 delete the old ones.
85 delete the old ones.
86 """
86 """
87
87
88 # TODO Try to find a better way to get the active thread count.
88 # TODO Try to find a better way to get the active thread count.
89
89
90 # TODO Move old threads to the archive instead of deleting them.
90 # TODO Move old threads to the archive instead of deleting them.
91 # Maybe make some 'old' field in the model to indicate the thread
91 # Maybe make some 'old' field in the model to indicate the thread
92 # must not be shown and be able for replying.
92 # must not be shown and be able for replying.
93
93
94 threads = self.get_threads()
94 threads = self.get_threads()
95 thread_count = len(threads)
95 thread_count = len(threads)
96
96
97 if thread_count > settings.MAX_THREAD_COUNT:
97 if thread_count > settings.MAX_THREAD_COUNT:
98 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
98 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
99 old_threads = threads[-num_threads_to_delete:]
99 old_threads = threads[-num_threads_to_delete:]
100
100
101 for thread in old_threads:
101 for thread in old_threads:
102 self.delete_post(thread)
102 self.delete_post(thread)
103
103
104
104
105 class Tag(models.Model):
105 class Tag(models.Model):
106 """
106 """
107 A tag is a text node assigned to the post. The tag serves as a board
107 A tag is a text node assigned to the post. The tag serves as a board
108 section. There can be multiple tags for each message
108 section. There can be multiple tags for each message
109 """
109 """
110
110
111 name = models.CharField(max_length=100)
111 name = models.CharField(max_length=100)
112 # TODO Connect the tag to its posts to check the number of threads for
112 # TODO Connect the tag to its posts to check the number of threads for
113 # the tag.
113 # the tag.
114
114
115
115
116 class Post(models.Model):
116 class Post(models.Model):
117 """A post is a message."""
117 """A post is a message."""
118
118
119 objects = PostManager()
119 objects = PostManager()
120
120
121 title = models.CharField(max_length=50)
121 title = models.CharField(max_length=50)
122 pub_time = models.DateTimeField()
122 pub_time = models.DateTimeField()
123 text = models.TextField()
123 text = models.TextField()
124 image = thumbs.ImageWithThumbsField(upload_to=update_image_filename,
124 image = thumbs.ImageWithThumbsField(upload_to=update_image_filename,
125 blank=True, sizes=((200, 150),))
125 blank=True, sizes=((200, 150),))
126 poster_ip = models.IPAddressField()
126 poster_ip = models.IPAddressField()
127 poster_user_agent = models.TextField()
127 poster_user_agent = models.TextField()
128 parent = models.BigIntegerField()
128 parent = models.BigIntegerField()
129 tags = models.ManyToManyField(Tag)
129 tags = models.ManyToManyField(Tag)
130 last_edit_time = models.DateTimeField()
130 last_edit_time = models.DateTimeField()
131
131
132 def __unicode__(self):
132 def __unicode__(self):
133 return self.title + ' (' + self.text + ')'
133 return self.title + ' (' + self.text + ')'
134
134
135 def _get_replies(self):
136 return Post.objects.filter(parent=self.id)
137
138 def get_reply_count(self):
139 return len(self._get_replies())
140
141 def get_images_count(self):
142 images_count = 1 if self.image else 0
143 for reply in self._get_replies():
144 if reply.image:
145 images_count += 1
146
147 return images_count
148
149 def get_gets_count(self):
150 gets_count = 1 if self.is_get() else 0
151 for reply in self._get_replies():
152 if reply.is_get():
153 gets_count += 1
154
155 return gets_count
156
157 def is_get(self):
158 """If the post has pretty id (1, 1000, 77777), than it is called GET"""
159
160 # TODO Make a better algorithm for describing gets
161 return self.id == 1 or self.id % 10 == 0
162
135
163
136 class Admin(models.Model):
164 class Admin(models.Model):
137 """
165 """
138 Model for admin users
166 Model for admin users
139 """
167 """
140 name = models.CharField(max_length=100)
168 name = models.CharField(max_length=100)
141 password = models.CharField(max_length=100)
169 password = models.CharField(max_length=100)
142
170
143 def __unicode__(self):
171 def __unicode__(self):
144 return self.name + '/' + '*' * len(self.password)
172 return self.name + '/' + '*' * len(self.password)
145
173
@@ -1,71 +1,79 b''
1 html {
1 html {
2 background: #444;
2 background: #444;
3 color: #ffffff;
3 color: #ffffff;
4 }
4 }
5
5
6 #admin_panel {
6 #admin_panel {
7 background: #FF0000;
7 background: #FF0000;
8 color: #00FF00
8 color: #00FF00
9 }
9 }
10
10
11 .title {
11 .title {
12 font-weight: bold;
12 font-weight: bold;
13 color: #ffcc00;
13 color: #ffcc00;
14 }
14 }
15
15
16 .post-form {
16 .post-form {
17 text-align: left;
17 text-align: left;
18 display: table;
18 display: table;
19 border-radius: 5px;
19 border-radius: 5px;
20 padding: 5px;
20 padding: 5px;
21 margin: 5px;
21 margin: 5px;
22 background: #334;
22 background: #334;
23 }
23 }
24
24
25 .form-row {
25 .form-row {
26 display: table-row;
26 display: table-row;
27 }
27 }
28
28
29 .form-input {
29 .form-input {
30 display: table-cell;
30 display: table-cell;
31 }
31 }
32
32
33 .link {
33 .link {
34 color: #829dba;
34 color: #afdcec;
35 }
35 }
36
36
37 .link:hover {
37 .link:hover {
38 color: #ba9f82;
38 color: #fff380;
39 }
39 }
40
40
41 .post_id {
41 .post_id {
42 color: #ffffff;
42 color: #ffffff;
43 }
43 }
44
44
45 .block {
45 .block {
46 display: inline-block;
46 display: inline-block;
47 vertical-align: top;
47 vertical-align: top;
48 }
48 }
49
49
50 .tag {
50 .tag {
51 color: #b4cfec;
51 color: #b4cfec;
52 }
52 }
53
53
54 .tag:hover {
54 .tag:hover {
55 color: #d0edb4;
55 color: #d0edb4;
56 }
56 }
57
57
58 .post_id {
58 .post_id {
59 color: #fff380;
59 color: #fff380;
60 }
60 }
61
61
62 .post {
62 .post {
63 background: #333;
63 background: #333;
64 margin: 5px;
64 margin: 5px;
65 padding: 10px;
65 padding: 10px;
66 border-radius: 5px;
66 border-radius: 5px;
67 }
67 }
68
68
69 .form-title {
69 .form-title {
70 font-weight: bolder;
70 font-weight: bolder;
71 }
72
73 .metadata {
74 background: #666;
75 padding: 10px;
76 border-radius: 5px;
77 float: left;
78 margin-top: 5px;
71 } No newline at end of file
79 }
@@ -1,44 +1,60 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 #, fuzzy
7 msgid ""
6 msgid ""
8 msgstr ""
7 msgstr ""
9 "Project-Id-Version: PACKAGE VERSION\n"
8 "Project-Id-Version: PACKAGE VERSION\n"
10 "Report-Msgid-Bugs-To: \n"
9 "Report-Msgid-Bugs-To: \n"
11 "POT-Creation-Date: 2013-04-05 20:55+0300\n"
10 "POT-Creation-Date: 2013-04-11 21:26+0300\n"
12 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14 "Language-Team: LANGUAGE <LL@li.org>\n"
13 "Language-Team: LANGUAGE <LL@li.org>\n"
15 "Language: \n"
14 "Language: ru\n"
16 "MIME-Version: 1.0\n"
15 "MIME-Version: 1.0\n"
17 "Content-Type: text/plain; charset=UTF-8\n"
16 "Content-Type: text/plain; charset=UTF-8\n"
18 "Content-Transfer-Encoding: 8bit\n"
17 "Content-Transfer-Encoding: 8bit\n"
19 "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"
20 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
19 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
21
20
21 #: templates/posting_general.html:22
22 msgid "View"
23 msgstr "ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€"
24
25 #: templates/posting_general.html:25
26 msgid "replies"
27 msgstr "ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ²"
28
29 #: templates/posting_general.html:26
30 msgid "images"
31 msgstr "ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ"
32
33 #: templates/posting_general.html:29 templates/posting_general.html.py:61
34 #: templates/thread.html:23
35 msgid "Tags"
36 msgstr "Π’Π΅Π³ΠΈ"
37
38 #: templates/posting_general.html:46
39 msgid "Create new thread"
40 msgstr "Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ Π½ΠΎΠ²Ρ‹ΠΉ Ρ‚Ρ€Π΅Π΄"
41
42 #: templates/posting_general.html:49 templates/thread.html:42
22 msgid "Title"
43 msgid "Title"
23 msgstr "Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ"
44 msgstr "Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ"
24
45
46 #: templates/posting_general.html:53 templates/thread.html:46
25 msgid "Text"
47 msgid "Text"
26 msgstr "ВСкст"
48 msgstr "ВСкст"
27
49
50 #: templates/posting_general.html:57 templates/thread.html:50
28 msgid "Image"
51 msgid "Image"
29 msgstr "ΠšΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠ°"
52 msgstr "ΠšΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠ°"
30
53
54 #: templates/posting_general.html:64 templates/thread.html:53
31 msgid "Post"
55 msgid "Post"
32 msgstr "ΠžΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ"
56 msgstr "ΠžΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ"
33
57
34 msgid "Tags"
58 #: templates/thread.html:39
35 msgstr "Π’Π΅Π³ΠΈ"
36
37 msgid "View"
38 msgstr "ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€"
39
40 msgid "Create new thread"
41 msgstr "Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ Π½ΠΎΠ²Ρ‹ΠΉ Ρ‚Ρ€Π΅Π΄"
42
43 msgid "Reply to the thread"
59 msgid "Reply to the thread"
44 msgstr "ΠžΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ Π² Ρ‚Ρ€Π΅Π΄" No newline at end of file
60 msgstr "ΠžΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ Π² Ρ‚Ρ€Π΅Π΄"
@@ -1,63 +1,68 b''
1 {% extends "base.html" %}
1 {% extends "base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4
4
5 {% block content %}
5 {% block content %}
6
6
7 {% if threads %}
7 {% if threads %}
8 {% for thread in threads %}
8 {% for thread in threads %}
9 <div class="post">
9 <div class="post">
10 {% if thread.image %}
10 {% if thread.image %}
11 <div class="block">
11 <div class="block">
12 <a href="{{ thread.image.url }}"><img
12 <a href="{{ thread.image.url }}"><img
13 src="{{ thread.image.url_200x150 }}" />
13 src="{{ thread.image.url_200x150 }}" />
14 </a>
14 </a>
15 </div>
15 </div>
16 {% endif %}
16 {% endif %}
17 <div class="block">
17 <div class="block">
18 <span class="title">{{ thread.title }}</span>
18 <span class="title">{{ thread.title }}</span>
19 <span class="post_id">(#{{ thread.id }})</span>
19 <span class="post_id">(#{{ thread.id }})</span>
20 [{{ thread.pub_time }}]
20 [{{ thread.pub_time }}]
21 <a class="link" href="/thread/{{ thread.id }}/">
21 [<a class="link" href="/thread/{{ thread.id }}/">{% trans "View" %}</a>]<br />
22 [{% trans "View" %}]</a><br />
23 {{ thread.text|truncatechars:300 }}<br />
22 {{ thread.text|truncatechars:300 }}<br />
24 {% if thread.tags %}
23 <div class="metadata">
25 <span class="tags">{% trans 'Tags' %}:
24 ({{ thread.get_reply_count }} {% trans 'replies' %},
26 {% for tag in thread.tags.all %}
25 {{ thread.get_images_count }} {% trans 'images' %},
27 <a class="tag" href="/tag/{{ tag.name }}">
26 {{ thread.get_gets_count }} {% trans 'gets' %})
28 {{ tag.name }}</a>
27 <br />
29 {% endfor %}
28 {% if thread.tags.all %}
30 </span>
29 <span class="tags">{% trans 'Tags' %}:
31 {% endif %}
30 {% for tag in thread.tags.all %}
31 <a class="tag" href="/tag/{{ tag.name }}">
32 {{ tag.name }}</a>
33 {% endfor %}
34 </span>
35 {% endif %}
36 </div>
32 </div>
37 </div>
33 </div>
38 </div>
34 {% endfor %}
39 {% endfor %}
35 {% else %}
40 {% else %}
36 No threads found.
41 No threads found.
37 <hr />
42 <hr />
38 {% endif %}
43 {% endif %}
39
44
40 <div class="post-form">
45 <div class="post-form">
41 <span class="form-title">{% trans "Create new thread" %}</span>
46 <span class="form-title">{% trans "Create new thread" %}</span>
42 <form enctype="multipart/form-data" method="post">{% csrf_token %}
47 <form enctype="multipart/form-data" method="post">{% csrf_token %}
43 <div class="form-row">
48 <div class="form-row">
44 <div class="form-input">{% trans 'Title' %}</div>
49 <div class="form-input">{% trans 'Title' %}</div>
45 <div class="form-input">{{ form.title }}</div>
50 <div class="form-input">{{ form.title }}</div>
46 </div>
51 </div>
47 <div class="form-row">
52 <div class="form-row">
48 <div class="form-input">{% trans 'Text' %}</div>
53 <div class="form-input">{% trans 'Text' %}</div>
49 <div class="form-input">{{ form.text }}</div>
54 <div class="form-input">{{ form.text }}</div>
50 </div>
55 </div>
51 <div class="form-row">
56 <div class="form-row">
52 <div class="form-input">{% trans 'Image' %}</div>
57 <div class="form-input">{% trans 'Image' %}</div>
53 <div class="form-input">{{ form.image }}</div>
58 <div class="form-input">{{ form.image }}</div>
54 </div>
59 </div>
55 <div class="form-row">
60 <div class="form-row">
56 <div class="form-input">{% trans 'Tags' %}</div>
61 <div class="form-input">{% trans 'Tags' %}</div>
57 <div class="form-input">{{ form.tags }}</div>
62 <div class="form-input">{{ form.tags }}</div>
58 </div>
63 </div>
59 <input type="submit" value="{% trans 'Post' %}" />
64 <input type="submit" value="{% trans 'Post' %}" />
60 </form>
65 </form>
61 </div>
66 </div>
62
67
63 {% endblock %} No newline at end of file
68 {% endblock %}
@@ -1,57 +1,61 b''
1 {% extends "base.html" %}
1 {% extends "base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4
4
5 {% block content %}
5 {% block content %}
6
6
7 {% if posts %}
7 {% if posts %}
8 {% for post in posts %}
8 {% for post in posts %}
9 <a name="{{ post.id }}"></a>
9 <div class="post">
10 <div class="post">
10 {% if post.image %}
11 {% if post.image %}
11 <div class="block">
12 <div class="block">
12 <a href="{{ post.image.url }}"><img
13 <a href="{{ post.image.url }}"><img
13 src="{{ post.image.url_200x150 }}" />
14 src="{{ post.image.url_200x150 }}" />
14 </a>
15 </a>
15 </div>
16 </div>
16 {% endif %}
17 {% endif %}
17 <div class="block">
18 <div class="block">
18 <span class="title">{{ post.title }}</span>
19 <span class="title">{{ post.title }}</span>
19 <span class="post_id">(#{{ post.id }})</span>
20 <a class="post_id" href="#{{ post.id }}">
21 (#{{ post.id }})</a>
20 [{{ post.pub_time }}]<br />
22 [{{ post.pub_time }}]<br />
21 {{ post.text }}<br />
23 {{ post.text }}<br />
22 {% ifnotequal post.tags.all|length 0 %}
24 {% if post.tags.all %}
23 <span class="tags">{% trans 'Tags' %}:
25 <div class="metadata">
24 {% for tag in post.tags.all %}
26 <span class="tags">{% trans 'Tags' %}:
25 <a class="tag" href="/tag/{{ tag.name }}">
27 {% for tag in post.tags.all %}
26 {{ tag.name }}</a>
28 <a class="tag" href="/tag/{{ tag.name }}">
27 {% endfor %}
29 {{ tag.name }}</a>
28 </span>
30 {% endfor %}
29 {% endifnotequal %}
31 </span>
32 </div>
33 {% endif %}
30 </div>
34 </div>
31 </div>
35 </div>
32 {% endfor %}
36 {% endfor %}
33 {% else %}
37 {% else %}
34 No threads found.
38 No threads found.
35 <hr />
39 <hr />
36 {% endif %}
40 {% endif %}
37
41
38 <div class="post-form">
42 <div class="post-form">
39 <span class="form-title">{% trans "Reply to the thread" %}</span>
43 <span class="form-title">{% trans "Reply to the thread" %}</span>
40 <form enctype="multipart/form-data" method="post">{% csrf_token %}
44 <form enctype="multipart/form-data" method="post">{% csrf_token %}
41 <div class="form-row">
45 <div class="form-row">
42 <div class="form-input">{% trans 'Title' %}</div>
46 <div class="form-input">{% trans 'Title' %}</div>
43 <div class="form-input">{{ form.title }}</div>
47 <div class="form-input">{{ form.title }}</div>
44 </div>
48 </div>
45 <div class="form-row">
49 <div class="form-row">
46 <div class="form-input">{% trans 'Text' %}</div>
50 <div class="form-input">{% trans 'Text' %}</div>
47 <div class="form-input">{{ form.text }}</div>
51 <div class="form-input">{{ form.text }}</div>
48 </div>
52 </div>
49 <div class="form-row">
53 <div class="form-row">
50 <div class="form-input">{% trans 'Image' %}</div>
54 <div class="form-input">{% trans 'Image' %}</div>
51 <div class="form-input">{{ form.image }}</div>
55 <div class="form-input">{{ form.image }}</div>
52 </div>
56 </div>
53 <input type="submit" value="{% trans 'Post' %}" />
57 <input type="submit" value="{% trans 'Post' %}" />
54 </form>
58 </form>
55 </div>
59 </div>
56
60
57 {% endblock %} No newline at end of file
61 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now