Merge pull request #2183 from consul/related-content
Related contents functionalities
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
// 18. Banners
|
||||
// 19. Recommended Section Home
|
||||
// 20. Documents
|
||||
// 21. Related content
|
||||
//
|
||||
|
||||
// 01. Global styles
|
||||
@@ -2391,3 +2392,56 @@ table {
|
||||
background: #fafafa;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
// 21. Related content
|
||||
// -------------------
|
||||
|
||||
.related-content {
|
||||
border-top: 1px solid $border;
|
||||
|
||||
h2 {
|
||||
font-size: rem-calc(24);
|
||||
|
||||
span {
|
||||
color: #4f4f4f;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-related-content {
|
||||
display: block;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.related-content-list {
|
||||
list-style-type: none;
|
||||
margin-left: 0;
|
||||
|
||||
li {
|
||||
border-bottom: 1px solid $border;
|
||||
padding: $line-height / 4;
|
||||
|
||||
&:first-child {
|
||||
border-top: 1px solid $border;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: $base-font-size;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #4f4f4f;
|
||||
font-size: rem-calc(12);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.flag {
|
||||
margin-top: $line-height / 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ class Management::ProposalsController < Management::BaseController
|
||||
def show
|
||||
super
|
||||
@notifications = @proposal.notifications
|
||||
@related_contents = Kaminari.paginate_array(@proposal.relationed_contents).page(params[:page]).per(5)
|
||||
|
||||
redirect_to management_proposal_path(@proposal), status: :moved_permanently if request.path != management_proposal_path(@proposal)
|
||||
end
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ class ProposalsController < ApplicationController
|
||||
def show
|
||||
super
|
||||
@notifications = @proposal.notifications
|
||||
@related_contents = Kaminari.paginate_array(@proposal.relationed_contents).page(params[:page]).per(5)
|
||||
|
||||
redirect_to proposal_path(@proposal), status: :moved_permanently if request.path != proposal_path(@proposal)
|
||||
end
|
||||
|
||||
|
||||
42
app/controllers/related_contents_controller.rb
Normal file
42
app/controllers/related_contents_controller.rb
Normal file
@@ -0,0 +1,42 @@
|
||||
class RelatedContentsController < ApplicationController
|
||||
VALID_URL = /#{Setting['url']}\/.*\/.*/
|
||||
|
||||
skip_authorization_check
|
||||
|
||||
def create
|
||||
if relationable_object && related_object
|
||||
@relationable.relate_content(@related)
|
||||
|
||||
flash[:success] = t('related_content.success')
|
||||
else
|
||||
flash[:error] = t('related_content.error', url: Setting['url'])
|
||||
end
|
||||
|
||||
redirect_to @relationable
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def valid_url?
|
||||
params[:url].match(VALID_URL)
|
||||
end
|
||||
|
||||
def relationable_object
|
||||
@relationable = (params[:relationable_klass].singularize.camelize.constantize).find_by_id(params[:relationable_id])
|
||||
end
|
||||
|
||||
def related_object
|
||||
begin
|
||||
if valid_url?
|
||||
url = params[:url]
|
||||
|
||||
related_klass = url.match(/\/(#{RelatedContent::RELATIONABLE_MODELS.join("|")})\//)[0].gsub("/", "")
|
||||
related_id = url.match(/\/[0-9]+/)[0].gsub("/", "")
|
||||
|
||||
@related = (related_klass.singularize.camelize.constantize).find_by_id(related_id)
|
||||
end
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,6 @@
|
||||
class RelatedContent < ActiveRecord::Base
|
||||
RELATED_CONTENTS_REPORT_THRESHOLD = Setting['related_contents_report_threshold'].to_i
|
||||
RELATIONABLE_MODELS = %w{proposals debates}
|
||||
|
||||
belongs_to :parent_relationable, polymorphic: true
|
||||
belongs_to :child_relationable, polymorphic: true
|
||||
|
||||
@@ -108,6 +108,8 @@
|
||||
|
||||
<%= render 'shared/geozone', geozonable: @proposal %>
|
||||
|
||||
<%= render 'relationable/related_content', relationable: @proposal %>
|
||||
|
||||
<div class="js-moderator-proposal-actions margin">
|
||||
<%= render 'proposals/actions', proposal: @proposal %>
|
||||
</div>
|
||||
|
||||
22
app/views/relationable/_form.html.erb
Normal file
22
app/views/relationable/_form.html.erb
Normal file
@@ -0,0 +1,22 @@
|
||||
<%= form_tag related_contents_path, method: :post, id: "related_content", class: "hide", "data-toggler": ".hide" do %>
|
||||
<label><%= t("related_content.label") %></label>
|
||||
|
||||
<p class="help-text" id="related_content_help_text">
|
||||
<%= t("related_content.help", models: t('related_content.content_title').values.to_sentence, org: setting['org_name']) %>
|
||||
</p>
|
||||
|
||||
<div class="input-group">
|
||||
<div class="input-group-field">
|
||||
<%= text_field_tag :url, '',
|
||||
"aria-describedby": "related_content_help_text",
|
||||
placeholder: t("related_content.placeholder", url: setting['url']) %>
|
||||
|
||||
<%= hidden_field_tag :relationable_klass, relationable.class.name %>
|
||||
<%= hidden_field_tag :relationable_id, relationable.id %>
|
||||
</div>
|
||||
|
||||
<div class="input-group-button">
|
||||
<%= submit_tag t("related_content.submit"), class: "button" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
18
app/views/relationable/_related_content.html.erb
Normal file
18
app/views/relationable/_related_content.html.erb
Normal file
@@ -0,0 +1,18 @@
|
||||
<div class="related-content padding">
|
||||
|
||||
<div class="margin-bottom">
|
||||
<h2 class="inline-block">
|
||||
<%= t("related_content.title") %> <span>(<%= relationable.relationed_contents.count %>)</span>
|
||||
</h2>
|
||||
<a>
|
||||
<button type="button" data-toggle="related_content" class="add-related-content" id="add-related-content">
|
||||
<%= t("related_content.add") %>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<%= render 'relationable/form', relationable: relationable %>
|
||||
|
||||
<%= render 'relationable/related_list', relationable: relationable %>
|
||||
|
||||
</div>
|
||||
16
app/views/relationable/_related_list.html.erb
Normal file
16
app/views/relationable/_related_list.html.erb
Normal file
@@ -0,0 +1,16 @@
|
||||
<ul class="related-content-list" id="related-content-list">
|
||||
<% @related_contents.each do |related| %>
|
||||
<li>
|
||||
<a href="#" class="float-right flag">
|
||||
<span class="icon-flag"></span> <!-- This should be similar to comments/flag_actions -->
|
||||
</a>
|
||||
|
||||
<span><%= t("related_content.content_title.#{related.class.name.downcase}") %></span><br>
|
||||
<h3 class="inline-block">
|
||||
<%= link_to related.title, eval("#{related.class.name.downcase}_path(related)") %>
|
||||
</h3>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
||||
<%= paginate @related_contents %>
|
||||
@@ -811,3 +811,15 @@ en:
|
||||
invisible_captcha:
|
||||
sentence_for_humans: "If you are human, ignore this field"
|
||||
timestamp_error_message: "Sorry, that was too quick! Please resubmit."
|
||||
related_content:
|
||||
title: "Related content"
|
||||
add: "Add related content"
|
||||
label: "Link to related content"
|
||||
placeholder: "%{url}"
|
||||
help: "You can add links of %{models} inside of %{org}."
|
||||
submit: "Add"
|
||||
error: "Link not valid. Remember to start with %{url}."
|
||||
success: "You added a new related content"
|
||||
content_title:
|
||||
proposal: "Proposal"
|
||||
debate: "Debate"
|
||||
|
||||
@@ -808,3 +808,15 @@ es:
|
||||
invisible_captcha:
|
||||
sentence_for_humans: "Si eres humano, por favor ignora este campo"
|
||||
timestamp_error_message: "Eso ha sido demasiado rápido. Por favor, reenvía el formulario."
|
||||
related_content:
|
||||
title: "Contenido relacionado"
|
||||
add: "Añadir contenido relacionado"
|
||||
label: "Enlace a contenido relacionado"
|
||||
placeholder: "%{url}"
|
||||
help: "Puedes introducir cualquier enlace de %{models} que esté dentro de %{org}."
|
||||
submit: "Añadir"
|
||||
error: "Enlace no válido. Recuerda que debe empezar por %{url}."
|
||||
success: "Has añadido un nuevo contenido relacionado"
|
||||
content_title:
|
||||
proposal: "Propuesta"
|
||||
debate: "Debate"
|
||||
|
||||
@@ -462,6 +462,8 @@ Rails.application.routes.draw do
|
||||
root to: "dashboard#index"
|
||||
end
|
||||
|
||||
resources :related_contents, only: [:create]
|
||||
|
||||
# GraphQL
|
||||
get '/graphql', to: 'graphql#query'
|
||||
post '/graphql', to: 'graphql#query'
|
||||
|
||||
@@ -142,6 +142,73 @@ feature 'Proposals' do
|
||||
visit proposal_path(proposal)
|
||||
expect(page).not_to have_content "Access the community"
|
||||
end
|
||||
|
||||
scenario 'related contents are listed' do
|
||||
proposal1 = create(:proposal)
|
||||
proposal2 = create(:proposal)
|
||||
related_content = create(:related_content, parent_relationable: proposal1, child_relationable: proposal2)
|
||||
|
||||
visit proposal_path(proposal1)
|
||||
within("#related-content-list") do
|
||||
expect(page).to have_content(proposal2.title)
|
||||
end
|
||||
|
||||
visit proposal_path(proposal2)
|
||||
within("#related-content-list") do
|
||||
expect(page).to have_content(proposal1.title)
|
||||
end
|
||||
end
|
||||
|
||||
scenario 'related contents can be added' do
|
||||
proposal1 = create(:proposal)
|
||||
proposal2 = create(:proposal)
|
||||
debate1 = create(:debate)
|
||||
|
||||
visit proposal_path(proposal1)
|
||||
|
||||
expect(page).to have_selector('#related_content', visible: false)
|
||||
click_on("Add related content")
|
||||
expect(page).to have_selector('#related_content', visible: true)
|
||||
|
||||
within("#related_content") do
|
||||
fill_in 'url', with: "#{Setting['url']}/proposals/#{proposal2.to_param}"
|
||||
click_button "Add"
|
||||
end
|
||||
|
||||
within("#related-content-list") do
|
||||
expect(page).to have_content(proposal2.title)
|
||||
end
|
||||
|
||||
visit proposal_path(proposal2)
|
||||
|
||||
within("#related-content-list") do
|
||||
expect(page).to have_content(proposal1.title)
|
||||
end
|
||||
|
||||
within("#related_content") do
|
||||
fill_in 'url', with: "#{Setting['url']}/debates/#{debate1.to_param}"
|
||||
click_button "Add"
|
||||
end
|
||||
|
||||
within("#related-content-list") do
|
||||
expect(page).to have_content(debate1.title)
|
||||
end
|
||||
end
|
||||
|
||||
scenario 'if related content URL is invalid returns error' do
|
||||
proposal1 = create(:proposal)
|
||||
|
||||
visit proposal_path(proposal1)
|
||||
|
||||
click_on("Add related content")
|
||||
|
||||
within("#related_content") do
|
||||
fill_in 'url', with: "http://invalidurl.com"
|
||||
click_button "Add"
|
||||
end
|
||||
|
||||
expect(page).to have_content("Link not valid. Remember to start with #{Setting[:url]}.")
|
||||
end
|
||||
end
|
||||
|
||||
context "Embedded video" do
|
||||
|
||||
Reference in New Issue
Block a user