Merge lp:~widelands-dev/widelands-website/delete_user into lp:widelands-website

Proposed by kaputtnik
Status: Merged
Merged at revision: 500
Proposed branch: lp:~widelands-dev/widelands-website/delete_user
Merge into: lp:widelands-website
Diff against target: 749 lines (+295/-143)
25 files modified
django_messages_wl/views.py (+1/-1)
media/css/comments.css (+1/-0)
news/feeds.py (+0/-22)
news/urls.py (+4/-4)
pybb/feeds.py (+1/-10)
settings.py (+8/-0)
templates/django_messages/view.html (+3/-2)
templates/mainpage.html (+1/-1)
templates/pybb/feeds/posts_description.html (+5/-1)
templates/pybb/feeds/topics_description.html (+5/-1)
templates/pybb/last_posts.html (+1/-1)
templates/threadedcomments/inlines/comments.html (+1/-1)
templates/wiki/feeds/history_description.html (+6/-1)
templates/wiki/recentchanges.html (+2/-1)
templates/wlprofile/delete_me.html (+42/-0)
templates/wlprofile/edit_profile.html (+7/-12)
templates/wlprofile/view_profile.html (+75/-59)
urls.py (+0/-4)
wiki/feeds.py (+2/-16)
wlprofile/context_processors.py (+6/-0)
wlprofile/migrations/0002_profile_deleted.py (+20/-0)
wlprofile/models.py (+1/-0)
wlprofile/templatetags/wlprofile_extras.py (+9/-2)
wlprofile/urls.py (+2/-0)
wlprofile/views.py (+92/-4)
To merge this branch: bzr merge lp:~widelands-dev/widelands-website/delete_user
Reviewer Review Type Date Requested Status
GunChleoc Approve
kaputtnik (community) Needs Resubmitting
Review via email: mp+354978@code.launchpad.net

Commit message

Make it possible to 'delete a user by himself, which means clean his data and show 'Deleted' in every place where a username is shown.

Description of the change

Added a link in the edit profile page which switch to a page showing what deletion means. After clicking on 'I am sure, delete me', the following changes are made:

- Log the user immediately out
- Delete possible playtime scheduling dates
- Delete the Online Gaming Password
- Clean the users profile page, including physically deleting his current image of avatar
- Deactivate all subscriptions for this user
- Put all his PMs in the users trash
- Set the user inactive, also set 'is_staff' and 'is_superuser' to false
- Set the users E-Mail to the value of settings.DELETED_MAIL_ADDRESS
- Show the value of settings.DELETED_USERNAME in all places where a username is shown on the website. Wehen hovering over such a name a tooltip is shown saying 'This user has left our community'
- Prevent sending PMs to a deleted user
- Prevent showing the users profile by directly typing the url in the browsers addressbar

The decision when to show the DELETED_USERNAME is done by comparing the value of the new boolean field 'deleted' in the model wlprofile.Profile. I have added this new field because other comparisons (e.g. against 'is_staff' or the anonymized Email address) seems a bit iffy to me.

What username should shown for a deleted user? Currently this is 'Deleted', if we want another name it should be consistent with the username rules, which means it should contain only alphanumeric, _, @, +, . and - characters.

Just found that the DELETED_USER_MAILADDRESS can also be an empty value. I will set it to an empty string then.

Additional cleanup:
- Removed unused functions for feeds

To post a comment you must log in.
Revision history for this message
kaputtnik (franku) wrote :

Just found that it is still possible to write a PM to a deleted user, if the recipient is written by hand in the recipient field. When the DELETED_USER_MAILADDRESS is empty an email isn't sent, but there is no error message shown to the user.

review: Needs Fixing
Revision history for this message
GunChleoc (gunchleoc) wrote :

How about calling the deleted user "Anonymous"?

Revision history for this message
GunChleoc (gunchleoc) :
512. By kaputtnik

addressed code review

513. By kaputtnik

set email to an empty string

Revision history for this message
kaputtnik (franku) wrote :

Thanks for your suggestions, all changed :)

> How about calling the deleted user "Anonymous"?

Not sure... reading "Anonymous" as a username may lead into the conclusion that anonymous writing is possible. My first idea for such a username was "Deleted_User" or "User_deleted" but i found it too long. Ideally it should be some name which describes the action and that makes clear that a user did it by himself (and not a forum moderator or admin -> censor alarm). "Self_removed" or so...

Regarding my comment on 2018-09-15: I wasn't able to find a solution to prevent writing PMs to such a user. It is a flaw of django-messages, so i filed a bugreport to the project -> https://github.com/arneb/django-messages/issues/118 I did everything to prevent sending such messages: The reply buttons for messages are not shown, the profile page isn't shown and the autosearch for users when writing a string in the Recipient box will not show inactive users. The only solution i can think of is some javascript. But i don't think it is worth the work.

So from my side this can go in if we have agreed on a username to show for deleted users (plural ;) ). But we can change it also anytime later on.

