Update UI for forum actions

The actions are now consolidated in one location for each piece of
content. Primary actions (vote, follow, endorse, mark as answer) are
buttons, and secondary actions (pin, edit, delete, report, close) are in
a menu. This also includes improved front-end error handling for the
actions and significant test cleanup.

Co-authored-by: jsa <jsa@edx.org>
Co-authored-by: marco <marcotuts@gmail.com>
Co-authored-by: Frances Botsford <frances@edx.org>
Co-authored-by: Brian Talbot <btalbot@edx.org>
This commit is contained in:
Greg Price
2014-08-15 15:34:56 -04:00
parent a99196a164
commit 988e4e6da5
29 changed files with 2052 additions and 1308 deletions

View File

@@ -51,10 +51,12 @@
@import "discussion/utilities/variables";
@import "discussion/mixins";
@import 'discussion/discussion'; // Process old file after definitions but before everything else
@import "discussion/views/new-post";
@import "discussion/elements/actions";
@import "discussion/elements/editor";
@import "discussion/elements/labels";
@import "discussion/elements/navigation";
@import "discussion/views/thread";
@import "discussion/views/new-post";
@import "discussion/views/response";
@import 'discussion/utilities/developer';
@import 'discussion/utilities/shame';

View File

@@ -42,6 +42,9 @@ $very-light-text: #fff;
// ====================
// COLORS - utility
$transparent: rgba(0,0,0,0); // used when color value is needed for UI width/transitions but element is transparent
// COLORS
$black: rgb(0,0,0);
$black-t0: rgba($black, 0.125);

View File

