from django.http import HttpResponse, Http404, HttpResponseRedirect from django.db import IntegrityError from django.conf import settings as django_settings from django.shortcuts import render, redirect from django.urls import reverse from django.views.decorators.cache import never_cache, cache_page from django.urls import reverse from django.core.files.uploadhandler import TemporaryFileUploadHandler from django.utils.translation import gettext as _ from brutaldon.forms import LoginForm, OAuthLoginForm, PreferencesForm, PostForm, FilterForm from brutaldon.models import Client, Account, Preference, Theme from mastodon import Mastodon, AttribAccessDict, MastodonError, MastodonAPIError, MastodonNotFoundError from urllib import parse from pdb import set_trace from inscriptis import get_text from time import sleep import re class NotLoggedInException(Exception): pass ### ### Utility functions ### def get_usercontext(request): if is_logged_in(request): try: client = Client.objects.get(api_base_id=request.session['active_instance']) user = Account.objects.get(username=request.session['active_username']) except (Client.DoesNotExist, Client.MultipleObjectsReturned, Account.DoesNotExist, Account.MultipleObjectsReturned): raise NotLoggedInException() mastodon = Mastodon( client_id = client.client_id, client_secret = client.client_secret, access_token = user.access_token, api_base_url = client.api_base_id, ratelimit_method="throw") return user, mastodon else: return None, None def is_logged_in(request): return request.session.has_key('active_user') def _notes_count(account, mastodon): if not mastodon: return "" notes = mastodon.notifications(limit=40) if account.preferences.filter_notifications: notes = [ note for note in notes if note.type == 'mention' or note.type == 'follow'] for index, item in enumerate(notes): if account.note_seen is None: account.note_seen = "0" account.save() if str(item.id) <= str(account.note_seen): break else: index = "40+" return str(index) def br_login_required(function=None, home_url=None, redirect_field_name=None): """Check that the user is logged in to a Mastodon instance. This decorator ensures that the view functions it is called on can be accessed only by logged in users. When an instanceless user accesses such a protected view, they are redirected to the address specified in the field named in `next_field` or, lacking such a value, the URL in `home_url`, or the `ANONYMOUS_HOME_URL` setting. """ if home_url is None: home_url = django_settings.ANONYMOUS_HOME_URL def _dec(view_func): def _view(request, *args, **kwargs): if not is_logged_in(request): url = None if redirect_field_name and redirect_field_name in request.REQUEST: url = request.REQUEST[redirect_field_name] if not url: url = home_url if not url: url = "/" return HttpResponseRedirect(url) else: return view_func(request, *args, **kwargs) _view.__name__ = view_func.__name__ _view.__dict__ = view_func.__dict__ _view.__doc__ = view_func.__doc__ return _view if function is None: return _dec else: return _dec(function) def user_search(request): check = request.POST.get("status", "").split() if len(check): check = check[-1] if len(check) > 1 and check.startswith('@'): check = check[1:] return user_search_inner(request, check) else: check = " " else: check = " " return HttpResponse(check) def user_search_inner(request, query): account, mastodon = get_usercontext(request) results = mastodon.search(query) return render(request, 'intercooler/users.html', {'active_users': "\n".join([ user.acct for user in results.accounts ]), 'preferences': account.preferences }) def timeline(request, timeline='home', timeline_name='Home', max_id=None, since_id=None, filter_context='home'): account, mastodon = get_usercontext(request) data = mastodon.timeline(timeline, limit=40, max_id=max_id, since_id=since_id) form = PostForm(initial={'visibility': request.session['active_user'].source.privacy}) try: prev = data[0]._pagination_prev if len(mastodon.timeline(since_id=prev['since_id'])) == 0: prev = None else: prev['since_id'] = data[0].id except (IndexError, AttributeError): prev = None try: next = data[-1]._pagination_next next['max_id'] = data[-1].id except (IndexError, AttributeError): next = None notifications = _notes_count(account, mastodon) filters = get_filters(mastodon, filter_context) # This filtering has to be done *after* getting next/prev links if account.preferences.filter_replies: data = [x for x in data if not x.in_reply_to_id] if account.preferences.filter_boosts: data = [x for x in data if not x.reblog] # Apply filters data = [x for x in data if not toot_matches_filters(x, filters)] return render(request, 'main/%s_timeline.html' % timeline, {'toots': data, 'form': form, 'timeline': timeline, 'timeline_name': timeline_name, 'own_acct': request.session['active_user'], 'preferences': account.preferences, 'notifications': notifications, 'prev': prev, 'next': next}) def get_filters(mastodon, context=None): try: if context: return [ff for ff in mastodon.filters() if context in ff.context] else: return mastodon.filters() except: return [] def toot_matches_filters(toot, filters=[]): if not filters: return False def maybe_rewrite_filter(filter): if filter.whole_word: return f"\\b{filter.phrase}\\b" else: return filter.phrase phrases = [maybe_rewrite_filter(x) for x in filters] pattern = "|".join(phrases) try: if toot.get('type') in ['reblog', 'favourite']: return re.search(pattern, toot.status.spoiler_text + toot.status.content, re.I) return re.search(pattern, toot.spoiler_text + toot.content, re.I) except: return False def switch_accounts(request, new_account): """Try to switch accounts to the specified account, if it is already in the user's session. Sets up new session variables. Returns boolean success code.""" accounts_dict = request.session.get("accounts_dict") if not accounts_dict or not new_account in accounts_dict.keys(): return False try: account = Account.objects.get(id=accounts_dict[new_account]['account_id']) if account.username != new_account: return False except Account.DoesNotExist: return False request.session['active_user'] = accounts_dict[new_account]['user'] request.session['active_username'] = account.username request.session['active_instance'] = account.client.api_base_id return True def forget_account(request, account_name): """Forget that you were logged into an account. If it's the last one, log out entirely. Sets up session variables. Returns a redirect to the correct view. """ accounts_dict = request.session.get("accounts_dict") if not accounts_dict or not account_name in accounts_dict.keys(): return redirect("accounts") del accounts_dict[account_name] if len(accounts_dict) == 0: request.session.flush() return redirect("about") elif account_name == request.session["active_username"]: key = [*accounts_dict][0] if switch_accounts(request, key): return redirect("accounts") else: request.session.flush() return redirect("about") else: request.session["accounts_dict"] = accounts_dict return redirect("accounts") ### ### View functions ### def notes_count(request): account, mastodon = get_usercontext(request) count = _notes_count(account, mastodon) return render(request, 'intercooler/notes.html', {'notifications': count, 'preferences': account.preferences }) @br_login_required def home(request, next=None, prev=None): return timeline(request, 'home', 'Home', max_id=next, since_id=prev, filter_context='home') @br_login_required def local(request, next=None, prev=None, filter_context='public'): return timeline(request, 'local', 'Local', max_id=next, since_id=prev) @br_login_required def fed(request, next=None, prev=None, filter_context='public'): return timeline(request, 'public', 'Federated', max_id=next, since_id=prev) @br_login_required def tag(request, tag): try: account, mastodon = get_usercontext(request) except NotLoggedInException: return redirect(login) data = mastodon.timeline_hashtag(tag) notifications = _notes_count(account, mastodon) return render(request, 'main/timeline.html', {'toots': data, 'timeline_name': '#'+tag, 'own_acct': request.session['active_user'], 'notifications': notifications, 'preferences': account.preferences}) @never_cache def login(request): # User posts instance name in form. # POST page redirects user to instance, where they log in. # Instance redirects user to oauth_after_login view. # oauth_after_login view saves credential in session, then redirects to home. if request.method == "GET": form = OAuthLoginForm() return render(request, 'setup/login-oauth.html', {'form': form}) elif request.method == "POST": form = OAuthLoginForm(request.POST) redirect_uris = request.build_absolute_uri(reverse('oauth_callback')) if form.is_valid(): api_base_url = form.cleaned_data['instance'] tmp_base = parse.urlparse(api_base_url.lower()) if tmp_base.netloc == '': api_base_url = parse.urlunparse(('https', tmp_base.path, '','','','')) request.session['active_instance_hostname'] = tmp_base.path else: api_base_url = api_base_url.lower() request.session['active_instance_hostname'] = tmp_base.netloc request.session['active_instance'] = api_base_url try: client = Client.objects.get(api_base_id=api_base_url) except (Client.DoesNotExist, Client.MultipleObjectsReturned): (client_id, client_secret) = Mastodon.create_app('brutaldon', api_base_url=api_base_url, redirect_uris=redirect_uris, scopes=['read', 'write', 'follow']) client = Client( api_base_id = api_base_url, client_id=client_id, client_secret = client_secret) client.save() request.session['active_client_id'] = client.client_id request.session['active_client_secret'] = client.client_secret mastodon = Mastodon( client_id = client.client_id, client_secret = client.client_secret, api_base_url = api_base_url) return redirect(mastodon.auth_request_url(redirect_uris=redirect_uris, scopes=['read', 'write', 'follow'])) else: return render(request, 'setup/login.html', {'form': form}) else: return redirect(login) @never_cache def oauth_callback(request): code = request.GET.get('code', '') mastodon = Mastodon(client_id=request.session['active_client_id'], client_secret=request.session['active_client_secret'], api_base_url=request.session['active_instance']) redirect_uri = request.build_absolute_uri(reverse('oauth_callback')) access_token = mastodon.log_in(code=code, redirect_uri=redirect_uri, scopes=['read', 'write', 'follow']) request.session['access_token'] = access_token user = mastodon.account_verify_credentials() try: account = Account.objects.get(username=user.username + '@' + request.session['active_instance_hostname']) account.access_token = access_token if not account.preferences: preferences = Preference(theme = Theme.objects.get(id=1)) preferences.save() account.preferences = preferences else: request.session['timezone'] = account.preferences.timezone account.save() except (Account.DoesNotExist, Account.MultipleObjectsReturned): preferences = Preference(theme = Theme.objects.get(id=1)) preferences.save() account = Account(username=user.username + '@' + request.session['active_instance_hostname'], access_token = access_token, client = Client.objects.get(api_base_id=request.session['active_instance']), preferences = preferences) account.save() request.session['active_user'] = user request.session['active_username'] = user.username + '@' + request.session['active_instance_hostname'] accounts_dict = request.session.get('accounts_dict') if not accounts_dict: accounts_dict = {} accounts_dict[account.username] = { 'account_id': account.id, 'user': user } request.session['accounts_dict'] = accounts_dict return redirect(home) @never_cache def old_login(request): if request.method == "GET": form = LoginForm() return render(request, 'setup/login.html', {'form': form}) elif request.method == "POST": form = LoginForm(request.POST) if form.is_valid(): api_base_url = form.cleaned_data['instance'] tmp_base = parse.urlparse(api_base_url.lower()) if tmp_base.netloc == '': api_base_url = parse.urlunparse(('https', tmp_base.path, '','','','')) request.session['active_instance_hostname'] = tmp_base.path else: api_base_url = api_base_url.lower() request.session['active_instance_hostname'] = tmp_base.netloc request.session['active_instance'] = api_base_url email = form.cleaned_data['email'] password = form.cleaned_data['password'] try: client = Client.objects.get(api_base_id=api_base_url) except (Client.DoesNotExist, Client.MultipleObjectsReturned): (client_id, client_secret) = Mastodon.create_app('brutaldon', api_base_url=api_base_url, scopes=['read', 'write', 'follow']) client = Client( api_base_id = api_base_url, client_id=client_id, client_secret = client_secret) client.save() mastodon = Mastodon( client_id = client.client_id, client_secret = client.client_secret, api_base_url = api_base_url) try: account = Account.objects.get(email=email, client_id=client.id) except (Account.DoesNotExist, Account.MultipleObjectsReturned): preferences = Preference(theme = Theme.objects.get(id=1)) preferences.save() account = Account( email = email, access_token = "", client = client, preferences = preferences) try: access_token = mastodon.log_in(email, password, scopes=['read', 'write', 'follow']) account.access_token = access_token user = mastodon.account_verify_credentials() request.session['active_user'] = user request.session['active_username'] = user.username + '@' + request.session['active_instance_hostname'] account.username = request.session['active_username'] request.session['timezone'] = account.preferences.timezone; accounts_dict = request.session.get('accounts_dict') if not accounts_dict: accounts_dict = {} accounts_dict[account.username] = { 'account_id': account.id, 'user': user } request.session['accounts_dict'] = accounts_dict account.save() return redirect(home) except IntegrityError: account = Account.objects.get(username=account.username) accounts_dict[account.username] = { 'account_id': account.id, 'user': user } request.session['accounts_dict'] = accounts_dict return redirect(home) except Exception as ex: form.add_error('', ex) return render(request, 'setup/login.html', {'form': form}) else: return render(request, 'setup/login.html', {'form': form}) @never_cache def logout(request): request.session.flush() return redirect(about) def error(request): return render(request, 'error.html', { 'error': _("Not logged in yet.")}) @br_login_required def note(request, next=None, prev=None): try: account, mastodon = get_usercontext(request) except NotLoggedInException: return redirect(about) last_seen = mastodon.notifications(limit=1)[0] account.note_seen = last_seen.id account.save() notes = mastodon.notifications(limit=40, max_id=next, since_id=prev) filters = get_filters(mastodon, context='notifications') if account.preferences.filter_notifications: notes = [ note for note in notes if note.type == 'mention' or note.type == 'follow'] # Apply filters notes = [x for x in notes if not toot_matches_filters(x, filters)] try: prev = notes[0]._pagination_prev if len(mastodon.notifications(since_id=prev['since_id'])) == 0: prev = None except (IndexError, AttributeError): prev = None try: next = notes[-1]._pagination_next except (IndexError, AttributeError): next = None return render(request, 'main/notifications.html', {'notes': notes,'timeline': 'Notifications', 'timeline_name': 'Notifications', 'own_acct': request.session['active_user'], 'preferences': account.preferences, 'prev': prev, 'next': next}) @br_login_required def thread(request, id): account, mastodon = get_usercontext(request) try: context = mastodon.status_context(id) except MastodonNotFoundError: raise Http404(_("Thread not found; the message may have been deleted.")) toot = mastodon.status(id) notifications = _notes_count(account, mastodon) filters = get_filters(mastodon, context='thread') # Apply filters ancestors = [x for x in context.ancestors if not toot_matches_filters(x, filters)] descendants = [x for x in context.descendants if not toot_matches_filters(x, filters)] return render(request, 'main/thread.html', {'context': context, 'toot': toot, 'ancestors': ancestors, 'descendants': descendants, 'own_acct': request.session['active_user'], 'notifications': notifications, 'preferences': account.preferences}) @br_login_required def user(request, username, prev=None, next=None): try: account, mastodon = get_usercontext(request) except NotLoggedInException: return redirect(about) try: user_dict = [dict for dict in mastodon.account_search(username) if ((dict.acct == username) or (dict.acct == username.split('@')[0] and username.split('@')[1] == account.username.split('@')[1]))][0] except (IndexError, AttributeError): raise Http404(_("The user %s could not be found.") % username) data = mastodon.account_statuses(user_dict.id, max_id=next, since_id=prev) relationship = mastodon.account_relationships(user_dict.id)[0] notifications = _notes_count(account, mastodon) try: prev = data[0]._pagination_prev if len(mastodon.account_statuses(user_dict.id, since_id=prev['since_id'])) == 0: prev = None except (IndexError, AttributeError): prev = None try: next = data[-1]._pagination_next except (IndexError, AttributeError): next = None return render(request, 'main/user.html', {'toots': data, 'user': user_dict, 'relationship': relationship, 'own_acct': request.session['active_user'], 'preferences': account.preferences, 'notifications': notifications, 'prev': prev, 'next': next}) @never_cache @br_login_required def settings(request): try: account, mastodon = get_usercontext(request) except NotLoggedInException: return redirect(about) if request.method == 'POST': form = PreferencesForm(request.POST) if form.is_valid(): account.preferences.theme = form.cleaned_data['theme'] account.preferences.filter_replies = form.cleaned_data['filter_replies'] account.preferences.filter_boosts = form.cleaned_data['filter_boosts'] account.preferences.timezone = form.cleaned_data['timezone'] account.preferences.no_javascript = form.cleaned_data['no_javascript'] account.preferences.notifications = form.cleaned_data['notifications'] account.preferences.click_to_load = form.cleaned_data['click_to_load'] account.preferences.lightbox = form.cleaned_data['lightbox'] account.preferences.filter_notifications = form.cleaned_data['filter_notifications'] account.preferences.poll_frequency = form.cleaned_data['poll_frequency'] request.session['timezone'] = account.preferences.timezone account.preferences.save() account.save() # Update this here because it's a handy place to do it. user_info = mastodon.account_verify_credentials() request.session['active_user'] = user_info return redirect(home) else: return render(request, 'setup/settings.html', {'form' : form, 'account': account}) else: request.session['timezone'] = account.preferences.timezone form = PreferencesForm(instance=account.preferences) return render(request, 'setup/settings.html', { 'form': form, 'account': account, 'preferences': account.preferences}) @never_cache @br_login_required def toot(request, mention=None): account, mastodon = get_usercontext(request) if request.method == 'GET': if mention: if not mention.startswith('@'): mention = '@'+mention form = PostForm(initial={'visibility': request.session['active_user'].source.privacy, 'status': mention + ' ' }) else: form = PostForm(initial={'visibility': request.session['active_user'].source.privacy}) if request.GET.get('ic-request'): return render(request, 'intercooler/post.html', {'form': form, 'own_acct': request.session['active_user'], 'preferences': account.preferences}) else: return render(request, 'main/post.html', {'form': form, 'own_acct': request.session['active_user'], 'preferences': account.preferences}) elif request.method == 'POST': form = PostForm(request.POST, request.FILES) if form.is_valid(): # create media objects media_objects = [] for index in range(1,5): if 'media_file_'+str(index) in request.FILES: media_objects.append( mastodon.media_post(request.FILES['media_file_'+str(index)] .temporary_file_path(), description=request.POST.get('media_text_' +str(index), None))) if form.cleaned_data['visibility'] == '': form.cleaned_data['visibility'] = request.session['active_user'].source.privacy try: try: mastodon.status_post(status=form.cleaned_data['status'], visibility=form.cleaned_data['visibility'], spoiler_text=form.cleaned_data['spoiler_text'], media_ids=media_objects, content_type='text/markdown') except TypeError: mastodon.status_post(status=form.cleaned_data['status'], visibility=form.cleaned_data['visibility'], spoiler_text=form.cleaned_data['spoiler_text'], media_ids=media_objects) except MastodonAPIError as error: form.add_error("", "%s (%s used)" % (error.args[-1], len(form.cleaned_data['status']) + len(form.cleaned_data['spoiler_text']))) return render(request, 'main/post.html', {'form': form, 'own_acct': request.session['active_user'], 'preferences': account.preferences}) return redirect(home) else: return render(request, 'main/post.html', {'form': form, 'own_acct': request.session['active_user'], 'preferences': account.preferences}) else: return redirect(toot) @br_login_required def redraft(request, id): if request.method == 'GET': account, mastodon = get_usercontext(request) toot = mastodon.status(id) toot_content = get_text(toot.content) # convert to plain text # fix up white space toot_content = re.sub("(^\n)|(\n$)", '', re.sub("\n\n", "\n", toot_content)) # Fix up references for mention in toot.mentions: menchie_re = re.compile( r"\s?@" + mention.username + r"\s", re.I) toot_content = menchie_re.sub(" @" + mention.acct + " ", toot_content, count=1) form = PostForm({'status': toot_content.strip(), 'visibility': toot.visibility, 'spoiler_text': toot.spoiler_text, 'media_text_1': safe_get_attachment(toot, 0).description, 'media_text_2': safe_get_attachment(toot, 1).description, 'media_text_3': safe_get_attachment(toot, 2).description, 'media_text_4': safe_get_attachment(toot, 3).description, }) return render(request, 'main/redraft.html', {'toot': toot, 'form': form, 'redraft':True, 'own_acct': request.session['active_user'], 'preferences': account.preferences}) elif request.method == 'POST': form = PostForm(request.POST, request.FILES) account, mastodon = get_usercontext(request) toot = mastodon.status(id) if form.is_valid(): media_objects = [] for index in range(1,5): if 'media_file_'+str(index) in request.FILES: media_objects.append( mastodon.media_post(request.FILES['media_file_'+str(index)] .temporary_file_path(), description=request.POST.get('media_text_' +str(index), None))) if form.cleaned_data['visibility'] == '': form.cleaned_data['visibility'] = request.session['active_user'].source.privacy try: try: mastodon.status_post(status=form.cleaned_data['status'], visibility=form.cleaned_data['visibility'], spoiler_text=form.cleaned_data['spoiler_text'], media_ids=media_objects, in_reply_to_id=toot.in_reply_to_id, content_type='text/markdown') except TypeError: mastodon.status_post(status=form.cleaned_data['status'], visibility=form.cleaned_data['visibility'], spoiler_text=form.cleaned_data['spoiler_text'], media_ids=media_objects, in_reply_to_id=toot.in_reply_to_id) mastodon.status_delete(id) except MastodonAPIError as error: form.add_error("", "%s (%s used)" % (error.args[-1], len(form.cleaned_data['status']) + len(form.cleaned_data['spoiler_text']))) return render(request, 'main/redraft.html', {'toot': toot, 'form': form, 'redraft': True, 'own_acct': request.session['active_user'], 'preferences': account.preferences}) return redirect(home) else: return render(request, 'main/redraft.html', {'toot': toot, 'form': form, 'redraft': True, 'own_acct': request.session['active_user'], 'preferences': account.preferences}) else: return redirect(redraft, id) def safe_get_attachment(toot, index): """Get an attachment from a toot, without crashing if it isn't there.""" try: return toot.media_attachments[index] except IndexError: adict = AttribAccessDict() adict.id, adict.type, adict.description = "", "unknown", "" adict.url, adict.remote_url, adict.preview_url = '', '', '' adict.text_url = '' return adict @br_login_required def reply(request, id): if request.method == 'GET': account, mastodon = get_usercontext(request) toot = mastodon.status(id) context = mastodon.status_context(id) notifications = _notes_count(account, mastodon) if toot.account.acct != request.session['active_user'].acct: initial_text = '@' + toot.account.acct + " " else: initial_text = "" for mention in [x for x in toot.mentions if x.acct != request.session['active_user'].acct and x.acct != toot.account.acct]: initial_text +=('@' + mention.acct + " ") form = PostForm(initial={'status': initial_text, 'visibility': toot.visibility, 'spoiler_text': toot.spoiler_text}) return render(request, 'main/reply.html', {'context': context, 'toot': toot, 'form': form, 'reply':True, 'own_acct': request.session['active_user'], 'notifications': notifications, 'preferences': account.preferences}) elif request.method == 'POST': form = PostForm(request.POST, request.FILES) account, mastodon = get_usercontext(request) toot = mastodon.status(id) context = mastodon.status_context(id) notifications = _notes_count(account, mastodon) if form.is_valid(): # create media objects media_objects = [] for index in range(1,5): if 'media_file_'+str(index) in request.FILES: media_objects.append( mastodon.media_post(request.FILES['media_file_'+str(index)] .temporary_file_path(), description=request.POST.get('media_text_' +str(index), None))) try: try: mastodon.status_post(status=form.cleaned_data['status'], visibility=form.cleaned_data['visibility'], spoiler_text=form.cleaned_data['spoiler_text'], media_ids=media_objects, in_reply_to_id=id, content_type="text/markdown") except TypeError: mastodon.status_post(status=form.cleaned_data['status'], visibility=form.cleaned_data['visibility'], spoiler_text=form.cleaned_data['spoiler_text'], media_ids=media_objects, in_reply_to_id=id) except MastodonAPIError as error: form.add_error("", "%s (%s used)" % (error.args[-1], len(form.cleaned_data['status']) + len(form.cleaned_data['spoiler_text']))) return render(request, 'main/reply.html', {'context': context, 'toot': toot, 'form': form, 'reply': True, 'own_acct': request.session['active_user'], 'notifications': notifications, 'preferences': account.preferences}) return HttpResponseRedirect(reverse('thread', args=[id]) + "#toot-"+str(id)) else: return render(request, 'main/reply.html', {'context': context, 'toot': toot, 'form': form, 'reply': True, 'own_acct': request.session['active_user'], 'preferences': account.preferences}) else: return HttpResponseRedirect(reverse('reply', args=[id]) + "#toot-"+str(id)) @never_cache @br_login_required def fav(request, id): account, mastodon = get_usercontext(request) toot = mastodon.status(id) if request.method == 'POST': if not request.POST.get('cancel', None): if toot.favourited: mastodon.status_unfavourite(id) else: mastodon.status_favourite(id) if request.POST.get('ic-request'): toot['favourited'] = not toot['favourited'] return render(request, 'intercooler/fav.html', {"toot": toot, 'own_acct': request.session['active_user'], "preferences": account.preferences}) else: return HttpResponseRedirect(reverse('thread', args=[id]) + "#toot-"+str(id)) else: return render(request, 'main/fav.html', {"toot": toot, 'own_acct': request.session['active_user'], "confirm_page": True, 'preferences': account.preferences}) @never_cache @br_login_required def boost(request, id): account, mastodon = get_usercontext(request) toot = mastodon.status(id) if request.method == 'POST': if not request.POST.get('cancel', None): if toot.reblogged: mastodon.status_unreblog(id) else: mastodon.status_reblog(id) if request.POST.get('ic-request'): toot['reblogged'] = not toot['reblogged'] return render(request, 'intercooler/boost.html', {"toot": toot, 'own_acct': request.session['active_user'], "preferences": account.preferences}) else: return HttpResponseRedirect(reverse('thread', args=[id]) + "#toot-"+str(id)) else: return render(request, 'main/boost.html', {"toot": toot, 'own_acct': request.session['active_user'], 'confirm_page': True, "preferences": account.preferences}) @never_cache @br_login_required def delete(request, id): account, mastodon = get_usercontext(request) toot = mastodon.status(id) if request.method == 'POST' or request.method == 'DELETE': if toot.account.acct != request.session['active_user'].acct: return redirect('home') if not request.POST.get('cancel', None): mastodon.status_delete(id) if request.POST.get('ic-request'): return HttpResponse("") return redirect(home) else: return render(request, 'main/delete.html', {"toot": toot, 'own_acct': request.session['active_user'], 'confirm_page': True, "preferences": account.preferences}) @never_cache @br_login_required def follow(request, id): account, mastodon = get_usercontext(request) try: user_dict = mastodon.account(id) relationship = mastodon.account_relationships(user_dict.id)[0] except (IndexError, AttributeError): raise Http404("The user could not be found.") if request.method == 'POST': if not request.POST.get('cancel', None): if relationship.requested or relationship.following: mastodon.account_unfollow(id) else: mastodon.account_follow(id) if request.POST.get('ic-request'): sleep(1) # This is annoying, but the next call will return Requested instead of Following in some cases relationship = mastodon.account_relationships(user_dict.id)[0] return render(request, 'intercooler/follow.html', {"user": user_dict, "relationship": relationship, 'own_acct': request.session['active_user'], 'preferences': account.preferences}) else: return redirect(user, user_dict.acct) else: return render(request, 'main/follow.html', {"user": user_dict, "relationship": relationship, "confirm_page": True, 'own_acct': request.session['active_user'], 'preferences': account.preferences}) @never_cache @br_login_required def block(request, id): account, mastodon = get_usercontext(request) try: user_dict = mastodon.account(id) relationship = mastodon.account_relationships(user_dict.id)[0] except (IndexError, AttributeError): raise Http404("The user could not be found.") if request.method == 'POST': if not request.POST.get('cancel', None): if relationship.blocking: mastodon.account_unblock(id) else: mastodon.account_block(id) if request.POST.get('ic-request'): relationship['blocking'] = not relationship['blocking'] return render(request, 'intercooler/block.html', {"user": user_dict, "relationship": relationship, }) else: return redirect(user, user_dict.acct) else: return render(request, 'main/block.html', {"user": user_dict, "relationship": relationship, "confirm_page": True, 'own_acct': request.session['active_user'], 'preferences': account.preferences}) @never_cache @br_login_required def mute(request, id): account, mastodon = get_usercontext(request) try: user_dict = mastodon.account(id) relationship = mastodon.account_relationships(user_dict.id)[0] except (IndexError, AttributeError): raise Http404("The user could not be found.") if request.method == 'POST': if not request.POST.get('cancel', None): if relationship.muting: mastodon.account_unmute(id) else: mastodon.account_mute(id) if request.POST.get('ic-request'): relationship['muting'] = not relationship['muting'] return render(request, 'intercooler/mute.html', {"user": user_dict, "relationship": relationship, }) else: return redirect(user, user_dict.acct) else: return render(request, 'main/mute.html', {"user": user_dict, "relationship": relationship, "confirm_page": True, 'own_acct': request.session['active_user'], 'preferences': account.preferences}) @br_login_required def search(request): account, mastodon = get_usercontext(request) if request.GET.get('ic-request'): return render(request, 'intercooler/search.html', {"preferences": account.preferences, 'own_acct': request.session['active_user'], }) else: return render(request, 'main/search.html', {"preferences": account.preferences, 'own_acct': request.session['active_user'], }) @br_login_required @cache_page(60*5) def search_results(request): if request.method == 'GET': query = request.GET.get('q', '') elif request.method == 'POST': query = request.POST.get('q', '') else: query = '' account, mastodon = get_usercontext(request) results = mastodon.search(query) notifications = _notes_count(account, mastodon) return render(request, 'main/search_results.html', {"results": results, 'own_acct': request.session['active_user'], 'notifications': notifications, "preferences": account.preferences}) @cache_page(60*30) def about(request): version = django_settings.BRUTALDON_VERSION account, mastodon = get_usercontext(request) if account: preferences = account.preferences else: preferences = None return render(request, 'about.html', {"preferences": preferences, "version": version, 'own_acct': request.session.get('active_user', None), }) @cache_page(60*30) def privacy(request): account, mastodon = get_usercontext(request) if account: preferences = account.preferences else: preferences = None return render(request, 'privacy.html', {"preferences": preferences, 'own_acct' : request.session.get('active_user', None)}) @cache_page(60 * 30) @br_login_required def emoji_reference(request): account, mastodon = get_usercontext(request) emojos = mastodon.custom_emojis() notifications = _notes_count(account, mastodon) return render(request, 'main/emoji.html', {"preferences": account.preferences, "emojos": sorted(emojos, key=lambda x: x['shortcode']), "notifications": notifications, 'own_acct' : request.session['active_user']}) @br_login_required def list_filters(request): account, mastodon = get_usercontext(request) filters = mastodon.filters() return render(request, 'filters/list.html', {'account': account, 'preferences': account.preferences, 'filters': filters }) @br_login_required def create_filter(request): account, mastodon = get_usercontext(request) if request.method == 'POST': form = FilterForm(request.POST) if form.is_valid(): contexts = [] if form.cleaned_data['context_home']: contexts.append('home') if form.cleaned_data['context_public']: contexts.append('public') if form.cleaned_data['context_notes']: contexts.append('notifications') if form.cleaned_data['context_thread']: contexts.append('thread') expires = form.cleaned_data['expires_in'] if expires == "": expires = None mastodon.filter_create(form.cleaned_data['phrase'], contexts, whole_word=form.cleaned_data['whole_word'], expires_in=expires) return redirect(list_filters) else: return render(request, 'filters/create.html', { 'form': form, 'account': account, 'preferences': account.preferences}) else: form = FilterForm() return render(request, 'filters/create.html', { 'form': form, 'account': account, 'preferences': account.preferences}) @br_login_required def delete_filter(request, id): account, mastodon = get_usercontext(request) filter = mastodon.filter(id) if request.method == 'POST' or request.method == 'DELETE': if not request.POST.get('cancel', None): mastodon.filter_delete(filter.id) if request.POST.get("ic-request"): return HttpResponse("") return redirect(list_filters) else: return render(request, "filters/delete.html", {"filter": filter, "own_acct": request.session["active_user"], "confirm_page": True, "preferences": account.preferences}) @br_login_required def edit_filter(request, id): account, mastodon = get_usercontext(request) filter = mastodon.filter(id) contexts = [] if request.method == 'POST': form = FilterForm(request.POST) if form.is_valid(): if form.cleaned_data['context_home']: contexts.append('home') if form.cleaned_data['context_public']: contexts.append('public') if form.cleaned_data['context_notes']: contexts.append('notifications') if form.cleaned_data['context_thread']: contexts.append('thread') expires = form.cleaned_data['expires_in'] if expires == "": expires = None mastodon.filter_update(id, form.cleaned_data['phrase'], contexts, whole_word=form.cleaned_data['whole_word'], expires_in=expires) return redirect(list_filters) else: return render(request, 'filters/edit.html', { 'form': form, 'account': account, 'filter': filter, 'preferences': account.preferences}) else: contexts = [] form = FilterForm({'phrase': filter.phrase, 'context_home': "home" in filter.context, 'context_public': "public" in filter.context, 'context_notes': "notifications" in filter.context, 'context_thread': "thread" in filter.context, 'whole_word': filter.whole_word}) return render(request, 'filters/edit.html', { 'form': form, 'account': account, 'filter': filter, 'preferences': account.preferences}) @br_login_required def follow_requests(request, id=None): account, mastodon = get_usercontext(request) if request.method == 'GET': reqs = mastodon.follow_requests() return render(request, 'requests/list.html', {'account': account, 'preferences': account.preferences, 'requests': reqs}) elif id is None: return redirect(follow_requests) else: if request.POST.get("accept", None): mastodon.follow_request_authorize(id) elif request.POST.get("reject", None): mastodon.follow_request_reject(id) return redirect(follow_requests) @br_login_required def accounts(request, id=None): active_account, mastodon = get_usercontext(request) if request.method == 'GET': accounts = [x for x in request.session.get('accounts_dict').values()] return render(request, 'accounts/list.html', {'active_account': active_account, "own_acct": request.session["active_user"], 'accounts': accounts, 'preferences': active_account.preferences}) if request.method == 'POST': if request.POST.get('activate'): to_account = Account.objects.get(id=id).username if switch_accounts(request, to_account): return redirect(home) else: return redirect("accounts") elif request.POST.get('forget'): account = Account.objects.get(id=id).username return forget_account(request, account) else: accounts = [x for x in request.session.get('accounts_dict').values()] return render(request, 'accounts/list.html', {'active_account': active_account, "own_acct": request.session["active_user"], 'accounts': accounts, 'preferences': active_account.preferences})