diff options
author | Jeremy Kerr <jk@ozlabs.org> | 2015-05-24 16:57:33 +0800 |
---|---|---|
committer | Jeremy Kerr <jk@ozlabs.org> | 2015-05-27 10:26:41 +0800 |
commit | ad2762cf775a8dde508de47164d6429f3fd724f1 (patch) | |
tree | e63015a468cfe32c961908f0338d423227799815 /apps/patchwork/views | |
parent | f09e982f58384946111d4157fd2b7c2b31b78612 (diff) | |
download | patchwork-ad2762cf775a8dde508de47164d6429f3fd724f1.tar.bz2 patchwork-ad2762cf775a8dde508de47164d6429f3fd724f1.tar.xz |
Move to a more recent django project structure
This change updates patchwor to the newer project struture: we've moved
the actual application out of the apps/ directory, and the
patchwork-specific templates to under the patchwork application.
This gives us the manage.py script in the top-level now.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Diffstat (limited to 'apps/patchwork/views')
-rw-r--r-- | apps/patchwork/views/__init__.py | 220 | ||||
-rw-r--r-- | apps/patchwork/views/base.py | 122 | ||||
-rw-r--r-- | apps/patchwork/views/bundle.py | 221 | ||||
-rw-r--r-- | apps/patchwork/views/mail.py | 119 | ||||
-rw-r--r-- | apps/patchwork/views/patch.py | 107 | ||||
-rw-r--r-- | apps/patchwork/views/project.py | 38 | ||||
-rw-r--r-- | apps/patchwork/views/user.py | 216 | ||||
-rw-r--r-- | apps/patchwork/views/xmlrpc.py | 450 |
8 files changed, 0 insertions, 1493 deletions
diff --git a/apps/patchwork/views/__init__.py b/apps/patchwork/views/__init__.py deleted file mode 100644 index dfca56d..0000000 --- a/apps/patchwork/views/__init__.py +++ /dev/null @@ -1,220 +0,0 @@ -# Patchwork - automated patch tracking system -# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org> -# -# This file is part of the Patchwork package. -# -# Patchwork is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# Patchwork is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Patchwork; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -from base import * -from patchwork.utils import Order, get_patch_ids, bundle_actions, set_bundle -from patchwork.paginator import Paginator -from patchwork.forms import MultiplePatchForm -from patchwork.models import Comment -import re -import datetime - -try: - from email.mime.nonmultipart import MIMENonMultipart - from email.encoders import encode_7or8bit - from email.parser import HeaderParser - from email.header import Header - import email.utils -except ImportError: - # Python 2.4 compatibility - from email.MIMENonMultipart import MIMENonMultipart - from email.Encoders import encode_7or8bit - from email.Parser import HeaderParser - from email.Header import Header - import email.Utils - email.utils = email.Utils - -def generic_list(request, project, view, - view_args = {}, filter_settings = [], patches = None, - editable_order = False): - - context = PatchworkRequestContext(request, - list_view = view, - list_view_params = view_args) - - context.project = project - order = Order(request.REQUEST.get('order'), editable = editable_order) - - # Explicitly set data to None because request.POST will be an empty dict - # when the form is not submitted, but passing a non-None data argument to - # a forms.Form will make it bound and we don't want that to happen unless - # there's been a form submission. - data = None - if request.method == 'POST': - data = request.POST - user = request.user - properties_form = None - if project.is_editable(user): - - # we only pass the post data to the MultiplePatchForm if that was - # the actual form submitted - data_tmp = None - if data and data.get('form', '') == 'patchlistform': - data_tmp = data - - properties_form = MultiplePatchForm(project, data = data_tmp) - - if request.method == 'POST' and data.get('form') == 'patchlistform': - action = data.get('action', '').lower() - - # special case: the user may have hit enter in the 'create bundle' - # text field, so if non-empty, assume the create action: - if data.get('bundle_name', False): - action = 'create' - - ps = Patch.objects.filter(id__in = get_patch_ids(data)) - - if action in bundle_actions: - errors = set_bundle(user, project, action, data, ps, context) - - elif properties_form and action == properties_form.action: - errors = process_multiplepatch_form(properties_form, user, - action, ps, context) - else: - errors = [] - - if errors: - context['errors'] = errors - - for (filterclass, setting) in filter_settings: - if isinstance(setting, dict): - context.filters.set_status(filterclass, **setting) - elif isinstance(setting, list): - context.filters.set_status(filterclass, *setting) - else: - context.filters.set_status(filterclass, setting) - - if patches is None: - patches = Patch.objects.filter(project=project) - - patches = context.filters.apply(patches) - if not editable_order: - patches = order.apply(patches) - - # we don't need the content or headers for a list; they're text fields - # that can potentially contain a lot of data - patches = patches.defer('content', 'headers') - - # but we will need to follow the state and submitter relations for - # rendering the list template - patches = patches.select_related('state', 'submitter') - - paginator = Paginator(request, patches) - - context.update({ - 'page': paginator.current_page, - 'patchform': properties_form, - 'project': project, - 'order': order, - }) - - return context - - -def process_multiplepatch_form(form, user, action, patches, context): - errors = [] - if not form.is_valid() or action != form.action: - return ['The submitted form data was invalid'] - - if len(patches) == 0: - context.add_message("No patches selected; nothing updated") - return errors - - changed_patches = 0 - for patch in patches: - if not patch.is_editable(user): - errors.append("You don't have permissions to edit patch '%s'" - % patch.name) - continue - - changed_patches += 1 - form.save(patch) - - if changed_patches == 1: - context.add_message("1 patch updated") - elif changed_patches > 1: - context.add_message("%d patches updated" % changed_patches) - else: - context.add_message("No patches updated") - - return errors - -class PatchMbox(MIMENonMultipart): - patch_charset = 'utf-8' - def __init__(self, _text): - MIMENonMultipart.__init__(self, 'text', 'plain', - **{'charset': self.patch_charset}) - self.set_payload(_text.encode(self.patch_charset)) - encode_7or8bit(self) - -def patch_to_mbox(patch): - postscript_re = re.compile('\n-{2,3} ?\n') - - comment = None - try: - comment = Comment.objects.get(patch = patch, msgid = patch.msgid) - except Exception: - pass - - body = '' - if comment: - body = comment.content.strip() + "\n" - - parts = postscript_re.split(body, 1) - if len(parts) == 2: - (body, postscript) = parts - body = body.strip() + "\n" - postscript = postscript.rstrip() - else: - postscript = '' - - for comment in Comment.objects.filter(patch = patch) \ - .exclude(msgid = patch.msgid): - body += comment.patch_responses() - - if postscript: - body += '---\n' + postscript + '\n' - - if patch.content: - body += '\n' + patch.content - - delta = patch.date - datetime.datetime.utcfromtimestamp(0) - utc_timestamp = delta.seconds + delta.days*24*3600 - - mail = PatchMbox(body) - mail['Subject'] = patch.name - mail['From'] = email.utils.formataddr(( - str(Header(patch.submitter.name, mail.patch_charset)), - patch.submitter.email)) - mail['X-Patchwork-Id'] = str(patch.id) - mail['Message-Id'] = patch.msgid - mail.set_unixfrom('From patchwork ' + patch.date.ctime()) - - - copied_headers = ['To', 'Cc', 'Date'] - orig_headers = HeaderParser().parsestr(str(patch.headers)) - for header in copied_headers: - if header in orig_headers: - mail[header] = orig_headers[header] - - if 'Date' not in mail: - mail['Date'] = email.utils.formatdate(utc_timestamp) - - return mail diff --git a/apps/patchwork/views/base.py b/apps/patchwork/views/base.py deleted file mode 100644 index 6d7dd13..0000000 --- a/apps/patchwork/views/base.py +++ /dev/null @@ -1,122 +0,0 @@ -# Patchwork - automated patch tracking system -# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org> -# -# This file is part of the Patchwork package. -# -# Patchwork is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# Patchwork is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Patchwork; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -from patchwork.models import Patch, Project, Person, EmailConfirmation -from django.shortcuts import render_to_response, get_object_or_404 -from django.http import HttpResponse, HttpResponseRedirect, Http404 -from patchwork.requestcontext import PatchworkRequestContext -from django.core import serializers, urlresolvers -from django.template.loader import render_to_string -from django.conf import settings -from django.db.models import Q - -def projects(request): - context = PatchworkRequestContext(request) - projects = Project.objects.all() - - if projects.count() == 1: - return HttpResponseRedirect( - urlresolvers.reverse('patchwork.views.patch.list', - kwargs = {'project_id': projects[0].linkname})) - - context['projects'] = projects - return render_to_response('patchwork/projects.html', context) - -def pwclientrc(request, project_id): - project = get_object_or_404(Project, linkname = project_id) - context = PatchworkRequestContext(request) - context.project = project - if settings.FORCE_HTTPS_LINKS or request.is_secure(): - context['scheme'] = 'https' - else: - context['scheme'] = 'http' - response = HttpResponse(content_type = "text/plain") - response['Content-Disposition'] = 'attachment; filename=.pwclientrc' - response.write(render_to_string('patchwork/pwclientrc', context)) - return response - -def pwclient(request): - context = PatchworkRequestContext(request) - response = HttpResponse(content_type = "text/x-python") - response['Content-Disposition'] = 'attachment; filename=pwclient' - response.write(render_to_string('patchwork/pwclient', context)) - return response - -def confirm(request, key): - import patchwork.views.user, patchwork.views.mail - views = { - 'userperson': patchwork.views.user.link_confirm, - 'registration': patchwork.views.user.register_confirm, - 'optout': patchwork.views.mail.optout_confirm, - 'optin': patchwork.views.mail.optin_confirm, - } - - conf = get_object_or_404(EmailConfirmation, key = key) - if conf.type not in views: - raise Http404 - - if conf.active and conf.is_valid(): - return views[conf.type](request, conf) - - context = PatchworkRequestContext(request) - context['conf'] = conf - if not conf.active: - context['error'] = 'inactive' - elif not conf.is_valid(): - context['error'] = 'expired' - - return render_to_response('patchwork/confirm-error.html', context) - -def submitter_complete(request): - search = request.GET.get('q', '') - limit = request.GET.get('l', None) - response = HttpResponse(content_type = "text/plain") - - if len(search) <= 3: - return response - - queryset = Person.objects.filter(Q(name__icontains = search) | - Q(email__icontains = search)) - if limit is not None: - try: - limit = int(limit) - except ValueError: - limit = None - - if limit is not None and limit > 0: - queryset = queryset[:limit] - - json_serializer = serializers.get_serializer("json")() - json_serializer.serialize(queryset, ensure_ascii=False, stream=response) - return response - -help_pages = {'': 'index.html', - 'about/': 'about.html', - } - -if settings.ENABLE_XMLRPC: - help_pages['pwclient/'] = 'pwclient.html' - -def help(request, path): - context = PatchworkRequestContext(request) - if path in help_pages: - return render_to_response('patchwork/help/' + help_pages[path], context) - raise Http404 - diff --git a/apps/patchwork/views/bundle.py b/apps/patchwork/views/bundle.py deleted file mode 100644 index 3fb47e2..0000000 --- a/apps/patchwork/views/bundle.py +++ /dev/null @@ -1,221 +0,0 @@ -# Patchwork - automated patch tracking system -# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org> -# -# This file is part of the Patchwork package. -# -# Patchwork is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# Patchwork is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Patchwork; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User -from django.shortcuts import render_to_response, get_object_or_404 -from patchwork.requestcontext import PatchworkRequestContext -from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound -import django.core.urlresolvers -from patchwork.models import Patch, Bundle, BundlePatch, Project -from patchwork.utils import get_patch_ids -from patchwork.forms import BundleForm, DeleteBundleForm -from patchwork.views import generic_list, patch_to_mbox -from patchwork.filters import DelegateFilter - -@login_required -def setbundle(request): - context = PatchworkRequestContext(request) - - bundle = None - - if request.method == 'POST': - action = request.POST.get('action', None) - if action is None: - pass - elif action == 'create': - project = get_object_or_404(Project, - id = request.POST.get('project')) - bundle = Bundle(owner = request.user, project = project, - name = request.POST['name']) - bundle.save() - patch_id = request.POST.get('patch_id', None) - if patch_id: - patch = get_object_or_404(Patch, id = patch_id) - try: - bundle.append_patch(patch) - except Exception: - pass - bundle.save() - elif action == 'add': - bundle = get_object_or_404(Bundle, - owner = request.user, id = request.POST['id']) - bundle.save() - - patch_id = request.get('patch_id', None) - if patch_id: - patch_ids = patch_id - else: - patch_ids = get_patch_ids(request.POST) - - for id in patch_ids: - try: - patch = Patch.objects.get(id = id) - bundle.append_patch(patch) - except: - pass - - bundle.save() - elif action == 'delete': - try: - bundle = Bundle.objects.get(owner = request.user, - id = request.POST['id']) - bundle.delete() - except Exception: - pass - - bundle = None - - else: - bundle = get_object_or_404(Bundle, owner = request.user, - id = request.POST['bundle_id']) - - if 'error' in context: - pass - - if bundle: - return HttpResponseRedirect( - django.core.urlresolvers.reverse( - 'patchwork.views.bundle.bundle', - kwargs = {'bundle_id': bundle.id} - ) - ) - else: - return HttpResponseRedirect( - django.core.urlresolvers.reverse( - 'patchwork.views.bundle.list') - ) - -@login_required -def bundles(request): - context = PatchworkRequestContext(request) - - if request.method == 'POST': - form_name = request.POST.get('form_name', '') - - if form_name == DeleteBundleForm.name: - form = DeleteBundleForm(request.POST) - if form.is_valid(): - bundle = get_object_or_404(Bundle, - id = form.cleaned_data['bundle_id']) - bundle.delete() - - bundles = Bundle.objects.filter(owner = request.user) - for bundle in bundles: - bundle.delete_form = DeleteBundleForm(auto_id = False, - initial = {'bundle_id': bundle.id}) - - context['bundles'] = bundles - - return render_to_response('patchwork/bundles.html', context) - -def bundle(request, username, bundlename): - bundle = get_object_or_404(Bundle, owner__username = username, - name = bundlename) - filter_settings = [(DelegateFilter, DelegateFilter.AnyDelegate)] - - is_owner = request.user == bundle.owner - - if not (is_owner or bundle.public): - return HttpResponseNotFound() - - if is_owner: - if request.method == 'POST' and request.POST.get('form') == 'bundle': - action = request.POST.get('action', '').lower() - if action == 'delete': - bundle.delete() - return HttpResponseRedirect( - django.core.urlresolvers.reverse( - 'patchwork.views.user.profile') - ) - elif action == 'update': - form = BundleForm(request.POST, instance = bundle) - if form.is_valid(): - form.save() - - # if we've changed the bundle name, redirect to new URL - bundle = Bundle.objects.get(pk = bundle.pk) - if bundle.name != bundlename: - return HttpResponseRedirect(bundle.get_absolute_url()) - - else: - form = BundleForm(instance = bundle) - else: - form = BundleForm(instance = bundle) - - if request.method == 'POST' and \ - request.POST.get('form') == 'reorderform': - order = get_object_or_404(BundlePatch, bundle = bundle, - patch__id = request.POST.get('order_start')).order - - for patch_id in request.POST.getlist('neworder'): - bundlepatch = get_object_or_404(BundlePatch, - bundle = bundle, patch__id = patch_id) - bundlepatch.order = order - bundlepatch.save() - order += 1 - else: - form = None - - context = generic_list(request, bundle.project, - 'patchwork.views.bundle.bundle', - view_args = {'username': bundle.owner.username, - 'bundlename': bundle.name}, - filter_settings = filter_settings, - patches = bundle.ordered_patches(), - editable_order = is_owner) - - context['bundle'] = bundle - context['bundleform'] = form - - return render_to_response('patchwork/bundle.html', context) - -def mbox(request, username, bundlename): - bundle = get_object_or_404(Bundle, owner__username = username, - name = bundlename) - - if not (request.user == bundle.owner or bundle.public): - return HttpResponseNotFound() - - mbox = '\n'.join([patch_to_mbox(p).as_string(True) - for p in bundle.ordered_patches()]) - - response = HttpResponse(content_type='text/plain') - response['Content-Disposition'] = \ - 'attachment; filename=bundle-%d-%s.mbox' % (bundle.id, bundle.name) - - response.write(mbox) - return response - -@login_required -def bundle_redir(request, bundle_id): - bundle = get_object_or_404(Bundle, id = bundle_id, owner = request.user) - return HttpResponseRedirect(bundle.get_absolute_url()) - -@login_required -def mbox_redir(request, bundle_id): - bundle = get_object_or_404(Bundle, id = bundle_id, owner = request.user) - return HttpResponseRedirect(django.core.urlresolvers.reverse( - 'patchwork.views.bundle.mbox', kwargs = { - 'username': request.user.username, - 'bundlename': bundle.name, - })) - - - diff --git a/apps/patchwork/views/mail.py b/apps/patchwork/views/mail.py deleted file mode 100644 index aebba34..0000000 --- a/apps/patchwork/views/mail.py +++ /dev/null @@ -1,119 +0,0 @@ -# Patchwork - automated patch tracking system -# Copyright (C) 2010 Jeremy Kerr <jk@ozlabs.org> -# -# This file is part of the Patchwork package. -# -# Patchwork is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# Patchwork is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Patchwork; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -from patchwork.requestcontext import PatchworkRequestContext -from patchwork.models import EmailOptout, EmailConfirmation -from patchwork.forms import OptinoutRequestForm, EmailForm -from django.shortcuts import render_to_response -from django.template.loader import render_to_string -from django.conf import settings as conf_settings -from django.core.mail import send_mail -from django.core.urlresolvers import reverse -from django.http import HttpResponseRedirect - -def settings(request): - context = PatchworkRequestContext(request) - if request.method == 'POST': - form = EmailForm(data = request.POST) - if form.is_valid(): - email = form.cleaned_data['email'] - is_optout = EmailOptout.objects.filter(email = email).count() > 0 - context.update({ - 'email': email, - 'is_optout': is_optout, - }) - return render_to_response('patchwork/mail-settings.html', context) - - else: - form = EmailForm() - context['form'] = form - return render_to_response('patchwork/mail-form.html', context) - -def optout_confirm(request, conf): - context = PatchworkRequestContext(request) - - email = conf.email.strip().lower() - # silently ignore duplicated optouts - if EmailOptout.objects.filter(email = email).count() == 0: - optout = EmailOptout(email = email) - optout.save() - - conf.deactivate() - context['email'] = conf.email - - return render_to_response('patchwork/optout.html', context) - -def optin_confirm(request, conf): - context = PatchworkRequestContext(request) - - email = conf.email.strip().lower() - EmailOptout.objects.filter(email = email).delete() - - conf.deactivate() - context['email'] = conf.email - - return render_to_response('patchwork/optin.html', context) - -def optinout(request, action, description): - context = PatchworkRequestContext(request) - - mail_template = 'patchwork/%s-request.mail' % action - html_template = 'patchwork/%s-request.html' % action - - if request.method != 'POST': - return HttpResponseRedirect(reverse(settings)) - - form = OptinoutRequestForm(data = request.POST) - if not form.is_valid(): - context['error'] = ('There was an error in the %s form. ' + - 'Please review the form and re-submit.') % \ - description - context['form'] = form - return render_to_response(html_template, context) - - email = form.cleaned_data['email'] - if action == 'optin' and \ - EmailOptout.objects.filter(email = email).count() == 0: - context['error'] = ('The email address %s is not on the ' + - 'patchwork opt-out list, so you don\'t ' + - 'need to opt back in') % email - context['form'] = form - return render_to_response(html_template, context) - - conf = EmailConfirmation(type = action, email = email) - conf.save() - context['confirmation'] = conf - mail = render_to_string(mail_template, context) - try: - send_mail('Patchwork %s confirmation' % description, mail, - conf_settings.DEFAULT_FROM_EMAIL, [email]) - context['email'] = mail - context['email_sent'] = True - except Exception, ex: - context['error'] = 'An error occurred during confirmation . ' + \ - 'Please try again later.' - context['admins'] = conf_settings.ADMINS - - return render_to_response(html_template, context) - -def optout(request): - return optinout(request, 'optout', 'opt-out') - -def optin(request): - return optinout(request, 'optin', 'opt-in') diff --git a/apps/patchwork/views/patch.py b/apps/patchwork/views/patch.py deleted file mode 100644 index 62ff853..0000000 --- a/apps/patchwork/views/patch.py +++ /dev/null @@ -1,107 +0,0 @@ -# Patchwork - automated patch tracking system -# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org> -# -# This file is part of the Patchwork package. -# -# Patchwork is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# Patchwork is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Patchwork; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -from patchwork.models import Patch, Project, Bundle -from patchwork.forms import PatchForm, CreateBundleForm -from patchwork.requestcontext import PatchworkRequestContext -from django.shortcuts import render_to_response, get_object_or_404 -from django.http import HttpResponse, HttpResponseForbidden -from patchwork.views import generic_list, patch_to_mbox - -def patch(request, patch_id): - context = PatchworkRequestContext(request) - patch = get_object_or_404(Patch, id=patch_id) - context.project = patch.project - editable = patch.is_editable(request.user) - - form = None - createbundleform = None - - if editable: - form = PatchForm(instance = patch) - if request.user.is_authenticated(): - createbundleform = CreateBundleForm() - - if request.method == 'POST': - action = request.POST.get('action', None) - if action: - action = action.lower() - - if action == 'createbundle': - bundle = Bundle(owner = request.user, project = patch.project) - createbundleform = CreateBundleForm(instance = bundle, - data = request.POST) - if createbundleform.is_valid(): - createbundleform.save() - bundle.append_patch(patch) - bundle.save() - createbundleform = CreateBundleForm() - context.add_message('Bundle %s created' % bundle.name) - - elif action == 'addtobundle': - bundle = get_object_or_404(Bundle, id = \ - request.POST.get('bundle_id')) - try: - bundle.append_patch(patch) - bundle.save() - context.add_message('Patch added to bundle "%s"' % bundle.name) - except Exception, ex: - context.add_message("Couldn't add patch '%s' to bundle %s: %s" \ - % (patch.name, bundle.name, ex.message)) - - # all other actions require edit privs - elif not editable: - return HttpResponseForbidden() - - elif action is None: - form = PatchForm(data = request.POST, instance = patch) - if form.is_valid(): - form.save() - context.add_message('Patch updated') - - context['patch'] = patch - context['patchform'] = form - context['createbundleform'] = createbundleform - context['project'] = patch.project - - return render_to_response('patchwork/patch.html', context) - -def content(request, patch_id): - patch = get_object_or_404(Patch, id=patch_id) - response = HttpResponse(content_type="text/x-patch") - response.write(patch.content) - response['Content-Disposition'] = 'attachment; filename=' + \ - patch.filename().replace(';', '').replace('\n', '') - return response - -def mbox(request, patch_id): - patch = get_object_or_404(Patch, id=patch_id) - response = HttpResponse(content_type="text/plain") - response.write(patch_to_mbox(patch).as_string(True)) - response['Content-Disposition'] = 'attachment; filename=' + \ - patch.filename().replace(';', '').replace('\n', '') - return response - - -def list(request, project_id): - project = get_object_or_404(Project, linkname=project_id) - context = generic_list(request, project, 'patchwork.views.patch.list', - view_args = {'project_id': project.linkname}) - return render_to_response('patchwork/list.html', context) diff --git a/apps/patchwork/views/project.py b/apps/patchwork/views/project.py deleted file mode 100644 index 114dbe0..0000000 --- a/apps/patchwork/views/project.py +++ /dev/null @@ -1,38 +0,0 @@ -# Patchwork - automated patch tracking system -# Copyright (C) 2009 Jeremy Kerr <jk@ozlabs.org> -# -# This file is part of the Patchwork package. -# -# Patchwork is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# Patchwork is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Patchwork; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -from patchwork.models import Patch, Project -from django.shortcuts import render_to_response, get_object_or_404 -from django.contrib.auth.models import User -from patchwork.requestcontext import PatchworkRequestContext - -def project(request, project_id): - context = PatchworkRequestContext(request) - project = get_object_or_404(Project, linkname = project_id) - context.project = project - - context['maintainers'] = User.objects.filter( \ - profile__maintainer_projects = project) - context['n_patches'] = Patch.objects.filter(project = project, - archived = False).count() - context['n_archived_patches'] = Patch.objects.filter(project = project, - archived = True).count() - - return render_to_response('patchwork/project.html', context) diff --git a/apps/patchwork/views/user.py b/apps/patchwork/views/user.py deleted file mode 100644 index 126ecc9..0000000 --- a/apps/patchwork/views/user.py +++ /dev/null @@ -1,216 +0,0 @@ -# Patchwork - automated patch tracking system -# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org> -# -# This file is part of the Patchwork package. -# -# Patchwork is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# Patchwork is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Patchwork; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -from django.contrib.auth.decorators import login_required -from patchwork.requestcontext import PatchworkRequestContext -from django.shortcuts import render_to_response, get_object_or_404 -from django.contrib import auth -from django.contrib.sites.models import Site -from django.http import HttpResponseRedirect -from patchwork.models import Project, Bundle, Person, EmailConfirmation, \ - State, EmailOptout -from patchwork.forms import UserProfileForm, UserPersonLinkForm, \ - RegistrationForm -from patchwork.filters import DelegateFilter -from patchwork.views import generic_list -from django.template.loader import render_to_string -from django.conf import settings -from django.core.mail import send_mail -import django.core.urlresolvers - -def register(request): - context = PatchworkRequestContext(request) - if request.method == 'POST': - form = RegistrationForm(request.POST) - if form.is_valid(): - data = form.cleaned_data - # create inactive user - user = auth.models.User.objects.create_user(data['username'], - data['email'], - data['password']) - user.is_active = False; - user.first_name = data.get('first_name', '') - user.last_name = data.get('last_name', '') - user.save() - - # create confirmation - conf = EmailConfirmation(type = 'registration', user = user, - email = user.email) - conf.save() - - # send email - mail_ctx = {'site': Site.objects.get_current(), - 'confirmation': conf} - - subject = render_to_string('patchwork/activation_email_subject.txt', - mail_ctx).replace('\n', ' ').strip() - - message = render_to_string('patchwork/activation_email.txt', - mail_ctx) - - send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, - [conf.email]) - - # setting 'confirmation' in the template indicates success - context['confirmation'] = conf - - else: - form = RegistrationForm() - - return render_to_response('patchwork/registration_form.html', - { 'form': form }, - context_instance=context) - -def register_confirm(request, conf): - conf.user.is_active = True - conf.user.save() - conf.deactivate() - try: - person = Person.objects.get(email__iexact = conf.user.email) - except Person.DoesNotExist: - person = Person(email = conf.user.email, - name = conf.user.profile.name()) - person.user = conf.user - person.save() - - return render_to_response('patchwork/registration-confirm.html') - -@login_required -def profile(request): - context = PatchworkRequestContext(request) - - if request.method == 'POST': - form = UserProfileForm(instance = request.user.profile, - data = request.POST) - if form.is_valid(): - form.save() - else: - form = UserProfileForm(instance = request.user.profile) - - context.project = request.user.profile.primary_project - context['bundles'] = Bundle.objects.filter(owner = request.user) - context['profileform'] = form - - optout_query = '%s.%s IN (SELECT %s FROM %s)' % ( - Person._meta.db_table, - Person._meta.get_field('email').column, - EmailOptout._meta.get_field('email').column, - EmailOptout._meta.db_table) - people = Person.objects.filter(user = request.user) \ - .extra(select = {'is_optout': optout_query}) - context['linked_emails'] = people - context['linkform'] = UserPersonLinkForm() - - return render_to_response('patchwork/profile.html', context) - -@login_required -def link(request): - context = PatchworkRequestContext(request) - - if request.method == 'POST': - form = UserPersonLinkForm(request.POST) - if form.is_valid(): - conf = EmailConfirmation(type = 'userperson', - user = request.user, - email = form.cleaned_data['email']) - conf.save() - context['confirmation'] = conf - - try: - send_mail('Patchwork email address confirmation', - render_to_string('patchwork/user-link.mail', - context), - settings.DEFAULT_FROM_EMAIL, - [form.cleaned_data['email']]) - except Exception: - context['confirmation'] = None - context['error'] = 'An error occurred during confirmation. ' + \ - 'Please try again later' - else: - form = UserPersonLinkForm() - context['linkform'] = form - - return render_to_response('patchwork/user-link.html', context) - -@login_required -def link_confirm(request, conf): - context = PatchworkRequestContext(request) - - try: - person = Person.objects.get(email__iexact = conf.email) - except Person.DoesNotExist: - person = Person(email = conf.email) - - person.link_to_user(conf.user) - person.save() - conf.deactivate() - - context['person'] = person - - return render_to_response('patchwork/user-link-confirm.html', context) - -@login_required -def unlink(request, person_id): - person = get_object_or_404(Person, id = person_id) - - if request.method == 'POST': - if person.email != request.user.email: - person.user = None - person.save() - - url = django.core.urlresolvers.reverse('patchwork.views.user.profile') - return HttpResponseRedirect(url) - - -@login_required -def todo_lists(request): - todo_lists = [] - - for project in Project.objects.all(): - patches = request.user.profile.todo_patches(project = project) - if not patches.count(): - continue - - todo_lists.append({'project': project, 'n_patches': patches.count()}) - - if len(todo_lists) == 1: - return todo_list(request, todo_lists[0]['project'].linkname) - - context = PatchworkRequestContext(request) - context['todo_lists'] = todo_lists - context.project = request.user.profile.primary_project - return render_to_response('patchwork/todo-lists.html', context) - -@login_required -def todo_list(request, project_id): - project = get_object_or_404(Project, linkname = project_id) - patches = request.user.profile.todo_patches(project = project) - filter_settings = [(DelegateFilter, - {'delegate': request.user})] - - context = generic_list(request, project, - 'patchwork.views.user.todo_list', - view_args = {'project_id': project.linkname}, - filter_settings = filter_settings, - patches = patches) - - context['action_required_states'] = \ - State.objects.filter(action_required = True).all() - return render_to_response('patchwork/todo-list.html', context) diff --git a/apps/patchwork/views/xmlrpc.py b/apps/patchwork/views/xmlrpc.py deleted file mode 100644 index 84ed408..0000000 --- a/apps/patchwork/views/xmlrpc.py +++ /dev/null @@ -1,450 +0,0 @@ -# Patchwork - automated patch tracking system -# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org> -# -# This file is part of the Patchwork package. -# -# Patchwork is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# Patchwork is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Patchwork; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# Patchwork XMLRPC interface -# - -from SimpleXMLRPCServer import SimpleXMLRPCDispatcher -from django.http import HttpResponse, HttpResponseRedirect, \ - HttpResponseServerError -from django.core import urlresolvers -from django.contrib.auth import authenticate -from patchwork.models import Patch, Project, Person, State -from patchwork.views import patch_to_mbox -from django.views.decorators.csrf import csrf_exempt - -import sys -import base64 -import xmlrpclib - -class PatchworkXMLRPCDispatcher(SimpleXMLRPCDispatcher): - def __init__(self): - if sys.version_info[:3] >= (2,5,): - SimpleXMLRPCDispatcher.__init__(self, allow_none=False, - encoding=None) - def _dumps(obj, *args, **kwargs): - kwargs['allow_none'] = self.allow_none - kwargs['encoding'] = self.encoding - return xmlrpclib.dumps(obj, *args, **kwargs) - else: - def _dumps(obj, *args, **kwargs): - return xmlrpclib.dumps(obj, *args, **kwargs) - SimpleXMLRPCDispatcher.__init__(self) - - self.dumps = _dumps - - # map of name => (auth, func) - self.func_map = {} - - def register_function(self, fn, auth_required): - self.func_map[fn.__name__] = (auth_required, fn) - - - def _user_for_request(self, request): - auth_header = None - - if 'HTTP_AUTHORIZATION' in request.META: - auth_header = request.META.get('HTTP_AUTHORIZATION') - elif 'Authorization' in request.META: - auth_header = request.META.get('Authorization') - - if auth_header is None or auth_header == '': - raise Exception("No authentication credentials given") - - str = auth_header.strip() - - if not str.startswith('Basic '): - raise Exception("Authentication scheme not supported") - - str = str[len('Basic '):].strip() - - try: - decoded = base64.decodestring(str) - username, password = decoded.split(':', 1) - except: - raise Exception("Invalid authentication credentials") - - return authenticate(username = username, password = password) - - - def _dispatch(self, request, method, params): - if method not in self.func_map.keys(): - raise Exception('method "%s" is not supported' % method) - - auth_required, fn = self.func_map[method] - - if auth_required: - user = self._user_for_request(request) - if not user: - raise Exception("Invalid username/password") - - params = (user,) + params - - return fn(*params) - - def _marshaled_dispatch(self, request): - try: - params, method = xmlrpclib.loads(request.body) - - response = self._dispatch(request, method, params) - # wrap response in a singleton tuple - response = (response,) - response = self.dumps(response, methodresponse=1) - except xmlrpclib.Fault, fault: - response = self.dumps(fault) - except: - # report exception back to server - response = self.dumps( - xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value)), - ) - - return response - -dispatcher = PatchworkXMLRPCDispatcher() - -# XMLRPC view function -@csrf_exempt -def xmlrpc(request): - if request.method != 'POST': - return HttpResponseRedirect( - urlresolvers.reverse('patchwork.views.help', - kwargs = {'path': 'pwclient/'})) - - response = HttpResponse() - try: - ret = dispatcher._marshaled_dispatch(request) - response.write(ret) - except Exception: - return HttpResponseServerError() - - return response - -# decorator for XMLRPC methods. Setting login_required to true will call -# the decorated function with a non-optional user as the first argument. -def xmlrpc_method(login_required = False): - def wrap(f): - dispatcher.register_function(f, login_required) - return f - - return wrap - - - -# We allow most of the Django field lookup types for remote queries -LOOKUP_TYPES = ["iexact", "contains", "icontains", "gt", "gte", "lt", - "in", "startswith", "istartswith", "endswith", - "iendswith", "range", "year", "month", "day", "isnull" ] - -####################################################################### -# Helper functions -####################################################################### - -def project_to_dict(obj): - """Return a trimmed down dictionary representation of a Project - object which is OK to send to the client.""" - return \ - { - 'id' : obj.id, - 'linkname' : obj.linkname, - 'name' : obj.name, - } - -def person_to_dict(obj): - """Return a trimmed down dictionary representation of a Person - object which is OK to send to the client.""" - - # Make sure we don't return None even if the user submitted a patch - # with no real name. XMLRPC can't marshall None. - if obj.name is not None: - name = obj.name - else: - name = obj.email - - return \ - { - 'id' : obj.id, - 'email' : obj.email, - 'name' : name, - 'user' : unicode(obj.user).encode("utf-8"), - } - -def patch_to_dict(obj): - """Return a trimmed down dictionary representation of a Patch - object which is OK to send to the client.""" - return \ - { - 'id' : obj.id, - 'date' : unicode(obj.date).encode("utf-8"), - 'filename' : obj.filename(), - 'msgid' : obj.msgid, - 'name' : obj.name, - 'project' : unicode(obj.project).encode("utf-8"), - 'project_id' : obj.project_id, - 'state' : unicode(obj.state).encode("utf-8"), - 'state_id' : obj.state_id, - 'archived' : obj.archived, - 'submitter' : unicode(obj.submitter).encode("utf-8"), - 'submitter_id' : obj.submitter_id, - 'delegate' : unicode(obj.delegate).encode("utf-8"), - 'delegate_id' : max(obj.delegate_id, 0), - 'commit_ref' : max(obj.commit_ref, ''), - } - -def bundle_to_dict(obj): - """Return a trimmed down dictionary representation of a Bundle - object which is OK to send to the client.""" - return \ - { - 'id' : obj.id, - 'name' : obj.name, - 'n_patches' : obj.n_patches(), - 'public_url' : obj.public_url(), - } - -def state_to_dict(obj): - """Return a trimmed down dictionary representation of a State - object which is OK to send to the client.""" - return \ - { - 'id' : obj.id, - 'name' : obj.name, - } - -####################################################################### -# Public XML-RPC methods -####################################################################### - -@xmlrpc_method(False) -def pw_rpc_version(): - """Return Patchwork XML-RPC interface version.""" - return 1 - -@xmlrpc_method(False) -def project_list(search_str="", max_count=0): - """Get a list of projects matching the given filters.""" - try: - if len(search_str) > 0: - projects = Project.objects.filter(linkname__icontains = search_str) - else: - projects = Project.objects.all() - - if max_count > 0: - return map(project_to_dict, projects)[:max_count] - else: - return map(project_to_dict, projects) - except: - return [] - -@xmlrpc_method(False) -def project_get(project_id): - """Return structure for the given project ID.""" - try: - project = Project.objects.filter(id = project_id)[0] - return project_to_dict(project) - except: - return {} - -@xmlrpc_method(False) -def person_list(search_str="", max_count=0): - """Get a list of Person objects matching the given filters.""" - try: - if len(search_str) > 0: - people = (Person.objects.filter(name__icontains = search_str) | - Person.objects.filter(email__icontains = search_str)) - else: - people = Person.objects.all() - - if max_count > 0: - return map(person_to_dict, people)[:max_count] - else: - return map(person_to_dict, people) - - except: - return [] - -@xmlrpc_method(False) -def person_get(person_id): - """Return structure for the given person ID.""" - try: - person = Person.objects.filter(id = person_id)[0] - return person_to_dict(person) - except: - return {} - -@xmlrpc_method(False) -def patch_list(filter={}): - """Get a list of patches matching the given filters.""" - try: - # We allow access to many of the fields. But, some fields are - # filtered by raw object so we must lookup by ID instead over - # XML-RPC. - ok_fields = [ - "id", - "name", - "project_id", - "submitter_id", - "delegate_id", - "archived", - "state_id", - "date", - "commit_ref", - "hash", - "msgid", - "max_count", - ] - - dfilter = {} - max_count = 0 - - for key in filter: - parts = key.split("__") - if parts[0] not in ok_fields: - # Invalid field given - return [] - if len(parts) > 1: - if LOOKUP_TYPES.count(parts[1]) == 0: - # Invalid lookup type given - return [] - - if parts[0] == 'project_id': - dfilter['project'] = Project.objects.filter(id = - filter[key])[0] - elif parts[0] == 'submitter_id': - dfilter['submitter'] = Person.objects.filter(id = - filter[key])[0] - elif parts[0] == 'delegate_id': - dfilter['delegate'] = Person.objects.filter(id = - filter[key])[0] - elif parts[0] == 'state_id': - dfilter['state'] = State.objects.filter(id = - filter[key])[0] - elif parts[0] == 'max_count': - max_count = filter[key] - else: - dfilter[key] = filter[key] - - patches = Patch.objects.filter(**dfilter) - - if max_count > 0: - return map(patch_to_dict, patches[:max_count]) - else: - return map(patch_to_dict, patches) - - except: - return [] - -@xmlrpc_method(False) -def patch_get(patch_id): - """Return structure for the given patch ID.""" - try: - patch = Patch.objects.filter(id = patch_id)[0] - return patch_to_dict(patch) - except: - return {} - -@xmlrpc_method(False) -def patch_get_by_hash(hash): - """Return structure for the given patch hash.""" - try: - patch = Patch.objects.filter(hash = hash)[0] - return patch_to_dict(patch) - except: - return {} - -@xmlrpc_method(False) -def patch_get_by_project_hash(project, hash): - """Return structure for the given patch hash.""" - try: - patch = Patch.objects.filter(project__linkname = project, - hash = hash)[0] - return patch_to_dict(patch) - except: - return {} - -@xmlrpc_method(False) -def patch_get_mbox(patch_id): - """Return mbox string for the given patch ID.""" - try: - patch = Patch.objects.filter(id = patch_id)[0] - return patch_to_mbox(patch).as_string(True) - except: - return "" - -@xmlrpc_method(False) -def patch_get_diff(patch_id): - """Return diff for the given patch ID.""" - try: - patch = Patch.objects.filter(id = patch_id)[0] - return patch.content - except: - return "" - -@xmlrpc_method(True) -def patch_set(user, patch_id, params): - """Update a patch with the key,value pairs in params. Only some parameters - can be set""" - try: - ok_params = ['state', 'commit_ref', 'archived'] - - patch = Patch.objects.get(id = patch_id) - - if not patch.is_editable(user): - raise Exception('No permissions to edit this patch') - - for (k, v) in params.iteritems(): - if k not in ok_params: - continue - - if k == 'state': - patch.state = State.objects.get(id = v) - - else: - setattr(patch, k, v) - - patch.save() - - return True - - except: - raise - -@xmlrpc_method(False) -def state_list(search_str="", max_count=0): - """Get a list of state structures matching the given search string.""" - try: - if len(search_str) > 0: - states = State.objects.filter(name__icontains = search_str) - else: - states = State.objects.all() - - if max_count > 0: - return map(state_to_dict, states)[:max_count] - else: - return map(state_to_dict, states) - except: - return [] - -@xmlrpc_method(False) -def state_get(state_id): - """Return structure for the given state ID.""" - try: - state = State.objects.filter(id = state_id)[0] - return state_to_dict(state) - except: - return {} |