Add more visibility and control around reactions/flags into the Admin USERS page (#19558)

* Basic implementation without dropdown

* Dropdown functionality

* Partial i18n

* Nit fixes

* Nit fixes

* Flag count fix

* I18n fr.yml

* Nit design fix

* added external div to article_item

* added external div to article_item

* Code merge of article_item and individual_article

* Minor bug fix

* Extacted more common code

* Nit fix

* Optimised code

* Optimised code

* Ni fix

* Test fix

* Minor suggested changes applied

* Nit fix

* Nit fix
This commit is contained in:
Rajat Talesra 2023-06-15 22:08:45 +05:30 committed by GitHub
parent 165f0bbf1f
commit 150199e0d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 383 additions and 526 deletions

View file

@ -30,10 +30,17 @@ module Admin
@pinned_article = PinnedArticle.get
@articles = @articles.where.not(id: @pinned_article) if @pinned_article
@countable_vomits = {}
@articles.each do |article|
@countable_vomits[article.id] = calculate_flags_for_single_article(article)
end
end
def show
@article = Article.includes(reactions: :user).find(params[:id])
@countable_vomits = {}
@countable_vomits[@article.id] = calculate_flags_for_single_article(@article)
end
def update
@ -126,5 +133,21 @@ module Admin
def authorize_admin
authorize Article, :access?, policy_class: InternalPolicy
end
def calculate_flags_for_single_article(article)
privileged_article_reactions = article.reactions.privileged_category.select do |reaction|
reaction.reactable_type == "Article"
end
vomit_article_reactions = privileged_article_reactions.select { |reaction| reaction.category == "vomit" }
vomit_count = 0
if vomit_article_reactions.present?
vomit_article_reactions.each do |vomit_reaction|
vomit_count += 1 if vomit_reaction.status != "invalid"
end
end
vomit_count
end
end
end

View file

@ -185,7 +185,7 @@ module Admin
user: @user.username,
email: @user.email.presence || I18n.t("admin.users_controller.no_email"),
id: @user.id,
the_page: link).html_safe # rubocop:disable Rails/OutputSafety
the_page: link).html_safe
rescue StandardError => e
flash[:danger] = e.message
end
@ -363,8 +363,11 @@ module Admin
Reaction.where(reactable_type: "Comment", reactable_id: user_comment_ids, category: "vomit")
.or(Reaction.where(reactable_type: "Article", reactable_id: user_article_ids, category: "vomit"))
.or(Reaction.where(reactable_type: "User", reactable_id: @user.id, category: "vomit"))
.includes(:reactable)
.includes(:user)
.order(created_at: :desc).limit(15)
@countable_flags = calculate_countable_flags(@related_vomit_reactions)
@score = calculate_score(@related_vomit_reactions)
end
def user_params
@ -410,5 +413,21 @@ module Admin
# but let's query and display the last one for now, that should be enough for most cases
@unpublish_all_data = AuditLog::UnpublishAllsQuery.call(@user.id)
end
def calculate_countable_flags(reactions)
countable_flags = 0
reactions.each do |reaction|
countable_flags += 1 if reaction.status != "invalid"
end
countable_flags
end
def calculate_score(reactions)
score = 0
reactions.each do |reaction|
score += reaction.points
end
score
end
end
end

View file

