diff --git a/.travis.yml b/.travis.yml index 4eb86bbf6..9da8210d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,10 +10,10 @@ before_script: - bundle exec rake db:setup script: - "bundle exec rake assets:precompile RAILS_ENV=test" - - "bundle exec rake knapsack:rspec" + - "bin/knapsack_pro_rspec" env: global: - - CI_NODE_TOTAL=2 + - KNAPSACK_PRO_CI_NODE_TOTAL=2 matrix: - - CI_NODE_INDEX=0 - - CI_NODE_INDEX=1 + - KNAPSACK_PRO_CI_NODE_INDEX=0 + - KNAPSACK_PRO_CI_NODE_INDEX=1 diff --git a/CHANGELOG.md b/CHANGELOG.md index a487eadb4..2f39815c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,38 +1,148 @@ -### 0.9 - 2017-06-15 +# Changelog +All notable changes to this project will be documented in this file. -* New features - * Budgets - * Basic polls - * Collaborative legistlation - * Custom pages - * GraphQL API - * Improved admin section -* Enhancements - * Improved admin section - * Rails 4.2.8 - * Ruby 2.3.2 -* Bug fixes - * CKEditor locale compilation fixed - * Fixed bugs in mobile layouts -* Deprecations - * SpendingProposals are deprecated now in favor of Budgets +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -### 0.8 - 2016-07-21 +## [Unreleased](https://github.com/consul/consul/compare/v0.11...consul:master) -* New features - * Support for customization schema, vía specific custom files, assets and folders -* Enhancements - * Rails 2.4.7 - * Ruby 2.3.1 -* Bug fixes - * Fixed bug causing errors on user deletion +## [0.11.0](https://github.com/consul/consul/compare/v0.10...v0.11) - 2017-12-05 -### 0.7 - 2016-04-25 +### Added +- Allow social media image meta tags to be overwritten https://github.com/consul/consul/pull/1756 & https://github.com/consul/consul/pull/2153 +- Allow users to verify their account against a local Census https://github.com/consul/consul/pull/1752 +- Make Proposals & Budgets Investments followable by users https://github.com/consul/consul/pull/1727 +- Show user followable activity on public user page https://github.com/consul/consul/pull/1750 +- Add Budget results view & table https://github.com/consul/consul/pull/1748 +- Improved Budget winners calculations https://github.com/consul/consul/pull/1738 +- Allow Documents to be uploaded to Proposals and Budget Investments https://github.com/consul/consul/pull/1809 +- Allow Communities creation on Proposals and Budget Investments (Run rake task 'communities:associate_community') https://github.com/consul/consul/pull/1815 https://github.com/consul/consul/pull/1833 +- Allow user to geolocate Proposals and Budget Investments on a map https://github.com/consul/consul/pull/1864 +- Legislation Process Proposals https://github.com/consul/consul/pull/1906 +- Autocomplete user tags https://github.com/consul/consul/pull/1905 +- GraphQL API docs https://github.com/consul/consul/pull/1763 +- Show recommended proposals and debates to users based in their interests https://github.com/consul/consul/pull/1824 +- Allow images & videos to be added to Poll questions https://github.com/consul/consul/pull/1835 https://github.com/consul/consul/pull/1915 +- Add Poll Shifts, to soon replace Poll OfficerAssignments usage entirely (for now just partially) +- Added dropdown menu for advanced users https://github.com/consul/consul/pull/1761 +- Help text headers and footers https://github.com/consul/consul/pull/1807 +- Added a couple of steps for linux installation guidelines https://github.com/consul/consul/pull/1846 +- Added TotalResult model, to replace Poll::FinalRecount https://github.com/consul/consul/pull/1866 1885 +- Preview Budget Results by admins https://github.com/consul/consul/pull/1923 +- Added comments to Polls https://github.com/consul/consul/pull/1961 +- Added images & videos to Polls https://github.com/consul/consul/pull/1990 https://github.com/consul/consul/pull/1989 +- Poll Answers are orderable now https://github.com/consul/consul/pull/2037 +- Poll Booth Assigment management https://github.com/consul/consul/pull/2087 +- Legislation processes documents https://github.com/consul/consul/pull/2084 +- Poll results https://github.com/consul/consul/pull/2082 +- Poll stats https://github.com/consul/consul/pull/2075 +- Poll stats on admin panel https://github.com/consul/consul/pull/2102 +- Added investment user tags admin interface https://github.com/consul/consul/pull/2068 +- Added Poll comments to GraphQL API https://github.com/consul/consul/pull/2148 +- Added option to unassign Valuator role https://github.com/consul/consul/pull/2110 +- Added search by name/email on several Admin sections https://github.com/consul/consul/pull/2105 +- Added Docker support https://github.com/consul/consul/pull/2127 & documentation https://consul_docs.gitbooks.io/docs/content/en/getting_started/docker.html +- Added population restriction validation on Budget Headings https://github.com/consul/consul/pull/2115 +- Added a `/consul.json` route that returns installation details (current release version and feature flags status) for a future dashboard app https://github.com/consul/consul/pull/2164 -* New features - * Debates - * Proposals - * Basic Spending Proposals -* Enhancements - * Rails 2.4.6 - * Ruby 2.2.3 \ No newline at end of file +### Changed +- Gem versions locked & cleanup https://github.com/consul/consul/pull/1730 +- Upgraded many minor versions https://github.com/consul/consul/pull/1747 +- Rails 4.2.10 https://github.com/consul/consul/pull/2128 +- Updated Code of Conduct to use contributor covenant 1.4 https://github.com/consul/consul/pull/1733 +- Improved consistency to all "Go back" buttons https://github.com/consul/consul/pull/1770 +- New CONSUL brand https://github.com/consul/consul/pull/1808 +- Admin panel redesign https://github.com/consul/consul/pull/1875 https://github.com/consul/consul/pull/2060 +- Swapped Poll White/Null/Total Results for Poll Recount https://github.com/consul/consul/pull/1963 +- Improved Poll index view https://github.com/consul/consul/pull/1959 https://github.com/consul/consul/pull/1987 +- Update secrets and deploy secrets example files https://github.com/consul/consul/pull/1966 +- Improved Poll Officer panel features +- Consistency across all admin profiles sections https://github.com/consul/consul/pull/2089 +- Improved dev_seeds with more Poll content https://github.com/consul/consul/pull/2121 +- Comment count now updates live after publishing a new one https://github.com/consul/consul/pull/2090 + +### Removed +- Removed Tolk gem usage, we've moved to Crowdin service https://github.com/consul/consul/pull/1729 +- Removed Polls manual recounts (model Poll::FinalRecount) https://github.com/consul/consul/pull/1764 +- Skipped specs for deprecated Spending Proposal model https://github.com/consul/consul/pull/1773 +- Moved Documentation to https://github.com/consul/docs https://github.com/consul/consul/pull/1861 +- Remove Poll Officer recounts, add Final & Totals votes https://github.com/consul/consul/pull/1919 +- Remove deprecated Poll results models https://github.com/consul/consul/pull/1964 +- Remove deprecated Poll::Question valid_answers attribute & usage https://github.com/consul/consul/pull/2073 https://github.com/consul/consul/pull/2074 + +### Fixed +- Foundation settings stylesheet https://github.com/consul/consul/pull/1766 +- Budget milestone date localization https://github.com/consul/consul/pull/1734 +- Return datetime format for en locale https://github.com/consul/consul/pull/1795 +- Show bottom proposals button only if proposals exists https://github.com/consul/consul/pull/1798 +- Check SMS verification in a more consistent way https://github.com/consul/consul/pull/1832 +- Allow only YouTube/Vimeo URLs on 'video_url' attributes https://github.com/consul/consul/pull/1854 +- Remove empty comments html https://github.com/consul/consul/pull/1862 +- Fixed admin/poll routing errors https://github.com/consul/consul/pull/1863 +- Display datepicker arrows https://github.com/consul/consul/pull/1869 +- Validate presence poll presence on Poll::Question creation https://github.com/consul/consul/pull/1868 +- Switch flag/unflag buttons on use via ajax https://github.com/consul/consul/pull/1883 +- Flaky specs fixed https://github.com/consul/consul/pull/1888 +- Fixed link back from moderation dashboard to root_path https://github.com/consul/consul/pull/2132 +- Fixed Budget random pagination order https://github.com/consul/consul/pull/2131 +- Fixed `direct_messages_max_per_day` set to nil https://github.com/consul/consul/pull/2100 +- Fixed notification link error when someone commented a Topic https://github.com/consul/consul/pull/2094 +- Lots of small UI/UX/SEO/SEM improvements + +## [0.10.0](https://github.com/consul/consul/compare/v0.9...v0.10) - 2017-07-05 +### Added +- Milestones on Budget Investment's +- Feature flag to enable/disable Legislative Processes +- Locale site pages customization +- Incompatible investments + +### Changed +- Localization files reorganization. Check migration instruction at https://github.com/consul/consul/releases/tag/v0.10 +- Rails 4.2.9 + +## [0.9.0](https://github.com/consul/consul/compare/v0.8...v0.9) - 2017-06-15 +### Added +- Budgets +- Basic polls +- Collaborative legistlation +- Custom pages +- GraphQL API +- Improved admin section + +### Changed +- Improved admin section +- Rails 4.2.8 +- Ruby 2.3.2 + +### Deprecated +- SpendingProposals are deprecated now in favor of Budgets + +### Fixed +- CKEditor locale compilation fixed +- Fixed bugs in mobile layouts + +## [0.8.0](https://github.com/consul/consul/compare/v0.7...v0.8)- 2016-07-21 +### Added +- Support for customization schema, vía specific custom files, assets and folders + +### Changed +- Rails 4.2.7 +- Ruby 2.3.1 + +### Fixed +- Fixed bug causing errors on user deletion + +## [0.7.0] - 2016-04-25 +### Added +- Debates +- Proposals +- Basic Spending Proposals + +### Changed +- Rails 4.2.6 +- Ruby 2.2.3 + +[Unreleased]: https://github.com/consul/consul/compare/v0.10...consul:master +[0.10.0]: https://github.com/consul/consul/compare/v0.9...v0.10 +[0.9.0]: https://github.com/consul/consul/compare/v0.8...v0.9 +[0.8.0]: https://github.com/consul/consul/compare/v0.7...v0.8 diff --git a/Gemfile b/Gemfile index b0c7f2e9a..94002104e 100644 --- a/Gemfile +++ b/Gemfile @@ -64,7 +64,7 @@ group :development, :test do gem 'factory_girl_rails', '~> 4.8.0' gem 'faker', '~> 1.7.3' gem 'i18n-tasks', '~> 0.9.15' - gem 'knapsack', '~> 1.13.3' + gem 'knapsack_pro', '~> 0.53.0' gem 'launchy', '~> 2.4.3' gem 'letter_opener_web', '~> 1.3.1' gem 'quiet_assets', '~> 1.1.0' diff --git a/Gemfile.lock b/Gemfile.lock index 8bfbbb27c..d7d210576 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -229,9 +229,8 @@ GEM kaminari-core (= 1.0.1) kaminari-core (1.0.1) kgio (2.11.0) - knapsack (1.13.3) + knapsack_pro (0.53.0) rake - timecop (>= 0.1.0) kramdown (1.14.0) launchy (2.4.3) addressable (~> 2.3) @@ -451,7 +450,6 @@ GEM thread (0.2.2) thread_safe (0.3.6) tilt (2.0.7) - timecop (0.9.1) tins (1.15.0) turbolinks (2.5.3) coffee-rails @@ -531,7 +529,7 @@ DEPENDENCIES jquery-rails (~> 4.3.1) jquery-ui-rails (~> 6.0.1) kaminari (~> 1.0.1) - knapsack (~> 1.13.3) + knapsack_pro (~> 0.53.0) launchy (~> 2.4.3) letter_opener_web (~> 1.3.1) mdl (~> 0.4.0) @@ -573,4 +571,4 @@ DEPENDENCIES whenever (~> 0.9.7) BUNDLED WITH - 1.15.4 + 1.16.0 diff --git a/Rakefile b/Rakefile index ef2c381ff..13a99536b 100644 --- a/Rakefile +++ b/Rakefile @@ -4,4 +4,4 @@ require File.expand_path('../config/application', __FILE__) Rails.application.load_tasks -Knapsack.load_tasks if defined?(Knapsack) \ No newline at end of file +KnapsackPro.load_tasks if defined?(KnapsackPro) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 51de9c678..611ecc546 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -73,6 +73,7 @@ //= require map //= require polls //= require sortable +//= require table_sortable var initialize_modules = function() { App.Comments.initialize(); @@ -113,6 +114,7 @@ var initialize_modules = function() { App.Map.initialize(); App.Polls.initialize(); App.Sortable.initialize(); + App.TableSortable.initialize(); }; $(function(){ diff --git a/app/assets/javascripts/table_sortable.js.coffee b/app/assets/javascripts/table_sortable.js.coffee new file mode 100644 index 000000000..331f794a4 --- /dev/null +++ b/app/assets/javascripts/table_sortable.js.coffee @@ -0,0 +1,23 @@ +App.TableSortable = + getCellValue: (row, index) -> + $(row).children('td').eq(index).text() + + comparer: (index) -> + (a, b) -> + valA = App.TableSortable.getCellValue(a, index) + valB = App.TableSortable.getCellValue(b, index) + return if $.isNumeric(valA) and $.isNumeric(valB) then valA - valB else valA.localeCompare(valB) + + initialize: -> + $('table.sortable th').click -> + table = $(this).parents('table').eq(0) + rows = table.find('tr:gt(0)').not('tfoot tr').toArray().sort(App.TableSortable.comparer($(this).index())) + @asc = !@asc + if !@asc + rows = rows.reverse() + i = 0 + while i < rows.length + table.append rows[i] + i++ + return + \ No newline at end of file diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 85997aecd..a2e3e0e7f 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -270,6 +270,11 @@ $sidebar-active: #f4fcd0; } } +.sortable thead th:hover { + text-decoration: underline; + cursor: pointer; +} + // 02. Sidebar // ----------- diff --git a/app/controllers/admin/budget_investments_controller.rb b/app/controllers/admin/budget_investments_controller.rb index dd10930fb..4b6bd6b3a 100644 --- a/app/controllers/admin/budget_investments_controller.rb +++ b/app/controllers/admin/budget_investments_controller.rb @@ -51,7 +51,7 @@ class Admin::BudgetInvestmentsController < Admin::BaseController def budget_investment_params params.require(:budget_investment) - .permit(:title, :description, :external_url, :heading_id, :administrator_id, :valuation_tag_list, :incompatible, + .permit(:title, :description, :external_url, :heading_id, :administrator_id, :tag_list, :valuation_tag_list, :incompatible, :selected, valuator_ids: []) end diff --git a/app/controllers/admin/stats_controller.rb b/app/controllers/admin/stats_controller.rb index 5a50d03b4..91e266b77 100644 --- a/app/controllers/admin/stats_controller.rb +++ b/app/controllers/admin/stats_controller.rb @@ -36,4 +36,9 @@ class Admin::StatsController < Admin::BaseController @users_who_have_sent_message = DirectMessage.select(:sender_id).distinct.count end -end \ No newline at end of file + def polls + @polls = ::Poll.current + @participants = ::Poll::Voter.where(poll: @polls) + end + +end diff --git a/app/controllers/admin/valuators_controller.rb b/app/controllers/admin/valuators_controller.rb index d567a38b2..8b454a1a2 100644 --- a/app/controllers/admin/valuators_controller.rb +++ b/app/controllers/admin/valuators_controller.rb @@ -19,6 +19,11 @@ class Admin::ValuatorsController < Admin::BaseController redirect_to admin_valuators_path end + def destroy + @valuator.destroy + redirect_to admin_valuators_path + end + def summary @valuators = Valuator.order(spending_proposals_count: :desc) end diff --git a/app/controllers/budgets/investments_controller.rb b/app/controllers/budgets/investments_controller.rb index bb00f24af..5e7c1ddf0 100644 --- a/app/controllers/budgets/investments_controller.rb +++ b/app/controllers/budgets/investments_controller.rb @@ -28,8 +28,8 @@ module Budgets respond_to :html, :js def index - @investments = @investments.apply_filters_and_search(@budget, params, @current_filter) - .send("sort_by_#{@current_order}").page(params[:page]).per(10).for_render + @investments = investments.page(params[:page]).per(10).for_render + @investment_ids = @investments.pluck(:id) load_investment_votes(@investments) @tag_cloud = tag_cloud @@ -94,9 +94,8 @@ module Budgets def set_random_seed if params[:order] == 'random' || params[:order].blank? - params[:random_seed] ||= rand(99) / 100.0 - seed = Float(params[:random_seed]) rescue 0 - Budget::Investment.connection.execute("select setseed(#{seed})") + seed = rand(-100..100) / 100.0 + params[:random_seed] ||= Float(seed) rescue 0 else params[:random_seed] = nil end @@ -131,6 +130,16 @@ module Budgets TagCloud.new(Budget::Investment, params[:search]) end + def investments + if @current_order == 'random' + @investments.apply_filters_and_search(@budget, params, @current_filter) + .send("sort_by_#{@current_order}", params[:random_seed]) + else + @investments.apply_filters_and_search(@budget, params, @current_filter) + .send("sort_by_#{@current_order}") + end + end + end end diff --git a/app/controllers/installation_controller.rb b/app/controllers/installation_controller.rb new file mode 100644 index 000000000..a0bc0aa74 --- /dev/null +++ b/app/controllers/installation_controller.rb @@ -0,0 +1,24 @@ +class InstallationController < ApplicationController + + skip_authorization_check + + def details + respond_to do |format| + format.any { render json: consul_installation_details.to_json, content_type: 'application/json' } + end + end + + private + + def consul_installation_details + { + release: 'v0.11' + }.merge(features: settings_feature_flags) + end + + def settings_feature_flags + Setting.where("key LIKE 'feature.%'").each_with_object({}) { |x, n| n[x.key.remove('feature.')] = x.value } + end + +end + diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 369cabe84..3191a2ab9 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -60,4 +60,8 @@ module ApplicationHelper def format_price(number) number_to_currency(number, precision: 0, locale: I18n.default_locale) end + + def kaminari_path(url) + "#{root_url.chomp("\/")}#{url}" + end end diff --git a/app/models/abilities/administrator.rb b/app/models/abilities/administrator.rb index 951f06249..3f136f913 100644 --- a/app/models/abilities/administrator.rb +++ b/app/models/abilities/administrator.rb @@ -43,7 +43,7 @@ module Abilities can [:search, :create, :index, :destroy], ::Administrator can [:search, :create, :index, :destroy], ::Moderator - can [:search, :create, :index, :summary], ::Valuator + can [:search, :create, :index, :destroy, :summary], ::Valuator can [:search, :create, :index, :destroy], ::Manager can [:search, :index], ::User diff --git a/app/models/budget/heading.rb b/app/models/budget/heading.rb index 1a232c75e..d740db502 100644 --- a/app/models/budget/heading.rb +++ b/app/models/budget/heading.rb @@ -10,6 +10,7 @@ class Budget validates :name, presence: true, uniqueness: { if: :name_exists_in_budget_headings } validates :price, presence: true validates :slug, presence: true, format: /\A[a-z0-9\-_]+\z/ + validates :population, numericality: { greater_than: 0 }, allow_nil: true delegate :budget, :budget_id, to: :group, allow_nil: true diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb index ddf6e819c..173a7fd36 100644 --- a/app/models/budget/investment.rb +++ b/app/models/budget/investment.rb @@ -17,6 +17,7 @@ class Budget acts_as_votable acts_as_paranoid column: :hidden_at include ActsAsParanoidAliases + include Relationable belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id' belongs_to :heading @@ -43,7 +44,7 @@ class Budget scope :sort_by_confidence_score, -> { reorder(confidence_score: :desc, id: :desc) } scope :sort_by_ballots, -> { reorder(ballot_lines_count: :desc, id: :desc) } scope :sort_by_price, -> { reorder(price: :desc, confidence_score: :desc, id: :desc) } - scope :sort_by_random, -> { reorder("RANDOM()") } + scope :sort_by_random, ->(seed) { reorder("budget_investments.id % #{seed.to_f&.positive? ? seed : 1}, budget_investments.id") } scope :valuation_open, -> { where(valuation_finished: false) } scope :without_admin, -> { valuation_open.where(administrator_id: nil) } diff --git a/app/models/comment.rb b/app/models/comment.rb index 8e3024d06..ccfc04602 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -30,9 +30,11 @@ class Comment < ActiveRecord::Base scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) } scope :public_for_api, -> do where(%{(comments.commentable_type = 'Debate' and comments.commentable_id in (?)) or - (comments.commentable_type = 'Proposal' and comments.commentable_id in (?))}, + (comments.commentable_type = 'Proposal' and comments.commentable_id in (?)) or + (comments.commentable_type = 'Poll' and comments.commentable_id in (?))}, Debate.public_for_api.pluck(:id), - Proposal.public_for_api.pluck(:id)) + Proposal.public_for_api.pluck(:id), + Poll.public_for_api.pluck(:id)) end scope :sort_by_most_voted, -> { order(confidence_score: :desc, created_at: :desc) } diff --git a/app/models/concerns/relationable.rb b/app/models/concerns/relationable.rb new file mode 100644 index 000000000..26f755f03 --- /dev/null +++ b/app/models/concerns/relationable.rb @@ -0,0 +1,23 @@ +module Relationable + extend ActiveSupport::Concern + + included do + 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) + 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!(:times_reported) + related_content.opposite_related_content.increment!(:times_reported) + end + end +end diff --git a/app/models/debate.rb b/app/models/debate.rb index 65c06345d..e949e8092 100644 --- a/app/models/debate.rb +++ b/app/models/debate.rb @@ -9,6 +9,7 @@ class Debate < ActiveRecord::Base include Filterable include HasPublicAuthor include Graphqlable + include Relationable acts_as_votable acts_as_paranoid column: :hidden_at diff --git a/app/models/poll.rb b/app/models/poll.rb index 27125d8a1..ffb4e36f4 100644 --- a/app/models/poll.rb +++ b/app/models/poll.rb @@ -28,6 +28,7 @@ class Poll < ActiveRecord::Base scope :recounting, -> { Poll.where(ends_at: (Date.current.beginning_of_day - RECOUNT_DURATION)..Date.current.beginning_of_day) } scope :published, -> { where('published = ?', true) } scope :by_geozone_id, ->(geozone_id) { where(geozones: {id: geozone_id}.joins(:geozones)) } + scope :public_for_api, -> { all } scope :sort_for_list, -> { order(:geozone_restricted, :starts_at, :name) } diff --git a/app/models/poll/booth_assignment.rb b/app/models/poll/booth_assignment.rb index 8489c3cf0..81759ee0f 100644 --- a/app/models/poll/booth_assignment.rb +++ b/app/models/poll/booth_assignment.rb @@ -3,10 +3,26 @@ class Poll belongs_to :booth belongs_to :poll + before_destroy :destroy_poll_shifts, only: :destroy + has_many :officer_assignments, class_name: "Poll::OfficerAssignment", dependent: :destroy has_many :officers, through: :officer_assignments has_many :voters has_many :partial_results has_many :recounts + + def shifts? + shifts.empty? ? false : true + end + + private + + def shifts + Poll::Shift.where(booth_id: booth_id, officer_id: officer_assignments.pluck(:officer_id), date: officer_assignments.pluck(:date)) + end + + def destroy_poll_shifts + shifts.destroy_all + end end end diff --git a/app/models/proposal.rb b/app/models/proposal.rb index 3b0696d26..aaf5bea87 100644 --- a/app/models/proposal.rb +++ b/app/models/proposal.rb @@ -17,6 +17,7 @@ class Proposal < ActiveRecord::Base max_file_size: 3.megabytes, accepted_content_types: [ "application/pdf" ] include EmbedVideosHelper + include Relationable acts_as_votable acts_as_paranoid column: :hidden_at diff --git a/app/models/related_content.rb b/app/models/related_content.rb new file mode 100644 index 000000000..ab72aefe3 --- /dev/null +++ b/app/models/related_content.rb @@ -0,0 +1,33 @@ +class RelatedContent < ActiveRecord::Base + RELATED_CONTENTS_REPORT_THRESHOLD = Setting['related_contents_report_threshold'].to_i + + belongs_to :parent_relationable, polymorphic: true + belongs_to :child_relationable, polymorphic: true + has_one :opposite_related_content, class_name: 'RelatedContent', foreign_key: :related_content_id + + validates :parent_relationable_id, presence: true + validates :parent_relationable_type, presence: true + validates :child_relationable_id, presence: true + validates :child_relationable_type, presence: true + 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? } + + scope :not_hidden, -> { where('times_reported <= ?', RELATED_CONTENTS_REPORT_THRESHOLD) } + + def hidden_by_reports? + times_reported > RELATED_CONTENTS_REPORT_THRESHOLD + end + + private + + def create_opposite_related_content + related_content = RelatedContent.create!(opposite_related_content: self, parent_relationable: child_relationable, child_relationable: parent_relationable) + self.opposite_related_content = related_content + end + + def destroy_opposite_related_content + opposite_related_content.destroy + end +end diff --git a/app/views/admin/budget_investments/_written_by_author.html.erb b/app/views/admin/budget_investments/_written_by_author.html.erb index a913aff00..8cf2eca57 100644 --- a/app/views/admin/budget_investments/_written_by_author.html.erb +++ b/app/views/admin/budget_investments/_written_by_author.html.erb @@ -56,3 +56,8 @@ <% end %> <%= safe_html_with_links @investment.description %> + +
diff --git a/app/views/admin/budget_investments/edit.html.erb b/app/views/admin/budget_investments/edit.html.erb index c33039021..ab843cf24 100644 --- a/app/views/admin/budget_investments/edit.html.erb +++ b/app/views/admin/budget_investments/edit.html.erb @@ -18,6 +18,13 @@ <%= f.cktext_area :description, maxlength: Budget::Investment.description_max_length, ckeditor: { language: I18n.locale } %> +