This commit is contained in:
Matt Claypotch 2011-07-19 21:26:38 -07:00
Родитель 60c87b28b4
Коммит 8e2e64f151
15 изменённых файлов: 501 добавлений и 121 удалений

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

@ -191,6 +191,13 @@ def impala_addon_listing_header(context, url_base, sort_opts, selected):
return new_context(**locals())
@register.filter
@jinja2.contextfilter
@register.inclusion_tag('addons/impala/sidebar_listing.html')
def sidebar_listing(context, addon):
return new_context(**locals())
@register.filter
@jinja2.contextfilter
def addon_hovercard(context, addon):

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

@ -6,7 +6,7 @@
<div class="review">
<h3>
{% if review.title %}
<span>{{ review.title }}</span>
<b>{{ review.title }}</b>
{% endif %}
{{ review.rating|stars }}
</h3>
@ -24,7 +24,11 @@
{% endfor %}
{% if addon %}
<p>
<a class="more-info" href="{{ url('reviews.list', addon.slug) }}">
{% if settings.IMPALA_REVIEWS %}
<a class="more-info" href="{{ url('i_reviews.list', addon.slug) }}">
{% else %}
<a class="more-info" href="{{ url('reviews.list', addon.slug) }}">
{% endif %}
{% trans num=addon.total_reviews, cnt=addon.total_reviews|numberfmt %}
See all user reviews
{% pluralize %}

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

@ -0,0 +1,13 @@
<div class="item addon">
<hgroup class="c">
<img id="addon-icon" src="{{ addon.get_icon_url(32) }}" class="icon">
<h2 class="addon"{{ addon.name|locale_html }}>
<a href="{{ addon.get_url_path(impala=True) }}">
{{ addon.name }}
</a>
</h2>
<h4 class="author">
{{ _('by') }} {{ users_list(addon.listed_authors) }}
</h4>
</hgroup>
</div>

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