@ -1,203 +1,240 @@
<% decorated_article = article.decorate %>
<div
data-controller="article"
data-article-id-value="<%= article.id %>"
data-article-pin-path-value="<%= stories_feed_pinned_article_path %>"
data-article-bg-highlighted-class="bg-highlighted"
data-article-border-highlighted-class="border-highlighted"
data-action="ajax:success@document->article#ajaxSuccess">
<article class="js-individual-article crayons-card p-6 flex flex-col gap-4"
data-controller="article"
data-article-id-value="<%= article.id %>"
data-article-pin-path-value="<%= stories_feed_pinned_article_path %>"
data-article-bg-highlighted-class="bg-highlighted"
data-article-border-highlighted-class="border-highlighted"
data-action="ajax:success@document->article#ajaxSuccess">
<header>
<% if decorated_article.pinned? || article.featured || article.approved || (!article.published? && article.published_from_feed?) || article.video || article.user&.warned? %>
<div class="flex gap-1 mb-1">
<% if !article.published? && article.published_from_feed? %>
<span class="c-indicator c-indicator--danger"><%= t("views.moderations.article.unplublished") %></span>
<% end %>
<% if decorated_article.pinned? %>
<span class="c-indicator c-indicator--warning" data-testid="pinned-indicator"><%= t("views.moderations.article.pinned") %></span>
<% end %>
<% if article.featured %>
<span class="c-indicator c-indicator--success"><%= t("views.moderations.article.featured") %></span>
<% end %>
<% if article.approved %>
<span class="c-indicator c-indicator--info"><%= t("views.moderations.article.approved") %></span>
<% end %>
<% if article.video %>
<span class="c-indicator"><%= t("views.moderations.article.contains_video") %></span>
<% end %>
<% cache "admin-user-info-#{article.user_id}-#{article.user&.updated_at}", expires_in: 4.hours do %>
<% if article.user&.warned? %>
<span class="c-indicator c-indicator--danger"><%= t("views.moderations.article.user_warned") %></span>
<article class="js-individual-article crayons-card p-6 flex flex-col gap-4">
<header>
<% if decorated_article.pinned? || article.featured || article.approved || (!article.published? && article.published_from_feed?) || article.video || article.user&.warned? %>
<div class="flex gap-1 mb-1">
<% if !article.published? && article.published_from_feed? %>
<span class="c-indicator c-indicator--danger"><%= t("views.moderations.article.unplublished") %></span>
<% end %>
<% end %>
<% if decorated_article.pinned? %>
<span class="c-indicator c-indicator--warning" data-testid="pinned-indicator"><%= t("views.moderations.article.pinned") %></span>
<% end %>
<% if article.featured %>
<span class="c-indicator c-indicator--success"><%= t("views.moderations.article.featured") %></span>
<% end %>
<% if article.approved %>
<span class="c-indicator c-indicator--info"><%= t("views.moderations.article.approved") %></span>
<% end %>
<% if article.video %>
<span class="c-indicator"><%= t("views.moderations.article.contains_video") %></span>
<% end %>
<% cache "admin-user-info-#{article.user_id}-#{article.user&.updated_at}", expires_in: 4.hours do %>
<% if article.user&.warned? %>
<span class="c-indicator c-indicator--danger"><%= t("views.moderations.article.user_warned") %></span>
<% end %>
<% end %>
</div>
<% end %>
<% if article.main_image.present? %>
<a href="<%= article.path %>" target="_blank" rel="noopener" class="c-link c-link--branded mb-4">
<img src="<%= cloud_cover_url(article.main_image) %>" style="background: <%= article.main_image_background_hex_color %>; max-height:150px; max-width: 300px;" alt="" loading="lazy" class="block radius-default h-auto w-100" />
</a>
<% end %>
<div class="flex justify-between gap-4 items-center">
<h2 class="crayons-subtitle-1">
<a href="<%= article.path %>" target="_blank" rel="noopener" class="c-link c-link--branded">
<%= article.title %>
</a>
</h2>
<div class="flex items-center gap-1">
<span class="crayons-card crayons-card--secondary px-3 py-1 flex gap-2 items-center" title="<%= t("views.moderations.actions.thumb_up") %>">
<%= crayons_icon_tag("twemoji/thumb-up", native: true, width: 16, height: 16) %>
<span class="fs-s fw-medium lh-base"><%= article.privileged_reaction_counts["thumbsup"] || "0" %></span>
</span>
<span class="crayons-card crayons-card--secondary px-3 py-1 flex gap-2 items-center" title="<%= t("views.moderations.actions.thumb_down") %>">
<%= crayons_icon_tag("twemoji/thumb-down", native: true, width: 16, height: 16) %>
<span class="fs-s fw-medium lh-base"><%= article.privileged_reaction_counts["thumbsdown"] || "0" %></span>
</span>
<span class="crayons-card crayons-card--secondary px-3 py-1 flex gap-2 items-center" title="<%= t("views.moderations.actions.vomit") %>">
<%= crayons_icon_tag("twemoji/suspicious", native: true, width: 16, height: 16) %>
<span class="fs-s fw-medium lh-base"><%= (@countable_vomits&.dig(article.id) || 0) %></span>
</span>
<span class="crayons-card crayons-card--secondary px-3 py-1 ml-3 flex gap-2 items-center" title="<%= t("views.moderations.actions.score") %>">
<%= crayons_icon_tag("analytics", native: true, width: 16, height: 16) %>
<span class="fs-s fw-medium lh-base"><%= article.score %></span>
</span>
</div>
</div>
<% end %>
<% if article.main_image.present? %>
<a href="<%= article.path %>" target="_blank" rel="noopener" class="c-link c-link--branded mb-4">
<img src="<%= cloud_cover_url(article.main_image) %>" style="background: <%= article.main_image_background_hex_color %>; max-height:150px; max-width: 300px;" alt="" loading="lazy" class="block radius-default h-auto w-100" />
</a>
<% end %>
<div class="-ml-1 fs-s flex items-center gap-4">
<div class="flex gap-px">
<% decorated_article.cached_tag_list_array.each do |tag| %>
<a class="crayons-tag crayons-tag--monochrome" href='<%= tag_path(tag) %>'><span class="crayons-tag__prefix">#</span><%= tag %></a>
<% end %>
</div>
<% privileged_article_reactions = article.reactions.privileged_category.select { |reaction| reaction.reactable_type == "Article" } %>
<% vomit_article_reactions = privileged_article_reactions.select { |reaction| reaction.category == "vomit" } %>
<% countable_vomits = 0 %>
<% if vomit_article_reactions.present? %>
<% vomit_article_reactions.each do |vomit_reaction| %>
<% if vomit_reaction.status != 'invalid' %>
<% countable_vomits += 1 %>
<% end %>
<span>❤️&nbsp;<%= article.public_reactions_count %> <%= t("views.moderations.article.likes") %></span>
<span>💬&nbsp;<%= article.comments_count %> <%= t("views.moderations.article.comments") %></span>
</div>
<div class="flex justify-between gap-4 items-center">
<div>
<a href="<%= admin_user_path(article.user_id) %>" class="c-link">@<%= article.user&.username %></a>
<% if article.published_from_feed? && !article.published? %>
RSS Import <%= article.created_at.strftime("%b %d, %Y") %>
Originally Published <%= article.published_at&.strftime("%b %d, %Y") %>
<% elsif article.crossposted_at? %>
Crossposted <%= article.crossposted_at.strftime("%b %d, %Y") %> & Published
<%= article.published_at&.strftime("%b %d, %Y") %>
<% else %>
<%= article.published_at&.strftime("%b %d, %Y") %>
<% end %>
</div>
<div class="flex gap-1">
<% if !defined?(show_on_individual_article_only) || !show_on_individual_article_only %>
<a href="/admin/content_manager/articles/<%= article.id %>" class="c-link c-link--block">
<%= t("views.moderations.article.view_details") %>
</a>
<% end %>
<% if decorated_article.pinned? %>
<form method="post" action="<%= unpin_admin_article_path(decorated_article.id) %>" class="inline"
data-action="submit->article#unpinArticle">
<input type="hidden" name="_method" value="delete" />
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>" />
<button type="submit" class="c-btn">
<%= t("views.moderations.article.unpin_post") %>
</button>
</form>
<% else %>
<form method="post" action="<%= pin_admin_article_path(decorated_article.id) %>" class="inline"
data-action="submit->article#pinArticle">
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>" />
<button type="submit" class="c-btn">
<%= t("views.moderations.article.pin_post") %>
</button>
</form>
<% end %>
<a class="c-link c-link--block" href="<%= admin_user_path(article.user_id) %>" target="_blank" rel="noopener"><%= t("views.moderations.article.user") %></a>
<a href="<%= article.path %>/edit" target="_blank" rel="noopener" class="c-link c-link--block"><%= t("views.moderations.article.edit") %></a>
</div>
</div>
</header>
<% cache "admin-user-notes-#{article.user_id}-#{article.user&.updated_at}", expires_in: 4.hours do %>
<% if article.user&.notes&.any? %>
<div class="crayons-card crayons-card--secondary p-4 pt-3">
<div class="flex gap-2 items-center">
<h3 class="crayons-subtitle-2"><%= article.user&.notes&.size %> <%= t("views.moderations.article.notes.user_notes") %></h3>
<a href="<%= admin_user_path(article.user_id) %>" class="c-link c-link--block c-link--branded"><%= t("views.moderations.article.notes.view_all") %></a>
</div>
<% article.user&.notes&.last(3)&.each do |note| %>
<p>
<%= note.content %>
<span class="color-secondary fs-s">
<%= note.author_id ? User.find(note.author_id).username : "No author" %>
<time datetime="<%= note.created_at.strftime("%Y-%m-%dT%H:%M:%S%z") %>" title="<%= note.created_at.strftime("%Y-%m-%d, %H:%M") %>">
<%= note.created_at.strftime("%b %e '%y") %>
</time>
</span>
</p>
<% end %>
</div>
<% end %>
<% end %>
<div class="flex justify-between gap-4 items-center">
<h2 class="crayons-subtitle-1">
<a href="<%= article.path %>" target="_blank" rel="noopener" class="c-link c-link--branded">
<%= article.title %>
</a>
</h2>
<div class="flex items-center gap-1">
<span class="crayons-card crayons-card--secondary px-3 py-1 flex gap-2 items-center" title="<%= t("views.moderations.actions.thumb_up") %>">
<%= crayons_icon_tag("twemoji/thumb-up", native: true, width: 16, height: 16) %>
<span class="fs-s fw-medium lh-base"><%= article.privileged_reaction_counts["thumbsup"] || "0" %></span>
</span>
<span class="crayons-card crayons-card--secondary px-3 py-1 flex gap-2 items-center" title="<%= t("views.moderations.actions.thumb_down") %>">
<%= crayons_icon_tag("twemoji/thumb-down", native: true, width: 16, height: 16) %>
<span class="fs-s fw-medium lh-base"><%= article.privileged_reaction_counts["thumbsdown"] || "0" %></span>
</span>
<span class="crayons-card crayons-card--secondary px-3 py-1 flex gap-2 items-center" title="<%= t("views.moderations.actions.vomit") %>">
<%= crayons_icon_tag("twemoji/suspicious", native: true, width: 16, height: 16) %>
<span class="fs-s fw-medium lh-base"><%= countable_vomits %></span>
</span>
<span class="crayons-card crayons-card--secondary px-3 py-1 ml-3 flex gap-2 items-center" title="<%= t("views.moderations.actions.score") %>">
<%= crayons_icon_tag("analytics", native: true, width: 16, height: 16) %>
<span class="fs-s fw-medium lh-base"><%= article.score %></span>
</span>
</div>
</div>
<div class="-ml-1 fs-s flex items-center gap-4">
<div class="flex gap-px">
<% decorated_article.cached_tag_list_array.each do |tag| %>
<a class="crayons-tag crayons-tag--monochrome" href='<%= tag_path(tag) %>'><span class="crayons-tag__prefix">#</span><%= tag %></a>
<% end %>
</div>
<span>❤️&nbsp;<%= article.public_reactions_count %> <%= t("views.moderations.article.likes") %></span>
<span>💬&nbsp;<%= article.comments_count %> <%= t("views.moderations.article.comments") %></span>
</div>
<div class="flex justify-between gap-4 items-center">
<div>
<a href="<%= admin_user_path(article.user_id) %>" class="c-link">@<%= article.user&.username %></a>
<% if article.published_from_feed? && !article.published? %>
RSS Import <%= article.created_at.strftime("%b %d, %Y") %>
Originally Published <%= article.published_at&.strftime("%b %d, %Y") %>
<% elsif article.crossposted_at? %>
Crossposted <%= article.crossposted_at.strftime("%b %d, %Y") %> & Published
<%= article.published_at&.strftime("%b %d, %Y") %>
<% else %>
<%= article.published_at&.strftime("%b %d, %Y") %>
<% end %>
</div>
<div class="flex gap-1">
<a href="/admin/content_manager/articles/<%= article.id %>" class="c-link c-link--block"><%= t("views.moderations.article.view_details") %></a>
<% if decorated_article.pinned? %>
<form method="post" action="<%= unpin_admin_article_path(decorated_article.id) %>" class="inline"
data-action="submit->article#unpinArticle">
<input type="hidden" name="_method" value="delete" />
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>" />
<button type="submit" class="c-btn">
<%= t("views.moderations.article.unpin_post") %>
</button>
</form>
<% else %>
<form method="post" action="<%= pin_admin_article_path(decorated_article.id) %>" class="inline"
data-action="submit->article#pinArticle">
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>" />
<button type="submit" class="c-btn">
<%= t("views.moderations.article.pin_post") %>
</button>
</form>
<% end %>
<a class="c-link c-link--block" href="<%= admin_user_path(article.user_id) %>" target="_blank" rel="noopener"><%= t("views.moderations.article.user") %></a>
<a href="<%= article.path %>/edit" target="_blank" rel="noopener" class="c-link c-link--block"><%= t("views.moderations.article.edit") %></a>
</div>
</div>
</header>
<% cache "admin-user-notes-#{article.user_id}-#{article.user&.updated_at}", expires_in: 4.hours do %>
<% if article.user&.notes&.any? %>
<div class="crayons-card crayons-card--secondary p-4 pt-3">
<div class="flex gap-2 items-center">
<h3 class="crayons-subtitle-2"><%= article.user&.notes&.size %> <%= t("views.moderations.article.notes.user_notes") %></h3>
<a href="<%= admin_user_path(article.user_id) %>" class="c-link c-link--block c-link--branded"><%= t("views.moderations.article.notes.view_all") %></a>
<%= form_with url: admin_article_path(article.id), model: article, local: true, class: "crayons-card crayons-card--secondary p-4 flex flex-col gap-3" do |f| %>
<input name="utf8" type="hidden" value="✓">
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>" />
<input type="hidden" name="_method" value="patch" />
<div class="flex gap-4">
<div class="crayons-field flex-1">
<label for="author_id_<%= article.id %>" class="crayons-field__label"><%= t("views.moderations.article.author_id") %></label>
<input id="author_id_<%= article.id %>" class="crayons-textfield" size="6" name="article[user_id]"
value="<%= article.user_id %>">
</div>
<% article.user&.notes&.last(3)&.each do |note| %>
<p>
<%= note.content %>
<span class="color-secondary fs-s">
<%= note.author_id ? User.find(note.author_id).username : "No author" %>
<time datetime="<%= note.created_at.strftime("%Y-%m-%dT%H:%M:%S%z") %>" title="<%= note.created_at.strftime("%Y-%m-%d, %H:%M") %>">
<%= note.created_at.strftime("%b %e '%y") %>
</time>
</span>
</p>
<% end %>
</div>
<% end %>
<% end %>
<%= form_with url: admin_article_path(article.id), model: article, local: true, class: "crayons-card crayons-card--secondary p-4 flex flex-col gap-3" do |f| %>
<input name="utf8" type="hidden" value="✓">
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>" />
<input type="hidden" name="_method" value="patch" />
<div class="flex gap-4">
<div class="crayons-field flex-1">
<label for="author_id_<%= article.id %>" class="crayons-field__label"><%= t("views.moderations.article.author_id") %></label>
<input id="author_id_<%= article.id %>" class="crayons-textfield" size="6" name="article[user_id]"
value="<%= article.user_id %>">
<div class="crayons-field flex-1">
<label for="co_author_ids_list_<%= article.id %>" class="crayons-field__label"><%= t("views.moderations.article.co_author_ids") %></label>
<input id="co_author_ids_list_<%= article.id %>" class="crayons-textfield" size="6" name="article[co_author_ids_list]" placeholder="Comma separated"
value="<%= article.co_author_ids&.join(", ") %>">
</div>
</div>
<div class="crayons-field flex-1">
<label for="co_author_ids_list_<%= article.id %>" class="crayons-field__label"><%= t("views.moderations.article.co_author_ids") %></label>
<input id="co_author_ids_list_<%= article.id %>" class="crayons-textfield" size="6" name="article[co_author_ids_list]" placeholder="Comma separated"
value="<%= article.co_author_ids&.join(", ") %>">
</div>
</div>
<% if article.published? %>
<div class="flex">
<div class="crayons-field">
<label for="published_at_<%= article.id %>" class="crayons-field__label"><%= t("views.moderations.article.published_at") %></label>
<div class="flex gap-1 items-center flex-wrap">
<%= f.datetime_select :published_at, { required: true, include_blank: true, include_seconds: true }, class: "crayons-select w-auto" %>
<span class="whitespace-nowrap color-secondary">UTC</span>
<% if article.published? %>
<div class="flex">
<div class="crayons-field">
<label for="published_at_<%= article.id %>" class="crayons-field__label"><%= t("views.moderations.article.published_at") %></label>
<div class="flex gap-1 items-center flex-wrap">
<%= f.datetime_select :published_at, { required: true, include_blank: true, include_seconds: true }, class: "crayons-select w-auto" %>
<span class="whitespace-nowrap color-secondary">UTC</span>
</div>
</div>
</div>
<% end %>
<div class="flex items-center gap-4">
<div class="crayons-field crayons-field--checkbox">
<%= f.check_box :featured, id: "featured-#{article.id}", class: "crayons-checkbox" %>
<label for="featured-<%= article.id %>" class="crayons-field__label">
<%= t("views.moderations.article.featured") %>
</label>
</div>
<div class="crayons-field crayons-field--checkbox">
<%= f.check_box :approved, id: "approved-#{article.id}", class: "crayons-checkbox" %>
<label for="approved-<%= article.id %>" class="crayons-field__label">
<%= t("views.moderations.article.approved") %>
</label>
</div>
</div>
<div>
<button class="c-btn c-btn--secondary"><%= t("views.moderations.article.save") %></button>
</div>
<% end %>
<div class="flex items-center gap-4">
<div class="crayons-field crayons-field--checkbox">
<%= f.check_box :featured, id: "featured-#{article.id}", class: "crayons-checkbox" %>
<label for="featured-<%= article.id %>" class="crayons-field__label">
<%= t("views.moderations.article.featured") %>
</label>
</div>
</article>
<div class="crayons-field crayons-field--checkbox">
<%= f.check_box :approved, id: "approved-#{article.id}", class: "crayons-checkbox" %>
<label for="approved-<%= article.id %>" class="crayons-field__label">
<%= t("views.moderations.article.approved") %>
</label>
</div>
</div>
<div>
<button class="c-btn c-btn--secondary"><%= t("views.moderations.article.save") %></button>
</div>
<% if defined?(show_on_individual_article_only) && show_on_individual_article_only %>
<% privileged_article_reactions = article.reactions.privileged_category.select { |reaction| reaction.reactable_type == "Article" } %>
<% vomit_article_reactions = privileged_article_reactions.select { |reaction| reaction.category == "vomit" }.reverse %>
<% quality_article_reactions = (privileged_article_reactions - vomit_article_reactions).reverse %>
<article class="js-individual-article crayons-card py-6 flex flex-col mt-4">
<h2 class="crayons-subtitle-2 mx-6"><%= t("views.admin.articles.priviliged_actions.title") %></h2>
<p class="crayons-subtitle-3 fw-normal color-secondary mt-1 mx-6"><%= t("views.admin.articles.priviliged_actions.description") %></p>
<nav class="mt-4 pt-1 pb-2 px-3 member-data-heading" aria-label="Member details">
<ul class="crayons-navigation crayons-navigation--horizontal">
<li><%= link_to "Flags", admin_article_path(tab: :flags), class: "crayons-navigation__item #{'crayons-navigation__item crayons-navigation__item--current' if params[:tab] == 'flags' || params[:tab].blank?}", aria: @current_tab == "flags" ? { current: "" } : {} %></li></li>
<li><%= link_to "Quality reactions", admin_article_path(tab: :quality_reactions), class: "crayons-navigation__item #{'crayons-navigation__item crayons-navigation__item--current' if params[:tab] == 'quality_reactions'}",
aria: @current_tab == "quality_reactions" ? { current: "" } : {} %></li></li>
</ul>
</nav>
<div id="reaction-content" class="flex flex-col gap-3 px-6 mt-6" style="overflow: auto; height: 406px;">
<% if params[:tab].blank? || params[:tab] == "flags" %>
<%= render "admin/shared/flag_reactions_table",
vomit_reactions: vomit_article_reactions,
text_section: "articles",
empty_text: t("views.admin.articles.priviliged_actions.no_flags") %>
<% end %>
<% if params[:tab] == "quality_reactions" %>
<% if quality_article_reactions.present? %>
<% quality_article_reactions.each do |quality_reaction| %>
<%= render "admin/articles/quality_action_item", quality_reaction: quality_reaction %>
<hr id="js__reaction__div__hr__<%= quality_reaction.id %>" class="w-100 hr-no-margins">
<% end %>
<% else %>
<div class="flex flex-col justify-center items-center gap-4 h-100">
<div class="flex p-4 gap-2 radius-default" style="background: #EEF2FF;">
<%= crayons_icon_tag("quality-reactions", native: true, width: 56, height: 56) %>
</div>
<p class="crayons-subtitle-3 fw-normal color-secondary"><%= t("views.admin.articles.priviliged_actions.no_quality_reactions") %></p>
</div>
<% end %>
<% end %>
<div>
</article>
<% end %>
</article>
</div>

