Skip to content

Commit

Permalink
Merge pull request #194 from wildfish/feature/issue_191_clearable
Browse files Browse the repository at this point in the history
Added STAR_RATINGS_CLEARABLE to add a clearable form
  • Loading branch information
OmegaDroid authored May 11, 2020
2 parents e45d1f9 + d4b7225 commit 3495eaa
Show file tree
Hide file tree
Showing 17 changed files with 306 additions and 37 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
__next__
------------------

* Added setting to allow clearing a rating.
* Fixed some deprecation warnings (thanks @mvillalba)
* Added tests for model url swapping
* Added fallback to DOM based CSRF token to handle CSRF_COOKIE_HTTPONLY
Expand Down
11 changes: 9 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,20 @@ To prohibit users from altering their ratings set
``STAR_RATINGS_RERATE = False`` in settings.py

To allow users to delete a rating by selecting the same score again, set
``STAR_RATINGS_RERATE_SAME_DELETE = False`` in settings.py, note
``STAR_RATINGS_RERATE_SAME_DELETE = True`` in settings.py, note
that ``STAR_RATINGS_RERATE`` must be True if this is set.

To allow uses to delete a rating via a clear button, set
``STAR_RATINGS_CLEARABLE = True``` in settings.py. This can be used
with or without STAR_RATINGS_RERATE.

To change the number of rating stars, set ``STAR_RATINGS_RANGE``
(defaults to 5)

To enable anonymous rating set ``STAR_RATINGS_ANONYMOUS = True``.
To enable anonymous rating set ``STAR_RATINGS_CLEARABLE = True``.

Please note that ``STAR_RATINGS_RERATE``, ``STAR_RATINGS_RERATE_SAME_DELETE`` and ``STAR_RATINGS_CLEARABLE``
will have no affect when anonymous rating is enabled.

Anonymous Rating
================
Expand Down
4 changes: 4 additions & 0 deletions star_ratings/app_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ class Settings:
def STAR_RATINGS_RANGE(self):
return getattr(settings, 'STAR_RATINGS_RANGE', 5)

@property
def STAR_RATINGS_CLEARABLE(self):
return getattr(settings, 'STAR_RATINGS_CLEARABLE', False)

@property
def STAR_RATINGS_ANONYMOUS(self):
return getattr(settings, 'STAR_RATINGS_ANONYMOUS', False)
Expand Down
10 changes: 8 additions & 2 deletions star_ratings/forms.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from __future__ import absolute_import
from django import forms

from . import get_star_ratings_rating_model
from . import get_star_ratings_rating_model, app_settings
from .models import UserRating


class CreateUserRatingForm(forms.ModelForm):
clear = forms.BooleanField(required=False)

class Meta:
model = UserRating
exclude = [
Expand All @@ -19,10 +21,14 @@ def __init__(self, obj=None, *args, **kwargs):
self.obj = obj
super(CreateUserRatingForm, self).__init__(*args, **kwargs)

if self.data.get('clear', False) and app_settings.STAR_RATINGS_CLEARABLE:
self.fields['score'].required = False

def save(self, commit=True):
return get_star_ratings_rating_model().objects.rate(
self.obj,
self.cleaned_data['score'],
user=self.cleaned_data['user'],
ip=self.cleaned_data['ip']
ip=self.cleaned_data['ip'],
clear=self.cleaned_data['clear'],
)
29 changes: 18 additions & 11 deletions star_ratings/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,13 @@ def ratings_for_instance(self, instance):
warn("RatingManager method 'ratings_for_instance' has been renamed to 'for_instance'. Please change uses of 'Rating.objects.ratings_for_instance' to 'Rating.objects.for_instance' in your code.", DeprecationWarning)
return self.for_instance(instance)

def rate(self, instance, score, user=None, ip=None):
def delete_existing(self, existing_rating):
rating = existing_rating.rating
existing_rating.delete()
rating._user_rating_deleted = True
return rating

def rate(self, instance, score, user=None, ip=None, clear=False):
if isinstance(instance, self.model):
raise TypeError("Rating manager 'rate' expects model to be rated, not Rating model.")
ct = ContentType.objects.get_for_model(instance)
Expand All @@ -44,20 +50,21 @@ def rate(self, instance, score, user=None, ip=None):
existing_rating = UserRating.objects.for_instance_by_user(instance, user)

if existing_rating:
if not app_settings.STAR_RATINGS_RERATE:
if not app_settings.STAR_RATINGS_CLEARABLE and not app_settings.STAR_RATINGS_RERATE:
raise ValidationError(_('Already rated.'))

same_as_previous = existing_rating.score == score

if app_settings.STAR_RATINGS_RERATE_SAME_DELETE and same_as_previous:
rating = existing_rating.rating
existing_rating.delete()
rating._user_rating_deleted = True
return rating

existing_rating.score = score
existing_rating.save()
return existing_rating.rating
if (app_settings.STAR_RATINGS_CLEARABLE and clear) or \
(app_settings.STAR_RATINGS_RERATE_SAME_DELETE and same_as_previous):
return self.delete_existing(existing_rating=existing_rating)
elif score is not None:
existing_rating.score = score
existing_rating.save()
return existing_rating.rating
elif clear:
# user has cleared without an existing_rating
return
else:
rating, created = self.get_or_create(content_type=ct, object_id=instance.pk)
return UserRating.objects.create(user=user, score=score, rating=rating, ip=ip).rating
Expand Down
5 changes: 5 additions & 0 deletions star_ratings/static/star-ratings/css/star-ratings.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,8 @@
display: inline-block;
position: relative;
}

/* line 39, ../sass/star-ratings.scss */
.star-ratings-clear-hidden {
display: none;
}
33 changes: 26 additions & 7 deletions star_ratings/static/star-ratings/js/dist/star-ratings.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,17 @@ function bindRatings(el) {

el.onmouseenter = function () {
var maxRating = getMaxRating(this);
var score = this.querySelector('[name=score]').value;
var parent = utils.findParent(this, "star-ratings");
parent.querySelector(".star-ratings-rating-foreground").style.width = 100 / maxRating * score + "%";
var scoreEl = this.querySelector('[name=score]');

if (scoreEl){
var parent = utils.findParent(this, "star-ratings");
parent.querySelector(".star-ratings-rating-foreground").style.width = 100 / maxRating * scoreEl.value + "%";
}
};

el.onmouseleave = function () {
var avgRating = getAvgRating(this);
var maxRating = getMaxRating(this);
var score = this.querySelector('[name=score]').value;
var parent = utils.findParent(this, "star-ratings");
var percentage = 100 / maxRating * avgRating + "%";
parent.querySelector(".star-ratings-rating-foreground").style.width = percentage;
Expand Down Expand Up @@ -145,11 +147,10 @@ function updateRating(rating, sender) {
}

parent.setAttribute("data-avg-rating", rating.average);

var avgElem = parent.getElementsByClassName("star-ratings-rating-average")[0];
if(avgElem) {
valueElem = avgElem.getElementsByClassName('star-ratings-rating-value')[0];
if (valueElem) {
if (valueElem ) {
var average = rating.average.toFixed(2);

// suppress . if 0.
Expand All @@ -170,20 +171,38 @@ function updateRating(rating, sender) {
}

var userElem = parent.getElementsByClassName("star-ratings-rating-user")[0];
var clearElem = parent.getElementsByClassName('star-ratings-clear')[0];
if(userElem) {
valueElem = userElem.getElementsByClassName('star-ratings-rating-value')[0];
if (valueElem) {
if (rating.user_rating == null && valueElem.getAttribute('data-when-null', false)){
rating.user_rating = valueElem.getAttribute('data-when-null');
}

if (clearElem) {
clearElem.classList.remove('star-ratings-clear-visible');
clearElem.classList.add('star-ratings-clear-hidden');
}
}
else {
if (clearElem) {
clearElem.classList.remove('star-ratings-clear-hidden');
clearElem.classList.add('star-ratings-clear-visible');
}
}
valueElem.innerHTML = rating.user_rating;
}
}

parent.querySelector(".star-ratings-rating-foreground").style.width = rating.percentage + '%';
}

function toggleClass (el, cls) {
if (el.classList.contains(cls)) {
el.classList.remove(cls);
} else {
el.classList.add(cls);
}
}

function showError (errors, sender) {
var parent = utils.findParent(sender, "star-ratings");
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 3495eaa

Please sign in to comment.