##// END OF EJS Templates
Added anonymous mode in which the board does not save poster IP addresses
neko259 -
r1362:9b48eb30 default
parent child Browse files
Show More
@@ -1,33 +1,34
1 1 [Version]
2 2 Version = 2.10.0 BT
3 3 SiteName = Neboard DEV
4 4
5 5 [Cache]
6 6 # Timeout for caching, if cache is used
7 7 CacheTimeout = 600
8 8
9 9 [Forms]
10 10 # Max post length in characters
11 11 MaxTextLength = 30000
12 12 MaxFileSize = 8000000
13 13 LimitPostingSpeed = false
14 14
15 15 [Messages]
16 16 # Thread bumplimit
17 17 MaxPostsPerThread = 10
18 18 # Old posts will be archived or deleted if this value is reached
19 19 MaxThreadCount = 5
20 AnonymousMode = false
20 21
21 22 [View]
22 23 DefaultTheme = md
23 24 DefaultImageViewer = simple
24 25 LastRepliesCount = 3
25 26 ThreadsPerPage = 3
26 27
27 28 [Storage]
28 29 # Enable archiving threads instead of deletion when the thread limit is reached
29 30 ArchiveThreads = true
30 31
31 32 [External]
32 33 # Thread update
33 34 WebsocketsEnabled = false
@@ -1,125 +1,126
1 1 import logging
2 2
3 3 from datetime import datetime, timedelta, date
4 4 from datetime import time as dtime
5 5
6 6 from django.db import models, transaction
7 7 from django.utils import timezone
8 8
9 9 import boards
10 10
11 11 from boards.models.user import Ban
12 12 from boards.mdx_neboard import Parser
13 13 from boards.models import PostImage, Attachment
14 14 from boards import utils
15 15
16 16 __author__ = 'neko259'
17 17
18 18 IMAGE_TYPES = (
19 19 'jpeg',
20 20 'jpg',
21 21 'png',
22 22 'bmp',
23 23 'gif',
24 24 )
25 25
26 26 POSTS_PER_DAY_RANGE = 7
27 27 NO_IP = '0.0.0.0'
28 28
29 29
30 30 class PostManager(models.Manager):
31 31 @transaction.atomic
32 32 def create_post(self, title: str, text: str, file=None, thread=None,
33 33 ip=NO_IP, tags: list=None, opening_posts: list=None, tripcode=None):
34 34 """
35 35 Creates new post
36 36 """
37 37
38 is_banned = Ban.objects.filter(ip=ip).exists()
38 if not utils.is_anonymous_mode():
39 is_banned = Ban.objects.filter(ip=ip).exists()
39 40
40 41 # TODO Raise specific exception and catch it in the views
41 42 if is_banned:
42 43 raise Exception("This user is banned")
43 44
44 45 if not tags:
45 46 tags = []
46 47 if not opening_posts:
47 48 opening_posts = []
48 49
49 50 posting_time = timezone.now()
50 51 new_thread = False
51 52 if not thread:
52 53 thread = boards.models.thread.Thread.objects.create(
53 54 bump_time=posting_time, last_edit_time=posting_time)
54 55 list(map(thread.tags.add, tags))
55 56 boards.models.thread.Thread.objects.process_oldest_threads()
56 57 new_thread = True
57 58
58 59 pre_text = Parser().preparse(text)
59 60
60 61 post = self.create(title=title,
61 62 text=pre_text,
62 63 pub_time=posting_time,
63 64 poster_ip=ip,
64 65 thread=thread,
65 66 last_edit_time=posting_time,
66 67 tripcode=tripcode,
67 68 opening=new_thread)
68 69 post.threads.add(thread)
69 70
70 71 logger = logging.getLogger('boards.post.create')
71 72
72 73 logger.info('Created post {} by {}'.format(post, post.poster_ip))
73 74
74 75 # TODO Move this to other place
75 76 if file:
76 77 file_type = file.name.split('.')[-1].lower()
77 78 if file_type in IMAGE_TYPES:
78 79 post.images.add(PostImage.objects.create_with_hash(file))
79 80 else:
80 81 post.attachments.add(Attachment.objects.create_with_hash(file))
81 82
82 83 post.build_url()
83 84 post.connect_replies()
84 85 post.connect_threads(opening_posts)
85 86 post.connect_notifications()
86 87
87 88 # Thread needs to be bumped only when the post is already created
88 89 if not new_thread:
89 90 thread.last_edit_time = posting_time
90 91 thread.bump()
91 92 thread.save()
92 93
93 94 return post
94 95
95 96 def delete_posts_by_ip(self, ip):
96 97 """
97 98 Deletes all posts of the author with same IP
98 99 """
99 100
100 101 posts = self.filter(poster_ip=ip)
101 102 for post in posts:
102 103 post.delete()
103 104
104 105 @utils.cached_result()
105 106 def get_posts_per_day(self) -> float:
106 107 """
107 108 Gets average count of posts per day for the last 7 days
108 109 """
109 110
110 111 day_end = date.today()
111 112 day_start = day_end - timedelta(POSTS_PER_DAY_RANGE)
112 113
113 114 day_time_start = timezone.make_aware(datetime.combine(
114 115 day_start, dtime()), timezone.get_current_timezone())
115 116 day_time_end = timezone.make_aware(datetime.combine(
116 117 day_end, dtime()), timezone.get_current_timezone())
117 118
118 119 posts_per_period = float(self.filter(
119 120 pub_time__lte=day_time_end,
120 121 pub_time__gte=day_time_start).count())
121 122
122 123 ppd = posts_per_period / POSTS_PER_DAY_RANGE
123 124
124 125 return ppd
125 126
@@ -1,103 +1,121
1 1 """
2 2 This module contains helper functions and helper classes.
3 3 """
4 4 import hashlib
5 5 import time
6 6 import hmac
7 7
8 8 from django.core.cache import cache
9 9 from django.db.models import Model
10 10 from django import forms
11 11
12 12 from django.utils import timezone
13 13 from django.utils.translation import ugettext_lazy as _
14
14 15 import boards
16 from boards.settings import get_bool
15 17
16 18 from neboard import settings
17 19
18 20
19 21 CACHE_KEY_DELIMITER = '_'
20 22 PERMISSION_MODERATE = 'moderation'
21 23
24 HTTP_FORWARDED = 'HTTP_X_FORWARDED_FOR'
25 META_REMOTE_ADDR = 'REMOTE_ADDR'
26
27 SETTING_MESSAGES = 'Messages'
28 SETTING_ANON_MODE = 'AnonymousMode'
29
30 ANON_IP = '127.0.0.1'
31
32
33 def is_anonymous_mode():
34 return get_bool(SETTING_MESSAGES, SETTING_ANON_MODE)
35
36
22 37 def get_client_ip(request):
23 x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
24 if x_forwarded_for:
25 ip = x_forwarded_for.split(',')[-1].strip()
38 if is_anonymous_mode():
39 ip = ANON_IP
26 40 else:
27 ip = request.META.get('REMOTE_ADDR')
41 x_forwarded_for = request.META.get(HTTP_FORWARDED)
42 if x_forwarded_for:
43 ip = x_forwarded_for.split(',')[-1].strip()
44 else:
45 ip = request.META.get(META_REMOTE_ADDR)
28 46 return ip
29 47
30 48
31 49 # TODO The output format is not epoch because it includes microseconds
32 50 def datetime_to_epoch(datetime):
33 51 return int(time.mktime(timezone.localtime(
34 52 datetime,timezone.get_current_timezone()).timetuple())
35 53 * 1000000 + datetime.microsecond)
36 54
37 55
38 56 def get_websocket_token(user_id='', timestamp=''):
39 57 """
40 58 Create token to validate information provided by new connection.
41 59 """
42 60
43 61 sign = hmac.new(settings.CENTRIFUGE_PROJECT_SECRET.encode())
44 62 sign.update(settings.CENTRIFUGE_PROJECT_ID.encode())
45 63 sign.update(user_id.encode())
46 64 sign.update(timestamp.encode())
47 65 token = sign.hexdigest()
48 66
49 67 return token
50 68
51 69
52 70 def cached_result(key_method=None):
53 71 """
54 72 Caches method result in the Django's cache system, persisted by object name,
55 73 object name and model id if object is a Django model.
56 74 """
57 75 def _cached_result(function):
58 76 def inner_func(obj, *args, **kwargs):
59 77 # TODO Include method arguments to the cache key
60 78 cache_key_params = [obj.__class__.__name__, function.__name__]
61 79 if isinstance(obj, Model):
62 80 cache_key_params.append(str(obj.id))
63 81
64 82 if key_method is not None:
65 83 cache_key_params += [str(arg) for arg in key_method(obj)]
66 84
67 85 cache_key = CACHE_KEY_DELIMITER.join(cache_key_params)
68 86
69 87 persisted_result = cache.get(cache_key)
70 88 if persisted_result is not None:
71 89 result = persisted_result
72 90 else:
73 91 result = function(obj, *args, **kwargs)
74 92 cache.set(cache_key, result)
75 93
76 94 return result
77 95
78 96 return inner_func
79 97 return _cached_result
80 98
81 99
82 100 def is_moderator(request):
83 101 try:
84 102 moderate = request.user.has_perm(PERMISSION_MODERATE)
85 103 except AttributeError:
86 104 moderate = False
87 105
88 106 return moderate
89 107
90 108
91 109 def get_file_hash(file) -> str:
92 110 md5 = hashlib.md5()
93 111 for chunk in file.chunks():
94 112 md5.update(chunk)
95 113 return md5.hexdigest()
96 114
97 115
98 116 def validate_file_size(size: int):
99 117 max_size = boards.settings.get_int('Forms', 'MaxFileSize')
100 118 if size > max_size:
101 119 raise forms.ValidationError(
102 120 _('File must be less than %s bytes')
103 121 % str(max_size))
General Comments 0
You need to be logged in to leave comments. Login now