View file

@ -1,252 +0,0 @@
<% decorated_article = article.decorate %>
<%= javascript_packs_with_chunks_tag "admin/articles/vomitReactionItem", defer: true %>
<div
data-controller="article"
data-article-id-value="<%= article.id %>"
data-article-pin-path-value="<%= stories_feed_pinned_article_path %>"
data-article-bg-highlighted-class="bg-highlighted"
data-article-border-highlighted-class="border-highlighted"
data-action="ajax:success@document->article#ajaxSuccess">
<% privileged_article_reactions = article.reactions.privileged_category.select { |reaction| reaction.reactable_type == "Article" } %>
<% vomit_article_reactions = privileged_article_reactions.select { |reaction| reaction.category == "vomit" }.reverse %>
<% quality_article_reactions = (privileged_article_reactions - vomit_article_reactions).reverse %>
<% countable_vomits = 0 %>
<% if vomit_article_reactions.present? %>
<% vomit_article_reactions.each do |vomit_reaction| %>
<% if vomit_reaction.status != 'invalid' %>
<% countable_vomits += 1 %>
<% end %>
<% end %>
<% end %>
<article class="js-individual-article crayons-card p-6 flex flex-col gap-4">
<header>
<% if decorated_article.pinned? || article.featured || article.approved || (!article.published? && article.published_from_feed?) || article.video || article.user&.warned? %>
<div class="flex gap-1 mb-1">
<% if !article.published? && article.published_from_feed? %>
<span class="c-indicator c-indicator--danger"><%= t("views.moderations.article.unplublished") %></span>
<% end %>
<% if decorated_article.pinned? %>
<span class="c-indicator c-indicator--warning" data-testid="pinned-indicator"><%= t("views.moderations.article.pinned") %></span>
<% end %>
<% if article.featured %>
<span class="c-indicator c-indicator--success"><%= t("views.moderations.article.featured") %></span>
<% end %>
<% if article.approved %>
<span class="c-indicator c-indicator--info"><%= t("views.moderations.article.approved") %></span>
<% end %>
<% if article.video %>
<span class="c-indicator"><%= t("views.moderations.article.contains_video") %></span>
<% end %>
<% cache "admin-user-info-#{article.user_id}-#{article.user&.updated_at}", expires_in: 4.hours do %>
<% if article.user&.warned? %>
<span class="c-indicator c-indicator--danger"><%= t("views.moderations.article.user_warned") %></span>
<% end %>
<% end %>
</div>
<% end %>
<% if article.main_image.present? %>
<a href="<%= article.path %>" target="_blank" rel="noopener" class="c-link c-link--branded mb-4">
<img src="<%= cloud_cover_url(article.main_image) %>" style="background: <%= article.main_image_background_hex_color %>; max-height:150px; max-width: 300px;" alt="" loading="lazy" class="block radius-default h-auto w-100" />
</a>
<% end %>
<div class="flex justify-between gap-4 items-center">
<h2 class="crayons-subtitle-1">
<a href="<%= article.path %>" target="_blank" rel="noopener" class="c-link c-link--branded">
<%= article.title %>
</a>
</h2>
<div class="flex items-center gap-1">
<span class="crayons-card crayons-card--secondary px-3 py-1 flex gap-2 items-center" title="<%= t("views.moderations.actions.thumb_up") %>">
<%= crayons_icon_tag("twemoji/thumb-up", native: true, width: 16, height: 16) %>
<span class="fs-s fw-medium lh-base"><%= article.privileged_reaction_counts["thumbsup"] || "0" %></span>
</span>
<span class="crayons-card crayons-card--secondary px-3 py-1 flex gap-2 items-center" title="<%= t("views.moderations.actions.thumb_down") %>">
<%= crayons_icon_tag("twemoji/thumb-down", native: true, width: 16, height: 16) %>
<span class="fs-s fw-medium lh-base"><%= article.privileged_reaction_counts["thumbsdown"] || "0" %></span>
</span>
<span class="crayons-card crayons-card--secondary px-3 py-1 flex gap-2 items-center" title="<%= t("views.moderations.actions.vomit") %>">
<%= crayons_icon_tag("twemoji/suspicious", native: true, width: 16, height: 16) %>
<span class="fs-s fw-medium lh-base"><%= countable_vomits %></span>
</span>
<span class="crayons-card crayons-card--secondary px-3 py-1 ml-3 flex gap-2 items-center" title="<%= t("views.moderations.actions.score") %>">
<%= crayons_icon_tag("analytics", native: true, width: 16, height: 16) %>
<span class="fs-s fw-medium lh-base"><%= article.score %></span>
</span>
</div>
</div>
<div class="-ml-1 fs-s flex items-center gap-4">
<div class="flex gap-px">
<% decorated_article.cached_tag_list_array.each do |tag| %>
<a class="crayons-tag crayons-tag--monochrome" href='<%= tag_path(tag) %>'><span class="crayons-tag__prefix">#</span><%= tag %></a>
<% end %>
</div>
<span>❤️&nbsp;<%= article.public_reactions_count %> <%= t("views.moderations.article.likes") %></span>
<span>💬&nbsp;<%= article.comments_count %> <%= t("views.moderations.article.comments") %></span>
</div>
<div class="flex justify-between gap-4 items-center">
<div>
<a href="<%= admin_user_path(article.user_id) %>" class="c-link">@<%= article.user&.username %></a>
<% if article.published_from_feed? && !article.published? %>
RSS Import <%= article.created_at.strftime("%b %d, %Y") %>
Originally Published <%= article.published_at&.strftime("%b %d, %Y") %>
<% elsif article.crossposted_at? %>
Crossposted <%= article.crossposted_at.strftime("%b %d, %Y") %> & Published
<%= article.published_at&.strftime("%b %d, %Y") %>
<% else %>
<%= article.published_at&.strftime("%b %d, %Y") %>
<% end %>
</div>
<div class="flex gap-1">
<% if decorated_article.pinned? %>
<form method="post" action="<%= unpin_admin_article_path(decorated_article.id) %>" class="inline"
data-action="submit->article#unpinArticle">
<input type="hidden" name="_method" value="delete" />
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>" />
<button type="submit" class="c-btn">
<%= t("views.moderations.article.unpin_post") %>
</button>
</form>
<% else %>
<form method="post" action="<%= pin_admin_article_path(decorated_article.id) %>" class="inline"
data-action="submit->article#pinArticle">
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>" />
<button type="submit" class="c-btn">
<%= t("views.moderations.article.pin_post") %>
</button>
</form>
<% end %>
<a class="c-link c-link--block" href="<%= admin_user_path(article.user_id) %>" target="_blank" rel="noopener">User</a>
<a href="<%= article.path %>/edit" target="_blank" rel="noopener" class="c-link c-link--block">Edit</a>
</div>
</div>
</header>
<% cache "admin-user-notes-#{article.user_id}-#{article.user&.updated_at}", expires_in: 4.hours do %>
<% if article.user&.notes&.any? %>
<div class="crayons-card crayons-card--secondary p-4 pt-3">
<div class="flex gap-2 items-center">
<h3 class="crayons-subtitle-2"><%= article.user&.notes&.size %> <%= t("views.moderations.article.notes.user_notes") %></h3>
<a href="<%= admin_user_path(article.user_id) %>" class="c-link c-link--block c-link--branded"><%= t("views.moderations.article.notes.view_all") %></a>
</div>
<% article.user&.notes&.last(3)&.each do |note| %>
<p>
<%= note.content %>
<span class="color-secondary fs-s">
<%= note.author_id ? User.find(note.author_id).username : "No author" %>
<time datetime="<%= note.created_at.strftime("%Y-%m-%dT%H:%M:%S%z") %>" title="<%= note.created_at.strftime("%Y-%m-%d, %H:%M") %>">
<%= note.created_at.strftime("%b %e '%y") %>
</time>
</span>
</p>
<% end %>
</div>
<% end %>
<% end %>
<%= form_with url: admin_article_path(article.id), model: article, local: true, class: "crayons-card crayons-card--secondary p-4 flex flex-col gap-3" do |f| %>
<input name="utf8" type="hidden" value="✓">
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>" />
<input type="hidden" name="_method" value="patch" />
<div class="flex gap-4">
<div class="crayons-field flex-1">
<label for="author_id_<%= article.id %>" class="crayons-field__label"><%= t("views.moderations.article.author_id") %></label>
<input id="author_id_<%= article.id %>" class="crayons-textfield" size="6" name="article[user_id]"
value="<%= article.user_id %>">
</div>
<div class="crayons-field flex-1">
<label for="co_author_ids_list_<%= article.id %>" class="crayons-field__label"><%= t("views.moderations.article.co_author_ids") %></label>
<input id="co_author_ids_list_<%= article.id %>" class="crayons-textfield" size="6" name="article[co_author_ids_list]" placeholder="Comma separated"
value="<%= article.co_author_ids&.join(", ") %>">
</div>
</div>
<% if article.published? %>
<div class="flex">
<div class="crayons-field">
<label for="published_at_<%= article.id %>" class="crayons-field__label"><%= t("views.moderations.article.published_at") %></label>
<div class="flex gap-1 items-center flex-wrap">
<%= f.datetime_select :published_at, { required: true, include_blank: true, include_seconds: true }, class: "crayons-select w-auto" %>
<span class="whitespace-nowrap color-secondary">UTC</span>
</div>
</div>
</div>
<% end %>
<div class="flex items-center gap-4">
<div class="crayons-field crayons-field--checkbox">
<%= f.check_box :featured, id: "featured-#{article.id}", class: "crayons-checkbox" %>
<label for="featured-<%= article.id %>" class="crayons-field__label">
<%= t("views.moderations.article.featured") %>
</label>
</div>
<div class="crayons-field crayons-field--checkbox">
<%= f.check_box :approved, id: "approved-#{article.id}", class: "crayons-checkbox" %>
<label for="approved-<%= article.id %>" class="crayons-field__label">
<%= t("views.moderations.article.approved") %>
</label>
</div>
</div>
<div>
<button class="c-btn c-btn--secondary"><%= t("views.moderations.article.save") %></button>
</div>
<% end %>
</article>
<article class="js-individual-article crayons-card py-6 flex flex-col mt-4">
<h2 class="crayons-subtitle-2 mx-6"><%= t("views.moderations.priviliged_actions.title") %></h2>
<p class="crayons-subtitle-3 fw-normal color-secondary mt-1 mx-6"><%= t("views.moderations.priviliged_actions.description") %></p>
<nav class="mt-4 pt-1 pb-2 px-3 member-data-heading" aria-label="Member details">
<ul class="crayons-navigation crayons-navigation--horizontal">
<li><%= link_to "Flags", admin_article_path(tab: :flags), class: "crayons-navigation__item #{'crayons-navigation__item crayons-navigation__item--current' if params[:tab] == 'flags' || params[:tab].blank?}", aria: @current_tab == "flags" ? { current: "" } : {} %></li></li>
<li><%= link_to "Quality reactions", admin_article_path(tab: :quality_reactions), class: "crayons-navigation__item #{'crayons-navigation__item crayons-navigation__item--current' if params[:tab] == 'quality_reactions'}",
aria: @current_tab == "quality_reactions" ? { current: "" } : {} %></li></li>
</ul>
</nav>
<div id="reaction-content" class="flex flex-col gap-3 px-6 mt-6" style="overflow: scroll; height: 406px;">
<% if params[:tab].blank? || params[:tab] == "flags" %>
<% if vomit_article_reactions.present? %>
<% vomit_article_reactions.each do |vomit_reaction| %>
<%= render "admin/articles/flag_action_item", vomit_reaction: vomit_reaction %>
<hr id="js__reaction__div__hr__<%= vomit_reaction.id %>" class="w-100 hr-no-margins">
<% end %>
<% else %>
<div class="flex flex-col justify-center items-center gap-4 h-100">
<div class="flex p-4 gap-2 radius-default" style="background: #EEF2FF;">
<%= crayons_icon_tag("flags", native: true, width: 56, height: 56) %>
</div>
<p class="crayons-subtitle-3 fw-normal color-secondary"><%= t("views.moderations.priviliged_actions.no_flags") %></p>
</div>
<% end %>
<% end %>
<% if params[:tab] == "quality_reactions" %>
<% if quality_article_reactions.present? %>
<% quality_article_reactions.each do |quality_reaction| %>
<%= render "admin/articles/quality_action_item", quality_reaction: quality_reaction %>
<hr id="js__reaction__div__hr__<%= quality_reaction.id %>" class="w-100 hr-no-margins">
<% end %>
<% else %>
<div class="flex flex-col justify-center items-center gap-4 h-100">
<div class="flex p-4 gap-2 radius-default" style="background: #EEF2FF;">
<%= crayons_icon_tag("quality-reactions", native: true, width: 56, height: 56) %>
</div>
<p class="crayons-subtitle-3 fw-normal color-secondary"><%= t("views.moderations.priviliged_actions.no_quality_reactions") %></p>
</div>
<% end %>
<% end %>
<div>
</article>
</div>

