Skip to content

Commit

Permalink
profile merging interface added
Browse files Browse the repository at this point in the history
  • Loading branch information
Natay committed May 25, 2021
1 parent f1160d1 commit 655a05f
Show file tree
Hide file tree
Showing 10 changed files with 202 additions and 9 deletions.
31 changes: 30 additions & 1 deletion biostar/forum/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from biostar.utils.helpers import get_ip
from . import util, awards
from .const import *
from .models import Post, Vote, Subscription, Badge, delete_post_cache, Log
from .models import Post, Vote, Subscription, Badge, delete_post_cache, Log, Award

User = get_user_model()

Expand Down Expand Up @@ -228,6 +228,35 @@ def create_post(author, title, content, request, root=None, parent=None, ptype=P
return post


def merge_profiles(main, alias):
"""
Merge alias profile into main
"""

# Transfer posts
Post.objects.filter(author=alias).update(author=main)

Post.objects.filter(lastedit_user=alias).update(lastedit_user=main)

# Transfer votes
Vote.objects.filter(author=alias).update(author=main)

# Transfer subscriptions
Subscription.objects.filter(user=alias).update(user=main)

# Transfer awards
Award.objects.filter(user=alias).update(user=main)

# Transfer messages
Message.objects.filter(sender=alias).update(sender=main)
Message.objects.filter(recipient=alias).update(recipient=main)

# Remove alias profile.
#Profile.objects.filter(user=alias).update(state=Profile.SUSPENDED)

return


def create_subscription(post, user, sub_type=None, update=False):
"""
Creates subscription to a post. Returns a list of subscriptions.
Expand Down
45 changes: 45 additions & 0 deletions biostar/forum/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,48 @@ def clean(self):
if self.user.is_anonymous:
raise forms.ValidationError("You need to be logged in.")
return cleaned_data


class MergeProfiles(forms.Form):

main = forms.CharField(label='Main user email', max_length=100, required=True)
alias = forms.CharField(label='Alias email to merge to main', max_length=100, required=True)

def clean_main(self):
cleaned_data = super(MergeProfiles, self).clean()
main = cleaned_data['main']
if not User.objects.filter(email=main).first():
raise forms.ValidationError(f'{main} email does not exist.')

return main

def clean_alias(self):
cleaned_data = super(MergeProfiles, self).clean()
alias = cleaned_data['alias']
if not User.objects.filter(email=alias).first():
raise forms.ValidationError(f'{alias} email does not exist.')

return alias

def clean(self):
cleaned_data = super(MergeProfiles, self).clean()
alias = cleaned_data['alias']
main = cleaned_data['main']

if main == alias:
raise forms.ValidationError('Main and alias profiles are the same.')

return cleaned_data

def save(self):

alias = self.cleaned_data['alias']
main = self.cleaned_data['main']

main = User.objects.filter(email=main).first()
alias = User.objects.filter(email=alias).first()

# Merge the two accounts.
auth.merge_profiles(main=main, alias=alias)

return main
64 changes: 64 additions & 0 deletions biostar/forum/templates/accounts/merge_profile.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{% extends "forum_base.html" %}
{% load forum_tags %}
{% load socialaccount %}
{% block headtitle %}Login{% endblock %}

{% block content %}
<form class="ui form" method="post" action="{% url 'merge_profile' %}">

<div class="ui segment inputcolor socialbox">

<div class="ui center aligned header">
Merge Profiles
</div>

{% csrf_token %}

{{ form.errors }}

<div class="fields">

<div class="field">

<label><i class="user outline icon"></i> Alias Profile</label>
{{ form.alias }}
<div class="muted">
Email to the alias profile
</div>

</div>
<div class="field" style="margin: auto">
<i class="angle double right icon"></i>
</div>

<div class="field">

<label><i class="user icon"></i> Main Profile</label>
{{ form.main }}
<div class="muted">
Email to main profile
</div>

</div>


</div>

<div class="field">
<button class="ui primary button" type="submit">
<i class="user plus icon"></i>Merge
</button>

<a class="ui right floated button" href="#" onclick="window.history.back()">
<i class="chevron left icon"></i>Back
</a>
</div>

</div>


</form>



{% endblock %}
13 changes: 10 additions & 3 deletions biostar/forum/templates/banners/menu-header.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,17 @@
<i class="info circle icon"></i>About
</a>

<a class="item {% activate tab "faq" %}" href="/info/faq/">
<a class="item {% activate tab "faq" %}" href="/info/faq/">
<i class="info recycle icon"></i>FAQ
</a>

{% if request.user.is_staff or request.user.is_superuser %}
<a class="item {% activate tab "merge" %}" href="{% url 'merge_profile' %}">
<i class="user plus icon"></i>Merge
</a>

{% endif %}

</div>

<div class="ui labeled icon top attached evenly divided menu" id="menu-header" user-id="{{ request.user.id }}">
Expand All @@ -53,12 +60,12 @@
</a>

<a class="item {% activate tab "planet" %} " href="{% url 'blog_list' %}">
<i class="rss icon"></i> Planet {% count_badge counts.planet_count %}
<i class="rss icon"></i> Planet {% count_badge counts.planet_count %}
</a>

{% if request.user.profile.is_moderator %}
<a class="item {% activate tab 'spam' %} " href="{% url 'post_topic' 'spam' %}">
<i class="bug icon"></i> Spam {% count_badge counts.spam_count %}
<i class="bug icon"></i> Spam {% count_badge counts.spam_count %}
</a>
<a class="item {% activate tab 'activity' %} " href="{% url 'view_logs' %}">
<i class="chess queen icon"></i> Mods {% count_badge counts.mod_count %}
Expand Down
15 changes: 14 additions & 1 deletion biostar/forum/tests/test_moderate.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ def setUp(self):
logger.setLevel(logging.WARNING)
self.owner = User.objects.create(username=f"tested{get_uuid(10)}", email="[email protected]",
password="tested", is_superuser=True, is_staff=True)

self.user2 = User.objects.create(username=f"test{get_uuid(10)}", email="[email protected]",
password="test", is_superuser=True, is_staff=True)
# Create an existing tested post
self.post = models.Post.objects.create(title="Test", author=self.owner, content="Test", type=models.Post.QUESTION, uid='foo')
self.uid = 'foo'
Expand Down Expand Up @@ -78,6 +79,18 @@ def test_comment_moderation(self):

self.moderate(choices=choices, post=comment, extra={'pid': self.post.uid})

def test_merge_profile(self):
"Test merging two profiles"

# Create fake request
data = {'main': self.owner.email, 'alias': self.user2.email}

request = fake_request(url=reverse('merge_profile'), data=data, user=self.owner)
response = views.merge_profile(request=request)

self.process_response(response)

pass

def process_response(self, response):
"Check the response on POST request is redirected"
Expand Down
4 changes: 2 additions & 2 deletions biostar/forum/tests/test_navigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class ForumNavigation(TestCase):
def setUp(self):
logger.setLevel(logging.WARNING)

self.owner = User.objects.create(username=f"tested{get_uuid(10)}", email="[email protected]")
self.owner = User.objects.create(username=f"tested{get_uuid(10)}", email="[email protected]", is_staff=True)
self.owner.set_password("tested")
self.badge = Badge.objects.first()
# Create a tested post
Expand Down Expand Up @@ -60,7 +60,7 @@ def test_public_pages(self):
reverse("myvotes"),
reverse('api_traffic'),
reverse('latest_feed'),
reverse('latest_feed'),
reverse('merge_profile'),
reverse('post_tags', kwargs=dict(tag='tag1')),
reverse('tag_feed', kwargs=dict(text='tag1')),
reverse('post_feed', kwargs=dict(text=self.post.uid)),
Expand Down
1 change: 1 addition & 0 deletions biostar/forum/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
path(r'feeds/post/<str:text>/', feed.PostFeed(), name='post_feed' ),
path(r'feeds/type/<str:text>/', feed.PostTypeFeed(), name='post_type'),
#path(r'^feeds/planet/$', feed.PlanetFeed(), name='planet-feed'),
path(r'merge/', views.merge_profile, name="merge_profile"),

]

Expand Down
22 changes: 21 additions & 1 deletion biostar/forum/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from biostar.forum import forms, auth, tasks, util, search, models, moderate
from biostar.forum.const import *
from biostar.forum.models import Post, Vote, Badge, Subscription, Log
from biostar.utils.decorators import is_moderator, check_params, reset_count
from biostar.utils.decorators import is_moderator, check_params, reset_count, is_staff

User = get_user_model()

Expand Down Expand Up @@ -627,6 +627,26 @@ def view_logs(request):
return render(request, "view_logs.html", context=context)


@is_staff
def merge_profile(request):
"""
Merge two profiles into one.
"""

form = forms.MergeProfiles()

if request.method == 'POST':
form = forms.MergeProfiles(data=request.POST)

if form.is_valid():
merged = form.save()
messages.success(request, "Merged profiles")
return redirect(reverse('user_profile', kwargs=dict(uid=merged.profile.uid)))

context = dict(form=form)
return render(request, "accounts/merge_profile.html", context=context)


def error(request):
"""
Checking error propagation and logging
Expand Down
2 changes: 1 addition & 1 deletion biostar/server/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# Do not multi-thread tests.

TASK_RUNNER = "disable"
TASK_RUNNER = "block"

INIT_PLANET = False

Expand Down
14 changes: 14 additions & 0 deletions biostar/utils/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@ def inner(request, **kwargs):
return inner


def is_staff(f):
"""
Only run functions with the
"""
def inner(request, **kwargs):
user = request.user
if user.is_authenticated and (user.is_staff or user.is_superuser):
return f(request, **kwargs)
messages.warning(request, "You need to be a staff member to perform this action.")
return redirect('/')

return inner


def reset_count(key):
"""
Set value of given key in settings.SESSION_COUNT_KEY to 0.
Expand Down

0 comments on commit 655a05f

Please sign in to comment.