Revision history for this message
kaputtnik (franku) :
review: Needs Resubmitting
Revision history for this message
GunChleoc (gunchleoc) wrote :

How about "Former Member(s)"?

Or "Ancestor(s)"? Would fit with the tribes terminology theme

I agree, don't waste too much energy into trying to write a Javascript.

review: Approve
Revision history for this message
kaputtnik (franku) wrote :

"Ancestor" = "Vorfahre" oder "Ahn"? I think that wouldn't fit.

The name has to follow the Username rules, so no space.

Ex-Member?

We could choose "anonymized" insted of "Anonymous".

One question about the online gaming password: I have removed the string "WARNING: The online gaming password is transmitted in cleartext. Do not use your website password!" AFAIK it is not transmitted in cleartext anymore? Just to be sure.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Let's go with Ex-Member, that's good enough for me.

I think removing the warning is fine - the warning is shown in Build 19, and will become obsolete in Build 20. So, warning people in-game should suffice.

514. By kaputtnik

changed deleted username; small css-fix for comments where the username is small

Revision history for this message
kaputtnik (franku) wrote :

Merged and deployed, sorry for the server errors... had forgotten to run the migrate command :-D

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'django_messages_wl/views.py'
--- django_messages_wl/views.py 2018-04-18 12:04:12 +0000
+++ django_messages_wl/views.py 2018-09-19 18:09:08 +0000
@@ -15,7 +15,7 @@
15 if request.is_ajax():15 if request.is_ajax():
16 q = request.GET.get('term', '')16 q = request.GET.get('term', '')
1717
18 usernames = User.objects.filter(username__icontains=q)18 usernames = User.objects.exclude(is_active=False).filter(username__icontains=q)
19 results = []19 results = []
20 for user in usernames:20 for user in usernames:
21 name_json = {'value': user.username}21 name_json = {'value': user.username}
2222
=== modified file 'media/css/comments.css'
--- media/css/comments.css 2012-05-04 19:07:35 +0000
+++ media/css/comments.css 2018-09-19 18:09:08 +0000
@@ -17,6 +17,7 @@
17 text-align: center;17 text-align: center;
18 border-right: 1px solid #998;18 border-right: 1px solid #998;
19 height: 100%;19 height: 100%;
20 min-width: 54px;
20}21}
2122
22.comment .text {23.comment .text {
2324
=== modified file 'news/feeds.py'
--- news/feeds.py 2018-04-08 14:40:17 +0000
+++ news/feeds.py 2018-09-19 18:09:08 +0000
@@ -21,25 +21,3 @@
2121
22 def item_pubdate(self, item):22 def item_pubdate(self, item):
23 return item.publish23 return item.publish
24
25# Currently not used / not checked for compatibility for django 1.8
26
27
28class NewsPostsByCategory(Feed):
29 title = 'Widelands.org posts category feed'
30
31 def get_object(self, bits):
32 if len(bits) != 1:
33 raise ObjectDoesNotExist
34 return Category.objects.get(slug__exact=bits[0])
35
36 def link(self, item):
37 if not item:
38 raise FeedDoesNotExist
39 return item.get_absolute_url()
40
41 def description(self, item):
42 return 'Posts recently categorized as %s' % item.title
43
44 def items(self, item):
45 return item.post_set.published()[:10]
4624
=== modified file 'news/urls.py'
--- news/urls.py 2016-12-13 18:28:51 +0000
+++ news/urls.py 2018-09-19 18:09:08 +0000
@@ -1,25 +1,25 @@
1from django.conf.urls import *1from django.conf.urls import *
2from django.views.generic import ListView2from django.views.generic import ListView
3from news.views import NewsList, YearNews, MonthNews, NewsDetail, CategoryView3from news.views import NewsList, YearNews, MonthNews, NewsDetail, CategoryView
4from news.feeds import NewsPostsFeed
5
46
5urlpatterns = [7urlpatterns = [
6 url(r'^(?P<year>[0-9]{4})/(?P<month>[-\w]+)/(?P<day>[0-9]+)/(?P<slug>[-\w]+)/$',8 url(r'^(?P<year>[0-9]{4})/(?P<month>[-\w]+)/(?P<day>[0-9]+)/(?P<slug>[-\w]+)/$',
7 NewsDetail.as_view(),9 NewsDetail.as_view(),
8 name='news_detail'),10 name='news_detail'),
9
10 url(r'^(?P<year>\d{4})/(?P<month>[-\w]+)/$',11 url(r'^(?P<year>\d{4})/(?P<month>[-\w]+)/$',
11 MonthNews.as_view(),12 MonthNews.as_view(),
12 name='news_archive_month'),13 name='news_archive_month'),
13
14 url(r'^(?P<year>\d{4})/$',14 url(r'^(?P<year>\d{4})/$',
15 YearNews.as_view(),15 YearNews.as_view(),
16 name='news_archive_year'),16 name='news_archive_year'),
17
18 url(r'^category/(?P<slug>[-\w]+)/',17 url(r'^category/(?P<slug>[-\w]+)/',
19 CategoryView.as_view(),18 CategoryView.as_view(),
20 name='category_posts'),19 name='category_posts'),
21
22 url(r'^$',20 url(r'^$',
23 NewsList.as_view(template_name='news/post_list.html'),21 NewsList.as_view(template_name='news/post_list.html'),
24 name='news_index'),22 name='news_index'),
23 # Feed
24 url(r'^feed/$', NewsPostsFeed())
25]25]
2626
=== modified file 'pybb/feeds.py'
--- pybb/feeds.py 2018-04-08 14:40:17 +0000
+++ pybb/feeds.py 2018-09-19 18:09:08 +0000
@@ -3,6 +3,7 @@
3from django.core.exceptions import ObjectDoesNotExist3from django.core.exceptions import ObjectDoesNotExist
4from django.utils.feedgenerator import Atom1Feed4from django.utils.feedgenerator import Atom1Feed
5from pybb.models import Post, Topic, Forum5from pybb.models import Post, Topic, Forum
6from django.conf import settings
67
78
8class PybbFeed(Feed):9class PybbFeed(Feed):
@@ -60,11 +61,6 @@
60 def items_for_object(self, obj):61 def items_for_object(self, obj):
61 return Post.objects.filter(hidden=False, topic__forum=obj).order_by('-created')[:15]62 return Post.objects.filter(hidden=False, topic__forum=obj).order_by('-created')[:15]
6263
63 def item_author_name(self, item):
64 """Takes the object returned by get_object and returns the feeds's
65 auhor's name as a Python string."""
66 return item.user.username
67
68# Validated through http://validator.w3.org/feed/64# Validated through http://validator.w3.org/feed/
6965
7066
@@ -78,8 +74,3 @@
7874
79 def items_for_object(self, item):75 def items_for_object(self, item):
80 return Topic.objects.exclude(posts__hidden=True).filter(forum=item).order_by('-created')[:15]76 return Topic.objects.exclude(posts__hidden=True).filter(forum=item).order_by('-created')[:15]
81
82 def item_author_name(self, item):
83 """Takes the object returned by get_object and returns the feeds's
84 auhor's name as a Python string."""
85 return item.user.username
8677
=== modified file 'settings.py'
--- settings.py 2018-09-04 18:17:07 +0000
+++ settings.py 2018-09-19 18:09:08 +0000
@@ -142,6 +142,7 @@
142 'django.template.context_processors.static',142 'django.template.context_processors.static',
143 'django.template.context_processors.tz',143 'django.template.context_processors.tz',
144 'django_messages.context_processors.inbox',144 'django_messages.context_processors.inbox',
145 'wlprofile.context_processors.deleted_user_data',
145 ],146 ],
146 },147 },
147 },148 },
@@ -322,6 +323,13 @@
322# Number of stored users323# Number of stored users
323ONLINE_MAX = 25324ONLINE_MAX = 25
324325
326###########################################
327# Settings for users who deleted themself #
328###########################################
329
330DELETED_MAIL_ADDRESS = ''
331DELETED_USERNAME = 'Ex-Member'
332
325try:333try:
326 from local_settings import *334 from local_settings import *
327except ImportError:335except ImportError:
328336
=== modified file 'templates/django_messages/view.html'
--- templates/django_messages/view.html 2016-03-02 21:02:38 +0000
+++ templates/django_messages/view.html 2018-09-19 18:09:08 +0000
@@ -30,8 +30,9 @@
30 </td>30 </td>
31 </tr>31 </tr>
32 </table>32 </table>
33 {% ifequal message.recipient user %}33 {% if message.recipient == user and not message.sender.wlprofile.deleted %}
34 {{ context.deleted_email_address }}
34 <button type="button" onclick="location.href='{% url 'messages_reply' message.id %}';">{% trans "Reply" %}</button>35 <button type="button" onclick="location.href='{% url 'messages_reply' message.id %}';">{% trans "Reply" %}</button>
35 {% endifequal %}36 {% endif %}
36 <button type="button" onclick="location.href='{% url 'messages_delete' message.id %}';">{% trans "Delete" %}</button>37 <button type="button" onclick="location.href='{% url 'messages_delete' message.id %}';">{% trans "Delete" %}</button>
37{% endblock %}38{% endblock %}
3839
=== modified file 'templates/mainpage.html'
--- templates/mainpage.html 2016-03-02 21:02:38 +0000
+++ templates/mainpage.html 2018-09-19 18:09:08 +0000
@@ -12,7 +12,7 @@
12{% block extra_head %}12{% block extra_head %}
13<meta name="google-site-verification" content="1A5uFV_zNuXazJ46-572-_lLzcCTEQ77iHaSPFZd53Y" />13<meta name="google-site-verification" content="1A5uFV_zNuXazJ46-572-_lLzcCTEQ77iHaSPFZd53Y" />
14<link rel="stylesheet" type="text/css" media="all" href="{{ MEDIA_URL }}css/news.css" />14<link rel="stylesheet" type="text/css" media="all" href="{{ MEDIA_URL }}css/news.css" />
15<link rel="alternate" type="application/rss+xml" title="Widelands News" href="/feeds/news/" />15<link rel="alternate" type="application/rss+xml" title="Widelands News" href="news/feed/" />
1616
17{{ block.super}}{% endblock %}17{{ block.super}}{% endblock %}
18{% block content %}18{% block content %}
1919
=== modified file 'templates/pybb/feeds/posts_description.html'
--- templates/pybb/feeds/posts_description.html 2014-12-01 06:32:12 +0000
+++ templates/pybb/feeds/posts_description.html 2018-09-19 18:09:08 +0000
@@ -1,2 +1,6 @@
1{{ obj.user }}:<br>1{% if obj.user.wlprofile.deleted %}
2 {{ DELETED_USERNAME }}
3{% else %}
4 {{ obj.user }}
5{% endif %}wrote:<br>
2{{ obj.body_html|safe }}6{{ obj.body_html|safe }}
37
=== modified file 'templates/pybb/feeds/topics_description.html'
--- templates/pybb/feeds/topics_description.html 2014-12-01 06:32:12 +0000
+++ templates/pybb/feeds/topics_description.html 2018-09-19 18:09:08 +0000
@@ -1,2 +1,6 @@
1{{ obj.head.user }}:<br>1{% if obj.head.user.wlprofile.deleted %}
2 {{ DELETED_USERNAME }}
3{% else %}
4 {{ obj.head.user }}
5{% endif %}wrote:<br>
2{{ obj.head.body_html|safe }}6{{ obj.head.body_html|safe }}
37
=== modified file 'templates/pybb/last_posts.html'
--- templates/pybb/last_posts.html 2016-10-10 19:32:14 +0000
+++ templates/pybb/last_posts.html 2018-09-19 18:09:08 +0000
@@ -12,7 +12,7 @@
12 <li>12 <li>
13 {{ post.topic.forum.name }}<br />13 {{ post.topic.forum.name }}<br />
14 <a href="{{ post.get_absolute_url }}" title="{{ post.topic.name }}">{{ post.topic.name|pybb_cut_string:30 }}</a><br />14 <a href="{{ post.get_absolute_url }}" title="{{ post.topic.name }}">{{ post.topic.name|pybb_cut_string:30 }}</a><br />
15 by <a href="{% url 'profile_view' post.user %}">{{post.user.username}}</a> {{ post.created|minutes }} ago15 by {{ post.user|user_link }} {{ post.created|minutes }} ago
16 </li>16 </li>
17 {% endfor %}17 {% endfor %}
18 </ul>18 </ul>
1919
=== modified file 'templates/threadedcomments/inlines/comments.html'
--- templates/threadedcomments/inlines/comments.html 2016-05-15 14:41:54 +0000
+++ templates/threadedcomments/inlines/comments.html 2018-09-19 18:09:08 +0000
@@ -16,7 +16,7 @@
16 <td class="author" rowspan="2">16 <td class="author" rowspan="2">
17 {% if comment.user.wlprofile.avatar %}17 {% if comment.user.wlprofile.avatar %}
18 <a href="{% url 'profile_view' comment.user %}">18 <a href="{% url 'profile_view' comment.user %}">
19 <img style="width: 50px; height: 50px;" src="{{ comment.user.wlprofile.avatar.url }}" />19 <img src="{{ comment.user.wlprofile.avatar.url }}" />
20 </a>20 </a>
21 <br />21 <br />
22 {% endif %}22 {% endif %}
2323
=== modified file 'templates/wiki/feeds/history_description.html'
--- templates/wiki/feeds/history_description.html 2017-12-11 08:42:55 +0000
+++ templates/wiki/feeds/history_description.html 2018-09-19 18:09:08 +0000
@@ -1,4 +1,9 @@
1{% load i18n %}1{% load i18n %}
22
3{% trans "edited by user" %} {{ obj.editor.username }} {% trans "at"%} {{ obj.modified }}<br>3{% trans "Edited by user" %} {% if obj.editor.wlprofile.deleted %}
4 {{ DELETED_USERNAME }}
5 {% else %}
6 {{ obj.editor.username }}
7 {% endif %}
8 {% trans "at"%} {{ obj.modified }}<br>
4{{ obj.comment }}9{{ obj.comment }}
510
=== modified file 'templates/wiki/recentchanges.html'
--- templates/wiki/recentchanges.html 2017-08-16 21:13:43 +0000
+++ templates/wiki/recentchanges.html 2018-09-19 18:09:08 +0000
@@ -3,6 +3,7 @@
3{% load humanize i18n %}3{% load humanize i18n %}
4{% load pagination_tags %}4{% load pagination_tags %}
5{% load custom_date %}5{% load custom_date %}
6{% load wlprofile_extras %}
67
7{% block title %}8{% block title %}
8{% trans "Recent Changes" %} - {{ block.super }}9{% trans "Recent Changes" %} - {{ block.super }}
@@ -42,7 +43,7 @@
42 <a href="{% url 'wiki_article' change.article.title %}">{{ change.article.title }}</a>43 <a href="{% url 'wiki_article' change.article.title %}">{{ change.article.title }}</a>
43 </td>44 </td>
44 <td>{{ change.modified|custom_date:user }}</td>45 <td>{{ change.modified|custom_date:user }}</td>
45 <td>{{ change.editor }}</td>46 <td>{{ change.editor|user_link }}</td>
46 <td>{{ change.comment }}</td>47 <td>{{ change.comment }}</td>
47 </tr>48 </tr>
48{% endfor %}49{% endfor %}
4950
=== added file 'templates/wlprofile/delete_me.html'
--- templates/wlprofile/delete_me.html 1970-01-01 00:00:00 +0000
+++ templates/wlprofile/delete_me.html 2018-09-19 18:09:08 +0000
@@ -0,0 +1,42 @@
1{% extends "wlprofile/base.html" %}
2
3{% load i18n %}
4{% load wlprofile_extras %}
5
6{% block title %}
7{% trans "Delete me" %} - {{ block.super }}
8{% endblock %}
9
10{% block content %}
11<h1>{% trans "Delete your account" %}</h1>
12
13<div class="blogEntry">
14 <h3>Hi {{ user }},</h3>
15 <p>we are sorry that you want to leave our community <img src="/wlmedia/img/smileys/face-sad.png" alt="Sad smiley"></p>
16 <h3>What deleting yourself means:</h3>
17 <ul>
18 <li>Your account will be deactivated. This means:
19 <ul>
20 <li>You will be immediately logged out and unable to log in again.</li>
21 <li>The Username "{{ user }}" will continue to be reserved, so registering again with that username will not be possible.</li>
22 </ul>
23 </li>
24 <li>Your <a href="{% url 'profile_edit' %}">profile</a> will be deleted and your currently used avatar image will be deleted.</li>
25 <li>
26 All your <a href="{% url 'messages_inbox' %}">private messages</a> will be moved into the <a href="{% url 'messages_trash' %}">trash</a>.
27 They will stay there until such time that the sender or recipient will also delete them.</li>
28 <li>All your <a href="{% url 'notification_notices' %}">subscriptions</a> will be removed.</li>
29 <li>Your email address will be removed, so you will not receive any notification mails anymore.</li>
30 <li>
31 <b>Nothing</b> that you have posted (forum posts, comments and uploaded maps) will be deleted.
32 Instead of your user name, the string "{{ DELETED_USERNAME }}" will be shown.</li>
33 <li>Your online gaming password will be reset.</li>
34 <li>All dates given in the <a href="{% url 'scheduling_scheduling' %}">Playtime scheduler</a> will be deleted</li>
35 </ul>
36 <p class="errormessage"><b>This step can't be undone!</b></p>
37 <p>
38 <button type="button" onclick="location.href='{% url 'do_delete' %}';" style="font-weight: normal;">I am sure, please delete me</button>
39 <button type="button" onclick="location.href='{% url 'profile_edit' %}';">Cancel</button>
40 </p>
41</div>
42{% endblock %}
043
=== modified file 'templates/wlprofile/edit_profile.html'
--- templates/wlprofile/edit_profile.html 2016-06-26 09:04:15 +0000
+++ templates/wlprofile/edit_profile.html 2018-09-19 18:09:08 +0000
@@ -41,17 +41,12 @@
41 <input type="submit" value="{% trans "Save" %}" />41 <input type="submit" value="{% trans "Save" %}" />
42 {% csrf_token %}42 {% csrf_token %}
43 </form>43 </form>
44 <br />44
45 <br />45 <h2>Other options:</h2>
46 <p>46 <ul>
47 <a href="{% url 'auth_password_change' %}">Change website password</a>47 <li><a href="{% url 'auth_password_change' %}">Change website password</a></li>
48 <br />48 <li><a href="{% url 'wlggz_changepw' %}">Change online gaming password</a></li>
49 You will be redirected to an encrypted connection. The website password is <strong>not</strong> transmitted in cleartext.49 <li><a href="{% url 'delete_me' %}">Delete me</a></li>
50 </p>50 </ul>
51 <p>
52 <a href="{% url 'wlggz_changepw' %}">Change online gaming password</a>
53 <br />
54 <strong class="errormessage">WARNING: The online gaming password is transmitted in cleartext. Do not use your website password!</strong>
55 </p>
56</div>51</div>
57{% endblock %}52{% endblock %}
5853
=== modified file 'templates/wlprofile/view_profile.html'
--- templates/wlprofile/view_profile.html 2017-07-27 06:01:32 +0000
+++ templates/wlprofile/view_profile.html 2018-09-19 18:09:08 +0000
@@ -4,7 +4,13 @@
4{% load custom_date %}4{% load custom_date %}
55
6{% block title %}6{% block title %}
7{{ profile.user.username }}'s Profile - {{ block.super }}7 {% if not profile.deleted %}
8 {{ profile.user.username }}'s
9 {% else %}
10 Deleted Users Profile
11 {% endif %}
12 - Profile
13 {{ block.super }}
8{% endblock %}14{% endblock %}
915
10{% block content %}16{% block content %}
@@ -13,66 +19,76 @@
13 <a class="invertedColor" href="{% url 'profile_edit' %}">Edit Profile</a>19 <a class="invertedColor" href="{% url 'profile_edit' %}">Edit Profile</a>
14 {% endifequal %}20 {% endifequal %}
15</div>21</div>
16<h1>{{ profile.user.username }}'s Profile</h1>22<h1>{% if not profile.deleted %}
23 {{ profile.user.username }}'s
24 {% else %}
25 Deleted User
26 {% endif %}
27 Profile
28</h1>
1729
18<div class="blogEntry">30<div class="blogEntry">
19 <table>31 {% if not profile.deleted %}
20 <tr>32 <table>
21 <td>33 <tr>
22 {% if profile.avatar %}34 <td>
23 <img src="{{ profile.avatar.url }}" alt="Avatar" />35 {% if profile.avatar %}
24 {% endif %}36 <img src="{{ profile.avatar.url }}" alt="Avatar" />
25 </td>37 {% endif %}
26 <td>38 </td>
27 {% ifnotequal user profile.user %}39 <td>
28 <button onclick="window.location.href='{% url 'messages_compose_to' profile.user %}';">40 {% ifnotequal user profile.user %}
29 <img src="{{ MEDIA_URL }}forum/img/send_pm.png" alt ="" class="middle" />41 <button onclick="window.location.href='{% url 'messages_compose_to' profile.user %}';">
30 <span class="middle">{% trans "Send PM" %}</span>42 <img src="{{ MEDIA_URL }}forum/img/send_pm.png" alt ="" class="middle" />
31 </button>43 <span class="middle">{% trans "Send PM" %}</span>
32 {% endifnotequal %}44 </button>
33 </td>45 {% endifnotequal %}
34 </tr>46 </td>
35 <tr>47 </tr>
36 <td class="grey">Joined:</td>48 <tr>
37 <td>{{ profile.user.date_joined|custom_date:user|title }}</td>49 <td class="grey">Joined:</td>
38 </tr>50 <td>{{ profile.user.date_joined|custom_date:user|title }}</td>
39 <tr>51 </tr>
40 <td class="grey">Forum Posts:</td>52 <tr>
41 <td>{{ profile.post_count }}</td>53 <td class="grey">Forum Posts:</td>
42 </tr>54 <td>{{ profile.post_count }}</td>
43 <tr>55 </tr>
44 <td class="grey">Website:</td>56 <tr>
45 <td><a href="{{profile.site}}">{{ profile.site }}</a></td>57 <td class="grey">Website:</td>
46 </tr>58 <td><a href="{{profile.site}}">{{ profile.site }}</a></td>
47 <tr>59 </tr>
48 <td class="grey">Location:</td>60 <tr>
49 <td>{{ profile.location }}</td>61 <td class="grey">Location:</td>
50 </tr>62 <td>{{ profile.location }}</td>
51 <tr>63 </tr>
52 <td class="grey">Jabber:</td>64 <tr>
53 <td>{{ profile.jabber }}</td>65 <td class="grey">Jabber:</td>
54 </tr>66 <td>{{ profile.jabber }}</td>
55 <tr>67 </tr>
56 <td class="grey">ICQ:</td>68 <tr>
57 <td>{{ profile.icq }}</td>69 <td class="grey">ICQ:</td>
58 </tr>70 <td>{{ profile.icq }}</td>
59 <tr>71 </tr>
60 <td class="grey">MSN:</td>72 <tr>
61 <td>{{ profile.msn }}</td>73 <td class="grey">MSN:</td>
62 </tr>74 <td>{{ profile.msn }}</td>
63 <tr>75 </tr>
64 <td class="grey">AIM:</td>76 <tr>
65 <td>{{ profile.aim }}</td>77 <td class="grey">AIM:</td>
66 </tr>78 <td>{{ profile.aim }}</td>
67 <tr>79 </tr>
68 <td class="grey">Yahoo:</td>80 <tr>
69 <td>{{ profile.yahoo }}</td>81 <td class="grey">Yahoo:</td>
70 </tr>82 <td>{{ profile.yahoo }}</td>
71 <tr>83 </tr>
72 <td class="grey">Signature:</td>84 <tr>
73 <td>{{ profile.signature|urlize|linebreaks }}</td>85 <td class="grey">Signature:</td>
74 </tr>86 <td>{{ profile.signature|urlize|linebreaks }}</td>
75 </table>87 </tr>
88 </table>
89 {% else %}
90 <p>This user has deleted his data ...</p>
91 {% endif %}
76</div>92</div>
7793
78{% endblock %}94{% endblock %}
7995
=== modified file 'urls.py'
--- urls.py 2018-05-24 19:17:36 +0000
+++ urls.py 2018-09-19 18:09:08 +0000
@@ -4,7 +4,6 @@
4from django.contrib import admin4from django.contrib import admin
5admin.autodiscover()5admin.autodiscover()
66
7from news.feeds import NewsPostsFeed
8from django.views.generic.base import RedirectView7from django.views.generic.base import RedirectView
9from django.views.generic import TemplateView8from django.views.generic import TemplateView
10from django.contrib.syndication.views import Feed9from django.contrib.syndication.views import Feed
@@ -28,9 +27,6 @@
28 url(r'^accounts/', include('registration.backends.hmac.urls')),27 url(r'^accounts/', include('registration.backends.hmac.urls')),
29 url('^', include('django.contrib.auth.urls')),28 url('^', include('django.contrib.auth.urls')),
3029
31 # Feed for news
32 url(r'^feeds/news/$', NewsPostsFeed()),
33
34 # Formerly 3rd party30 # Formerly 3rd party
35 url(r'^notification/', include('notification.urls')),31 url(r'^notification/', include('notification.urls')),
36 32
3733
=== modified file 'wiki/feeds.py'
--- wiki/feeds.py 2016-12-13 18:28:51 +0000
+++ wiki/feeds.py 2018-09-19 18:09:08 +0000
@@ -19,19 +19,12 @@
1919
20 def item_pubdate(self, item):20 def item_pubdate(self, item):
21 """Return the item's pubdate.21 """Return the item's pubdate.
2222
23 It's this modified date23 It's this modified date
2424
25 """25 """
26 return item.modified26 return item.modified
2727
28 def item_author_name(self, item):
29 """Takes the object returned by items(), and returns the feeds's
30 auhor's name as a Python string."""
31 if item.is_anonymous_change():
32 return 'Anonymous'
33 return item.editor.username
34
35# Validated through http://validator.w3.org/feed/28# Validated through http://validator.w3.org/feed/
3629
3730
@@ -84,10 +77,3 @@
8477
85 def item_updateddate(self, item):78 def item_updateddate(self, item):
86 return item.modified79 return item.modified
87
88 def item_author_name(self, item):
89 """Takes the object returned by items(), and returns the feeds's
90 auhor's name as a Python string."""
91 if item.is_anonymous_change():
92 return 'Anonymous'
93 return item.editor.username
9480
=== added file 'wlprofile/context_processors.py'
--- wlprofile/context_processors.py 1970-01-01 00:00:00 +0000
+++ wlprofile/context_processors.py 2018-09-19 18:09:08 +0000
@@ -0,0 +1,6 @@
1from django.conf import settings
2
3
4def deleted_user_data(request):
5 context = {'DELETED_USERNAME': settings.DELETED_USERNAME}
6 return context
07
=== added file 'wlprofile/migrations/0002_profile_deleted.py'
--- wlprofile/migrations/0002_profile_deleted.py 1970-01-01 00:00:00 +0000
+++ wlprofile/migrations/0002_profile_deleted.py 2018-09-19 18:09:08 +0000
@@ -0,0 +1,20 @@
1# -*- coding: utf-8 -*-
2# Generated by Django 1.11.12 on 2018-09-13 21:23
3from __future__ import unicode_literals
4
5from django.db import migrations, models
6
7
8class Migration(migrations.Migration):
9
10 dependencies = [
11 ('wlprofile', '0001_initial'),
12 ]
13
14 operations = [
15 migrations.AddField(
16 model_name='profile',
17 name='deleted',
18 field=models.BooleanField(default=False),
19 ),
20 ]
021
=== modified file 'wlprofile/models.py'
--- wlprofile/models.py 2016-12-13 18:28:51 +0000
+++ wlprofile/models.py 2018-09-19 18:09:08 +0000
@@ -48,6 +48,7 @@
48 upload_to='wlprofile/avatars/', width=settings.AVATAR_WIDTH, height=settings.AVATAR_HEIGHT)48 upload_to='wlprofile/avatars/', width=settings.AVATAR_WIDTH, height=settings.AVATAR_HEIGHT)
49 show_signatures = models.BooleanField(49 show_signatures = models.BooleanField(
50 _('Show signatures'), blank=True, default=True)50 _('Show signatures'), blank=True, default=True)
51 deleted = models.BooleanField(default=False)
5152
52 class Meta:53 class Meta:
53 verbose_name = _('Profile')54 verbose_name = _('Profile')
5455
=== modified file 'wlprofile/templatetags/wlprofile_extras.py'
--- wlprofile/templatetags/wlprofile_extras.py 2018-04-08 14:40:17 +0000
+++ wlprofile/templatetags/wlprofile_extras.py 2018-09-19 18:09:08 +0000
@@ -12,12 +12,19 @@
12from django import template12from django import template
13from django.urls import reverse13from django.urls import reverse
14from django.utils.safestring import mark_safe14from django.utils.safestring import mark_safe
15from django.contrib.auth.models import User
16from django.conf import settings
1517
16register = template.Library()18register = template.Library()
1719
1820
19@register.filter21@register.filter
20def user_link(user):22def user_link(user):
21 data = u'<a href="%s">%s</a>' % (23
22 reverse('profile_view', args=[user.username]), user.username)24 if user.is_authenticated and user.wlprofile.deleted:
25 # Check for is_authenticated is needed for threadedcomments reply_to.js
26 return mark_safe('<span title="This user has left our community">{}</span>'.format(settings.DELETED_USERNAME))
27 else:
28 data = u'<a href="%s">%s</a>' % (
29 reverse('profile_view', args=[user.username]), user.username)
23 return mark_safe(data)30 return mark_safe(data)
2431
=== modified file 'wlprofile/urls.py'
--- wlprofile/urls.py 2016-12-13 18:28:51 +0000
+++ wlprofile/urls.py 2018-09-19 18:09:08 +0000
@@ -14,6 +14,8 @@
1414
15urlpatterns = [15urlpatterns = [
16 url(r'^edit/$', views.edit, name='profile_edit'),16 url(r'^edit/$', views.edit, name='profile_edit'),
17 url(r'^delete/$', views.delete_me, name='delete_me'),
18 url(r'^do_delete/$', views.do_delete, name='do_delete'),
17 url(r'^(?P<user>.*)/$', views.view, name='profile_view'),19 url(r'^(?P<user>.*)/$', views.view, name='profile_view'),
18 url(r'^$', views.view, name='profile_view'),20 url(r'^$', views.view, name='profile_view'),
19]21]
2022
=== modified file 'wlprofile/views.py'
--- wlprofile/views.py 2018-05-30 16:59:16 +0000
+++ wlprofile/views.py 2018-09-19 18:09:08 +0000
@@ -4,14 +4,102 @@
4from django.urls import reverse4from django.urls import reverse
5from django.contrib.auth.decorators import login_required5from django.contrib.auth.decorators import login_required
6from django.contrib.auth.models import User6from django.contrib.auth.models import User
7from django.shortcuts import render7from django.shortcuts import render, redirect
8from django.http import HttpResponseRedirect8from django.http import HttpResponseRedirect
9from django.shortcuts import get_object_or_4049from django.shortcuts import get_object_or_404
10from django.conf import settings
1011
11from forms import EditProfileForm12from forms import EditProfileForm
12import settings13
1314
14# Settings15@login_required
16def delete_me(request):
17 """Show a page to inform the user what deleting means."""
18
19 context = {
20 'user': request.user
21 }
22 return render(request, 'wlprofile/delete_me.html',
23 context)
24
25
26@login_required
27def do_delete(request):
28 """Delete user specific data.
29
30 We can't really delete a user who has posted some valid posts (no spam) because otherwise
31 we have foreign keys constraints in the database. All we can do is to do some cleanup and
32 anonymization.
33 """
34
35 from django.contrib.auth import logout
36 from wlprofile.models import Profile
37 from notification.models import NoticeSetting
38
39 user = get_object_or_404(User, username=request.user)
40
41 # Log the user out. We do this as early as possible but after
42 # we get the User object.
43 logout(request)
44
45 # Clean possible Playtime availabilities
46 from wlscheduling.models import Availabilities
47 try:
48 events = Availabilities.objects.filter(user=user)
49 for event in events:
50 event.delete()
51 except:
52 pass
53
54 # Clean the Online gaming password
55 from wlggz.models import GGZAuth
56 try:
57 ggz_user = GGZAuth.objects.get(user=user)
58 ggz_user.delete()
59 except:
60 pass
61
62 # Clean the profile
63 profile = user.wlprofile
64 upload_to = Profile._meta.get_field('avatar').upload_to
65
66 if upload_to in profile.avatar.name:
67 # Delete the avatar file
68 profile.avatar.delete()
69
70 # Delete the profile and recreate it to get a clean profile page
71 # We create a new one to have the anymous.png as avatar
72 profile.delete()
73 profile = Profile(user=user, deleted=True)
74 profile.save()
75
76 # Deactivate all subscriptions
77 notice_settings = NoticeSetting.objects.filter(user=user)
78 for setting in notice_settings:
79 setting.send = False
80 setting.save()
81
82 # Put all PMs in the trash of the user. They stay as long in the trash until the sender or recipient
83 # has also put the message in the trash.
84 from django_messages.models import Message
85 from datetime import datetime
86 messages = Message.objects.inbox_for(user)
87 for message in messages:
88 message.recipient_deleted_at = datetime.now()
89 message.save()
90 messages = Message.objects.outbox_for(user)
91 for message in messages:
92 message.sender_deleted_at = datetime.now()
93 message.save()
94
95 # Do some settings in django.auth.User
96 user.is_active = False
97 user.is_staff = False
98 user.is_superuser = False
99 user.email = settings.DELETED_MAIL_ADDRESS
100 user.save()
101
102 return redirect('mainpage')
15103
16104
17@login_required105@login_required

Subscribers

People subscribed via source and target branches