View file

@ -8,6 +8,6 @@
data-article-pinned-modal-ok-button-id-value="article-pin-modal-ok"
data-action="article-pinned-modal:open@document->article-pinned-modal#openModal">
<%= render partial: "individual_article", locals: { article: @article } %>
<%= render partial: "article_item", locals: { article: @article, show_on_individual_article_only: true } %>
<%= render partial: "pinned_article_modal" %>
</div>

View file

@ -21,29 +21,32 @@
<%# In both the FE and BE, 'Invalid' remains the same. This status appears when an admin user marks a flag or vomit reaction as invalid, with a score of 0. %>
<div class="flex w-100 m:w-60 gap-0 m:gap-6 ml-2">
<% if vomit_reaction.status == 'valid' %>
<span class="c-indicator c-indicator--relaxed c-indicator--warning" title="<%= t("views.moderations.flags.open.title") %>">
<%= t("views.moderations.flags.open.value") %>
<span class="c-indicator c-indicator--relaxed c-indicator--warning" title="<%= t("views.admin.shared.flags.open.title") %>">
<%= t("views.admin.shared.flags.open.value") %>
</span>
<% elsif vomit_reaction.status == 'invalid' %>
<span class="c-indicator c-indicator--relaxed" title="<%= t("views.moderations.flags.invalid.title") %>">
<%= t("views.moderations.flags.invalid.value") %>
<span class="c-indicator c-indicator--relaxed" title="<%= t("views.admin.shared.flags.invalid.title") %>">
<%= t("views.admin.shared.flags.invalid.value") %>
</span>
<% elsif vomit_reaction.status == 'confirmed' %>
<span class="c-indicator c-indicator--relaxed c-indicator--danger" title="<%= t("views.moderations.flags.valid.title") %>">
<%= t("views.moderations.flags.valid.value") %>
<span class="c-indicator c-indicator--relaxed c-indicator--danger" title="<%= t("views.admin.shared.flags.valid.title") %>">
<%= t("views.admin.shared.flags.valid.value") %>
</span>
<% else %>
<%# This case should never arise. %>
<span class="c-indicator c-indicator--relaxed c-indicator--info" title="<%= t("views.moderations.flags.unidentified.title") %>">
<%= t("views.moderations.flags.unidentified.value") %>
<span class="c-indicator c-indicator--relaxed c-indicator--info" title="<%= t("views.admin.shared.flags.unidentified.title") %>">
<%= t("views.admin.shared.flags.unidentified.value") %>
</span>
<% end %>
<span data-testid="flag-score" class="crayons-card crayons-card--secondary px-3 py-1 ml-3 flex gap-2 items-center" title="Score affected by a particular flag">
<span
data-testid="flag-score"
class="crayons-card crayons-card--secondary px-3 py-1 ml-3 flex gap-2 items-center"
title="<%= t("views.admin.#{text_section}.flags.score") %>">
<%= crayons_icon_tag("analytics", native: true, width: 16, height: 16) %>
<span class="fs-s fw-medium lh-base"><%= vomit_reaction.points %></span>
</span>
</div>
</div>
<%= render "admin/articles/priviledged_reaction_actions_dropdown", vomit_reaction: vomit_reaction %>
<%= render "admin/shared/flag_reaction_item_dropdown_menu", vomit_reaction: vomit_reaction %>
</div>

