Refactor and tests.

This commit is contained in:
taitus
2017-08-10 15:02:02 +02:00
parent 555c47e012
commit 4539c5fa00
36 changed files with 1023 additions and 110 deletions

View File

@@ -22,4 +22,8 @@ class CommunitiesController < ApplicationController
def load_topics def load_topics
@topics = @community.topics.send("sort_by_#{@order}").page(params[:page]) @topics = @community.topics.send("sort_by_#{@order}").page(params[:page])
end end
def load_participants
@participants = @community.participants
end
end end

View File

@@ -1,7 +1,9 @@
class TopicsController < ApplicationController class TopicsController < ApplicationController
include CommentableActions include CommentableActions
include FlagActions
before_action :set_community before_action :load_community
before_action :load_topic, only: [:show, :edit, :update]
has_orders %w{most_voted newest oldest}, only: :show has_orders %w{most_voted newest oldest}, only: :show
@@ -13,7 +15,6 @@ class TopicsController < ApplicationController
def create def create
@topic = Topic.new(topic_params.merge(author: current_user, community_id: params[:community_id])) @topic = Topic.new(topic_params.merge(author: current_user, community_id: params[:community_id]))
if @topic.save if @topic.save
redirect_to community_path(@community), notice: I18n.t('flash.actions.create.topic') redirect_to community_path(@community), notice: I18n.t('flash.actions.create.topic')
else else
@@ -22,20 +23,17 @@ class TopicsController < ApplicationController
end end
def show def show
@topic = Topic.find(params[:id])
@commentable = @topic @commentable = @topic
@comment_tree = CommentTree.new(@commentable, params[:page], @current_order) @comment_tree = CommentTree.new(@commentable, params[:page], @current_order)
set_comment_flags(@comment_tree.comments) set_comment_flags(@comment_tree.comments)
end end
def edit def edit
@topic = Topic.find(params[:id])
end end
def update def update
@topic = Topic.find(params[:id])
if @topic.update(topic_params) if @topic.update(topic_params)
redirect_to community_path(@community), notice: t('topic.update.notice') redirect_to community_path(@community), notice: t('flash.actions.update.topic')
else else
render :edit render :edit
end end
@@ -44,10 +42,14 @@ class TopicsController < ApplicationController
private private
def topic_params def topic_params
params.require(:topic).permit(:title, :community_id, :description_as_comment) params.require(:topic).permit(:title, :description)
end end
def set_community def load_community
@community = Community.find(params[:community_id]) @community = Community.find(params[:community_id])
end end
def load_topic
@topic = Topic.find(params[:id])
end
end end

View File

@@ -42,7 +42,7 @@ module CommentsHelper
def commentable_path(comment) def commentable_path(comment)
commentable = comment.commentable commentable = comment.commentable
case comment.commentable_type case comment.commentable_type
when "Budget::Investment" when "Budget::Investment"
budget_investment_path(commentable.budget_id, commentable) budget_investment_path(commentable.budget_id, commentable)
@@ -50,6 +50,8 @@ module CommentsHelper
legislation_process_question_path(commentable.process, commentable) legislation_process_question_path(commentable.process, commentable)
when "Legislation::Annotation" when "Legislation::Annotation"
legislation_process_draft_version_annotation_path(commentable.draft_version.process, commentable.draft_version, commentable) legislation_process_draft_version_annotation_path(commentable.draft_version.process, commentable.draft_version, commentable)
when "Topic"
community_topic_path(comment.commentable.community, comment.commentable)
else else
commentable commentable
end end

View File

@@ -0,0 +1,11 @@
module TopicsHelper
def disabled_create_topic
"disabled" unless current_user
end
def disabled_info_title
t("community.show.sidebar.disabled_info_title") unless current_user
end
end

View File

@@ -33,7 +33,7 @@ module Abilities
can :unmark_featured, Debate can :unmark_featured, Debate
can :comment_as_administrator, [Debate, Comment, Proposal, Poll::Question, Budget::Investment, can :comment_as_administrator, [Debate, Comment, Proposal, Poll::Question, Budget::Investment,
Legislation::Question, Legislation::Annotation] Legislation::Question, Legislation::Annotation, Topic]
can [:search, :create, :index, :destroy], ::Administrator can [:search, :create, :index, :destroy], ::Administrator
can [:search, :create, :index, :destroy], ::Moderator can [:search, :create, :index, :destroy], ::Moderator

View File

@@ -6,7 +6,7 @@ module Abilities
merge Abilities::Moderation.new(user) merge Abilities::Moderation.new(user)
can :comment_as_moderator, [Debate, Comment, Proposal, Budget::Investment, Poll::Question, can :comment_as_moderator, [Debate, Comment, Proposal, Budget::Investment, Poll::Question,
Legislation::Question, Legislation::Annotation] Legislation::Question, Legislation::Annotation, Topic]
end end
end end
end end

View File

@@ -3,4 +3,7 @@ class Community < ActiveRecord::Base
has_one :investment has_one :investment
has_many :topics has_many :topics
def participants
User.community_participants(self)
end
end end

View File

@@ -1,4 +1,6 @@
class Topic < ActiveRecord::Base class Topic < ActiveRecord::Base
include Flaggable
acts_as_paranoid column: :hidden_at acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases include ActsAsParanoidAliases
@@ -7,15 +9,12 @@ class Topic < ActiveRecord::Base
has_many :comments, as: :commentable has_many :comments, as: :commentable
after_create :associate_comment validates :title, presence: true
validates :description, presence: true
validates :author, presence: true
scope :sort_by_newest, -> { order(created_at: :desc) } scope :sort_by_newest, -> { order(created_at: :desc) }
scope :sort_by_oldest, -> { order(created_at: :asc) } scope :sort_by_oldest, -> { order(created_at: :asc) }
scope :sort_by_most_commented, -> { reorder(comments_count: :desc) } scope :sort_by_most_commented, -> { reorder(comments_count: :desc) }
private
def associate_comment
Comment.create(commentable: self, user: self.author, body: self.description_as_comment)
end
end end

View File

