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
1=== modified file 'django_messages_wl/views.py'
2--- django_messages_wl/views.py 2018-04-18 12:04:12 +0000
3+++ django_messages_wl/views.py 2018-09-19 18:09:08 +0000
4@@ -15,7 +15,7 @@
5 if request.is_ajax():
6 q = request.GET.get('term', '')
7
8- usernames = User.objects.filter(username__icontains=q)
9+ usernames = User.objects.exclude(is_active=False).filter(username__icontains=q)
10 results = []
11 for user in usernames:
12 name_json = {'value': user.username}
13
14=== modified file 'media/css/comments.css'
15--- media/css/comments.css 2012-05-04 19:07:35 +0000
16+++ media/css/comments.css 2018-09-19 18:09:08 +0000
17@@ -17,6 +17,7 @@
18 text-align: center;
19 border-right: 1px solid #998;
20 height: 100%;
21+ min-width: 54px;
22 }
23
24 .comment .text {
25
26=== modified file 'news/feeds.py'
27--- news/feeds.py 2018-04-08 14:40:17 +0000
28+++ news/feeds.py 2018-09-19 18:09:08 +0000
29@@ -21,25 +21,3 @@
30
31 def item_pubdate(self, item):
32 return item.publish
33-
34-# Currently not used / not checked for compatibility for django 1.8
35-
36-
37-class NewsPostsByCategory(Feed):
38- title = 'Widelands.org posts category feed'
39-
40- def get_object(self, bits):
41- if len(bits) != 1:
42- raise ObjectDoesNotExist
43- return Category.objects.get(slug__exact=bits[0])
44-
45- def link(self, item):
46- if not item:
47- raise FeedDoesNotExist
48- return item.get_absolute_url()
49-
50- def description(self, item):
51- return 'Posts recently categorized as %s' % item.title
52-
53- def items(self, item):
54- return item.post_set.published()[:10]
55
56=== modified file 'news/urls.py'
57--- news/urls.py 2016-12-13 18:28:51 +0000
58+++ news/urls.py 2018-09-19 18:09:08 +0000
59@@ -1,25 +1,25 @@
60 from django.conf.urls import *
61 from django.views.generic import ListView
62 from news.views import NewsList, YearNews, MonthNews, NewsDetail, CategoryView
63+from news.feeds import NewsPostsFeed
64+
65
66 urlpatterns = [
67 url(r'^(?P<year>[0-9]{4})/(?P<month>[-\w]+)/(?P<day>[0-9]+)/(?P<slug>[-\w]+)/$',
68 NewsDetail.as_view(),
69 name='news_detail'),
70-
71 url(r'^(?P<year>\d{4})/(?P<month>[-\w]+)/$',
72 MonthNews.as_view(),
73 name='news_archive_month'),
74-
75 url(r'^(?P<year>\d{4})/$',
76 YearNews.as_view(),
77 name='news_archive_year'),
78-
79 url(r'^category/(?P<slug>[-\w]+)/',
80 CategoryView.as_view(),
81 name='category_posts'),
82-
83 url(r'^$',
84 NewsList.as_view(template_name='news/post_list.html'),
85 name='news_index'),
86+ # Feed
87+ url(r'^feed/$', NewsPostsFeed())
88 ]
89
90=== modified file 'pybb/feeds.py'
91--- pybb/feeds.py 2018-04-08 14:40:17 +0000
92+++ pybb/feeds.py 2018-09-19 18:09:08 +0000
93@@ -3,6 +3,7 @@
94 from django.core.exceptions import ObjectDoesNotExist
95 from django.utils.feedgenerator import Atom1Feed
96 from pybb.models import Post, Topic, Forum
97+from django.conf import settings
98
99
100 class PybbFeed(Feed):
101@@ -60,11 +61,6 @@
102 def items_for_object(self, obj):
103 return Post.objects.filter(hidden=False, topic__forum=obj).order_by('-created')[:15]
104
105- def item_author_name(self, item):
106- """Takes the object returned by get_object and returns the feeds's
107- auhor's name as a Python string."""
108- return item.user.username
109-
110 # Validated through http://validator.w3.org/feed/
111
112
113@@ -78,8 +74,3 @@
114
115 def items_for_object(self, item):
116 return Topic.objects.exclude(posts__hidden=True).filter(forum=item).order_by('-created')[:15]
117-
118- def item_author_name(self, item):
119- """Takes the object returned by get_object and returns the feeds's
120- auhor's name as a Python string."""
121- return item.user.username
122
123=== modified file 'settings.py'
124--- settings.py 2018-09-04 18:17:07 +0000
125+++ settings.py 2018-09-19 18:09:08 +0000
126@@ -142,6 +142,7 @@
127 'django.template.context_processors.static',
128 'django.template.context_processors.tz',
129 'django_messages.context_processors.inbox',
130+ 'wlprofile.context_processors.deleted_user_data',
131 ],
132 },
133 },
134@@ -322,6 +323,13 @@
135 # Number of stored users
136 ONLINE_MAX = 25
137
138+###########################################
139+# Settings for users who deleted themself #
140+###########################################
141+
142+DELETED_MAIL_ADDRESS = ''
143+DELETED_USERNAME = 'Ex-Member'
144+
145 try:
146 from local_settings import *
147 except ImportError:
148
149=== modified file 'templates/django_messages/view.html'
150--- templates/django_messages/view.html 2016-03-02 21:02:38 +0000
151+++ templates/django_messages/view.html 2018-09-19 18:09:08 +0000
152@@ -30,8 +30,9 @@
153 </td>
154 </tr>
155 </table>
156- {% ifequal message.recipient user %}
157+ {% if message.recipient == user and not message.sender.wlprofile.deleted %}
158+ {{ context.deleted_email_address }}
159 <button type="button" onclick="location.href='{% url 'messages_reply' message.id %}';">{% trans "Reply" %}</button>
160- {% endifequal %}
161+ {% endif %}
162 <button type="button" onclick="location.href='{% url 'messages_delete' message.id %}';">{% trans "Delete" %}</button>
163 {% endblock %}
164
165=== modified file 'templates/mainpage.html'
166--- templates/mainpage.html 2016-03-02 21:02:38 +0000
167+++ templates/mainpage.html 2018-09-19 18:09:08 +0000
168@@ -12,7 +12,7 @@
169 {% block extra_head %}
170 <meta name="google-site-verification" content="1A5uFV_zNuXazJ46-572-_lLzcCTEQ77iHaSPFZd53Y" />
171 <link rel="stylesheet" type="text/css" media="all" href="{{ MEDIA_URL }}css/news.css" />
172-<link rel="alternate" type="application/rss+xml" title="Widelands News" href="/feeds/news/" />
173+<link rel="alternate" type="application/rss+xml" title="Widelands News" href="news/feed/" />
174
175 {{ block.super}}{% endblock %}
176 {% block content %}
177
178=== modified file 'templates/pybb/feeds/posts_description.html'
179--- templates/pybb/feeds/posts_description.html 2014-12-01 06:32:12 +0000
180+++ templates/pybb/feeds/posts_description.html 2018-09-19 18:09:08 +0000
181@@ -1,2 +1,6 @@
182-{{ obj.user }}:<br>
183+{% if obj.user.wlprofile.deleted %}
184+ {{ DELETED_USERNAME }}
185+{% else %}
186+ {{ obj.user }}
187+{% endif %}wrote:<br>
188 {{ obj.body_html|safe }}
189
190=== modified file 'templates/pybb/feeds/topics_description.html'
191--- templates/pybb/feeds/topics_description.html 2014-12-01 06:32:12 +0000
192+++ templates/pybb/feeds/topics_description.html 2018-09-19 18:09:08 +0000
193@@ -1,2 +1,6 @@
194-{{ obj.head.user }}:<br>
195+{% if obj.head.user.wlprofile.deleted %}
196+ {{ DELETED_USERNAME }}
197+{% else %}
198+ {{ obj.head.user }}
199+{% endif %}wrote:<br>
200 {{ obj.head.body_html|safe }}
201
202=== modified file 'templates/pybb/last_posts.html'
203--- templates/pybb/last_posts.html 2016-10-10 19:32:14 +0000
204+++ templates/pybb/last_posts.html 2018-09-19 18:09:08 +0000
205@@ -12,7 +12,7 @@
206 <li>
207 {{ post.topic.forum.name }}<br />
208 <a href="{{ post.get_absolute_url }}" title="{{ post.topic.name }}">{{ post.topic.name|pybb_cut_string:30 }}</a><br />
209- by <a href="{% url 'profile_view' post.user %}">{{post.user.username}}</a> {{ post.created|minutes }} ago
210+ by {{ post.user|user_link }} {{ post.created|minutes }} ago
211 </li>
212 {% endfor %}
213 </ul>
214
215=== modified file 'templates/threadedcomments/inlines/comments.html'
216--- templates/threadedcomments/inlines/comments.html 2016-05-15 14:41:54 +0000
217+++ templates/threadedcomments/inlines/comments.html 2018-09-19 18:09:08 +0000
218@@ -16,7 +16,7 @@
219 <td class="author" rowspan="2">
220 {% if comment.user.wlprofile.avatar %}
221 <a href="{% url 'profile_view' comment.user %}">
222- <img style="width: 50px; height: 50px;" src="{{ comment.user.wlprofile.avatar.url }}" />
223+ <img src="{{ comment.user.wlprofile.avatar.url }}" />
224 </a>
225 <br />
226 {% endif %}
227
228=== modified file 'templates/wiki/feeds/history_description.html'
229--- templates/wiki/feeds/history_description.html 2017-12-11 08:42:55 +0000
230+++ templates/wiki/feeds/history_description.html 2018-09-19 18:09:08 +0000
231@@ -1,4 +1,9 @@
232 {% load i18n %}
233
234-{% trans "edited by user" %} {{ obj.editor.username }} {% trans "at"%} {{ obj.modified }}<br>
235+{% trans "Edited by user" %} {% if obj.editor.wlprofile.deleted %}
236+ {{ DELETED_USERNAME }}
237+ {% else %}
238+ {{ obj.editor.username }}
239+ {% endif %}
240+ {% trans "at"%} {{ obj.modified }}<br>
241 {{ obj.comment }}
242
243=== modified file 'templates/wiki/recentchanges.html'
244--- templates/wiki/recentchanges.html 2017-08-16 21:13:43 +0000
245+++ templates/wiki/recentchanges.html 2018-09-19 18:09:08 +0000
246@@ -3,6 +3,7 @@
247 {% load humanize i18n %}
248 {% load pagination_tags %}
249 {% load custom_date %}
250+{% load wlprofile_extras %}
251
252 {% block title %}
253 {% trans "Recent Changes" %} - {{ block.super }}
254@@ -42,7 +43,7 @@
255 <a href="{% url 'wiki_article' change.article.title %}">{{ change.article.title }}</a>
256 </td>
257 <td>{{ change.modified|custom_date:user }}</td>
258- <td>{{ change.editor }}</td>
259+ <td>{{ change.editor|user_link }}</td>
260 <td>{{ change.comment }}</td>
261 </tr>
262 {% endfor %}
263
264=== added file 'templates/wlprofile/delete_me.html'
265--- templates/wlprofile/delete_me.html 1970-01-01 00:00:00 +0000
266+++ templates/wlprofile/delete_me.html 2018-09-19 18:09:08 +0000
267@@ -0,0 +1,42 @@
268+{% extends "wlprofile/base.html" %}
269+
270+{% load i18n %}
271+{% load wlprofile_extras %}
272+
273+{% block title %}
274+{% trans "Delete me" %} - {{ block.super }}
275+{% endblock %}
276+
277+{% block content %}
278+<h1>{% trans "Delete your account" %}</h1>
279+
280+<div class="blogEntry">
281+ <h3>Hi {{ user }},</h3>
282+ <p>we are sorry that you want to leave our community <img src="/wlmedia/img/smileys/face-sad.png" alt="Sad smiley"></p>
283+ <h3>What deleting yourself means:</h3>
284+ <ul>
285+ <li>Your account will be deactivated. This means:
286+ <ul>
287+ <li>You will be immediately logged out and unable to log in again.</li>
288+ <li>The Username "{{ user }}" will continue to be reserved, so registering again with that username will not be possible.</li>
289+ </ul>
290+ </li>
291+ <li>Your <a href="{% url 'profile_edit' %}">profile</a> will be deleted and your currently used avatar image will be deleted.</li>
292+ <li>
293+ All your <a href="{% url 'messages_inbox' %}">private messages</a> will be moved into the <a href="{% url 'messages_trash' %}">trash</a>.
294+ They will stay there until such time that the sender or recipient will also delete them.</li>
295+ <li>All your <a href="{% url 'notification_notices' %}">subscriptions</a> will be removed.</li>
296+ <li>Your email address will be removed, so you will not receive any notification mails anymore.</li>
297+ <li>
298+ <b>Nothing</b> that you have posted (forum posts, comments and uploaded maps) will be deleted.
299+ Instead of your user name, the string "{{ DELETED_USERNAME }}" will be shown.</li>
300+ <li>Your online gaming password will be reset.</li>
301+ <li>All dates given in the <a href="{% url 'scheduling_scheduling' %}">Playtime scheduler</a> will be deleted</li>
302+ </ul>
303+ <p class="errormessage"><b>This step can't be undone!</b></p>
304+ <p>
305+ <button type="button" onclick="location.href='{% url 'do_delete' %}';" style="font-weight: normal;">I am sure, please delete me</button>
306+ <button type="button" onclick="location.href='{% url 'profile_edit' %}';">Cancel</button>
307+ </p>
308+</div>
309+{% endblock %}
310
311=== modified file 'templates/wlprofile/edit_profile.html'
312--- templates/wlprofile/edit_profile.html 2016-06-26 09:04:15 +0000
313+++ templates/wlprofile/edit_profile.html 2018-09-19 18:09:08 +0000
314@@ -41,17 +41,12 @@
315 <input type="submit" value="{% trans "Save" %}" />
316 {% csrf_token %}
317 </form>
318- <br />
319- <br />
320- <p>
321- <a href="{% url 'auth_password_change' %}">Change website password</a>
322- <br />
323- You will be redirected to an encrypted connection. The website password is <strong>not</strong> transmitted in cleartext.
324- </p>
325- <p>
326- <a href="{% url 'wlggz_changepw' %}">Change online gaming password</a>
327- <br />
328- <strong class="errormessage">WARNING: The online gaming password is transmitted in cleartext. Do not use your website password!</strong>
329- </p>
330+
331+ <h2>Other options:</h2>
332+ <ul>
333+ <li><a href="{% url 'auth_password_change' %}">Change website password</a></li>
334+ <li><a href="{% url 'wlggz_changepw' %}">Change online gaming password</a></li>
335+ <li><a href="{% url 'delete_me' %}">Delete me</a></li>
336+ </ul>
337 </div>
338 {% endblock %}
339
340=== modified file 'templates/wlprofile/view_profile.html'
341--- templates/wlprofile/view_profile.html 2017-07-27 06:01:32 +0000
342+++ templates/wlprofile/view_profile.html 2018-09-19 18:09:08 +0000
343@@ -4,7 +4,13 @@
344 {% load custom_date %}
345
346 {% block title %}
347-{{ profile.user.username }}'s Profile - {{ block.super }}
348+ {% if not profile.deleted %}
349+ {{ profile.user.username }}'s
350+ {% else %}
351+ Deleted Users Profile
352+ {% endif %}
353+ - Profile
354+ {{ block.super }}
355 {% endblock %}
356
357 {% block content %}
358@@ -13,66 +19,76 @@
359 <a class="invertedColor" href="{% url 'profile_edit' %}">Edit Profile</a>
360 {% endifequal %}
361 </div>
362-<h1>{{ profile.user.username }}'s Profile</h1>
363+<h1>{% if not profile.deleted %}
364+ {{ profile.user.username }}'s
365+ {% else %}
366+ Deleted User
367+ {% endif %}
368+ Profile
369+</h1>
370
371 <div class="blogEntry">
372- <table>
373- <tr>
374- <td>
375- {% if profile.avatar %}
376- <img src="{{ profile.avatar.url }}" alt="Avatar" />
377- {% endif %}
378- </td>
379- <td>
380- {% ifnotequal user profile.user %}
381- <button onclick="window.location.href='{% url 'messages_compose_to' profile.user %}';">
382- <img src="{{ MEDIA_URL }}forum/img/send_pm.png" alt ="" class="middle" />
383- <span class="middle">{% trans "Send PM" %}</span>
384- </button>
385- {% endifnotequal %}
386- </td>
387- </tr>
388- <tr>
389- <td class="grey">Joined:</td>
390- <td>{{ profile.user.date_joined|custom_date:user|title }}</td>
391- </tr>
392- <tr>
393- <td class="grey">Forum Posts:</td>
394- <td>{{ profile.post_count }}</td>
395- </tr>
396- <tr>
397- <td class="grey">Website:</td>
398- <td><a href="{{profile.site}}">{{ profile.site }}</a></td>
399- </tr>
400- <tr>
401- <td class="grey">Location:</td>
402- <td>{{ profile.location }}</td>
403- </tr>
404- <tr>
405- <td class="grey">Jabber:</td>
406- <td>{{ profile.jabber }}</td>
407- </tr>
408- <tr>
409- <td class="grey">ICQ:</td>
410- <td>{{ profile.icq }}</td>
411- </tr>
412- <tr>
413- <td class="grey">MSN:</td>
414- <td>{{ profile.msn }}</td>
415- </tr>
416- <tr>
417- <td class="grey">AIM:</td>
418- <td>{{ profile.aim }}</td>
419- </tr>
420- <tr>
421- <td class="grey">Yahoo:</td>
422- <td>{{ profile.yahoo }}</td>
423- </tr>
424- <tr>
425- <td class="grey">Signature:</td>
426- <td>{{ profile.signature|urlize|linebreaks }}</td>
427- </tr>
428- </table>
429+ {% if not profile.deleted %}
430+ <table>
431+ <tr>
432+ <td>
433+ {% if profile.avatar %}
434+ <img src="{{ profile.avatar.url }}" alt="Avatar" />
435+ {% endif %}
436+ </td>
437+ <td>
438+ {% ifnotequal user profile.user %}
439+ <button onclick="window.location.href='{% url 'messages_compose_to' profile.user %}';">
440+ <img src="{{ MEDIA_URL }}forum/img/send_pm.png" alt ="" class="middle" />
441+ <span class="middle">{% trans "Send PM" %}</span>
442+ </button>
443+ {% endifnotequal %}
444+ </td>
445+ </tr>
446+ <tr>
447+ <td class="grey">Joined:</td>
448+ <td>{{ profile.user.date_joined|custom_date:user|title }}</td>
449+ </tr>
450+ <tr>
451+ <td class="grey">Forum Posts:</td>
452+ <td>{{ profile.post_count }}</td>
453+ </tr>
454+ <tr>
455+ <td class="grey">Website:</td>
456+ <td><a href="{{profile.site}}">{{ profile.site }}</a></td>
457+ </tr>
458+ <tr>
459+ <td class="grey">Location:</td>
460+ <td>{{ profile.location }}</td>
461+ </tr>
462+ <tr>
463+ <td class="grey">Jabber:</td>
464+ <td>{{ profile.jabber }}</td>
465+ </tr>
466+ <tr>
467+ <td class="grey">ICQ:</td>
468+ <td>{{ profile.icq }}</td>
469+ </tr>
470+ <tr>
471+ <td class="grey">MSN:</td>
472+ <td>{{ profile.msn }}</td>
473+ </tr>
474+ <tr>
475+ <td class="grey">AIM:</td>
476+ <td>{{ profile.aim }}</td>
477+ </tr>
478+ <tr>
479+ <td class="grey">Yahoo:</td>
480+ <td>{{ profile.yahoo }}</td>
481+ </tr>
482+ <tr>
483+ <td class="grey">Signature:</td>
484+ <td>{{ profile.signature|urlize|linebreaks }}</td>
485+ </tr>
486+ </table>
487+ {% else %}
488+ <p>This user has deleted his data ...</p>
489+ {% endif %}
490 </div>
491
492 {% endblock %}
493
494=== modified file 'urls.py'
495--- urls.py 2018-05-24 19:17:36 +0000
496+++ urls.py 2018-09-19 18:09:08 +0000
497@@ -4,7 +4,6 @@
498 from django.contrib import admin
499 admin.autodiscover()
500
501-from news.feeds import NewsPostsFeed
502 from django.views.generic.base import RedirectView
503 from django.views.generic import TemplateView
504 from django.contrib.syndication.views import Feed
505@@ -28,9 +27,6 @@
506 url(r'^accounts/', include('registration.backends.hmac.urls')),
507 url('^', include('django.contrib.auth.urls')),
508
509- # Feed for news
510- url(r'^feeds/news/$', NewsPostsFeed()),
511-
512 # Formerly 3rd party
513 url(r'^notification/', include('notification.urls')),
514
515
516=== modified file 'wiki/feeds.py'
517--- wiki/feeds.py 2016-12-13 18:28:51 +0000
518+++ wiki/feeds.py 2018-09-19 18:09:08 +0000
519@@ -19,19 +19,12 @@
520
521 def item_pubdate(self, item):
522 """Return the item's pubdate.
523-
524+
525 It's this modified date
526-
527+
528 """
529 return item.modified
530
531- def item_author_name(self, item):
532- """Takes the object returned by items(), and returns the feeds's
533- auhor's name as a Python string."""
534- if item.is_anonymous_change():
535- return 'Anonymous'
536- return item.editor.username
537-
538 # Validated through http://validator.w3.org/feed/
539
540
541@@ -84,10 +77,3 @@
542
543 def item_updateddate(self, item):
544 return item.modified
545-
546- def item_author_name(self, item):
547- """Takes the object returned by items(), and returns the feeds's
548- auhor's name as a Python string."""
549- if item.is_anonymous_change():
550- return 'Anonymous'
551- return item.editor.username
552
553=== added file 'wlprofile/context_processors.py'
554--- wlprofile/context_processors.py 1970-01-01 00:00:00 +0000
555+++ wlprofile/context_processors.py 2018-09-19 18:09:08 +0000
556@@ -0,0 +1,6 @@
557+from django.conf import settings
558+
559+
560+def deleted_user_data(request):
561+ context = {'DELETED_USERNAME': settings.DELETED_USERNAME}
562+ return context
563
564=== added file 'wlprofile/migrations/0002_profile_deleted.py'
565--- wlprofile/migrations/0002_profile_deleted.py 1970-01-01 00:00:00 +0000
566+++ wlprofile/migrations/0002_profile_deleted.py 2018-09-19 18:09:08 +0000
567@@ -0,0 +1,20 @@
568+# -*- coding: utf-8 -*-
569+# Generated by Django 1.11.12 on 2018-09-13 21:23
570+from __future__ import unicode_literals
571+
572+from django.db import migrations, models
573+
574+
575+class Migration(migrations.Migration):
576+
577+ dependencies = [
578+ ('wlprofile', '0001_initial'),
579+ ]
580+
581+ operations = [
582+ migrations.AddField(
583+ model_name='profile',
584+ name='deleted',
585+ field=models.BooleanField(default=False),
586+ ),
587+ ]
588
589=== modified file 'wlprofile/models.py'
590--- wlprofile/models.py 2016-12-13 18:28:51 +0000
591+++ wlprofile/models.py 2018-09-19 18:09:08 +0000
592@@ -48,6 +48,7 @@
593 upload_to='wlprofile/avatars/', width=settings.AVATAR_WIDTH, height=settings.AVATAR_HEIGHT)
594 show_signatures = models.BooleanField(
595 _('Show signatures'), blank=True, default=True)
596+ deleted = models.BooleanField(default=False)
597
598 class Meta:
599 verbose_name = _('Profile')
600
601=== modified file 'wlprofile/templatetags/wlprofile_extras.py'
602--- wlprofile/templatetags/wlprofile_extras.py 2018-04-08 14:40:17 +0000
603+++ wlprofile/templatetags/wlprofile_extras.py 2018-09-19 18:09:08 +0000
604@@ -12,12 +12,19 @@
605 from django import template
606 from django.urls import reverse
607 from django.utils.safestring import mark_safe
608+from django.contrib.auth.models import User
609+from django.conf import settings
610
611 register = template.Library()
612
613
614 @register.filter
615 def user_link(user):
616- data = u'<a href="%s">%s</a>' % (
617- reverse('profile_view', args=[user.username]), user.username)
618+
619+ if user.is_authenticated and user.wlprofile.deleted:
620+ # Check for is_authenticated is needed for threadedcomments reply_to.js
621+ return mark_safe('<span title="This user has left our community">{}</span>'.format(settings.DELETED_USERNAME))
622+ else:
623+ data = u'<a href="%s">%s</a>' % (
624+ reverse('profile_view', args=[user.username]), user.username)
625 return mark_safe(data)
626
627=== modified file 'wlprofile/urls.py'
628--- wlprofile/urls.py 2016-12-13 18:28:51 +0000
629+++ wlprofile/urls.py 2018-09-19 18:09:08 +0000
630@@ -14,6 +14,8 @@
631
632 urlpatterns = [
633 url(r'^edit/$', views.edit, name='profile_edit'),
634+ url(r'^delete/$', views.delete_me, name='delete_me'),
635+ url(r'^do_delete/$', views.do_delete, name='do_delete'),
636 url(r'^(?P<user>.*)/$', views.view, name='profile_view'),
637 url(r'^$', views.view, name='profile_view'),
638 ]
639
640=== modified file 'wlprofile/views.py'
641--- wlprofile/views.py 2018-05-30 16:59:16 +0000
642+++ wlprofile/views.py 2018-09-19 18:09:08 +0000
643@@ -4,14 +4,102 @@
644 from django.urls import reverse
645 from django.contrib.auth.decorators import login_required
646 from django.contrib.auth.models import User
647-from django.shortcuts import render
648+from django.shortcuts import render, redirect
649 from django.http import HttpResponseRedirect
650 from django.shortcuts import get_object_or_404
651+from django.conf import settings
652
653 from forms import EditProfileForm
654-import settings
655-
656-# Settings
657+
658+
659+@login_required
660+def delete_me(request):
661+ """Show a page to inform the user what deleting means."""
662+
663+ context = {
664+ 'user': request.user
665+ }
666+ return render(request, 'wlprofile/delete_me.html',
667+ context)
668+
669+
670+@login_required
671+def do_delete(request):
672+ """Delete user specific data.
673+
674+ We can't really delete a user who has posted some valid posts (no spam) because otherwise
675+ we have foreign keys constraints in the database. All we can do is to do some cleanup and
676+ anonymization.
677+ """
678+
679+ from django.contrib.auth import logout
680+ from wlprofile.models import Profile
681+ from notification.models import NoticeSetting
682+
683+ user = get_object_or_404(User, username=request.user)
684+
685+ # Log the user out. We do this as early as possible but after
686+ # we get the User object.
687+ logout(request)
688+
689+ # Clean possible Playtime availabilities
690+ from wlscheduling.models import Availabilities
691+ try:
692+ events = Availabilities.objects.filter(user=user)
693+ for event in events:
694+ event.delete()
695+ except:
696+ pass
697+
698+ # Clean the Online gaming password
699+ from wlggz.models import GGZAuth
700+ try:
701+ ggz_user = GGZAuth.objects.get(user=user)
702+ ggz_user.delete()
703+ except:
704+ pass
705+
706+ # Clean the profile
707+ profile = user.wlprofile
708+ upload_to = Profile._meta.get_field('avatar').upload_to
709+
710+ if upload_to in profile.avatar.name:
711+ # Delete the avatar file
712+ profile.avatar.delete()
713+
714+ # Delete the profile and recreate it to get a clean profile page
715+ # We create a new one to have the anymous.png as avatar
716+ profile.delete()
717+ profile = Profile(user=user, deleted=True)
718+ profile.save()
719+
720+ # Deactivate all subscriptions
721+ notice_settings = NoticeSetting.objects.filter(user=user)
722+ for setting in notice_settings:
723+ setting.send = False
724+ setting.save()
725+
726+ # Put all PMs in the trash of the user. They stay as long in the trash until the sender or recipient
727+ # has also put the message in the trash.
728+ from django_messages.models import Message
729+ from datetime import datetime
730+ messages = Message.objects.inbox_for(user)
731+ for message in messages:
732+ message.recipient_deleted_at = datetime.now()
733+ message.save()
734+ messages = Message.objects.outbox_for(user)
735+ for message in messages:
736+ message.sender_deleted_at = datetime.now()
737+ message.save()
738+
739+ # Do some settings in django.auth.User
740+ user.is_active = False
741+ user.is_staff = False
742+ user.is_superuser = False
743+ user.email = settings.DELETED_MAIL_ADDRESS
744+ user.save()
745+
746+ return redirect('mainpage')
747
748
749 @login_required

Subscribers

People subscribed via source and target branches