View file

@ -1,6 +1,6 @@
<div class="relative">
<button type="button" id="<%= vomit_reaction.id %>-action-dropdown-btn" data-toggle-dropdown="<%= vomit_reaction.id %>-action-dropdown" class="c-btn c-btn--icon-alone ml-auto"
aria-expanded="false" aria-controls="<%= vomit_reaction.id %>-action-dropdown" aria-haspopup="true" aria-label="Actions: <%= vomit_reaction.id %>">
aria-expanded="false" aria-controls="<%= vomit_reaction.id %>-action-dropdown" aria-haspopup="true" aria-label="Flag actions: <%= vomit_reaction.id %>">
<%= crayons_icon_tag("overflow-horizontal", aria_hidden: true) %>
</button>
<div id="<%= vomit_reaction.id %>-action-dropdown" class="crayons-dropdown right-0">
@ -17,7 +17,7 @@
data-remove-element="false"
class="c-btn align-left w-100 c-btn--icon-left">
<%= crayons_icon_tag("checkmark", aria_hidden: true, class: "c-btn__icon") %>
<%= t("views.moderations.flags.actions.mark_as_valid") %>
<%= t("views.admin.shared.flags.actions.mark_as_valid") %>
</button>
</li>
<% end %>
@ -33,7 +33,7 @@
data-remove-element="false"
class="c-btn align-left w-100 c-btn--icon-left">
<%= crayons_icon_tag("x", aria_hidden: true, class: "c-btn__icon") %>
<%= t("views.moderations.flags.actions.mark_as_invalid") %>
<%= t("views.admin.shared.flags.actions.mark_as_invalid") %>
</button>
</li>
<% end %>

