Skip to content
Permalink
Browse files

Generalize endorsements and apply to blog posts (#5542)

* Generalize endorsements permissions and extract from proposals.

* [WIP] Extracting Endorsable concern

* [WIP] EndorseResource and UnendorseRerouce commands.

* [WIP] EndorsementsController tests.

* Add timestamps to decidim_endorsements db migration.

* [I18N] Update yamls.

* [WIP] Extract endorsements from proposals module. But keep funcionality for proposals.

* [DOC] Update changelog upgrade notes.

* Apply edlint.js style.

* normalize i18n files.

* [TEST|I18N] Pending development translations.

* [TEST] Update factory modifications to not break other tests.

* [TEST] Extract endorse_proposal_spec logic into system_endorse_resource_examples.

* [TEST] Rename shared system example.

* [REFACTOR] Separate logic and view in endorsement_buttons_cell#render_endorsements_button_card_part.

* Get rid of Decidim::Proposals::ProposalEndorsement.

* Remove uses of proposal_endorsement.

* Remove legady proposal endorsements related exclude from i8n normalization.

* Revert "Remove legady proposal endorsements related exclude from i8n normalization."

* Update tests.

* [TEST] Update endorsement permissions tests.

* Do not remove dummy_resources.coauthorships_count column, as factories depend on that.

* Extract endorser identities partial into a generic identities cell. (#5564)

* Extract endorser identities partial into a generic identities cell. Check if some already exists.

* Remove Coauthorable to DummyResource

* Complete documentation and small refactors to extract helpers to invoke the cells.

* Update decidim-core/app/commands/decidim/endorse_resource.rb

Co-Authored-By: Txus <me@txus.io>

* Update decidim-core/app/commands/decidim/endorse_resource.rb

Co-Authored-By: Txus <me@txus.io>

* Update CHANGELOG.md

Co-Authored-By: Txus <me@txus.io>

* Rubocopify.

* Rubocopify again.

* Provide migration to automatically move ProposalEndorsements into new decidim-core Endorsement db structures.

* Update decidim-proposals/db/migrate/20200120215928_move_proposal_endorsements_to_core_endorsements.rb

Co-Authored-By: Ivan Vergés <ivan@platoniq.net>

* Fix mispellings in MoveProposalEndorsementsToCoreEndorsements migration

* Remove unneeded join clause in Proposal.newsletter_participant_ids(component)

* Allow legacy data on rollback move ProposalEndorsements to Endorsements

Allow to have rows in decidim_proposals_proposal_endorsements table when rolling back the
MoveProposalEndorsementsToCoreEndorsements. Use #find_or_create_by just in case a migrated
ProposalEndorsement is moved back to its original table.

* [Feature] Make Blog Posts endorsable (#5668)

* Fix errors

* CHANGELOG entry added

* Review when creation_date and comments should be rendered

* Refactor author cell

* Model tests added

* Remove deprecated proposal_endorsement factory.

Co-authored-by: Oliver Valls <tramuntanal@gmail.com>

* Make blog posts followable (#5690)

* Make blog posts followable

* Notify followers when creating new comment

* Add CHANGELOG entry

* Generalize follow button partial

* Edit CHANGELOG entry

* Fix proposal tests

* Update tests after merging latest changes from develop

* Update proposal_input_sort_spec when sorting by endorsement_count

* Update changelog

Co-authored-by: jarvisct <57717422+jarvisct@users.noreply.github.com>
Co-authored-by: Txus <me@txus.io>
Co-authored-by: Ivan Vergés <ivan@platoniq.net>
  • Loading branch information
4 people committed Mar 10, 2020
1 parent de58f62 commit 0af5a9a9671ac0701859699c1e89d479ba5f0888
Showing with 1,985 additions and 1,378 deletions.
  1. +10 −2 CHANGELOG.md
  2. +6 −0 decidim-blogs/app/cells/decidim/blogs/post_m_cell.rb
  3. +2 −0 decidim-blogs/app/helpers/decidim/blogs/application_helper.rb
  4. +6 −0 decidim-blogs/app/models/decidim/blogs/post.rb
  5. +29 −8 decidim-blogs/app/views/decidim/blogs/posts/show.html.erb
  6. +3 −0 decidim-blogs/config/locales/en.yml
  7. +7 −0 decidim-blogs/db/migrate/20200128094730_add_endorsements_counter_cache_to_blogs.rb
  8. +5 −0 decidim-blogs/lib/decidim/blogs/component.rb
  9. +44 −0 decidim-blogs/spec/models/decidim/blogs/post_spec.rb
  10. +13 −8 .../decidim/proposals → decidim-core/app/assets/javascripts/decidim}/identity_selector_dialog.js.es6
  11. +0 −4 decidim-core/app/cells/decidim/amendable/amend_button_card_cell.rb
  12. +6 −0 decidim-core/app/cells/decidim/author/endorsements.erb
  13. +2 −0 decidim-core/app/cells/decidim/author/show.erb
  14. +8 −12 decidim-core/app/cells/decidim/author_cell.rb
  15. +9 −0 decidim-core/app/cells/decidim/endorsement_buttons/select_identity_button.erb
  16. +4 −0 decidim-core/app/cells/decidim/endorsement_buttons/show.erb
  17. +178 −0 decidim-core/app/cells/decidim/endorsement_buttons_cell.rb
  18. 0 ...m-proposals/app/cells/decidim/proposals → decidim-core/app/cells/decidim}/endorsers_list/show.erb
  19. +29 −0 decidim-core/app/cells/decidim/endorsers_list_cell.rb
  20. +64 −0 decidim-core/app/commands/decidim/endorse_resource.rb
  21. +38 −0 decidim-core/app/commands/decidim/unendorse_resource.rb
  22. +71 −0 decidim-core/app/controllers/decidim/endorsements_controller.rb
  23. +35 −0 decidim-core/app/events/decidim/resource_endorsed_event.rb
  24. +93 −0 decidim-core/app/helpers/decidim/endorsable_helper.rb
  25. +11 −0 decidim-core/app/helpers/decidim/followable_helper.rb
  26. +30 −0 decidim-core/app/models/decidim/endorsement.rb
  27. +7 −0 decidim-core/app/permissions/decidim/permissions.rb
  28. +1 −1 ...proposals/proposal_endorsements → decidim-core/app/views/decidim/endorsements}/_identity.html.erb
  29. +2 −2 ...roposals/proposal_endorsements → decidim-core/app/views/decidim/endorsements}/identities.html.erb
  30. +20 −0 decidim-core/app/views/decidim/endorsements/update_buttons_and_counters.js.erb
  31. +24 −0 decidim-core/config/locales/en.yml
  32. +3 −0 decidim-core/config/routes.rb
  33. +13 −0 decidim-core/db/migrate/20191130151925_create_decidim_endorsements.rb
  34. +1 −0 decidim-core/lib/decidim/core.rb
  35. +2 −1 decidim-core/lib/decidim/core/engine.rb
  36. +3 −0 decidim-core/lib/decidim/core/test.rb
  37. +62 −0 decidim-core/lib/decidim/core/test/factories.rb
  38. +22 −0 decidim-core/lib/decidim/core/test/shared_examples/endorsements_controller_shared_context.rb
  39. +165 −0 decidim-core/lib/decidim/core/test/shared_examples/system_endorse_resource_examples.rb
  40. +89 −0 decidim-core/lib/decidim/core/test/shared_examples/with_endorsable_permissions_examples.rb
  41. +25 −0 decidim-core/lib/decidim/endorsable.rb
  42. +92 −0 decidim-core/spec/commands/decidim/endorse_resource_spec.rb
  43. +59 −0 decidim-core/spec/commands/decidim/unendorse_resource_spec.rb
  44. +104 −0 decidim-core/spec/controllers/decidim/endorsements_controller_as_org_spec.rb
  45. +129 −0 decidim-core/spec/controllers/decidim/endorsements_controller_as_user_spec.rb
  46. +11 −12 ...roposal_endorsed_event_spec.rb → decidim-core/spec/events/decidim/resource_endorsed_event_spec.rb
  47. +47 −0 decidim-core/spec/helpers/decidim/endorsable_helper_spec.rb
  48. +96 −0 decidim-core/spec/models/decidim/endorsement_spec.rb
  49. +2 −0 decidim-core/spec/permissions/decidim/permissions_spec.rb
  50. +6 −0 decidim-dev/config/locales/en.yml
  51. +4 −0 decidim-dev/lib/decidim/dev/test/rspec_support/component.rb
  52. +2 −2 ...m-participatory_processes/app/queries/decidim/participatory_processes/stats_participants_count.rb
  53. +0 −31 decidim-proposals/app/cells/decidim/proposals/endorsers_list_cell.rb
  54. +2 −2 decidim-proposals/app/cells/decidim/proposals/proposal_m_cell.rb
  55. +0 −59 decidim-proposals/app/commands/decidim/proposals/endorse_proposal.rb
  56. +0 −40 decidim-proposals/app/commands/decidim/proposals/unendorse_proposal.rb
  57. +1 −1 decidim-proposals/app/controllers/concerns/decidim/proposals/orderable.rb
  58. +0 −60 decidim-proposals/app/controllers/decidim/proposals/proposal_endorsements_controller.rb
  59. +2 −2 decidim-proposals/app/helpers/decidim/proposals/admin/proposal_rankings_helper.rb
  60. +2 −5 decidim-proposals/app/helpers/decidim/proposals/application_helper.rb
  61. +0 −145 decidim-proposals/app/helpers/decidim/proposals/proposal_endorsements_helper.rb
  62. +4 −14 decidim-proposals/app/models/decidim/proposals/proposal.rb
  63. +0 −37 decidim-proposals/app/models/decidim/proposals/proposal_endorsement.rb
  64. +1 −22 decidim-proposals/app/permissions/decidim/proposals/permissions.rb
  65. +15 −6 decidim-proposals/app/queries/decidim/proposals/metrics/endorsements_metric_manage.rb
  66. +5 −4 decidim-proposals/app/queries/decidim/proposals/metrics/proposal_participants_metric_measure.rb
  67. +1 −1 decidim-proposals/app/services/decidim/proposals/proposal_builder.rb
  68. +1 −1 decidim-proposals/app/types/decidim/proposals/proposal_input_sort.rb
  69. +1 −1 decidim-proposals/app/types/decidim/proposals/proposal_type.rb
  70. +1 −1 decidim-proposals/app/views/decidim/proposals/admin/proposals/show.html.erb
  71. +0 −20 ...im-proposals/app/views/decidim/proposals/proposal_endorsements/update_buttons_and_counters.js.erb
  72. +0 −11 decidim-proposals/app/views/decidim/proposals/proposals/_endorsement_button.html.erb
  73. +0 −13 decidim-proposals/app/views/decidim/proposals/proposals/_endorsement_identities_cabin.html.erb
  74. +0 −16 decidim-proposals/app/views/decidim/proposals/proposals/_endorsements_card_row.html.erb
  75. +13 −2 decidim-proposals/app/views/decidim/proposals/proposals/show.html.erb
  76. +1 −26 decidim-proposals/config/locales/en.yml
  77. +4 −0 decidim-proposals/db/migrate/20181003074440_fix_user_groups_ids_in_proposals_endorsements.rb
  78. +7 −0 decidim-proposals/db/migrate/20191206154128_add_endorsements_counter_cache_to_proposals.rb
  79. +52 −0 decidim-proposals/db/migrate/20200120215928_move_proposal_endorsements_to_core_endorsements.rb
  80. +2 −2 decidim-proposals/lib/decidim/proposals/component.rb
  81. +1 −5 decidim-proposals/lib/decidim/proposals/engine.rb
  82. +3 −12 decidim-proposals/lib/decidim/proposals/test/factories.rb
  83. +0 −94 decidim-proposals/spec/commands/decidim/proposals/endorse_proposal_spec.rb
  84. +0 −61 decidim-proposals/spec/commands/decidim/proposals/unendorse_proposal_spec.rb
  85. +0 −106 decidim-proposals/spec/controllers/decidim/proposal_endorsements_controller_as_org_spec.rb
  86. +0 −139 decidim-proposals/spec/controllers/decidim/proposal_endorsements_controller_as_user_spec.rb
  87. +1 −1 decidim-proposals/spec/forms/decidim/proposals/admin/proposals_merge_form_spec.rb
  88. +1 −1 decidim-proposals/spec/forms/decidim/proposals/admin/proposals_split_form_spec.rb
  89. +0 −29 decidim-proposals/spec/helpers/proposal_endorsements_helper_spec.rb
  90. +9 −3 decidim-proposals/spec/lib/decidim/proposals/component_spec.rb
  91. +0 −104 decidim-proposals/spec/models/decidim/proposals/proposal_endorsement_spec.rb
  92. +2 −2 decidim-proposals/spec/models/decidim/proposals/proposal_spec.rb
  93. +0 −65 decidim-proposals/spec/permissions/decidim/proposals/permissions_spec.rb
  94. +10 −2 decidim-proposals/spec/queries/decidim/proposals/metrics/endorsements_metric_manage_spec.rb
  95. +5 −1 ...dim-proposals/spec/queries/decidim/proposals/metrics/proposal_participants_metric_measure_spec.rb
  96. +11 −3 decidim-proposals/spec/shared/view_proposal_details_from_admin_examples.rb
  97. +11 −170 decidim-proposals/spec/system/endorse_proposal_spec.rb
  98. +5 −1 decidim-proposals/spec/system/proposals_spec.rb
  99. +6 −5 decidim-proposals/spec/types/proposal_input_sort_spec.rb
  100. +111 −0 docs/advanced/endorsable.md
