##// END OF EJS Templates
Switch to show only favorite tags (BB-94)
neko259 -
r1691:8a821ff8 default
parent child Browse files
Show More
@@ -1,199 +1,200 b''
1 1 from boards import settings
2 2 from boards.models import Tag
3 3 from boards.models.thread import FAV_THREAD_NO_UPDATES
4 4
5 5 MAX_TRIPCODE_COLLISIONS = 50
6 6
7 7 __author__ = 'neko259'
8 8
9 9 SESSION_SETTING = 'setting'
10 10
11 11 # Remove this, it is not used any more cause there is a user's permission
12 12 PERMISSION_MODERATE = 'moderator'
13 13
14 14 SETTING_THEME = 'theme'
15 15 SETTING_FAVORITE_TAGS = 'favorite_tags'
16 16 SETTING_FAVORITE_THREADS = 'favorite_threads'
17 17 SETTING_HIDDEN_TAGS = 'hidden_tags'
18 18 SETTING_PERMISSIONS = 'permissions'
19 19 SETTING_USERNAME = 'username'
20 20 SETTING_LAST_NOTIFICATION_ID = 'last_notification'
21 21 SETTING_IMAGE_VIEWER = 'image_viewer'
22 22 SETTING_TRIPCODE = 'tripcode'
23 23 SETTING_IMAGES = 'images_aliases'
24 SETTING_ONLY_FAVORITES = 'only_favorites'
24 25
25 26 DEFAULT_THEME = 'md'
26 27
27 28
28 29 class SettingsManager:
29 30 """
30 31 Base settings manager class. get_setting and set_setting methods should
31 32 be overriden.
32 33 """
33 34 def __init__(self):
34 35 pass
35 36
36 37 def get_theme(self) -> str:
37 38 theme = self.get_setting(SETTING_THEME)
38 39 if not theme:
39 40 theme = DEFAULT_THEME
40 41 self.set_setting(SETTING_THEME, theme)
41 42
42 43 return theme
43 44
44 45 def set_theme(self, theme):
45 46 self.set_setting(SETTING_THEME, theme)
46 47
47 48 def has_permission(self, permission):
48 49 permissions = self.get_setting(SETTING_PERMISSIONS)
49 50 if permissions:
50 51 return permission in permissions
51 52 else:
52 53 return False
53 54
54 55 def get_setting(self, setting, default=None):
55 56 pass
56 57
57 58 def set_setting(self, setting, value):
58 59 pass
59 60
60 61 def add_permission(self, permission):
61 62 permissions = self.get_setting(SETTING_PERMISSIONS)
62 63 if not permissions:
63 64 permissions = [permission]
64 65 else:
65 66 permissions.append(permission)
66 67 self.set_setting(SETTING_PERMISSIONS, permissions)
67 68
68 69 def del_permission(self, permission):
69 70 permissions = self.get_setting(SETTING_PERMISSIONS)
70 71 if not permissions:
71 72 permissions = []
72 73 else:
73 74 permissions.remove(permission)
74 75 self.set_setting(SETTING_PERMISSIONS, permissions)
75 76
76 77 def get_fav_tags(self) -> list:
77 78 tag_names = self.get_setting(SETTING_FAVORITE_TAGS)
78 79 tags = []
79 80 if tag_names:
80 81 tags = list(Tag.objects.filter(name__in=tag_names))
81 82 return tags
82 83
83 84 def add_fav_tag(self, tag):
84 85 tags = self.get_setting(SETTING_FAVORITE_TAGS)
85 86 if not tags:
86 87 tags = [tag.name]
87 88 else:
88 89 if not tag.name in tags:
89 90 tags.append(tag.name)
90 91
91 92 tags.sort()
92 93 self.set_setting(SETTING_FAVORITE_TAGS, tags)
93 94
94 95 def del_fav_tag(self, tag):
95 96 tags = self.get_setting(SETTING_FAVORITE_TAGS)
96 97 if tag.name in tags:
97 98 tags.remove(tag.name)
98 99 self.set_setting(SETTING_FAVORITE_TAGS, tags)
99 100
100 101 def get_hidden_tags(self) -> list:
101 102 tag_names = self.get_setting(SETTING_HIDDEN_TAGS)
102 103 tags = []
103 104 if tag_names:
104 105 tags = list(Tag.objects.filter(name__in=tag_names))
105 106
106 107 return tags
107 108
108 109 def add_hidden_tag(self, tag):
109 110 tags = self.get_setting(SETTING_HIDDEN_TAGS)
110 111 if not tags:
111 112 tags = [tag.name]
112 113 else:
113 114 if not tag.name in tags:
114 115 tags.append(tag.name)
115 116
116 117 tags.sort()
117 118 self.set_setting(SETTING_HIDDEN_TAGS, tags)
118 119
119 120 def del_hidden_tag(self, tag):
120 121 tags = self.get_setting(SETTING_HIDDEN_TAGS)
121 122 if tag.name in tags:
122 123 tags.remove(tag.name)
123 124 self.set_setting(SETTING_HIDDEN_TAGS, tags)
124 125
125 126 def get_fav_threads(self) -> dict:
126 127 return self.get_setting(SETTING_FAVORITE_THREADS, default=dict())
127 128
128 129 def add_or_read_fav_thread(self, opening_post):
129 130 threads = self.get_fav_threads()
130 131
131 132 max_fav_threads = settings.get_int('View', 'MaxFavoriteThreads')
132 133 if (str(opening_post.id) in threads) or (len(threads) < max_fav_threads):
133 134 thread = opening_post.get_thread()
134 135 # Don't check for new posts if the thread is archived already
135 136 if thread.is_archived():
136 137 last_id = FAV_THREAD_NO_UPDATES
137 138 else:
138 139 last_id = thread.get_replies().last().id
139 140 threads[str(opening_post.id)] = last_id
140 141 self.set_setting(SETTING_FAVORITE_THREADS, threads)
141 142
142 143 def del_fav_thread(self, opening_post):
143 144 threads = self.get_fav_threads()
144 145 if self.thread_is_fav(opening_post):
145 146 del threads[str(opening_post.id)]
146 147 self.set_setting(SETTING_FAVORITE_THREADS, threads)
147 148
148 149 def thread_is_fav(self, opening_post):
149 150 return str(opening_post.id) in self.get_fav_threads()
150 151
151 152 def get_notification_usernames(self):
152 153 names = set()
153 154 name_list = self.get_setting(SETTING_USERNAME)
154 155 if name_list is not None:
155 156 name_list = name_list.strip()
156 157 if len(name_list) > 0:
157 158 names = name_list.lower().split(',')
158 159 names = set(name.strip() for name in names)
159 160 return names
160 161
161 162 def get_image_by_alias(self, alias):
162 163 images = self.get_setting(SETTING_IMAGES)
163 164 if images is not None and len(images) > 0:
164 165 return images.get(alias)
165 166
166 167 def add_image_alias(self, alias, image):
167 168 images = self.get_setting(SETTING_IMAGES)
168 169 if images is None:
169 170 images = dict()
170 171 images.put(alias, image)
171 172
172 173
173 174 class SessionSettingsManager(SettingsManager):
174 175 """
175 176 Session-based settings manager. All settings are saved to the user's
176 177 session.
177 178 """
178 179 def __init__(self, session):
179 180 SettingsManager.__init__(self)
180 181 self.session = session
181 182
182 183 def get_setting(self, setting, default=None):
183 184 if setting in self.session:
184 185 return self.session[setting]
185 186 else:
186 187 self.set_setting(setting, default)
187 188 return default
188 189
189 190 def set_setting(self, setting, value):
190 191 self.session[setting] = value
191 192
192 193
193 194 def get_settings_manager(request) -> SettingsManager:
194 195 """
195 196 Get settings manager based on the request object. Currently only
196 197 session-based manager is supported. In the future, cookie-based or
197 198 database-based managers could be implemented.
198 199 """
199 200 return SessionSettingsManager(request.session)
@@ -1,88 +1,91 b''
1 1 from boards.abstracts.settingsmanager import get_settings_manager, \
2 SETTING_LAST_NOTIFICATION_ID, SETTING_IMAGE_VIEWER
2 SETTING_LAST_NOTIFICATION_ID, SETTING_IMAGE_VIEWER, SETTING_ONLY_FAVORITES
3 3 from boards.models.user import Notification
4 4 from boards.models import Banner
5 5
6 6 __author__ = 'neko259'
7 7
8 8 import neboard
9 9 from boards import settings
10 10 from boards.models import Post, Tag, Thread
11 11
12 12 CONTEXT_SITE_NAME = 'site_name'
13 13 CONTEXT_VERSION = 'version'
14 14 CONTEXT_THEME_CSS = 'theme_css'
15 15 CONTEXT_THEME = 'theme'
16 16 CONTEXT_PPD = 'posts_per_day'
17 17 CONTEXT_USER = 'user'
18 18 CONTEXT_NEW_NOTIFICATIONS_COUNT = 'new_notifications_count'
19 19 CONTEXT_USERNAMES = 'usernames'
20 20 CONTEXT_TAGS_STR = 'tags_str'
21 21 CONTEXT_IMAGE_VIEWER = 'image_viewer'
22 22 CONTEXT_HAS_FAV_THREADS = 'has_fav_threads'
23 23 CONTEXT_POW_DIFFICULTY = 'pow_difficulty'
24 24 CONTEXT_NEW_POST_COUNT = 'new_post_count'
25 25 CONTEXT_SEARCH_ENABLED = 'search_enabled'
26 26 CONTEXT_BANNERS = 'banners'
27 CONTEXT_ONLY_FAVORITES = 'only_favorites'
27 28
28 29
29 30 def get_notifications(context, settings_manager):
30 31 usernames = settings_manager.get_notification_usernames()
31 32 new_notifications_count = 0
32 33 if usernames:
33 34 last_notification_id = settings_manager.get_setting(
34 35 SETTING_LAST_NOTIFICATION_ID)
35 36
36 37 new_notifications_count = Notification.objects.get_notification_posts(
37 38 usernames=usernames, last=last_notification_id).only('id').count()
38 39 context[CONTEXT_NEW_NOTIFICATIONS_COUNT] = new_notifications_count
39 40 context[CONTEXT_USERNAMES] = usernames
40 41
41 42
42 43 def get_new_post_count(context, settings_manager):
43 44 fav_threads = settings_manager.get_fav_threads()
44 45 if fav_threads:
45 46 fav_thread_ops = Post.objects.filter(id__in=fav_threads.keys()) \
46 47 .order_by('-pub_time').only('thread_id', 'pub_time')
47 48 ops = [{'op': op, 'last_id': fav_threads[str(op.id)]} for op in fav_thread_ops]
48 49 count = Thread.objects.get_new_post_count(ops)
49 50 if count > 0:
50 51 context[CONTEXT_NEW_POST_COUNT] = '(+{})'.format(count)
51 52
52 53
53 54 def user_and_ui_processor(request):
54 55 context = dict()
55 56
56 57 context[CONTEXT_PPD] = float(Post.objects.get_posts_per_day())
57 58
58 59 settings_manager = get_settings_manager(request)
59 60 fav_tags = settings_manager.get_fav_tags()
60 61
61 62 context[CONTEXT_TAGS_STR] = Tag.objects.get_tag_url_list(fav_tags)
62 63 theme = settings_manager.get_theme()
63 64 context[CONTEXT_THEME] = theme
64 65
65 66 # TODO Use static here
66 67 context[CONTEXT_THEME_CSS] = 'css/' + theme + '/base_page.css'
67 68
68 69 context[CONTEXT_VERSION] = settings.get('Version', 'Version')
69 70 context[CONTEXT_SITE_NAME] = settings.get('Version', 'SiteName')
70 71
71 72 if (settings.get_bool('Forms', 'LimitFirstPosting') and not settings_manager.get_setting('confirmed_user'))\
72 73 or settings.get_bool('Forms', 'LimitPostingSpeed'):
73 74 context[CONTEXT_POW_DIFFICULTY] = settings.get_int('Forms', 'PowDifficulty')
74 75
75 76 context[CONTEXT_IMAGE_VIEWER] = settings_manager.get_setting(
76 77 SETTING_IMAGE_VIEWER,
77 78 default=settings.get('View', 'DefaultImageViewer'))
78 79
79 80 context[CONTEXT_HAS_FAV_THREADS] =\
80 81 len(settings_manager.get_fav_threads()) > 0
81 82
82 83 context[CONTEXT_SEARCH_ENABLED] = 'haystack' in neboard.settings.INSTALLED_APPS
83 84 context[CONTEXT_BANNERS] = Banner.objects.order_by('-id')
85 context[CONTEXT_ONLY_FAVORITES] = settings_manager.get_setting(
86 SETTING_ONLY_FAVORITES, False)
84 87
85 88 get_notifications(context, settings_manager)
86 89 get_new_post_count(context, settings_manager)
87 90
88 91 return context
@@ -1,90 +1,96 b''
1 1 {% load staticfiles %}
2 2 {% load i18n %}
3 3 {% load l10n %}
4 4 {% load static from staticfiles %}
5 5
6 6 <!DOCTYPE html>
7 7 <html>
8 8 <head>
9 9 <link rel="stylesheet" type="text/css" href="{% static 'css/base.css' %}" media="all"/>
10 10 <link rel="stylesheet" type="text/css" href="{% static 'css/3party/highlight.css' %}" media="all"/>
11 11 <link rel="stylesheet" type="text/css" href="{% static 'css/3party/jquery-ui.min.css' %}" media="all"/>
12 12 <link rel="stylesheet" type="text/css" href="{% static theme_css %}" media="all"/>
13 13
14 14 {% if rss_url %}
15 15 <link rel="alternate" type="application/rss+xml" href="{{ rss_url }}" title="{% trans 'Feed' %}"/>
16 16 {% endif %}
17 17
18 18 <link rel="icon" type="image/png"
19 19 href="{% static 'favicon.png' %}">
20 20
21 21 <meta name="viewport" content="width=device-width, initial-scale=1"/>
22 22 <meta charset="utf-8"/>
23 23
24 24 {% block head %}{% endblock %}
25 25 </head>
26 26 <body data-image-viewer="{{ image_viewer }}"
27 27 data-pow-difficulty="{{ pow_difficulty }}"
28 28 data-update-script="{% static 'js/updates.js' %}">
29 29 <script src="{% static 'js/jquery-2.2.0.min.js' %}"></script>
30 30
31 31 <header class="navigation_panel">
32 32 <a class="link" href="{% url 'index' %}">{% trans "All threads" %}</a>
33 33 {% if tags_str %}
34 <a href="{% url 'index' %}?filter=fav_tags" title="{% trans "interesting" %}">β˜…</a>,
34 <form action="{% url 'index' %}" method="post" class="post-button-form">{% csrf_token %}
35 {% if only_favorites %}
36 <button name="method" value="toggle_fav" class="fav">β˜…</button>,
37 {% else %}
38 <button name="method" value="toggle_fav" class="not_fav">β˜…</button>,
39 {% endif %}
40 </form>
35 41 {{ tags_str|safe }},
36 42 {% else %}
37 43 {% trans 'Add tags' %} β†’
38 44 {% endif %}
39 45 <a href="{% url 'tags' 'required'%}" title="{% trans 'Tag management' %}">{% trans "tags" %}</a>,
40 46 {% if search_enabled %}
41 47 <a href="{% url 'search' %}" title="{% trans 'Search' %}">{% trans 'search' %}</a>,
42 48 {% endif %}
43 49 <a href="{% url 'feed' %}" title="{% trans 'Feed' %}">{% trans 'feed' %}</a>,
44 50 <a href="{% url 'random' %}" title="{% trans 'Random images' %}">{% trans 'images' %}</a>{% if has_fav_threads %},
45 51
46 52 <a href="{% url 'feed' %}?favorites" id="fav-panel-btn">{% trans 'favorites' %} <span id="new-fav-post-count" {% if not new_post_count %}style="display: none" {% endif %}>{{ new_post_count }}</span></a>
47 53 {% endif %}
48 54
49 55 {% if usernames %}
50 56 <a class="right-link link" href="{% url 'notifications' %}" title="{% trans 'Notifications' %}">
51 57 {% trans 'Notifications' %}
52 58 {% ifnotequal new_notifications_count 0 %}
53 59 (<b>{{ new_notifications_count }}</b>)
54 60 {% endifnotequal %}
55 61 </a>
56 62 {% endif %}
57 63
58 64 <a class="right-link link" href="{% url 'settings' %}">{% trans 'Settings' %}</a>
59 65 </header>
60 66
61 67 <div id="fav-panel"><div class="post">{% trans "Loading..." %}</div></div>
62 68
63 69 {% block content %}{% endblock %}
64 70
65 71 <script src="{% static 'js/3party/jquery-ui.min.js' %}"></script>
66 72 <script src="{% static 'js/jquery.mousewheel.js' %}"></script>
67 73 <script src="{% static 'js/3party/highlight.min.js' %}"></script>
68 74
69 75 <script src="{% url 'js_info_dict' %}"></script>
70 76
71 77 <script src="{% static 'js/popup.js' %}"></script>
72 78 <script src="{% static 'js/image.js' %}"></script>
73 79 <script src="{% static 'js/refpopup.js' %}"></script>
74 80 <script src="{% static 'js/main.js' %}"></script>
75 81
76 82 <footer class="navigation_panel">
77 83 {% block metapanel %}{% endblock %}
78 84 {% if rss_url %}
79 85 [<a href="{{ rss_url }}">RSS</a>]
80 86 {% endif %}
81 87 [<a href="{% url 'admin:index' %}">{% trans 'Admin' %}</a>]
82 88 [<a href="{% url 'index' %}?order=pub">{% trans 'New threads' %}</a>]
83 89 {% with ppd=posts_per_day|floatformat:2 %}
84 90 {% blocktrans %}Speed: {{ ppd }} posts per day{% endblocktrans %}
85 91 {% endwith %}
86 92 <a class="link" href="#top" id="up">{% trans 'Up' %}</a>
87 93 </footer>
88 94
89 95 </body>
90 96 </html>
@@ -1,172 +1,184 b''
1 1 from django.core.urlresolvers import reverse
2 2 from django.core.files import File
3 3 from django.core.files.temp import NamedTemporaryFile
4 4 from django.core.paginator import EmptyPage
5 5 from django.db import transaction
6 6 from django.http import Http404
7 7 from django.shortcuts import render, redirect
8 8 from django.utils.decorators import method_decorator
9 9 from django.views.decorators.csrf import csrf_protect
10 10
11 11 from boards import utils, settings
12 12 from boards.abstracts.paginator import get_paginator
13 from boards.abstracts.settingsmanager import get_settings_manager
13 from boards.abstracts.settingsmanager import get_settings_manager,\
14 SETTING_ONLY_FAVORITES
14 15 from boards.forms import ThreadForm, PlainErrorList
15 16 from boards.models import Post, Thread, Ban
16 17 from boards.views.banned import BannedView
17 18 from boards.views.base import BaseBoardView, CONTEXT_FORM
18 19 from boards.views.posting_mixin import PostMixin
19 from boards.views.mixins import FileUploadMixin, PaginatedMixin
20 from boards.views.mixins import FileUploadMixin, PaginatedMixin,\
21 DispatcherMixin, PARAMETER_METHOD
20 22
21 23 FORM_TAGS = 'tags'
22 24 FORM_TEXT = 'text'
23 25 FORM_TITLE = 'title'
24 26 FORM_IMAGE = 'image'
25 27 FORM_THREADS = 'threads'
26 28
27 29 TAG_DELIMITER = ' '
28 30
29 31 PARAMETER_CURRENT_PAGE = 'current_page'
30 32 PARAMETER_PAGINATOR = 'paginator'
31 33 PARAMETER_THREADS = 'threads'
32 34 PARAMETER_ADDITIONAL = 'additional_params'
33 35 PARAMETER_MAX_FILE_SIZE = 'max_file_size'
34 36 PARAMETER_RSS_URL = 'rss_url'
35 37
36 38 TEMPLATE = 'boards/all_threads.html'
37 39 DEFAULT_PAGE = 1
38 40
39 41
40 class AllThreadsView(PostMixin, FileUploadMixin, BaseBoardView, PaginatedMixin):
42 class AllThreadsView(PostMixin, FileUploadMixin, BaseBoardView, PaginatedMixin, DispatcherMixin):
41 43
42 44 def __init__(self):
43 45 self.settings_manager = None
44 46 super(AllThreadsView, self).__init__()
45 47
46 48 @method_decorator(csrf_protect)
47 49 def get(self, request, form: ThreadForm=None):
48 50 page = request.GET.get('page', DEFAULT_PAGE)
49 51
50 52 params = self.get_context_data(request=request)
51 53
52 54 if not form:
53 55 form = ThreadForm(error_class=PlainErrorList)
54 56
55 57 self.settings_manager = get_settings_manager(request)
56 58
57 59 threads = self.get_threads()
58 60
59 61 order = request.GET.get('order', 'bump')
60 62 if order == 'bump':
61 63 threads = threads.order_by('-bump_time')
62 64 else:
63 65 threads = threads.filter(multi_replies__opening=True).order_by('-multi_replies__pub_time')
64 66 filter = request.GET.get('filter')
65 if filter == 'fav_tags':
67 if self.settings_manager.get_setting(SETTING_ONLY_FAVORITES):
66 68 fav_tags = self.settings_manager.get_fav_tags()
67 69 if len(fav_tags) > 0:
68 70 threads = threads.filter(tags__in=fav_tags)
69 71 threads = threads.distinct()
70 72
71 73 paginator = get_paginator(threads,
72 74 settings.get_int('View', 'ThreadsPerPage'))
73 75 paginator.current_page = int(page)
74 76
75 77 try:
76 78 threads = paginator.page(page).object_list
77 79 except EmptyPage:
78 80 raise Http404()
79 81
80 82 params[PARAMETER_THREADS] = threads
81 83 params[CONTEXT_FORM] = form
82 84 params[PARAMETER_MAX_FILE_SIZE] = self.get_max_upload_size()
83 85 params[PARAMETER_RSS_URL] = self.get_rss_url()
84 86
85 87 paginator.set_url(self.get_reverse_url(), request.GET.dict())
86 88 self.get_page_context(paginator, params, page)
87 89
88 90 return render(request, TEMPLATE, params)
89 91
90 92 @method_decorator(csrf_protect)
91 93 def post(self, request):
94 if PARAMETER_METHOD in request.POST:
95 self.dispatch_method(request)
96
97 return redirect('index') # FIXME Different for different modes
98
92 99 form = ThreadForm(request.POST, request.FILES,
93 100 error_class=PlainErrorList)
94 101 form.session = request.session
95 102
96 103 if form.is_valid():
97 104 return self.create_thread(request, form)
98 105 if form.need_to_ban:
99 106 # Ban user because he is suspected to be a bot
100 107 self._ban_current_user(request)
101 108
102 109 return self.get(request, form)
103 110
104 111 def get_page_context(self, paginator, params, page):
105 112 """
106 113 Get pagination context variables
107 114 """
108 115
109 116 params[PARAMETER_PAGINATOR] = paginator
110 117 current_page = paginator.page(int(page))
111 118 params[PARAMETER_CURRENT_PAGE] = current_page
112 119 self.set_page_urls(paginator, params)
113 120
114 121 def get_reverse_url(self):
115 122 return reverse('index')
116 123
117 124 @transaction.atomic
118 125 def create_thread(self, request, form: ThreadForm, html_response=True):
119 126 """
120 127 Creates a new thread with an opening post.
121 128 """
122 129
123 130 ip = utils.get_client_ip(request)
124 131 is_banned = Ban.objects.filter(ip=ip).exists()
125 132
126 133 if is_banned:
127 134 if html_response:
128 135 return redirect(BannedView().as_view())
129 136 else:
130 137 return
131 138
132 139 data = form.cleaned_data
133 140
134 141 title = form.get_title()
135 142 text = data[FORM_TEXT]
136 143 file = form.get_file()
137 144 file_url = form.get_file_url()
138 145 threads = data[FORM_THREADS]
139 146 images = form.get_images()
140 147
141 148 text = self._remove_invalid_links(text)
142 149
143 150 tags = data[FORM_TAGS]
144 151 monochrome = form.is_monochrome()
145 152
146 153 post = Post.objects.create_post(title=title, text=text, file=file,
147 154 ip=ip, tags=tags, opening_posts=threads,
148 155 tripcode=form.get_tripcode(),
149 156 monochrome=monochrome, images=images,
150 157 file_url = file_url)
151 158
152 159 # This is required to update the threads to which posts we have replied
153 160 # when creating this one
154 161 post.notify_clients()
155 162
156 163 if form.is_subscribe():
157 164 settings_manager = get_settings_manager(request)
158 165 settings_manager.add_or_read_fav_thread(post)
159 166
160 167 if html_response:
161 168 return redirect(post.get_absolute_url())
162 169
163 170 def get_threads(self):
164 171 """
165 172 Gets list of threads that will be shown on a page.
166 173 """
167 174
168 175 return Thread.objects\
169 176 .exclude(tags__in=self.settings_manager.get_hidden_tags())
170 177
171 178 def get_rss_url(self):
172 179 return self.get_reverse_url() + 'rss/'
180
181 def toggle_fav(self, request):
182 settings_manager = get_settings_manager(request)
183 settings_manager.set_setting(SETTING_ONLY_FAVORITES,
184 not settings_manager.get_setting(SETTING_ONLY_FAVORITES, False))
General Comments 0
You need to be logged in to leave comments. Login now