-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #283 from liqd/2017-05-jd-polls-frontend
Allow votes from the user frontend
- Loading branch information
Showing
22 changed files
with
967 additions
and
149 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
from django.http import Http404 | ||
|
||
from rest_framework import status | ||
from rest_framework.request import clone_request | ||
from rest_framework.response import Response | ||
|
||
|
||
class AllowPUTAsCreateMixin(object): | ||
"""Allow Put-as-create behaviour for incoming requests.""" | ||
|
||
def update(self, request, *args, **kwargs): | ||
partial = kwargs.pop('partial', False) | ||
instance = self.get_object_or_none() | ||
serializer = self.get_serializer(instance, | ||
data=request.data, | ||
partial=partial) | ||
serializer.is_valid(raise_exception=True) | ||
|
||
if instance is None: | ||
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field | ||
lookup_value = self.kwargs[lookup_url_kwarg] | ||
extra_kwargs = {self.lookup_field: lookup_value} | ||
serializer.save(**extra_kwargs) | ||
return Response(serializer.data, status=status.HTTP_201_CREATED) | ||
|
||
serializer.save() | ||
return Response(serializer.data) | ||
|
||
def partial_update(self, request, *args, **kwargs): | ||
kwargs['partial'] = True | ||
return self.update(request, *args, **kwargs) | ||
|
||
def get_object_or_none(self): | ||
try: | ||
return self.get_object() | ||
except Http404: | ||
if self.request.method == 'PUT': | ||
# For PUT-as-create operation, we need to ensure that we have | ||
# relevant permissions, as if this was a POST request. This | ||
# will either raise a PermissionDenied exception, or simply | ||
# return None. | ||
self.check_permissions(clone_request(self.request, 'POST')) | ||
else: | ||
# PATCH requests where the object does not exist should still | ||
# return a 404 response. | ||
raise |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
var React = require('react') | ||
|
||
const Alert = ({type, message}) => { | ||
if (type) { | ||
return ( | ||
<p className={`alert ${type}`}> | ||
{message} | ||
</p> | ||
) | ||
} | ||
|
||
return null | ||
} | ||
|
||
module.exports = Alert |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
var React = require('react') | ||
|
||
const ErrorList = ({errors}) => { | ||
if (errors && errors.label) { | ||
return ( | ||
<ul className="errorlist"> | ||
{errors.label.map(function (msg, index) { | ||
return <li key={msg}>{msg}</li> | ||
})} | ||
</ul> | ||
) | ||
} | ||
|
||
return null | ||
} | ||
|
||
module.exports = ErrorList |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
from django.shortcuts import get_object_or_404 | ||
from rest_framework import mixins | ||
from rest_framework import viewsets | ||
|
||
from adhocracy4.api.permissions import ViewSetRulesPermission | ||
from apps.contrib.api.mixins import AllowPUTAsCreateMixin | ||
from .models import Poll | ||
from .models import Question | ||
from .models import Vote | ||
from .serializers import PollSerializer | ||
from .serializers import VoteSerializer | ||
|
||
|
||
class PollViewSet(mixins.UpdateModelMixin, | ||
viewsets.GenericViewSet): | ||
queryset = Poll.objects.all() | ||
serializer_class = PollSerializer | ||
permission_classes = (ViewSetRulesPermission,) | ||
|
||
def get_permission_object(self): | ||
poll = self.get_object() | ||
return poll.module | ||
|
||
|
||
class VoteViewSetRulesPermission(ViewSetRulesPermission): | ||
"""Ensures the permission object is returned on update.""" | ||
|
||
non_object_actions = ['list', 'create', 'update'] | ||
|
||
|
||
class VoteViewSet(AllowPUTAsCreateMixin, | ||
mixins.UpdateModelMixin, | ||
viewsets.GenericViewSet): | ||
queryset = Vote.objects.all() | ||
serializer_class = VoteSerializer | ||
permission_classes = (VoteViewSetRulesPermission,) | ||
|
||
def dispatch(self, request, *args, **kwargs): | ||
self.question_pk = int(kwargs['pk']) | ||
return super().dispatch(request, *args, **kwargs) | ||
|
||
@property | ||
def question(self): | ||
return get_object_or_404( | ||
Question, | ||
pk=self.question_pk | ||
) | ||
|
||
def get_object(self): | ||
return get_object_or_404( | ||
Vote, | ||
creator=self.request.user, | ||
choice__question=self.question_pk | ||
) | ||
|
||
def get_permission_object(self): | ||
return self.question.poll.module | ||
|
||
def get_serializer_context(self): | ||
context = super(VoteViewSet, self).get_serializer_context() | ||
context.update({ | ||
'question_pk': self.question_pk, | ||
}) | ||
return context |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
var React = require('react') | ||
var django = require('django') | ||
var ErrorList = require('../../contrib/static/js/ErrorList') | ||
|
||
let ChoiceForm = React.createClass({ | ||
handleLabelChange: function (e) { | ||
var index = this.props.index | ||
var label = e.target.value | ||
this.props.updateChoiceLabel(index, label) | ||
}, | ||
|
||
handleDelete: function () { | ||
this.props.deleteChoice(this.props.index) | ||
}, | ||
|
||
render: function () { | ||
return ( | ||
<div> | ||
<label | ||
htmlFor={'id_choices-' + this.props.key + '-name'}> | ||
{django.gettext('Choice:')} | ||
</label> | ||
<input | ||
id={'id_choices-' + this.props.key + '-name'} | ||
name={'choices-' + this.props.key + '-name'} | ||
type="text" | ||
defaultValue={this.props.choice.label} | ||
onChange={this.handleLabelChange} /> | ||
<div className="button-group"> | ||
<button | ||
className="button" | ||
onClick={this.handleDelete} | ||
type="button"> | ||
<i className="fa fa-trash" /> | ||
</button> | ||
</div> | ||
<ErrorList errors={this.props.errors} /> | ||
</div> | ||
) | ||
} | ||
}) | ||
|
||
module.exports = ChoiceForm |
Oops, something went wrong.