show rating stars + counts on detail page (bug 755922)

This commit is contained in:
Chris Van 2012-06-11 22:24:22 -07:00
Родитель 146095af48
Коммит d748e6df6b
11 изменённых файлов: 181 добавлений и 172 удалений

Просмотреть файл

@ -10,7 +10,7 @@ import amo
@register.function
def emaillink(email, title=None):
def emaillink(email, title=None, klass=None):
if not email:
return ""
@ -28,8 +28,8 @@ def emaillink(email, title=None):
else:
title = '<span class="emaillink">%s</span>' % fallback
node = u'<a href="#">%s</a><span class="emaillink js-hidden">%s</span>' % (
title, fallback)
node = (u'<a%s href="#">%s</a><span class="emaillink js-hidden">%s</span>'
% ((' class="%s"' % klass) if klass else '', title, fallback))
return jinja2.Markup(node)

Просмотреть файл

@ -1,5 +1,25 @@
@import 'lib';
a.arrow {
position: relative;
&:after {
.arrow;
content: "";
display: block;
opacity: .7;
position: absolute;
top: 15px;
right: 10px;
height: 18px;
width: 18px;
}
&:hover, &:active {
&:after {
opacity: 1;
}
}
}
.button, button {
border: 0;

Просмотреть файл

@ -331,34 +331,51 @@ h1 .num {
}
}
.support ul {
margin-top: 47px;
list-style-type: none;
padding: 0;
.developer-comments {
margin-bottom: 10px;
}
.support {
font-size: 14px;
li {
float: left;
margin-bottom: 10px;
min-height: 100px;
.width(7);
h3 {
font-size: 22px;
}
p {
margin: 0;
a, time {
display: inline-block;
padding: 5px 20px 5px 0;
}
a {
background: url(../../img/mkt/arrows/support.png) no-repeat 100% 50%;
.average-rating {
.fat-button;
.width(4);
margin: 10px 0;
}
.overall-reviews {
text-align: left;
&:hover {
span {
color: darken(@green, 5%);
text-decoration: none;
}
}
}
a, time {
display: inline-block;
padding: 5px 20px 5px 0;
}
ul {
margin: 0;
list-style-type: none;
padding: 0;
}
li {
.width(7);
float: left;
margin-bottom: 10px;
min-height: 100px;
}
h3 {
font-size: 22px;
}
p {
margin: 0;
}
}
.html-rtl .support ul {
a, time {
.html-rtl .support {
p a, time {
padding: 5px 0 5px 20px;
}
}
@ -383,26 +400,45 @@ h1 .num {
}
}
// 7 columns
@media (max-width: @7col) {
.product-details {
.actions {
// Anything larger than mobile.
@media (min-width: @4col) {
#reviews {
margin: 40px 0 20px;
}
.support li {
margin-top: 20px;
}
.not-rated {
font-size: 16px;
}
.support {
p a.arrow:after {
background-image: url(../../img/mkt/arrows/support.png);
background-position: 100% 0;
right: 0;
top: 8px;
}
}
.support ul li {
}
@media (max-width: @7col) {
.support li {
.width(2);
}
}
//4 columns
@media (max-width: @4col) {
#page > .description {
border-width: 1px 0;
padding: 10px;
.summary {
margin: 0 0 5px;
max-width: 100%;
width: 100%;
}
.show .more {
margin-top: 15px;
}
a.collapse.narrow {
.transition(none);
display: block;
@ -565,45 +601,43 @@ h1 .num {
}
}
}
.support ul {
li {
margin-bottom: 15px;
min-height: inherit;
}
#add-first-review {
display: none;
}
.support {
.not-rated,
.average-rating {
margin: 20px 0 10px;
}
.not-rated {
color: @medium-gray;
}
.average-rating {
width: 100%;
}
a {
.fat-button;
}
.overall-reviews {
text-align: left;
margin: 25px 0 0;
+ ul {
margin: 0;
}
}
ul {
margin: 15px 0 0;
li {
.border-box;
float: left;
margin: 10px 0 0;
padding-right: 5px;
width: 50%;
&:nth-child(2n) {
padding: 0 0 0 5px;
}
li {
.border-box;
float: left;
margin: 10px 0 0;
min-height: inherit;
padding-right: 5px;
width: 50%;
&:nth-child(2n) {
padding: 0 0 0 5px;
}
}
dl {
margin: 20px 0 15px;
dt, dd {
color: @text;
display: inline-block;
}
dt {
margin-right: 5px;
}
margin: 15px 0 0;
}
dt, dd {
color: @text;
display: inline-block;
}
dt {
margin-right: 5px;
}
}
.previews.slider {
@ -614,12 +648,3 @@ h1 .num {
}
}
.detail #reviews {
margin-top: 45px;
h3 {
margin-bottom: 10px;
}
.not-rated {
font-size: 16px;
}
}

Просмотреть файл

@ -1,24 +1,11 @@
@import 'lib';
.item-arrows() {
&:after {
.arrow;
content: "";
display: block;
opacity: .7;
position: absolute;
top: 15px;
right: 10px;
height: 18px;
width: 18px;
}
.view-more a,
.mkt-tile {
&:hover,
&:active {
background: @faint-gray;
.box-shadow(none);
&:after {
opacity: 1;
}
}
&:active {
.depressed;
@ -233,7 +220,6 @@
}
}
.view-more a {
.item-arrows;
background: @bg;
border-top: 1px solid @light-gray;
display: block;
@ -301,7 +287,6 @@
background: @bg-lite;
height: 74px;
padding: 10px 40px 0 85px;
.item-arrows;
}
}
}

Просмотреть файл

@ -182,15 +182,8 @@ header.product {
}
#submit-review {
form {
.simple-field {
margin: 0;
}
}
.barometer {
font-size: 120%;
line-height: 1.2;
margin-bottom: 20px;
form .simple-field {
margin: 0;
}
.form-footer {
margin-top: 30px;
@ -214,52 +207,6 @@ header.product {
}
}
.barometer {
span {
display: inline-block;
.width(1.5);
&:before {
background: url(../../img/icons/thumbs.png) no-repeat;
content: "";
display: block;
float: left;
margin-right: 10px;
height: 20px;
width: 15px;
}
&:hover {
cursor: pointer;
}
&.voted {
font-weight: bold;
}
}
.upvotes {
color: #00b960;
&:before {
background-position: 0 -42px;
}
&:hover, &.voted {
&:before {
background-position: 0 0;
}
}
}
.downvotes {
color: #d93a40;
&:before {
background-position: 100% 100%;
}
&:hover, &.voted {
&:before {
background-position: 100% -82px;
}
}
}
}
.stars {
background: url(../../img/impala/stars.png) no-repeat left top;
display: inline-block;
@ -302,6 +249,9 @@ header.product {
color: @green;
vertical-align: middle;
}
&:hover span {
text-decoration: none;
}
}
.ratingwidget {

Просмотреть файл

@ -238,7 +238,7 @@ body {
}
.account {
.border-box;
margin: 15px 10px;
margin: 5px 10px;
color: @white;
.button {
padding: 5px 0;
@ -246,7 +246,7 @@ body {
}
}
.footer-links {
margin: 0 0 15px;
margin: 10px 0;
text-align: left;
a {
.border-box;

Просмотреть файл

@ -209,23 +209,31 @@
<section id="support" class="support c">
{% if waffle.switch('ratings') %}
<div id="reviews">
<div id="reviews">
{% if waffle.switch('ratings') %}
<h3 class="wide">{{ _('Reviews') }}</h3>
{% if reviews %}
<a href="{{ product.get_ratings_url() }}">{{ _('Reviews') }}</a>
{% else %}
{% if reviews %}
<a class="overall-reviews average-rating arrow"
href="{{ product.get_ratings_url() }}">
{{ product.average_rating|float|stars }}
<span>
{% trans num=product.total_reviews|numberfmt %}
{{ num }} review
{% pluralize %}
{{ num }} reviews
{% endtrans %}
</span>
</a>
{% endif %}
{% if can_review %}
<a class="overall-reviews button go" id="add-first-review"
<p><a class="overall-reviews arrow" id="add-first-review"
href="{{ product.get_ratings_url('add') }}">
{{ _('Submit a Review') }}</a><p>
{% else %}
<span class="not-rated">
{{ _('This app is not yet rated.') }}</span>
{{ _('Submit a Review') }}</a></p>
{% elif not reviews %}
<p class="not-rated">{{ _('This app is not yet rated.') }}</p>
{% endif %}
{% endif %}
</div>
{% endif %}
</div>
{# TODO: When we retool desktop/tablet to look like mobile, we will for sure
pare this down. But until then... code duplication :( #}
@ -233,7 +241,7 @@
{% if False %}
{# TODO: Link to Request Support page if I've purchased (up front or in-app). What about for free?
https://bugzilla.mozilla.org/show_bug.cgi?id=760287 #}
<li><a href="">{{ _('Support') }}</a></li>
<li><a href="" class="arrow">{{ _('Support') }}</a></li>
{% endif %}
{% if product.support_email %}
<li class="support-email">
@ -277,12 +285,12 @@
{% if product.support_email %}
<p class="support-email">
{{ emaillink(product.support_email.localized_string,
_('Support Email')) }}
_('Support Email'), 'arrow') }}
</p>
{% endif %}
{% if product.support_url %}
<p class="support-url">
<a href="{{ product.support_url|external_url }}">
<a class="arrow" href="{{ product.support_url|external_url }}">
{{ _('Support Site') }}</a>
</p>
{% endif %}
@ -292,7 +300,7 @@
<li class="homepage">
<h3>{{ _('App Homepage') }}</h3>
<p>
<a href="{{ product.homepage|external_url }}">
<a class="arrow" href="{{ product.homepage|external_url }}">
{{ product.homepage }}</a>
</p>
</li>
@ -302,14 +310,14 @@
<h3>{{ _('More Info') }}</h3>
{% if product.privacy_policy %}
<p class="privacy">
<a href="{{ product.get_detail_url('privacy') }}">
<a class="arrow" href="{{ product.get_detail_url('privacy') }}">
{{ _('Privacy Policy') }}
</a>
</p>
{% endif %}
{% if waffle.switch('app-stats') and product.public_stats %}
<p class="view-stats">
<a href="{{ url('mkt.stats.overview', product.app_slug) }}">
<a class="arrow" href="{{ url('mkt.stats.overview', product.app_slug) }}">
{{ _('Statistics') }}
</a>
</p>
@ -325,7 +333,7 @@
{% if abuse_form %}
<li class="abuse">
<h3>{{ _('Report Abuse') }}</h3>
<p><a href="{{ product.get_detail_url('abuse') }}">
<p><a class="arrow" href="{{ product.get_detail_url('abuse') }}">
{{ _('Report') }}</a></p>
</li>
{% endif %}

Просмотреть файл

@ -18,6 +18,7 @@ from amo.utils import memoize_get
from lib.metrics import send_request
from lib.crypto.receipt import SigningError
from lib.cef_loggers import receipt_cef
from reviews.models import Review
from mkt.site import messages
from mkt.webapps.models import create_receipt, Installed, Webapp
@ -30,7 +31,8 @@ addon_all_view = addon_view_factory(qs=Webapp.objects.all)
def detail(request, addon):
"""Product details page."""
ctx = {
'product': addon
'product': addon,
'reviews': Review.objects.latest().filter(addon=addon),
}
if addon.is_public():
ctx['abuse_form'] = AbuseForm(request=request)

Просмотреть файл

@ -47,8 +47,8 @@
</section>
<section data-group="popular" data-shown class="narrow popular view-more full">
<div>
<a href="{{ url('browse.apps')|urlparams(sort='popular') }}">
{{ _('View more') }}</a>
<a href="{{ url('browse.apps')|urlparams(sort='popular') }}"
class="arrow">{{ _('View more') }}</a>
</div>
</section>
<section class="hidden">
@ -60,8 +60,8 @@
</section>
<section data-group="new" class="narrow popular view-more full">
<div>
<a href="{{ url('browse.apps')|urlparams(sort='created') }}">
{{ _('View more') }}</a>
<a href="{{ url('browse.apps')|urlparams(sort='created') }}"
class="arrow">{{ _('View more') }}</a>
</div>
</section>
{% endif %}

Просмотреть файл

@ -4,6 +4,7 @@ from pyquery import PyQuery as pq
import waffle
import amo
from amo.helpers import numberfmt
import amo.tests
from reviews.models import Review
from users.models import UserProfile
@ -106,10 +107,11 @@ class TestCreate(ReviewTest):
eq_(pq(r.content)('#add-first-review').length, 1)
def test_add_link_logged(self):
"""Ensure logged user can see Add Review links."""
# Ensure logged user can see Add Review links.
self.enable_waffle()
r = self.client.get(self.detail)
eq_(pq(r.content)('#add-first-review').length, 1)
doc = pq(r.content)('#review')
eq_(doc('#add-first-review').length, 0)
def test_add_link_dev(self):
# Ensure developer cannot see Add Review links.
@ -174,6 +176,23 @@ class TestCreate(ReviewTest):
r = self.client.get(self.detail)
eq_(pq(r.content)('#add-first-review').length, 1)
def test_review_link(self):
# We have reviews.
self.enable_waffle()
r = self.client.get(self.detail)
rating = int(round(self.webapp.average_rating))
total = numberfmt(self.webapp.total_reviews)
eq_(pq(r.content)('.average-rating').text(),
'Rated %s out of 5 stars %s reviews' % (rating, total))
def test_not_rated(self):
# We don't have any reviews, and I'm not allowed to submit a review.
self.enable_waffle()
Review.objects.all().delete()
self.log_in_dev()
r = self.client.get(self.detail)
eq_(pq(r.content)('.not-rated').length, 1)
def test_add_logged_out(self):
self.client.logout()
r = self.client.get(self.add)

Просмотреть файл

@ -104,7 +104,7 @@ def product_as_dict(request, product, purchased=None, receipt_type=None):
def market_tile(context, product):
request = context['request']
if product.is_webapp():
classes = ['product','mkt-tile']
classes = ['product', 'mkt-tile', 'arrow']
product_dict = product_as_dict(request, product)
data_attrs = {
'product': json.dumps(product_dict, cls=JSONEncoder),