show rating stars + counts on detail page (bug 755922)
This commit is contained in:
Родитель
146095af48
Коммит
d748e6df6b
|
@ -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),
|
||||
|
|
Загрузка…
Ссылка в новой задаче