Skip to content

Commit

Permalink
Merge pull request #851 from Natay/master
Browse files Browse the repository at this point in the history
user merging added #847
  • Loading branch information
ialbert authored May 31, 2021
2 parents 2a8174f + 2ca7f04 commit 0f34f25
Show file tree
Hide file tree
Showing 13 changed files with 203 additions and 15 deletions.
6 changes: 6 additions & 0 deletions biostar/accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,12 @@ def low_rep(self):
"""
return self.score <= settings.LOW_REP_THRESHOLD and not self.is_moderator

@property
def high_rep(self):
"""
"""

return not self.low_rep

class UserLog(models.Model):
DEFAULT, ACTION = 1, 2
Expand Down
34 changes: 29 additions & 5 deletions 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 @@ -213,13 +213,13 @@ def create_post(author, title, content, request, root=None, parent=None, ptype=P
post = Post.objects.filter(content=content, author=author).order_by('-creation_date').first()

# How many seconds since the last post should we disallow duplicates.
since = 60
time_frame = 60
if post:
# Check to see if this post was made within the last minute.
# Check to see if this post was made within given timeframe
delta_secs = (util.now() - post.creation_date).seconds
if delta_secs < since:
if delta_secs < time_frame:
messages.warning(request, "Post with this content was created recently.")
return post.root
return post

post = Post.objects.create(title=title, content=content, root=root, parent=parent,
type=ptype, tag_val=tag_val, author=author)
Expand All @@ -228,6 +228,30 @@ 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 messages
Message.objects.filter(sender=alias).update(sender=main)
Message.objects.filter(recipient=alias).update(recipient=main)

# Do not delete older accounts.
older = (alias.profile.date_joined < main.profile.date_joined)

if alias.profile.is_moderator or alias.profile.high_rep or older:
return

alias.delete()

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 __init__(self, user=None, *args, **kwargs):
self.user = user
super().__init__(*args, **kwargs)

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

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

if self.user and not (self.user.is_staff or self.user.is_superuser):
raise forms.ValidationError(f'Only staff member can perform this action.')

if not to_delete:
raise forms.ValidationError(f'{alias} email does not exist.')

if not merge_to:
raise forms.ValidationError(f'{main} email does not exist.')

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
1 change: 0 additions & 1 deletion biostar/forum/templates/accounts/edit_profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
{% endblock %}

{% block js %}

<script src="{% static 'semantic.min.js' %}"></script>
<script src="{% static 'prism.js' %}"></script>
<script src="{% static 'setup.js' %}{% randparam %}"></script>
Expand Down
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="trash can icon"></i> Delete Profile</label>
{{ form.alias }}
<div class="muted">
Email to delete
</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 merge into
</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 %}
2 changes: 1 addition & 1 deletion biostar/forum/templates/accounts/profile_tab.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
{% if request.user.is_superuser or request.user.is_staff %}
<div class="ui mini button blue disable-emails">
<i class="mail icon"></i>
Disable Messages
Silence
</div>
{% endif %}

Expand Down
7 changes: 4 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,11 @@
<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>


</div>

<div class="ui labeled icon top attached evenly divided menu" id="menu-header" user-id="{{ request.user.id }}">
Expand All @@ -53,12 +54,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
23 changes: 22 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,27 @@ def view_logs(request):
return render(request, "view_logs.html", context=context)


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

user = request.user
form = forms.MergeProfiles(user=user)

if request.method == 'POST':
form = forms.MergeProfiles(user=user, 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 0f34f25

Please sign in to comment.