2
2
3
3
from __future__ import absolute_import
4
4
from django .contrib import admin
5
+ from django .contrib import messages
6
+ from django .contrib .admin .actions import delete_selected
7
+ from django .utils .translation import ugettext_lazy as _
5
8
from guardian .admin import GuardedModelAdmin
6
9
10
+ from readthedocs .core .models import UserProfile
7
11
from readthedocs .core .utils import broadcast
8
12
from readthedocs .builds .models import Version
9
13
from readthedocs .redirects .models import Redirect
12
16
from .notifications import ResourceUsageNotification
13
17
from .models import (Project , ImportedFile ,
14
18
ProjectRelationship , EmailHook , WebHook , Domain )
15
- from .tasks import clear_artifacts
19
+ from .tasks import remove_dir
16
20
17
21
18
22
class ProjectSendNotificationView (SendNotificationView ):
@@ -67,20 +71,45 @@ class DomainInline(admin.TabularInline):
67
71
# return instance.click_ratio * 100
68
72
69
73
74
+ class ProjectOwnerBannedFilter (admin .SimpleListFilter ):
75
+
76
+ """Filter for projects with banned owners
77
+
78
+ There are problems adding `users__profile__banned` to the `list_filter`
79
+ attribute, so we'll create a basic filter to capture banned owners.
80
+ """
81
+
82
+ title = 'project owner banned'
83
+ parameter_name = 'project_owner_banned'
84
+
85
+ OWNER_BANNED = 'true'
86
+
87
+ def lookups (self , request , model_admin ):
88
+ return (
89
+ (self .OWNER_BANNED , _ ('Yes' )),
90
+ )
91
+
92
+ def queryset (self , request , queryset ):
93
+ if self .value () == self .OWNER_BANNED :
94
+ return queryset .filter (users__profile__banned = True )
95
+ return queryset
96
+
97
+
70
98
class ProjectAdmin (GuardedModelAdmin ):
71
99
72
100
"""Project model admin view"""
73
101
74
102
prepopulated_fields = {'slug' : ('name' ,)}
75
103
list_display = ('name' , 'repo' , 'repo_type' , 'allow_comments' , 'featured' , 'theme' )
76
104
list_filter = ('repo_type' , 'allow_comments' , 'featured' , 'privacy_level' ,
77
- 'documentation_type' , 'programming_language' )
105
+ 'documentation_type' , 'programming_language' ,
106
+ ProjectOwnerBannedFilter )
78
107
list_editable = ('featured' ,)
79
108
search_fields = ('slug' , 'repo' )
80
109
inlines = [ProjectRelationshipInline , RedirectInline ,
81
110
VersionInline , DomainInline ]
82
111
raw_id_fields = ('users' , 'main_language_project' )
83
- actions = ['send_owner_email' , 'flag_project' , 'flag_project_ban_owner ' ]
112
+ actions = ['send_owner_email' , 'ban_owner ' ]
84
113
85
114
def send_owner_email (self , request , queryset ):
86
115
view = ProjectSendNotificationView .as_view (
@@ -90,26 +119,45 @@ def send_owner_email(self, request, queryset):
90
119
91
120
send_owner_email .short_description = 'Notify project owners'
92
121
93
- def flag_project (self , request , queryset ):
94
- """Flag project as spam
122
+ def ban_owner (self , request , queryset ):
123
+ """Ban project owner
95
124
96
- Delete project and wipe files from web servers
125
+ This will only ban single owners, because a malicious user could add a
126
+ user as a co-owner of the project. We don't want to induce and
127
+ collatoral damage when flagging users.
97
128
"""
98
- pass
99
-
100
- flag_project .short_description = flag_project .__doc__ .splitlines ()[0 ]
101
-
102
- def flag_project_ban_owner (self , request , queryset ):
103
- """Flag project as spam and ban owner
104
-
105
- This will only ban single owners. Because a malicious user could add a
106
- user as a co-owner of the project, we don't want to induce and
107
- collatoral damage when blowing up projects.
108
- """
109
- pass
110
-
111
- flag_project_ban_owners .short_description = (flag_project_ban_owners
112
- .__doc__ .splitlines ()[0 ])
129
+ total = 0
130
+ for project in queryset :
131
+ if project .users .count () == 1 :
132
+ count = (UserProfile .objects
133
+ .filter (user__projects = project )
134
+ .update (banned = True ))
135
+ total += count
136
+ else :
137
+ messages .add_message (request , messages .ERROR ,
138
+ 'Project has multiple owners: {0}' .format (project ))
139
+ if total == 0 :
140
+ messages .add_message (request , messages .ERROR , 'No users banned' )
141
+ else :
142
+ messages .add_message (request , messages .INFO ,
143
+ 'Banned {0} user(s)' .format (total ))
144
+
145
+ ban_owner .short_description = 'Ban project owner'
146
+
147
+ def delete_selected_and_artifacts (self , request , queryset ):
148
+ if request .POST .get ('post' ):
149
+ for project in queryset :
150
+ broadcast (type = 'app' , task = remove_dir , args = [project .doc_path ])
151
+ return delete_selected (self , request , queryset )
152
+
153
+ def get_actions (self , request ):
154
+ actions = super (ProjectAdmin , self ).get_actions (request )
155
+ actions ['delete_selected' ] = (
156
+ self .__class__ .delete_selected_and_artifacts ,
157
+ 'delete_selected' ,
158
+ delete_selected .short_description
159
+ )
160
+ return actions
113
161
114
162
115
163
class ImportedFileAdmin (admin .ModelAdmin ):
0 commit comments