We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Better User deletion
With #3175 we now have a way for users to mark themselves as wanting to be deleted, now we need to actually delete them. This could be done inline during delete, or as a batch job that runs after the web view returns.

We want to actually **delete** the User instance, but we need to confirm a couple things:

* Projects that the user _only_ owns are deleted. This is a [M2m](https://github.com/rtfd/readthedocs.org/blob/master/readthedocs/projects/models.py#L83), so we need to either *remove* them if there are multiple users, or *delete* the project if they are the only owner, so we don't end up with orphaned projects. 


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `readthedocs/profiles/views.py`
Content:
```
1 """Views for creating, editing and viewing site-specific user profiles."""
2 
3 from __future__ import absolute_import
4 
5 from django.contrib import messages
6 from django.contrib.auth import logout
7 from django.contrib.auth.decorators import login_required
8 from django.contrib.auth.models import User
9 from django.core.exceptions import ObjectDoesNotExist
10 from django.core.urlresolvers import reverse
11 from django.http import Http404
12 from django.http import HttpResponseRedirect
13 from django.shortcuts import get_object_or_404, render, redirect
14 from django.shortcuts import render_to_response
15 from django.template import RequestContext
16 
17 from readthedocs.core.forms import UserDeleteForm
18 
19 
20 def create_profile(request, form_class, success_url=None,
21                    template_name='profiles/private/create_profile.html',
22                    extra_context=None):
23     """
24     Create a profile for the current user, if one doesn't already exist.
25 
26     If the user already has a profile, a redirect will be issued to the
27     :view:`profiles.views.edit_profile` view.
28 
29     **Optional arguments:**
30 
31     ``extra_context``
32         A dictionary of variables to add to the template context. Any
33         callable object in this dictionary will be called to produce
34         the end result which appears in the context.
35 
36     ``form_class``
37         The form class to use for validating and creating the user
38         profile. This form class must define a method named
39         ``save()``, implementing the same argument signature as the
40         ``save()`` method of a standard Django ``ModelForm`` (this
41         view will call ``save(commit=False)`` to obtain the profile
42         object, and fill in the user before the final save). If the
43         profile object includes many-to-many relations, the convention
44         established by ``ModelForm`` of using a method named
45         ``save_m2m()`` will be used, and so your form class should
46         also define this method.
47 
48     ``success_url``
49         The URL to redirect to after successful profile creation. If
50         this argument is not supplied, this will default to the URL of
51         :view:`profiles.views.profile_detail` for the newly-created
52         profile object.
53 
54     ``template_name``
55         The template to use when displaying the profile-creation
56         form. If not supplied, this will default to
57         :template:`profiles/create_profile.html`.
58 
59     **Context:**
60 
61     ``form``
62         The profile-creation form.
63 
64     **Template:**
65 
66     ``template_name`` keyword argument, or
67     :template:`profiles/create_profile.html`.
68 
69     """
70     try:
71         profile_obj = request.user.profile
72         return HttpResponseRedirect(reverse('profiles_edit_profile'))
73     except ObjectDoesNotExist:
74         pass
75 
76     #
77     # We set up success_url here, rather than as the default value for
78     # the argument. Trying to do it as the argument's default would
79     # mean evaluating the call to reverse() at the time this module is
80     # first imported, which introduces a circular dependency: to
81     # perform the reverse lookup we need access to profiles/urls.py,
82     # but profiles/urls.py in turn imports this module.
83     #
84 
85     if success_url is None:
86         success_url = reverse('profiles_profile_detail',
87                               kwargs={'username': request.user.username})
88     if request.method == 'POST':
89         form = form_class(data=request.POST, files=request.FILES)
90         if form.is_valid():
91             profile_obj = form.save(commit=False)
92             profile_obj.user = request.user
93             profile_obj.save()
94             if hasattr(form, 'save_m2m'):
95                 form.save_m2m()
96             return HttpResponseRedirect(success_url)
97     else:
98         form = form_class()
99 
100     if extra_context is None:
101         extra_context = {}
102     context = RequestContext(request)
103     for key, value in list(extra_context.items()):
104         context[key] = (value() if callable(value) else value)
105 
106     return render_to_response(template_name,
107                               {'form': form},
108                               context_instance=context)
109 create_profile = login_required(create_profile)
110 
111 
112 def edit_profile(request, form_class, success_url=None,
113                  template_name='profiles/private/edit_profile.html',
114                  extra_context=None):
115     """
116     Edit the current user's profile.
117 
118     If the user does not already have a profile, a redirect will be issued to
119     the :view:`profiles.views.create_profile` view.
120 
121     **Optional arguments:**
122 
123     ``extra_context``
124         A dictionary of variables to add to the template context. Any
125         callable object in this dictionary will be called to produce
126         the end result which appears in the context.
127 
128     ``form_class``
129         The form class to use for validating and editing the user
130         profile. This form class must operate similarly to a standard
131         Django ``ModelForm`` in that it must accept an instance of the
132         object to be edited as the keyword argument ``instance`` to
133         its constructor, and it must implement a method named
134         ``save()`` which will save the updates to the object.
135 
136     ``success_url``
137         The URL to redirect to following a successful edit. If not
138         specified, this will default to the URL of
139         :view:`profiles.views.profile_detail` for the profile object
140         being edited.
141 
142     ``template_name``
143         The template to use when displaying the profile-editing
144         form. If not specified, this will default to
145         :template:`profiles/edit_profile.html`.
146 
147     **Context:**
148 
149     ``form``
150         The form for editing the profile.
151 
152     ``profile``
153          The user's current profile.
154 
155     **Template:**
156 
157     ``template_name`` keyword argument or
158     :template:`profiles/edit_profile.html`.
159 
160     """
161     try:
162         profile_obj = request.user.profile
163     except ObjectDoesNotExist:
164         return HttpResponseRedirect(reverse('profiles_profile_create'))
165 
166     if success_url is None:
167         success_url = reverse('profiles_profile_detail',
168                               kwargs={'username': request.user.username})
169     if request.method == 'POST':
170         form = form_class(data=request.POST, files=request.FILES, instance=profile_obj)
171         if form.is_valid():
172             form.save()
173             return HttpResponseRedirect(success_url)
174     else:
175         form = form_class(instance=profile_obj)
176 
177     if extra_context is None:
178         extra_context = {}
179     context = RequestContext(request)
180     for key, value in list(extra_context.items()):
181         context[key] = (value() if callable(value) else value)
182 
183     return render_to_response(template_name, {
184         'form': form,
185         'profile': profile_obj,
186         'user': profile_obj.user,
187     }, context_instance=context)
188 edit_profile = login_required(edit_profile)
189 
190 
191 @login_required()
192 def delete_account(request):
193     form = UserDeleteForm()
194     template_name = 'profiles/private/delete_account.html'
195 
196     if request.method == 'POST':
197         form = UserDeleteForm(instance=request.user, data=request.POST)
198         if form.is_valid():
199 
200             # Do not delete the account permanently because it may create disaster
201             # Inactive the user instead.
202             request.user.is_active = False
203             request.user.save()
204             logout(request)
205             messages.info(request, 'You have successfully deleted your account')
206 
207             return redirect('homepage')
208 
209     return render(request, template_name, {'form': form})
210 
211 
212 def profile_detail(request, username, public_profile_field=None,
213                    template_name='profiles/public/profile_detail.html',
214                    extra_context=None):
215     """
216     Detail view of a user's profile.
217 
218     If the user has not yet created a profile, ``Http404`` will be
219     raised.
220 
221     **Required arguments:**
222 
223     ``username``
224         The username of the user whose profile is being displayed.
225 
226     **Optional arguments:**
227 
228     ``extra_context``
229         A dictionary of variables to add to the template context. Any
230         callable object in this dictionary will be called to produce
231         the end result which appears in the context.
232 
233     ``public_profile_field``
234         The name of a ``BooleanField`` on the profile model; if the
235         value of that field on the user's profile is ``False``, the
236         ``profile`` variable in the template will be ``None``. Use
237         this feature to allow users to mark their profiles as not
238         being publicly viewable.
239 
240         If this argument is not specified, it will be assumed that all
241         users' profiles are publicly viewable.
242 
243     ``template_name``
244         The name of the template to use for displaying the profile. If
245         not specified, this will default to
246         :template:`profiles/profile_detail.html`.
247 
248     **Context:**
249 
250     ``profile``
251         The user's profile, or ``None`` if the user's profile is not
252         publicly viewable (see the description of
253         ``public_profile_field`` above).
254 
255     **Template:**
256 
257     ``template_name`` keyword argument or
258     :template:`profiles/profile_detail.html`.
259 
260     """
261     user = get_object_or_404(User, username=username)
262     try:
263         profile_obj = user.profile
264     except ObjectDoesNotExist:
265         raise Http404
266     if public_profile_field is not None and \
267        not getattr(profile_obj, public_profile_field):
268         profile_obj = None
269 
270     if extra_context is None:
271         extra_context = {}
272     context = RequestContext(request)
273     for key, value in list(extra_context.items()):
274         context[key] = (value() if callable(value) else value)
275 
276     return render_to_response(template_name,
277                               {'profile': profile_obj},
278                               context_instance=context)
279
```

Path: `readthedocs/core/signals.py`
Content:
```
1 """Signal handling for core app."""
2 
3 from __future__ import absolute_import
4 
5 import logging
6 
7 from corsheaders import signals
8 from django.dispatch import Signal
9 from django.db.models import Q
10 from future.backports.urllib.parse import urlparse
11 
12 from readthedocs.projects.models import Project, Domain
13 
14 
15 log = logging.getLogger(__name__)
16 
17 WHITELIST_URLS = ['/api/v2/footer_html', '/api/v2/search', '/api/v2/docsearch']
18 
19 
20 webhook_github = Signal(providing_args=['project', 'data', 'event'])
21 webhook_gitlab = Signal(providing_args=['project', 'data', 'event'])
22 webhook_bitbucket = Signal(providing_args=['project', 'data', 'event'])
23 
24 
25 def decide_if_cors(sender, request, **kwargs):  # pylint: disable=unused-argument
26     """
27     Decide whether a request should be given CORS access.
28 
29     This checks that:
30     * The URL is whitelisted against our CORS-allowed domains
31     * The Domain exists in our database, and belongs to the project being queried.
32 
33     Returns True when a request should be given CORS access.
34     """
35     if 'HTTP_ORIGIN' not in request.META:
36         return False
37     host = urlparse(request.META['HTTP_ORIGIN']).netloc.split(':')[0]
38     valid_url = False
39     for url in WHITELIST_URLS:
40         if request.path_info.startswith(url):
41             valid_url = True
42 
43     if valid_url:
44         project_slug = request.GET.get('project', None)
45         try:
46             project = Project.objects.get(slug=project_slug)
47         except Project.DoesNotExist:
48             log.warning(
49                 'Invalid project passed to domain. [{project}:{domain}'.format(
50                     project=project_slug,
51                     domain=host,
52                 )
53             )
54             return False
55 
56         domain = Domain.objects.filter(
57             Q(domain__icontains=host),
58             Q(project=project) | Q(project__subprojects__child=project)
59         )
60         if domain.exists():
61             return True
62 
63     return False
64 
65 signals.check_request_enabled.connect(decide_if_cors)
66
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/readthedocs/core/signals.py b/readthedocs/core/signals.py
--- a/readthedocs/core/signals.py
+++ b/readthedocs/core/signals.py
@@ -5,13 +5,15 @@
 import logging
 
 from corsheaders import signals
+from django.conf import settings
+from django.db.models.signals import pre_delete
 from django.dispatch import Signal
-from django.db.models import Q
+from django.db.models import Q, Count
+from django.dispatch import receiver
 from future.backports.urllib.parse import urlparse
 
 from readthedocs.projects.models import Project, Domain
 
-
 log = logging.getLogger(__name__)
 
 WHITELIST_URLS = ['/api/v2/footer_html', '/api/v2/search', '/api/v2/docsearch']
@@ -62,4 +64,20 @@
 
     return False
 
+
+@receiver(pre_delete, sender=settings.AUTH_USER_MODEL)
+def delete_projects_and_organizations(sender, instance, *args, **kwargs):
+    # Here we count the owner list from the projects that the user own
+    # Then exclude the projects where there are more than one owner
+    projects = instance.projects.all().annotate(num_users=Count('users')).exclude(num_users__gt=1)
+
+    # Here we count the users list from the organization that the user belong
+    # Then exclude the organizations where there are more than one user
+    oauth_organizations = (instance.oauth_organizations.annotate(num_users=Count('users'))
+                                                       .exclude(num_users__gt=1))
+
+    projects.delete()
+    oauth_organizations.delete()
+
+
 signals.check_request_enabled.connect(decide_if_cors)
diff --git a/readthedocs/profiles/views.py b/readthedocs/profiles/views.py
--- a/readthedocs/profiles/views.py
+++ b/readthedocs/profiles/views.py
@@ -196,11 +196,9 @@
     if request.method == 'POST':
         form = UserDeleteForm(instance=request.user, data=request.POST)
         if form.is_valid():
-
-            # Do not delete the account permanently because it may create disaster
-            # Inactive the user instead.
-            request.user.is_active = False
-            request.user.save()
+            # Delete the user permanently
+            # It will also delete some projects where he is the only owner
+            request.user.delete()
             logout(request)
             messages.info(request, 'You have successfully deleted your account')

{"golden_diff": "diff --git a/readthedocs/core/signals.py b/readthedocs/core/signals.py\n--- a/readthedocs/core/signals.py\n+++ b/readthedocs/core/signals.py\n@@ -5,13 +5,15 @@\n import logging\n \n from corsheaders import signals\n+from django.conf import settings\n+from django.db.models.signals import pre_delete\n from django.dispatch import Signal\n-from django.db.models import Q\n+from django.db.models import Q, Count\n+from django.dispatch import receiver\n from future.backports.urllib.parse import urlparse\n \n from readthedocs.projects.models import Project, Domain\n \n-\n log = logging.getLogger(__name__)\n \n WHITELIST_URLS = ['/api/v2/footer_html', '/api/v2/search', '/api/v2/docsearch']\n@@ -62,4 +64,20 @@\n \n     return False\n \n+\n+@receiver(pre_delete, sender=settings.AUTH_USER_MODEL)\n+def delete_projects_and_organizations(sender, instance, *args, **kwargs):\n+    # Here we count the owner list from the projects that the user own\n+    # Then exclude the projects where there are more than one owner\n+    projects = instance.projects.all().annotate(num_users=Count('users')).exclude(num_users__gt=1)\n+\n+    # Here we count the users list from the organization that the user belong\n+    # Then exclude the organizations where there are more than one user\n+    oauth_organizations = (instance.oauth_organizations.annotate(num_users=Count('users'))\n+                                                       .exclude(num_users__gt=1))\n+\n+    projects.delete()\n+    oauth_organizations.delete()\n+\n+\n signals.check_request_enabled.connect(decide_if_cors)\ndiff --git a/readthedocs/profiles/views.py b/readthedocs/profiles/views.py\n--- a/readthedocs/profiles/views.py\n+++ b/readthedocs/profiles/views.py\n@@ -196,11 +196,9 @@\n     if request.method == 'POST':\n         form = UserDeleteForm(instance=request.user, data=request.POST)\n         if form.is_valid():\n-\n-            # Do not delete the account permanently because it may create disaster\n-            # Inactive the user instead.\n-            request.user.is_active = False\n-            request.user.save()\n+            # Delete the user permanently\n+            # It will also delete some projects where he is the only owner\n+            request.user.delete()\n             logout(request)\n             messages.info(request, 'You have successfully deleted your account')\n", "issue": "Better User deletion\nWith #3175 we now have a way for users to mark themselves as wanting to be deleted, now we need to actually delete them. This could be done inline during delete, or as a batch job that runs after the web view returns.\r\n\r\nWe want to actually **delete** the User instance, but we need to confirm a couple things:\r\n\r\n* Projects that the user _only_ owns are deleted. This is a [M2m](https://github.com/rtfd/readthedocs.org/blob/master/readthedocs/projects/models.py#L83), so we need to either *remove* them if there are multiple users, or *delete* the project if they are the only owner, so we don't end up with orphaned projects. \r\n\n", "before_files": [{"content": "\"\"\"Views for creating, editing and viewing site-specific user profiles.\"\"\"\n\nfrom __future__ import absolute_import\n\nfrom django.contrib import messages\nfrom django.contrib.auth import logout\nfrom django.contrib.auth.decorators import login_required\nfrom django.contrib.auth.models import User\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.core.urlresolvers import reverse\nfrom django.http import Http404\nfrom django.http import HttpResponseRedirect\nfrom django.shortcuts import get_object_or_404, render, redirect\nfrom django.shortcuts import render_to_response\nfrom django.template import RequestContext\n\nfrom readthedocs.core.forms import UserDeleteForm\n\n\ndef create_profile(request, form_class, success_url=None,\n                   template_name='profiles/private/create_profile.html',\n                   extra_context=None):\n    \"\"\"\n    Create a profile for the current user, if one doesn't already exist.\n\n    If the user already has a profile, a redirect will be issued to the\n    :view:`profiles.views.edit_profile` view.\n\n    **Optional arguments:**\n\n    ``extra_context``\n        A dictionary of variables to add to the template context. Any\n        callable object in this dictionary will be called to produce\n        the end result which appears in the context.\n\n    ``form_class``\n        The form class to use for validating and creating the user\n        profile. This form class must define a method named\n        ``save()``, implementing the same argument signature as the\n        ``save()`` method of a standard Django ``ModelForm`` (this\n        view will call ``save(commit=False)`` to obtain the profile\n        object, and fill in the user before the final save). If the\n        profile object includes many-to-many relations, the convention\n        established by ``ModelForm`` of using a method named\n        ``save_m2m()`` will be used, and so your form class should\n        also define this method.\n\n    ``success_url``\n        The URL to redirect to after successful profile creation. If\n        this argument is not supplied, this will default to the URL of\n        :view:`profiles.views.profile_detail` for the newly-created\n        profile object.\n\n    ``template_name``\n        The template to use when displaying the profile-creation\n        form. If not supplied, this will default to\n        :template:`profiles/create_profile.html`.\n\n    **Context:**\n\n    ``form``\n        The profile-creation form.\n\n    **Template:**\n\n    ``template_name`` keyword argument, or\n    :template:`profiles/create_profile.html`.\n\n    \"\"\"\n    try:\n        profile_obj = request.user.profile\n        return HttpResponseRedirect(reverse('profiles_edit_profile'))\n    except ObjectDoesNotExist:\n        pass\n\n    #\n    # We set up success_url here, rather than as the default value for\n    # the argument. Trying to do it as the argument's default would\n    # mean evaluating the call to reverse() at the time this module is\n    # first imported, which introduces a circular dependency: to\n    # perform the reverse lookup we need access to profiles/urls.py,\n    # but profiles/urls.py in turn imports this module.\n    #\n\n    if success_url is None:\n        success_url = reverse('profiles_profile_detail',\n                              kwargs={'username': request.user.username})\n    if request.method == 'POST':\n        form = form_class(data=request.POST, files=request.FILES)\n        if form.is_valid():\n            profile_obj = form.save(commit=False)\n            profile_obj.user = request.user\n            profile_obj.save()\n            if hasattr(form, 'save_m2m'):\n                form.save_m2m()\n            return HttpResponseRedirect(success_url)\n    else:\n        form = form_class()\n\n    if extra_context is None:\n        extra_context = {}\n    context = RequestContext(request)\n    for key, value in list(extra_context.items()):\n        context[key] = (value() if callable(value) else value)\n\n    return render_to_response(template_name,\n                              {'form': form},\n                              context_instance=context)\ncreate_profile = login_required(create_profile)\n\n\ndef edit_profile(request, form_class, success_url=None,\n                 template_name='profiles/private/edit_profile.html',\n                 extra_context=None):\n    \"\"\"\n    Edit the current user's profile.\n\n    If the user does not already have a profile, a redirect will be issued to\n    the :view:`profiles.views.create_profile` view.\n\n    **Optional arguments:**\n\n    ``extra_context``\n        A dictionary of variables to add to the template context. Any\n        callable object in this dictionary will be called to produce\n        the end result which appears in the context.\n\n    ``form_class``\n        The form class to use for validating and editing the user\n        profile. This form class must operate similarly to a standard\n        Django ``ModelForm`` in that it must accept an instance of the\n        object to be edited as the keyword argument ``instance`` to\n        its constructor, and it must implement a method named\n        ``save()`` which will save the updates to the object.\n\n    ``success_url``\n        The URL to redirect to following a successful edit. If not\n        specified, this will default to the URL of\n        :view:`profiles.views.profile_detail` for the profile object\n        being edited.\n\n    ``template_name``\n        The template to use when displaying the profile-editing\n        form. If not specified, this will default to\n        :template:`profiles/edit_profile.html`.\n\n    **Context:**\n\n    ``form``\n        The form for editing the profile.\n\n    ``profile``\n         The user's current profile.\n\n    **Template:**\n\n    ``template_name`` keyword argument or\n    :template:`profiles/edit_profile.html`.\n\n    \"\"\"\n    try:\n        profile_obj = request.user.profile\n    except ObjectDoesNotExist:\n        return HttpResponseRedirect(reverse('profiles_profile_create'))\n\n    if success_url is None:\n        success_url = reverse('profiles_profile_detail',\n                              kwargs={'username': request.user.username})\n    if request.method == 'POST':\n        form = form_class(data=request.POST, files=request.FILES, instance=profile_obj)\n        if form.is_valid():\n            form.save()\n            return HttpResponseRedirect(success_url)\n    else:\n        form = form_class(instance=profile_obj)\n\n    if extra_context is None:\n        extra_context = {}\n    context = RequestContext(request)\n    for key, value in list(extra_context.items()):\n        context[key] = (value() if callable(value) else value)\n\n    return render_to_response(template_name, {\n        'form': form,\n        'profile': profile_obj,\n        'user': profile_obj.user,\n    }, context_instance=context)\nedit_profile = login_required(edit_profile)\n\n\n@login_required()\ndef delete_account(request):\n    form = UserDeleteForm()\n    template_name = 'profiles/private/delete_account.html'\n\n    if request.method == 'POST':\n        form = UserDeleteForm(instance=request.user, data=request.POST)\n        if form.is_valid():\n\n            # Do not delete the account permanently because it may create disaster\n            # Inactive the user instead.\n            request.user.is_active = False\n            request.user.save()\n            logout(request)\n            messages.info(request, 'You have successfully deleted your account')\n\n            return redirect('homepage')\n\n    return render(request, template_name, {'form': form})\n\n\ndef profile_detail(request, username, public_profile_field=None,\n                   template_name='profiles/public/profile_detail.html',\n                   extra_context=None):\n    \"\"\"\n    Detail view of a user's profile.\n\n    If the user has not yet created a profile, ``Http404`` will be\n    raised.\n\n    **Required arguments:**\n\n    ``username``\n        The username of the user whose profile is being displayed.\n\n    **Optional arguments:**\n\n    ``extra_context``\n        A dictionary of variables to add to the template context. Any\n        callable object in this dictionary will be called to produce\n        the end result which appears in the context.\n\n    ``public_profile_field``\n        The name of a ``BooleanField`` on the profile model; if the\n        value of that field on the user's profile is ``False``, the\n        ``profile`` variable in the template will be ``None``. Use\n        this feature to allow users to mark their profiles as not\n        being publicly viewable.\n\n        If this argument is not specified, it will be assumed that all\n        users' profiles are publicly viewable.\n\n    ``template_name``\n        The name of the template to use for displaying the profile. If\n        not specified, this will default to\n        :template:`profiles/profile_detail.html`.\n\n    **Context:**\n\n    ``profile``\n        The user's profile, or ``None`` if the user's profile is not\n        publicly viewable (see the description of\n        ``public_profile_field`` above).\n\n    **Template:**\n\n    ``template_name`` keyword argument or\n    :template:`profiles/profile_detail.html`.\n\n    \"\"\"\n    user = get_object_or_404(User, username=username)\n    try:\n        profile_obj = user.profile\n    except ObjectDoesNotExist:\n        raise Http404\n    if public_profile_field is not None and \\\n       not getattr(profile_obj, public_profile_field):\n        profile_obj = None\n\n    if extra_context is None:\n        extra_context = {}\n    context = RequestContext(request)\n    for key, value in list(extra_context.items()):\n        context[key] = (value() if callable(value) else value)\n\n    return render_to_response(template_name,\n                              {'profile': profile_obj},\n                              context_instance=context)\n", "path": "readthedocs/profiles/views.py"}, {"content": "\"\"\"Signal handling for core app.\"\"\"\n\nfrom __future__ import absolute_import\n\nimport logging\n\nfrom corsheaders import signals\nfrom django.dispatch import Signal\nfrom django.db.models import Q\nfrom future.backports.urllib.parse import urlparse\n\nfrom readthedocs.projects.models import Project, Domain\n\n\nlog = logging.getLogger(__name__)\n\nWHITELIST_URLS = ['/api/v2/footer_html', '/api/v2/search', '/api/v2/docsearch']\n\n\nwebhook_github = Signal(providing_args=['project', 'data', 'event'])\nwebhook_gitlab = Signal(providing_args=['project', 'data', 'event'])\nwebhook_bitbucket = Signal(providing_args=['project', 'data', 'event'])\n\n\ndef decide_if_cors(sender, request, **kwargs):  # pylint: disable=unused-argument\n    \"\"\"\n    Decide whether a request should be given CORS access.\n\n    This checks that:\n    * The URL is whitelisted against our CORS-allowed domains\n    * The Domain exists in our database, and belongs to the project being queried.\n\n    Returns True when a request should be given CORS access.\n    \"\"\"\n    if 'HTTP_ORIGIN' not in request.META:\n        return False\n    host = urlparse(request.META['HTTP_ORIGIN']).netloc.split(':')[0]\n    valid_url = False\n    for url in WHITELIST_URLS:\n        if request.path_info.startswith(url):\n            valid_url = True\n\n    if valid_url:\n        project_slug = request.GET.get('project', None)\n        try:\n            project = Project.objects.get(slug=project_slug)\n        except Project.DoesNotExist:\n            log.warning(\n                'Invalid project passed to domain. [{project}:{domain}'.format(\n                    project=project_slug,\n                    domain=host,\n                )\n            )\n            return False\n\n        domain = Domain.objects.filter(\n            Q(domain__icontains=host),\n            Q(project=project) | Q(project__subprojects__child=project)\n        )\n        if domain.exists():\n            return True\n\n    return False\n\nsignals.check_request_enabled.connect(decide_if_cors)\n", "path": "readthedocs/core/signals.py"}], "after_files": [{"content": "\"\"\"Views for creating, editing and viewing site-specific user profiles.\"\"\"\n\nfrom __future__ import absolute_import\n\nfrom django.contrib import messages\nfrom django.contrib.auth import logout\nfrom django.contrib.auth.decorators import login_required\nfrom django.contrib.auth.models import User\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.core.urlresolvers import reverse\nfrom django.http import Http404\nfrom django.http import HttpResponseRedirect\nfrom django.shortcuts import get_object_or_404, render, redirect\nfrom django.shortcuts import render_to_response\nfrom django.template import RequestContext\n\nfrom readthedocs.core.forms import UserDeleteForm\n\n\ndef create_profile(request, form_class, success_url=None,\n                   template_name='profiles/private/create_profile.html',\n                   extra_context=None):\n    \"\"\"\n    Create a profile for the current user, if one doesn't already exist.\n\n    If the user already has a profile, a redirect will be issued to the\n    :view:`profiles.views.edit_profile` view.\n\n    **Optional arguments:**\n\n    ``extra_context``\n        A dictionary of variables to add to the template context. Any\n        callable object in this dictionary will be called to produce\n        the end result which appears in the context.\n\n    ``form_class``\n        The form class to use for validating and creating the user\n        profile. This form class must define a method named\n        ``save()``, implementing the same argument signature as the\n        ``save()`` method of a standard Django ``ModelForm`` (this\n        view will call ``save(commit=False)`` to obtain the profile\n        object, and fill in the user before the final save). If the\n        profile object includes many-to-many relations, the convention\n        established by ``ModelForm`` of using a method named\n        ``save_m2m()`` will be used, and so your form class should\n        also define this method.\n\n    ``success_url``\n        The URL to redirect to after successful profile creation. If\n        this argument is not supplied, this will default to the URL of\n        :view:`profiles.views.profile_detail` for the newly-created\n        profile object.\n\n    ``template_name``\n        The template to use when displaying the profile-creation\n        form. If not supplied, this will default to\n        :template:`profiles/create_profile.html`.\n\n    **Context:**\n\n    ``form``\n        The profile-creation form.\n\n    **Template:**\n\n    ``template_name`` keyword argument, or\n    :template:`profiles/create_profile.html`.\n\n    \"\"\"\n    try:\n        profile_obj = request.user.profile\n        return HttpResponseRedirect(reverse('profiles_edit_profile'))\n    except ObjectDoesNotExist:\n        pass\n\n    #\n    # We set up success_url here, rather than as the default value for\n    # the argument. Trying to do it as the argument's default would\n    # mean evaluating the call to reverse() at the time this module is\n    # first imported, which introduces a circular dependency: to\n    # perform the reverse lookup we need access to profiles/urls.py,\n    # but profiles/urls.py in turn imports this module.\n    #\n\n    if success_url is None:\n        success_url = reverse('profiles_profile_detail',\n                              kwargs={'username': request.user.username})\n    if request.method == 'POST':\n        form = form_class(data=request.POST, files=request.FILES)\n        if form.is_valid():\n            profile_obj = form.save(commit=False)\n            profile_obj.user = request.user\n            profile_obj.save()\n            if hasattr(form, 'save_m2m'):\n                form.save_m2m()\n            return HttpResponseRedirect(success_url)\n    else:\n        form = form_class()\n\n    if extra_context is None:\n        extra_context = {}\n    context = RequestContext(request)\n    for key, value in list(extra_context.items()):\n        context[key] = (value() if callable(value) else value)\n\n    return render_to_response(template_name,\n                              {'form': form},\n                              context_instance=context)\ncreate_profile = login_required(create_profile)\n\n\ndef edit_profile(request, form_class, success_url=None,\n                 template_name='profiles/private/edit_profile.html',\n                 extra_context=None):\n    \"\"\"\n    Edit the current user's profile.\n\n    If the user does not already have a profile, a redirect will be issued to\n    the :view:`profiles.views.create_profile` view.\n\n    **Optional arguments:**\n\n    ``extra_context``\n        A dictionary of variables to add to the template context. Any\n        callable object in this dictionary will be called to produce\n        the end result which appears in the context.\n\n    ``form_class``\n        The form class to use for validating and editing the user\n        profile. This form class must operate similarly to a standard\n        Django ``ModelForm`` in that it must accept an instance of the\n        object to be edited as the keyword argument ``instance`` to\n        its constructor, and it must implement a method named\n        ``save()`` which will save the updates to the object.\n\n    ``success_url``\n        The URL to redirect to following a successful edit. If not\n        specified, this will default to the URL of\n        :view:`profiles.views.profile_detail` for the profile object\n        being edited.\n\n    ``template_name``\n        The template to use when displaying the profile-editing\n        form. If not specified, this will default to\n        :template:`profiles/edit_profile.html`.\n\n    **Context:**\n\n    ``form``\n        The form for editing the profile.\n\n    ``profile``\n         The user's current profile.\n\n    **Template:**\n\n    ``template_name`` keyword argument or\n    :template:`profiles/edit_profile.html`.\n\n    \"\"\"\n    try:\n        profile_obj = request.user.profile\n    except ObjectDoesNotExist:\n        return HttpResponseRedirect(reverse('profiles_profile_create'))\n\n    if success_url is None:\n        success_url = reverse('profiles_profile_detail',\n                              kwargs={'username': request.user.username})\n    if request.method == 'POST':\n        form = form_class(data=request.POST, files=request.FILES, instance=profile_obj)\n        if form.is_valid():\n            form.save()\n            return HttpResponseRedirect(success_url)\n    else:\n        form = form_class(instance=profile_obj)\n\n    if extra_context is None:\n        extra_context = {}\n    context = RequestContext(request)\n    for key, value in list(extra_context.items()):\n        context[key] = (value() if callable(value) else value)\n\n    return render_to_response(template_name, {\n        'form': form,\n        'profile': profile_obj,\n        'user': profile_obj.user,\n    }, context_instance=context)\nedit_profile = login_required(edit_profile)\n\n\n@login_required()\ndef delete_account(request):\n    form = UserDeleteForm()\n    template_name = 'profiles/private/delete_account.html'\n\n    if request.method == 'POST':\n        form = UserDeleteForm(instance=request.user, data=request.POST)\n        if form.is_valid():\n            # Delete the user permanently\n            # It will also delete some projects where he is the only owner\n            request.user.delete()\n            logout(request)\n            messages.info(request, 'You have successfully deleted your account')\n\n            return redirect('homepage')\n\n    return render(request, template_name, {'form': form})\n\n\ndef profile_detail(request, username, public_profile_field=None,\n                   template_name='profiles/public/profile_detail.html',\n                   extra_context=None):\n    \"\"\"\n    Detail view of a user's profile.\n\n    If the user has not yet created a profile, ``Http404`` will be\n    raised.\n\n    **Required arguments:**\n\n    ``username``\n        The username of the user whose profile is being displayed.\n\n    **Optional arguments:**\n\n    ``extra_context``\n        A dictionary of variables to add to the template context. Any\n        callable object in this dictionary will be called to produce\n        the end result which appears in the context.\n\n    ``public_profile_field``\n        The name of a ``BooleanField`` on the profile model; if the\n        value of that field on the user's profile is ``False``, the\n        ``profile`` variable in the template will be ``None``. Use\n        this feature to allow users to mark their profiles as not\n        being publicly viewable.\n\n        If this argument is not specified, it will be assumed that all\n        users' profiles are publicly viewable.\n\n    ``template_name``\n        The name of the template to use for displaying the profile. If\n        not specified, this will default to\n        :template:`profiles/profile_detail.html`.\n\n    **Context:**\n\n    ``profile``\n        The user's profile, or ``None`` if the user's profile is not\n        publicly viewable (see the description of\n        ``public_profile_field`` above).\n\n    **Template:**\n\n    ``template_name`` keyword argument or\n    :template:`profiles/profile_detail.html`.\n\n    \"\"\"\n    user = get_object_or_404(User, username=username)\n    try:\n        profile_obj = user.profile\n    except ObjectDoesNotExist:\n        raise Http404\n    if public_profile_field is not None and \\\n       not getattr(profile_obj, public_profile_field):\n        profile_obj = None\n\n    if extra_context is None:\n        extra_context = {}\n    context = RequestContext(request)\n    for key, value in list(extra_context.items()):\n        context[key] = (value() if callable(value) else value)\n\n    return render_to_response(template_name,\n                              {'profile': profile_obj},\n                              context_instance=context)\n", "path": "readthedocs/profiles/views.py"}, {"content": "\"\"\"Signal handling for core app.\"\"\"\n\nfrom __future__ import absolute_import\n\nimport logging\n\nfrom corsheaders import signals\nfrom django.conf import settings\nfrom django.db.models.signals import pre_delete\nfrom django.dispatch import Signal\nfrom django.db.models import Q, Count\nfrom django.dispatch import receiver\nfrom future.backports.urllib.parse import urlparse\n\nfrom readthedocs.projects.models import Project, Domain\n\nlog = logging.getLogger(__name__)\n\nWHITELIST_URLS = ['/api/v2/footer_html', '/api/v2/search', '/api/v2/docsearch']\n\n\nwebhook_github = Signal(providing_args=['project', 'data', 'event'])\nwebhook_gitlab = Signal(providing_args=['project', 'data', 'event'])\nwebhook_bitbucket = Signal(providing_args=['project', 'data', 'event'])\n\n\ndef decide_if_cors(sender, request, **kwargs):  # pylint: disable=unused-argument\n    \"\"\"\n    Decide whether a request should be given CORS access.\n\n    This checks that:\n    * The URL is whitelisted against our CORS-allowed domains\n    * The Domain exists in our database, and belongs to the project being queried.\n\n    Returns True when a request should be given CORS access.\n    \"\"\"\n    if 'HTTP_ORIGIN' not in request.META:\n        return False\n    host = urlparse(request.META['HTTP_ORIGIN']).netloc.split(':')[0]\n    valid_url = False\n    for url in WHITELIST_URLS:\n        if request.path_info.startswith(url):\n            valid_url = True\n\n    if valid_url:\n        project_slug = request.GET.get('project', None)\n        try:\n            project = Project.objects.get(slug=project_slug)\n        except Project.DoesNotExist:\n            log.warning(\n                'Invalid project passed to domain. [{project}:{domain}'.format(\n                    project=project_slug,\n                    domain=host,\n                )\n            )\n            return False\n\n        domain = Domain.objects.filter(\n            Q(domain__icontains=host),\n            Q(project=project) | Q(project__subprojects__child=project)\n        )\n        if domain.exists():\n            return True\n\n    return False\n\n\n@receiver(pre_delete, sender=settings.AUTH_USER_MODEL)\ndef delete_projects_and_organizations(sender, instance, *args, **kwargs):\n    # Here we count the owner list from the projects that the user own\n    # Then exclude the projects where there are more than one owner\n    projects = instance.projects.all().annotate(num_users=Count('users')).exclude(num_users__gt=1)\n\n    # Here we count the users list from the organization that the user belong\n    # Then exclude the organizations where there are more than one user\n    oauth_organizations = (instance.oauth_organizations.annotate(num_users=Count('users'))\n                                                       .exclude(num_users__gt=1))\n\n    projects.delete()\n    oauth_organizations.delete()\n\n\nsignals.check_request_enabled.connect(decide_if_cors)\n", "path": "readthedocs/core/signals.py"}]}