@@ -315,7 +315,13 @@ class User < ActiveRecord::Base
def self.community_participants(community) def self.community_participants(community)
topics_ids = community.topics.pluck(:id) topics_ids = community.topics.pluck(:id)
User.joins(:comments).where("comments.commentable_id IN (?) and comments.commentable_type = 'Topic'", topics_ids) users_who_commented = User.joins(:comments).where("comments.commentable_id IN (?) and comments.commentable_type = 'Topic'", topics_ids).uniq
author_ids = community.topics.pluck(:author_id)
users_who_authors = User.where("users.id IN (?)", author_ids)
users_participants = users_who_commented + users_who_authors
users_participants.uniq
end end
private private

View File

@@ -11,4 +11,4 @@
<%= render @comment %> <%= render @comment %>
</div> </div>
</div> </div>
</section> </section>

View File

@@ -4,7 +4,7 @@
<div class="small-12 column"> <div class="small-12 column">
<h2><%= t("community.show.title") %></h2> <h2><%= t("community.show.title") %></h2>
<p class="lead"> <%= @community.proposal.title %> </p> <p class="lead"> <%= @community.proposal.title %> </p>
<p><%= t("community.show.description") %></p> <p><%= t("community.show.description") %> </p>
</div> </div>
</div> </div>
</div> </div>
@@ -13,8 +13,8 @@
<aside class="show-for-small-only small-12 column"> <aside class="show-for-small-only small-12 column">
<div class="sidebar-divider"></div> <div class="sidebar-divider"></div>
<h2><%= t("community.sidebar.topic.title") %></h2> <h2><%= t("community.show.sidebar.participate") %></h2>
<%= link_to t("community.sidebar.topic.new_topic"), new_community_topic_path(@community.id), class: 'button expanded' %> <%= link_to t("community.show.sidebar.new_topic"), new_community_topic_path(@community.id), class: "button expanded #{disabled_create_topic}" %>
</aside> </aside>
<div class="small-12 medium-9 column"> <div class="small-12 medium-9 column">
@@ -26,8 +26,8 @@
<aside class="small-12 medium-3 hide-for-small-only column"> <aside class="small-12 medium-3 hide-for-small-only column">
<div class="sidebar-divider"></div> <div class="sidebar-divider"></div>
<h2><%= t("community.sidebar.topic.title") %></h2> <h2><%= t("community.show.sidebar.participate") %></h2>
<%= link_to t("community.sidebar.topic.new_topic"), new_community_topic_path(@community.id), class: 'button expanded' %> <%= link_to t("community.show.sidebar.new_topic"), new_community_topic_path(@community.id), class: "button expanded #{disabled_create_topic}", title: "#{disabled_info_title}" %>
</aside> </aside>
</div> </div>
</div> </div>
@@ -35,9 +35,9 @@
<div class="row column communities-participant"> <div class="row column communities-participant">
<ul class="tabs" data-tabs id="communities-show-tabs"> <ul class="tabs" data-tabs id="communities-show-tabs">
<li class="tabs-title is-active"> <li class="tabs-title is-active">
<%= link_to "#tab-participantes" do %> <%= link_to "#tab-participants" do %>
<h3> <h3>
<%= t("community.tab.participant") %> <%= t("community.show.tab.participants") %>
<span class="js-comments-count">(<%= @participants.count %>)</span> <span class="js-comments-count">(<%= @participants.count %>)</span>
</h3> </h3>
<% end %> <% end %>

View File

@@ -3,9 +3,8 @@
<div id="comments" class="small-12 column"> <div id="comments" class="small-12 column">
<%= render 'shared/wide_order_selector', i18n_namespace: "comments" %> <%= render 'shared/wide_order_selector', i18n_namespace: "comments" %>
<!-- <p class="first-comment"><%= @topic.description_as_comment %></p> -->
<% @comment_tree.root_comments.each do |comment| %>
<% @comment_tree.root_comments.each do |comment| %>
<%= render 'comments/comment', comment: comment %> <%= render 'comments/comment', comment: comment %>
<% end %> <% end %>

View File

@@ -2,11 +2,15 @@
<%= render 'shared/errors', resource: @topic %> <%= render 'shared/errors', resource: @topic %>
<div class="row"> <%= render 'shared/errors', resource: @topic %>
<div class="small-12 column">
<%= f.label :title, t("community.topic.form.topic_title") %> <div class="row">
<%= f.text_field :title, label: false %> <div class="small-12 column">
</div> <%= f.label :title, t("community.topic.form.topic_title") %>
<%= f.text_field :title, label: false %>
<%= f.label :description, t("community.topic.form.topic_description") %>
<%= f.text_area :description, label: false %>
</div>
<div class="actions small-12 column"> <div class="actions small-12 column">
<%= f.submit(class: "button", value: t("community.topic.form.#{action_name}.submit_button")) %> <%= f.submit(class: "button", value: t("community.topic.form.#{action_name}.submit_button")) %>

View File

@@ -0,0 +1,9 @@
<div class="small-12 medium-3 column">
<span class="icon-proposals float-right"></span>
<h2><%= t("community.topic.sidebar.recommendations_title") %></h2>
<ul class="recommendations">
<li><%= t("community.topic.sidebar.recommendation_one") %></li>
<li><%= t("community.topic.sidebar.recommendation_two") %></li>
<li><%= t("community.topic.sidebar.recommendation_three") %></li>
</ul>
</div>

View File

@@ -11,7 +11,7 @@
<h3><%= link_to topic.title, community_topic_path(@community, topic) %></h3> <h3><%= link_to topic.title, community_topic_path(@community, topic) %></h3>
<p class="topic-info"> <p class="topic-info">
<span class="icon-comments"></span>&nbsp; <span class="icon-comments"></span>&nbsp;
<%= link_to t("proposals.proposal.comments", count: topic.comments_count), community_topic_path(@community, topic, anchor: "comments") %> <%= link_to t("community.show.topic.comments", count: topic.comments_count), community_topic_path(@community, topic, anchor: "comments") %>
<span class="bullet">&nbsp;&bull;&nbsp;</span> <span class="bullet">&nbsp;&bull;&nbsp;</span>
<%= I18n.l topic.created_at.to_date %> <%= I18n.l topic.created_at.to_date %>
<span class="bullet">&nbsp;&bull;&nbsp;</span> <span class="bullet">&nbsp;&bull;&nbsp;</span>
@@ -20,11 +20,11 @@
</div> </div>
<div class="small-2 column text-right"> <div class="small-2 column text-right">
<% if topic.author == current_user %> <% if topic.author == current_user %>
<%= link_to t("community.topic.edit_button"), edit_community_topic_path(@community.id, topic), class: 'button small hollow' %> <%= link_to t("community.show.topic.edit_button"), edit_community_topic_path(@community.id, topic), class: 'button small hollow' %>
<% end %> <% end %>
</div> </div>
</div> </div>
<% end %> <% end %>
<% else %> <% else %>
<h4><%= t("community.create_first_community_theme") %></h4> <h4><%= t("community.show.create_first_community_topic") %></h4>
<% end %> <% end %>

