Merge lp:~widelands-dev/widelands-website/delete_user into lp:widelands-website
- delete_user
- Merge into trunk
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 | ||||||||
Related bugs: |
|
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.
- Show the value of settings.
- 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_
Additional cleanup:
- Removed unused functions for feeds
GunChleoc (gunchleoc) wrote : | # |
How about calling the deleted user "Anonymous"?
GunChleoc (gunchleoc) : | # |
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:/
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.
kaputtnik (franku) : | # |
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.
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.
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.
kaputtnik (franku) wrote : | # |
Merged and deployed, sorry for the server errors... had forgotten to run the migrate command :-D
Preview Diff
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 |
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_MAILADDRES S is empty an email isn't sent, but there is no error message shown to the user.