@@ -4,17 +4,25 @@

### Upgrade notes

- **Endorsements**

This new version of Decidim has extracted the Endorsement feature into a generic concern that can now be applied to many resources.
To keep current Decidim::Proposals::Proposal's endorsement information, endorsements are copied into the new `Decidim::Endorsable` tables and counter cache columns. This is done via migrations.

After this, `Decidim::Proposals::ProposalEndorsement` and the corresponding counter cache column in `decidim_proposals_proposal.proposal_endorsements_count` should be removed. To do so, Decidim will provide the corresponding migration in the next release.

### Added

- **decidim-core**: Now messages inside conversations have their urls identified as links. [\#5755](https://github.com/decidim/decidim/pull/5755)
- **decidim-core**: Support node.js semver rules for release candidates. [\#5828](https://github.com/decidim/decidim/pull/5828)
- **decidim-proposals**, **decidim-core**, **decidim-blogs**: Extract proposals' endorsements into a polymorphic concern that can now be applied no any resource. It has, in turn, been aplied to blog posts. [\#5542](https://github.com/decidim/decidim/pull/5542)

### Changed

### Fixed

- **decidim-proposals**: Use simple_format to add a wrapper to proposals body [#5753](https://github.com/decidim/decidim/pull/5753)
- **decidim-sortitions**: Fix incorrect proposals sortition. [\5620](https://github.com/decidim/decidim/pull/5620)
- **decidim-proposals**: Use simple_format to add a wrapper to proposals body [\#5753](https://github.com/decidim/decidim/pull/5753)
- **decidim-sortitions**: Fix incorrect proposals sortition. [\#5620](https://github.com/decidim/decidim/pull/5620)
- **decidim-admin**: Fix: let components without step settings be added [\#5568](https://github.com/decidim/decidim/pull/5568)
- **decidim-proposals**: Fix proposals that have their state not published [\#5832](https://github.com/decidim/decidim/pull/5832)

@@ -10,6 +10,12 @@ class PostMCell < Decidim::CardMCell
def has_actions?
false
end

def endorsements_count
with_tooltip t("decidim.endorsable.endorsements") do
icon("bullhorn", class: "icon--small") + " " + model.endorsements_count.to_s
end
end
end
end
end
@@ -8,6 +8,8 @@ module ApplicationHelper
include PaginateHelper
include SanitizeHelper
include Decidim::Blogs::PostsHelper
include ::Decidim::EndorsableHelper
include ::Decidim::FollowableHelper
include Decidim::Comments::CommentsHelper
end
end
@@ -12,6 +12,8 @@ class Post < Blogs::ApplicationRecord
include Decidim::Authorable
include Decidim::Comments::Commentable
include Decidim::Searchable
include Decidim::Endorsable
include Decidim::Followable
include Traceable
include Loggable

@@ -56,6 +58,10 @@ def official?
def user_allowed_to_comment?(user)
can_participate_in_space?(user)
end

def users_to_notify_on_comment_created
followers
end
end
end
end
@@ -14,18 +14,39 @@
blogpost: post
)
%>

<div class="row column view-header">
<h2 class="heading2"><%= translated_attribute post.title %></h2>
<%= cell "decidim/author", present(post.author), from: post %>
</div>
<div class="row">
<div class="columns medium-7 mediumlarge-8">
<div class="row column view-header">
<h2 class="heading2"><%= translated_attribute post.title %></h2>
<%= cell "decidim/author", present(post.author), from: post %>
</div>
<% if show_endorsements_card? %>
<div class="columns section view-side mediumlarge-4 mediumlarge-push-8 large-3 large-push-9">
<div class="card">
<div class="card__content">
<div class="row collapse buttons__row">
<% if endorsements_enabled? %>
<div class="column small-9 collapse">
<%= endorsement_buttons_cell(post) %>
</div>
<% end %>
<div class="column collapse <%= endorsements_enabled? ? "small-3" : "" %>">
<%= link_to "#comments", class: "button small compact hollow secondary button--nomargin expanded" do %>
<%= icon "comment-square", class: "icon--small", aria_label: t(".comments"), role: "img" %> <%= post.comments.count %>
<% end %>
</div>
</div>
<br>
<%= follow_button_for(post) %>
</div>
</div>
</div>
<% end %>
<div class="columns mediumlarge-8 mediumlarge-pull-4">
<div class="section">
<p><%= decidim_sanitize translated_attribute post.body %></p>
</div>
</div>
<div id="most-commented" class="columns medium-5 mediumlarge-4 large-4">
<%= render partial: "sidebar_blog", locals: { posts: posts_most_commented } %>
<%= cell "decidim/endorsers_list", post %>
</div>
</div>
<%= attachments_for post %>
@@ -49,6 +49,7 @@ en:
title: title
posts:
show:
comments: Comments
view: View
sidebar_blog:
comments: comments
@@ -64,6 +65,8 @@ en:
step:
announcement: Announcement
comments_blocked: Comments blocked
endorsements_blocked: Endorsements blocked
endorsements_enabled: Endorsements enabled
events:
blogs:
post_created:
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class AddEndorsementsCounterCacheToBlogs < ActiveRecord::Migration[5.2]
def change
add_column :decidim_blogs_posts, :endorsements_count, :integer, null: false, default: 0
end
end
@@ -18,6 +18,8 @@
Decidim::Blogs::Post.where(component: components).count
end

component.actions = %w(endorse vote create withdraw amend)

component.settings(:global) do |settings|
settings.attribute :announcement, type: :text, translated: true, editor: true
settings.attribute :comments_enabled, type: :boolean, default: true
@@ -26,11 +28,14 @@
component.settings(:step) do |settings|
settings.attribute :announcement, type: :text, translated: true, editor: true
settings.attribute :comments_blocked, type: :boolean, default: false
settings.attribute :endorsements_enabled, type: :boolean, default: true
settings.attribute :endorsements_blocked, type: :boolean
end

component.register_resource(:blogpost) do |resource|
resource.model_class_name = "Decidim::Blogs::Post"
resource.card = "decidim/blogs/post"
resource.actions = %w(endorse vote amend)
resource.searchable = true
end

@@ -39,5 +39,49 @@ module Decidim::Blogs
it "has an associated component" do
expect(post.component).to be_a(Decidim::Component)
end

describe "#endorsed_by?" do
let(:user) { create(:user, organization: subject.organization) }

context "with User endorsement" do
it "returns false if the post is not endorsed by the given user" do
expect(subject).not_to be_endorsed_by(user)
end

it "returns true if the post is not endorsed by the given user" do
create(:endorsement, resource: subject, author: user)
expect(subject).to be_endorsed_by(user)
end
end

context "with Organization endorsement" do
let!(:user_group) { create(:user_group, verified_at: Time.current, organization: user.organization) }
let!(:membership) { create(:user_group_membership, user: user, user_group: user_group) }

before { user_group.reload }

it "returns false if the post is not endorsed by the given organization" do
expect(subject).not_to be_endorsed_by(user, user_group)
end

it "returns true if the post is not endorsed by the given organization" do
create(:endorsement, resource: subject, author: user, user_group: user_group)
expect(subject).to be_endorsed_by(user, user_group)
end
end
end

describe "#users_to_notify_on_comment_created" do
let!(:follows) { create_list(:follow, 3, followable: subject) }
let(:followers) { follows.map(&:user) }
let(:participatory_space) { subject.component.participatory_space }
let(:organization) { participatory_space.organization }

context "when creating new comment" do
it "returns the followers" do
expect(subject.users_to_notify_on_comment_created).to match_array(followers)
end
end
end
end
end
@@ -6,14 +6,14 @@
$(document).ready(function () {

let button = $("#select-identity-button"),
refreshUrl = null,
identitiesUrl = null,
userIdentitiesDialog = $("#user-identities");

if (userIdentitiesDialog.length) {
refreshUrl = userIdentitiesDialog.data("refresh-url");
identitiesUrl = userIdentitiesDialog.data("reveal-identities-url");

button.click(function () {
$.ajax(refreshUrl).done(function(response) {
$.ajax(identitiesUrl).done(function(response) {
userIdentitiesDialog.html(response).foundation("open");
button.trigger("ajax:success")
});
@@ -32,21 +32,26 @@ $(document).ready(function () {
$("#user-identities ul.reveal__list li").each(function(index, elem) {
let liTag = $(elem)
liTag.on("click", function() {
let method = liTag.data("method")
let url = liTag.data("url")
let method = liTag.data("method"),
urlDataAttr = null;
if (method === "POST") {
urlDataAttr = "create_url";
} else {
urlDataAttr = "destroy_url";
}
$.ajax({
url: url,
url: liTag.data(urlDataAttr),
method: method,
dataType: "script",
success: function() {
if (liTag.hasClass("selected")) {
liTag.removeClass("selected")
liTag.find(".icon--circle-check").addClass("invisible")
liTag.data("method", "post")
liTag.data("method", "POST")
} else {
liTag.addClass("selected")
liTag.find(".icon--circle-check").removeClass("invisible")
liTag.data("method", "delete")
liTag.data("method", "DELETE")
}
}
})
@@ -31,10 +31,6 @@ def new_amend_help_text
end
end

def decidim
Decidim::Core::Engine.routes.url_helpers
end

def button_classes
"amend_button_card_cell button secondary hollow expanded button--icon button--sc"
end
@@ -0,0 +1,6 @@
<% if endorsable? %>
<%= link_to "#{resource_locator(from_context).path}#list-of-endorsements", title: t("decidim.author.endorsements",count: from_context.endorsements_count) do %>
<%= icon "bullhorn", class: "icon--small", aria_label: t("decidim.author.endorsements", count: from_context.endorsements_count), role: "img" %>
<%= from_context.endorsements_count %> <%= t("decidim.author.endorsements", count: from_context.endorsements_count) %>
<% end %>
<% end %>
@@ -9,6 +9,8 @@

<%= render :comments %>

<%= render :endorsements %>

<%= render :flag %>

<%= render :withdraw %>
@@ -55,28 +55,25 @@ def withdraw_path
end

def creation_date?
return true if posts_controller?
return unless from_context
return unless proposals_controller? || collaborative_drafts_controller?
return unless show_action?
return unless show_action? && (from_context.respond_to?(:published_at) || from_context.respond_to?(:created_at))

true
end

def creation_date
date_at = if proposals_controller?
from_context.published_at
else
from_context.created_at
end
date_at = from_context.try(:published_at) || from_context.try(:created_at)

l date_at, format: :decidim_short
end

def commentable?
return unless posts_controller?
from_context && from_context.class.include?(Decidim::Comments::Commentable)
end

true
def endorsable?
from_context && from_context.class.include?(Decidim::Endorsable)
end

def author_classes
@@ -85,13 +82,12 @@ def author_classes

def actionable?
return false if options[:has_actions] == false
return true if user_author? && posts_controller?

true if withdrawable? || flagable?
(user_author? && posts_controller?) || withdrawable? || flagable?
end

def user_author?
true if "Decidim::UserPresenter".include? model.class.to_s
"Decidim::UserPresenter".include?(model.class.to_s)
end

def profile_path?
@@ -0,0 +1,9 @@
<%= javascript_include_tag "decidim/identity_selector_dialog" %>
<button id="select-identity-button" type="button" name="button" class="button small compact light button--sc secondary <%= fully_endorsed?(resource, current_user) && "success" %>">
<%= endorse_translated %>
</button>

<div class="reveal collapse"
id="user-identities"
data-reveal data-reveal-identities-url="<%= reveal_identities_url %>">
</div>
@@ -0,0 +1,4 @@
<div class="button-group button-group--collapse button--nomargin small">
<%= render_endorsements_count %>
<%= render_endorsements_button %>
</div>

0 comments on commit 0af5a9a

Please sign in to comment.
You can’t perform that action at this time.