View File

@@ -2,17 +2,9 @@
<div class="small-12 medium-9 column"> <div class="small-12 medium-9 column">
<%= render "shared/back_link" %> <%= render "shared/back_link" %>
<h1><%= t("topic.edit.editing") %></h1> <h1><%= t("community.topic.edit") %></h1>
<%= render "form" %> <%= render "form" %>
</div> </div>
<div class="small-12 medium-3 column"> <%= render "recommendations" %>
<span class="icon-proposals float-right"></span>
<h2><%= t("community.sidebar.topic.recommendations_title") %></h2>
<ul class="recommendations">
<li><%= t("community.sidebar.topic.recommendation_one") %></li>
<li><%= t("community.sidebar.topic.recommendation_two") %></li>
<li><%= t("community.sidebar.topic..recommendation_three") %></li>
</ul>
</div>
</div> </div>

View File

@@ -2,19 +2,9 @@
<div class="small-12 medium-9 column"> <div class="small-12 medium-9 column">
<%= render "shared/back_link" %> <%= render "shared/back_link" %>
<h1><%= t("community.topic.create") %></h1> <h1><%= t("community.topic.create") %></h1>
<%= render 'form' %>
<%= render '/topics/form' %>
<%#= render '/topics/form', form_url: budget_investments_path(@budget) %>
</div> </div>
<div class="small-12 medium-3 column"> <%= render "recommendations" %>
<span class="icon-proposals float-right"></span>
<h2><%= t("community.sidebar.topic.recommendations_title") %></h2>
<ul class="recommendations">
<li><%= t("community.sidebar.topic.recommendation_one") %></li>
<li><%= t("community.sidebar.topic.recommendation_two") %></li>
<li><%= t("community.sidebar.topic..recommendation_three") %></li>
</ul>
</div>
</div> </div>

View File

@@ -6,6 +6,7 @@
<p><%= t("community.topic.show.community_of_the_proposal") %> <strong><%= @community.proposal.title %></strong></p> <p><%= t("community.topic.show.community_of_the_proposal") %> <strong><%= @community.proposal.title %></strong></p>
<h1><%= @topic.title %></h1> <h1><%= @topic.title %></h1>
<div class="topic-info"> <div class="topic-info">
<p><%= @topic.description %></p>
<%= render '/shared/author_info', resource: @topic %> <%= render '/shared/author_info', resource: @topic %>
<span class="bullet">&nbsp;&bull;&nbsp;</span> <span class="bullet">&nbsp;&bull;&nbsp;</span>

View File

@@ -37,6 +37,7 @@ data:
- config/locales/%{locale}/officing.yml - config/locales/%{locale}/officing.yml
- config/locales/%{locale}/budgets.yml - config/locales/%{locale}/budgets.yml
- config/locales/%{locale}/legislation.yml - config/locales/%{locale}/legislation.yml
- config/locales/%{locale}/community.yml
- config/locales/%{locale}/documents.yml - config/locales/%{locale}/documents.yml
# Locale files to write new keys to, based on a list of key pattern => file rules. Matched from top to bottom: # Locale files to write new keys to, based on a list of key pattern => file rules. Matched from top to bottom:

View File

@@ -1,41 +1,48 @@
en: en:
community: community:
create_first_community_theme: Create the first community theme
join_the_community_of_users: Join the community of users, give your opinion.
tab:
participant: Participants
sidebar: sidebar:
title: Community title: Comunidad
description: Join the community of users, give your opinion. description: Partecipa a la comunidad de usuarios, da tu opinión.
button_to_access: Access the community button_to_access: Access the community
topic:
title: Participants
new_topic: Create topic
edit_topic: Edit theme
recommendations_title: Recommendations to create a theme
recommendation_one: Do not write the topic title or whole sentences in capital letters. On the internet that is considered shouting. And no one likes to be yelled at.
recommendation_two: Any topic or comment that implies an illegal action will be eliminated, also those that intend to sabotage the spaces of the subject, everything else is allowed.
recommendation_three: Enjoy this space, the voices that fill it, it's yours too
without_topics: Create the first community topic
show: show:
title: User community title: Proposal community
description: Participate in the community of this proposal description: Participate in the community of this proposal
participants: Participants create_first_community_topic: Create the first community topic
tab:
participants: Participants
sidebar:
participate: Participate
new_topic: Create topic
disabled_info_title: You need to be logged to create a new topic
topic:
edit_button: Edit
comments:
one: 1 comment
other: "%{count} comments"
zero: No comments
topic: topic:
create: Create a theme create: Create a topic
edit_button: Edit edit: Edit Topic
form: form:
topic_title: Topic Title topic_title: Topic Title
topic_description_as_comment: Initial comment topic_description: Description
new: new:
submit_button: Create theme submit_button: Create topic
edit: edit:
submit_button: Edit theme submit_button: Edit topic
create:
submit_button: Create topic
update:
submit_button: Update topic
show: show:
community_of_the_proposal: Community of the proposal community_of_the_proposal: Community of the proposal
tab: tab:
comments_tab: Comments comments_tab: Comments
sidebar:
recommendations_title: Recommendations to create a topic
recommendation_one: Do not write the topic title or whole sentences in capital letters. On the internet that is considered shouting. And no one likes to be yelled at.
recommendation_two: Any topic or comment that implies an illegal action will be eliminated, also those that intend to sabotage the spaces of the subject, everything else is allowed.
recommendation_three: Enjoy this space, the voices that fill it, it's yours too.
topics:
show:
login_to_comment: You must %{signin} or %{signup} to leave a comment.

View File

@@ -179,6 +179,7 @@ en:
verification/sms: phone verification/sms: phone
signature_sheet: Signature sheet signature_sheet: Signature sheet
document: Document document: Document
topic: Topic
geozones: geozones:
none: All city none: All city
all: All scopes all: All scopes

