Show More
@@ -1,45 +1,48 b'' | |||
|
1 | 1 | [Version] |
|
2 | 2 | Version = 4.0.0 2017 |
|
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 | LimitFirstPosting = true |
|
14 | 14 | LimitPostingSpeed = false |
|
15 | 15 | PowDifficulty = 0 |
|
16 | 16 | # Delay in seconds |
|
17 | 17 | PostingDelay = 30 |
|
18 | 18 | Autoban = false |
|
19 | 19 | DefaultTag = test |
|
20 | 20 | MaxFileCount = 1 |
|
21 | 21 | |
|
22 | 22 | [Messages] |
|
23 | 23 | # Thread bumplimit |
|
24 | 24 | MaxPostsPerThread = 10 |
|
25 | 25 | ThreadArchiveDays = 300 |
|
26 | 26 | AnonymousMode = false |
|
27 | 27 | |
|
28 | 28 | [View] |
|
29 | 29 | DefaultTheme = md |
|
30 | 30 | DefaultImageViewer = simple |
|
31 | 31 | LastRepliesCount = 3 |
|
32 | 32 | ThreadsPerPage = 3 |
|
33 | 33 | PostsPerPage = 10 |
|
34 | 34 | ImagesPerPageGallery = 20 |
|
35 | 35 | MaxFavoriteThreads = 20 |
|
36 | 36 | MaxLandingThreads = 20 |
|
37 | 37 | Themes=md:Mystic Dark,md_centered:Mystic Dark (centered),sw:Snow White,pg:Photon Grey,ad:Amanita Dark,iw:Inocibe White |
|
38 | 38 | ImageViewers=simple:Simple,popup:Popup |
|
39 | 39 | |
|
40 | 40 | [Storage] |
|
41 | 41 | # Enable archiving threads instead of deletion when the thread limit is reached |
|
42 | 42 | ArchiveThreads = true |
|
43 | 43 | |
|
44 | 44 | [RSS] |
|
45 | 45 | MaxItems = 20 |
|
46 | ||
|
47 | [External] | |
|
48 | ImageSearchHost=http://127.0.0.1/ |
@@ -1,39 +1,40 b'' | |||
|
1 | 1 | {% extends "boards/thread.html" %} |
|
2 | 2 | |
|
3 | 3 | {% load i18n %} |
|
4 | 4 | {% load static from staticfiles %} |
|
5 | 5 | {% load board %} |
|
6 | 6 | {% load tz %} |
|
7 | 7 | |
|
8 | 8 | {% block head %} |
|
9 | 9 | <meta name="robots" content="noindex"> |
|
10 | 10 | <title>{{ thread.get_opening_post.get_title|striptags|truncatewords:10 }} |
|
11 | 11 | - {{ site_name }}</title> |
|
12 | 12 | {% endblock %} |
|
13 | 13 | |
|
14 | 14 | {% block thread_content %} |
|
15 | 15 | {% get_current_language as LANGUAGE_CODE %} |
|
16 | 16 | {% get_current_timezone as TIME_ZONE %} |
|
17 | 17 | |
|
18 | 18 | <div id="posts-table"> |
|
19 | 19 | {% if posts %} |
|
20 | 20 | {% for post in posts %} |
|
21 | 21 | {% for image in post.get_images %} |
|
22 | 22 | <div class="gallery_image"> |
|
23 | 23 | {% autoescape off %} |
|
24 | 24 | {{ image.get_view }} |
|
25 | 25 | <div class="gallery_image_metadata"> |
|
26 | 26 | {{ image.get_size.0 }}x{{ image.get_size.1 }} |
|
27 |
{% image_actions image.file.url |
|
|
27 | {% image_actions image.file.url %}, | |
|
28 | [<a href="{% url 'feed' %}?image_hash={{ image.hash }}">{{ site_name }}</a>] | |
|
28 | 29 | <br /> |
|
29 | 30 | <a href="{{ post.get_absolute_url }}">>>{{ post.id }}</a> |
|
30 | 31 | </div> |
|
31 | 32 | {% endautoescape %} |
|
32 | 33 | </div> |
|
33 | 34 | {% endfor %} |
|
34 | 35 | {% endfor %} |
|
35 | 36 | {% else %} |
|
36 | 37 | {% trans 'No images.' %} |
|
37 | 38 | {% endif %} |
|
38 | 39 | </div> |
|
39 | 40 | {% endblock %} |
@@ -1,119 +1,123 b'' | |||
|
1 | 1 | import re |
|
2 | 2 | |
|
3 | 3 | from django.shortcuts import get_object_or_404 |
|
4 | 4 | from django import template |
|
5 | 5 | from django.utils.text import re_tag |
|
6 | from django.core.urlresolvers import reverse | |
|
6 | 7 | |
|
7 | 8 | from boards.mdx_neboard import LINE_BREAK_HTML |
|
9 | from boards import settings | |
|
8 | 10 | |
|
9 | 11 | |
|
10 | 12 | IMG_ACTION_URL = '[<a href="{}">{}</a>]' |
|
11 | 13 | REGEX_NEWLINE = re.compile(LINE_BREAK_HTML) |
|
12 | 14 | TRUNCATOR = '...' |
|
13 | 15 | HTML4_SINGLETS =( |
|
14 | 16 | 'br', 'col', 'link', 'base', 'img', 'param', 'area', 'hr', 'input' |
|
15 | 17 | ) |
|
16 | 18 | |
|
17 | 19 | |
|
18 | 20 | register = template.Library() |
|
19 | 21 | |
|
20 | 22 | actions = [ |
|
21 | 23 | { |
|
22 | 24 | 'name': 'google', |
|
23 |
'link': 'http://google.com/searchbyimage?image_url= |
|
|
25 | 'link': 'https://www.google.com/searchbyimage?image_url={}', | |
|
24 | 26 | }, |
|
25 | 27 | { |
|
26 | 28 | 'name': 'iqdb', |
|
27 |
'link': 'http://iqdb.org/?url= |
|
|
29 | 'link': 'http://iqdb.org/?url={}', | |
|
28 | 30 | }, |
|
29 | 31 | ] |
|
30 | 32 | |
|
31 | 33 | |
|
32 | 34 | @register.simple_tag(name='post_url') |
|
33 | 35 | def post_url(*args, **kwargs): |
|
34 | 36 | post_id = args[0] |
|
35 | 37 | |
|
36 | 38 | post = get_object_or_404('Post', id=post_id) |
|
37 | 39 | |
|
38 | 40 | return post.get_absolute_url() |
|
39 | 41 | |
|
40 | 42 | |
|
41 | 43 | @register.simple_tag(name='image_actions') |
|
42 | 44 | def image_actions(*args, **kwargs): |
|
43 | 45 | image_link = args[0] |
|
44 | if len(args) > 1: | |
|
45 | image_link = 'http://' + args[1] + image_link # TODO https? | |
|
46 | host = settings.get('External', 'ImageSearchHost') | |
|
47 | if host.endswith('/'): | |
|
48 | host = host[:-1] | |
|
49 | image_link = settings.get('External', 'ImageSearchHost') + image_link | |
|
46 | 50 | |
|
47 | 51 | return ', '.join([IMG_ACTION_URL.format( |
|
48 |
action['link'] |
|
|
52 | action['link'].format(image_link), action['name']) for action in actions]) | |
|
49 | 53 | |
|
50 | 54 | |
|
51 | 55 | @register.inclusion_tag('boards/post.html', name='post_view', takes_context=True) |
|
52 | 56 | def post_view(context, post, *args, **kwargs): |
|
53 | 57 | kwargs['perms'] = context['perms'] |
|
54 | 58 | return post.get_view_params(*args, **kwargs) |
|
55 | 59 | |
|
56 | 60 | |
|
57 | 61 | @register.simple_tag(name='page_url') |
|
58 | 62 | def page_url(paginator, page_number, *args, **kwargs): |
|
59 | 63 | if paginator.supports_urls(): |
|
60 | 64 | return paginator.get_page_url(page_number) |
|
61 | 65 | |
|
62 | 66 | |
|
63 | 67 | @register.filter(name='truncatenewlines_html') |
|
64 | 68 | def truncatenewlines_html(value, arg): |
|
65 | 69 | end_pos = 0 |
|
66 | 70 | start_pos = 0 |
|
67 | 71 | match_count = 0 |
|
68 | 72 | |
|
69 | 73 | # Collect places for truncation |
|
70 | 74 | while match_count <= arg: |
|
71 | 75 | m = REGEX_NEWLINE.search(value, end_pos) |
|
72 | 76 | if m is None: |
|
73 | 77 | break |
|
74 | 78 | else: |
|
75 | 79 | match_count += 1 |
|
76 | 80 | end_pos = m.end() |
|
77 | 81 | start_pos = m.start() |
|
78 | 82 | |
|
79 | 83 | # Find and close open tags |
|
80 | 84 | if match_count > arg: |
|
81 | 85 | truncate_pos = start_pos |
|
82 | 86 | |
|
83 | 87 | open_tags = [] |
|
84 | 88 | text = value[:truncate_pos] |
|
85 | 89 | current_pos = 0 |
|
86 | 90 | while True: |
|
87 | 91 | tag = re_tag.search(text, current_pos) |
|
88 | 92 | if tag is None: |
|
89 | 93 | break |
|
90 | 94 | else: |
|
91 | 95 | closing_tag, tagname, self_closing = tag.groups() |
|
92 | 96 | tagname = tagname.lower() |
|
93 | 97 | if self_closing or tagname in HTML4_SINGLETS: |
|
94 | 98 | pass |
|
95 | 99 | elif closing_tag: |
|
96 | 100 | # Check for match in open tags list |
|
97 | 101 | try: |
|
98 | 102 | i = open_tags.index(tagname) |
|
99 | 103 | except ValueError: |
|
100 | 104 | pass |
|
101 | 105 | else: |
|
102 | 106 | # SGML: An end tag closes, back to the matching start tag, |
|
103 | 107 | # all unclosed intervening start tags with omitted end tags |
|
104 | 108 | open_tags = open_tags[i + 1:] |
|
105 | 109 | else: |
|
106 | 110 | # Add it to the start of the open tags list |
|
107 | 111 | open_tags.insert(0, tagname) |
|
108 | 112 | |
|
109 | 113 | current_pos = tag.end() |
|
110 | 114 | |
|
111 | 115 | if not text.endswith(TRUNCATOR): |
|
112 | 116 | text += TRUNCATOR |
|
113 | 117 | for tag in open_tags: |
|
114 | 118 | text += '</{}>'.format(tag) |
|
115 | 119 | else: |
|
116 | 120 | text = value |
|
117 | 121 | |
|
118 | 122 | return text |
|
119 | 123 |
@@ -1,74 +1,123 b'' | |||
|
1 | 1 | from django.core.urlresolvers import reverse |
|
2 | 2 | from django.shortcuts import render |
|
3 | 3 | |
|
4 | 4 | from boards import settings |
|
5 | 5 | from boards.abstracts.paginator import get_paginator |
|
6 | 6 | from boards.abstracts.settingsmanager import get_settings_manager |
|
7 | 7 | from boards.models import Post |
|
8 | 8 | from boards.views.base import BaseBoardView |
|
9 | 9 | from boards.views.posting_mixin import PostMixin |
|
10 | 10 | |
|
11 | 11 | POSTS_PER_PAGE = settings.get_int('View', 'PostsPerPage') |
|
12 | 12 | |
|
13 | 13 | PARAMETER_CURRENT_PAGE = 'current_page' |
|
14 | 14 | PARAMETER_PAGINATOR = 'paginator' |
|
15 | 15 | PARAMETER_POSTS = 'posts' |
|
16 | 16 | |
|
17 | 17 | PARAMETER_PREV_LINK = 'prev_page_link' |
|
18 | 18 | PARAMETER_NEXT_LINK = 'next_page_link' |
|
19 | 19 | |
|
20 | 20 | TEMPLATE = 'boards/feed.html' |
|
21 | 21 | DEFAULT_PAGE = 1 |
|
22 | 22 | |
|
23 | 23 | |
|
24 | class FeedFilter: | |
|
25 | @staticmethod | |
|
26 | def get_filtered_posts(request, posts): | |
|
27 | return posts | |
|
28 | ||
|
29 | ||
|
30 | class TripcodeFilter(FeedFilter): | |
|
31 | @staticmethod | |
|
32 | def get_filtered_posts(request, posts): | |
|
33 | filtered_posts = posts | |
|
34 | tripcode = request.GET.get('tripcode', None) | |
|
35 | if tripcode: | |
|
36 | filtered_posts = filtered_posts.filter(tripcode) | |
|
37 | return filtered_posts | |
|
38 | ||
|
39 | ||
|
40 | class FavoritesFilter(FeedFilter): | |
|
41 | @staticmethod | |
|
42 | def get_filtered_posts(request, posts): | |
|
43 | filtered_posts = posts | |
|
44 | ||
|
45 | favorites = 'favorites' in request.GET | |
|
46 | if favorites: | |
|
47 | settings_manager = get_settings_manager(request) | |
|
48 | fav_thread_ops = Post.objects.filter(id__in=settings_manager.get_fav_threads().keys()) | |
|
49 | fav_threads = [op.get_thread() for op in fav_thread_ops] | |
|
50 | filtered_posts = filtered_posts.filter(thread__in=fav_threads) | |
|
51 | return filtered_posts | |
|
52 | ||
|
53 | ||
|
54 | class IpFilter(FeedFilter): | |
|
55 | @staticmethod | |
|
56 | def get_filtered_posts(request, posts): | |
|
57 | filtered_posts = posts | |
|
58 | ||
|
59 | ip = request.GET.get('ip', None) | |
|
60 | if ip and request.user.has_perm('post_delete'): | |
|
61 | filtered_posts = filtered_posts.filter(poster_ip=ip) | |
|
62 | return filtered_posts | |
|
63 | ||
|
64 | ||
|
65 | class HashFilter(FeedFilter): | |
|
66 | @staticmethod | |
|
67 | def get_filtered_posts(request, posts): | |
|
68 | filtered_posts = posts | |
|
69 | ||
|
70 | image_hash = request.GET.get('image_hash', None) | |
|
71 | if image_hash: | |
|
72 | filtered_posts = filtered_posts.filter(attachments__hash=image_hash) | |
|
73 | return filtered_posts | |
|
74 | ||
|
75 | ||
|
24 | 76 | class FeedView(PostMixin, BaseBoardView): |
|
77 | filters = ( | |
|
78 | TripcodeFilter, | |
|
79 | FavoritesFilter, | |
|
80 | IpFilter, | |
|
81 | HashFilter, | |
|
82 | ) | |
|
25 | 83 | |
|
26 | 84 | def get(self, request): |
|
27 | 85 | page = request.GET.get('page', DEFAULT_PAGE) |
|
28 | tripcode = request.GET.get('tripcode', None) | |
|
29 | favorites = 'favorites' in request.GET | |
|
30 | ip = request.GET.get('ip', None) | |
|
31 | 86 | |
|
32 | 87 | params = self.get_context_data(request=request) |
|
33 | 88 | |
|
34 | 89 | settings_manager = get_settings_manager(request) |
|
35 | 90 | |
|
36 | 91 | posts = Post.objects.exclude( |
|
37 | 92 | thread__tags__in=settings_manager.get_hidden_tags()).order_by( |
|
38 | 93 | '-pub_time').prefetch_related('attachments', 'thread') |
|
39 | if tripcode: | |
|
40 | posts = posts.filter(tripcode=tripcode) | |
|
41 | if favorites: | |
|
42 | fav_thread_ops = Post.objects.filter(id__in=settings_manager.get_fav_threads().keys()) | |
|
43 | fav_threads = [op.get_thread() for op in fav_thread_ops] | |
|
44 | posts = posts.filter(thread__in=fav_threads) | |
|
45 | if ip and request.user.has_perm('post_delete'): | |
|
46 | posts = posts.filter(poster_ip=ip) | |
|
94 | for filter in self.filters: | |
|
95 | posts = filter.get_filtered_posts(request, posts) | |
|
47 | 96 | |
|
48 | 97 | paginator = get_paginator(posts, POSTS_PER_PAGE) |
|
49 | 98 | paginator.current_page = int(page) |
|
50 | 99 | |
|
51 | 100 | params[PARAMETER_POSTS] = paginator.page(page).object_list |
|
52 | 101 | |
|
53 | 102 | paginator.set_url(reverse('feed'), request.GET.dict()) |
|
54 | 103 | |
|
55 | 104 | self.get_page_context(paginator, params, page) |
|
56 | 105 | |
|
57 | 106 | return render(request, TEMPLATE, params) |
|
58 | 107 | |
|
59 | 108 | # TODO Dedup this into PagedMixin |
|
60 | 109 | def get_page_context(self, paginator, params, page): |
|
61 | 110 | """ |
|
62 | 111 | Get pagination context variables |
|
63 | 112 | """ |
|
64 | 113 | |
|
65 | 114 | params[PARAMETER_PAGINATOR] = paginator |
|
66 | 115 | current_page = paginator.page(int(page)) |
|
67 | 116 | params[PARAMETER_CURRENT_PAGE] = current_page |
|
68 | 117 | if current_page.has_previous(): |
|
69 | 118 | params[PARAMETER_PREV_LINK] = paginator.get_page_url( |
|
70 | 119 | current_page.previous_page_number()) |
|
71 | 120 | if current_page.has_next(): |
|
72 | 121 | params[PARAMETER_NEXT_LINK] = paginator.get_page_url( |
|
73 | 122 | current_page.next_page_number()) |
|
74 | 123 |
General Comments 0
You need to be logged in to leave comments.
Login now