View file

@ -0,0 +1,14 @@
<%= javascript_packs_with_chunks_tag "admin/shared/flagReactionItemDropdownButton", defer: true %>
<% if vomit_reactions.present? %>
<% vomit_reactions.each do |vomit_reaction| %>
<%= render "admin/shared/flag_reaction_item", vomit_reaction: vomit_reaction, text_section: text_section %>
<hr id="js__reaction__div__hr__<%= vomit_reaction.id %>" class="w-100 hr-no-margins">
<% end %>
<% else %>
<div class="flex flex-col justify-center items-center gap-4 h-100">
<div class="flex p-4 gap-2 radius-default" style="background: #EEF2FF;">
<%= crayons_icon_tag("flags", native: true, width: 56, height: 56) %>
</div>
<p class="crayons-subtitle-3 fw-normal color-secondary"><%= empty_text %></p>
</div>
<% end %>

View file

@ -51,7 +51,7 @@
<%= render "admin/users/show/reports/index" %>
</section>
<% elsif @current_tab == "flags" %>
<section class="crayons-card p-3 s:p-4 m:p-7">
<section class="crayons-card p-3 s:p-4 m:p-6">
<%= render "admin/users/show/flags/index" %>
</section>
<% elsif @current_tab == "unpublish_logs" %>

