* Add SiteConfig.feed_pinned_article and validation * Display pinned article at the top of feed * Add (basic) functionality to pin/unpin post * Admins can pin other users posts as well * Hide the button if looking at the non pinned post * Add pinned/unpinned snackbar message * Rename SiteConfig usage to Settings::General * Add pinned article to the Admin articles index * Show the pin post button when there's no pinned article * Move pinning to a separate controller * Fix SiteConfig reference * Hide PinController actions to unauthorized users * PinnedArticlesController#show action and refactor some of the code * Add Modal interaction * Fix modal-pinned checkbox interaction * Fixed pin/unpin post * Add ArticleDecorator#pinned? specs * Add PinnedArticlePolicy and PinnedArticlesController specs * Add ability to actually pin an article from the admin after submit * Add partial Cypress pin/unpin spec * Fix pinned article and add basic Cypress interaction tests * Add Crayons styling to modal * Only render the pinned article on the default Feed page * Use persisted? * Add some comments * Update app/javascript/articles/Article.jsx Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Update app/javascript/packs/homePageFeed.jsx Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Fix Cypress tests * Update app/javascript/admin/controllers/article_controller.js Co-authored-by: Ridhwana <Ridhwana.Khan16@gmail.com> * Fix pinning in article show page * Used PinnedArticle domain model * Fix spec * Update cypress/integration/adminFlows/articles/pinArticle.spec.js Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Update cypress/integration/adminFlows/articles/pinArticle.spec.js Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Update cypress/integration/adminFlows/articles/pinArticle.spec.js Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Update cypress/integration/adminFlows/articles/pinArticle.spec.js Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Update cypress/integration/adminFlows/articles/pinArticle.spec.js Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Update cypress/integration/articleFlows/pinArticle.spec.js Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Update cypress/integration/articleFlows/pinArticle.spec.js Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Update app/views/admin/articles/index.html.erb Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Fix merge woes * Add missing article pin post flows * Add missing admin article flows * Add Unpin to Admin as well * Add Audit::Log entries for pin/unpin actions * Update app/controllers/stories/feeds_controller.rb Co-authored-by: Michael Kohl <citizen428@dev.to> * Do not rate limit in E2E tests * Use .find instead of .filter * Rename ArticleIdValidator to ExistingArticleIdValidator * Treat draft and deleted articles the same * Make sure posts can be pinned after the pinned article is unpublished or deleted * Use .get directly * Fix spec and fix PinnedArticlesController#show * Strengthen pinArticle Cypress tests * Add Cypress test heading guard * Add another Cypress test heading guard * Remove duplicate validator * Try using the Tools: header instead of the article title Co-authored-by: Suzanne Aitchison <suzanne@forem.com> Co-authored-by: Ridhwana <Ridhwana.Khan16@gmail.com> Co-authored-by: Michael Kohl <citizen428@dev.to>
182 lines
7.5 KiB
Text
182 lines
7.5 KiB
Text
<style>
|
|
.article-body-html img {
|
|
max-width: 100%;
|
|
}
|
|
</style>
|
|
|
|
<% decorated_article = article.decorate %>
|
|
|
|
<div class="card my-3"
|
|
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">
|
|
|
|
<div class="card-header">
|
|
<h2>
|
|
<a href="<%= article.path %>" target="_blank" rel="noopener">
|
|
<%= article.title %>
|
|
</a>
|
|
</h2>
|
|
|
|
<div>
|
|
<a href="<%= article.path %>/edit" target="_blank" rel="noopener"><span class="btn btn-sm btn-primary">Edit</span></a>
|
|
|
|
<button class="btn btn-sm btn-success" data-action="article#increaseFeaturedNumber">Boost</button>
|
|
|
|
<a class="btn btn-sm btn-secondary" href="<%= admin_user_path(article.user_id) %>" target="_blank" rel="noopener">Manage User</a>
|
|
|
|
<% if decorated_article.pinned? %>
|
|
<%= link_to(
|
|
"Unpin Post",
|
|
unpin_admin_article_path(decorated_article.id),
|
|
method: :delete,
|
|
remote: true,
|
|
class: "btn btn-sm btn-secondary",
|
|
data: { "article-target": "unpinButton" },
|
|
) %>
|
|
<% end %>
|
|
|
|
<a href="<%= admin_user_path(article.user_id) %>" class="badge badge-light">@<%= article.user&.username %></a>
|
|
|
|
<span class="badge badge-light">❤️ <%= article.public_reactions_count %> 💬 <%= article.comments_count %></span>
|
|
|
|
<span class="badge badge-light">
|
|
<% 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 %>
|
|
</span>
|
|
|
|
<% decorated_article.cached_tag_list_array.each do |tag| %>
|
|
<a class="badge badge-secondary" href='<%= tag_path(tag) %>'>#<%= tag %></a>
|
|
<% end %>
|
|
</div>
|
|
</div>
|
|
|
|
<% featured = article.featured ? "bg-featured" : "" %>
|
|
<% approved = article.approved ? "bg-approved" : featured %>
|
|
<% pinned = decorated_article.pinned? ? "bg-pinned" : approved %>
|
|
<% background_color = pinned %>
|
|
|
|
<div
|
|
data-article-target="cardBody"
|
|
class="card-body <%= background_color %> <%= "bg-danger" if !article.published? && article.published_from_feed? %>">
|
|
|
|
<% if article.video %>
|
|
<h2>Contains Video</h2>
|
|
<% end %>
|
|
|
|
<% cache "admin-user-info-#{article.user_id}-#{article.user&.updated_at}", expires_in: 4.hours do %>
|
|
|
|
<% if article.user&.warned %>
|
|
<h2><span class="badge badge-warning">USER WARNED</span></h2>
|
|
<% end %>
|
|
|
|
<% if article.user&.notes&.any? %>
|
|
<h2>User Notes (<%= article.user&.notes&.size %> total)</h2>
|
|
<% article.user&.notes&.last(3)&.each do |note| %>
|
|
<p>
|
|
<em><%= note.created_at.strftime("%d %B %Y %H:%M UTC") %> by
|
|
<%= note.author_id ? User.find(note.author_id).username : "No Author" %></em> - <%= note.content %>
|
|
</p>
|
|
<% end %>
|
|
<p><a href="<%= admin_user_path(article.user_id) %>">View All</a></p>
|
|
<% end %>
|
|
<% end %>
|
|
<% if article.main_image.present? %>
|
|
<div style="max-height:450px;overflow:hidden;">
|
|
<img src="<%= cloud_cover_url(article.main_image) %>" style="width:100%;max-width:380px;border-radius:8px;background:<%= article.main_image_background_hex_color %>;" alt="cover image" loading="lazy" />
|
|
<br />
|
|
</div>
|
|
<% end %>
|
|
<%= form_with url: admin_article_path(article.id), model: article, local: true do |f| %>
|
|
<div class="form-group">
|
|
<input name="utf8" type="hidden" value="✓">
|
|
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>" />
|
|
<input type="hidden" name="_method" value="patch" />
|
|
</div>
|
|
<div class="row">
|
|
<div class="form-group col">
|
|
<label for="featured_number_<%= article.id %>">Featured Number:</label>
|
|
<input id="featured_number_<%= article.id %>"
|
|
class="form-control"
|
|
name="article[featured_number]"
|
|
value="<%= article.featured_number %>"
|
|
data-article-target="featuredNumber">
|
|
</div>
|
|
<div class="form-group col">
|
|
<label for="author_id_<%= article.id %>">Author ID:</label>
|
|
<input id="author_id_<%= article.id %>" class="form-control" size="6" name="article[user_id]"
|
|
value="<%= article.user_id %>">
|
|
</div>
|
|
<div class="form-group col">
|
|
<label for="co_author_ids_list_<%= article.id %>">Co-Author IDs (comma separated):</label>
|
|
<input id="co_author_ids_list_<%= article.id %>" class="form-control" size="6" name="article[co_author_ids_list]"
|
|
value="<%= article.co_author_ids&.join(", ") %>">
|
|
</div>
|
|
</div>
|
|
<% if article.published? %>
|
|
<div class="row">
|
|
<div class="form-group col">
|
|
<label for="published_at_<%= article.id %>">Published at:</label>
|
|
<%= f.datetime_select :published_at, required: true, include_blank: true, include_seconds: true, class: "form-control" %> UTC
|
|
</div>
|
|
</div>
|
|
<% end %>
|
|
<div class="row">
|
|
<div class="form-check col">
|
|
<%= f.check_box :featured, id: "featured-#{article.id}" %>
|
|
<label for="featured-<%= article.id %>">Featured</label>
|
|
</div>
|
|
<div class="form-check col">
|
|
<%= f.check_box :approved, id: "approved-#{article.id}" %>
|
|
<label for="approved-<%= article.id %>">Approved</label>
|
|
</div>
|
|
<div class="form-check col">
|
|
<%= f.check_box :boosted_additional_articles, id: "boosted_additional_articles-#{article.id}" %>
|
|
<label for="boosted_additional_articles-<%= article.id %>">Boosted</label>
|
|
</div>
|
|
<div class="form-check col">
|
|
<%= check_box_tag :pinned, "1", decorated_article.pinned?,
|
|
name: "article[pinned]",
|
|
id: "pinned-#{article.id}",
|
|
data: {
|
|
"article-target": "pinnedCheckbox",
|
|
"article-pinned-modal-target": "pinnedCheckbox",
|
|
action: "click->article#togglePin"
|
|
} %>
|
|
<label for="pinned-<%= article.id %>">Pinned</label>
|
|
</div>
|
|
<div class="col">
|
|
<button class="btn btn-primary float-right">Submit</button>
|
|
</div>
|
|
</div>
|
|
<% end %>
|
|
|
|
<% if article.boosted_dev_digest_email %>
|
|
<br /><br />
|
|
<% phrase = "#{article.path}?booster_org=#{article.organization&.slug || 'generic'}" %>
|
|
<b>BOOSTED IN <%= EmailMessage.where("subject LIKE ?", "%#{phrase}%").where.not(opened_at: nil).size %> EMAILS</b>
|
|
<br />
|
|
<b>BOOSTED IN
|
|
<%= EmailMessage.where("subject LIKE ?", "%#{phrase}%").where.not(opened_at: nil).where("sent_at > ?", 1.week.ago).size %>
|
|
EMAILS IN THE PAST WEEK</b>
|
|
<br /><br />
|
|
<b>BOOSTED IN
|
|
<%= EmailMessage.where("subject LIKE ?", "%#{phrase}%").where.not(opened_at: nil).where.not(opened_at: nil).size %>
|
|
OPENED EMAILS</b>
|
|
<br />
|
|
<b>BOOSTED IN
|
|
<%= EmailMessage.where("subject LIKE ?", "%#{phrase}%").where.not(opened_at: nil).where.not(opened_at: nil).where("sent_at > ?", 1.week.ago).size %>
|
|
OPENED EMAILS IN THE PAST WEEK</b>
|
|
<% end %>
|
|
</div>
|
|
</div>
|