View File

@@ -13,6 +13,7 @@ en:
spending_proposal: "Spending proposal created successfully. You can access it from %{activity}" spending_proposal: "Spending proposal created successfully. You can access it from %{activity}"
budget_investment: "Budget Investment created successfully." budget_investment: "Budget Investment created successfully."
signature_sheet: "Signature sheet created successfully" signature_sheet: "Signature sheet created successfully"
topic: "Topic created successfully."
save_changes: save_changes:
notice: Changes saved notice: Changes saved
update: update:
@@ -23,6 +24,7 @@ en:
proposal: "Proposal updated successfully." proposal: "Proposal updated successfully."
spending_proposal: "Investment project updated succesfully." spending_proposal: "Investment project updated succesfully."
budget_investment: "Investment project updated succesfully." budget_investment: "Investment project updated succesfully."
topic: "Topic updated successfully."
destroy: destroy:
spending_proposal: "Spending proposal deleted succesfully." spending_proposal: "Spending proposal deleted succesfully."
budget_investment: "Investment project deleted succesfully." budget_investment: "Investment project deleted succesfully."

View File

@@ -1,39 +1,48 @@
es: es:
community: community:
create_first_community_theme: Crea el primer tema de la comunidad
join_the_community_of_users: Partecipa a la comunidad de usuarios, da tu opinión.
tab:
participant: Participantes
sidebar: sidebar:
title: Comunidad title: Comunidad
description: Partecipa a la comunidad de usuarios, da tu opinión. description: Partecipa a la comunidad de usuarios, da tu opinión.
button_to_access: Acceder a la comunidad button_to_access: Acceder a la comunidad
topic:
title: Participa
new_topic: Crea un tema
recommendations_title: Recomendaciones para crear un tema
recommendation_one: No escribas el título del tema o frases enteras en mayúsculas. En internet eso se considera gritar. Y a nadie le gusta que le griten.
recommendation_two: Cualquier tema o comentario que implique una acción ilegal será eliminada, también las que tengan la intención de sabotear los espacios del tema, todo lo demás está permitido.
recommendation_three: Disfruta de este espacio, de las voces que lo llenan, también es tuyo
without_topics: Crea el primer tema de la comunidad
show: show:
title: Comunidad de usuarios title: Comunidad de la propuesta
description: Participa en la comunidad de esta propuesta description: Participa en la comunidad de esta propuesta
paricipants: Participantes create_first_community_topic: Crea el primer tema de la comunidad
tab:
paricipants: Participantes
sidebar:
participate: Participa
new_topic: Crea un tema
disabled_info_title: Necesitas estar logueado para crear un nuevo tema
topic:
edit_button: Editar
comments:
one: 1 Comentario
other: "%{count} Comentarios"
zero: Sin comentarios
topic: topic:
create: Crear un tema create: Crear un tema
edit_button: Editar edit: Editar tema
form: form:
topic_title: Titulo del tema topic_title: Titulo del tema
topic_description_as_comment: Comentario inicial topic_description: Descripción
new: new:
submit_button: Crear tema submit_button: Crear tema
edit: edit:
submit_button: Guardar cambios submit_button: Guardar cambios
create:
submit_button: Crear tema
update:
submit_button: Actualizar tema
show: show:
community_of_the_proposal: Comunidad de la propuesta community_of_the_proposal: Comunidad de la propuesta
tab: tab:
comments_tab: Comentarios comments_tab: Comentarios
sidebar:
recommendations_title: Recomendaciones para crear un tema
recommendation_one: No escribas el título del tema o frases enteras en mayúsculas. En internet eso se considera gritar. Y a nadie le gusta que le griten.
recommendation_two: Cualquier tema o comentario que implique una acción ilegal será eliminada, también las que tengan la intención de sabotear los espacios del tema, todo lo demás está permitido.
recommendation_three: Disfruta de este espacio, de las voces que lo llenan, también es tuyo
topics:
show:
login_to_comment: Necesitas %{signin} o %{signup} para comentar.

View File

@@ -179,6 +179,7 @@ es:
verification/sms: el teléfono verification/sms: el teléfono
signature_sheet: la hoja de firmas signature_sheet: la hoja de firmas
document: el documento document: el documento
topic: Tema
geozones: geozones:
none: Toda la ciudad none: Toda la ciudad
all: Todos los ámbitos de actuación all: Todos los ámbitos de actuación

View File

@@ -13,6 +13,7 @@ es:
spending_proposal: "Propuesta de inversión creada correctamente. Puedes acceder a ella desde %{activity}" spending_proposal: "Propuesta de inversión creada correctamente. Puedes acceder a ella desde %{activity}"
budget_investment: "Propuesta de inversión creada correctamente." budget_investment: "Propuesta de inversión creada correctamente."
signature_sheet: "Hoja de firmas creada correctamente" signature_sheet: "Hoja de firmas creada correctamente"
topic: "Tema creado correctamente."
save_changes: save_changes:
notice: Cambios guardados notice: Cambios guardados
update: update:
@@ -23,6 +24,7 @@ es:
poll_booth: "Urna actualizada correctamente." poll_booth: "Urna actualizada correctamente."
spending_proposal: "Propuesta de inversión actualizada correctamente." spending_proposal: "Propuesta de inversión actualizada correctamente."
budget_investment: "Propuesta de inversión actualizada correctamente" budget_investment: "Propuesta de inversión actualizada correctamente"
topic: "Tema actualizado correctamente."
destroy: destroy:
spending_proposal: "Propuesta de inversión eliminada." spending_proposal: "Propuesta de inversión eliminada."
budget_investment: "Propuesta de inversión eliminada." budget_investment: "Propuesta de inversión eliminada."

View File

@@ -2,7 +2,7 @@ class CreateTopics < ActiveRecord::Migration
def change def change
create_table :topics do |t| create_table :topics do |t|
t.string :title, null: false t.string :title, null: false
t.text :description_as_comment t.text :description
t.integer :author_id t.integer :author_id
t.integer "comments_count", default: 0 t.integer "comments_count", default: 0
t.references :community, index: true t.references :community, index: true

View File

