Show More
@@ -0,0 +1,115 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | from south.db import db | |
|
3 | from south.v2 import SchemaMigration | |
|
4 | from django.db import models | |
|
5 | ||
|
6 | ||
|
7 | class Migration(SchemaMigration): | |
|
8 | ||
|
9 | def forwards(self, orm): | |
|
10 | # Adding model 'Thread' | |
|
11 | db.create_table(u'boards_thread', ( | |
|
12 | (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), | |
|
13 | ('bump_time', self.gf('django.db.models.fields.DateTimeField')()), | |
|
14 | ('last_edit_time', self.gf('django.db.models.fields.DateTimeField')()), | |
|
15 | )) | |
|
16 | db.send_create_signal('boards', ['Thread']) | |
|
17 | ||
|
18 | # Adding M2M table for field tags on 'Thread' | |
|
19 | m2m_table_name = db.shorten_name(u'boards_thread_tags') | |
|
20 | db.create_table(m2m_table_name, ( | |
|
21 | ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), | |
|
22 | ('thread', models.ForeignKey(orm['boards.thread'], null=False)), | |
|
23 | ('tag', models.ForeignKey(orm['boards.tag'], null=False)) | |
|
24 | )) | |
|
25 | db.create_unique(m2m_table_name, ['thread_id', 'tag_id']) | |
|
26 | ||
|
27 | db.delete_table(db.shorten_name(u'boards_tag_threads')) | |
|
28 | # Adding M2M table for field threads on 'Tag' | |
|
29 | m2m_table_name = db.shorten_name(u'boards_tag_threads') | |
|
30 | db.create_table(m2m_table_name, ( | |
|
31 | ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), | |
|
32 | ('thread', models.ForeignKey(orm['boards.thread'], null=False)), | |
|
33 | ('tag', models.ForeignKey(orm['boards.tag'], null=False)) | |
|
34 | )) | |
|
35 | db.create_unique(m2m_table_name, ['thread_id', 'tag_id']) | |
|
36 | ||
|
37 | # Adding M2M table for field replies on 'Thread' | |
|
38 | m2m_table_name = db.shorten_name(u'boards_thread_replies') | |
|
39 | db.create_table(m2m_table_name, ( | |
|
40 | ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), | |
|
41 | ('thread', models.ForeignKey(orm['boards.thread'], null=False)), | |
|
42 | ('post', models.ForeignKey(orm['boards.post'], null=False)) | |
|
43 | )) | |
|
44 | db.create_unique(m2m_table_name, ['thread_id', 'post_id']) | |
|
45 | ||
|
46 | db.add_column(u'boards_post', 'thread_new', | |
|
47 | self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['boards.Thread'], null=True), | |
|
48 | keep_default=False) | |
|
49 | ||
|
50 | def backwards(self, orm): | |
|
51 | pass | |
|
52 | ||
|
53 | models = { | |
|
54 | 'boards.ban': { | |
|
55 | 'Meta': {'object_name': 'Ban'}, | |
|
56 | 'can_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | |
|
57 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
58 | 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}), | |
|
59 | 'reason': ('django.db.models.fields.CharField', [], {'default': "'Auto'", 'max_length': '200'}) | |
|
60 | }, | |
|
61 | 'boards.post': { | |
|
62 | 'Meta': {'object_name': 'Post'}, | |
|
63 | '_text_rendered': ('django.db.models.fields.TextField', [], {}), | |
|
64 | 'bump_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
65 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
66 | 'image': ('boards.thumbs.ImageWithThumbsField', [], {'max_length': '100', 'blank': 'True'}), | |
|
67 | 'image_height': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | |
|
68 | 'image_width': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | |
|
69 | 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
70 | 'poster_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}), | |
|
71 | 'poster_user_agent': ('django.db.models.fields.TextField', [], {}), | |
|
72 | 'pub_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
73 | 'referenced_posts': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'rfp+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}), | |
|
74 | 'replies': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'re+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}), | |
|
75 | 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['boards.Tag']", 'symmetrical': 'False'}), | |
|
76 | 'text': ('markupfield.fields.MarkupField', [], {'rendered_field': 'True'}), | |
|
77 | 'text_markup_type': ('django.db.models.fields.CharField', [], {'default': "'markdown'", 'max_length': '30'}), | |
|
78 | 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['boards.Thread']", 'null': 'True'}), | |
|
79 | 'title': ('django.db.models.fields.CharField', [], {'max_length': '50'}), | |
|
80 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['boards.User']", 'null': 'True'}) | |
|
81 | }, | |
|
82 | 'boards.setting': { | |
|
83 | 'Meta': {'object_name': 'Setting'}, | |
|
84 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
85 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), | |
|
86 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['boards.User']"}), | |
|
87 | 'value': ('django.db.models.fields.CharField', [], {'max_length': '50'}) | |
|
88 | }, | |
|
89 | 'boards.tag': { | |
|
90 | 'Meta': {'object_name': 'Tag'}, | |
|
91 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
92 | 'linked': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['boards.Tag']", 'null': 'True', 'blank': 'True'}), | |
|
93 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | |
|
94 | 'threads': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'tag+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Thread']"}) | |
|
95 | }, | |
|
96 | 'boards.thread': { | |
|
97 | 'Meta': {'object_name': 'Thread'}, | |
|
98 | 'bump_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
99 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
100 | 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
101 | 'replies': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'tre+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}), | |
|
102 | 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['boards.Tag']", 'symmetrical': 'False'}) | |
|
103 | }, | |
|
104 | 'boards.user': { | |
|
105 | 'Meta': {'object_name': 'User'}, | |
|
106 | 'fav_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['boards.Tag']", 'null': 'True', 'blank': 'True'}), | |
|
107 | 'fav_threads': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}), | |
|
108 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
109 | 'rank': ('django.db.models.fields.IntegerField', [], {}), | |
|
110 | 'registration_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
111 | 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '50'}) | |
|
112 | } | |
|
113 | } | |
|
114 | ||
|
115 | complete_apps = ['boards'] No newline at end of file |
@@ -0,0 +1,98 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | from south.utils import datetime_utils as datetime | |
|
3 | from south.db import db | |
|
4 | from south.v2 import DataMigration | |
|
5 | from django.db import models | |
|
6 | from boards import views | |
|
7 | ||
|
8 | ||
|
9 | class Migration(DataMigration): | |
|
10 | ||
|
11 | def forwards(self, orm): | |
|
12 | for post in orm.Post.objects.filter(thread=None): | |
|
13 | thread = orm.Thread.objects.create( | |
|
14 | bump_time=post.bump_time, | |
|
15 | last_edit_time=post.last_edit_time) | |
|
16 | ||
|
17 | thread.replies.add(post) | |
|
18 | post.thread_new = thread | |
|
19 | post.save() | |
|
20 | print str(post.thread_new.id) | |
|
21 | ||
|
22 | for reply in post.replies.all(): | |
|
23 | thread.replies.add(reply) | |
|
24 | reply.thread_new = thread | |
|
25 | reply.save() | |
|
26 | ||
|
27 | for tag in post.tags.all(): | |
|
28 | thread.tags.add(tag) | |
|
29 | tag.threads.add(thread) | |
|
30 | ||
|
31 | def backwards(self, orm): | |
|
32 | pass | |
|
33 | ||
|
34 | models = { | |
|
35 | 'boards.ban': { | |
|
36 | 'Meta': {'object_name': 'Ban'}, | |
|
37 | 'can_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | |
|
38 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
39 | 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}), | |
|
40 | 'reason': ('django.db.models.fields.CharField', [], {'default': "'Auto'", 'max_length': '200'}) | |
|
41 | }, | |
|
42 | 'boards.post': { | |
|
43 | 'Meta': {'object_name': 'Post'}, | |
|
44 | '_text_rendered': ('django.db.models.fields.TextField', [], {}), | |
|
45 | 'bump_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
46 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
47 | 'image': ('boards.thumbs.ImageWithThumbsField', [], {'max_length': '100', 'blank': 'True'}), | |
|
48 | 'image_height': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | |
|
49 | 'image_width': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | |
|
50 | 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
51 | 'poster_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}), | |
|
52 | 'poster_user_agent': ('django.db.models.fields.TextField', [], {}), | |
|
53 | 'pub_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
54 | 'referenced_posts': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'rfp+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}), | |
|
55 | 'replies': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'re+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}), | |
|
56 | 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['boards.Tag']", 'symmetrical': 'False'}), | |
|
57 | 'text': ('markupfield.fields.MarkupField', [], {'rendered_field': 'True'}), | |
|
58 | 'text_markup_type': ('django.db.models.fields.CharField', [], {'default': "'markdown'", 'max_length': '30'}), | |
|
59 | 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['boards.Post']", 'null': 'True'}), | |
|
60 | 'thread_new': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['boards.Thread']", 'null': 'True'}), | |
|
61 | 'title': ('django.db.models.fields.CharField', [], {'max_length': '50'}), | |
|
62 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['boards.User']", 'null': 'True'}) | |
|
63 | }, | |
|
64 | 'boards.setting': { | |
|
65 | 'Meta': {'object_name': 'Setting'}, | |
|
66 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
67 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), | |
|
68 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['boards.User']"}), | |
|
69 | 'value': ('django.db.models.fields.CharField', [], {'max_length': '50'}) | |
|
70 | }, | |
|
71 | 'boards.tag': { | |
|
72 | 'Meta': {'object_name': 'Tag'}, | |
|
73 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
74 | 'linked': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['boards.Tag']", 'null': 'True', 'blank': 'True'}), | |
|
75 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | |
|
76 | 'threads': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'tag+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Thread']"}) | |
|
77 | }, | |
|
78 | 'boards.thread': { | |
|
79 | 'Meta': {'object_name': 'Thread'}, | |
|
80 | 'bump_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
81 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
82 | 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
83 | 'replies': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'tre+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}), | |
|
84 | 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['boards.Tag']", 'symmetrical': 'False'}) | |
|
85 | }, | |
|
86 | 'boards.user': { | |
|
87 | 'Meta': {'object_name': 'User'}, | |
|
88 | 'fav_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['boards.Tag']", 'null': 'True', 'blank': 'True'}), | |
|
89 | 'fav_threads': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}), | |
|
90 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
91 | 'rank': ('django.db.models.fields.IntegerField', [], {}), | |
|
92 | 'registration_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
93 | 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '50'}) | |
|
94 | } | |
|
95 | } | |
|
96 | ||
|
97 | complete_apps = ['boards'] | |
|
98 | symmetrical = True |
@@ -0,0 +1,110 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | from south.utils import datetime_utils as datetime | |
|
3 | from south.db import db | |
|
4 | from south.v2 import SchemaMigration | |
|
5 | from django.db import models | |
|
6 | ||
|
7 | ||
|
8 | class Migration(SchemaMigration): | |
|
9 | ||
|
10 | def forwards(self, orm): | |
|
11 | # Deleting field 'Post.bump_time' | |
|
12 | db.delete_column(u'boards_post', 'bump_time') | |
|
13 | ||
|
14 | # Removing M2M table for field tags on 'Post' | |
|
15 | db.delete_table(db.shorten_name(u'boards_post_tags')) | |
|
16 | ||
|
17 | # Removing M2M table for field replies on 'Post' | |
|
18 | db.delete_table(db.shorten_name(u'boards_post_replies')) | |
|
19 | ||
|
20 | ||
|
21 | def backwards(self, orm): | |
|
22 | ||
|
23 | # User chose to not deal with backwards NULL issues for 'Post.bump_time' | |
|
24 | raise RuntimeError("Cannot reverse this migration. 'Post.bump_time' and its values cannot be restored.") | |
|
25 | ||
|
26 | # The following code is provided here to aid in writing a correct migration # Adding field 'Post.bump_time' | |
|
27 | db.add_column(u'boards_post', 'bump_time', | |
|
28 | self.gf('django.db.models.fields.DateTimeField')(), | |
|
29 | keep_default=False) | |
|
30 | ||
|
31 | # Adding M2M table for field tags on 'Post' | |
|
32 | m2m_table_name = db.shorten_name(u'boards_post_tags') | |
|
33 | db.create_table(m2m_table_name, ( | |
|
34 | ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), | |
|
35 | ('post', models.ForeignKey(orm['boards.post'], null=False)), | |
|
36 | ('tag', models.ForeignKey(orm['boards.tag'], null=False)) | |
|
37 | )) | |
|
38 | db.create_unique(m2m_table_name, ['post_id', 'tag_id']) | |
|
39 | ||
|
40 | # Adding M2M table for field replies on 'Post' | |
|
41 | m2m_table_name = db.shorten_name(u'boards_post_replies') | |
|
42 | db.create_table(m2m_table_name, ( | |
|
43 | ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), | |
|
44 | ('from_post', models.ForeignKey(orm['boards.post'], null=False)), | |
|
45 | ('to_post', models.ForeignKey(orm['boards.post'], null=False)) | |
|
46 | )) | |
|
47 | db.create_unique(m2m_table_name, ['from_post_id', 'to_post_id']) | |
|
48 | ||
|
49 | ||
|
50 | models = { | |
|
51 | 'boards.ban': { | |
|
52 | 'Meta': {'object_name': 'Ban'}, | |
|
53 | 'can_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | |
|
54 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
55 | 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}), | |
|
56 | 'reason': ('django.db.models.fields.CharField', [], {'default': "'Auto'", 'max_length': '200'}) | |
|
57 | }, | |
|
58 | 'boards.post': { | |
|
59 | 'Meta': {'object_name': 'Post'}, | |
|
60 | '_text_rendered': ('django.db.models.fields.TextField', [], {}), | |
|
61 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
62 | 'image': ('boards.thumbs.ImageWithThumbsField', [], {'max_length': '100', 'blank': 'True'}), | |
|
63 | 'image_height': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | |
|
64 | 'image_width': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | |
|
65 | 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
66 | 'poster_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}), | |
|
67 | 'poster_user_agent': ('django.db.models.fields.TextField', [], {}), | |
|
68 | 'pub_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
69 | 'referenced_posts': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'rfp+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}), | |
|
70 | 'text': ('markupfield.fields.MarkupField', [], {'rendered_field': 'True'}), | |
|
71 | 'text_markup_type': ('django.db.models.fields.CharField', [], {'default': "'markdown'", 'max_length': '30'}), | |
|
72 | 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['boards.Post']", 'null': 'True'}), | |
|
73 | 'thread_new': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['boards.Thread']", 'null': 'True'}), | |
|
74 | 'title': ('django.db.models.fields.CharField', [], {'max_length': '50'}), | |
|
75 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['boards.User']", 'null': 'True'}) | |
|
76 | }, | |
|
77 | 'boards.setting': { | |
|
78 | 'Meta': {'object_name': 'Setting'}, | |
|
79 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
80 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), | |
|
81 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['boards.User']"}), | |
|
82 | 'value': ('django.db.models.fields.CharField', [], {'max_length': '50'}) | |
|
83 | }, | |
|
84 | 'boards.tag': { | |
|
85 | 'Meta': {'object_name': 'Tag'}, | |
|
86 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
87 | 'linked': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['boards.Tag']", 'null': 'True', 'blank': 'True'}), | |
|
88 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | |
|
89 | 'threads': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'tag+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Thread']"}) | |
|
90 | }, | |
|
91 | 'boards.thread': { | |
|
92 | 'Meta': {'object_name': 'Thread'}, | |
|
93 | 'bump_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
94 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
95 | 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
96 | 'replies': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'tre+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}), | |
|
97 | 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['boards.Tag']", 'symmetrical': 'False'}) | |
|
98 | }, | |
|
99 | 'boards.user': { | |
|
100 | 'Meta': {'object_name': 'User'}, | |
|
101 | 'fav_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['boards.Tag']", 'null': 'True', 'blank': 'True'}), | |
|
102 | 'fav_threads': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}), | |
|
103 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | |
|
104 | 'rank': ('django.db.models.fields.IntegerField', [], {}), | |
|
105 | 'registration_time': ('django.db.models.fields.DateTimeField', [], {}), | |
|
106 | 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '50'}) | |
|
107 | } | |
|
108 | } | |
|
109 | ||
|
110 | complete_apps = ['boards'] No newline at end of file |
@@ -5,7 +5,7 b' from boards.models import Post, Tag, Use' | |||
|
5 | 5 | class PostAdmin(admin.ModelAdmin): |
|
6 | 6 | |
|
7 | 7 | list_display = ('id', 'title', 'text') |
|
8 |
list_filter = ('pub_time', 't |
|
|
8 | list_filter = ('pub_time', 'thread_new') | |
|
9 | 9 | search_fields = ('id', 'title', 'text') |
|
10 | 10 | |
|
11 | 11 |
@@ -14,6 +14,7 b" SPOILER_PATTERN = r'%%(.+)%%'" | |||
|
14 | 14 | COMMENT_PATTERN = r'^(//(.+))' |
|
15 | 15 | STRIKETHROUGH_PATTERN = r'~(.+)~' |
|
16 | 16 | |
|
17 | ||
|
17 | 18 | class AutolinkPattern(Pattern): |
|
18 | 19 | def handleMatch(self, m): |
|
19 | 20 | link_element = etree.Element('a') |
@@ -41,17 +42,17 b' class ReflinkPattern(Pattern):' | |||
|
41 | 42 | if posts.count() > 0: |
|
42 | 43 | ref_element = etree.Element('a') |
|
43 | 44 | |
|
44 |
|
|
|
45 | post = posts[0] | |
|
45 | 46 | if post.thread: |
|
46 | 47 | link = reverse(boards.views.thread, kwargs={'post_id': post.thread.id}) \ |
|
47 | 48 | + '#' + post_id |
|
48 | 49 | else: |
|
49 |
|
|
|
50 | link = reverse(boards.views.thread, kwargs={'post_id': post_id}) | |
|
50 | 51 | |
|
51 | 52 | ref_element.set('href', link) |
|
52 | 53 | ref_element.text = m.group(2) |
|
53 | 54 | |
|
54 |
|
|
|
55 | return ref_element | |
|
55 | 56 | |
|
56 | 57 | |
|
57 | 58 | class SpoilerPattern(Pattern): |
@@ -1,6 +1,7 b'' | |||
|
1 | 1 | __author__ = 'neko259' |
|
2 | 2 | |
|
3 | 3 | from boards.models.post import Post |
|
4 | from boards.models.post import Thread | |
|
4 | 5 | from boards.models.tag import Tag |
|
5 | 6 | from boards.models.user import Ban |
|
6 | 7 | from boards.models.user import Setting |
@@ -38,18 +38,25 b' class PostManager(models.Manager):' | |||
|
38 | 38 | def create_post(self, title, text, image=None, thread=None, |
|
39 | 39 | ip=NO_IP, tags=None, user=None): |
|
40 | 40 | posting_time = timezone.now() |
|
41 | if not thread: | |
|
42 | thread = Thread.objects.create(bump_time=posting_time, | |
|
43 | last_edit_time=posting_time) | |
|
44 | else: | |
|
45 | thread.bump() | |
|
46 | thread.last_edit_time = posting_time | |
|
47 | thread.save() | |
|
41 | 48 | |
|
42 | 49 | post = self.create(title=title, |
|
43 | 50 | text=text, |
|
44 | 51 | pub_time=posting_time, |
|
45 | thread=thread, | |
|
52 | thread_new=thread, | |
|
46 | 53 | image=image, |
|
47 | 54 | poster_ip=ip, |
|
48 | 55 | poster_user_agent=UNKNOWN_UA, |
|
49 | 56 | last_edit_time=posting_time, |
|
50 | bump_time=posting_time, | |
|
51 | 57 | user=user) |
|
52 | 58 | |
|
59 | thread.replies.add(post) | |
|
53 | 60 | if tags: |
|
54 | 61 | linked_tags = [] |
|
55 | 62 | for tag in tags: |
@@ -58,31 +65,18 b' class PostManager(models.Manager):' | |||
|
58 | 65 | linked_tags.extend(tag_linked_tags) |
|
59 | 66 | |
|
60 | 67 | tags.extend(linked_tags) |
|
61 |
map( |
|
|
62 | for tag in tags: | |
|
63 | tag.threads.add(post) | |
|
68 | map(thread.add_tag, tags) | |
|
64 | 69 | |
|
65 | if thread: | |
|
66 | thread.replies.add(post) | |
|
67 | thread.bump() | |
|
68 | thread.last_edit_time = posting_time | |
|
69 | thread.save() | |
|
70 | else: | |
|
71 | self._delete_old_threads() | |
|
72 | ||
|
70 | self._delete_old_threads() | |
|
73 | 71 | self.connect_replies(post) |
|
74 | 72 | |
|
75 | 73 | return post |
|
76 | 74 | |
|
75 | # TODO Remove this method after migration | |
|
77 | 76 | def delete_post(self, post): |
|
78 | if post.replies.count() > 0: | |
|
79 | map(self.delete_post, post.replies.all()) | |
|
80 | ||
|
81 | # Update thread's last edit time | |
|
82 | thread = post.thread | |
|
83 | if thread: | |
|
84 | thread.last_edit_time = timezone.now() | |
|
85 | thread.save() | |
|
77 | thread = post.thread_new | |
|
78 | thread.last_edit_time = timezone.now() | |
|
79 | thread.save() | |
|
86 | 80 | |
|
87 | 81 | post.delete() |
|
88 | 82 | |
@@ -90,15 +84,16 b' class PostManager(models.Manager):' | |||
|
90 | 84 | posts = self.filter(poster_ip=ip) |
|
91 | 85 | map(self.delete_post, posts) |
|
92 | 86 | |
|
87 | # TODO Remove this method after migration | |
|
93 | 88 | def get_threads(self, tag=None, page=ALL_PAGES, |
|
94 | 89 | order_by='-bump_time'): |
|
95 | 90 | if tag: |
|
96 | 91 | threads = tag.threads |
|
97 | 92 | |
|
98 |
if threads. |
|
|
93 | if not threads.exists(): | |
|
99 | 94 | raise Http404 |
|
100 | 95 | else: |
|
101 |
threads = |
|
|
96 | threads = Thread.objects.all() | |
|
102 | 97 | |
|
103 | 98 | threads = threads.order_by(order_by) |
|
104 | 99 | |
@@ -113,26 +108,25 b' class PostManager(models.Manager):' | |||
|
113 | 108 | |
|
114 | 109 | return threads |
|
115 | 110 | |
|
111 | # TODO Remove this method after migration | |
|
116 | 112 | def get_thread(self, opening_post_id): |
|
117 | 113 | try: |
|
118 |
opening_post = self.get(id=opening_post_id |
|
|
114 | opening_post = self.get(id=opening_post_id) | |
|
119 | 115 | except Post.DoesNotExist: |
|
120 | 116 | raise Http404 |
|
121 | 117 | |
|
122 |
|
|
|
123 | thread = [opening_post] | |
|
124 | thread.extend(opening_post.replies.all().order_by('pub_time')) | |
|
118 | return opening_post.thread_new | |
|
125 | 119 | |
|
126 | return thread | |
|
127 | ||
|
120 | # TODO Move this method to thread manager | |
|
128 | 121 | def get_thread_page_count(self, tag=None): |
|
129 | 122 | if tag: |
|
130 |
threads = |
|
|
123 | threads = Thread.objects.filter(tags=tag) | |
|
131 | 124 | else: |
|
132 |
threads = |
|
|
125 | threads = Thread.objects.all() | |
|
133 | 126 | |
|
134 | 127 | return self._get_page_count(threads.count()) |
|
135 | 128 | |
|
129 | # TODO Move this method to thread manager | |
|
136 | 130 | def _delete_old_threads(self): |
|
137 | 131 | """ |
|
138 | 132 | Preserves maximum thread count. If there are too many threads, |
@@ -143,14 +137,14 b' class PostManager(models.Manager):' | |||
|
143 | 137 | # Maybe make some 'old' field in the model to indicate the thread |
|
144 | 138 | # must not be shown and be able for replying. |
|
145 | 139 | |
|
146 |
threads = |
|
|
140 | threads = Thread.objects.all() | |
|
147 | 141 | thread_count = threads.count() |
|
148 | 142 | |
|
149 | 143 | if thread_count > settings.MAX_THREAD_COUNT: |
|
150 | 144 | num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT |
|
151 | 145 | old_threads = threads[thread_count - num_threads_to_delete:] |
|
152 | 146 | |
|
153 |
map( |
|
|
147 | map(Thread.delete_with_posts, old_threads) | |
|
154 | 148 | |
|
155 | 149 | def connect_replies(self, post): |
|
156 | 150 | """Connect replies to a post to show them as a refmap""" |
@@ -204,13 +198,10 b' class Post(models.Model):' | |||
|
204 | 198 | poster_user_agent = models.TextField() |
|
205 | 199 | |
|
206 | 200 | thread = models.ForeignKey('Post', null=True, default=None) |
|
207 | tags = models.ManyToManyField('Tag') | |
|
201 | thread_new = models.ForeignKey('Thread', null=True, default=None) | |
|
208 | 202 | last_edit_time = models.DateTimeField() |
|
209 | bump_time = models.DateTimeField() | |
|
210 | 203 | user = models.ForeignKey('User', null=True, default=None) |
|
211 | 204 | |
|
212 | replies = models.ManyToManyField('Post', symmetrical=False, null=True, | |
|
213 | blank=True, related_name='re+') | |
|
214 | 205 | referenced_posts = models.ManyToManyField('Post', symmetrical=False, |
|
215 | 206 | null=True, |
|
216 | 207 | blank=True, related_name='rfp+') |
@@ -226,14 +217,40 b' class Post(models.Model):' | |||
|
226 | 217 | |
|
227 | 218 | return title |
|
228 | 219 | |
|
220 | def get_sorted_referenced_posts(self): | |
|
221 | return self.referenced_posts.order_by('id') | |
|
222 | ||
|
223 | def is_referenced(self): | |
|
224 | return self.referenced_posts.all().exists() | |
|
225 | ||
|
226 | ||
|
227 | class Thread(models.Model): | |
|
228 | ||
|
229 | class Meta: | |
|
230 | app_label = 'boards' | |
|
231 | ||
|
232 | tags = models.ManyToManyField('Tag') | |
|
233 | bump_time = models.DateTimeField() | |
|
234 | last_edit_time = models.DateTimeField() | |
|
235 | replies = models.ManyToManyField('Post', symmetrical=False, null=True, | |
|
236 | blank=True, related_name='tre+') | |
|
237 | ||
|
238 | def get_tags(self): | |
|
239 | """Get a sorted tag list""" | |
|
240 | ||
|
241 | return self.tags.order_by('name') | |
|
242 | ||
|
243 | def bump(self): | |
|
244 | """Bump (move to up) thread""" | |
|
245 | ||
|
246 | if self.can_bump(): | |
|
247 | self.bump_time = timezone.now() | |
|
248 | ||
|
229 | 249 | def get_reply_count(self): |
|
230 | 250 | return self.replies.count() |
|
231 | 251 | |
|
232 | 252 | def get_images_count(self): |
|
233 | images_count = 1 if self.image else 0 | |
|
234 | images_count += self.replies.filter(image_width__gt=0).count() | |
|
235 | ||
|
236 | return images_count | |
|
253 | return self.replies.filter(image_width__gt=0).count() | |
|
237 | 254 | |
|
238 | 255 | def can_bump(self): |
|
239 | 256 | """Check if the thread can be bumped by replying""" |
@@ -242,31 +259,38 b' class Post(models.Model):' | |||
|
242 | 259 | |
|
243 | 260 | return post_count <= settings.MAX_POSTS_PER_THREAD |
|
244 | 261 | |
|
245 |
def |
|
|
246 |
""" |
|
|
262 | def delete_with_posts(self): | |
|
263 | """Completely delete thread""" | |
|
247 | 264 | |
|
248 |
if self. |
|
|
249 | self.bump_time = timezone.now() | |
|
265 | if self.replies.count() > 0: | |
|
266 | map(Post.objects.delete_post, self.replies.all()) | |
|
267 | ||
|
268 | self.delete() | |
|
250 | 269 | |
|
251 | 270 | def get_last_replies(self): |
|
271 | """Get last replies, not including opening post""" | |
|
272 | ||
|
252 | 273 | if settings.LAST_REPLIES_COUNT > 0: |
|
253 | 274 | reply_count = self.get_reply_count() |
|
254 | 275 | |
|
255 | 276 | if reply_count > 0: |
|
256 | 277 | reply_count_to_show = min(settings.LAST_REPLIES_COUNT, |
|
257 | reply_count) | |
|
278 | reply_count - 1) | |
|
258 | 279 | last_replies = self.replies.all().order_by('pub_time')[ |
|
259 | 280 | reply_count - reply_count_to_show:] |
|
260 | 281 | |
|
261 | 282 | return last_replies |
|
262 | 283 | |
|
263 |
def get_ |
|
|
264 |
"""Get |
|
|
284 | def get_replies(self): | |
|
285 | """Get sorted thread posts""" | |
|
265 | 286 | |
|
266 |
return self. |
|
|
287 | return self.replies.all().order_by('pub_time') | |
|
267 | 288 | |
|
268 | def get_sorted_referenced_posts(self): | |
|
269 | return self.referenced_posts.order_by('id') | |
|
289 | def add_tag(self, tag): | |
|
290 | """Connect thread to a tag and tag to a thread""" | |
|
270 | 291 | |
|
271 | def is_referenced(self): | |
|
272 | return self.referenced_posts.all().exists() | |
|
292 | self.tags.add(tag) | |
|
293 | tag.threads.add(self) | |
|
294 | ||
|
295 | def __unicode__(self): | |
|
296 | return str(self.get_replies()[0].id) No newline at end of file |
@@ -1,4 +1,4 b'' | |||
|
1 |
from boards.models import |
|
|
1 | from boards.models import Thread | |
|
2 | 2 | from django.db import models |
|
3 | 3 | from django.db.models import Count |
|
4 | 4 | |
@@ -20,8 +20,8 b' class TagManager(models.Manager):' | |||
|
20 | 20 | |
|
21 | 21 | class Tag(models.Model): |
|
22 | 22 | """ |
|
23 |
A tag is a text node assigned to the |
|
|
24 |
section. There can be multiple tags for each |
|
|
23 | A tag is a text node assigned to the thread. The tag serves as a board | |
|
24 | section. There can be multiple tags for each thread | |
|
25 | 25 | """ |
|
26 | 26 | |
|
27 | 27 | objects = TagManager() |
@@ -30,7 +30,7 b' class Tag(models.Model):' | |||
|
30 | 30 | app_label = 'boards' |
|
31 | 31 | |
|
32 | 32 | name = models.CharField(max_length=100) |
|
33 |
threads = models.ManyToManyField( |
|
|
33 | threads = models.ManyToManyField(Thread, null=True, | |
|
34 | 34 | blank=True, related_name='tag+') |
|
35 | 35 | linked = models.ForeignKey('Tag', null=True, blank=True) |
|
36 | 36 | |
@@ -43,14 +43,15 b' class Tag(models.Model):' | |||
|
43 | 43 | def get_post_count(self): |
|
44 | 44 | return self.threads.count() |
|
45 | 45 | |
|
46 | def get_popularity(self): | |
|
47 | posts_with_tag = Post.objects.get_threads(tag=self) | |
|
48 | reply_count = 0 | |
|
49 | for post in posts_with_tag: | |
|
50 | reply_count += post.get_reply_count() | |
|
51 | reply_count += OPENING_POST_POPULARITY_WEIGHT | |
|
52 | ||
|
53 | return reply_count | |
|
46 | # TODO Reenable this method after migration | |
|
47 | # def get_popularity(self): | |
|
48 | # posts_with_tag = Thread.objects.get_threads(tag=self) | |
|
49 | # reply_count = 0 | |
|
50 | # for post in posts_with_tag: | |
|
51 | # reply_count += post.get_reply_count() | |
|
52 | # reply_count += OPENING_POST_POPULARITY_WEIGHT | |
|
53 | # | |
|
54 | # return reply_count | |
|
54 | 55 | |
|
55 | 56 | def get_linked_tags(self): |
|
56 | 57 | tag_list = [] |
@@ -104,7 +104,7 b' p {' | |||
|
104 | 104 | border: solid 1px #888; |
|
105 | 105 | color: #fff; |
|
106 | 106 | padding: 10px; |
|
107 | margin: 5; | |
|
107 | margin: 5px; | |
|
108 | 108 | } |
|
109 | 109 | |
|
110 | 110 | .form-row { |
@@ -55,10 +55,10 b'' | |||
|
55 | 55 | </div> |
|
56 | 56 | {% endif %} |
|
57 | 57 | </div> |
|
58 | {% if post.tags.exists %} | |
|
58 | {% if post.thread.tags.exists %} | |
|
59 | 59 | <div class="metadata"> |
|
60 | 60 | <span class="tags">{% trans 'Tags' %}: |
|
61 |
{% for tag in post.tags |
|
|
61 | {% for tag in post.thread.get_tags %} | |
|
62 | 62 | <a class="tag" href="{% url 'tag' tag.name %}"> |
|
63 | 63 | {{ tag.name }}</a> |
|
64 | 64 | {% endfor %} |
@@ -68,47 +68,50 b'' | |||
|
68 | 68 | {% cache 600 thread_short thread.thread.last_edit_time moderator LANGUAGE_CODE %} |
|
69 | 69 | <div class="thread"> |
|
70 | 70 | {% if thread.bumpable %} |
|
71 |
<div class="post" id="{{ thread. |
|
|
71 | <div class="post" id="{{ thread.op.id }}"> | |
|
72 | 72 | {% else %} |
|
73 |
<div class="post dead_post" id="{{ thread. |
|
|
73 | <div class="post dead_post" id="{{ thread.op.id }}"> | |
|
74 | 74 | {% endif %} |
|
75 |
{% if thread. |
|
|
75 | {% if thread.op.image %} | |
|
76 | 76 | <div class="image"> |
|
77 | 77 | <a class="thumb" |
|
78 |
href="{{ thread. |
|
|
79 |
src="{{ thread. |
|
|
80 |
alt="{{ thread. |
|
|
81 |
data-width="{{ thread. |
|
|
82 |
data-height="{{ thread. |
|
|
78 | href="{{ thread.op.image.url }}"><img | |
|
79 | src="{{ thread.op.image.url_200x150 }}" | |
|
80 | alt="{{ thread.op.id }}" | |
|
81 | data-width="{{ thread.op.image_width }}" | |
|
82 | data-height="{{ thread.op.image_height }}"/> | |
|
83 | 83 | </a> |
|
84 | 84 | </div> |
|
85 | 85 | {% endif %} |
|
86 | 86 | <div class="message"> |
|
87 | 87 | <div class="post-info"> |
|
88 |
<span class="title">{{ thread. |
|
|
89 |
<a class="post_id" href="{% url 'thread' thread. |
|
|
90 |
>({{ thread. |
|
|
91 |
[{{ thread. |
|
|
92 |
[<a class="link" href=" |
|
|
88 | <span class="title">{{ thread.op.title }}</span> | |
|
89 | <a class="post_id" href="{% url 'thread' thread.op.id %}" | |
|
90 | >({{ thread.op.id }})</a> | |
|
91 | [{{ thread.op.pub_time }}] | |
|
92 | [<a class="link" href=" | |
|
93 | {% url 'thread' thread.op.id %}#form" | |
|
93 | 94 | >{% trans "Reply" %}</a>] |
|
94 | 95 | |
|
95 | 96 | {% if moderator %} |
|
96 | 97 | <span class="moderator_info"> |
|
97 | [<a href="{% url 'delete' post_id=thread.thread.id %}?next={{ request.path }}" | |
|
98 | [<a href=" | |
|
99 | {% url 'delete' post_id=thread.op.id %}?next={{ request.path }}" | |
|
98 | 100 | >{% trans 'Delete' %}</a>] |
|
99 | 101 | ({{ thread.thread.poster_ip }}) |
|
100 | [<a href="{% url 'ban' post_id=thread.thread.id %}?next={{ request.path }}" | |
|
102 | [<a href=" | |
|
103 | {% url 'ban' post_id=thread.op.id %}?next={{ request.path }}" | |
|
101 | 104 | >{% trans 'Ban IP' %}</a>] |
|
102 | 105 | </span> |
|
103 | 106 | {% endif %} |
|
104 | 107 | </div> |
|
105 | 108 | {% autoescape off %} |
|
106 |
{{ thread. |
|
|
109 | {{ thread.op.text.rendered|truncatewords_html:50 }} | |
|
107 | 110 | {% endautoescape %} |
|
108 |
{% if thread. |
|
|
111 | {% if thread.op.is_referenced %} | |
|
109 | 112 | <div class="refmap"> |
|
110 | 113 | {% trans "Replies" %}: |
|
111 |
{% for ref_post in thread. |
|
|
114 | {% for ref_post in thread.op.get_sorted_referenced_posts %} | |
|
112 | 115 | <a href="{% post_url ref_post.id %}">>>{{ ref_post.id }}</a |
|
113 | 116 | >{% if not forloop.last %},{% endif %} |
|
114 | 117 | {% endfor %} |
@@ -153,7 +156,7 b'' | |||
|
153 | 156 | <div class="post-info"> |
|
154 | 157 | <span class="title">{{ post.title }}</span> |
|
155 | 158 | <a class="post_id" href=" |
|
156 |
{% url 'thread' thread. |
|
|
159 | {% url 'thread' thread.op.id %}#{{ post.id }}"> | |
|
157 | 160 | ({{ post.id }})</a> |
|
158 | 161 | [{{ post.pub_time }}] |
|
159 | 162 | </div> |
@@ -257,7 +260,7 b'' | |||
|
257 | 260 | {% block metapanel %} |
|
258 | 261 | |
|
259 | 262 | <span class="metapanel"> |
|
260 |
<b><a href="{% url "authors" %}">Neboard</a> 1. |
|
|
263 | <b><a href="{% url "authors" %}">Neboard</a> 1.4</b> | |
|
261 | 264 | {% trans "Pages:" %} |
|
262 | 265 | {% for page in pages %} |
|
263 | 266 | [<a href=" |
@@ -15,84 +15,82 b'' | |||
|
15 | 15 | <script src="{% static 'js/thread_update.js' %}"></script> |
|
16 | 16 | <script src="{% static 'js/thread.js' %}"></script> |
|
17 | 17 | |
|
18 | {% if posts %} | |
|
19 | {% cache 600 thread_view posts.0.last_edit_time moderator LANGUAGE_CODE %} | |
|
20 | {% if bumpable %} | |
|
21 | <div class="bar-bg"> | |
|
22 | <div class="bar-value" style="width:{{ bumplimit_progress }}%"> | |
|
23 | </div> | |
|
24 | <div class="bar-text"> | |
|
25 | {{ posts_left }} {% trans 'posts to bumplimit' %} | |
|
26 | </div> | |
|
18 | {% cache 600 thread_view thread.last_edit_time moderator LANGUAGE_CODE %} | |
|
19 | {% if bumpable %} | |
|
20 | <div class="bar-bg"> | |
|
21 | <div class="bar-value" style="width:{{ bumplimit_progress }}%"> | |
|
27 | 22 | </div> |
|
28 | {% endif %} | |
|
29 | <div class="thread"> | |
|
30 | {% for post in posts %} | |
|
31 | {% if bumpable %} | |
|
32 | <div class="post" id="{{ post.id }}"> | |
|
33 | {% else %} | |
|
34 | <div class="post dead_post" id="{{ post.id }}"> | |
|
35 | {% endif %} | |
|
36 | {% if post.image %} | |
|
37 | <div class="image"> | |
|
38 | <a | |
|
39 | class="thumb" | |
|
40 | href="{{ post.image.url }}"><img | |
|
41 | src="{{ post.image.url_200x150 }}" | |
|
42 | alt="{{ post.id }}" | |
|
43 | data-width="{{ post.image_width }}" | |
|
44 | data-height="{{ post.image_height }}"/> | |
|
45 | </a> | |
|
23 | <div class="bar-text"> | |
|
24 | {{ posts_left }} {% trans 'posts to bumplimit' %} | |
|
46 | 25 | </div> |
|
47 |
|
|
|
48 | <div class="message"> | |
|
49 |
|
|
|
50 | <span class="title">{{ post.title }}</span> | |
|
51 | <a class="post_id" href="#{{ post.id }}"> | |
|
52 |
|
|
|
53 | [{{ post.pub_time }}] | |
|
54 | [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}') | |
|
55 | ; return false;">>></a>] | |
|
56 | ||
|
57 | {% if moderator %} | |
|
58 | <span class="moderator_info"> | |
|
59 | [<a href="{% url 'delete' post_id=post.id %}" | |
|
60 | >{% trans 'Delete' %}</a>] | |
|
61 |
|
|
|
62 | [<a href="{% url 'ban' post_id=post.id %}?next={{ request.path }}" | |
|
63 | >{% trans 'Ban IP' %}</a>] | |
|
64 | </span> | |
|
65 |
|
|
|
66 | </div> | |
|
67 | {% autoescape off %} | |
|
68 | {{ post.text.rendered }} | |
|
69 | {% endautoescape %} | |
|
70 | {% if post.is_referenced %} | |
|
71 | <div class="refmap"> | |
|
72 | {% trans "Replies" %}: | |
|
73 | {% for ref_post in post.get_sorted_referenced_posts %} | |
|
74 | <a href="{% post_url ref_post.id %}">>>{{ ref_post.id }}</a | |
|
75 | >{% if not forloop.last %},{% endif %} | |
|
76 | {% endfor %} | |
|
26 | </div> | |
|
27 | {% endif %} | |
|
28 | <div class="thread"> | |
|
29 | {% for post in thread.get_replies %} | |
|
30 | {% if bumpable %} | |
|
31 | <div class="post" id="{{ post.id }}"> | |
|
32 | {% else %} | |
|
33 | <div class="post dead_post" id="{{ post.id }}"> | |
|
34 | {% endif %} | |
|
35 | {% if post.image %} | |
|
36 | <div class="image"> | |
|
37 | <a | |
|
38 | class="thumb" | |
|
39 | href="{{ post.image.url }}"><img | |
|
40 | src="{{ post.image.url_200x150 }}" | |
|
41 | alt="{{ post.id }}" | |
|
42 | data-width="{{ post.image_width }}" | |
|
43 | data-height="{{ post.image_height }}"/> | |
|
44 | </a> | |
|
77 | 45 | </div> |
|
78 | 46 | {% endif %} |
|
47 | <div class="message"> | |
|
48 | <div class="post-info"> | |
|
49 | <span class="title">{{ post.title }}</span> | |
|
50 | <a class="post_id" href="#{{ post.id }}"> | |
|
51 | ({{ post.id }})</a> | |
|
52 | [{{ post.pub_time }}] | |
|
53 | [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}') | |
|
54 | ; return false;">>></a>] | |
|
55 | ||
|
56 | {% if moderator %} | |
|
57 | <span class="moderator_info"> | |
|
58 | [<a href="{% url 'delete' post_id=post.id %}" | |
|
59 | >{% trans 'Delete' %}</a>] | |
|
60 | ({{ post.poster_ip }}) | |
|
61 | [<a href="{% url 'ban' post_id=post.id %}?next={{ request.path }}" | |
|
62 | >{% trans 'Ban IP' %}</a>] | |
|
63 | </span> | |
|
64 | {% endif %} | |
|
79 | 65 | </div> |
|
80 |
{% |
|
|
81 | <div class="metadata"> | |
|
82 | <span class="tags"> | |
|
83 | {% for tag in post.get_tags %} | |
|
84 | <a class="tag" href="{% url 'tag' tag.name %}"> | |
|
85 | #{{ tag.name }}</a | |
|
86 | >{% if not forloop.last %},{% endif %} | |
|
87 | {% endfor %} | |
|
88 | </span> | |
|
89 | </div> | |
|
90 | {% endif %} | |
|
91 | </div> | |
|
66 | {% autoescape off %} | |
|
67 | {{ post.text.rendered }} | |
|
68 | {% endautoescape %} | |
|
69 | {% if post.is_referenced %} | |
|
70 | <div class="refmap"> | |
|
71 | {% trans "Replies" %}: | |
|
72 | {% for ref_post in post.get_sorted_referenced_posts %} | |
|
73 | <a href="{% post_url ref_post.id %}">>>{{ ref_post.id }}</a | |
|
74 | >{% if not forloop.last %},{% endif %} | |
|
92 | 75 | {% endfor %} |
|
93 | 76 | </div> |
|
94 | {% endcache %} | |
|
95 | 77 | {% endif %} |
|
78 | </div> | |
|
79 | {% if forloop.first %} | |
|
80 | <div class="metadata"> | |
|
81 | <span class="tags"> | |
|
82 | {% for tag in thread.get_tags %} | |
|
83 | <a class="tag" href="{% url 'tag' tag.name %}"> | |
|
84 | #{{ tag.name }}</a | |
|
85 | >{% if not forloop.last %},{% endif %} | |
|
86 | {% endfor %} | |
|
87 | </span> | |
|
88 | </div> | |
|
89 | {% endif %} | |
|
90 | </div> | |
|
91 | {% endfor %} | |
|
92 | </div> | |
|
93 | {% endcache %} | |
|
96 | 94 | |
|
97 | 95 | <form id="form" enctype="multipart/form-data" method="post" |
|
98 | 96 | >{% csrf_token %} |
@@ -152,10 +150,10 b'' | |||
|
152 | 150 | {% get_current_language as LANGUAGE_CODE %} |
|
153 | 151 | |
|
154 | 152 | <span class="metapanel" data-last-update="{{ last_update }}"> |
|
155 |
{% cache 600 thread_meta |
|
|
156 |
<span id="reply-count">{{ |
|
|
157 |
<span id="image-count">{{ |
|
|
158 |
{% trans 'Last update: ' %}{{ |
|
|
153 | {% cache 600 thread_meta thread.last_edit_time moderator LANGUAGE_CODE %} | |
|
154 | <span id="reply-count">{{ thread.get_reply_count }}</span> {% trans 'replies' %}, | |
|
155 | <span id="image-count">{{ thread.get_images_count }}</span> {% trans 'images' %}. | |
|
156 | {% trans 'Last update: ' %}{{ thread.last_edit_time }} | |
|
159 | 157 | [<a href="rss/">RSS</a>] |
|
160 | 158 | {% endcache %} |
|
161 | 159 | </span> |
@@ -30,8 +30,7 b' class PostTests(TestCase):' | |||
|
30 | 30 | |
|
31 | 31 | post = self._create_post() |
|
32 | 32 | |
|
33 | self.assertIsNotNone(post) | |
|
34 | self.assertIsNone(post.thread, 'Opening post has a thread') | |
|
33 | self.assertIsNotNone(post, 'No post was created') | |
|
35 | 34 | |
|
36 | 35 | def test_delete_post(self): |
|
37 | 36 | """Test post deletion""" |
@@ -46,10 +45,11 b' class PostTests(TestCase):' | |||
|
46 | 45 | def test_delete_thread(self): |
|
47 | 46 | """Test thread deletion""" |
|
48 | 47 | |
|
49 |
|
|
|
48 | opening_post = self._create_post() | |
|
49 | thread = opening_post.thread_new | |
|
50 | 50 | reply = Post.objects.create_post("", "", thread=thread) |
|
51 | 51 | |
|
52 |
|
|
|
52 | thread.delete_with_posts() | |
|
53 | 53 | |
|
54 | 54 | self.assertFalse(Post.objects.filter(id=reply.id).exists()) |
|
55 | 55 | |
@@ -57,10 +57,10 b' class PostTests(TestCase):' | |||
|
57 | 57 | """Test adding post to a thread""" |
|
58 | 58 | |
|
59 | 59 | op = self._create_post() |
|
60 | post = Post.objects.create_post("", "", thread=op) | |
|
60 | post = Post.objects.create_post("", "", thread=op.thread_new) | |
|
61 | 61 | |
|
62 | 62 | self.assertIsNotNone(post, 'Reply to thread wasn\'t created') |
|
63 | self.assertEqual(op.last_edit_time, post.pub_time, | |
|
63 | self.assertEqual(op.thread_new.last_edit_time, post.pub_time, | |
|
64 | 64 | 'Post\'s create time doesn\'t match thread last edit' |
|
65 | 65 | ' time') |
|
66 | 66 | |
@@ -80,18 +80,23 b' class PostTests(TestCase):' | |||
|
80 | 80 | opening_post = self._create_post() |
|
81 | 81 | |
|
82 | 82 | for i in range(0, 2): |
|
83 |
Post.objects.create_post('title', 'text', |
|
|
83 | Post.objects.create_post('title', 'text', | |
|
84 | thread=opening_post.thread_new) | |
|
84 | 85 | |
|
85 | 86 | thread = Post.objects.get_thread(opening_post.id) |
|
86 | 87 | |
|
87 |
self.assertEqual(3, len( |
|
|
88 | self.assertEqual(3, thread.replies.count()) | |
|
88 | 89 | |
|
89 | 90 | def test_create_post_with_tag(self): |
|
90 | 91 | """Test adding tag to post""" |
|
91 | 92 | |
|
92 | 93 | tag = Tag.objects.create(name='test_tag') |
|
93 | 94 | post = Post.objects.create_post(title='title', text='text', tags=[tag]) |
|
94 | self.assertIsNotNone(post) | |
|
95 | ||
|
96 | thread = post.thread_new | |
|
97 | self.assertIsNotNone(post, 'Post not created') | |
|
98 | self.assertTrue(tag in thread.tags.all(), 'Tag not added to thread') | |
|
99 | self.assertTrue(thread in tag.threads.all(), 'Thread not added to tag') | |
|
95 | 100 | |
|
96 | 101 | def test_thread_max_count(self): |
|
97 | 102 | """Test deletion of old posts when the max thread count is reached""" |
@@ -124,7 +129,7 b' class PostTests(TestCase):' | |||
|
124 | 129 | |
|
125 | 130 | post = Post.objects.create_post("", "", tags=[tag]) |
|
126 | 131 | |
|
127 | self.assertTrue(linked_tag in post.tags.all(), | |
|
132 | self.assertTrue(linked_tag in post.thread_new.tags.all(), | |
|
128 | 133 | 'Linked tag was not added') |
|
129 | 134 | |
|
130 | 135 | |
@@ -162,7 +167,8 b' class PagesTest(TestCase):' | |||
|
162 | 167 | u'Not existing tag is opened') |
|
163 | 168 | |
|
164 | 169 | reply_id = Post.objects.create_post('', TEST_TEXT, |
|
165 |
thread=Post.objects.all()[0] |
|
|
170 | thread=Post.objects.all()[0] | |
|
171 | .thread) | |
|
166 | 172 | response_not_existing = client.get(THREAD_PAGE + str( |
|
167 | 173 | reply_id) + '/') |
|
168 | 174 | self.assertEqual(PAGE_404, |
@@ -54,11 +54,12 b' def index(request, page=0):' | |||
|
54 | 54 | form = threadFormClass(error_class=PlainErrorList, **kwargs) |
|
55 | 55 | |
|
56 | 56 | threads = [] |
|
57 | for thread in Post.objects.get_threads(page=int(page)): | |
|
57 | for thread_to_show in Post.objects.get_threads(page=int(page)): | |
|
58 | 58 | threads.append({ |
|
59 | 'thread': thread, | |
|
60 |
' |
|
|
61 | 'last_replies': thread.get_last_replies(), | |
|
59 | 'thread': thread_to_show, | |
|
60 | 'op': thread_to_show.get_replies()[0], | |
|
61 | 'bumpable': thread_to_show.can_bump(), | |
|
62 | 'last_replies': thread_to_show.get_last_replies(), | |
|
62 | 63 | }) |
|
63 | 64 | |
|
64 | 65 | # TODO Make this generic for tag and threads list pages |
@@ -111,9 +112,12 b' def _new_post(request, form, opening_pos' | |||
|
111 | 112 | if len(tag_name) > 0: |
|
112 | 113 | tag, created = Tag.objects.get_or_create(name=tag_name) |
|
113 | 114 | tags.append(tag) |
|
115 | post_thread = None | |
|
116 | else: | |
|
117 | post_thread = opening_post.thread_new | |
|
114 | 118 | |
|
115 | 119 | post = Post.objects.create_post(title=title, text=text, ip=ip, |
|
116 |
thread= |
|
|
120 | thread=post_thread, image=image, | |
|
117 | 121 | tags=tags, user=_get_user(request)) |
|
118 | 122 | |
|
119 | 123 | thread_to_show = (opening_post.id if opening_post else post.id) |
@@ -133,12 +137,13 b' def tag(request, tag_name, page=0):' | |||
|
133 | 137 | |
|
134 | 138 | tag = get_object_or_404(Tag, name=tag_name) |
|
135 | 139 | threads = [] |
|
136 |
for thread in Post.objects.get_threads( |
|
|
140 | for thread_to_show in Post.objects.get_threads(page=int(page)): | |
|
137 | 141 | threads.append({ |
|
138 | 'thread': thread, | |
|
139 |
' |
|
|
140 | 'last_replies': thread.get_last_replies(), | |
|
141 | }) | |
|
142 | 'thread': thread_to_show, | |
|
143 | 'op': thread_to_show.get_replies()[0], | |
|
144 | 'bumpable': thread_to_show.can_bump(), | |
|
145 | 'last_replies': thread_to_show.get_last_replies(), | |
|
146 | }) | |
|
142 | 147 | |
|
143 | 148 | if request.method == 'POST': |
|
144 | 149 | form = ThreadForm(request.POST, request.FILES, |
@@ -196,20 +201,21 b' def thread(request, post_id):' | |||
|
196 | 201 | else: |
|
197 | 202 | form = postFormClass(error_class=PlainErrorList, **kwargs) |
|
198 | 203 | |
|
199 | posts = Post.objects.get_thread(post_id) | |
|
204 | thread_to_show = get_object_or_404(Post, id=post_id).thread_new | |
|
200 | 205 | |
|
201 | 206 | context = _init_default_context(request) |
|
202 | 207 | |
|
203 | context['posts'] = posts | |
|
208 | posts = thread_to_show.get_replies() | |
|
204 | 209 | context['form'] = form |
|
205 |
context['bumpable'] = |
|
|
210 | context['bumpable'] = thread_to_show.can_bump() | |
|
206 | 211 | if context['bumpable']: |
|
207 |
context['posts_left'] = neboard.settings.MAX_POSTS_PER_THREAD - |
|
|
208 |
|
|
|
212 | context['posts_left'] = neboard.settings.MAX_POSTS_PER_THREAD - posts\ | |
|
213 | .count() | |
|
209 | 214 | context['bumplimit_progress'] = str( |
|
210 | 215 | float(context['posts_left']) / |
|
211 | 216 | neboard.settings.MAX_POSTS_PER_THREAD * 100) |
|
212 |
context["last_update"] = _datetime_to_epoch( |
|
|
217 | context["last_update"] = _datetime_to_epoch(thread_to_show.last_edit_time) | |
|
218 | context["thread"] = thread_to_show | |
|
213 | 219 | |
|
214 | 220 | return render(request, 'boards/thread.html', context) |
|
215 | 221 |
@@ -9,6 +9,8 b' denied". Use second only for autoban for' | |||
|
9 | 9 | [DONE] Clean up tests and make them run ALWAYS |
|
10 | 10 | [DONE] Use transactions in tests |
|
11 | 11 | [DONE] Thread autoupdate (JS + API) |
|
12 | [IN PROGRESS] Split up post model into post and thread, | |
|
13 | and move everything that is used only in 1st post to thread model. | |
|
12 | 14 | |
|
13 | 15 | [NOT STARTED] Tree view (JS) |
|
14 | 16 | [NOT STARTED] Adding tags to images filename |
@@ -22,8 +24,6 b' denied". Use second only for autoban for' | |||
|
22 | 24 | [NOT STARTED] Character counter in the post field |
|
23 | 25 | [NOT STARTED] Save image thumbnails size to the separate field |
|
24 | 26 | [NOT STARTED] Whitelist functionality. Permin autoban of an address |
|
25 | [NOT STARTED] Split up post model into post and thread, | |
|
26 | and move everything that is used only in 1st post to thread model. | |
|
27 | 27 | [NOT STARTED] Statistics module. Count views (optional, may result in bad |
|
28 | 28 | performance), posts per day/week/month, users (or IPs) |
|
29 | 29 | [NOT STARTED] Quote button next to "reply" for posts in thread to include full |
General Comments 0
You need to be logged in to leave comments.
Login now