View file

@ -1,43 +1,26 @@
<h2 class="crayons-subtitle-1">Flags received (<%= @related_vomit_reactions.count %>)</h2>
<% user_vomit_reactions = @related_vomit_reactions.select { |flag| flag.reactable_type == "User" } %>
<% unless @related_vomit_reactions.empty? %>
<div class="pt-3">
<% @related_vomit_reactions.each do |flag| %>
<article class="c-list-item flex gap-2">
<span class="color-secondary shrink-0">
<% if flag.reactable_type == "Article" %>
<%= crayons_icon_tag("post", aria_hidden: true) %>
<% elsif flag.reactable_type == "User" %>
<%= crayons_icon_tag("user-line", aria_hidden: true) %>
<% elsif flag.reactable_type == "Comment" %>
<%= crayons_icon_tag("comment", aria_hidden: true) %>
<% end %>
</span>
<div class="flex-1">
<h3 class="crayons-subtitle-2 mb-1">
<%= link_to flag.reactable.path, class: "c-link" do %>
<% if flag.reactable_type == "Article" %>
<%= t("views.admin.users.flags.type.article", title: flag.reactable.title) %>
<% elsif flag.reactable_type == "User" %>
<%= t("views.admin.users.flags.type.profile") %>
<% elsif flag.reactable_type == "Comment" %>
<%= t("views.admin.users.flags.type.comment", title: flag.reactable.title) %>
<% end %>
<% end %>
</h3>
<p class="fs-s color-secondary">
<%= t("views.admin.users.flags.origin_html", user: link_to(flag.user.username, admin_user_path(flag.user_id), class: "c-link c-link--branded")) %>
</p>
</div>
<time datetime="<%= flag.created_at&.strftime("%Y-%m-%dT%H:%M:%S%z") %>" class="color-secondary fs-s shrink-0" title="<%= l(flag.created_at, format: :admin_user) if flag.created_at %>">
<%= l(flag.created_at, format: :short_with_yy) %>
</time>
</article>
<% end %>
<div class="flex justify-between gap-10">
<div class="flex flex-col flex-1">
<h2 class="crayons-subtitle-2 px-1"><%= t("views.admin.users.priviliged_actions.title") %></h2>
<p class="crayons-subtitle-3 fw-normal color-secondary px-1 mt-1"><%= t("views.admin.users.priviliged_actions.description") %></p>
</div>
<% else %>
<div class="align-center flex flex-col justify-center my-auto py-8">
<h3 class="crayons-subtitle-2 mb-2"><%= t("views.admin.users.flags.empty1", user: @user.name) %></h3>
<p class="color-secondary"><%= t("views.admin.users.flags.empty2", user: @user.name) %></p>
<div class="flex items-start gap-1">
<span class="crayons-card crayons-card--secondary px-3 py-1 flex gap-2 items-center" title="<%= t("views.moderations.actions.vomit") %>">
<%= crayons_icon_tag("twemoji/suspicious", native: true, width: 16, height: 16) %>
<span class="fs-s fw-medium lh-base"><%= @countable_flags %></span>
</span>
<span class="crayons-card crayons-card--secondary px-3 py-1 ml-3 flex gap-2 items-center" title="<%= t("views.admin.users.flags.total_score") %>">
<%= crayons_icon_tag("analytics", native: true, width: 16, height: 16) %>
<span class="fs-s fw-medium lh-base"><%= @score %></span>
</span>
</div>
<% end %>
</div>
<div id="reaction-content" class="flex flex-col gap-3 px-1 pt-6" style="overflow: auto; height: 406px;">
<%= render "admin/shared/flag_reactions_table",
vomit_reactions: user_vomit_reactions,
text_section: "users",
empty_text: t("views.admin.users.priviliged_actions.no_flags") %>
<div>