@@ -891,14 +891,14 @@ ActiveRecord::Schema.define(version: 20170807082243) do
add_index "tags", ["spending_proposals_count"], name: "index_tags_on_spending_proposals_count", using: :btree add_index "tags", ["spending_proposals_count"], name: "index_tags_on_spending_proposals_count", using: :btree
create_table "topics", force: :cascade do |t| create_table "topics", force: :cascade do |t|
t.string "title", null: false t.string "title", null: false
t.text "description_as_comment" t.text "description"
t.integer "author_id" t.integer "author_id"
t.integer "comments_count", default: 0 t.integer "comments_count", default: 0
t.integer "community_id" t.integer "community_id"
t.datetime "hidden_at" t.datetime "hidden_at"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
end end
add_index "topics", ["community_id"], name: "index_topics_on_community_id", using: :btree add_index "topics", ["community_id"], name: "index_topics_on_community_id", using: :btree

View File

@@ -784,4 +784,11 @@ LOREM_IPSUM
locale "en" locale "en"
body "Some top links content" body "Some top links content"
end end
factory :topic do
sequence(:title) { |n| "Topic title #{n}" }
sequence(:description) { |n| "Description as comment #{n}" }
association :author, factory: :user
end
end end

View File

@@ -31,7 +31,7 @@ feature 'Commenting proposals' do
expect(page).to have_content parent_comment.body expect(page).to have_content parent_comment.body
expect(page).to have_content first_child.body expect(page).to have_content first_child.body
expect(page).to have_content second_child.body expect(page).to have_content second_child.body
debugger
expect(page).to have_link "Go back to #{proposal.title}", href: proposal_path(proposal) expect(page).to have_link "Go back to #{proposal.title}", href: proposal_path(proposal)
end end

View File

