Skip to content
Browse files

Added reatings, removed snippets

  • Loading branch information...
1 parent eb8ea12 commit a08c87a9dd0c76a04e319f445503f800cbeb1498 @elpaso elpaso committed Oct 7, 2012
View
118 ...ins/migrations/0007_auto__add_field_plugin_rating_votes__add_field_plugin_rating_score.py
@@ -0,0 +1,118 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Plugin.rating_votes'
+ db.add_column('plugins_plugin', 'rating_votes',
+ self.gf('django.db.models.fields.PositiveIntegerField')(default=0, blank=True),
+ keep_default=False)
+
+ # Adding field 'Plugin.rating_score'
+ db.add_column('plugins_plugin', 'rating_score',
+ self.gf('django.db.models.fields.IntegerField')(default=0, blank=True),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'Plugin.rating_votes'
+ db.delete_column('plugins_plugin', 'rating_votes')
+
+ # Deleting field 'Plugin.rating_score'
+ db.delete_column('plugins_plugin', 'rating_score')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'plugins.plugin': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Plugin'},
+ 'author': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'plugins_created_by'", 'to': "orm['auth.User']"}),
+ 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'deprecated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {}),
+ 'downloads': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'featured': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'homepage': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'modified_on': ('django.db.models.fields.DateTimeField', [], {}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
+ 'owners': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'package_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
+ 'rating_score': ('django.db.models.fields.IntegerField', [], {'default': '0', 'blank': 'True'}),
+ 'rating_votes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
+ 'repository': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'tracker': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
+ },
+ 'plugins.pluginversion': {
+ 'Meta': {'ordering': "('plugin', '-version', 'experimental')", 'unique_together': "(('plugin', 'version'),)", 'object_name': 'PluginVersion'},
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'changelog': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'downloads': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'experimental': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'min_qg_version': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'package': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'plugin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['plugins.Plugin']"}),
+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '32'})
+ },
+ 'taggit.tag': {
+ 'Meta': {'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'})
+ },
+ 'taggit.taggeditem': {
+ 'Meta': {'object_name': 'TaggedItem'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"})
+ }
+ }
+
+ complete_apps = ['plugins']
View
9 qgis-app/plugins/models.py
@@ -8,6 +8,7 @@
from django.core.urlresolvers import reverse
from django.conf import settings
import datetime, os, re
+from djangoratings.fields import AnonymousRatingField
# Tagging
from taggit_autosuggest.managers import TaggableManager
@@ -86,7 +87,11 @@ class PopularPlugins(ApprovedPlugins):
Shows only unapproved plugins, sort by downloads
"""
def get_query_set(self):
- return super(PopularPlugins, self).get_query_set().filter(deprecated=False ).order_by('-downloads').distinct()
+ return super(PopularPlugins, self).get_query_set().filter(deprecated=False).extra(
+ select={
+ 'popularity': 'SELECT downloads * (1 + (rating_score/(rating_votes+0.01)/3)) FROM plugins_plugin AS pp WHERE pp.id = plugins_plugin.id'
+ }
+ ).order_by('-popularity').distinct()
class TaggablePlugins (TaggableManager):
@@ -142,6 +147,8 @@ class Plugin (models.Model):
deprecated_objects = DeprecatedPlugins()
popular_objects = PopularPlugins()
+ rating = AnonymousRatingField(range=5, use_cookies=True, can_change_vote=True, allow_delete=True)
+
tags = TaggableManager(blank=True)
View
48 qgis-app/plugins/templates/plugins/plugin_detail.html
@@ -1,9 +1,51 @@
{% extends 'plugins/plugin_base.html' %}{% load i18n %}
+{% block extrajs %}
+{{ block.super }}
+<script type="text/javascript" src="/static/js/jquery-1.5.1.min.js"></script>
+<script type="text/javascript" src="/static/js/jquery.cookie.js"></script>
+<script type="text/javascript" src="/static/jquery-ratings/jquery.ratings.js"></script>
+<script type="text/javascript">
+
+ function csrfSafeMethod(method) {
+ // these HTTP methods do not require CSRF protection
+ return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
+ }
+
+ // Rating
+ jQuery(function(){
+ var csrftoken = jQuery.cookie('csrftoken');
+ jQuery.ajaxSetup({
+ crossDomain: false, // obviates need for sameOrigin test
+ beforeSend: function(xhr, settings) {
+ if (!csrfSafeMethod(settings.type)) {
+ xhr.setRequestHeader("X-CSRFToken", csrftoken);
+ }
+ }
+ });
+ var has_voted = false;
+ jQuery('#rating').ratings(5, {% firstof rating '0' %}).bind('ratingchanged', function(event, data) {
+ jQuery.post('{% url plugin_rate object.pk '0' %}'.replace('/0', '/' + data.rating), function(data){
+ if(!has_voted){
+ has_voted = true;
+ jQuery('#votes').html(parseInt(jQuery('#votes').html())+1);
+ }
+ jQuery('#vote-message').html(data).fadeIn('slow', function(){jQuery(this).fadeOut('slow');});
+ });
+ });
+ });
+</script>
+{% endblock %}
+{% block extracss %}
+<link type="text/css" href="/static/jquery-ratings/jquery.ratings.css" rel="stylesheet" />
+{{ block.super }}
+{% endblock %}
{% block content %}
- <h2>{% trans "Plugin" %}: {{ object.name }}</h2>
- <div class="details">
+
+ <h2>{% trans "Plugin" %}: {{ object.name }}</h2>
+ <div class="plugin-details">
{% if object.deprecated %}<p class="error"><strong>{% trans "This plugin is deprecated!" %}</strong></p>{% endif %}
- <p>{% if object.icon %}<img class="plugin-icon" src="{{ object.icon.url }}" alt="{% trans "Plugin icon" %}" />{% endif %}<strong>{% trans "Description" %}:</strong> {{ object.description }}</p>
+ <p class="plugin-description">{% if object.icon %}<img class="plugin-icon" src="{{ object.icon.url }}" alt="{% trans "Plugin icon" %}" />{% endif %}<strong>{% trans "Description" %}:</strong> {{ object.description }}</p>
+ <div id="rating"> (<span id="votes">{% firstof rating '0' %}</span>) {% trans "votes" %}&nbsp;<span id="vote-message" style="display:none"></span></div>
{% if object.author %}
<p><strong>{% trans "Author"%}:</strong> <a title="{% trans "Plugins by"%} {{ object.author }}" href="{% url author_plugins object.author|urlencode %}">{{ object.author }}</a></p>
{% endif %}
View
1 qgis-app/plugins/templates/plugins/plugin_list.html
@@ -39,6 +39,7 @@
}
});
+
</script>
{% endblock %}
{% block content %}
View
12 qgis-app/plugins/urls.py
@@ -61,6 +61,18 @@
(r'^RPC2/$', 'rpc4django.views.serve_rpc_request'),
)
+
+# plugin rating
+from djangoratings.views import AddRatingFromModel
+
+urlpatterns += patterns('',
+ url(r'rate/(?P<object_id>\d+)/(?P<score>\d+)/', AddRatingFromModel(), {
+ 'app_label': 'plugins',
+ 'model': 'plugin',
+ 'field_name': 'rating',
+ }, name='plugin_rate'),
+)
+
# Plugin detail (keep last)
urlpatterns += patterns('plugins.views',
url(r'^(?P<package_name>[A-Za-z][A-Za-z0-9-_]+)/$', 'plugin_detail', { 'queryset' : Plugin.objects.all() }, name='plugin_detail'),
View
13 qgis-app/plugins/views.py
@@ -20,6 +20,7 @@
from django.views.generic.list_detail import object_list, object_detail
from django.views.decorators.http import require_POST
+from django.views.decorators.csrf import csrf_protect
from django.core.mail import send_mail
from django.contrib.sites.models import Site
@@ -285,7 +286,7 @@ def plugin_upload(request):
return render_to_response('plugins/plugin_upload.html', { 'form' : form }, context_instance=RequestContext(request))
-
+@csrf_protect
def plugin_detail(request, package_name, **kwargs):
"""
Just a wrapper for clean urls
@@ -295,6 +296,13 @@ def plugin_detail(request, package_name, **kwargs):
if check_plugin_access(request.user, plugin) and not (plugin.homepage and plugin.tracker and plugin.repository):
msg = _("Some important informations are missing from the plugin metadata (homepage, tracker or repository). Please consider creating a project on <a href=\"http://hub.qgis.org\">hub.qgis.org</a> and filling the missing metadata.")
messages.warning(request, msg, fail_silently=True)
+ # add rating to context
+ if not kwargs:
+ kwargs = {}
+ kwargs['extra_context'] = {
+ 'rating': int(plugin.rating.get_rating()),
+ 'votes': plugin.rating.votes,
+ }
return object_detail(request, object_id=plugin.pk, **kwargs)
@login_required
@@ -414,8 +422,7 @@ def plugin_manage(request, package_name):
return plugin_delete(request, package_name)
return HttpResponseRedirect(reverse('user_details', args=[username]))
-
-
+
###############################################
View
8 qgis-app/settings.py
@@ -113,7 +113,7 @@
'django.contrib.comments',
'django.contrib.markup',
'django.contrib.syndication',
- 'ratings',
+ #'ratings',
'taggit',
'taggit_autosuggest',
'taggit_templatetags',
@@ -122,7 +122,7 @@
'simplemenu',
'tinymce',
# Tim for django snippets app support
- 'cab', #the django snippets app itself
+ #'cab', #the django snippets app itself
# Tim for Debug toolbar
'debug_toolbar',
# Tim for command extensions so we can run feedjack cron using python manage.py runscript
@@ -138,6 +138,7 @@
# RPC
'rpc4django',
'south',
+ 'djangoratings',
)
TEMPLATE_CONTEXT_PROCESSORS = (
@@ -203,6 +204,9 @@
# Media URL for taggit autocomplete
TAGGIT_AUTOCOMPLETE_JS_BASE_URL=MEDIA_ROOT + '/taggit-autocomplete'
+# ratings
+RATINGS_VOTES_PER_IP=10000
+
# auth overrids
from settings_auth import *
View
BIN qgis-app/static/jquery-ratings/empty-star-16.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN qgis-app/static/jquery-ratings/empty-star.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
15 qgis-app/static/jquery-ratings/example.html
@@ -0,0 +1,15 @@
+<html>
+ <head>
+ <link type="text/css" rel="stylesheet" href="jquery.ratings.css" />
+ <script src="jquery-1.3.2.min.js"></script>
+ <script src="jquery.ratings.js"></script>
+ <script src="example.js"></script>
+ </head>
+ <body>
+ <div id="example-1"></div> <br />
+ Your Rating: <span id="example-rating-1">not set</span>
+ <br /><br />
+ <div id="example-2"></div> <br />
+ Your Rating: <span id="example-rating-2">not set</span>
+ </body>
+</html>
View
9 qgis-app/static/jquery-ratings/example.js
@@ -0,0 +1,9 @@
+$(document).ready(function() {
+ $('#example-1').ratings(10).bind('ratingchanged', function(event, data) {
+ $('#example-rating-1').text(data.rating);
+ });
+
+ $('#example-2').ratings(5).bind('ratingchanged', function(event, data) {
+ $('#example-rating-2').text(data.rating);
+ });
+});
View
BIN qgis-app/static/jquery-ratings/full-star-16.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN qgis-app/static/jquery-ratings/full-star.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
13 qgis-app/static/jquery-ratings/jquery.ratings.css
@@ -0,0 +1,13 @@
+.jquery-ratings-star {
+ width: 16px;
+ height: 16px;
+ background-image: url('empty-star-16.png');
+ background-repeat: no-repeat;
+ position: relative;
+ float: left;
+ margin-right: 2px;
+}
+
+.jquery-ratings-full {
+ background-image: url('full-star-16.png');
+}
View
83 qgis-app/static/jquery-ratings/jquery.ratings.js
@@ -0,0 +1,83 @@
+jQuery.fn.ratings = function(stars, initialRating) {
+
+ //Save the jQuery object for later use.
+ var elements = this;
+
+ //Go through each object in the selector and create a ratings control.
+ return this.each(function() {
+
+ //Make sure intialRating is set.
+ if(!initialRating)
+ initialRating = 0;
+
+ //Save the current element for later use.
+ var containerElement = this;
+
+ //grab the jQuery object for the current container div
+ var container = jQuery(this);
+
+ //Create an array of stars so they can be referenced again.
+ var starsCollection = Array();
+
+ //Save the initial rating.
+ containerElement.rating = initialRating;
+
+ //Set the container div's overflow to auto. This ensure it will grow to
+ //hold all of its children.
+ container.css('overflow', 'auto');
+
+ //create each star
+ for(var starIdx = 0; starIdx < stars; starIdx++) {
+
+ //Create a div to hold the star.
+ var starElement = document.createElement('div');
+
+ //Get a jQuery object for this star.
+ var star = jQuery(starElement);
+
+ //Store the rating that represents this star.
+ starElement.rating = starIdx + 1;
+
+ //Add the style.
+ star.addClass('jquery-ratings-star');
+
+ //Add the full css class if the star is beneath the initial rating.
+ if(starIdx < initialRating) {
+ star.addClass('jquery-ratings-full');
+ }
+
+ //add the star to the container
+ container.append(star);
+ starsCollection.push(star);
+
+ //hook up the click event
+ star.click(function() {
+ //When clicked, fire the 'ratingchanged' event handler. Pass the rating through as the data argument.
+ elements.triggerHandler("ratingchanged", {rating: this.rating});
+ containerElement.rating = this.rating;
+ });
+
+ star.mouseenter(function() {
+ //Highlight selected stars.
+ for(var index = 0; index < this.rating; index++) {
+ starsCollection[index].addClass('jquery-ratings-full');
+ }
+ //Unhighlight unselected stars.
+ for(var index = this.rating; index < stars; index++) {
+ starsCollection[index].removeClass('jquery-ratings-full');
+ }
+ });
+
+ container.mouseleave(function() {
+ //Highlight selected stars.
+ for(var index = 0; index < containerElement.rating; index++) {
+ starsCollection[index].addClass('jquery-ratings-full');
+ }
+ //Unhighlight unselected stars.
+ for(var index = containerElement.rating; index < stars ; index++) {
+ starsCollection[index].removeClass('jquery-ratings-full');
+ }
+ });
+ }
+ });
+};
View
16 qgis-app/static/style/style.css
@@ -46,7 +46,7 @@ table.plugins th {
padding: 0.25em;
border-top: solid 2px #ddd;
border-bottom: solid 2px #ddd;
- font-size: 110%;
+ font-size: 100%;
background: url("../images/bg_menu.png") repeat-x scroll center top transparent;
line-height: 1.4em;
}
@@ -113,3 +113,17 @@ pre, .wp-caption-text {
.fieldWrapper input[type=text] {
width: 100%;
}
+
+
+div.plugin-details {
+ clear: both;
+}
+
+img.plugin-icon {
+ float: right;
+}
+
+div.plugin-details #rating {
+ margin-bottom: 1em;
+ height: 18px;
+}
View
4 qgis-app/static/style/template.css
@@ -17,7 +17,7 @@ body {
body, td{
font-family: Helvetica,Arial,sans-serif;
- font-size: 12px;
+ font-size: 80%;
color: #333;
line-height: 1.6em;
}
@@ -189,7 +189,9 @@ div#user4{
#maincolumn h2{
font-size: 200%;
+/*
padding-bottom:20px;
+*/
}
div.path{
View
7 qgis-app/templates/ratings/stars.html
@@ -0,0 +1,7 @@
+<div class="rating">
+ <input name="rating" type="radio" class="star"/>
+ <input name="rating" type="radio" class="star"/>
+ <input name="rating" type="radio" class="star"/>
+ <input name="rating" type="radio" class="star"/>
+ <input name="rating" type="radio" class="star"/>
+</div>

0 comments on commit a08c87a

Please sign in to comment.
Something went wrong with that request. Please try again.