@@ -1,6 +1,5 @@
// forums - main app styling
// ====================
body.discussion {
.course-tabs .right {
@@ -423,7 +422,7 @@ body.discussion {
}
h1 {
margin-bottom: $baseline/2;
margin-bottom: ($baseline/4);
font-size: 28px;
font-weight: 700;
letter-spacing: 0;
@@ -432,18 +431,14 @@ body.discussion {
.posted-details {
font-size: 12px;
font-style: italic;
color: #888;
.username {
display: block;
font-size: 16px;
font-weight: 700;
}
.timeago, .top-post-status {
color: inherit;
font-style: italic;
}
}
@@ -456,37 +451,6 @@ body.discussion {
p + p {
margin-top: $baseline;
}
.dogear {
display: block;
position: absolute;
top: -1px;
right: -1px;
width: 52px;
height: 51px;
background: url(../images/follow-dog-ear.png) 0 -52px no-repeat;
@include transition(none);
&.is-followed {
background-position: 0 0;
}
}
}
.discussion-post {
padding: ($baseline*2) ($baseline*2) 0 ($baseline*2);
> header .vote-btn {
position: relative;
z-index: 100;
margin-top: ($baseline/4);
margin-left: ($baseline*2);
}
.post-tools {
@include clearfix;
margin-top: 15px;
}
}
.discussion-post header,
@@ -565,7 +529,7 @@ body.discussion {
.discussion-response {
@include box-sizing(border-box);
border-radius: 3px 3px 0 0;
padding: $baseline $baseline 0;
padding: $baseline;
background-color: $white;
}
.posted-by {
@@ -594,94 +558,6 @@ body.discussion {
}
}
.vote-btn {
position: relative;
z-index: 100;
float: right;
display: block;
height: 27px;
padding: 0 8px;
border-radius: 5px;
border: 1px solid #b2b2b2;
@include linear-gradient(top, $white 35%, #ebebeb);
box-shadow: 0 1px 1px rgba(0, 0, 0, .15);
font-size: 12px;
font-weight: 700;
line-height: 25px;
color: #333;
.plus-icon {
display: inline-block;
width: 10px;
height: 10px;
margin: 8px 6px 0 0;
background: url(../images/vote-plus-icon.png) no-repeat;
font-size: 18px;
text-indent: -9999px;
color: #17b429;
overflow: hidden;
}
&.is-cast {
border-color: #379a42;
@include linear-gradient(top, #50cc5e, #3db84b);
color: $white;
text-shadow: 0 1px 0 rgba(0, 0, 0, .3);
box-shadow: 0 1px 0 rgba(255, 255, 255, .4) inset, 0 1px 2px $shadow;
.plus-icon {
background-position: 0 -10px;
color: #336a39;
text-shadow: 0 1px 0 rgba(255, 255, 255, .4);
}
}
}
.endorse-btn {
display: block;
float: right;
width: 27px;
height: 27px;
margin-right: ($baseline/2);
border-radius: 27px;
border: 1px solid #a0a0a0;
@include linear-gradient(top, $white 35%, $gray-l4);
box-shadow: 0 1px 1px $shadow-l1;
cursor: default;
&.is-clickable {
cursor: auto;
}
.check-icon {
display: block;
width: 13px;
height: 12px;
margin: 8px auto;
background: url(../images/endorse-icon.png) no-repeat;
pointer-events: none;
}
&.mark-answer .check-icon {
background: url(../images/answer-icon.png) no-repeat;
}
&.is-endorsed {
border: 1px solid #4697c1;
@include linear-gradient(top, #6dccf1, #38a8e5);
box-shadow: 0 1px 1px $shadow-l1, 0 1px 0 rgba(255, 255, 255, .4) inset;
.check-icon {
background-position: 0 -12px;
}
&.mark-answer {
@include linear-gradient(top, tint(#1d9348, 60%), tint(#1d9348, 20%));
border: 1px solid #1d9348;
}
}
}
blockquote {
background: $gray-l5;
border-radius: 3px;
@@ -689,89 +565,6 @@ body.discussion {
font-size: 14px;
}
.comments {
margin: 0;
border-radius: 0 0 3px 3px;
padding: 0;
background: $gray-l6;
box-shadow: 0 1px 3px -1px $shadow inset;
list-style: none;
> li {
border-top: 1px solid $gray-l4;
padding: ($baseline/2) $baseline;
}
blockquote {
background: $gray-l4;
border-radius: 3px;
padding: ($baseline/4) ($baseline/2);
font-size: 14px;
}
.comment-form {
@include clearfix;
.comment-form-input {
padding: ($baseline/4) ($baseline/2);
background-color: $white;
font-size: 14px;
}
.discussion-submit-comment {
@include blue-button;
float: left;
margin-top: 8px;
}
.wmd-input {
height: 40px;
}
.discussion-errors {
margin: 0;
}
}
.response-body {
font-size: 13px;
margin-bottom: ($baseline/2);
p + p {
margin-top: 12px;
}
}
.posted-details {
font-size: 11px;
}
.staff-label {
margin-left: ($baseline/10);
padding: 0 ($baseline/5);
border-radius: 2px;
background: #009FE2;
font-size: 9px;
font-weight: 700;
font-style: normal;
color: white;
text-transform: uppercase;
}
}
.community-ta-label{
margin-left: ($baseline/10);
padding: 0 ($baseline/5);
border-radius: 2px;
background: $forum-color-community-ta;
font-size: 9px;
font-weight: 700;
font-style: normal;
color: white;
text-transform: uppercase;
}
.comment-form {
padding: ($baseline/2) 0;
@@ -803,51 +596,6 @@ body.discussion {
}
}
.moderator-actions {
margin: 0;
padding: $baseline 0;
@include clearfix;
li {
float: left;
margin-right: ($baseline/2);
list-style: none;
}
a {
@include white-button;
height: 26px;
@include linear-gradient(top, $white 35%, #ebebeb);
font-size: 13px;
line-height: 24px;
color: #737373;
font-weight: normal;
box-shadow: 0 1px 1px $shadow-l1;
&:hover, &:focus {
@include linear-gradient(top, $white 35%, #ddd);
}
.delete-icon {
display: block;
float: left;
width: 10px;
height: 10px;
margin: 8px 4px 0 0;
background: url(../images/moderator-delete-icon.png) no-repeat;
}
.edit-icon {
display: block;
float: left;
width: 10px;
height: 10px;
margin: 7px 4px 0 0;
background: url(../images/moderator-edit-icon.png) no-repeat;
}
}
}
.main-article.new {
display: none;
padding: ($baseline*2.5);
@@ -900,16 +648,6 @@ body.discussion {
// ====================
// post actions -global
.global-discussion-actions {
height: 60px;
@include linear-gradient(top, #ebebeb, #d9d9d9);
border-radius: 0 3px 0 0;
border-bottom: 1px solid #bcbcbc;
}
// ====================
// inline discussion module and profile thread styling
.discussion-module {
@extend .discussion-body;
@@ -993,16 +731,6 @@ body.discussion {
margin-bottom: $baseline;
@include transition(all .25s linear 0s);
.dogear {
display: none;
}
&.expanded {
.dogear{
display: block;
}
}
p {
margin-bottom: 0;
}
@@ -1174,10 +902,6 @@ body.discussion {
color: $white;
}
.moderator-actions {
padding-left: 0 !important;
}
section.pagination {
margin-top: 30px;
@@ -1260,99 +984,6 @@ body.discussion {
}
}
// post actions - pinning
.discussion-pin {
font-size: 12px;
float:right;
padding-right: 5px;
font-style: italic;
margin-right: $baseline/2;
opacity: 0.8;
&.admin-pin {
cursor: pointer;
&:hover, &:focus {
@include transition(opacity .2s linear 0s);
opacity: 1.0;
}
}
}
.discussion-pin-inline {
font-size: 12px;
float:right;
font-style: italic;
position: relative;
right:-20px;
top:-13px;
margin-right:35px;
margin-top:13px;
opacity: 1.0;
}
.notpinned .icon {
display: block;
float: left;
margin: 3px;
width: 10px;
height: 14px;
padding-right: 3px;
color: #333;
}
.pinned .icon {
display: block;
float: left;
margin: 3px;
width: 10px;
height: 14px;
padding-right: 3px;
color: $pink;
}
.pinned span {
color: $pink;
font-style: italic;
}
.notpinned span {
color: #333;
font-style: italic;
}
.pinned-false
{
display:none;
}
// ====================
// post actions - flagging
.discussion-flag-abuse, .discussion-delete-comment, .discussion-edit-comment {
font-size: 12px;
float:right;
margin-left: ($baseline/2);
font-style: italic;
cursor:pointer;
color: $dark-gray;
opacity: 0.8;
&:hover, &:focus {
@include transition(opacity .2s linear 0s);
opacity: 1.0;
}
.flag-label {
font-style: italic;
margin-left: ($baseline/4);
}
}
.flagged * {
color: $pink;
}
// ====================
// post pagination

View File

@@ -113,4 +113,44 @@
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
@mixin forum-post-label($color) {
@extend %t-weight4;
@include font-size(9);
display: inline;
margin-top: ($baseline/4);
border: 1px solid;
border-radius: 3px;
padding: 1px 6px;
text-transform: uppercase;
white-space: nowrap;
border-color: $color;
color: $color;
.icon {
margin-right: ($baseline/5);
}
&:last-child {
margin-right: 0;
}
&.is-hidden {
display: none;
}
}
@mixin forum-user-label($color) {
@include font-size(9);
@extend %t-weight5;
vertical-align: middle;
margin-left: ($baseline/4);
border-radius: 2px;
padding: 0 ($baseline/5);
background: $color;
font-style: normal;
text-transform: uppercase;
color: white;
}

View File

@@ -0,0 +1,313 @@
.discussion.container, .discussion-module {
// discussion - elements - actions
// ====================
// UI: general action list
.post-actions-list,
.response-actions-list,
.comment-actions-list {
@extend %ui-no-list;
text-align: right;
.actions-item {
@include box-sizing(border-box);
display: block;
margin: ($baseline/4) 0;
&.is-hidden {
display: none;
}
}
.more-wrapper {
position: relative;
}
}
// ====================
// UI: general actions dropdown layout
.actions-dropdown {
@extend %ui-no-list;
@extend %ui-depth1;
display: none;
position: absolute;
top: 100%;
right: 0;
pointer-events: none;
min-width: ($baseline*6.5);
&.is-expanded {
display: block;
pointer-events: auto;
}
.actions-dropdown-list {
@include box-sizing(border-box);
box-shadow: 0 1px 1px $shadow-l1;
position: relative;
width: 100%;
border-radius: 3px;
margin: 5px 0 0 0;
border: 1px solid $gray-l3;
padding: ($baseline/2) ($baseline*0.75);
background: $white;
// ui triangle/nub
&:after,
&:before {
bottom: 100%;
right: 3px;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
&:after {
border-color: $transparent;
border-bottom-color: $white;
border-width: 6px;
margin-right: 1px;
}
&:before {
border-color: $transparent;
border-bottom-color: $gray-l3;
border-width: 7px;
}
}
.actions-item {
display: block;
margin: 0;
&.is-hidden {
display: none;
}
}
}
// ====================
// UI: general action
.action-button {
@include transition(border .5s linear 0s);
@include box-sizing(border-box);
display: inline-block;
border: 1px solid transparent;
border-radius: 5px;
color: $gray-l1;
.action-icon {
@extend %t-icon7;
display: inline-block;
height: $baseline;
width: $baseline;
border: 1px solid $gray-l3;
border-radius: 3px;
text-align: center;
color: $gray-l1;
.icon {
vertical-align: middle;
}
}
.action-label {
@extend %t-copy-sub2;
display: inline-block;
vertical-align: middle;
padding: 0 8px;
color: $gray-l1;
opacity: 0;
}
&:hover, &:focus {
.action-label {
opacity: 1;
}
.action-icon {
border-radius: 0 3px 3px 0;
}
}
// specific button styles
&.action-follow {
.action-label {
color: $blue-d1;
}
&.is-checked, &:hover, &:focus {
.action-icon {
background-color: $forum-color-following;
border: 1px solid $blue-d1;
color: $white;
}
}
&:hover, &:focus {
border-color: $forum-color-following;
}
}
&.action-vote {
.action-label {
opacity: 1;
}
&.is-checked, &:hover, &:focus {
.action-icon {
background-color: $green-d1;
border: 1px solid $green-d2;
color: $white;
}
}
&:hover, &:focus {
border-color: $green-d2;
.action-label {
color: $green-d2;
}
}
}
&.action-endorse {
&.is-checked, &:hover, &:focus {
.action-icon {
background-color: $blue-d1;
border: 1px solid $blue-d2;
color: $white;
}
}
&:hover, &:focus {
border-color: $blue-d2;
.action-label {
color: $blue-d2;
}
}
}
&.action-answer {
&.is-checked, &:hover, &:focus {
.action-icon {
border: 1px solid $green-d1;
background-color: $green-d1;
color: $white;
}
}
&:hover, &:focus {
border-color: $green-d1;
.action-label {
color: $green-d2;
}
}
}
// more drop-down menu
&.action-more {
position: relative;
&:hover, &:focus {
border-color: $gray;
.action-icon {
border: 1px solid $gray;
background-color: $gray;
color: $white;
}
.action-label {
opacity: 1;
color: $black;
}
}
}
}
// ====================
.actions-dropdown {
// UI: secondary action
.action-list-item {
@extend %t-copy-sub2;
display: block;
padding: ($baseline/10) 0;
white-space: nowrap;
text-align: right;
color: $gray-l1;
&:hover, &:focus {
color: $link-color;
}
.action-icon {
display: inline-block;
width: ($baseline/2);
margin-left: ($baseline/4);
color: inherit;
}
.action-label {
display: inline-block;
color: inherit;
}
// CASE: checked
&.is-checked {
// CASE: pin action
&.action-pin {
color: $pink;
}
// CASE: report action
&.action-report {
color: $pink;
}
// CASE: hover for any action
&:hover, &:focus {
color: $link-color;
}
}
}
}
.action-button, .action-list-item {
.action-label {
.label-checked {
display: none;
}
}
&.is-checked {
.label-unchecked {
display: none;
}
.label-checked {
display: inline;
}
}
}
}

View File

@@ -0,0 +1,37 @@
// discussion - elements - labels
// ====================
body.discussion, .discussion-module {
.post-label-pinned {
@include forum-post-label($forum-color-pinned);
}
.post-label-following {
@include forum-post-label($forum-color-following);
}
.post-label-reported {
@include forum-post-label($forum-color-reported);
}
.post-label-closed {
@include forum-post-label($forum-color-closed);
}
.post-label-by-staff {
@include forum-post-label($forum-color-staff);
}
.post-label-by-community-ta {
@include forum-post-label($forum-color-community-ta);
}
.user-label-staff {
@include forum-user-label($forum-color-staff);
}
.user-label-community-ta {
@include forum-user-label($forum-color-community-ta);
}
}

View File

@@ -230,51 +230,6 @@
display: block;
}
%forum-nav-thread-label {
@extend %t-weight4;
@include font-size(9);
display: inline;
margin-top: ($baseline/4);
border: 1px solid;
border-radius: 3px;
padding: 1px 6px;
text-transform: uppercase;
white-space: nowrap;
&:last-child {
margin-right: 0;
}
.icon {
margin-right: ($baseline/5);
}
}
.forum-nav-thread-label-pinned {
@extend %forum-nav-thread-label;
border-color: $forum-color-pinned;
color: $forum-color-pinned;
}
.forum-nav-thread-label-following {
@extend %forum-nav-thread-label;
border-color: $forum-color-following;
color: $forum-color-following;
}
.forum-nav-thread-label-staff {
@extend %forum-nav-thread-label;
border-color: $forum-color-staff;
color: $forum-color-staff;
}
.forum-nav-thread-label-community-ta {
@extend %forum-nav-thread-label;
border-color: $forum-color-community-ta;
color: $forum-color-community-ta;
}
%forum-nav-thread-wrapper-2-content {
@include font-size(11);
display: inline-block;

View File

@@ -136,3 +136,21 @@ li[class*=forum-nav-thread-label-] {
line-height: 14px;
}
}
// -------
// Actions
// -------
.discussion.container, .discussion-module {
// Override courseware
.post-actions-list, .response-actions-list, .comment-actions-list {
@extend %t-copy-sub2;
padding-left: 0 !important;
}
// Override global span
.action-label span, .action-icon span {
color: inherit;
}
}

View File

@@ -1,5 +1,7 @@
$forum-color-active-thread: tint($blue, 85%);
$forum-color-pinned: $pink;
$forum-color-reported: $pink;
$forum-color-closed: $black;
$forum-color-following: $blue;
$forum-color-staff: $blue;
$forum-color-community-ta: $green-d1;

View File

@@ -1,3 +1,117 @@
// discussion - thread layout
// ====================
// general thread layout
body.discussion, .discussion-module {
// post layout
.discussion-post {
padding: ($baseline*2) ($baseline*2) $baseline ($baseline*2);
border-radius: 3px 3px 0 0;
background-color: $white;
.post-header-content {
display: inline-block;
width: flex-grid(9,12);
}
.post-header-actions {
display: inline-block;
float: right;
vertical-align: middle;
width: flex-grid(3,12);
}
}
// response layout
.discussion-response {
min-height: ($baseline*7.5);
.username {
@include font-size(14);
@extend %t-weight5;
}
.response-header-content {
display: inline-block;
vertical-align: top;
width: flex-grid(9,12);
}
.response-header-actions {
width: flex-grid(3,12);
float: right;
}
}
// comments layout
.comments {
@extend %ui-no-list;
border-radius: 0 0 3px 3px;
background: $gray-l6;
box-shadow: 0 1px 3px -1px $shadow inset;
> li {
border-top: 1px solid $gray-l4;
padding: ($baseline/2) $baseline;
}
blockquote {
background: $gray-l4;
border-radius: 3px;
padding: ($baseline/4) ($baseline/2);
font-size: 14px;
}
.comment-form {
@include clearfix;
.comment-form-input {
padding: ($baseline/4) ($baseline/2);
background-color: $white;
font-size: 14px;
}
.discussion-submit-comment {
@include blue-button;
float: left;
margin-top: 8px;
}
.wmd-input {
height: 40px;
}
.discussion-errors {
margin: 0;
}
}
.response-body {
display: inline-block;
margin-bottom: ($baseline/2);
width: flex-grid(10,12);
font-size: 13px;
p + p {
margin-top: 12px;
}
}
.comment-actions-list {
display: inline-block;
width: flex-grid(2,12);
vertical-align: top;
}
//TO-DO : clean up posted-details styling, currently reused by responses and comments
.posted-details {
margin-top: 0;
}
}
}
.forum-thread-main-wrapper {
border-bottom: 1px solid $white; // Prevent collapsing margins
border-radius: 3px 3px 0 0;
@@ -6,7 +120,7 @@
body.discussion, .discussion-thread.expanded {
.forum-thread-main-wrapper {
margin-bottom: $baseline;
box-shadow: 0 1px 3px $shadow;
}
}

View File

@@ -1,7 +1,8 @@
<%! from django.utils.translation import ugettext as _ %>
<%! from django.template.defaultfilters import escapejs %>
<%! from django_comment_client.permissions import has_permission %>
## IMPORTANT: In order to keep js tests valid and relevant, please be sure to update the appropriate HTML in
## common/static/coffee/spec/discussion_spec_helper.coffee is changed and regenerated, whenever this one changes.
<script aria-hidden="true" type="text/template" id="thread-template">
<article class="discussion-article" data-id="${'<%- id %>'}">
<div class="thread-wrapper">
@@ -50,48 +51,43 @@
<div class="group-visibility-label">${"<%- obj.group_string%>"}</div>
${"<% } %>"}
<a href="#" class="vote-btn" role="button" aria-pressed="false"><!-- DiscussionContentView.renderVote populates this --></a>
<h1>${'<%- title %>'}</h1>
<p class="posted-details">
${"<% if (obj.username) { %>"}
<a href="${'<%- user_url %>'}" class="username">${'<%- username %>'}</a>
${"<% } else { %>"}
${_('anonymous') | h}
${"<% } %>"}
## This part is incredibly gross but necessary to combine i18n in
## mako with logic in underscore.
## Translators: post_type describes the kind of post this is
## (e.g. "question" or "discussion"); time_ago is how much time
## has passed since the post was created (e.g. "4 hours ago")
${"{post_type} posted {time_ago}".format(
post_type="<%- thread_type %>",
time_ago="<span class='timeago' title='<%- created_at %>'><%- created_at %></span>"
)}
<div class="post-header-content">
<span class="post-status-closed top-post-status" style="display: none">
${_("&bull; This thread is closed.")}
</span>
</p>
<a href="javascript:void(0)" class="dogear action-follow" data-tooltip="${_('follow') | h}" role="checkbox" aria-checked="false">
<span class="sr">${_("Follow this post")}</span>
</a>
<h1>${'<%- title %>'}</h1>
<p class="posted-details">
## This part is incredibly gross but necessary to combine i18n in
## mako with logic in underscore.
## Translators: post_type describes the kind of post this is
## (e.g. "question" or "discussion"); time_ago is how much time
## has passed since the post was created (e.g. "4 hours ago")
${_("{post_type} posted {time_ago} by {author}").format(
post_type="<%- thread_type %>",
time_ago="<span class='timeago' title='<%- created_at %>'><%- created_at %></span>",
author="<%= author_display %>"
)}
</p>
<div class="post-labels">
<span class="post-label-pinned"><i class="icon icon-pushpin"></i>${_("Pinned")}</span>
<span class="post-label-reported"><i class="icon icon-flag"></i>${_("Reported")}</span>
<span class="post-label-closed"><i class="icon icon-lock"></i>${_("Closed")}</span>
</div>
</div>
<div class="post-header-actions post-extended-content">
${"""<%=
_.template(
$('#forum-actions').html(),
{
contentId: cid,
contentType: 'post',
primaryActions: ['vote', 'follow'],
secondaryActions: ['pin', 'edit', 'delete', 'report', 'close']
}
)
%>"""}
</div>
</header>
<div class="post-body">${'<%- body %>'}</div>
<div class="discussion-flag-abuse notflagged" data-role="thread-flag" role="button" aria-pressed="false" tabindex="0">
<i class="icon icon-flag"></i><span class="flag-label">${_("Report Misuse")}</span></div>
% if course and has_permission(user, 'openclose_thread', course.id):
<div class="admin-pin discussion-pin notpinned" role="button" aria-pressed="false" tabindex="0">
<i class="icon icon-pushpin"></i><span class="pin-label">${_("Pin Thread")}</span></div>
%else:
${"<% if (pinned) { %>"}
<div class="discussion-pin notpinned">
<i class="icon icon-pushpin"></i><span class="pin-label">${_("Pinned")}</span></div>
${"<% } %>"}
% endif
<% js_block = u"""
var courseware_link = interpolate('<a href="%s">%s</a>', [courseware_url, _.escape(courseware_title)]);
@@ -104,14 +100,6 @@
${'<% if (mode == "tab" && obj.courseware_url) { %>'}
<div class="post-context">${'<%'}${js_block}${'%>'}</div>
${'<% } %>'}
<div class="post-extended-content">
<ul class="moderator-actions">
<li style="display: none"><a class="action-edit" href="javascript:void(0)"><span class="edit-icon"></span> ${_("Edit")}</a></li>
<li style="display: none"><a class="action-delete" href="javascript:void(0)"><span class="delete-icon"></span> ${_("Delete")}</a></li>
<li style="display: none"><a class="action-openclose" href="javascript:void(0)"><span class="edit-icon"></span> ${_("Close")}</a></li>
</ul>
</div>
</div>
</script>
@@ -156,57 +144,58 @@
<script aria-hidden="true" type="text/template" id="thread-response-show-template">
<header>
<a href="#" class="vote-btn" role="button" aria-pressed="false"><!-- DiscussionContentView.renderVote() populates this --></a>
<%
tooltip_expr = u"<%- thread.get('thread_type') == 'question' ? '{mark_answer}' : '{endorse}' %>".format(
endorse=_("endorse"),
mark_answer=_("mark as answer")
)
%>
<a
href="javascript:void(0)"
class="endorse-btn action-endorse ${"<%= thread.get('thread_type') == 'question' ? 'mark-answer' : '' %>"}"
data-tooltip="${tooltip_expr}"
>
<span class="check-icon"></span>
</a>
${"<% if (obj.username) { %>"}
<a href="${'<%- user_url %>'}" class="posted-by">${'<%- username %>'}</a>
${"<% } else { %>"}
<span class="anonymous"><em>${_('anonymous')}</em></span>
${"<% } %>"}
<div class="response-header-content">
${'<%= author_display %>'}
<p class="posted-details">
<span class="timeago" title="${'<%= created_at %>'}">${'<%= created_at %>'}</span>
<%
js_block = u"""
interpolate(
endorsement.username ? "{user_fmt_str}" : "{anon_fmt_str}",
thread.get("thread_type") == "question" ?
(endorsement.username ? "{question_user_fmt_str}" : "{question_anon_fmt_str}") :
(endorsement.username ? "{discussion_user_fmt_str}" : "{discussion_anon_fmt_str}"),
{{
'time_ago': '<span class="timeago" title="' + endorsement.time + '">' + endorsement.time + '</span>',
'user': endorsement.username
'user': endorser_display
}},
true
)""".format(
## Translators: time_ago is a placeholder for a fuzzy, relative timestamp
## like "4 hours ago" or "about a month ago"
user_fmt_str=escapejs(_("marked as answer %(time_ago)s by %(user)s")),
question_user_fmt_str=escapejs(_("marked as answer %(time_ago)s by %(user)s")),
## Translators: time_ago is a placeholder for a fuzzy, relative timestamp
## like "4 hours ago" or "about a month ago"
anon_fmt_str=escapejs(_("marked as answer %(time_ago)s")),
question_anon_fmt_str=escapejs(_("marked as answer %(time_ago)s")),
## Translators: time_ago is a placeholder for a fuzzy, relative timestamp
## like "4 hours ago" or "about a month ago"
discussion_user_fmt_str=escapejs(_("endorsed %(time_ago)s by %(user)s")),
## Translators: time_ago is a placeholder for a fuzzy, relative timestamp
## like "4 hours ago" or "about a month ago"
discussion_anon_fmt_str=escapejs(_("endorsed %(time_ago)s")),
)
%>
${"<% if (thread.get('thread_type') == 'question' && obj.endorsement) { %> - <%="}${js_block}${"%><% } %>"}
</p>
${"<% if (obj.endorsement) { %> - <%="}${js_block}${"%><% } %>"}
</p>
<div class="post-labels">
<span class="post-label-reported"><i class="icon icon-flag"></i>${_("Reported")}</span>
</div>
</div>
<div class="response-header-actions">
${"""<%=
_.template(
$('#forum-actions').html(),
{
contentId: cid,
contentType: 'response',
primaryActions: ['vote', thread.get('thread_type') == 'question' ? 'answer' : 'endorse'],
secondaryActions: ['edit', 'delete', 'report']
}
)
%>"""}
</div>
</header>
<div class="response-body">${"<%- body %>"}</div>
<div class="discussion-flag-abuse notflagged" data-role="thread-flag" role="button" aria-pressed="false" tabindex="0">
<i class="icon icon-flag"></i><span class="flag-label">${_("Report Misuse")}</span></div>
</div>
<ul class="moderator-actions">
<li style="display: none"><a class="action-edit" href="javascript:void(0)"><span class="edit-icon"></span> ${_("Edit")}</a></li>
<li style="display: none"><a class="action-delete" href="javascript:void(0)"><span class="delete-icon"></span> ${_("Delete")}</a></li>
<li style="display: none"><a class="action-openclose" href="javascript:void(0)"><span class="edit-icon"></span> ${_("Close")}</a></li>
</ul>
</script>
<script aria-hidden="true" type="text/template" id="thread-response-edit-template">
@@ -224,31 +213,34 @@
<script aria-hidden="true" type="text/template" id="response-comment-show-template">
<div id="comment_${'<%- id %>'}">
<div class="response-body">${'<%- body %>'}</div>
<div class="discussion-flag-abuse notflagged" data-role="thread-flag" data-tooltip="${_('Report Misuse') | h}" role="button" aria-pressed="false" tabindex="0">
<i class="icon icon-flag"></i><span class="sr flag-label">${_("Report Misuse")}</span></div>
<div style="display: none" class="discussion-delete-comment action-delete" data-tooltip="${_('Delete Comment') | h}" role="button" tabindex="0">
<i class="icon icon-remove"></i><span class="sr">${_("Delete Comment")}</span></div>
<div class="discussion-edit-comment action-edit" data-tooltip="${_('Edit') | h}" role="button" tabindex="0">
<i class="icon icon-pencil"></i><span class="sr">${_("Edit")}</span></div>
<%
js_block = u"""
interpolate(
'{}',
{{'time_ago': '<span class=\"timeago\" title=\"' + created_at + '\">' + created_at + '</span>'}},
true
)""".format(
## Translators: 'timeago' is a placeholder for a fuzzy, relative timestamp (see: https://github.com/rmm5t/jquery-timeago)
escapejs(_('-posted %(time_ago)s by'))
)
%>
<p class="posted-details">
${'<%='}${js_block}${'%>'}
${"<% if (obj.username) { %>"}
<a href="${'<%- user_url %>'}" class="profile-link">${'<%- username %>'}</a>
${"<% } else { %>"}
${_('anonymous')}
${"<% } %>"}
${"""<%=
_.template(
$('#forum-actions').html(),
{
contentId: cid,
contentType: 'comment',
primaryActions: [],
secondaryActions: ['edit', 'delete', 'report']
}
)
%>"""}
<%
js_block = u"""
interpolate(
'{}',
{{'time_ago': '<span class=\"timeago\" title=\"' + created_at + '\">' + created_at + '</span>', 'author': author_display}},
true
)""".format(
## Translators: 'timeago' is a placeholder for a fuzzy, relative timestamp (see: https://github.com/rmm5t/jquery-timeago)
escapejs(_('posted %(time_ago)s by %(author)s'))
)
%>
<p class="posted-details">
${'<%='}${js_block}${'%>'}
</p>
<div class="post-labels">
<span class="post-label-reported"><i class="icon icon-flag"></i>${_("Reported")}</span>
</div>
</div>
</script>
@@ -296,16 +288,16 @@
js_block = u"""
var labels = "";
if (pinned) {{
labels += '<li class="forum-nav-thread-label-pinned"><i class="icon icon-pushpin"></i>{pinned_text}</li> ';
labels += '<li class="post-label-pinned"><i class="icon icon-pushpin"></i>{pinned_text}</li> ';
}}
if (typeof(subscribed) != "undefined" && subscribed) {{
labels += '<li class="forum-nav-thread-label-following"><i class="icon icon-star"></i>{following_text}</li> ';
labels += '<li class="post-label-following"><i class="icon icon-star"></i>{following_text}</li> ';
}}
if (staff_authored) {{
labels += '<li class="forum-nav-thread-label-staff"><i class="icon icon-user"></i>{staff_text}</li> ';
labels += '<li class="post-label-by-staff"><i class="icon icon-user"></i>{staff_text}</li> ';
}}
if (community_ta_authored) {{
labels += '<li class="forum-nav-thread-label-community-ta"><i class="icon icon-user"></i>{community_ta_text}</li> ';
labels += '<li class="post-label-by-community-ta"><i class="icon icon-user"></i>{community_ta_text}</li> ';
}}
if (labels != "") {{
print('<ul class="forum-nav-thread-labels">' + labels + '</ul>');
@@ -554,3 +546,107 @@
<ul role="menu" class="topic-submenu">${'<%= entries %>'}</ul>
</li>
</script>
<%def name="primaryAction(action_class, icon, sr_label, unchecked_label, checked_label)">
<script type="text/template" id="forum-action-${action_class}">
<li class="actions-item">
<a href="javascript:void(0)" class="action-button action-${action_class}" role="checkbox" aria-checked="false">
<span class="sr">${sr_label}</span>
<span class="action-label" aria-hidden="true">
<span class="label-unchecked">${unchecked_label}</span>
<span class="label-checked">${checked_label}</span>
</span>
<span class="action-icon"><i class="icon icon-${icon}"></i></span>
</a>
</li>
</script>
</%def>
${primaryAction("endorse", "ok", _("Endorse"), _("Endorse"), _("Unendorse"))}
${primaryAction("answer", "ok", _("Mark as Answer"), _("Mark as Answer"), _("Unmark as Answer"))}
${primaryAction("follow", "star", _("Follow"), _("Follow"), _("Unfollow"))}
<script type="text/template" id="forum-action-vote">
<li class="actions-item">
<a href="#" class="action-button action-vote" role="checkbox" aria-checked="false">
## Vote counts are populated by JS
<span class="sr">${_("Vote")}</span>
<span class="sr js-sr-vote-count"></span>
<span class="action-label" aria-hidden="true">
<span class="js-visual-vote-count"></span>
</span>
<span class="action-icon" aria-hidden="true">
<i class="icon icon-plus"></i>
</span>
</a>
</li>
</script>
<%def name="secondaryStateAction(action_class, icon, sr_label, unchecked_label, checked_label)">
<script type="text/template" id="forum-action-${action_class}">
<li class="actions-item">
<a href="javascript:void(0)" class="action-list-item action-${action_class}" role="checkbox" aria-checked="false">
<span class="sr">${sr_label}</span>
<span class="action-label" aria-hidden="true">
<span class="label-unchecked">${unchecked_label}</span>
<span class="label-checked">${checked_label}</span>
</span>
<span class="action-icon">
<i class="icon icon-${icon}"></i>
</span>
</a>
</li>
</script>
</%def>
${secondaryStateAction("report", "flag", _("Report abuse"), _("Report"), _("Unreport"))}
${secondaryStateAction("pin", "pushpin", _("Pin"), _("Pin"), _("Unpin"))}
${secondaryStateAction("close", "lock", _("Close"), _("Close"), _("Open"))}
<%def name="secondaryAction(action_class, icon, label)">
<script type="text/template" id="forum-action-${action_class}">
<li class="actions-item">
<a href="javascript:void(0)" class="action-list-item action-${action_class}" role="button">
<span class="action-label">${label}</span>
<span class="action-icon"><i class="icon icon-${icon}"></i></span>
</a>
</li>
</script>
</%def>
${secondaryAction("edit", "pencil", _("Edit"))}
${secondaryAction("delete", "remove", _("Delete"))}
<script type="text/template" id="forum-actions">
<ul class="${"<%= contentType %>"}-actions-list">
${"<% _.each(primaryActions, function(action) { print(_.template($('#forum-action-' + action).html(), {})) }) %>"}
<li class="actions-item is-visible">
<div class="more-wrapper">
<a href="javascript:void(0)" class="action-button action-more" role="button" aria-haspopup="true" aria-controls="action-menu-${"<%= contentId %>"}">
<span class="action-label">${_("More")}</span>
<span class="action-icon"><i class="icon icon-ellipsis-horizontal"></i></span>
</a>
<div class="actions-dropdown" id="action-menu-${"<%= contentType %>"}" aria-expanded="false">
<ul class="actions-dropdown-list">
${"<% _.each(secondaryActions, function(action) { print(_.template($('#forum-action-' + action).html(), {})) }) %>"}
</ul>
</div>
</div>
</li>
</ul>
</script>
<script aria-hidden="true" type="text/template" id="post-user-display-template">
${"<% if (username) { %>"}
<a href="${'<%- user_url %>'}" class="username">${'<%- username %>'}</a>
${"<% if (is_community_ta) { %>"}
<span class="user-label-community-ta">${_("Community TA")}</span>
${"<% } else if (is_staff) { %>"}
<span class="user-label-staff">${_("Staff")}</span>
${"<% } %>"}
${"<% } else { %>"}
${_('anonymous') | h}
${"<% } %>"}
</script>