@@ -0,0 +1,548 @@
require 'rails_helper'
include ActionView::Helpers::DateHelper
feature 'Commenting topics' do
let(:user) { create :user }
let(:proposal) { create :proposal }
scenario 'Index', :js do
community = proposal.community
topic = create(:topic, community: community)
3.times { create(:comment, commentable: topic) }
visit community_topic_path(community, topic)
expect(page).to have_css('.comment', count: 3)
comment = Comment.last
within first('.comment') do
expect(page).to have_content comment.user.name
expect(page).to have_content I18n.l(comment.created_at, format: :datetime)
expect(page).to have_content comment.body
end
end
scenario 'Show', :js do
community = proposal.community
topic = create(:topic, community: community)
parent_comment = create(:comment, commentable: topic)
first_child = create(:comment, commentable: topic, parent: parent_comment)
second_child = create(:comment, commentable: topic, parent: parent_comment)
visit comment_path(parent_comment)
expect(page).to have_css(".comment", count: 3)
expect(page).to have_content parent_comment.body
expect(page).to have_content first_child.body
expect(page).to have_content second_child.body
expect(page).to have_link "Go back to #{topic.title}", href: community_topic_path(community, topic)
end
scenario 'Collapsable comments', :js do
community = proposal.community
topic = create(:topic, community: community)
parent_comment = create(:comment, body: "Main comment", commentable: topic)
child_comment = create(:comment, body: "First subcomment", commentable: topic, parent: parent_comment)
grandchild_comment = create(:comment, body: "Last subcomment", commentable: topic, parent: child_comment)
visit community_topic_path(community, topic)
expect(page).to have_css('.comment', count: 3)
find("#comment_#{child_comment.id}_children_arrow").trigger('click')
expect(page).to have_css('.comment', count: 2)
expect(page).to_not have_content grandchild_comment.body
find("#comment_#{child_comment.id}_children_arrow").trigger('click')
expect(page).to have_css('.comment', count: 3)
expect(page).to have_content grandchild_comment.body
find("#comment_#{parent_comment.id}_children_arrow").trigger('click')
expect(page).to have_css('.comment', count: 1)
expect(page).to_not have_content child_comment.body
expect(page).to_not have_content grandchild_comment.body
end
scenario 'Comment order' do
community = proposal.community
topic = create(:topic, community: community)
c1 = create(:comment, :with_confidence_score, commentable: topic, cached_votes_up: 100,
cached_votes_total: 120, created_at: Time.current - 2)
c2 = create(:comment, :with_confidence_score, commentable: topic, cached_votes_up: 10,
cached_votes_total: 12, created_at: Time.current - 1)
c3 = create(:comment, :with_confidence_score, commentable: topic, cached_votes_up: 1,
cached_votes_total: 2, created_at: Time.current)
visit community_topic_path(community, topic, order: :most_voted)
expect(c1.body).to appear_before(c2.body)
expect(c2.body).to appear_before(c3.body)
visit community_topic_path(community, topic, order: :newest)
expect(c3.body).to appear_before(c2.body)
expect(c2.body).to appear_before(c1.body)
visit community_topic_path(community, topic, order: :oldest)
expect(c1.body).to appear_before(c2.body)
expect(c2.body).to appear_before(c3.body)
end
scenario 'Creation date works differently in roots and in child comments, when sorting by confidence_score' do
community = proposal.community
topic = create(:topic, community: community)
old_root = create(:comment, commentable: topic, created_at: Time.current - 10)
new_root = create(:comment, commentable: topic, created_at: Time.current)
old_child = create(:comment, commentable: topic, parent_id: new_root.id, created_at: Time.current - 10)
new_child = create(:comment, commentable: topic, parent_id: new_root.id, created_at: Time.current)
visit community_topic_path(community, topic, order: :most_voted)
expect(new_root.body).to appear_before(old_root.body)
expect(old_child.body).to appear_before(new_child.body)
visit community_topic_path(community, topic, order: :newest)
expect(new_root.body).to appear_before(old_root.body)
expect(new_child.body).to appear_before(old_child.body)
visit community_topic_path(community, topic, order: :oldest)
expect(old_root.body).to appear_before(new_root.body)
expect(old_child.body).to appear_before(new_child.body)
end
scenario 'Turns links into html links' do
community = proposal.community
topic = create(:topic, community: community)
create :comment, commentable: topic, body: 'Built with http://rubyonrails.org/'
visit community_topic_path(community, topic)
within first('.comment') do
expect(page).to have_content 'Built with http://rubyonrails.org/'
expect(page).to have_link('http://rubyonrails.org/', href: 'http://rubyonrails.org/')
expect(find_link('http://rubyonrails.org/')[:rel]).to eq('nofollow')
expect(find_link('http://rubyonrails.org/')[:target]).to eq('_blank')
end
end
scenario 'Sanitizes comment body for security' do
community = proposal.community
topic = create(:topic, community: community)
create :comment, commentable: topic,
body: "<script>alert('hola')</script> <a href=\"javascript:alert('sorpresa!')\">click me<a/> http://www.url.com"
visit community_topic_path(community, topic)
within first('.comment') do
expect(page).to have_content "click me http://www.url.com"
expect(page).to have_link('http://www.url.com', href: 'http://www.url.com')
expect(page).not_to have_link('click me')
end
end
scenario 'Paginated comments' do
community = proposal.community
topic = create(:topic, community: community)
per_page = 10
(per_page + 2).times { create(:comment, commentable: topic)}
visit community_topic_path(community, topic)
expect(page).to have_css('.comment', count: per_page)
within("ul.pagination") do
expect(page).to have_content("1")
expect(page).to have_content("2")
expect(page).to_not have_content("3")
click_link "Next", exact: false
end
expect(page).to have_css('.comment', count: 2)
end
feature 'Not logged user' do
scenario 'can not see comments forms' do
community = proposal.community
topic = create(:topic, community: community)
create(:comment, commentable: topic)
visit community_topic_path(community, topic)
expect(page).to have_content 'You must Sign in or Sign up to leave a comment'
within('#comments') do
expect(page).to_not have_content 'Write a comment'
expect(page).to_not have_content 'Reply'
end
end
end
scenario 'Create', :js do
login_as(user)
community = proposal.community
topic = create(:topic, community: community)
visit community_topic_path(community, topic)
fill_in "comment-body-topic_#{topic.id}", with: 'Have you thought about...?'
click_button 'Publish comment'
within "#comments" do
expect(page).to have_content 'Have you thought about...?'
end
within "#tab-comments-label" do
expect(page).to have_content 'Comments (1)'
end
end
scenario 'Errors on create', :js do
login_as(user)
community = proposal.community
topic = create(:topic, community: community)
visit community_topic_path(community, topic)
click_button 'Publish comment'
expect(page).to have_content "Can't be blank"
end
scenario 'Reply', :js do
community = proposal.community
topic = create(:topic, community: community)
citizen = create(:user, username: 'Ana')
manuela = create(:user, username: 'Manuela')
comment = create(:comment, commentable: topic, user: citizen)
login_as(manuela)
visit community_topic_path(community, topic)
click_link "Reply"
within "#js-comment-form-comment_#{comment.id}" do
fill_in "comment-body-comment_#{comment.id}", with: 'It will be done next week.'
click_button 'Publish reply'
end
within "#comment_#{comment.id}" do
expect(page).to have_content 'It will be done next week.'
end
expect(page).to_not have_selector("#js-comment-form-comment_#{comment.id}", visible: true)
end
scenario 'Errors on reply', :js do
community = proposal.community
topic = create(:topic, community: community)
comment = create(:comment, commentable: topic, user: user)
login_as(user)
visit community_topic_path(community, topic)
click_link "Reply"
within "#js-comment-form-comment_#{comment.id}" do
click_button 'Publish reply'
expect(page).to have_content "Can't be blank"
end
end
scenario "N replies", :js do
community = proposal.community
topic = create(:topic, community: community)
parent = create(:comment, commentable: topic)
7.times do
create(:comment, commentable: topic, parent: parent)
parent = parent.children.first
end
visit community_topic_path(community, topic)
expect(page).to have_css(".comment.comment.comment.comment.comment.comment.comment.comment")
end
scenario "Flagging as inappropriate", :js do
community = proposal.community
topic = create(:topic, community: community)
comment = create(:comment, commentable: topic)
login_as(user)
visit community_topic_path(community, topic)
within "#comment_#{comment.id}" do
page.find("#flag-expand-comment-#{comment.id}").click
page.find("#flag-comment-#{comment.id}").click
expect(page).to have_css("#unflag-expand-comment-#{comment.id}")
end
expect(Flag.flagged?(user, comment)).to be
end
scenario "Undoing flagging as inappropriate", :js do
community = proposal.community
topic = create(:topic, community: community)
comment = create(:comment, commentable: topic)
Flag.flag(user, comment)
login_as(user)
visit community_topic_path(community, topic)
within "#comment_#{comment.id}" do
page.find("#unflag-expand-comment-#{comment.id}").click
page.find("#unflag-comment-#{comment.id}").click
expect(page).to have_css("#flag-expand-comment-#{comment.id}")
end
expect(Flag.flagged?(user, comment)).to_not be
end
scenario "Flagging turbolinks sanity check", :js do
community = proposal.community
topic = create(:topic, community: community, title: "Should we change the world?")
comment = create(:comment, commentable: topic)
login_as(user)
visit community_path(community)
click_link "Should we change the world?"
within "#comment_#{comment.id}" do
page.find("#flag-expand-comment-#{comment.id}").click
expect(page).to have_selector("#flag-comment-#{comment.id}")
end
end
scenario "Erasing a comment's author" do
community = proposal.community
topic = create(:topic, community: community)
comment = create(:comment, commentable: topic, body: "this should be visible")
comment.user.erase
visit community_topic_path(community, topic)
within "#comment_#{comment.id}" do
expect(page).to have_content('User deleted')
expect(page).to have_content('this should be visible')
end
end
feature "Moderators" do
scenario "can create comment as a moderator", :js do
community = proposal.community
topic = create(:topic, community: community)
moderator = create(:moderator)
login_as(moderator.user)
visit community_topic_path(community, topic)
fill_in "comment-body-topic_#{topic.id}", with: "I am moderating!"
check "comment-as-moderator-topic_#{topic.id}"
click_button "Publish comment"
within "#comments" do
expect(page).to have_content "I am moderating!"
expect(page).to have_content "Moderator ##{moderator.id}"
expect(page).to have_css "div.is-moderator"
expect(page).to have_css "img.moderator-avatar"
end
end
scenario "can create reply as a moderator", :js do
community = proposal.community
topic = create(:topic, community: community)
citizen = create(:user, username: "Ana")
manuela = create(:user, username: "Manuela")
moderator = create(:moderator, user: manuela)
comment = create(:comment, commentable: topic, user: citizen)
login_as(manuela)
visit community_topic_path(community, topic)
click_link "Reply"
within "#js-comment-form-comment_#{comment.id}" do
fill_in "comment-body-comment_#{comment.id}", with: "I am moderating!"
check "comment-as-moderator-comment_#{comment.id}"
click_button 'Publish reply'
end
within "#comment_#{comment.id}" do
expect(page).to have_content "I am moderating!"
expect(page).to have_content "Moderator ##{moderator.id}"
expect(page).to have_css "div.is-moderator"
expect(page).to have_css "img.moderator-avatar"
end
expect(page).to_not have_selector("#js-comment-form-comment_#{comment.id}", visible: true)
end
scenario "can not comment as an administrator" do
community = proposal.community
topic = create(:topic, community: community)
moderator = create(:moderator)
login_as(moderator.user)
visit community_topic_path(community, topic)
expect(page).to_not have_content "Comment as administrator"
end
end
feature "Administrators" do
scenario "can create comment as an administrator", :js do
community = proposal.community
topic = create(:topic, community: community)
admin = create(:administrator)
login_as(admin.user)
visit community_topic_path(community, topic)
fill_in "comment-body-topic_#{topic.id}", with: "I am your Admin!"
check "comment-as-administrator-topic_#{topic.id}"
click_button "Publish comment"
within "#comments" do
expect(page).to have_content "I am your Admin!"
expect(page).to have_content "Administrator ##{admin.id}"
expect(page).to have_css "div.is-admin"
expect(page).to have_css "img.admin-avatar"
end
end
scenario "can create reply as an administrator", :js do
community = proposal.community
topic = create(:topic, community: community)
citizen = create(:user, username: "Ana")
manuela = create(:user, username: "Manuela")
admin = create(:administrator, user: manuela)
comment = create(:comment, commentable: topic, user: citizen)
login_as(manuela)
visit community_topic_path(community, topic)
click_link "Reply"
within "#js-comment-form-comment_#{comment.id}" do
fill_in "comment-body-comment_#{comment.id}", with: "Top of the world!"
check "comment-as-administrator-comment_#{comment.id}"
click_button 'Publish reply'
end
within "#comment_#{comment.id}" do
expect(page).to have_content "Top of the world!"
expect(page).to have_content "Administrator ##{admin.id}"
expect(page).to have_css "div.is-admin"
expect(page).to have_css "img.admin-avatar"
end
expect(page).to_not have_selector("#js-comment-form-comment_#{comment.id}", visible: true)
end
scenario "can not comment as a moderator" do
community = proposal.community
topic = create(:topic, community: community)
admin = create(:administrator)
login_as(admin.user)
visit community_topic_path(community, topic)
expect(page).to_not have_content "Comment as moderator"
end
end
feature 'Voting comments' do
background do
@manuela = create(:user, verified_at: Time.current)
@pablo = create(:user)
@proposal = create(:proposal)
@topic = create(:topic, community: @proposal.community)
@comment = create(:comment, commentable: @topic)
login_as(@manuela)
end
scenario 'Show' do
create(:vote, voter: @manuela, votable: @comment, vote_flag: true)
create(:vote, voter: @pablo, votable: @comment, vote_flag: false)
visit community_topic_path(@proposal.community, @topic)
within("#comment_#{@comment.id}_votes") do
within(".in_favor") do
expect(page).to have_content "1"
end
within(".against") do
expect(page).to have_content "1"
end
expect(page).to have_content "2 votes"
end
end
scenario 'Create', :js do
visit community_topic_path(@proposal.community, @topic)
within("#comment_#{@comment.id}_votes") do
find(".in_favor a").click
within(".in_favor") do
expect(page).to have_content "1"
end
within(".against") do
expect(page).to have_content "0"
end
expect(page).to have_content "1 vote"
end
end
scenario 'Update', :js do
visit community_topic_path(@proposal.community, @topic)
within("#comment_#{@comment.id}_votes") do
find('.in_favor a').click
find('.against a').click
within('.in_favor') do
expect(page).to have_content "0"
end
within('.against') do
expect(page).to have_content "1"
end
expect(page).to have_content "1 vote"
end
end
scenario 'Trying to vote multiple times', :js do
visit community_topic_path(@proposal.community, @topic)
within("#comment_#{@comment.id}_votes") do
find('.in_favor a').click
find('.in_favor a').click
within('.in_favor') do
expect(page).to have_content "1"
end
within('.against') do
expect(page).to have_content "0"
end
expect(page).to have_content "1 vote"
end
end
end
end

