regex=r'^[^/]+$',
min_length=1,
max_length=50,
- label='Name',
+ required=False,
error_messages={'invalid': "Bundle names can't contain slashes"},
)
class CreateBundleForm(BundleForm):
def clean_name(self):
name = self.cleaned_data['name']
+ if not name:
+ raise forms.ValidationError(
+ 'No bundle name was specified', code='invalid'
+ )
+
count = Bundle.objects.filter(
owner=self.instance.owner, name=name
).count()
if count > 0:
raise forms.ValidationError(
- 'A bundle called %s already exists' % name
+ 'A bundle called %(name)s already exists',
+ code='invalid',
+ params={'name': name},
)
return name
--- /dev/null
+<div class="patch-forms" id="patch-forms">
+{% if patch_form %}
+ <div id="patch-form-properties" class="patch-form">
+ <div id="patch-form-state">
+ {{ patch_form.state.errors }}
+ {{ patch_form.state }}
+ </div>
+ <div id="patch-form-delegate">
+ {{ patch_form.delegate.errors }}
+ {{ patch_form.delegate }}
+ </div>
+ <div id="patch-form-archive">
+ {{ patch_form.archived.errors }}
+ {{ patch_form.archived.label_tag }} {{ patch_form.archived }}
+ </div>
+ <button class="patch-form-submit btn btn-primary" name="action" value="update">
+ Update
+ </button>
+ </div>
+{% endif %}
+{% if user.is_authenticated %}
+ <div id="patch-form-bundle" class="patch-form">
+ <div id="create-bundle">
+ {{ create_bundle_form.name.errors }}
+ {{ create_bundle_form.name }}
+ <input class="patch-form-submit btn btn-primary" name="action" value="Create" type="submit"/>
+ </div>
+ {% if bundles %}
+ <div id="add-to-bundle">
+ <select class="add-bundle" name="bundle_id">
+ <option value="" selected>Add to bundle</option>
+ {% for bundle in bundles %}
+ <option value="{{bundle.id}}">{{bundle.name}}</option>
+ {% endfor %}
+ </select>
+ <input class="patch-form-submit btn btn-primary" name="action" value="Add" type="submit"/>
+ </div>
+ {% endif %}
+ {% if bundle %}
+ <div id="remove-bundle">
+ <input type="hidden" name="removed_bundle_id" value="{{bundle.id}}"/>
+ <button class="patch-form-submit btn btn-primary" name="action" value="Remove">
+ Remove from bundle
+ </button>
+ </div>
+ {% endif %}
+ </div>
+{% endif %}
+</div>
{% endblock %}
{% include "patchwork/partials/filters.html" %}
-{% include "patchwork/partials/pagination.html" %}
{% if order.editable %}
<table class="patch-list">
</table>
{% endif %}
-{% if page.paginator.long_page and user.is_authenticated %}
-<div class="floaty">
- <a title="jump to form" href="#patch-forms">
- <span style="font-size: 120%">▾</span>
- </a>
-</div>
-{% endif %}
-
-<form method="post">
+<form id="patch-list-form" method="post">
{% csrf_token %}
<input type="hidden" name="form" value="patch-list-form"/>
<input type="hidden" name="project" value="{{project.id}}"/>
+
+ <div class="patch-list-actions">
+{% include "patchwork/partials/patch-forms.html" %}
+{% include "patchwork/partials/pagination.html" %}
+ </div>
<table id="patch-list" class="table table-hover table-extra-condensed table-striped pw-list" data-toggle="checkboxes" data-range="true">
<thead>
<tr>
<tbody>
{% for patch in page.object_list %}
- <tr id="patch-row:{{patch.id}}">
+ <tr id="patch-row:{{patch.id}}" data-patch-id="{{patch.id}}">
{% if user.is_authenticated %}
<td id="select-patch:{{patch.id}}" style="text-align: center;">
<input type="checkbox" name="patch_id:{{patch.id}}"/>
{% if page.paginator.count %}
{% include "patchwork/partials/pagination.html" %}
-
- <div class="patch-forms" id="patch-forms">
-
-{% if patch_form %}
- <div class="patch-form patch-form-properties">
- <h3>Properties</h3>
- <table class="form">
- <tr>
- <th>Change state:</th>
- <td>
- {{ patch_form.state }}
- {{ patch_form.state.errors }}
- </td>
- </tr>
- <tr>
- <th>Delegate to:</th>
- <td>
- {{ patch_form.delegate }}
- {{ patch_form.delegate.errors }}
- </td>
- </tr>
- <tr>
- <th>Archive:</th>
- <td>
- {{ patch_form.archived }}
- {{ patch_form.archived.errors }}
- </td>
- </tr>
- <tr>
- <td></td>
- <td>
- <input type="submit" name="action" value="{{patch_form.action}}"/>
- </td>
- </tr>
- </table>
- </div>
-{% endif %}
-
-{% if user.is_authenticated %}
- <div class="patch-form patch-form-bundle">
- <h3>Bundling</h3>
- <table class="form">
- <tr>
- <td>Create bundle:</td>
- <td>
- <input type="text" name="bundle_name"/>
- <input name="action" value="Create" type="submit"/>
- </td>
- </tr>
-{% if bundles %}
- <tr>
- <td>Add to bundle:</td>
- <td>
- <select name="bundle_id">
-{% for bundle in bundles %}
- <option value="{{bundle.id}}">{{bundle.name}}</option>
-{% endfor %}
- </select>
- <input name="action" value="Add" type="submit"/>
- </td>
- </tr>
-{% endif %}
-{% if bundle %}
- <tr>
- <td>Remove from bundle:</td>
- <td>
- <input type="hidden" name="removed_bundle_id" value="{{bundle.id}}"/>
- <input name="action" value="Remove" type="submit"/>
- </td>
- </tr>
-{% endif %}
- </table>
- </div>
-{% endif %}
- <div style="clear: both;">
- </div>
- </div>
{% endif %}
</form>
{% endif %}
</table>
-<div class="patch-forms">
-{% if patch_form %}
- <div class="patch-form patch-form-properties">
- <h3>Patch Properties</h3>
- <form method="post">
- {% csrf_token %}
- <table class="form">
- <tr>
- <th>Change state:</th>
- <td>
- {{ patch_form.state }}
- {{ patch_form.state.errors }}
- </td>
- </tr>
- <tr>
- <th>Delegate to:</th>
- <td>
- {{ patch_form.delegate }}
- {{ patch_form.delegate.errors }}
- </td>
- </tr>
- <tr>
- <th>Archived:</th>
- <td>
- {{ patch_form.archived }}
- {{ patch_form.archived.errors }}
- </td>
- </tr>
- <tr>
- <td></td>
- <td>
- <input type="submit" value="Update">
- </td>
- </tr>
- </table>
- </form>
- </div>
-{% endif %}
-
-{% if create_bundle_form %}
- <div class="patch-form patch-form-bundle">
- <h3>Bundling</h3>
- <table class="form">
- <tr>
- <td>Create bundle:</td>
- <td>
-{% if create_bundle_form.non_field_errors %}
- <dd class="errors">{{create_bundle_form.non_field_errors}}</dd>
-{% endif %}
- <form method="post">
- {% csrf_token %}
- <input type="hidden" name="action" value="createbundle"/>
-{% if create_bundle_form.name.errors %}
- <dd class="errors">{{create_bundle_form.name.errors}}</dd>
-{% endif %}
- {{ create_bundle_form.name }}
- <input value="Create" type="submit"/>
- </form>
- </td>
- </tr>
-{% if bundles %}
- <tr>
- <td>Add to bundle:</td>
- <td>
- <form method="post">
-{% csrf_token %}
- <input type="hidden" name="action" value="addtobundle"/>
- <select name="bundle_id"/>
-{% for bundle in bundles %}
- <option value="{{bundle.id}}">{{bundle.name}}</option>
-{% endfor %}
- </select>
- <input value="Add" type="submit"/>
- </form>
- </td>
- </tr>
-{% endif %}
- </table>
- </div>
-{% endif %}
- <div style="clear: both;">
- </div>
-</div>
+<form id="patch-list-form" method="POST">
+ {% csrf_token %}
+ {% include "patchwork/partials/patch-forms.html" %}
+</form>
{% if submission.pull_url %}
<h2>Pull-request</h2>
newbundlename = 'testbundle-new'
params = {
'form': 'patch-list-form',
- 'bundle_name': newbundlename,
+ 'name': newbundlename,
'action': 'Create',
'project': self.project.id,
}
params = {
'form': 'patch-list-form',
- 'bundle_name': newbundlename,
+ 'name': newbundlename,
'action': 'Create',
'project': self.project.id,
'patch_id:%d' % patch.id: 'checked',
params = {
'form': 'patch-list-form',
- 'bundle_name': '',
+ 'name': '',
'action': 'Create',
'project': self.project.id,
'patch_id:%d' % patch.id: 'checked',
params = {
'form': 'patch-list-form',
- 'bundle_name': newbundlename,
+ 'name': newbundlename,
'action': 'Create',
'project': self.project.id,
'patch_id:%d' % patch.id: 'checked',
)
self.assertNotContains(response, 'Bundle %s created' % newbundlename)
- self.assertContains(response, 'You already have a bundle called')
+ self.assertContains(
+ response, 'A bundle called %s already exists' % newbundlename
+ )
self.assertEqual(Bundle.objects.count(), n_bundles)
self.assertEqual(bundle.patches.count(), 1)
newbundlename = 'testbundle-new'
patch = self.patches[0]
- params = {'name': newbundlename, 'action': 'createbundle'}
+ params = {'name': newbundlename, 'action': 'Create'}
response = self.client.post(
reverse(
newbundlename = self.bundle.name
patch = self.patches[0]
- params = {'name': newbundlename, 'action': 'createbundle'}
+ params = {'name': newbundlename, 'action': 'Create'}
response = self.client.post(
reverse(
class BundleAddFromPatchTest(BundleTestBase):
def test_add_to_empty_bundle(self):
patch = self.patches[0]
- params = {'action': 'addtobundle', 'bundle_id': self.bundle.id}
+ params = {'action': 'Add', 'bundle_id': self.bundle.id}
response = self.client.post(
reverse(
self.assertContains(
response,
- 'added to bundle "%s"' % self.bundle.name,
+ 'added to bundle %s' % self.bundle.name,
count=1,
)
def test_add_to_non_empty_bundle(self):
self.bundle.append_patch(self.patches[0])
patch = self.patches[1]
- params = {'action': 'addtobundle', 'bundle_id': self.bundle.id}
+ params = {'action': 'Add', 'bundle_id': self.bundle.id}
response = self.client.post(
reverse(
self.assertContains(
response,
- 'added to bundle "%s"' % self.bundle.name,
+ 'added to bundle %s' % self.bundle.name,
count=1,
)
# need to define our querystring explicity to enforce ordering
params = {
'form': 'patch-list-form',
- 'bundle_name': newbundlename,
+ 'name': newbundlename,
'action': 'Create',
'project': self.project.id,
}
#
# SPDX-License-Identifier: GPL-2.0-or-later
+import json
+
from django.contrib import messages
from django.shortcuts import get_object_or_404
from django.db.models import Prefetch
from patchwork.filters import Filters
+from patchwork.forms import CreateBundleForm
from patchwork.forms import MultiplePatchForm
from patchwork.models import Bundle
from patchwork.models import BundlePatch
# TODO(stephenfin): Refactor this to break it into multiple, testable functions
-def set_bundle(request, project, action, data, patches, context):
+def set_bundle(request, project, action, data, patches):
# set up the bundle
bundle = None
user = request.user
if action == 'create':
- bundle_name = data['bundle_name'].strip()
- if '/' in bundle_name:
- return ["Bundle names can't contain slashes"]
-
- if not bundle_name:
- return ['No bundle name was specified']
-
- if Bundle.objects.filter(owner=user, name=bundle_name).count() > 0:
- return ['You already have a bundle called "%s"' % bundle_name]
-
+ bundle_name = data['name'].strip()
bundle = Bundle(owner=user, project=project, name=bundle_name)
- bundle.save()
- messages.success(request, 'Bundle %s created' % bundle.name)
+ create_bundle_form = CreateBundleForm(
+ instance=bundle, data=request.POST
+ )
+ if create_bundle_form.is_valid():
+ create_bundle_form.save()
+ add_bundle_patches(request, patches, bundle)
+ bundle.save()
+ messages.success(request, 'Bundle %s created' % bundle.name)
+ else:
+ formErrors = json.loads(create_bundle_form.errors.as_json())
+ errors = [e['message'] for e in formErrors['name']]
+ return errors
elif action == 'add':
+ if not data['bundle_id']:
+ return ['No bundle was selected']
bundle = get_object_or_404(Bundle, id=data['bundle_id'])
+ add_bundle_patches(request, patches, bundle)
elif action == 'remove':
bundle = get_object_or_404(Bundle, id=data['removed_bundle_id'])
-
- if not bundle:
- return ['no such bundle']
-
- for patch in patches:
- if action in ['create', 'add']:
- bundlepatch_count = BundlePatch.objects.filter(
- bundle=bundle, patch=patch
- ).count()
- if bundlepatch_count == 0:
- bundle.append_patch(patch)
- messages.success(
- request,
- "Patch '%s' added to bundle %s"
- % (patch.name, bundle.name),
- )
- else:
- messages.warning(
- request,
- "Patch '%s' already in bundle %s"
- % (patch.name, bundle.name),
- )
- elif action == 'remove':
+ for patch in patches:
try:
bp = BundlePatch.objects.get(bundle=bundle, patch=patch)
bp.delete()
"Patch '%s' removed from bundle %s\n"
% (patch.name, bundle.name),
)
+ return []
- bundle.save()
- return []
+def add_bundle_patches(request, patches, bundle):
+ for patch in patches:
+ bundlepatch_count = BundlePatch.objects.filter(
+ bundle=bundle, patch=patch
+ ).count()
+ if bundlepatch_count == 0:
+ bundle.append_patch(patch)
+ bundle.save()
+ messages.success(
+ request,
+ "Patch '%s' added to bundle %s" % (patch.name, bundle.name),
+ )
+ else:
+ messages.warning(
+ request,
+ "Patch '%s' already in bundle %s" % (patch.name, bundle.name),
+ )
def generic_list(
data = None
user = request.user
properties_form = None
+ create_bundle_form = None
if user.is_authenticated:
# we only pass the post data to the MultiplePatchForm if that was
data_tmp = data
properties_form = MultiplePatchForm(project, data=data_tmp)
+ create_bundle_form = CreateBundleForm()
if request.method == 'POST' and data.get('form') == 'patch-list-form':
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):
+ if data.get('name', False):
action = 'create'
ps = Patch.objects.filter(id__in=get_patch_ids(data))
if action in bundle_actions:
- errors = set_bundle(request, project, action, data, ps, context)
+ errors = set_bundle(request, project, action, data, ps)
elif properties_form and action == properties_form.action:
errors = process_multiplepatch_form(
{
'page': paginator.current_page,
'patch_form': properties_form,
+ 'create_bundle_form': create_bundle_form,
'project': project,
'order': order,
}
from patchwork.forms import CreateBundleForm
from patchwork.forms import PatchForm
-from patchwork.models import Bundle
from patchwork.models import Cover
from patchwork.models import Patch
from patchwork.models import Project
from patchwork.views import generic_list
+from patchwork.views import set_bundle
from patchwork.views.utils import patch_to_mbox
from patchwork.views.utils import series_patch_to_mbox
form = None
create_bundle_form = None
+ errors = None
if editable:
form = PatchForm(instance=patch)
if action:
action = action.lower()
- if action == 'createbundle':
- bundle = Bundle(owner=request.user, project=project)
- create_bundle_form = CreateBundleForm(
- instance=bundle, data=request.POST
+ if action in ['create', 'add']:
+ errors = set_bundle(
+ request, project, action, request.POST, [patch]
)
- if create_bundle_form.is_valid():
- create_bundle_form.save()
- bundle.append_patch(patch)
- bundle.save()
- create_bundle_form = CreateBundleForm()
- messages.success(request, 'Bundle %s created' % bundle.name)
- elif action == 'addtobundle':
- bundle = get_object_or_404(
- Bundle, id=request.POST.get('bundle_id')
- )
- if bundle.append_patch(patch):
- messages.success(
- request,
- 'Patch "%s" added to bundle "%s"'
- % (patch.name, bundle.name),
- )
- else:
- messages.error(
- request,
- 'Failed to add patch "%s" to bundle "%s": '
- 'patch is already in bundle' % (patch.name, bundle.name),
- )
- # all other actions require edit privs
elif not editable:
return HttpResponseForbidden()
- elif action is None:
+ elif action == 'update':
form = PatchForm(data=request.POST, instance=patch)
if form.is_valid():
form.save()
context['project'] = patch.project
context['related_same_project'] = related_same_project
context['related_different_project'] = related_different_project
+ if errors:
+ context['errors'] = errors
return render(request, 'patchwork/submission.html', context)