View file

@ -2,6 +2,31 @@
en:
views:
admin:
articles:
flags:
score: Score affected by a particular flag
priviliged_actions:
description: All the moderator actions affect the score. The overall score is a combination of moderator actions and public reactions.
no_flags: Article has no flags.
no_quality_reactions: Article has no quality reactions by trusted users.
title: Moderator actions
shared:
flags:
actions:
mark_as_invalid: Mark as Invalid
mark_as_valid: Mark as Valid
invalid:
title: Flag marked as invalid by admin
value: Invalid
open:
title: Open Flag
value: Open
unidentified:
title: Unidentified
value: Unidentified
valid:
title: Flag marked as valid by admin
value: Valid
users:
heading: Members
aria_paginate: Paginate users
@ -188,6 +213,8 @@ en:
comment: Comment on "%{title}"
profile: User's profile
origin_html: Flagged by %{user}
score: Article and comment score affected by particular flag
total_score: Article and comment score affected by user flags
notes:
empty1: No previous notes found for %{user}.
empty2: Any notes added for %{user} will be found here.
@ -264,6 +291,10 @@ en:
remove: "Remove tag:"
notice_html: Adding tags for moderation is currently only possible through a particular %{tag_page}, by providing the user's ID (%{user_id}).
tag_page: tag's page
priviliged_actions:
description: The user flags affect the scores of articles and comments. Each valid or open user flag further reduces the score of the article and comment.
no_flags: No flags received against this user yet.
title: Flags received
profile:
locked:
icon: Access locked

View file

@ -2,6 +2,31 @@
fr:
views:
admin:
articles:
flags:
score: Score affected by a particular flag
priviliged_actions:
description: All the moderator actions affect the score. The overall score is a combination of moderator actions and public reactions.
no_flags: Article has no flags.
no_quality_reactions: Article has no quality reactions by trusted users.
title: Moderator actions
shared:
flags:
actions:
mark_as_invalid: Mark as Invalid
mark_as_valid: Mark as Valid
invalid:
title: Flag marked as invalid by admin
value: Invalid
open:
title: Open Flag
value: Open
unidentified:
title: Unidentified
value: Unidentified
valid:
title: Flag marked as valid by admin
value: Valid
users:
heading: Members
aria_paginate: Paginate users
@ -188,6 +213,8 @@ fr:
comment: Comment on "%{title}"
profile: User's profile
origin_html: Flagged by %{user}
score: Article and comment score affected by particular flag
total_score: Article and comment score affected by user flags
notes:
empty1: No previous notes found for %{user}.
empty2: Any notes added for %{user} will be found here.
@ -264,6 +291,10 @@ fr:
remove: "Remove tag:"
notice_html: Adding tags for moderation is currently only possible through a particular %{tag_page}, by providing the user's ID (%{user_id}).
tag_page: tag's page
priviliged_actions:
description: The user flags affect the scores of articles and comments. Each valid or open user flag further reduces the score of the article and comment.
no_flags: No flags received against this user yet.
title: Flags received
profile:
locked:
icon: Access locked

View file

@ -163,22 +163,6 @@ en:
trusted: Trusted User Guide
author: Author
date: Date
flags:
actions:
mark_as_invalid: Mark as Invalid
mark_as_valid: Mark as Valid
invalid:
title: Flag marked as invalid by admin
value: Invalid
open:
title: Open Flag
value: Open
unidentified:
title: Unidentified
value: Unidentified
valid:
title: Flag marked as valid by admin
value: Valid
notice:
subtitle: '%{community} Mods'
desc1_html: We periodically award some %{community} members with heightened privileges to help moderate the community.

View file

@ -162,22 +162,6 @@ fr:
trusted: Guide de l'utilisateur de confiance
author: Auteur
date: Date
flags:
actions:
mark_as_invalid: Marquer comme non valide
mark_as_valid: Marquer comme valide
invalid:
title: Drapeau marqué comme invalide par l'administrateur
value: Invalide
open:
title: Drapeau ouvert
value: Ouvrir
unidentified:
title: Non identifié
value: Non identifié
valid:
title: Drapeau marqué comme valide par l'administrateur
value: Valide
notice:
subtitle: '%{community} Mods'
desc1_html: Nous accordons périodiquement à certains membres de la %{community} des privilèges accrus afin de contribuer à la modération de la communauté.