@ -0,0 +1,109 @@
{% set perms = review_perms or {} %}
{% set outdated = (review.version_id
and review.version_id != addon._current_version_id) %}
{% set is_reply = review.reply_to_id is not none %}
{% set has_reply = review.id in replies %}
{% if flags %}
{% set is_flagged = flags[review.id] %}
{% endif %}
<!-- <div class="review">
<h3>
{% if review.title %}
<span>{{ review.title }}</span>
{% endif %}
{{ review.rating|stars }}
</h3>
<p class="byline">
{% trans user=review.user|user_link, date=review.created|datetime %}
by {{ user }} on {{ date }}
{% endtrans %}
</p>
<p class="description">{{ review.body|nl2br }}</p>
{% if replies[review.id] %}
<a class="more-info" href="{{ replies[review.id].get_url_path() }}">
{{ _("Show the developer's reply to this review") }}</a>
{% endif %}
</div> -->
<div class="review item
{% if is_reply %}reply{% endif %}
{% if is_flagged %}flagged{% endif %}"
id="review-{{ review.id }}"
data-rating="{{ review.rating }}">
<h3>
{% if review.title %}
<b>{{ review.title }}</b>
{% endif %}
{% if not is_reply %}
{{ review.rating|stars }}
{% endif %}
</h3>
<p class="byline">
{% if is_reply %}
{% trans user=review.user|user_link, date=review.created|datetime %}
by {{ user }} <b>(Developer)</b> on {{ date }}
{% endtrans %}
{% else %}
{% trans user=review.user|user_link, date=review.created|datetime %}
by {{ user }} on {{ date }}
{% endtrans %}
{% endif %}
{% if (perms.is_admin or perms.is_editor)
and review.ip_address != '0.0.0.0' %}
<span>[{{ review.ip_address }}]</span>
{% endif %}
<a class="permalink" href="{{ url('reviews.detail', addon.slug, review.id) }}">#</a>
</p>
<p class="description">{{ review.body|nl2br }}</p>
{% if outdated and not is_reply %}
{# L10n: {0} is a version number (like 1.01) #}
<span class="item-note">{{ _('This review is for a previous version of the add-on ({0}).')|f(review.version.version) }}&nbsp;</span>
{% endif %}
{% if page != 'user' and review.previous_count %}
<span class="item-note">
{% with user_review_url = url('reviews.user', addon.slug, review.user.id) %}
{% if review.is_latest %}
{% trans cnt=review.previous_count %}
This user has a <a href="{{ user_review_url }}">previous review</a> of this add-on.
{% pluralize %}
This user has <a href="{{ user_review_url }}">{{ cnt }} previous reviews</a> of this add-on.
{% endtrans %}
{% else %}
{% trans %}
This user has <a href="{{ user_review_url }}">other reviews</a> of this add-on.
{% endtrans %}
{% endif %}
{% endwith %}
</span>
{% endif %}
{% if request.user.is_authenticated() %}
<ul class="item-actions">
{% if is_flagged %}
<li>{{ _('Flagged for review') }}</li>
{% else %}
<li class="review-wrapper">
<a class="flag-review" href="{{ url('reviews.flag', addon.slug, review.id) }}">
{{ _('Report this review') }}</a>
</li>
{% endif %}
{% if not (is_reply or has_reply) and (perms.is_author or perms.is_admin) %}
<li>
<a class="review-delete" href="{{ url('reviews.reply', addon.slug, review.id) }}">
{{ _('Reply to review') }}</a>
</li>
{% endif %}
{% if review.user_id == request.user.id %}
<li>
<a class="review-edit" href="#">
{{ _('Edit review') }}</a>
</li>
{% endif %}
{% if perms.can_delete %}
<li>
<a class="delete-review" href="{{ url('reviews.delete', addon.slug, review.id) }}">
{{ _('Delete review') }}</a>
</li>
{% endif %}
</ul>
{% endif %}
</div>

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

@ -3,7 +3,7 @@
{# L10n: {0} is an add-on name. #}
{% block title %}{{ page_title(_('{0} :: Reviews')|f(addon.name)) }}{% endblock %}
{% block bodyclass %}reviews gutter wide-gutter{% endblock %}
{% block bodyclass %}reviews gutter{% endblock %}
{% block rss_feed %}
<link rel="alternate" type="application/rss+xml"
@ -12,92 +12,91 @@
{% block content %}
{{ impala_breadcrumbs([(addon.type_url(), amo.ADDON_TYPES[addon.type]),
(addon.get_url_path(), addon.name),
(addon.get_url_path(impala=True), addon.name),
(link, _('Reviews'))]) }}
<div class="secondary">
<div class="island">
{{ addon|addon_hovercard }}
<div class="average_rating">
<span>
{{ addon.average_rating|float|stars }}
{% trans total=addon.total_reviews|numberfmt %}
<strong>Average Rating</strong> ({{ total }})
{% endtrans %}
</span>
</div>
<section>
{{ addon|sidebar_listing }}
</section>
<section class="average-rating">
{{ addon.average_rating|float|stars }}
{% trans total=addon.total_reviews|numberfmt %}
<strong>Average</strong> ({{ total }})
{% endtrans %}
</section>
<section>
{% include "reviews/grouped_ratings.html" %}
</section>
<section>
{% if not review_perms.is_author %}
<div>
<a class="button" href="{{ url('reviews.add', addon.slug) }}">
{{ _('Write a New Review') }}</a>
</div>
<a class="button" href="{{ url('reviews.add', addon.slug) }}">
{{ _('Write a New Review') }}</a>
{% endif %}
</section>
</div>
<div class="primary island hero" id="reviews" role="main">
{% block review_header %}
<header>
{# Give a link back to reviews if we're looking at user reviews or a detail page. #}
{% with link = None if page == 'list' else url('reviews.list', addon.slug) %}
{% endwith %}
<hgroup>
{% if page == "list" %}
<h2>{{ _('Reviews for {0}')|f(addon.name) }}</h2>
{% with num = reviews.paginator.count %}
{# L10n: {0} is a number. #}
<h3>{{ ngettext('<b>{0}</b> review for this add-on',
'<b>{0}</b> reviews for this add-on',
num)|f(num|numberfmt)|safe }}</h3>
{% endwith %}
{% elif reply %}
{# L10n: {0} is a developer's name. #}
<h2>{{ _('Developer reply by {0}')|f(reply.user.name) }}</h2>
{% elif reviews.object_list %}
<h2>{% trans cnt=reviews.object_list|length, addon=addon.name,
user=reviews.object_list[0].user.name %}
Review for {{ addon }} by {{ user }}
{% pluralize %}
Reviews for {{ addon }} by {{ user }}
{% endtrans %}</h2>
{% else %}
<h2>{{ _('No reviews found.') }}</h2>
{% endif %}
</hgroup>
</header>
{% endblock %}
{% block review_list %}
{% if not reviews.object_list %}
<p><a href="{{ url('reviews.add', addon.slug) }}">
{{ _('Be the first to write a review.') }}</a><p>
{% endif %}
{% for review in reviews.object_list %}
{% include "reviews/impala/review.html" %}
{% if review.id in replies %}
{% with review=replies[review.id] %}
{% include "reviews/impala/review.html" %}
{% endwith %}
{% endif %}
{% endfor %}
{{ reviews|impala_paginator }}
{% endblock review_list %}
<div class="hidden">
<form method="post" id="review-edit-form" action="#"
class="review article review-form">
{{ csrf() }}
{{ field(form.title, _('Title:')) }}
{{ field(form.rating, _('Rating:')) }}
{{ field(form.body, _('Review:')) }}
<p>
<input type="submit" value="{{ _('Submit review') }}">
or <a href="#" id="review-edit-cancel">Cancel</a>
</p>
</form>
</div>
</div>
{% block review_header %}
<header>
{# Give a link back to reviews if we're looking at user reviews or a detail page. #}
{% with link = None if page == 'list' else url('reviews.list', addon.slug) %}
{% endwith %}
<hgroup>
{% if page == "list" %}
<h2>{{ _('Reviews for {0}')|f(addon.name) }}</h2>
{% with num = reviews.paginator.count %}
{# L10n: {0} is a number. #}
<h3>{{ ngettext('<b>{0}</b> review for this add-on',
'<b>{0}</b> reviews for this add-on',
num)|f(num|numberfmt)|safe }}</h3>
{% endwith %}
{% elif reply %}
{# L10n: {0} is a developer's name. #}
<h2>{{ _('Developer reply by {0}')|f(reply.user.name) }}</h2>
{% elif reviews.object_list %}
<h2>{% trans cnt=reviews.object_list|length, addon=addon.name,
user=reviews.object_list[0].user.name %}
Review for {{ addon }} by {{ user }}
{% pluralize %}
Reviews for {{ addon }} by {{ user }}
{% endtrans %}</h2>
{% else %}
<h2>{{ _('No reviews found.') }}</h2>
{% endif %}
</hgroup>
</header>
{% endblock %}
<div class="primary" role="main">
{% block review_list %}
{% if not reviews.object_list %}
<p><a href="{{ url('reviews.add', addon.slug) }}">
{{ _('Be the first to write a review.') }}</a><p>
{% endif %}
{% for review in reviews.object_list %}
{% include "reviews/review.html" %}
{% if review.id in replies %}
{% with review=replies[review.id] %}
{% include "reviews/review.html" %}
{% endwith %}
{% endif %}
{% endfor %}
{{ reviews|paginator }}
{% endblock review_list %}
<div class="hidden">
<form method="post" id="review-edit-form" action="#"
class="review article review-form">
{{ csrf() }}
{{ field(form.title, _('Title:')) }}
{{ field(form.rating, _('Rating:')) }}
{{ field(form.body, _('Review:')) }}
<p>
<input type="submit" value="{{ _('Submit review') }}">
or <a href="#" id="review-edit-cancel">Cancel</a>
</p>
</form>
</div>
</div>
{{ report_review_popup() }}
{{ report_review_popup() }}
{% endblock content %}

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

@ -1,6 +1,6 @@
<div class="hidden">
<div class="popup review-reason">
<strong>{{ _('Please select a reason:') }}</strong>
<h4>{{ _('Please select a reason:') }}</h4>
<ul>
{% for flag, text in ReviewFlag.FLAGS %}
<li><a href="#{{ flag }}">{{ text }}</a></li>

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

@ -111,7 +111,7 @@ def impala_review_list(request, addon, review_id=None, user_id=None, template=No
ctx['flags'] = get_flags(request, reviews.object_list)
else:
ctx['review_perms'] = {}
return jingo.render(request, template, ctx)
return jingo.render(request, 'reviews/impala/review_list.html', ctx)
def get_flags(request, reviews):

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

@ -288,42 +288,6 @@
}
}
#reviews .review {
border-bottom: 1px dotted @border-blue;
overflow: hidden;
&.no-reviews {
border-bottom: 0;
h3 {
font-style: normal;
}
}
padding: 1em 0;
h3 {
color: #333;
font-size: 16px;
line-height: 16px;
font-style: italic;
.stars {
padding: 2px;
}
.stars:not(:first-child) {
margin-left: .5em;
}
}
.byline {
font-style: italic;
margin: 2px 0;
color: #999;
font-size: 12px;
a {
color: #666;
}
}
.description {
margin: 0;
}
}
.metadata {
li {
line-height: 1.4em;
@ -418,6 +382,31 @@
}
}
.secondary .addon {
hgroup {
position: relative;
padding-left: 40px;
img {
position: absolute;
left: 0;
top: 0;
width: 32px;
height: 32px;
}
h2 {
font: bold 16px/18px @serif-stack;
font-family: @serif-stack;
text-transform: none;
margin: 0 0 4px;
padding: 0;
}
h4 {
font-size: 12px;
}
}
}
span.no-restart,
span.featured {
background-color: #e8933a;

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

@ -264,6 +264,13 @@
}
}
}
.secondary {
.listing-grid {
li .item.addon {
margin: 0 0 0 56px;
}
}
}
.html-rtl .listing-grid {
li {
float: right;

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

@ -175,3 +175,44 @@ header .feed {
}
}
}
.item:hover {
.item-actions > li > a {
color: @link;
}
}
.item-actions {
font-family: @sans-stack;
float: right;
font-size: 11px;
margin-top: 1em;
margin-bottom: 2px;
color: #aaa;
> li {
float: left;
> a {
font-weight: normal;
color: #aaa;
-moz-transition: color .2s;
-webkit-transition: color .2s;
transition: color .2s;
}
&:not(:first-child) {
list-style-type: disc;
margin-left: 1.6em;
}
}
}
.html-rtl .item-actions {
float: left;
> li {
float: right;
}
}
.item-note {
float: left;
color: #666;
font: italic 0.9em @sans-stack;
margin-bottom: 2px;
margin-top: 1em;
}

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

@ -78,6 +78,9 @@ div.popup-shim {
margin-top: 0;
color: #2e5186;
}
h4 {
margin-bottom: 4px;
}
}
.popup {
@ -90,6 +93,9 @@ div.popup-shim {
margin-bottom: 1.5em;
padding-bottom: 1em;
}
li {
line-height: 1.5em;
}
}
.modal-delete h3 {
color: #6c1a1a;

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

@ -1,5 +1,42 @@
@import 'lib';
#reviews .review {
border-bottom: 1px dotted @border-blue;
overflow: hidden;
&.no-reviews {
border-bottom: 0;
h3 {
font-style: normal;
}
}
padding: 1em 0;
h3 {
color: #333;
font-size: 16px;
line-height: 16px;
font-style: italic;
.stars {
padding: 2px;
}
.stars:not(:first-child) {
margin-left: .5em;
}
}
.byline {
font-style: italic;
margin: 2px 0;
color: #999;
font-size: 12px;
a {
color: #666;
}
}
.description {
margin: 1em 0 0;
color: #333;
}
}
.stars {
display: inline-block;
vertical-align: middle;
@ -33,6 +70,27 @@
/* grouped ratings style */
.average-rating {
position: relative;
padding-left: 74px;
strong {
font-weight: bold;
}
.stars {
position: absolute;
left: 0;
top: 2px;
}
}
.html-rtl .average-rating {
padding-left: 0;
padding-right: 74px;
.stars {
left: auto;
right: 0;
}
}
.grouped_ratings {
font-family: @sans-stack;
color: #666;
@ -71,6 +129,30 @@
}
}
.review-reason {
a {
display: block;
}
}
.other-note {
display: none;
clear: left;
padding: 8px 0 0;
input[type='text'] {
width: 138px;
margin-right: 8px;
padding: 7px;
}
input[type='submit'] {
margin-top: -2px;
}
}
.other {
.other-note {
display: block;
}
}
.html-rtl .grouped_ratings {
.rating_bar {
border-left: 0;

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

@ -103,6 +103,19 @@ pre, code, kbd, tt, samp, tt {
color: @red;
font-size: 24px;
}
hgroup {
h3 {
color: #666;
font-size: 14px;
}
b {
font-weight: bold;
}
}
header + .item {
border-top: 1px dotted #C9DDF2;
margin-top: 1em;
}
}
}

109
media/js/impala/reviews.js Normal file
Просмотреть файл

@ -0,0 +1,109 @@
$(document).ready(function() {
var report = $('.review-reason').html();
$(".review-reason").popup(".flag-review", {
delegate: $(document.body),
width: 'inherit',
callback: function(obj) {
var ct = $(obj.click_target),
$popup = this;
function addFlag(flag, note) {
$.ajax({type: 'POST',
url: ct.attr("href"),
data: {flag: flag, note: note},
success: function() {
$popup.removeClass("other")
.hideMe();
ct.replaceWith(gettext('Flagged for review'));
},
error: function(){ },
dataType: 'json'
});
};
$popup.delegate("li a", "click", function(e) {
e.preventDefault();
var el = $(e.target);
if (el.attr("href") == "#review_flag_reason_other") {
$popup.addClass('other')
.delegate("form", "submit", function(e) {
e.preventDefault();
var note = $popup.find('#id_note').val();
if (!note) {
alert(gettext('Your input is required'));
} else {
addFlag('review_flag_reason_other', note);
}
})
.setPos(ct)
.find('input[type=text]')
.focus();
} else {
addFlag(el.attr("href").slice(1));
}
});
$popup.html(report);
return { pointTo: ct };
}
});
$('.primary').delegate('.review-edit', 'click', function(e) {
e.preventDefault();
var $form = $("#review-edit-form"),
$review = $(this).parents(".review"),
rating = $review.attr("data-rating"),
edit_url = $("a.permalink", $review).attr("href") + "edit";
$cancel = $("#review-edit-cancel");
$review.attr("action", edit_url);
$form.detach().insertAfter($review);
$("#id_title").val($review.find("h3 > b").text());
$(".ratingwidget input:radio[value=" + rating + "]", $form).click();
$("#id_body").val($review.children("p.description").text());
$review.hide();
$form.show();
function done_edit() {
$form.unbind().hide();
$review.show();
$cancel.unbind();
}
$cancel.click(function(e) {
e.preventDefault();
done_edit();
});
$form.submit(function (e) {
e.preventDefault();
$.ajax({type: 'POST',
url: edit_url,
data: $form.serialize(),
success: function(response, status) {
$review.find("h3 > b").text($("#id_title").val());
rating = $(".ratingwidget input:radio:checked", $form).val();
$(".stars", $review).removeClass('stars-0 stars-1 stars-2 stars-3 stars-4 stars-5').addClass('stars-' + rating);
rating = $review.attr("data-rating", rating);
$review.children("p.description").text($("#id_body").val());
done_edit();
},
dataType: 'json'
});
return false;
});
});
$('.delete-review').click(function(e) {
e.preventDefault();
var target = $(e.target);
$.post(target.attr('href'), function() {
target.replaceWith(gettext('Marked for deletion'));
});
target.closest('.review').addClass('deleted');
});
$("select[name='rating']").ratingwidget();
});

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

@ -543,7 +543,7 @@ MINIFY_BUNDLES = {
'js/zamboni/contributions.js',
'js/impala/addon_details.js',
'js/impala/abuse.js',
'js/zamboni/reviews.js',
'js/impala/reviews.js',
# Personas
'js/lib/jquery.hoverIntent.min.js',
@ -956,6 +956,7 @@ UNLINK_SITE_STATS = True
# Use the new featured add-ons system which makes use of featured collections.
NEW_FEATURES = False
IMPALA_BROWSE = False
IMPALA_REVIEWS = False
# Set to True if we're allowed to use X-SENDFILE.
XSENDFILE = True