diff --git a/app/assets/stylesheets/layout.scss b/app/assets/stylesheets/layout.scss index 62e09e088..e47ba0343 100644 --- a/app/assets/stylesheets/layout.scss +++ b/app/assets/stylesheets/layout.scss @@ -2424,11 +2424,28 @@ table { li { border-bottom: 1px solid $border; - padding: $line-height / 4; + margin-bottom: 0 !important; + padding: $line-height / 2; &:first-child { border-top: 1px solid $border; } + + @include breakpoint(medium) { + + .score-actions { + display: none; + float: right; + } + } + + &:hover { + background: #f9f9f9; + + .score-actions { + display: block; + } + } } h3 { @@ -2436,7 +2453,7 @@ table { font-weight: normal; } - span:not(.icon-flag) { + .related-content-title { color: #4f4f4f; font-size: rem-calc(12); text-transform: uppercase; @@ -2447,6 +2464,47 @@ table { } } +.relate-content-score { + display: block; + + @include breakpoint(medium) { + text-align: center; + } + + a { + font-weight: bold; + margin-right: $line-height; + padding-left: rem-calc(20); + position: relative; + text-decoration: none; + + &.score-positive:before, + &.score-negative:before { + font-family: 'icons'; + left: 0; + position: absolute; + } + + &.score-positive { + color: $color-success; + + &:before { + color: $color-success; + content: '\6c'; + } + } + + &.score-negative { + color: $color-alert; + + &:before { + color: $color-alert; + content: '\76'; + } + } + } +} + // 22. Images // ----------------- diff --git a/app/controllers/related_contents_controller.rb b/app/controllers/related_contents_controller.rb index 8e2cd854d..aac64dd99 100644 --- a/app/controllers/related_contents_controller.rb +++ b/app/controllers/related_contents_controller.rb @@ -1,13 +1,11 @@ class RelatedContentsController < ApplicationController - VALID_URL = /#{Setting['url']}\/.*\/.*/ - skip_authorization_check respond_to :html, :js def create if relationable_object && related_object - @relationable.relate_content(@related) + RelatedContent.create(parent_relationable: @relationable, child_relationable: @related, author: current_user) flash[:success] = t('related_content.success') else @@ -17,28 +15,25 @@ class RelatedContentsController < ApplicationController redirect_to @relationable end - def flag - @related = RelatedContent.find_by(id: params[:id]) - - Flag.flag(current_user, @related) - Flag.flag(current_user, @related.opposite_related_content) - - render template: 'relationable/_refresh_flag_actions' + def score_positive + score(:positive) end - def unflag - @related = RelatedContent.find_by(id: params[:id]) - - Flag.unflag(current_user, @related) - Flag.unflag(current_user, @related.opposite_related_content) - - render template: 'relationable/_refresh_flag_actions' + def score_negative + score(:negative) end private + def score(action) + @related = RelatedContent.find_by(id: params[:id]) + @related.send("score_#{action}", current_user) + + render template: 'relationable/_refresh_score_actions' + end + def valid_url? - params[:url].match(VALID_URL) + params[:url].start_with?(Setting['url']) end def relationable_object diff --git a/app/helpers/flags_helper.rb b/app/helpers/flags_helper.rb index 44da7938d..9983b34ae 100644 --- a/app/helpers/flags_helper.rb +++ b/app/helpers/flags_helper.rb @@ -21,8 +21,6 @@ module FlagsHelper def own_flaggable?(flaggable) if flaggable.is_a? Comment flaggable.user_id == current_user.id - elsif flaggable.is_a? RelatedContent - false else flaggable.author_id == current_user.id end diff --git a/app/models/concerns/relationable.rb b/app/models/concerns/relationable.rb index 4ef03990e..4ce6bd3d3 100644 --- a/app/models/concerns/relationable.rb +++ b/app/models/concerns/relationable.rb @@ -5,19 +5,11 @@ module Relationable has_many :related_contents, as: :parent_relationable, dependent: :destroy end - def relate_content(relationable) - RelatedContent.find_or_create_by(parent_relationable: self, child_relationable: relationable) + def find_related_content(relationable) + RelatedContent.where(parent_relationable: self, child_relationable: relationable).first end def relationed_contents related_contents.not_hidden.map { |related_content| related_content.child_relationable } end - - def report_related_content(relationable) - related_content = related_contents.find_by(child_relationable: relationable) - if related_content.present? - related_content.increment!(:flags_count) - related_content.opposite_related_content.increment!(:flags_count) - end - end end diff --git a/app/models/related_content.rb b/app/models/related_content.rb index 7c9515035..1fd1b6b0c 100644 --- a/app/models/related_content.rb +++ b/app/models/related_content.rb @@ -1,12 +1,15 @@ class RelatedContent < ActiveRecord::Base - include Flaggable - - RELATED_CONTENTS_REPORT_THRESHOLD = Setting['related_contents_report_threshold'].to_i + RELATED_CONTENT_SCORE_THRESHOLD = Setting['related_content_score_threshold'].to_f RELATIONABLE_MODELS = %w{proposals debates}.freeze + acts_as_paranoid column: :hidden_at + include ActsAsParanoidAliases + + belongs_to :author, class_name: 'User', foreign_key: 'author_id' belongs_to :parent_relationable, polymorphic: true, touch: true belongs_to :child_relationable, polymorphic: true, touch: true has_one :opposite_related_content, class_name: 'RelatedContent', foreign_key: :related_content_id + has_many :related_content_scores validates :parent_relationable_id, presence: true validates :parent_relationable_type, presence: true @@ -15,22 +18,46 @@ class RelatedContent < ActiveRecord::Base validates :parent_relationable_id, uniqueness: { scope: [:parent_relationable_type, :child_relationable_id, :child_relationable_type] } after_create :create_opposite_related_content, unless: proc { opposite_related_content.present? } - after_destroy :destroy_opposite_related_content, if: proc { opposite_related_content.present? } + after_create :create_author_score - scope :not_hidden, -> { where('flags_count <= ?', RELATED_CONTENTS_REPORT_THRESHOLD) } + scope :not_hidden, -> { where(hidden_at: nil) } - def hidden_by_reports? - flags_count > RELATED_CONTENTS_REPORT_THRESHOLD + def score_positive(user) + score(RelatedContentScore::SCORES[:POSITIVE], user) + end + + def score_negative(user) + score(RelatedContentScore::SCORES[:NEGATIVE], user) + end + + def scored_by_user?(user) + related_content_scores.exists?(user: user) end private def create_opposite_related_content - related_content = RelatedContent.create!(opposite_related_content: self, parent_relationable: child_relationable, child_relationable: parent_relationable) + related_content = RelatedContent.create!(opposite_related_content: self, parent_relationable: child_relationable, + child_relationable: parent_relationable, author: author) self.opposite_related_content = related_content end - def destroy_opposite_related_content - opposite_related_content.destroy + def score(value, user) + score_with_opposite(value, user) + hide_with_opposite if (related_content_scores.sum(:value) / related_content_scores_count) < RELATED_CONTENT_SCORE_THRESHOLD + end + + def hide_with_opposite + hide + opposite_related_content.hide + end + + def create_author_score + score_positive(author) + end + + def score_with_opposite(value, user) + RelatedContentScore.create(user: user, related_content: self, value: value) + RelatedContentScore.create(user: user, related_content: opposite_related_content, value: value) end end diff --git a/app/models/related_content_score.rb b/app/models/related_content_score.rb new file mode 100644 index 000000000..da37fc07d --- /dev/null +++ b/app/models/related_content_score.rb @@ -0,0 +1,13 @@ +class RelatedContentScore < ActiveRecord::Base + SCORES = { + POSITIVE: 1, + NEGATIVE: -1 + }.freeze + + belongs_to :related_content, touch: true, counter_cache: :related_content_scores_count + belongs_to :user + + validates :user, presence: true + validates :related_content, presence: true + validates :related_content_id, uniqueness: { scope: [:user_id] } +end diff --git a/app/views/relationable/_flag_actions.html.erb b/app/views/relationable/_flag_actions.html.erb deleted file mode 100644 index d501b098d..000000000 --- a/app/views/relationable/_flag_actions.html.erb +++ /dev/null @@ -1,19 +0,0 @@ - - <% if show_flag_action? related %> - - - - - <%= link_to t('shared.flag'), flag_related_content_path(related), method: :put, remote: true, id: "flag-related-#{ related.id }" %> - - <% end %> - - <% if show_unflag_action? related %> - - - - - <%= link_to t('shared.unflag'), unflag_related_content_path(related), method: :put, remote: true, id: "unflag-related-#{ related.id }" %> - - <% end %> - diff --git a/app/views/relationable/_refresh_flag_actions.js.erb b/app/views/relationable/_refresh_flag_actions.js.erb deleted file mode 100644 index d3755c4b1..000000000 --- a/app/views/relationable/_refresh_flag_actions.js.erb +++ /dev/null @@ -1 +0,0 @@ -$("#<%= dom_id(@related) %>.js-flag-actions").html('<%= j render("relationable/flag_actions", related: @related) %>'); diff --git a/app/views/relationable/_refresh_score_actions.js.erb b/app/views/relationable/_refresh_score_actions.js.erb new file mode 100644 index 000000000..a0f2101c6 --- /dev/null +++ b/app/views/relationable/_refresh_score_actions.js.erb @@ -0,0 +1 @@ +$("#<%= dom_id(@related) %>.js-score-actions").html(''); diff --git a/app/views/relationable/_related_content.html.erb b/app/views/relationable/_related_content.html.erb index a034fd14f..66994a033 100644 --- a/app/views/relationable/_related_content.html.erb +++ b/app/views/relationable/_related_content.html.erb @@ -4,11 +4,13 @@

<%= t("related_content.title") %> (<%= relationable.relationed_contents.count %>)

- - - + <% if current_user %> + + + + <% end %> <%= render 'relationable/form', relationable: relationable %> diff --git a/app/views/relationable/_related_list.html.erb b/app/views/relationable/_related_list.html.erb index f1d86b748..2312a589e 100644 --- a/app/views/relationable/_related_list.html.erb +++ b/app/views/relationable/_related_list.html.erb @@ -1,11 +1,14 @@