Merge branch 'master' into aperez-email-specs-for-commentables
This commit is contained in:
@@ -10,10 +10,10 @@ before_script:
|
|||||||
- bundle exec rake db:setup
|
- bundle exec rake db:setup
|
||||||
script:
|
script:
|
||||||
- "bundle exec rake assets:precompile RAILS_ENV=test"
|
- "bundle exec rake assets:precompile RAILS_ENV=test"
|
||||||
- "bundle exec rake knapsack:rspec"
|
- "bin/knapsack_pro_rspec"
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- CI_NODE_TOTAL=2
|
- KNAPSACK_PRO_CI_NODE_TOTAL=2
|
||||||
matrix:
|
matrix:
|
||||||
- CI_NODE_INDEX=0
|
- KNAPSACK_PRO_CI_NODE_INDEX=0
|
||||||
- CI_NODE_INDEX=1
|
- KNAPSACK_PRO_CI_NODE_INDEX=1
|
||||||
|
|||||||
176
CHANGELOG.md
176
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
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||||
* Budgets
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||||
* 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
|
|
||||||
|
|
||||||
### 0.8 - 2016-07-21
|
## [Unreleased](https://github.com/consul/consul/compare/v0.11...consul:master)
|
||||||
|
|
||||||
* New features
|
## [0.11.0](https://github.com/consul/consul/compare/v0.10...v0.11) - 2017-12-05
|
||||||
* 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.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
|
### Changed
|
||||||
* Debates
|
- Gem versions locked & cleanup https://github.com/consul/consul/pull/1730
|
||||||
* Proposals
|
- Upgraded many minor versions https://github.com/consul/consul/pull/1747
|
||||||
* Basic Spending Proposals
|
- Rails 4.2.10 https://github.com/consul/consul/pull/2128
|
||||||
* Enhancements
|
- Updated Code of Conduct to use contributor covenant 1.4 https://github.com/consul/consul/pull/1733
|
||||||
* Rails 2.4.6
|
- Improved consistency to all "Go back" buttons https://github.com/consul/consul/pull/1770
|
||||||
* Ruby 2.2.3
|
- 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
|
||||||
|
|||||||
2
Gemfile
2
Gemfile
@@ -64,7 +64,7 @@ group :development, :test do
|
|||||||
gem 'factory_girl_rails', '~> 4.8.0'
|
gem 'factory_girl_rails', '~> 4.8.0'
|
||||||
gem 'faker', '~> 1.7.3'
|
gem 'faker', '~> 1.7.3'
|
||||||
gem 'i18n-tasks', '~> 0.9.15'
|
gem 'i18n-tasks', '~> 0.9.15'
|
||||||
gem 'knapsack', '~> 1.13.3'
|
gem 'knapsack_pro', '~> 0.53.0'
|
||||||
gem 'launchy', '~> 2.4.3'
|
gem 'launchy', '~> 2.4.3'
|
||||||
gem 'letter_opener_web', '~> 1.3.1'
|
gem 'letter_opener_web', '~> 1.3.1'
|
||||||
gem 'quiet_assets', '~> 1.1.0'
|
gem 'quiet_assets', '~> 1.1.0'
|
||||||
|
|||||||
@@ -229,9 +229,8 @@ GEM
|
|||||||
kaminari-core (= 1.0.1)
|
kaminari-core (= 1.0.1)
|
||||||
kaminari-core (1.0.1)
|
kaminari-core (1.0.1)
|
||||||
kgio (2.11.0)
|
kgio (2.11.0)
|
||||||
knapsack (1.13.3)
|
knapsack_pro (0.53.0)
|
||||||
rake
|
rake
|
||||||
timecop (>= 0.1.0)
|
|
||||||
kramdown (1.14.0)
|
kramdown (1.14.0)
|
||||||
launchy (2.4.3)
|
launchy (2.4.3)
|
||||||
addressable (~> 2.3)
|
addressable (~> 2.3)
|
||||||
@@ -451,7 +450,6 @@ GEM
|
|||||||
thread (0.2.2)
|
thread (0.2.2)
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
tilt (2.0.7)
|
tilt (2.0.7)
|
||||||
timecop (0.9.1)
|
|
||||||
tins (1.15.0)
|
tins (1.15.0)
|
||||||
turbolinks (2.5.3)
|
turbolinks (2.5.3)
|
||||||
coffee-rails
|
coffee-rails
|
||||||
@@ -531,7 +529,7 @@ DEPENDENCIES
|
|||||||
jquery-rails (~> 4.3.1)
|
jquery-rails (~> 4.3.1)
|
||||||
jquery-ui-rails (~> 6.0.1)
|
jquery-ui-rails (~> 6.0.1)
|
||||||
kaminari (~> 1.0.1)
|
kaminari (~> 1.0.1)
|
||||||
knapsack (~> 1.13.3)
|
knapsack_pro (~> 0.53.0)
|
||||||
launchy (~> 2.4.3)
|
launchy (~> 2.4.3)
|
||||||
letter_opener_web (~> 1.3.1)
|
letter_opener_web (~> 1.3.1)
|
||||||
mdl (~> 0.4.0)
|
mdl (~> 0.4.0)
|
||||||
@@ -573,4 +571,4 @@ DEPENDENCIES
|
|||||||
whenever (~> 0.9.7)
|
whenever (~> 0.9.7)
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.15.4
|
1.16.0
|
||||||
|
|||||||
2
Rakefile
2
Rakefile
@@ -4,4 +4,4 @@
|
|||||||
require File.expand_path('../config/application', __FILE__)
|
require File.expand_path('../config/application', __FILE__)
|
||||||
|
|
||||||
Rails.application.load_tasks
|
Rails.application.load_tasks
|
||||||
Knapsack.load_tasks if defined?(Knapsack)
|
KnapsackPro.load_tasks if defined?(KnapsackPro)
|
||||||
|
|||||||
@@ -73,6 +73,7 @@
|
|||||||
//= require map
|
//= require map
|
||||||
//= require polls
|
//= require polls
|
||||||
//= require sortable
|
//= require sortable
|
||||||
|
//= require table_sortable
|
||||||
|
|
||||||
var initialize_modules = function() {
|
var initialize_modules = function() {
|
||||||
App.Comments.initialize();
|
App.Comments.initialize();
|
||||||
@@ -113,6 +114,7 @@ var initialize_modules = function() {
|
|||||||
App.Map.initialize();
|
App.Map.initialize();
|
||||||
App.Polls.initialize();
|
App.Polls.initialize();
|
||||||
App.Sortable.initialize();
|
App.Sortable.initialize();
|
||||||
|
App.TableSortable.initialize();
|
||||||
};
|
};
|
||||||
|
|
||||||
$(function(){
|
$(function(){
|
||||||
|
|||||||
23
app/assets/javascripts/table_sortable.js.coffee
Normal file
23
app/assets/javascripts/table_sortable.js.coffee
Normal file
@@ -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
|
||||||
|
|
||||||
@@ -270,6 +270,11 @@ $sidebar-active: #f4fcd0;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sortable thead th:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
// 02. Sidebar
|
// 02. Sidebar
|
||||||
// -----------
|
// -----------
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class Admin::BudgetInvestmentsController < Admin::BaseController
|
|||||||
|
|
||||||
def budget_investment_params
|
def budget_investment_params
|
||||||
params.require(:budget_investment)
|
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: [])
|
:selected, valuator_ids: [])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -36,4 +36,9 @@ class Admin::StatsController < Admin::BaseController
|
|||||||
@users_who_have_sent_message = DirectMessage.select(:sender_id).distinct.count
|
@users_who_have_sent_message = DirectMessage.select(:sender_id).distinct.count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def polls
|
||||||
|
@polls = ::Poll.current
|
||||||
|
@participants = ::Poll::Voter.where(poll: @polls)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -19,6 +19,11 @@ class Admin::ValuatorsController < Admin::BaseController
|
|||||||
redirect_to admin_valuators_path
|
redirect_to admin_valuators_path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@valuator.destroy
|
||||||
|
redirect_to admin_valuators_path
|
||||||
|
end
|
||||||
|
|
||||||
def summary
|
def summary
|
||||||
@valuators = Valuator.order(spending_proposals_count: :desc)
|
@valuators = Valuator.order(spending_proposals_count: :desc)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ module Budgets
|
|||||||
respond_to :html, :js
|
respond_to :html, :js
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@investments = @investments.apply_filters_and_search(@budget, params, @current_filter)
|
@investments = investments.page(params[:page]).per(10).for_render
|
||||||
.send("sort_by_#{@current_order}").page(params[:page]).per(10).for_render
|
|
||||||
@investment_ids = @investments.pluck(:id)
|
@investment_ids = @investments.pluck(:id)
|
||||||
load_investment_votes(@investments)
|
load_investment_votes(@investments)
|
||||||
@tag_cloud = tag_cloud
|
@tag_cloud = tag_cloud
|
||||||
@@ -94,9 +94,8 @@ module Budgets
|
|||||||
|
|
||||||
def set_random_seed
|
def set_random_seed
|
||||||
if params[:order] == 'random' || params[:order].blank?
|
if params[:order] == 'random' || params[:order].blank?
|
||||||
params[:random_seed] ||= rand(99) / 100.0
|
seed = rand(-100..100) / 100.0
|
||||||
seed = Float(params[:random_seed]) rescue 0
|
params[:random_seed] ||= Float(seed) rescue 0
|
||||||
Budget::Investment.connection.execute("select setseed(#{seed})")
|
|
||||||
else
|
else
|
||||||
params[:random_seed] = nil
|
params[:random_seed] = nil
|
||||||
end
|
end
|
||||||
@@ -131,6 +130,16 @@ module Budgets
|
|||||||
TagCloud.new(Budget::Investment, params[:search])
|
TagCloud.new(Budget::Investment, params[:search])
|
||||||
end
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
24
app/controllers/installation_controller.rb
Normal file
24
app/controllers/installation_controller.rb
Normal file
@@ -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
|
||||||
|
|
||||||
@@ -60,4 +60,8 @@ module ApplicationHelper
|
|||||||
def format_price(number)
|
def format_price(number)
|
||||||
number_to_currency(number, precision: 0, locale: I18n.default_locale)
|
number_to_currency(number, precision: 0, locale: I18n.default_locale)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def kaminari_path(url)
|
||||||
|
"#{root_url.chomp("\/")}#{url}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ module Abilities
|
|||||||
|
|
||||||
can [:search, :create, :index, :destroy], ::Administrator
|
can [:search, :create, :index, :destroy], ::Administrator
|
||||||
can [:search, :create, :index, :destroy], ::Moderator
|
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, :create, :index, :destroy], ::Manager
|
||||||
can [:search, :index], ::User
|
can [:search, :index], ::User
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class Budget
|
|||||||
validates :name, presence: true, uniqueness: { if: :name_exists_in_budget_headings }
|
validates :name, presence: true, uniqueness: { if: :name_exists_in_budget_headings }
|
||||||
validates :price, presence: true
|
validates :price, presence: true
|
||||||
validates :slug, presence: true, format: /\A[a-z0-9\-_]+\z/
|
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
|
delegate :budget, :budget_id, to: :group, allow_nil: true
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class Budget
|
|||||||
acts_as_votable
|
acts_as_votable
|
||||||
acts_as_paranoid column: :hidden_at
|
acts_as_paranoid column: :hidden_at
|
||||||
include ActsAsParanoidAliases
|
include ActsAsParanoidAliases
|
||||||
|
include Relationable
|
||||||
|
|
||||||
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
|
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
|
||||||
belongs_to :heading
|
belongs_to :heading
|
||||||
@@ -43,7 +44,7 @@ class Budget
|
|||||||
scope :sort_by_confidence_score, -> { reorder(confidence_score: :desc, id: :desc) }
|
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_ballots, -> { reorder(ballot_lines_count: :desc, id: :desc) }
|
||||||
scope :sort_by_price, -> { reorder(price: :desc, confidence_score: :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 :valuation_open, -> { where(valuation_finished: false) }
|
||||||
scope :without_admin, -> { valuation_open.where(administrator_id: nil) }
|
scope :without_admin, -> { valuation_open.where(administrator_id: nil) }
|
||||||
|
|||||||
@@ -30,9 +30,11 @@ class Comment < ActiveRecord::Base
|
|||||||
scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) }
|
scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) }
|
||||||
scope :public_for_api, -> do
|
scope :public_for_api, -> do
|
||||||
where(%{(comments.commentable_type = 'Debate' and comments.commentable_id in (?)) or
|
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),
|
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
|
end
|
||||||
|
|
||||||
scope :sort_by_most_voted, -> { order(confidence_score: :desc, created_at: :desc) }
|
scope :sort_by_most_voted, -> { order(confidence_score: :desc, created_at: :desc) }
|
||||||
|
|||||||
23
app/models/concerns/relationable.rb
Normal file
23
app/models/concerns/relationable.rb
Normal file
@@ -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
|
||||||
@@ -9,6 +9,7 @@ class Debate < ActiveRecord::Base
|
|||||||
include Filterable
|
include Filterable
|
||||||
include HasPublicAuthor
|
include HasPublicAuthor
|
||||||
include Graphqlable
|
include Graphqlable
|
||||||
|
include Relationable
|
||||||
|
|
||||||
acts_as_votable
|
acts_as_votable
|
||||||
acts_as_paranoid column: :hidden_at
|
acts_as_paranoid column: :hidden_at
|
||||||
|
|||||||
@@ -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 :recounting, -> { Poll.where(ends_at: (Date.current.beginning_of_day - RECOUNT_DURATION)..Date.current.beginning_of_day) }
|
||||||
scope :published, -> { where('published = ?', true) }
|
scope :published, -> { where('published = ?', true) }
|
||||||
scope :by_geozone_id, ->(geozone_id) { where(geozones: {id: geozone_id}.joins(:geozones)) }
|
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) }
|
scope :sort_for_list, -> { order(:geozone_restricted, :starts_at, :name) }
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,26 @@ class Poll
|
|||||||
belongs_to :booth
|
belongs_to :booth
|
||||||
belongs_to :poll
|
belongs_to :poll
|
||||||
|
|
||||||
|
before_destroy :destroy_poll_shifts, only: :destroy
|
||||||
|
|
||||||
has_many :officer_assignments, class_name: "Poll::OfficerAssignment", dependent: :destroy
|
has_many :officer_assignments, class_name: "Poll::OfficerAssignment", dependent: :destroy
|
||||||
has_many :officers, through: :officer_assignments
|
has_many :officers, through: :officer_assignments
|
||||||
has_many :voters
|
has_many :voters
|
||||||
has_many :partial_results
|
has_many :partial_results
|
||||||
has_many :recounts
|
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
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class Proposal < ActiveRecord::Base
|
|||||||
max_file_size: 3.megabytes,
|
max_file_size: 3.megabytes,
|
||||||
accepted_content_types: [ "application/pdf" ]
|
accepted_content_types: [ "application/pdf" ]
|
||||||
include EmbedVideosHelper
|
include EmbedVideosHelper
|
||||||
|
include Relationable
|
||||||
|
|
||||||
acts_as_votable
|
acts_as_votable
|
||||||
acts_as_paranoid column: :hidden_at
|
acts_as_paranoid column: :hidden_at
|
||||||
|
|||||||
33
app/models/related_content.rb
Normal file
33
app/models/related_content.rb
Normal file
@@ -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
|
||||||
@@ -56,3 +56,8 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= safe_html_with_links @investment.description %>
|
<%= safe_html_with_links @investment.description %>
|
||||||
|
|
||||||
|
<p id="user-tags">
|
||||||
|
<strong><%= t("admin.budget_investments.show.user_tags") %>: </strong>
|
||||||
|
<%= @investment.tag_list.sort.join(', ') %>
|
||||||
|
</p>
|
||||||
|
|||||||
@@ -18,6 +18,13 @@
|
|||||||
<%= f.cktext_area :description, maxlength: Budget::Investment.description_max_length, ckeditor: { language: I18n.locale } %>
|
<%= f.cktext_area :description, maxlength: Budget::Investment.description_max_length, ckeditor: { language: I18n.locale } %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="small-12 column">
|
||||||
|
<%= f.label :tag_list, t("admin.budget_investments.edit.user_tags") %>
|
||||||
|
<%= f.text_field :tag_list,
|
||||||
|
value: @investment.tag_list.sort.join(','),
|
||||||
|
label: false %>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="small-12 column">
|
<div class="small-12 column">
|
||||||
<%= f.text_field :external_url %>
|
<%= f.text_field :external_url %>
|
||||||
</div>
|
</div>
|
||||||
@@ -39,7 +46,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="small-12 column">
|
<div class="small-12 column">
|
||||||
<%= f.label :tag_list, t("admin.budget_investments.edit.tags") %>
|
<%= f.label :valuation_tag_list, t("admin.budget_investments.edit.tags") %>
|
||||||
<div class="tags">
|
<div class="tags">
|
||||||
<% @tags.each do |tag| %>
|
<% @tags.each do |tag| %>
|
||||||
<a class="js-add-tag-link"><%= tag.name %></a>
|
<a class="js-add-tag-link"><%= tag.name %></a>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<p id="tags">
|
<p id="tags">
|
||||||
<strong><%= t("admin.budget_investments.show.tags") %>:</strong>
|
<strong><%= t("admin.budget_investments.show.tags") %>:</strong>
|
||||||
|
|
||||||
<%= @investment.tags_on(:valuation).pluck(:name).join(', ') %>
|
<%= @investment.tags_on(:valuation).pluck(:name).sort.join(', ') %>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p id="assigned_valuators">
|
<p id="assigned_valuators">
|
||||||
|
|||||||
@@ -49,10 +49,17 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="small-12 medium-6 column">
|
<div class="small-12 medium-6 column">
|
||||||
<label><%= t("admin.budgets.form.population") %></label>
|
<label><%= t("admin.budgets.form.population") %></label>
|
||||||
<%= f.text_field :population,
|
<%= f.number_field :population,
|
||||||
label: false,
|
label: false,
|
||||||
maxlength: 8,
|
maxlength: 8,
|
||||||
placeholder: t("admin.budgets.form.population") %>
|
min: 1,
|
||||||
|
placeholder: t("admin.budgets.form.population"),
|
||||||
|
data: {toggle_focus: "population-info"} %>
|
||||||
|
</div>
|
||||||
|
<div class="small-12 medium-6 column " >
|
||||||
|
<div id="population-info" class="is-hidden" data-toggler="is-hidden">
|
||||||
|
<%= t("admin.budgets.form.population_info") %>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
method: :delete,
|
method: :delete,
|
||||||
remote: true,
|
remote: true,
|
||||||
title: t("admin.booth_assignments.manage.actions.unassign"),
|
title: t("admin.booth_assignments.manage.actions.unassign"),
|
||||||
class: "button hollow alert" %>
|
class: "button hollow alert",
|
||||||
|
data: (booth_assignment.shifts? ? {confirm: "#{t("admin.poll_booth_assignments.alert.shifts")}"} : nil) if !@poll.expired? %>
|
||||||
</td>
|
</td>
|
||||||
<% else %>
|
<% else %>
|
||||||
<td>
|
<td>
|
||||||
@@ -26,6 +27,6 @@
|
|||||||
method: :post,
|
method: :post,
|
||||||
remote: true,
|
remote: true,
|
||||||
title: t("admin.booth_assignments.manage.actions.assign"),
|
title: t("admin.booth_assignments.manage.actions.assign"),
|
||||||
class: "button" %>
|
class: "button" if !@poll.expired? %>
|
||||||
</td>
|
</td>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
87
app/views/admin/stats/polls.html.erb
Normal file
87
app/views/admin/stats/polls.html.erb
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<%= back_link_to %>
|
||||||
|
|
||||||
|
<h2 id="top"><%= t("admin.stats.polls.title")%></h2>
|
||||||
|
|
||||||
|
<div class="stats">
|
||||||
|
<div class="row stats-numbers">
|
||||||
|
<div class="small-12 medium-3 column">
|
||||||
|
<p class="featured">
|
||||||
|
<%= t("admin.stats.polls.web_participants") %><br>
|
||||||
|
<span id="web_participants" class="number">
|
||||||
|
<%= @participants.web.select(:user_id).distinct.count %>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="small-12 medium-3 column end">
|
||||||
|
<p class="featured">
|
||||||
|
<%= t("admin.stats.polls.total_participants") %><br>
|
||||||
|
<span id="participants" class="number">
|
||||||
|
<%= @participants.select(:user_id).distinct.count %>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2><%= t("admin.stats.polls.all") %></h2>
|
||||||
|
<table id="polls" class="stack sortable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><%= t("admin.stats.polls.table.poll_name") %></th>
|
||||||
|
<th class="name text-right"><%= t("admin.stats.polls.total_participants") %></th>
|
||||||
|
<th class="name text-right"><%= t("admin.stats.polls.table.origin_web") %></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<% @polls.each do |poll| %>
|
||||||
|
<tr id="<%= dom_id(poll) %>">
|
||||||
|
<td class="name">
|
||||||
|
<a href="#<%= dom_id(poll) %>_questions"><%= poll.name %></a>
|
||||||
|
</td>
|
||||||
|
<td class="name text-right">
|
||||||
|
<%= poll.voters.select(:user_id).distinct.count %>
|
||||||
|
</td>
|
||||||
|
<td class="name text-right">
|
||||||
|
<%= poll.voters.web.select(:user_id).distinct.count %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<% @polls.each do |poll| %>
|
||||||
|
<h3 id="<%= dom_id(poll) %>_questions">
|
||||||
|
<%= t("admin.stats.polls.poll_questions", poll: poll.name) %>
|
||||||
|
</h3>
|
||||||
|
<table class="stack sortable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><%= t("admin.stats.polls.table.question_name") %></th>
|
||||||
|
<th class="name text-right">
|
||||||
|
<%= t("admin.stats.polls.table.origin_web") %>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<% poll.questions.each do |question| %>
|
||||||
|
<tr id="<%= dom_id(question) %>">
|
||||||
|
<td class="name">
|
||||||
|
<%= question.title %>
|
||||||
|
</td>
|
||||||
|
<td class="name text-right">
|
||||||
|
<%= ::Poll::Answer.by_question(question).count %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
<tfoot>
|
||||||
|
<tr id="<%= dom_id(poll) %>_questions_total">
|
||||||
|
<th></th>
|
||||||
|
<th class="name text-right">
|
||||||
|
<strong>
|
||||||
|
<%= t("admin.stats.polls.table.origin_total") %>:
|
||||||
|
<%= ::Poll::Answer.where(question: poll.questions)
|
||||||
|
.select(:author_id).distinct.count %>
|
||||||
|
</strong>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
<% end %>
|
||||||
@@ -7,6 +7,8 @@
|
|||||||
<h1 class="inline-block"><%= t "admin.stats.show.stats_title" %></h1>
|
<h1 class="inline-block"><%= t "admin.stats.show.stats_title" %></h1>
|
||||||
|
|
||||||
<div class="float-right clear">
|
<div class="float-right clear">
|
||||||
|
<%= link_to t("admin.stats.show.polls"),
|
||||||
|
polls_admin_stats_path, class: "button hollow" %>
|
||||||
<%= link_to t("admin.stats.show.direct_messages"),
|
<%= link_to t("admin.stats.show.direct_messages"),
|
||||||
direct_messages_admin_stats_path, class: "button hollow" %>
|
direct_messages_admin_stats_path, class: "button hollow" %>
|
||||||
<%= link_to t("admin.stats.show.proposal_notifications"),
|
<%= link_to t("admin.stats.show.proposal_notifications"),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<h2><%= t("admin.valuators.index.title") %></h2>
|
<h2><%= t("admin.valuators.index.title") %></h2>
|
||||||
|
|
||||||
<%= render 'admin/shared/user_search', url: search_admin_valuators_path %>
|
<%= render "admin/shared/user_search", url: search_admin_valuators_path %>
|
||||||
|
|
||||||
<div id="valuators">
|
<div id="valuators">
|
||||||
<% if @valuators.any? %>
|
<% if @valuators.any? %>
|
||||||
@@ -11,16 +11,13 @@
|
|||||||
<th scope="col"><%= t("admin.valuators.index.name") %></th>
|
<th scope="col"><%= t("admin.valuators.index.name") %></th>
|
||||||
<th scope="col"><%= t("admin.valuators.index.email") %></th>
|
<th scope="col"><%= t("admin.valuators.index.email") %></th>
|
||||||
<th scope="col"><%= t("admin.valuators.index.description") %></th>
|
<th scope="col"><%= t("admin.valuators.index.description") %></th>
|
||||||
|
<th scope="col"><%= t("admin.actions.actions") %></th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<% @valuators.each do |valuator| %>
|
<% @valuators.each do |valuator| %>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td><%= valuator.name %></td>
|
||||||
<%= valuator.name %>
|
<td><%= valuator.email %></td>
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<%= valuator.email %>
|
|
||||||
</td>
|
|
||||||
<td>
|
<td>
|
||||||
<% if valuator.description.present? %>
|
<% if valuator.description.present? %>
|
||||||
<%= valuator.description %>
|
<%= valuator.description %>
|
||||||
@@ -28,6 +25,12 @@
|
|||||||
<%= t("admin.valuators.index.no_description") %>
|
<%= t("admin.valuators.index.no_description") %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<%= link_to t("admin.valuators.valuator.delete"),
|
||||||
|
admin_valuator_path(valuator),
|
||||||
|
method: :delete,
|
||||||
|
class: "button hollow alert expanded" %>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -29,7 +29,12 @@
|
|||||||
<%= t("admin.valuators.index.no_description") %>
|
<%= t("admin.valuators.index.no_description") %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<td>
|
<td>
|
||||||
<% unless user.valuator? %>
|
<% if user.valuator? %>
|
||||||
|
<%= link_to t("admin.valuators.valuator.delete"),
|
||||||
|
admin_valuator_path(user),
|
||||||
|
method: :delete,
|
||||||
|
class: "button hollow alert expanded" %>
|
||||||
|
<% else %>
|
||||||
<%= form_for Valuator.new(user: user), url: admin_valuators_path do |f| %>
|
<%= form_for Valuator.new(user: user), url: admin_valuators_path do |f| %>
|
||||||
<%= f.text_field :description,
|
<%= f.text_field :description,
|
||||||
label: false,
|
label: false,
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<li>
|
<li>
|
||||||
<%= link_to t("views.pagination.first").html_safe, url, :remote => remote %>
|
<%= link_to t("views.pagination.first").html_safe, kaminari_path(url), :remote => remote %>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<li>
|
<li>
|
||||||
<%= link_to t("views.pagination.last").html_safe, url, :remote => remote %>
|
<%= link_to t("views.pagination.last").html_safe, kaminari_path(url), :remote => remote %>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<li class="pagination-next">
|
<li class="pagination-next">
|
||||||
<%= link_to t("views.pagination.next").html_safe, url, :rel => "next", :remote => remote %>
|
<%= link_to t("views.pagination.next").html_safe, kaminari_path(url), :rel => "next", :remote => remote %>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -5,6 +5,6 @@
|
|||||||
</li>
|
</li>
|
||||||
<% else %>
|
<% else %>
|
||||||
<li>
|
<li>
|
||||||
<%= link_to page, url, {:remote => remote, :rel => page.next? ? "next" : page.prev? ? "prev" : nil} %>
|
<%= link_to page, kaminari_path(url), {:remote => remote, :rel => page.next? ? "next" : page.prev? ? "prev" : nil} %>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<li class="pagination-previous">
|
<li class="pagination-previous">
|
||||||
<%= link_to t("views.pagination.previous").html_safe, url, :rel => "prev", :remote => remote %>
|
<%= link_to t("views.pagination.previous").html_safe, kaminari_path(url), :rel => "prev", :remote => remote %>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<% if Setting['feature.budgets'] %>
|
||||||
<li <%= "class=active" if (controller_name == "budget_investments" && action_name == "new") ||
|
<li <%= "class=active" if (controller_name == "budget_investments" && action_name == "new") ||
|
||||||
(controller_name == "budget" && action_name == 'create_investments') %>>
|
(controller_name == "budget" && action_name == 'create_investments') %>>
|
||||||
<%= link_to create_investments_management_budgets_path do %>
|
<%= link_to create_investments_management_budgets_path do %>
|
||||||
@@ -60,6 +61,15 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li <%= "class=active" if (controller_name == "budget_investments" && action_name == "print") ||
|
||||||
|
(controller_name == "budgets" && action_name == "print_investments") %>>
|
||||||
|
<%= link_to print_investments_management_budgets_path do %>
|
||||||
|
<span class="icon-print"></span>
|
||||||
|
<%= t("management.menu.print_budget_investments") %>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<li <%= "class=active" if controller_name == "proposals" && action_name == "print" %>>
|
<li <%= "class=active" if controller_name == "proposals" && action_name == "print" %>>
|
||||||
<%= link_to print_management_proposals_path do %>
|
<%= link_to print_management_proposals_path do %>
|
||||||
<span class="icon-print"></span>
|
<span class="icon-print"></span>
|
||||||
@@ -74,15 +84,6 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li <%= "class=active" if (controller_name == "budget_investments" && action_name == "print") ||
|
|
||||||
(controller_name == "budgets" && action_name == "print_investments") %>>
|
|
||||||
<%= link_to print_investments_management_budgets_path do %>
|
|
||||||
<span class="icon-print"></span>
|
|
||||||
<%= t("management.menu.print_budget_investments") %>
|
|
||||||
<% end %>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<%= link_to new_management_user_invite_path do %>
|
<%= link_to new_management_user_invite_path do %>
|
||||||
<span class="icon-letter"></span>
|
<span class="icon-letter"></span>
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
<!-- Twitter -->
|
<!-- Twitter -->
|
||||||
<meta name="twitter:card" content="summary" />
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:site" content="@consul_dev" />
|
<meta name="twitter:site" content="<%= setting['twitter_handle'] %>"/>
|
||||||
<meta name="twitter:title" content="<%= social_title %>" />
|
<meta name="twitter:title" content="<%= local_assigns[:social_title] || setting['meta_title'] %>"/>
|
||||||
<meta name="twitter:description" content="<%= social_description %>" />
|
<meta name="twitter:description" content="<%= local_assigns[:social_description] || setting['meta_description'] %>"/>
|
||||||
<meta name="twitter:image" content="<%= image_url local_assigns[:twitter_image_url] || image_path_for('social_media_icon_twitter.png') %>" />
|
<meta name="twitter:image" content="<%= root_url + (local_assigns[:twitter_image_url] || 'social_media_icon_twitter.png') %>"/>
|
||||||
<!-- Facebook OG -->
|
<!-- Facebook OG -->
|
||||||
<meta id="ogtitle" property="og:title" content="<%= social_title %>"/>
|
<meta id="ogtitle" property="og:title" content="<%= local_assigns[:social_title] || setting['meta_title'] %>"/>
|
||||||
<% if setting['url'] %>
|
<% if setting['url'] %>
|
||||||
<meta property="article:publisher" content=<%= setting['url'] %>/>
|
<meta property="article:publisher" content="<%= setting['url'] %>"/>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if setting['facebook_handle'] %>
|
<% if setting['facebook_handle'] %>
|
||||||
<meta property="article:author" content="https://www.facebook.com/<%= setting['facebook_handle'] %>"/>
|
<meta property="article:author" content="https://www.facebook.com/<%= setting['facebook_handle'] %>"/>
|
||||||
<% end %>
|
<% end %>
|
||||||
<meta property="og:type" content="article"/>
|
<meta property="og:type" content="article"/>
|
||||||
<meta id="ogurl" property="og:url" content="<%= social_url %>"/>
|
<meta id="ogurl" property="og:url" content="<%= social_url %>"/>
|
||||||
<meta id="ogimage" property="og:image" content="<%= image_url local_assigns[:og_image_url] || image_path_for('social_media_icon.png') %>"/>
|
<meta id="ogimage" property="og:image" content="<%= root_url + (local_assigns[:og_image_url] || 'social_media_icon.png') %>"/>
|
||||||
<meta property="og:site_name" content="<%= setting['org_name'] %>"/>
|
<meta property="og:site_name" content="<%= setting['org_name'] %>"/>
|
||||||
<meta id="ogdescription" property="og:description" content="<%= social_description %>"/>
|
<meta id="ogdescription" property="og:description" content="<%= local_assigns[:social_description] || setting['meta_description'] %>"/>
|
||||||
<meta property="fb:app_id" content="<%= Rails.application.secrets.facebook_key %>"/>
|
<meta property="fb:app_id" content="<%= Rails.application.secrets.facebook_key %>"/>
|
||||||
|
|||||||
@@ -4,6 +4,11 @@
|
|||||||
<%= render "shared/canonical", href: root_url %>
|
<%= render "shared/canonical", href: root_url %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<% provide :social_media_meta_tags do %>
|
||||||
|
<%= render "shared/social_media_meta_tags",
|
||||||
|
social_url: root_url %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<div class="jumbo highlight">
|
<div class="jumbo highlight">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="small-12 medium-9 small-centered column text-center">
|
<div class="small-12 medium-9 small-centered column text-center">
|
||||||
|
|||||||
9
bin/knapsack_pro_rspec
Executable file
9
bin/knapsack_pro_rspec
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
if [ "$KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC" = "" ]; then
|
||||||
|
KNAPSACK_PRO_ENDPOINT=https://api-disabled-for-fork.knapsackpro.com \
|
||||||
|
KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC=disabled-for-fork \
|
||||||
|
bundle exec rake knapsack_pro:rspec # use Regular Mode here always
|
||||||
|
else
|
||||||
|
# Queue Mode - dynamic tests allocation across CI nodes
|
||||||
|
bundle exec rake knapsack_pro:queue:rspec
|
||||||
|
fi
|
||||||
@@ -288,3 +288,8 @@ en:
|
|||||||
image:
|
image:
|
||||||
image_width: "Width must be %{required_width}px"
|
image_width: "Width must be %{required_width}px"
|
||||||
image_height: "Height must be %{required_height}px"
|
image_height: "Height must be %{required_height}px"
|
||||||
|
messages:
|
||||||
|
record_invalid: "Validation failed: %{errors}"
|
||||||
|
restrict_dependent_destroy:
|
||||||
|
has_one: "Cannot delete record because a dependent %{record} exists"
|
||||||
|
has_many: "Cannot delete record because dependent %{record} exist"
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ en:
|
|||||||
table_heading: Heading
|
table_heading: Heading
|
||||||
table_amount: Amount
|
table_amount: Amount
|
||||||
table_population: Population
|
table_population: Population
|
||||||
|
population_info: "Budget Heading population field is used for Statistic purposes at the end of the Budget to show for each Heading that represents an area with population what percentage voted. The field is optional so you can leave it empty if it doesn't apply."
|
||||||
winners:
|
winners:
|
||||||
calculate: Calculate Winner Investments
|
calculate: Calculate Winner Investments
|
||||||
calculated: Winners being calculated, it may take a minute.
|
calculated: Winners being calculated, it may take a minute.
|
||||||
@@ -157,6 +158,7 @@ en:
|
|||||||
dossier: Dossier
|
dossier: Dossier
|
||||||
edit_dossier: Edit dossier
|
edit_dossier: Edit dossier
|
||||||
tags: Tags
|
tags: Tags
|
||||||
|
user_tags: User tags
|
||||||
undefined: Undefined
|
undefined: Undefined
|
||||||
milestone: Milestone
|
milestone: Milestone
|
||||||
new_milestone: Create new milestone
|
new_milestone: Create new milestone
|
||||||
@@ -181,6 +183,7 @@ en:
|
|||||||
assigned_valuators: Valuators
|
assigned_valuators: Valuators
|
||||||
select_heading: Select heading
|
select_heading: Select heading
|
||||||
submit_button: Update
|
submit_button: Update
|
||||||
|
user_tags: User assigned tags
|
||||||
tags: Tags
|
tags: Tags
|
||||||
tags_placeholder: "Write the tags you want separated by commas (,)"
|
tags_placeholder: "Write the tags you want separated by commas (,)"
|
||||||
undefined: Undefined
|
undefined: Undefined
|
||||||
@@ -481,6 +484,7 @@ en:
|
|||||||
valuator:
|
valuator:
|
||||||
description_placeholder: 'Description (optional)'
|
description_placeholder: 'Description (optional)'
|
||||||
add: Add to valuators
|
add: Add to valuators
|
||||||
|
delete: Delete
|
||||||
search:
|
search:
|
||||||
title: 'Valuators: User search'
|
title: 'Valuators: User search'
|
||||||
summary:
|
summary:
|
||||||
@@ -555,6 +559,8 @@ en:
|
|||||||
assign: Assign booth
|
assign: Assign booth
|
||||||
unassign: Unassign booth
|
unassign: Unassign booth
|
||||||
poll_booth_assignments:
|
poll_booth_assignments:
|
||||||
|
alert:
|
||||||
|
shifts: "There are shifts associated to this booth. If you remove the booth assignment, the shifts will be also deleted. Continue?"
|
||||||
flash:
|
flash:
|
||||||
destroy: "Booth not assigned anymore"
|
destroy: "Booth not assigned anymore"
|
||||||
create: "Booth assigned"
|
create: "Booth assigned"
|
||||||
@@ -952,6 +958,7 @@ en:
|
|||||||
direct_messages: Direct messages
|
direct_messages: Direct messages
|
||||||
proposal_notifications: Proposal notifications
|
proposal_notifications: Proposal notifications
|
||||||
incomplete_verifications: Incomplete verifications
|
incomplete_verifications: Incomplete verifications
|
||||||
|
polls: Polls
|
||||||
direct_messages:
|
direct_messages:
|
||||||
title: Direct messages
|
title: Direct messages
|
||||||
total: Total
|
total: Total
|
||||||
@@ -960,6 +967,17 @@ en:
|
|||||||
title: Proposal notifications
|
title: Proposal notifications
|
||||||
total: Total
|
total: Total
|
||||||
proposals_with_notifications: Proposals with notifications
|
proposals_with_notifications: Proposals with notifications
|
||||||
|
polls:
|
||||||
|
title: Poll Stats
|
||||||
|
all: Polls
|
||||||
|
web_participants: Web participants
|
||||||
|
total_participants: Total Participants
|
||||||
|
poll_questions: "Questions from poll: %{poll}"
|
||||||
|
table:
|
||||||
|
poll_name: Poll
|
||||||
|
question_name: Question
|
||||||
|
origin_web: Web participants
|
||||||
|
origin_total: Total participants
|
||||||
tags:
|
tags:
|
||||||
create: Create topic
|
create: Create topic
|
||||||
destroy: Destroy topic
|
destroy: Destroy topic
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ en:
|
|||||||
map_zoom: Zoom
|
map_zoom: Zoom
|
||||||
mailer_from_name: Origin email name
|
mailer_from_name: Origin email name
|
||||||
mailer_from_address: Origin email address
|
mailer_from_address: Origin email address
|
||||||
|
meta_title: "Site title (SEO)"
|
||||||
meta_description: "Site description (SEO)"
|
meta_description: "Site description (SEO)"
|
||||||
meta_keywords: "Keywords (SEO)"
|
meta_keywords: "Keywords (SEO)"
|
||||||
verification_offices_url: Verification offices URL
|
verification_offices_url: Verification offices URL
|
||||||
|
|||||||
@@ -283,3 +283,8 @@ es:
|
|||||||
image:
|
image:
|
||||||
image_width: "Debe tener %{required_width}px de ancho"
|
image_width: "Debe tener %{required_width}px de ancho"
|
||||||
image_height: "Debe tener %{required_height}px de alto"
|
image_height: "Debe tener %{required_height}px de alto"
|
||||||
|
messages:
|
||||||
|
record_invalid: "La validación falló: %{errors}"
|
||||||
|
restrict_dependent_destroy:
|
||||||
|
has_one: No se puede eliminar el registro porque existe un %{record} dependiente
|
||||||
|
has_many: No se puede eliminar el registro porque existen %{record} dependientes
|
||||||
@@ -104,6 +104,7 @@ es:
|
|||||||
table_heading: Partida
|
table_heading: Partida
|
||||||
table_amount: Cantidad
|
table_amount: Cantidad
|
||||||
table_population: Población
|
table_population: Población
|
||||||
|
population_info: "El campo población de las partidas presupuestarias se usa con fines estadísticos únicamente, con el objetivo de mostrar el porcentaje de votos habidos en cada partida que represente un área con población. Es un campo opcional, así que puedes dejarlo en blanco si no aplica."
|
||||||
winners:
|
winners:
|
||||||
calculate: Calcular propuestas ganadoras
|
calculate: Calcular propuestas ganadoras
|
||||||
calculated: Calculando ganadoras, puede tardar un minuto.
|
calculated: Calculando ganadoras, puede tardar un minuto.
|
||||||
@@ -157,6 +158,7 @@ es:
|
|||||||
dossier: Informe
|
dossier: Informe
|
||||||
edit_dossier: Editar informe
|
edit_dossier: Editar informe
|
||||||
tags: Etiquetas
|
tags: Etiquetas
|
||||||
|
user_tags: Etiquetas del usuario
|
||||||
undefined: Sin definir
|
undefined: Sin definir
|
||||||
milestone: Seguimiento
|
milestone: Seguimiento
|
||||||
new_milestone: Crear nuevo hito
|
new_milestone: Crear nuevo hito
|
||||||
@@ -181,6 +183,7 @@ es:
|
|||||||
assigned_valuators: Evaluadores
|
assigned_valuators: Evaluadores
|
||||||
select_heading: Seleccionar partida
|
select_heading: Seleccionar partida
|
||||||
submit_button: Actualizar
|
submit_button: Actualizar
|
||||||
|
user_tags: Etiquetas asignadas por el usuario
|
||||||
tags: Etiquetas
|
tags: Etiquetas
|
||||||
tags_placeholder: "Escribe las etiquetas que desees separadas por comas (,)"
|
tags_placeholder: "Escribe las etiquetas que desees separadas por comas (,)"
|
||||||
undefined: Sin definir
|
undefined: Sin definir
|
||||||
@@ -481,6 +484,7 @@ es:
|
|||||||
valuator:
|
valuator:
|
||||||
description_placeholder: 'Descripción (opcional)'
|
description_placeholder: 'Descripción (opcional)'
|
||||||
add: Añadir como evaluador
|
add: Añadir como evaluador
|
||||||
|
delete: Borrar
|
||||||
search:
|
search:
|
||||||
title: 'Evaluadores: Búsqueda de usuarios'
|
title: 'Evaluadores: Búsqueda de usuarios'
|
||||||
summary:
|
summary:
|
||||||
@@ -553,8 +557,10 @@ es:
|
|||||||
unassigned: No asignada
|
unassigned: No asignada
|
||||||
actions:
|
actions:
|
||||||
assign: Asignar urna
|
assign: Asignar urna
|
||||||
unassign: Asignar urna
|
unassign: Desasignar urna
|
||||||
poll_booth_assignments:
|
poll_booth_assignments:
|
||||||
|
alert:
|
||||||
|
shifts: "Hay turnos asignados para esta urna. Si la desasignas, esos turnos se eliminarán. ¿Deseas continuar?"
|
||||||
flash:
|
flash:
|
||||||
destroy: "Urna desasignada"
|
destroy: "Urna desasignada"
|
||||||
create: "Urna asignada"
|
create: "Urna asignada"
|
||||||
@@ -952,6 +958,7 @@ es:
|
|||||||
direct_messages: Mensajes directos
|
direct_messages: Mensajes directos
|
||||||
proposal_notifications: Notificaciones de propuestas
|
proposal_notifications: Notificaciones de propuestas
|
||||||
incomplete_verifications: Verificaciones incompletas
|
incomplete_verifications: Verificaciones incompletas
|
||||||
|
polls: Votaciones
|
||||||
direct_messages:
|
direct_messages:
|
||||||
title: Mensajes directos
|
title: Mensajes directos
|
||||||
total: Total
|
total: Total
|
||||||
@@ -960,6 +967,17 @@ es:
|
|||||||
title: Notificaciones de propuestas
|
title: Notificaciones de propuestas
|
||||||
total: Total
|
total: Total
|
||||||
proposals_with_notifications: Propuestas con notificaciones
|
proposals_with_notifications: Propuestas con notificaciones
|
||||||
|
polls:
|
||||||
|
title: Estadísticas de votaciones
|
||||||
|
all: Votaciones
|
||||||
|
web_participants: Participantes en Web
|
||||||
|
total_participants: Participantes totales
|
||||||
|
poll_questions: "Preguntas de votación: %{poll}"
|
||||||
|
table:
|
||||||
|
poll_name: Votación
|
||||||
|
question_name: Pregunta
|
||||||
|
origin_web: Participantes Web
|
||||||
|
origin_total: Participantes Totales
|
||||||
tags:
|
tags:
|
||||||
create: Crear tema
|
create: Crear tema
|
||||||
destroy: Eliminar tema
|
destroy: Eliminar tema
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ es:
|
|||||||
map_zoom: Zoom
|
map_zoom: Zoom
|
||||||
mailer_from_name: Nombre email remitente
|
mailer_from_name: Nombre email remitente
|
||||||
mailer_from_address: Dirección email remitente
|
mailer_from_address: Dirección email remitente
|
||||||
|
meta_title: "Título del sitio (SEO)"
|
||||||
meta_description: "Descripción del sitio (SEO)"
|
meta_description: "Descripción del sitio (SEO)"
|
||||||
meta_keywords: "Palabras clave (SEO)"
|
meta_keywords: "Palabras clave (SEO)"
|
||||||
verification_offices_url: URL oficinas verificación
|
verification_offices_url: URL oficinas verificación
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ Rails.application.routes.draw do
|
|||||||
get '/welcome', to: 'welcome#welcome'
|
get '/welcome', to: 'welcome#welcome'
|
||||||
get '/cuentasegura', to: 'welcome#verification', as: :cuentasegura
|
get '/cuentasegura', to: 'welcome#verification', as: :cuentasegura
|
||||||
|
|
||||||
|
get '/consul.json', to: "installation#details"
|
||||||
|
|
||||||
resources :debates do
|
resources :debates do
|
||||||
member do
|
member do
|
||||||
post :vote
|
post :vote
|
||||||
@@ -272,7 +274,7 @@ Rails.application.routes.draw do
|
|||||||
get :search, on: :collection
|
get :search, on: :collection
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :valuators, only: [:index, :create] do
|
resources :valuators, only: [:index, :create, :destroy] do
|
||||||
get :search, on: :collection
|
get :search, on: :collection
|
||||||
get :summary, on: :collection
|
get :summary, on: :collection
|
||||||
end
|
end
|
||||||
@@ -339,6 +341,7 @@ Rails.application.routes.draw do
|
|||||||
resource :stats, only: :show do
|
resource :stats, only: :show do
|
||||||
get :proposal_notifications, on: :collection
|
get :proposal_notifications, on: :collection
|
||||||
get :direct_messages, on: :collection
|
get :direct_messages, on: :collection
|
||||||
|
get :polls, on: :collection
|
||||||
end
|
end
|
||||||
|
|
||||||
namespace :legislation do
|
namespace :legislation do
|
||||||
|
|||||||
@@ -50,11 +50,13 @@ section "Creating Settings" do
|
|||||||
Setting.create(key: 'feature.user.recommendations', value: "true")
|
Setting.create(key: 'feature.user.recommendations', value: "true")
|
||||||
Setting.create(key: 'feature.community', value: "true")
|
Setting.create(key: 'feature.community', value: "true")
|
||||||
Setting.create(key: 'feature.map', value: "true")
|
Setting.create(key: 'feature.map', value: "true")
|
||||||
|
Setting.create(key: 'feature.public_stats', value: "true")
|
||||||
Setting.create(key: 'per_page_code_head', value: "")
|
Setting.create(key: 'per_page_code_head', value: "")
|
||||||
Setting.create(key: 'per_page_code_body', value: "")
|
Setting.create(key: 'per_page_code_body', value: "")
|
||||||
Setting.create(key: 'comments_body_max_length', value: '1000')
|
Setting.create(key: 'comments_body_max_length', value: '1000')
|
||||||
Setting.create(key: 'mailer_from_name', value: 'CONSUL')
|
Setting.create(key: 'mailer_from_name', value: 'CONSUL')
|
||||||
Setting.create(key: 'mailer_from_address', value: 'noreply@consul.dev')
|
Setting.create(key: 'mailer_from_address', value: 'noreply@consul.dev')
|
||||||
|
Setting.create(key: 'meta_title', value: 'CONSUL')
|
||||||
Setting.create(key: 'meta_description', value: 'Citizen Participation and Open Government Application')
|
Setting.create(key: 'meta_description', value: 'Citizen Participation and Open Government Application')
|
||||||
Setting.create(key: 'meta_keywords', value: 'citizen participation, open government')
|
Setting.create(key: 'meta_keywords', value: 'citizen participation, open government')
|
||||||
Setting.create(key: 'verification_offices_url', value: 'http://oficinas-atencion-ciudadano.url/')
|
Setting.create(key: 'verification_offices_url', value: 'http://oficinas-atencion-ciudadano.url/')
|
||||||
@@ -63,6 +65,7 @@ section "Creating Settings" do
|
|||||||
Setting.create(key: 'map_latitude', value: 51.48)
|
Setting.create(key: 'map_latitude', value: 51.48)
|
||||||
Setting.create(key: 'map_longitude', value: 0.0)
|
Setting.create(key: 'map_longitude', value: 0.0)
|
||||||
Setting.create(key: 'map_zoom', value: 10)
|
Setting.create(key: 'map_zoom', value: 10)
|
||||||
|
Setting.create(key: 'related_contents_report_threshold', value: 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
section "Creating Geozones" do
|
section "Creating Geozones" do
|
||||||
|
|||||||
12
db/migrate/20171127171925_create_related_content.rb
Normal file
12
db/migrate/20171127171925_create_related_content.rb
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
class CreateRelatedContent < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :related_contents do |t|
|
||||||
|
t.references :parent_relationable, polymorphic: true, index: { name: 'index_related_contents_on_parent_relationable' }
|
||||||
|
t.references :child_relationable, polymorphic: true, index: { name: 'index_related_contents_on_child_relationable' }
|
||||||
|
t.references :related_content, index: { name: 'opposite_related_content' }
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :related_contents, [:parent_relationable_id, :parent_relationable_type, :child_relationable_id, :child_relationable_type], name: "unique_parent_child_related_content", unique: true, using: :btree
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
class AddTimeReportedToRelatedContent < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_column :related_contents, :times_reported, :integer, default: 0
|
||||||
|
end
|
||||||
|
end
|
||||||
18
db/schema.rb
18
db/schema.rb
@@ -11,7 +11,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20171115164152) do
|
ActiveRecord::Schema.define(version: 20171127230716) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
@@ -851,6 +851,22 @@ ActiveRecord::Schema.define(version: 20171115164152) do
|
|||||||
add_index "proposals", ["title"], name: "index_proposals_on_title", using: :btree
|
add_index "proposals", ["title"], name: "index_proposals_on_title", using: :btree
|
||||||
add_index "proposals", ["tsv"], name: "index_proposals_on_tsv", using: :gin
|
add_index "proposals", ["tsv"], name: "index_proposals_on_tsv", using: :gin
|
||||||
|
|
||||||
|
create_table "related_contents", force: :cascade do |t|
|
||||||
|
t.integer "parent_relationable_id"
|
||||||
|
t.string "parent_relationable_type"
|
||||||
|
t.integer "child_relationable_id"
|
||||||
|
t.string "child_relationable_type"
|
||||||
|
t.integer "related_content_id"
|
||||||
|
t.datetime "created_at"
|
||||||
|
t.datetime "updated_at"
|
||||||
|
t.integer "times_reported", default: 0
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "related_contents", ["child_relationable_type", "child_relationable_id"], name: "index_related_contents_on_child_relationable", using: :btree
|
||||||
|
add_index "related_contents", ["parent_relationable_id", "parent_relationable_type", "child_relationable_id", "child_relationable_type"], name: "unique_parent_child_related_content", unique: true, using: :btree
|
||||||
|
add_index "related_contents", ["parent_relationable_type", "parent_relationable_id"], name: "index_related_contents_on_parent_relationable", using: :btree
|
||||||
|
add_index "related_contents", ["related_content_id"], name: "opposite_related_content", using: :btree
|
||||||
|
|
||||||
create_table "settings", force: :cascade do |t|
|
create_table "settings", force: :cascade do |t|
|
||||||
t.string "key"
|
t.string "key"
|
||||||
t.string "value"
|
t.string "value"
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ Setting["org_name"] = "CONSUL"
|
|||||||
Setting["place_name"] = "CONSUL-land"
|
Setting["place_name"] = "CONSUL-land"
|
||||||
|
|
||||||
# Meta tags for SEO
|
# Meta tags for SEO
|
||||||
|
Setting["meta_title"] = nil
|
||||||
Setting["meta_description"] = nil
|
Setting["meta_description"] = nil
|
||||||
Setting["meta_keywords"] = nil
|
Setting["meta_keywords"] = nil
|
||||||
|
|
||||||
@@ -115,3 +116,6 @@ Setting['proposal_improvement_path'] = nil
|
|||||||
Setting['map_latitude'] = 51.48
|
Setting['map_latitude'] = 51.48
|
||||||
Setting['map_longitude'] = 0.0
|
Setting['map_longitude'] = 0.0
|
||||||
Setting['map_zoom'] = 10
|
Setting['map_zoom'] = 10
|
||||||
|
|
||||||
|
# Related content
|
||||||
|
Setting['related_contents_report_threshold'] = 5
|
||||||
|
|||||||
54
spec/controllers/installation_controller_spec.rb
Normal file
54
spec/controllers/installation_controller_spec.rb
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe InstallationController, type: :request do
|
||||||
|
|
||||||
|
describe "consul.json" do
|
||||||
|
let(:feature_settings) do
|
||||||
|
{
|
||||||
|
'debates' => nil,
|
||||||
|
'spending_proposals' => 't',
|
||||||
|
'polls' => nil,
|
||||||
|
'twitter_login' => nil,
|
||||||
|
'facebook_login' => nil,
|
||||||
|
'google_login' => nil,
|
||||||
|
'public_stats' => nil,
|
||||||
|
'budgets' => nil,
|
||||||
|
'signature_sheets' => nil,
|
||||||
|
'legislation' => nil,
|
||||||
|
'user.recommendations' => nil,
|
||||||
|
'community' => nil,
|
||||||
|
'map' => 't',
|
||||||
|
'spending_proposal_features.voting_allowed' => 't'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
feature_settings.each { |feature_name, feature_value| Setting["feature.#{feature_name}"] = feature_value }
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
Setting['feature.debates'] = true
|
||||||
|
Setting['feature.spending_proposals'] = nil
|
||||||
|
Setting['feature.polls'] = true
|
||||||
|
Setting['feature.twitter_login'] = true
|
||||||
|
Setting['feature.facebook_login'] = true
|
||||||
|
Setting['feature.google_login'] = true
|
||||||
|
Setting['feature.public_stats'] = true
|
||||||
|
Setting['feature.budgets'] = true
|
||||||
|
Setting['feature.signature_sheets'] = true
|
||||||
|
Setting['feature.legislation'] = true
|
||||||
|
Setting['feature.user.recommendations'] = true
|
||||||
|
Setting['feature.community'] = true
|
||||||
|
Setting['feature.map'] = nil
|
||||||
|
Setting['feature.spending_proposal_features.voting_allowed'] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "with query string inside query params" do
|
||||||
|
get '/consul.json'
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
expect(JSON.parse(response.body)['release']).not_to be_empty
|
||||||
|
expect(JSON.parse(response.body)['features']).to eq(feature_settings)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -882,4 +882,7 @@ LOREM_IPSUM
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
factory :related_content do
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -421,20 +421,37 @@ feature 'Admin budget investments' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario "Only displays valuation tags" do
|
scenario "Changes valuation and user generated tags" do
|
||||||
budget_investment = create(:budget_investment, tag_list: 'Park')
|
budget_investment = create(:budget_investment, tag_list: 'Park')
|
||||||
budget_investment.set_tag_list_on(:valuation, 'Education')
|
budget_investment.set_tag_list_on(:valuation, 'Education')
|
||||||
budget_investment.save
|
budget_investment.save
|
||||||
|
|
||||||
visit admin_budget_budget_investment_path(budget_investment.budget, budget_investment)
|
visit admin_budget_budget_investment_path(budget_investment.budget, budget_investment)
|
||||||
|
|
||||||
expect(page).to have_content "Education"
|
within("#user-tags") do
|
||||||
expect(page).to_not have_content "Park"
|
expect(page).to_not have_content "Education"
|
||||||
|
expect(page).to have_content "Park"
|
||||||
|
end
|
||||||
|
|
||||||
click_link 'Edit classification'
|
click_link 'Edit classification'
|
||||||
|
|
||||||
expect(page).to have_content "Education"
|
fill_in 'budget_investment_tag_list', with: 'Park, Trees'
|
||||||
|
fill_in 'budget_investment_valuation_tag_list', with: 'Education, Environment'
|
||||||
|
click_button 'Update'
|
||||||
|
|
||||||
|
visit admin_budget_budget_investment_path(budget_investment.budget, budget_investment)
|
||||||
|
|
||||||
|
within("#user-tags") do
|
||||||
|
expect(page).to_not have_content "Education"
|
||||||
|
expect(page).to_not have_content "Environment"
|
||||||
|
expect(page).to have_content "Park, Trees"
|
||||||
|
end
|
||||||
|
|
||||||
|
within("#tags") do
|
||||||
|
expect(page).to have_content "Education, Environment"
|
||||||
expect(page).to_not have_content "Park"
|
expect(page).to_not have_content "Park"
|
||||||
|
expect(page).to_not have_content "Trees"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario "Maintains user tags" do
|
scenario "Maintains user tags" do
|
||||||
|
|||||||
@@ -106,6 +106,41 @@ feature 'Admin booths assignments' do
|
|||||||
expect(page).to have_content 'There are no booths assigned to this poll.'
|
expect(page).to have_content 'There are no booths assigned to this poll.'
|
||||||
expect(page).not_to have_content booth.name
|
expect(page).not_to have_content booth.name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scenario 'Unassing booth whith associated shifts', :js do
|
||||||
|
assignment = create(:poll_booth_assignment, poll: poll, booth: booth)
|
||||||
|
officer = create(:poll_officer)
|
||||||
|
create(:poll_officer_assignment, officer: officer, booth_assignment: assignment)
|
||||||
|
create(:poll_shift, booth: booth, officer: officer)
|
||||||
|
|
||||||
|
visit manage_admin_poll_booth_assignments_path(poll)
|
||||||
|
|
||||||
|
within("#poll_booth_#{booth.id}") do
|
||||||
|
expect(page).to have_content(booth.name)
|
||||||
|
expect(page).to have_content "Assigned"
|
||||||
|
|
||||||
|
click_link 'Unassign booth'
|
||||||
|
|
||||||
|
expect(page).to have_content "Unassigned"
|
||||||
|
expect(page).not_to have_content "Assigned"
|
||||||
|
expect(page).to have_link "Assign booth"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Cannot unassing booth if poll is expired" do
|
||||||
|
poll_expired = create(:poll, :expired)
|
||||||
|
create(:poll_booth_assignment, poll: poll_expired, booth: booth)
|
||||||
|
|
||||||
|
visit manage_admin_poll_booth_assignments_path(poll_expired)
|
||||||
|
|
||||||
|
within("#poll_booth_#{booth.id}") do
|
||||||
|
expect(page).to have_content(booth.name)
|
||||||
|
expect(page).to have_content "Assigned"
|
||||||
|
|
||||||
|
expect(page).not_to have_link 'Unassign booth'
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
feature 'Show' do
|
feature 'Show' do
|
||||||
|
|||||||
@@ -162,4 +162,99 @@ feature 'Stats' do
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "Polls" do
|
||||||
|
|
||||||
|
scenario "Total participants by origin" do
|
||||||
|
oa = create(:poll_officer_assignment)
|
||||||
|
3.times { create(:poll_voter, origin: "web") }
|
||||||
|
|
||||||
|
visit admin_stats_path
|
||||||
|
|
||||||
|
within(".stats") do
|
||||||
|
click_link "Polls"
|
||||||
|
end
|
||||||
|
|
||||||
|
within("#web_participants") do
|
||||||
|
expect(page).to have_content "3"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Total participants" do
|
||||||
|
user = create(:user, :level_two)
|
||||||
|
3.times { create(:poll_voter, user: user) }
|
||||||
|
create(:poll_voter)
|
||||||
|
|
||||||
|
visit admin_stats_path
|
||||||
|
|
||||||
|
within(".stats") do
|
||||||
|
click_link "Polls"
|
||||||
|
end
|
||||||
|
|
||||||
|
within("#participants") do
|
||||||
|
expect(page).to have_content "2"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Participants by poll" do
|
||||||
|
oa = create(:poll_officer_assignment)
|
||||||
|
|
||||||
|
poll1 = create(:poll)
|
||||||
|
poll2 = create(:poll)
|
||||||
|
|
||||||
|
1.times { create(:poll_voter, poll: poll1, origin: "web") }
|
||||||
|
2.times { create(:poll_voter, poll: poll2, origin: "web") }
|
||||||
|
|
||||||
|
visit admin_stats_path
|
||||||
|
|
||||||
|
within(".stats") do
|
||||||
|
click_link "Polls"
|
||||||
|
end
|
||||||
|
|
||||||
|
within("#polls") do
|
||||||
|
|
||||||
|
within("#poll_#{poll1.id}") do
|
||||||
|
expect(page).to have_content "1"
|
||||||
|
end
|
||||||
|
|
||||||
|
within("#poll_#{poll2.id}") do
|
||||||
|
expect(page).to have_content "2"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Participants by poll question" do
|
||||||
|
user1 = create(:user, :level_two)
|
||||||
|
user2 = create(:user, :level_two)
|
||||||
|
|
||||||
|
poll = create(:poll)
|
||||||
|
|
||||||
|
question1 = create(:poll_question, :with_answers, poll: poll)
|
||||||
|
question2 = create(:poll_question, :with_answers, poll: poll)
|
||||||
|
|
||||||
|
create(:poll_answer, question: question1, author: user1)
|
||||||
|
create(:poll_answer, question: question2, author: user1)
|
||||||
|
create(:poll_answer, question: question2, author: user2)
|
||||||
|
|
||||||
|
visit admin_stats_path
|
||||||
|
|
||||||
|
within(".stats") do
|
||||||
|
click_link "Polls"
|
||||||
|
end
|
||||||
|
|
||||||
|
within("#poll_question_#{question1.id}") do
|
||||||
|
expect(page).to have_content "1"
|
||||||
|
end
|
||||||
|
|
||||||
|
within("#poll_question_#{question2.id}") do
|
||||||
|
expect(page).to have_content "2"
|
||||||
|
end
|
||||||
|
|
||||||
|
within("#poll_#{poll.id}_questions_total") do
|
||||||
|
expect(page).to have_content "2"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -29,6 +29,14 @@ feature 'Admin valuators' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scenario 'Delete Valuator' do
|
||||||
|
click_link 'Delete'
|
||||||
|
|
||||||
|
within('#valuators') do
|
||||||
|
expect(page).to_not have_content(@valuator.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'Search' do
|
context 'Search' do
|
||||||
|
|
||||||
background do
|
background do
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
require 'sessions_helper'
|
||||||
|
|
||||||
feature 'Budget Investments' do
|
feature 'Budget Investments' do
|
||||||
|
|
||||||
@@ -170,6 +171,25 @@ feature 'Budget Investments' do
|
|||||||
expect(order).to eq(new_order)
|
expect(order).to eq(new_order)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scenario "Investments are not repeated with random order", :js do
|
||||||
|
12.times { create(:budget_investment, heading: heading) }
|
||||||
|
# 12 instead of per_page + 2 because in each page there are 10 (in this case), not 25
|
||||||
|
|
||||||
|
visit budget_investments_path(budget, order: 'random')
|
||||||
|
|
||||||
|
first_page_investments = investments_order
|
||||||
|
|
||||||
|
click_link 'Next'
|
||||||
|
expect(page).to have_content "You're on page 2"
|
||||||
|
|
||||||
|
second_page_investments = investments_order
|
||||||
|
|
||||||
|
common_values = first_page_investments & second_page_investments
|
||||||
|
|
||||||
|
expect(common_values.length).to eq(0)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
scenario 'Proposals are ordered by confidence_score', :js do
|
scenario 'Proposals are ordered by confidence_score', :js do
|
||||||
create(:budget_investment, heading: heading, title: 'Best proposal').update_column(:confidence_score, 10)
|
create(:budget_investment, heading: heading, title: 'Best proposal').update_column(:confidence_score, 10)
|
||||||
create(:budget_investment, heading: heading, title: 'Worst proposal').update_column(:confidence_score, 2)
|
create(:budget_investment, heading: heading, title: 'Worst proposal').update_column(:confidence_score, 2)
|
||||||
@@ -188,6 +208,46 @@ feature 'Budget Investments' do
|
|||||||
expect(current_url).to include('page=1')
|
expect(current_url).to include('page=1')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scenario 'Each user as a different and consistent random budget investment order', :js do
|
||||||
|
12.times { create(:budget_investment, heading: heading) }
|
||||||
|
|
||||||
|
in_browser(:one) do
|
||||||
|
visit budget_investments_path(budget, heading: heading)
|
||||||
|
@first_user_investments_order = investments_order
|
||||||
|
end
|
||||||
|
|
||||||
|
in_browser(:two) do
|
||||||
|
visit budget_investments_path(budget, heading: heading)
|
||||||
|
@second_user_investments_order = investments_order
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(@first_user_investments_order).not_to eq(@second_user_investments_order)
|
||||||
|
|
||||||
|
in_browser(:one) do
|
||||||
|
click_link 'Next'
|
||||||
|
expect(page).to have_content "You're on page 2"
|
||||||
|
|
||||||
|
click_link 'Previous'
|
||||||
|
expect(page).to have_content "You're on page 1"
|
||||||
|
|
||||||
|
expect(investments_order).to eq(@first_user_investments_order)
|
||||||
|
end
|
||||||
|
|
||||||
|
in_browser(:two) do
|
||||||
|
click_link 'Next'
|
||||||
|
expect(page).to have_content "You're on page 2"
|
||||||
|
|
||||||
|
click_link 'Previous'
|
||||||
|
expect(page).to have_content "You're on page 1"
|
||||||
|
|
||||||
|
expect(investments_order).to eq(@second_user_investments_order)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def investments_order
|
||||||
|
all(".budget-investment h3").collect {|i| i.text }
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'Phase I - Accepting' do
|
context 'Phase I - Accepting' do
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ feature 'Proposals' do
|
|||||||
|
|
||||||
within("ul.pagination") do
|
within("ul.pagination") do
|
||||||
expect(page).to have_content("1")
|
expect(page).to have_content("1")
|
||||||
expect(page).to have_content("2")
|
expect(page).to have_link('2', href: 'http://www.example.com/proposals?page=2')
|
||||||
expect(page).to_not have_content("3")
|
expect(page).to_not have_content("3")
|
||||||
click_link "Next", exact: false
|
click_link "Next", exact: false
|
||||||
end
|
end
|
||||||
@@ -153,8 +153,8 @@ feature 'Proposals' do
|
|||||||
proposal = create(:proposal)
|
proposal = create(:proposal)
|
||||||
|
|
||||||
visit proposal_path(proposal)
|
visit proposal_path(proposal)
|
||||||
expect(page.html).to include "<meta name=\"twitter:title\" content=\"#{proposal.title}\" />"
|
expect(page).to have_css "meta[name='twitter:title'][content=\"#{proposal.title}\"]", visible: false
|
||||||
expect(page.html).to include "<meta id=\"ogtitle\" property=\"og:title\" content=\"#{proposal.title}\"/>"
|
expect(page).to have_css "meta[property='og:title'][content=\"#{proposal.title}\"]", visible: false
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'Create' do
|
scenario 'Create' do
|
||||||
|
|||||||
54
spec/features/social_media_meta_tags_spec.rb
Normal file
54
spec/features/social_media_meta_tags_spec.rb
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
feature 'Social media meta tags' do
|
||||||
|
|
||||||
|
context 'Setting social media meta tags' do
|
||||||
|
|
||||||
|
let(:meta_keywords) { 'citizen, participation, open government' }
|
||||||
|
let(:meta_title) { 'CONSUL TEST' }
|
||||||
|
let(:meta_description) { 'Citizen Participation and Open Government Application' }
|
||||||
|
let(:twitter_handle) { '@consul_test' }
|
||||||
|
let(:url) { 'http://consul.dev' }
|
||||||
|
let(:facebook_handle) { 'consultest' }
|
||||||
|
let(:org_name) { 'CONSUL TEST' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
Setting['meta_keywords'] = meta_keywords
|
||||||
|
Setting['meta_title'] = meta_title
|
||||||
|
Setting['meta_description'] = meta_description
|
||||||
|
Setting['twitter_handle'] = twitter_handle
|
||||||
|
Setting['url'] = url
|
||||||
|
Setting['facebook_handle'] = facebook_handle
|
||||||
|
Setting['org_name'] = org_name
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
Setting['meta_keywords'] = nil
|
||||||
|
Setting['meta_title'] = nil
|
||||||
|
Setting['meta_description'] = nil
|
||||||
|
Setting['twitter_handle'] = nil
|
||||||
|
Setting['url'] = 'http://example.com'
|
||||||
|
Setting['facebook_handle'] = nil
|
||||||
|
Setting['org_name'] = 'CONSUL'
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'Social media meta tags partial render settings content' do
|
||||||
|
|
||||||
|
visit root_path
|
||||||
|
|
||||||
|
expect(page).to have_css 'meta[name="keywords"][content="'+ meta_keywords + '"]', visible: false
|
||||||
|
expect(page).to have_css 'meta[name="twitter:site"][content="'+ twitter_handle + '"]', visible: false
|
||||||
|
expect(page).to have_css 'meta[name="twitter:title"][content="'+ meta_title + '"]', visible: false
|
||||||
|
expect(page).to have_css 'meta[name="twitter:description"][content="' + meta_description + '"]', visible: false
|
||||||
|
expect(page).to have_css 'meta[name="twitter:image"][content="http://www.example.com/social_media_icon_twitter.png"]', visible: false
|
||||||
|
expect(page).to have_css 'meta[property="og:title"][content="'+ meta_title + '"]', visible: false
|
||||||
|
expect(page).to have_css 'meta[property="article:publisher"][content="' + url + '"]', visible: false
|
||||||
|
expect(page).to have_css 'meta[property="article:author"][content="https://www.facebook.com/' + facebook_handle + '"]', visible: false
|
||||||
|
expect(page).to have_css 'meta[property="og:url"][content="http://www.example.com/"]', visible: false
|
||||||
|
expect(page).to have_css 'meta[property="og:image"][content="http://www.example.com/social_media_icon.png"]', visible: false
|
||||||
|
expect(page).to have_css 'meta[property="og:site_name"][content="' + org_name + '"]', visible: false
|
||||||
|
expect(page).to have_css 'meta[property="og:description"][content="' + meta_description + '"]', visible: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -282,15 +282,16 @@ describe 'ConsulSchema' do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe 'Comments' do
|
describe 'Comments' do
|
||||||
it 'only returns comments from proposals and debates' do
|
it 'only returns comments from proposals, debates and polls' do
|
||||||
proposal_comment = create(:comment, commentable: create(:proposal))
|
proposal_comment = create(:comment, commentable: create(:proposal))
|
||||||
debate_comment = create(:comment, commentable: create(:debate))
|
debate_comment = create(:comment, commentable: create(:debate))
|
||||||
|
poll_comment = create(:comment, commentable: create(:poll))
|
||||||
spending_proposal_comment = build(:comment, commentable: create(:spending_proposal)).save(skip_validation: true)
|
spending_proposal_comment = build(:comment, commentable: create(:spending_proposal)).save(skip_validation: true)
|
||||||
|
|
||||||
response = execute('{ comments { edges { node { commentable_type } } } }')
|
response = execute('{ comments { edges { node { commentable_type } } } }')
|
||||||
received_commentables = extract_fields(response, 'comments', 'commentable_type')
|
received_commentables = extract_fields(response, 'comments', 'commentable_type')
|
||||||
|
|
||||||
expect(received_commentables).to match_array ['Proposal', 'Debate']
|
expect(received_commentables).to match_array ['Proposal', 'Debate', 'Poll']
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'displays comments of authors even if public activity is set to false' do
|
it 'displays comments of authors even if public activity is set to false' do
|
||||||
@@ -355,6 +356,19 @@ describe 'ConsulSchema' do
|
|||||||
expect(received_comments).to match_array [visible_debate_comment.body]
|
expect(received_comments).to match_array [visible_debate_comment.body]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'does not include comments from hidden polls' do
|
||||||
|
visible_poll = create(:poll)
|
||||||
|
hidden_poll = create(:poll, hidden_at: Time.current)
|
||||||
|
|
||||||
|
visible_poll_comment = create(:comment, commentable: visible_poll)
|
||||||
|
hidden_poll_comment = create(:comment, commentable: hidden_poll)
|
||||||
|
|
||||||
|
response = execute('{ comments { edges { node { body } } } }')
|
||||||
|
received_comments = extract_fields(response, 'comments', 'body')
|
||||||
|
|
||||||
|
expect(received_comments).to match_array [visible_poll_comment.body]
|
||||||
|
end
|
||||||
|
|
||||||
it 'does not include comments of debates that are not public' do
|
it 'does not include comments of debates that are not public' do
|
||||||
not_public_debate = create(:debate, :hidden)
|
not_public_debate = create(:debate, :hidden)
|
||||||
not_public_debate_comment = create(:comment, commentable: not_public_debate)
|
not_public_debate_comment = create(:comment, commentable: not_public_debate)
|
||||||
@@ -377,6 +391,17 @@ describe 'ConsulSchema' do
|
|||||||
expect(received_comments).to_not include(not_public_proposal_comment.body)
|
expect(received_comments).to_not include(not_public_proposal_comment.body)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'does not include comments of polls that are not public' do
|
||||||
|
not_public_poll = create(:poll)
|
||||||
|
not_public_poll_comment = create(:comment, commentable: not_public_poll)
|
||||||
|
allow(Comment).to receive(:public_for_api).and_return([])
|
||||||
|
|
||||||
|
response = execute('{ comments { edges { node { body } } } }')
|
||||||
|
received_comments = extract_fields(response, 'comments', 'body')
|
||||||
|
|
||||||
|
expect(received_comments).to_not include(not_public_poll_comment.body)
|
||||||
|
end
|
||||||
|
|
||||||
it 'only returns date and hour for created_at' do
|
it 'only returns date and hour for created_at' do
|
||||||
created_at = Time.zone.parse("2017-12-31 9:30:15")
|
created_at = Time.zone.parse("2017-12-31 9:30:15")
|
||||||
create(:comment, created_at: created_at)
|
create(:comment, created_at: created_at)
|
||||||
|
|||||||
@@ -25,4 +25,23 @@ describe Budget::Heading do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "Save population" do
|
||||||
|
it "Allows population == nil" do
|
||||||
|
expect(create(:budget_heading, group: group, name: 'Population is nil', population: nil)).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "Doesn't allow population <= 0" do
|
||||||
|
heading = create(:budget_heading, group: group, name: 'Population is > 0')
|
||||||
|
|
||||||
|
heading.population = 0
|
||||||
|
expect(heading).not_to be_valid
|
||||||
|
|
||||||
|
heading.population = -10
|
||||||
|
expect(heading).not_to be_valid
|
||||||
|
|
||||||
|
heading.population = 10
|
||||||
|
expect(heading).to be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
29
spec/models/poll/booth_assignment_spec.rb
Normal file
29
spec/models/poll/booth_assignment_spec.rb
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe :booth_assignment do
|
||||||
|
let(:poll){create(:poll)}
|
||||||
|
let(:booth){create(:poll_booth)}
|
||||||
|
let(:booth1){create(:poll_booth)}
|
||||||
|
|
||||||
|
it "should check if there are shifts" do
|
||||||
|
assignment_with_shifts = create(:poll_booth_assignment, poll: poll, booth: booth)
|
||||||
|
assignment_without_shifts = create(:poll_booth_assignment, poll: poll, booth: booth1)
|
||||||
|
officer = create(:poll_officer)
|
||||||
|
create(:poll_officer_assignment, officer: officer, booth_assignment: assignment_with_shifts)
|
||||||
|
create(:poll_shift, booth: booth, officer: officer)
|
||||||
|
|
||||||
|
expect(assignment_with_shifts.shifts?).to eq(true)
|
||||||
|
expect(assignment_without_shifts.shifts?).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should delete shifts associated to booth assignments" do
|
||||||
|
assignment = create(:poll_booth_assignment, poll: poll, booth: booth)
|
||||||
|
officer = create(:poll_officer)
|
||||||
|
create(:poll_officer_assignment, officer: officer, booth_assignment: assignment)
|
||||||
|
create(:poll_shift, booth: booth, officer: officer)
|
||||||
|
|
||||||
|
assignment.destroy
|
||||||
|
|
||||||
|
expect(Poll::Shift.all.count).to eq(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
76
spec/models/relation_spec.rb
Normal file
76
spec/models/relation_spec.rb
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe RelatedContent do
|
||||||
|
|
||||||
|
let(:parent_relationable) { create([:proposal, :debate, :budget_investment].sample) }
|
||||||
|
let(:child_relationable) { create([:proposal, :debate, :budget_investment].sample) }
|
||||||
|
|
||||||
|
it "should allow relationables from various classes" do
|
||||||
|
expect(build(:related_content, parent_relationable: parent_relationable, child_relationable: child_relationable)).to be_valid
|
||||||
|
expect(build(:related_content, parent_relationable: parent_relationable, child_relationable: child_relationable)).to be_valid
|
||||||
|
expect(build(:related_content, parent_relationable: parent_relationable, child_relationable: child_relationable)).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not allow empty relationables" do
|
||||||
|
expect(build(:related_content)).not_to be_valid
|
||||||
|
expect(build(:related_content, parent_relationable: parent_relationable)).not_to be_valid
|
||||||
|
expect(build(:related_content, child_relationable: child_relationable)).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not allow repeated related contents" do
|
||||||
|
related_content = create(:related_content, parent_relationable: parent_relationable, child_relationable: child_relationable)
|
||||||
|
new_related_content = build(:related_content, parent_relationable: related_content.parent_relationable, child_relationable: related_content.child_relationable)
|
||||||
|
expect(new_related_content).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'create_opposite_related_content' do
|
||||||
|
let(:parent_relationable) { create(:proposal) }
|
||||||
|
let(:child_relationable) { create(:debate) }
|
||||||
|
let(:related_content) { build(:related_content, parent_relationable: parent_relationable, child_relationable: child_relationable) }
|
||||||
|
|
||||||
|
it 'creates an opposite related_content' do
|
||||||
|
expect { related_content.save }.to change { RelatedContent.count }.by(2)
|
||||||
|
expect(related_content.opposite_related_content.child_relationable_id).to eq(parent_relationable.id)
|
||||||
|
expect(related_content.opposite_related_content.child_relationable_type).to eq(parent_relationable.class.name)
|
||||||
|
expect(related_content.opposite_related_content.parent_relationable_id).to eq(child_relationable.id)
|
||||||
|
expect(related_content.opposite_related_content.parent_relationable_type).to eq(child_relationable.class.name)
|
||||||
|
expect(related_content.opposite_related_content.opposite_related_content.id).to eq(related_content.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'relationable destroy' do
|
||||||
|
let(:parent_relationable) { create(:proposal) }
|
||||||
|
let(:child_relationable) { create(:debate) }
|
||||||
|
|
||||||
|
it 'destroys both related contents involved' do
|
||||||
|
related_content = create(:related_content, parent_relationable: parent_relationable, child_relationable: child_relationable)
|
||||||
|
expect { related_content.parent_relationable.destroy }.to change { RelatedContent.all.count }.by(-2)
|
||||||
|
expect(child_relationable.related_contents).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: Move this into a Relationable shared context
|
||||||
|
describe '#report_related_content' do
|
||||||
|
it 'increments both relation and opposite relation times_reported counters' do
|
||||||
|
related_content = create(:related_content, parent_relationable: parent_relationable, child_relationable: child_relationable)
|
||||||
|
parent_relationable.report_related_content(child_relationable)
|
||||||
|
|
||||||
|
expect(related_content.reload.times_reported).to eq(1)
|
||||||
|
expect(related_content.reload.opposite_related_content.times_reported).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#relationed_contents' do
|
||||||
|
before do
|
||||||
|
create(:related_content, parent_relationable: parent_relationable, child_relationable: create(:proposal), times_reported: 6)
|
||||||
|
create(:related_content, parent_relationable: parent_relationable, child_relationable: child_relationable)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns not hidden by reports related contents' do
|
||||||
|
expect(parent_relationable.relationed_contents.count).to eq(1)
|
||||||
|
expect(parent_relationable.relationed_contents.first.class.name).to eq(child_relationable.class.name)
|
||||||
|
expect(parent_relationable.relationed_contents.first.id).to eq(child_relationable.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -2,7 +2,8 @@ require 'factory_girl_rails'
|
|||||||
require 'database_cleaner'
|
require 'database_cleaner'
|
||||||
require 'email_spec'
|
require 'email_spec'
|
||||||
require 'devise'
|
require 'devise'
|
||||||
require 'knapsack'
|
require 'knapsack_pro'
|
||||||
|
|
||||||
Dir["./spec/models/concerns/*.rb"].each { |f| require f }
|
Dir["./spec/models/concerns/*.rb"].each { |f| require f }
|
||||||
Dir["./spec/support/**/*.rb"].sort.each { |f| require f }
|
Dir["./spec/support/**/*.rb"].sort.each { |f| require f }
|
||||||
Dir["./spec/shared/**/*.rb"].sort.each { |f| require f }
|
Dir["./spec/shared/**/*.rb"].sort.each { |f| require f }
|
||||||
@@ -107,4 +108,4 @@ RSpec.configure do |config|
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Parallel build helper configuration for travis
|
# Parallel build helper configuration for travis
|
||||||
Knapsack::Adapters::RSpecAdapter.bind
|
KnapsackPro::Adapters::RSpecAdapter.bind
|
||||||
|
|||||||
Reference in New Issue
Block a user