View File

@@ -0,0 +1,107 @@
require 'rails_helper'
feature 'Communities' do
context 'Show' do
scenario 'Should display default content' do
proposal = create(:proposal)
community = proposal.community
user = create(:user)
login_as(user)
visit community_path(community)
expect(page).to have_content "Proposal community"
expect(page).to have_content proposal.title
expect(page).to have_content "Participate in the community of this proposal"
expect(page).to have_link("Create topic", href: new_community_topic_path(community))
expect(page).not_to have_selector(".button.disabled", text: "Create topic")
end
scenario 'Should display disabled create topic button when user is not logged' do
proposal = create(:proposal)
community = proposal.community
visit community_path(community)
expect(page).to have_selector(".button.disabled", text: "Create topic")
end
scenario 'Should display without_topics_text and empty participants when there are not topics' do
proposal = create(:proposal)
community = proposal.community
visit community_path(community)
expect(page).to have_content "Create the first community topic"
expect(page).to have_content "Participants (0)"
end
scenario 'Should display order selector and topic content when there are topics' do
proposal = create(:proposal)
community = proposal.community
topic = create(:topic, community: community)
create(:comment, commentable: topic)
visit community_path(community)
expect(page).to have_selector ".wide-order-selector"
within "#topic_#{topic.id}" do
expect(page).to have_content topic.title
expect(page).to have_content "#{topic.comments_count} comment"
expect(page).to have_content I18n.l(topic.created_at.to_date)
expect(page).to have_content topic.author.name
end
end
scenario 'Should display topic edit button when author is logged' do
proposal = create(:proposal)
community = proposal.community
user = create(:user)
topic1 = create(:topic, community: community, author: user)
topic2 = create(:topic, community: community)
login_as(user)
visit community_path(community)
within "#topic_#{topic1.id}" do
expect(page).to have_link("Edit", href: edit_community_topic_path(community, topic1))
end
within "#topic_#{topic2.id}" do
expect(page).not_to have_link("Edit", href: edit_community_topic_path(community, topic2))
end
end
scenario 'Should display participant when there is topics' do
proposal = create(:proposal)
community = proposal.community
topic = create(:topic, community: community)
visit community_path(community)
within ".communities-participant" do
expect(page).to have_content "Participants (1)"
expect(page).to have_content topic.author.name
end
end
scenario 'Should display participants when there are topics and comments' do
proposal = create(:proposal)
community = proposal.community
topic = create(:topic, community: community)
comment = create(:comment, commentable: topic)
visit community_path(community)
within ".communities-participant" do
expect(page).to have_content "Participants (2)"
expect(page).to have_content topic.author.name
expect(page).to have_content comment.author.name
end
end
end
end

View File

@@ -63,6 +63,7 @@ feature 'Proposals' do
expect(page.html).to include "<title>#{proposal.title}</title>" expect(page.html).to include "<title>#{proposal.title}</title>"
expect(page).not_to have_selector ".js-flag-actions" expect(page).not_to have_selector ".js-flag-actions"
expect(page).not_to have_selector ".js-follow" expect(page).not_to have_selector ".js-follow"
expect(page).to have_content "Access the community"
within('.social-share-button') do within('.social-share-button') do
expect(page.all('a').count).to be(4) # Twitter, Facebook, Google+, Telegram expect(page.all('a').count).to be(4) # Twitter, Facebook, Google+, Telegram

View File

@@ -0,0 +1,102 @@
require 'rails_helper'
feature 'Topics' do
context 'New' do
scenario 'Should display disabled button to new topic page without user logged', :js do
proposal = create(:proposal)
community = proposal.community
visit community_path(community)
expect(page).to have_selector(".button.expanded.disabled")
end
scenario 'Should can access to new topic page with user logged', :js do
proposal = create(:proposal)
community = proposal.community
user = create(:user)
login_as(user)
visit community_path(community)
click_link "Create topic"
expect(page).to have_content "Create a topic"
end
scenario 'Should have content on new topic page', :js do
proposal = create(:proposal)
community = proposal.community
user = create(:user)
login_as(user)
visit community_path(community)
click_link "Create topic"
expect(page).to have_content "Topic Title"
expect(page).to have_content "Description"
expect(page).to have_content "Recommendations to create a topic"
expect(page).to have_content "Do not write the topic title or whole sentences in capital letters. On the internet that is considered shouting. And no one likes to be yelled at."
expect(page).to have_content "Any topic or comment that implies an illegal action will be eliminated, also those that intend to sabotage the spaces of the subject, everything else is allowed."
expect(page).to have_content "Enjoy this space, the voices that fill it, it's yours too."
expect(page).to have_button("Create topic")
end
end
context 'Create' do
scenario 'Should can create a new topic', :js do
proposal = create(:proposal)
community = proposal.community
user = create(:user)
login_as(user)
visit new_community_topic_path(community)
fill_in "topic_title", with: "New topic title"
fill_in "topic_description", with: "Topic description"
click_button "Create topic"
expect(page).to have_content "New topic title"
expect(current_path).to eq(community_path(community))
end
end
context 'Edit' do
scenario 'Should can edit a topic' do
proposal = create(:proposal)
community = proposal.community
user = create(:user)
topic = create(:topic, community: community, author: user)
login_as(user)
visit edit_community_topic_path(community, topic)
fill_in "topic_title", with: "Edit topic title"
fill_in "topic_description", with: "Edit topic description"
click_button "Edit topic"
expect(page).to have_content "Edit topic title"
expect(current_path).to eq(community_path(community))
end
end
context 'Show' do
scenario 'Should can show topic' do
proposal = create(:proposal)
community = proposal.community
topic = create(:topic, community: community)
visit community_topic_path(community, topic)
expect(page).to have_content community.proposal.title
expect(page).to have_content topic.title
end
end
end

View File

@@ -0,0 +1,10 @@
require 'rails_helper'
RSpec.describe Community, type: :model do
it "should be valid when create proposal" do
proposal = create(:proposal)
expect(proposal.community).to be_valid
end
end

75
spec/models/topic_spec.rb Normal file
View File

@@ -0,0 +1,75 @@
require 'rails_helper'
describe Topic do
let(:topic) { build(:topic) }
it "should be valid" do
expect(topic).to be_valid
end
it "should not be valid without an author" do
topic.author = nil
expect(topic).to_not be_valid
end
it "should not be valid without a title" do
topic.title = nil
expect(topic).to_not be_valid
end
it "should not be valid without a description" do
topic.description = nil
expect(topic).to_not be_valid
end
context "order" do
it "orders by newest" do
proposal = create(:proposal)
community = proposal.community
topic1 = create(:topic, community: community)
topic2 = create(:topic, community: community)
topic3 = create(:topic, community: community)
results = community.topics.sort_by_newest
expect(results.first).to eq(topic3)
expect(results.second).to eq(topic2)
expect(results.third).to eq(topic1)
end
it "orders by oldest" do
proposal = create(:proposal)
community = proposal.community
topic1 = create(:topic, community: community)
topic2 = create(:topic, community: community)
topic3 = create(:topic, community: community)
results = community.topics.sort_by_oldest
expect(results.first).to eq(topic1)
expect(results.second).to eq(topic2)
expect(results.third).to eq(topic3)
end
it "orders by most_commented" do
proposal = create(:proposal)
community = proposal.community
topic1 = create(:topic, community: community)
create(:comment, commentable: topic1)
create(:comment, commentable: topic1)
topic2 = create(:topic, community: community)
create(:comment, commentable: topic2)
topic3 = create(:topic, community: community)
results = community.topics.sort_by_most_commented
expect(results.first).to eq(topic1)
expect(results.second).to eq(topic2)
expect(results.third).to eq(topic3)
end
end
end

View File

@@ -680,4 +680,22 @@ describe User do
end end
end end
describe "#community_participants" do
it "should return participants without duplicates" do
proposal = create(:proposal)
community = proposal.community
user1 = create(:user)
user2 = create(:user)
topic1 = create(:topic, community: community, author: user1)
create(:comment, commentable: topic1, author: user1)
create(:comment, commentable: topic1, author: user2)
topic2 = create(:topic, community: community, author: user2)
expect(User.community_participants(community)).to eq [user1, user2]
end
end
end end