Merge branch 'master' into user-recomendations
This commit is contained in:
1
Gemfile
1
Gemfile
@@ -33,6 +33,7 @@ gem 'omniauth-facebook', '~> 4.0.0'
|
|||||||
gem 'omniauth-google-oauth2', '~> 0.4.0'
|
gem 'omniauth-google-oauth2', '~> 0.4.0'
|
||||||
gem 'omniauth-twitter', '~> 1.4.0'
|
gem 'omniauth-twitter', '~> 1.4.0'
|
||||||
gem 'paperclip', '~> 5.1.0'
|
gem 'paperclip', '~> 5.1.0'
|
||||||
|
gem 'jquery-fileupload-rails'
|
||||||
gem 'paranoia', '~> 2.3.1'
|
gem 'paranoia', '~> 2.3.1'
|
||||||
gem 'pg', '~> 0.20.0'
|
gem 'pg', '~> 0.20.0'
|
||||||
gem 'pg_search', '~> 2.0.1'
|
gem 'pg_search', '~> 2.0.1'
|
||||||
|
|||||||
86
Gemfile.lock
86
Gemfile.lock
@@ -52,7 +52,7 @@ GEM
|
|||||||
safely_block (>= 0.1.1)
|
safely_block (>= 0.1.1)
|
||||||
user_agent_parser
|
user_agent_parser
|
||||||
uuidtools
|
uuidtools
|
||||||
airbrussh (1.2.0)
|
airbrussh (1.3.0)
|
||||||
sshkit (>= 1.6.1, != 1.7.0)
|
sshkit (>= 1.6.1, != 1.7.0)
|
||||||
akami (1.3.1)
|
akami (1.3.1)
|
||||||
gyoku (>= 0.4.0)
|
gyoku (>= 0.4.0)
|
||||||
@@ -73,7 +73,7 @@ GEM
|
|||||||
uniform_notifier (~> 1.10.0)
|
uniform_notifier (~> 1.10.0)
|
||||||
byebug (9.0.6)
|
byebug (9.0.6)
|
||||||
cancancan (1.16.0)
|
cancancan (1.16.0)
|
||||||
capistrano (3.8.1)
|
capistrano (3.8.2)
|
||||||
airbrussh (>= 1.0.0)
|
airbrussh (>= 1.0.0)
|
||||||
i18n
|
i18n
|
||||||
rake (>= 10.0.0)
|
rake (>= 10.0.0)
|
||||||
@@ -87,7 +87,7 @@ GEM
|
|||||||
capistrano3-delayed-job (1.7.3)
|
capistrano3-delayed-job (1.7.3)
|
||||||
capistrano (~> 3.0, >= 3.0.0)
|
capistrano (~> 3.0, >= 3.0.0)
|
||||||
daemons (~> 1.2.4)
|
daemons (~> 1.2.4)
|
||||||
capybara (2.14.0)
|
capybara (2.14.4)
|
||||||
addressable
|
addressable
|
||||||
mime-types (>= 1.16)
|
mime-types (>= 1.16)
|
||||||
nokogiri (>= 1.3.3)
|
nokogiri (>= 1.3.3)
|
||||||
@@ -95,17 +95,17 @@ GEM
|
|||||||
rack-test (>= 0.5.4)
|
rack-test (>= 0.5.4)
|
||||||
xpath (~> 2.0)
|
xpath (~> 2.0)
|
||||||
chronic (0.10.2)
|
chronic (0.10.2)
|
||||||
ckeditor (4.2.3)
|
ckeditor (4.2.4)
|
||||||
cocaine
|
cocaine
|
||||||
orm_adapter (~> 0.5.0)
|
orm_adapter (~> 0.5.0)
|
||||||
climate_control (0.1.0)
|
climate_control (0.2.0)
|
||||||
cliver (0.3.2)
|
cliver (0.3.2)
|
||||||
cocaine (0.5.8)
|
cocaine (0.5.8)
|
||||||
climate_control (>= 0.0.3, < 1.0)
|
climate_control (>= 0.0.3, < 1.0)
|
||||||
cocoon (1.2.9)
|
cocoon (1.2.10)
|
||||||
coffee-rails (4.2.1)
|
coffee-rails (4.2.2)
|
||||||
coffee-script (>= 2.2.0)
|
coffee-script (>= 2.2.0)
|
||||||
railties (>= 4.0.0, < 5.2.x)
|
railties (>= 4.0.0)
|
||||||
coffee-script (2.4.1)
|
coffee-script (2.4.1)
|
||||||
coffee-script-source
|
coffee-script-source
|
||||||
execjs
|
execjs
|
||||||
@@ -120,11 +120,11 @@ GEM
|
|||||||
daemons (1.2.4)
|
daemons (1.2.4)
|
||||||
dalli (2.7.6)
|
dalli (2.7.6)
|
||||||
database_cleaner (1.5.3)
|
database_cleaner (1.5.3)
|
||||||
debug_inspector (0.0.2)
|
debug_inspector (0.0.3)
|
||||||
delayed_job (4.1.2)
|
delayed_job (4.1.3)
|
||||||
activesupport (>= 3.0, < 5.1)
|
activesupport (>= 3.0, < 5.2)
|
||||||
delayed_job_active_record (4.1.1)
|
delayed_job_active_record (4.1.2)
|
||||||
activerecord (>= 3.0, < 5.1)
|
activerecord (>= 3.0, < 5.2)
|
||||||
delayed_job (>= 3.0, < 5)
|
delayed_job (>= 3.0, < 5)
|
||||||
devise (3.5.10)
|
devise (3.5.10)
|
||||||
bcrypt (~> 3.0)
|
bcrypt (~> 3.0)
|
||||||
@@ -144,10 +144,10 @@ GEM
|
|||||||
json
|
json
|
||||||
thread
|
thread
|
||||||
thread_safe
|
thread_safe
|
||||||
email_spec (2.1.0)
|
email_spec (2.1.1)
|
||||||
htmlentities (~> 4.3.3)
|
htmlentities (~> 4.3.3)
|
||||||
launchy (~> 2.1)
|
launchy (~> 2.1)
|
||||||
mail (~> 2.6.3)
|
mail (~> 2.6)
|
||||||
errbase (0.0.3)
|
errbase (0.0.3)
|
||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
execjs (2.7.0)
|
execjs (2.7.0)
|
||||||
@@ -158,7 +158,7 @@ GEM
|
|||||||
railties (>= 3.0.0)
|
railties (>= 3.0.0)
|
||||||
faker (1.7.3)
|
faker (1.7.3)
|
||||||
i18n (~> 0.5)
|
i18n (~> 0.5)
|
||||||
faraday (0.11.0)
|
faraday (0.12.1)
|
||||||
multipart-post (>= 1.2, < 3)
|
multipart-post (>= 1.2, < 3)
|
||||||
foundation-rails (6.2.4.0)
|
foundation-rails (6.2.4.0)
|
||||||
railties (>= 3.1.0)
|
railties (>= 3.1.0)
|
||||||
@@ -170,12 +170,12 @@ GEM
|
|||||||
activesupport (>= 4.1)
|
activesupport (>= 4.1)
|
||||||
railties (>= 4.1)
|
railties (>= 4.1)
|
||||||
tzinfo (~> 1.2, >= 1.2.2)
|
tzinfo (~> 1.2, >= 1.2.2)
|
||||||
geocoder (1.4.3)
|
geocoder (1.4.4)
|
||||||
globalid (0.4.0)
|
globalid (0.4.0)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
graphiql-rails (1.4.1)
|
graphiql-rails (1.4.2)
|
||||||
rails
|
rails
|
||||||
graphql (1.6.3)
|
graphql (1.6.4)
|
||||||
groupdate (3.2.0)
|
groupdate (3.2.0)
|
||||||
activesupport (>= 3)
|
activesupport (>= 3)
|
||||||
gyoku (1.3.1)
|
gyoku (1.3.1)
|
||||||
@@ -183,9 +183,10 @@ GEM
|
|||||||
hashie (3.5.5)
|
hashie (3.5.5)
|
||||||
highline (1.7.8)
|
highline (1.7.8)
|
||||||
htmlentities (4.3.4)
|
htmlentities (4.3.4)
|
||||||
httpi (2.4.1)
|
httpi (2.4.2)
|
||||||
rack
|
rack
|
||||||
i18n (0.8.4)
|
socksify
|
||||||
|
i18n (0.8.6)
|
||||||
i18n-tasks (0.9.15)
|
i18n-tasks (0.9.15)
|
||||||
activesupport (>= 4.0.2)
|
activesupport (>= 4.0.2)
|
||||||
ast (>= 2.1.0)
|
ast (>= 2.1.0)
|
||||||
@@ -200,6 +201,10 @@ GEM
|
|||||||
railties (>= 3.1, < 6.0)
|
railties (>= 3.1, < 6.0)
|
||||||
invisible_captcha (0.9.2)
|
invisible_captcha (0.9.2)
|
||||||
rails (>= 3.2.0)
|
rails (>= 3.2.0)
|
||||||
|
jquery-fileupload-rails (0.4.7)
|
||||||
|
actionpack (>= 3.1)
|
||||||
|
railties (>= 3.1)
|
||||||
|
sass (>= 3.2)
|
||||||
jquery-rails (4.3.1)
|
jquery-rails (4.3.1)
|
||||||
rails-dom-testing (>= 1, < 3)
|
rails-dom-testing (>= 1, < 3)
|
||||||
railties (>= 4.2.0)
|
railties (>= 4.2.0)
|
||||||
@@ -224,7 +229,7 @@ GEM
|
|||||||
knapsack (1.13.3)
|
knapsack (1.13.3)
|
||||||
rake
|
rake
|
||||||
timecop (>= 0.1.0)
|
timecop (>= 0.1.0)
|
||||||
kramdown (1.13.2)
|
kramdown (1.14.0)
|
||||||
launchy (2.4.3)
|
launchy (2.4.3)
|
||||||
addressable (~> 2.3)
|
addressable (~> 2.3)
|
||||||
letter_opener (1.4.1)
|
letter_opener (1.4.1)
|
||||||
@@ -259,9 +264,9 @@ GEM
|
|||||||
nokogiri (1.8.0)
|
nokogiri (1.8.0)
|
||||||
mini_portile2 (~> 2.2.0)
|
mini_portile2 (~> 2.2.0)
|
||||||
nori (2.6.0)
|
nori (2.6.0)
|
||||||
oauth (0.5.1)
|
oauth (0.5.3)
|
||||||
oauth2 (1.3.1)
|
oauth2 (1.4.0)
|
||||||
faraday (>= 0.8, < 0.12)
|
faraday (>= 0.8, < 0.13)
|
||||||
jwt (~> 1.0)
|
jwt (~> 1.0)
|
||||||
multi_json (~> 1.3)
|
multi_json (~> 1.3)
|
||||||
multi_xml (~> 0.5)
|
multi_xml (~> 0.5)
|
||||||
@@ -328,7 +333,7 @@ GEM
|
|||||||
bundler (>= 1.3.0, < 2.0)
|
bundler (>= 1.3.0, < 2.0)
|
||||||
railties (= 4.2.9)
|
railties (= 4.2.9)
|
||||||
sprockets-rails
|
sprockets-rails
|
||||||
rails-assets-markdown-it (8.2.1)
|
rails-assets-markdown-it (8.2.2)
|
||||||
rails-deprecated_sanitizer (1.0.3)
|
rails-deprecated_sanitizer (1.0.3)
|
||||||
activesupport (>= 4.2.0.alpha)
|
activesupport (>= 4.2.0.alpha)
|
||||||
rails-dom-testing (1.0.8)
|
rails-dom-testing (1.0.8)
|
||||||
@@ -386,7 +391,7 @@ GEM
|
|||||||
sshkit (>= 1.2)
|
sshkit (>= 1.2)
|
||||||
safely_block (0.2.0)
|
safely_block (0.2.0)
|
||||||
errbase
|
errbase
|
||||||
sass (3.4.23)
|
sass (3.4.25)
|
||||||
sass-rails (5.0.6)
|
sass-rails (5.0.6)
|
||||||
railties (>= 4.0.0, < 6)
|
railties (>= 4.0.0, < 6)
|
||||||
sass (~> 3.1)
|
sass (~> 3.1)
|
||||||
@@ -408,12 +413,13 @@ GEM
|
|||||||
docile (~> 1.1.0)
|
docile (~> 1.1.0)
|
||||||
json (>= 1.8, < 3)
|
json (>= 1.8, < 3)
|
||||||
simplecov-html (~> 0.10.0)
|
simplecov-html (~> 0.10.0)
|
||||||
simplecov-html (0.10.0)
|
simplecov-html (0.10.1)
|
||||||
sitemap_generator (5.3.1)
|
sitemap_generator (5.3.1)
|
||||||
builder (~> 3.0)
|
builder (~> 3.0)
|
||||||
social-share-button (0.10.0)
|
social-share-button (0.10.0)
|
||||||
coffee-rails
|
coffee-rails
|
||||||
spring (2.0.1)
|
socksify (1.7.1)
|
||||||
|
spring (2.0.2)
|
||||||
activesupport (>= 4.2)
|
activesupport (>= 4.2)
|
||||||
spring-commands-rspec (1.0.4)
|
spring-commands-rspec (1.0.4)
|
||||||
spring (>= 0.9.1)
|
spring (>= 0.9.1)
|
||||||
@@ -428,19 +434,19 @@ GEM
|
|||||||
actionpack (>= 4.0)
|
actionpack (>= 4.0)
|
||||||
activesupport (>= 4.0)
|
activesupport (>= 4.0)
|
||||||
sprockets (>= 3.0.0)
|
sprockets (>= 3.0.0)
|
||||||
sshkit (1.13.1)
|
sshkit (1.14.0)
|
||||||
net-scp (>= 1.1.2)
|
net-scp (>= 1.1.2)
|
||||||
net-ssh (>= 2.8.0)
|
net-ssh (>= 2.8.0)
|
||||||
term-ansicolor (1.6.0)
|
term-ansicolor (1.6.0)
|
||||||
tins (~> 1.0)
|
tins (~> 1.0)
|
||||||
terminal-table (1.7.3)
|
terminal-table (1.8.0)
|
||||||
unicode-display_width (~> 1.1.1)
|
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||||
thor (0.19.4)
|
thor (0.19.4)
|
||||||
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.8.1)
|
timecop (0.9.1)
|
||||||
tins (1.13.2)
|
tins (1.15.0)
|
||||||
turbolinks (2.5.3)
|
turbolinks (2.5.3)
|
||||||
coffee-rails
|
coffee-rails
|
||||||
turnout (2.4.0)
|
turnout (2.4.0)
|
||||||
@@ -452,14 +458,14 @@ GEM
|
|||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
uglifier (3.2.0)
|
uglifier (3.2.0)
|
||||||
execjs (>= 0.3.0, < 3)
|
execjs (>= 0.3.0, < 3)
|
||||||
unicode-display_width (1.1.3)
|
unicode-display_width (1.3.0)
|
||||||
unicorn (5.3.0)
|
unicorn (5.3.0)
|
||||||
kgio (~> 2.6)
|
kgio (~> 2.6)
|
||||||
raindrops (~> 0.7)
|
raindrops (~> 0.7)
|
||||||
uniform_notifier (1.10.0)
|
uniform_notifier (1.10.0)
|
||||||
user_agent_parser (2.3.0)
|
user_agent_parser (2.3.1)
|
||||||
uuidtools (2.1.5)
|
uuidtools (2.1.5)
|
||||||
warden (1.2.6)
|
warden (1.2.7)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
wasabi (3.5.0)
|
wasabi (3.5.0)
|
||||||
httpi (~> 2.0)
|
httpi (~> 2.0)
|
||||||
@@ -473,7 +479,7 @@ GEM
|
|||||||
websocket-extensions (0.1.2)
|
websocket-extensions (0.1.2)
|
||||||
whenever (0.9.7)
|
whenever (0.9.7)
|
||||||
chronic (>= 0.6.3)
|
chronic (>= 0.6.3)
|
||||||
xpath (2.0.0)
|
xpath (2.1.0)
|
||||||
nokogiri (~> 1.3)
|
nokogiri (~> 1.3)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
@@ -515,6 +521,7 @@ DEPENDENCIES
|
|||||||
i18n-tasks (~> 0.9.15)
|
i18n-tasks (~> 0.9.15)
|
||||||
initialjs-rails (~> 0.2.0.5)
|
initialjs-rails (~> 0.2.0.5)
|
||||||
invisible_captcha (~> 0.9.2)
|
invisible_captcha (~> 0.9.2)
|
||||||
|
jquery-fileupload-rails
|
||||||
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)
|
||||||
@@ -558,5 +565,6 @@ DEPENDENCIES
|
|||||||
web-console (~> 3.3.0)
|
web-console (~> 3.3.0)
|
||||||
whenever (~> 0.9.7)
|
whenever (~> 0.9.7)
|
||||||
|
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.15.1
|
1.15.3
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
//= require jquery_ujs
|
//= require jquery_ujs
|
||||||
//= require jquery-ui/widgets/datepicker
|
//= require jquery-ui/widgets/datepicker
|
||||||
//= require jquery-ui/i18n/datepicker-es
|
//= require jquery-ui/i18n/datepicker-es
|
||||||
|
//= require jquery-fileupload/basic
|
||||||
//= require foundation
|
//= require foundation
|
||||||
//= require turbolinks
|
//= require turbolinks
|
||||||
//= require ckeditor/loader
|
//= require ckeditor/loader
|
||||||
@@ -59,6 +60,7 @@
|
|||||||
//= require legislation_annotatable
|
//= require legislation_annotatable
|
||||||
//= require watch_form_changes
|
//= require watch_form_changes
|
||||||
//= require followable
|
//= require followable
|
||||||
|
//= require documentable
|
||||||
//= require tree_navigator
|
//= require tree_navigator
|
||||||
//= require custom
|
//= require custom
|
||||||
|
|
||||||
@@ -94,6 +96,7 @@ var initialize_modules = function() {
|
|||||||
App.LegislationAnnotatable.initialize();
|
App.LegislationAnnotatable.initialize();
|
||||||
App.WatchFormChanges.initialize();
|
App.WatchFormChanges.initialize();
|
||||||
App.TreeNavigator.initialize();
|
App.TreeNavigator.initialize();
|
||||||
|
App.Documentable.initialize();
|
||||||
};
|
};
|
||||||
|
|
||||||
$(function(){
|
$(function(){
|
||||||
|
|||||||
101
app/assets/javascripts/documentable.js.coffee
Normal file
101
app/assets/javascripts/documentable.js.coffee
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
App.Documentable =
|
||||||
|
|
||||||
|
initialize: ->
|
||||||
|
@initializeDirectUploads()
|
||||||
|
@initializeInterface()
|
||||||
|
|
||||||
|
initializeDirectUploads: ->
|
||||||
|
|
||||||
|
$('input.document_ajax_attachment[type=file]').fileupload
|
||||||
|
|
||||||
|
paramName: "document[attachment]"
|
||||||
|
|
||||||
|
formData: null
|
||||||
|
|
||||||
|
add: (e, data) ->
|
||||||
|
wrapper = $(e.target).closest('.document')
|
||||||
|
index = $(e.target).data('index')
|
||||||
|
is_nested_document = $(e.target).data('nested-document')
|
||||||
|
$(wrapper).find('.progress-bar-placeholder').empty()
|
||||||
|
data.progressBar = $(wrapper).find('.progress-bar-placeholder').html('<div class="progress-bar"><div class="loading-bar uploading"></div></div>')
|
||||||
|
$(wrapper).find('.progress-bar-placeholder').css('display','block')
|
||||||
|
data.formData = {
|
||||||
|
"document[title]": $(wrapper).find('input.document-title').val() || data.files[0].name
|
||||||
|
"index": index,
|
||||||
|
"nested_document": is_nested_document
|
||||||
|
}
|
||||||
|
data.submit()
|
||||||
|
|
||||||
|
change: (e, data) ->
|
||||||
|
wrapper = $(e.target).parent()
|
||||||
|
$.each(data.files, (index, file)->
|
||||||
|
$(wrapper).find('.file-name').text(file.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
progress: (e, data) ->
|
||||||
|
progress = parseInt(data.loaded / data.total * 100, 10)
|
||||||
|
$(data.progressBar).find('.loading-bar').css 'width', progress + '%'
|
||||||
|
return
|
||||||
|
|
||||||
|
initializeInterface: ->
|
||||||
|
input_files = $('input.document_ajax_attachment[type=file]')
|
||||||
|
|
||||||
|
$.each input_files, (index, file) ->
|
||||||
|
wrapper = $(file).parent()
|
||||||
|
App.Documentable.watchRemoveDocumentbutton(wrapper)
|
||||||
|
|
||||||
|
watchRemoveDocumentbutton: (wrapper) ->
|
||||||
|
remove_document_button = $(wrapper).find('.remove-document')
|
||||||
|
$(remove_document_button).on 'click', (e) ->
|
||||||
|
e.preventDefault()
|
||||||
|
$(wrapper).remove()
|
||||||
|
$('#new_document_link').show()
|
||||||
|
$('.max-documents-notice').hide()
|
||||||
|
|
||||||
|
uploadNestedDocument: (id, nested_document, result) ->
|
||||||
|
$('#' + id).replaceWith(nested_document)
|
||||||
|
@updateLoadingBar(id, result)
|
||||||
|
@initialize()
|
||||||
|
|
||||||
|
uploadPlainDocument: (id, nested_document, result) ->
|
||||||
|
$('#' + id).replaceWith(nested_document)
|
||||||
|
@updateLoadingBar(id, result)
|
||||||
|
@initialize()
|
||||||
|
|
||||||
|
updateLoadingBar: (id, result) ->
|
||||||
|
if result
|
||||||
|
$('#' + id).find('.loading-bar').addClass 'complete'
|
||||||
|
else
|
||||||
|
$('#' + id).find('.loading-bar').addClass 'errors'
|
||||||
|
$('#' + id).find('.progress-bar-placeholder').css('display','block')
|
||||||
|
|
||||||
|
new: (nested_fields) ->
|
||||||
|
$(".documents-list").append(nested_fields)
|
||||||
|
@initialize()
|
||||||
|
|
||||||
|
destroyNestedDocument: (id, notice) ->
|
||||||
|
$('#' + id).remove()
|
||||||
|
@updateNotice(notice)
|
||||||
|
|
||||||
|
replacePlainDocument: (id, notice, plain_document) ->
|
||||||
|
$('#' + id).replaceWith(plain_document)
|
||||||
|
@updateNotice(notice)
|
||||||
|
@initialize()
|
||||||
|
|
||||||
|
updateNotice: (notice) ->
|
||||||
|
if $('[data-alert]').length > 0
|
||||||
|
$('[data-alert]').replaceWith(notice)
|
||||||
|
else
|
||||||
|
$("body").append(notice)
|
||||||
|
|
||||||
|
updateNewDocumentButton: (link) ->
|
||||||
|
if $('.document').length >= $('.documents').data('max-documents')
|
||||||
|
$('#new_document_link').hide()
|
||||||
|
$('.max-documents-notice').removeClass('hide')
|
||||||
|
$('.max-documents-notice').show()
|
||||||
|
else if $('#new_document_link').length > 0
|
||||||
|
$('#new_document_link').replaceWith(link)
|
||||||
|
$('.max-documents-notice').hide()
|
||||||
|
else
|
||||||
|
$('.max-documents-notice').hide()
|
||||||
|
$(link).insertBefore('.documents hr:last')
|
||||||
@@ -15,3 +15,4 @@
|
|||||||
@import 'annotator_overrides';
|
@import 'annotator_overrides';
|
||||||
@import 'jquery-ui/datepicker';
|
@import 'jquery-ui/datepicker';
|
||||||
@import 'datepicker_overrides';
|
@import 'datepicker_overrides';
|
||||||
|
@import 'documentable';
|
||||||
|
|||||||
59
app/assets/stylesheets/documentable.scss
Normal file
59
app/assets/stylesheets/documentable.scss
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
.progress-bar-placeholder {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-form {
|
||||||
|
.document .file-name {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
.progress-bar-placeholder {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.document .loading-bar.errors {
|
||||||
|
margin-top: $line-height * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.document {
|
||||||
|
.button {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
width: 100%;
|
||||||
|
background-color: $light-gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.document_ajax_attachment[type=file]{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-name {
|
||||||
|
margin-top: $line-height / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-bar {
|
||||||
|
height: 5px;
|
||||||
|
width: 0;
|
||||||
|
transition: width 500ms ease-out;
|
||||||
|
|
||||||
|
&.uploading {
|
||||||
|
background-color: $dark-gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.complete {
|
||||||
|
background-color: $success-color;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.errors {
|
||||||
|
background-color: $alert-color;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: $line-height / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-bar.no-transition {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -97,6 +97,10 @@
|
|||||||
content: '\72';
|
content: '\72';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-documents::before {
|
||||||
|
content: '\68';
|
||||||
|
}
|
||||||
|
|
||||||
.icon-proposals::before {
|
.icon-proposals::before {
|
||||||
content: '\68';
|
content: '\68';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
// 17. Activity
|
// 17. Activity
|
||||||
// 18. Banners
|
// 18. Banners
|
||||||
// 19. Recommended Section Home
|
// 19. Recommended Section Home
|
||||||
|
// 20. Documents
|
||||||
//
|
//
|
||||||
|
|
||||||
// 01. Global styles
|
// 01. Global styles
|
||||||
@@ -92,6 +93,11 @@ a {
|
|||||||
color: $link;
|
color: $link;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button.hollow.error {
|
||||||
|
border-color: $alert-border;
|
||||||
|
color: $color-alert;
|
||||||
|
}
|
||||||
|
|
||||||
.postfix.button {
|
.postfix.button {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
@@ -2154,7 +2160,6 @@ table {
|
|||||||
|
|
||||||
.section-recommended {
|
.section-recommended {
|
||||||
padding: $line-height * 2 0;
|
padding: $line-height * 2 0;
|
||||||
// padding-bottom: $line-height * 2;
|
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
margin-bottom: $line-height * 2;
|
margin-bottom: $line-height * 2;
|
||||||
@@ -2278,4 +2283,124 @@ table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 20. Documents
|
||||||
|
.document-form form {
|
||||||
|
|
||||||
|
.radio-buttons {
|
||||||
|
label {
|
||||||
|
margin-right: $line-height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-option-link {
|
||||||
|
input {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
margin-bottom: $line-height;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
&.error {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-option-file {
|
||||||
|
.file-name {
|
||||||
|
label {
|
||||||
|
|
||||||
|
@include breakpoint(small medium) {
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include breakpoint(large) {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
|
||||||
|
@include breakpoint(small medium) {
|
||||||
|
float: none;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include breakpoint(large) {
|
||||||
|
float: left;
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-top: $line-height / 2;
|
||||||
|
margin-left: $line-height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-errors {
|
||||||
|
margin-bottom: $line-height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.documents-list {
|
||||||
|
|
||||||
|
table {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
@include breakpoint(small) {
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include breakpoint(medium) {
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
padding-left: $line-height * 1.5;
|
||||||
|
|
||||||
|
@include breakpoint(small) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include breakpoint(medium) {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include breakpoint(large) {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child::before {
|
||||||
|
color: #007bb7;
|
||||||
|
content: 'G';
|
||||||
|
font-family: "icons" !important;
|
||||||
|
font-size: rem-calc(24);
|
||||||
|
left: rem-calc(6);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
@include breakpoint(small) {
|
||||||
|
padding-top: rem-calc(12);
|
||||||
|
}
|
||||||
|
|
||||||
|
@include breakpoint(medium) {
|
||||||
|
padding-top: rem-calc(22);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,11 +248,13 @@
|
|||||||
.debate-form,
|
.debate-form,
|
||||||
.proposal-form,
|
.proposal-form,
|
||||||
.budget-investment-form,
|
.budget-investment-form,
|
||||||
.spending-proposal-form {
|
.spending-proposal-form,
|
||||||
|
.document-form {
|
||||||
|
|
||||||
.icon-debates,
|
.icon-debates,
|
||||||
.icon-proposals,
|
.icon-proposals,
|
||||||
.icon-budget {
|
.icon-budget,
|
||||||
|
.icon-documents {
|
||||||
font-size: rem-calc(50);
|
font-size: rem-calc(50);
|
||||||
line-height: $line-height;
|
line-height: $line-height;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
@@ -262,7 +264,8 @@
|
|||||||
color: $debates;
|
color: $debates;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-proposals {
|
.icon-proposals,
|
||||||
|
.icon-documents {
|
||||||
color: $proposals;
|
color: $proposals;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +297,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.proposal-form {
|
.proposal-form,
|
||||||
|
.document-form {
|
||||||
|
|
||||||
.recommendations li::before {
|
.recommendations li::before {
|
||||||
color: $proposals;
|
color: $proposals;
|
||||||
@@ -746,6 +750,12 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.document-form{
|
||||||
|
max-width: 75rem;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.more-info {
|
.more-info {
|
||||||
clear: both;
|
clear: both;
|
||||||
color: $text-medium;
|
color: $text-medium;
|
||||||
|
|||||||
@@ -44,10 +44,12 @@ module Budgets
|
|||||||
set_comment_flags(@comment_tree.comments)
|
set_comment_flags(@comment_tree.comments)
|
||||||
load_investment_votes(@investment)
|
load_investment_votes(@investment)
|
||||||
@investment_ids = [@investment.id]
|
@investment_ids = [@investment.id]
|
||||||
|
@document = Document.new(documentable: @investment)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@investment.author = current_user
|
@investment.author = current_user
|
||||||
|
recover_documents_from_cache(@investment)
|
||||||
|
|
||||||
if @investment.save
|
if @investment.save
|
||||||
Mailer.budget_investment_created(@investment).deliver_later
|
Mailer.budget_investment_created(@investment).deliver_later
|
||||||
@@ -104,7 +106,8 @@ module Budgets
|
|||||||
|
|
||||||
def investment_params
|
def investment_params
|
||||||
params.require(:budget_investment).permit(:title, :description, :external_url, :heading_id, :tag_list,
|
params.require(:budget_investment).permit(:title, :description, :external_url, :heading_id, :tag_list,
|
||||||
:organization_name, :location, :terms_of_service)
|
:organization_name, :location, :terms_of_service,
|
||||||
|
documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_ballot
|
def load_ballot
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ module CommentableActions
|
|||||||
|
|
||||||
def update
|
def update
|
||||||
resource.assign_attributes(strong_params)
|
resource.assign_attributes(strong_params)
|
||||||
|
recover_documents_from_cache(resource)
|
||||||
|
|
||||||
if resource.save
|
if resource.save
|
||||||
redirect_to resource, notice: t("flash.actions.update.#{resource_name.underscore}")
|
redirect_to resource, notice: t("flash.actions.update.#{resource_name.underscore}")
|
||||||
else
|
else
|
||||||
@@ -115,4 +117,11 @@ module CommentableActions
|
|||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def recover_documents_from_cache(resource)
|
||||||
|
return false unless resource.try(:documents)
|
||||||
|
resource.documents = resource.documents.each do |document|
|
||||||
|
document.set_attachment_from_cached_attachment if document.cached_attachment.present?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
100
app/controllers/documents_controller.rb
Normal file
100
app/controllers/documents_controller.rb
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
class DocumentsController < ApplicationController
|
||||||
|
before_action :authenticate_user!
|
||||||
|
before_filter :find_documentable, except: :destroy
|
||||||
|
before_filter :prepare_new_document, only: [:new, :new_nested]
|
||||||
|
before_filter :prepare_document_for_creation, only: :create
|
||||||
|
|
||||||
|
load_and_authorize_resource except: :upload
|
||||||
|
skip_authorization_check only: :upload
|
||||||
|
|
||||||
|
def new
|
||||||
|
end
|
||||||
|
|
||||||
|
def new_nested
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
recover_attachments_from_cache
|
||||||
|
|
||||||
|
if @document.save
|
||||||
|
flash[:notice] = t "documents.actions.create.notice"
|
||||||
|
redirect_to params[:from]
|
||||||
|
else
|
||||||
|
flash[:alert] = t "documents.actions.create.alert"
|
||||||
|
render :new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
respond_to do |format|
|
||||||
|
format.html do
|
||||||
|
if @document.destroy
|
||||||
|
flash[:notice] = t "documents.actions.destroy.notice"
|
||||||
|
else
|
||||||
|
flash[:alert] = t "documents.actions.destroy.alert"
|
||||||
|
end
|
||||||
|
redirect_to params[:from]
|
||||||
|
end
|
||||||
|
format.js do
|
||||||
|
if @document.destroy
|
||||||
|
flash.now[:notice] = t "documents.actions.destroy.notice"
|
||||||
|
else
|
||||||
|
flash.now[:alert] = t "documents.actions.destroy.alert"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy_upload
|
||||||
|
@document = Document.new(cached_attachment: params[:path])
|
||||||
|
@document.set_attachment_from_cached_attachment
|
||||||
|
@document.documentable = @documentable
|
||||||
|
|
||||||
|
if @document.attachment.destroy
|
||||||
|
flash.now[:notice] = t "documents.actions.destroy.notice"
|
||||||
|
else
|
||||||
|
flash.now[:alert] = t "documents.actions.destroy.alert"
|
||||||
|
end
|
||||||
|
render :destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
def upload
|
||||||
|
@document = Document.new(document_params.merge(user: current_user))
|
||||||
|
@document.documentable = @documentable
|
||||||
|
|
||||||
|
if @document.valid?
|
||||||
|
@document.attachment.save
|
||||||
|
@document.set_cached_attachment_from_attachment(URI(request.url))
|
||||||
|
else
|
||||||
|
@document.attachment.destroy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def document_params
|
||||||
|
params.require(:document).permit(:title, :documentable_type, :documentable_id,
|
||||||
|
:attachment, :cached_attachment, :user_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_documentable
|
||||||
|
@documentable = params[:documentable_type].constantize.find_or_initialize_by(id: params[:documentable_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepare_new_document
|
||||||
|
@document = Document.new(documentable: @documentable, user_id: current_user.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepare_document_for_creation
|
||||||
|
@document = Document.new(document_params)
|
||||||
|
@document.documentable = @documentable
|
||||||
|
@document.user = current_user
|
||||||
|
end
|
||||||
|
|
||||||
|
def recover_attachments_from_cache
|
||||||
|
if @document.attachment.blank? && @document.cached_attachment.present?
|
||||||
|
@document.set_attachment_from_cached_attachment
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -19,11 +19,13 @@ class ProposalsController < ApplicationController
|
|||||||
def show
|
def show
|
||||||
super
|
super
|
||||||
@notifications = @proposal.notifications
|
@notifications = @proposal.notifications
|
||||||
|
@document = Document.new(documentable: @proposal)
|
||||||
redirect_to proposal_path(@proposal), status: :moved_permanently if request.path != proposal_path(@proposal)
|
redirect_to proposal_path(@proposal), status: :moved_permanently if request.path != proposal_path(@proposal)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@proposal = Proposal.new(proposal_params.merge(author: current_user))
|
@proposal = Proposal.new(proposal_params.merge(author: current_user))
|
||||||
|
recover_documents_from_cache(@proposal)
|
||||||
|
|
||||||
if @proposal.save
|
if @proposal.save
|
||||||
redirect_to share_proposal_path(@proposal), notice: I18n.t('flash.actions.create.proposal')
|
redirect_to share_proposal_path(@proposal), notice: I18n.t('flash.actions.create.proposal')
|
||||||
@@ -75,7 +77,8 @@ class ProposalsController < ApplicationController
|
|||||||
|
|
||||||
def proposal_params
|
def proposal_params
|
||||||
params.require(:proposal).permit(:title, :question, :summary, :description, :external_url, :video_url,
|
params.require(:proposal).permit(:title, :question, :summary, :description, :external_url, :video_url,
|
||||||
:responsible_name, :tag_list, :terms_of_service, :geozone_id)
|
:responsible_name, :tag_list, :terms_of_service, :geozone_id,
|
||||||
|
documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id] )
|
||||||
end
|
end
|
||||||
|
|
||||||
def retired_params
|
def retired_params
|
||||||
@@ -121,4 +124,5 @@ class ProposalsController < ApplicationController
|
|||||||
def load_successful_proposals
|
def load_successful_proposals
|
||||||
@proposal_successful_exists = Proposal.successful.exists?
|
@proposal_successful_exists = Proposal.successful.exists?
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
41
app/helpers/documentables_helper.rb
Normal file
41
app/helpers/documentables_helper.rb
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
module DocumentablesHelper
|
||||||
|
|
||||||
|
def documentable_class(documentable)
|
||||||
|
documentable.class.name.parameterize('_')
|
||||||
|
end
|
||||||
|
|
||||||
|
def max_documents_allowed(documentable)
|
||||||
|
documentable.class.max_documents_allowed
|
||||||
|
end
|
||||||
|
|
||||||
|
def max_file_size(documentable)
|
||||||
|
bytesToMeg(documentable.class.max_file_size)
|
||||||
|
end
|
||||||
|
|
||||||
|
def accepted_content_types(documentable)
|
||||||
|
documentable.class.accepted_content_types
|
||||||
|
end
|
||||||
|
|
||||||
|
def accepted_content_types_extensions(documentable_class)
|
||||||
|
documentable_class.accepted_content_types
|
||||||
|
.collect{ |content_type| ".#{content_type.split("/").last}" }
|
||||||
|
.join(",")
|
||||||
|
end
|
||||||
|
|
||||||
|
def humanized_accepted_content_types(documentable)
|
||||||
|
documentable.class.accepted_content_types
|
||||||
|
.collect{ |content_type| content_type.split("/").last }
|
||||||
|
.join(", ")
|
||||||
|
end
|
||||||
|
|
||||||
|
def documentables_note(documentable)
|
||||||
|
t "documents.form.note", max_documents_allowed: max_documents_allowed(documentable),
|
||||||
|
accepted_content_types: humanized_accepted_content_types(documentable),
|
||||||
|
max_file_size: max_file_size(documentable)
|
||||||
|
end
|
||||||
|
|
||||||
|
def max_documents_allowed?(documentable)
|
||||||
|
documentable.documents.count >= documentable.class.max_documents_allowed
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
89
app/helpers/documents_helper.rb
Normal file
89
app/helpers/documents_helper.rb
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
module DocumentsHelper
|
||||||
|
|
||||||
|
def document_attachment_file_name(document)
|
||||||
|
document.attachment_file_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def errors_on_attachment(document)
|
||||||
|
document.errors[:attachment].join(', ') if document.errors.key?(:attachment)
|
||||||
|
end
|
||||||
|
|
||||||
|
def bytesToMeg(bytes)
|
||||||
|
bytes / Numeric::MEGABYTE
|
||||||
|
end
|
||||||
|
|
||||||
|
def document_nested_field_name(document, index, field)
|
||||||
|
parent = document.documentable_type.parameterize.underscore
|
||||||
|
"#{parent.parameterize}[documents_attributes][#{index}][#{field}]"
|
||||||
|
end
|
||||||
|
|
||||||
|
def document_nested_field_id(document, index, field)
|
||||||
|
parent = document.documentable_type.parameterize.underscore
|
||||||
|
"#{parent.parameterize}_documents_attributes_#{index}_#{field}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def document_nested_field_wrapper_id(index)
|
||||||
|
"document_#{index}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_destroy_document_link(document, index)
|
||||||
|
if document.persisted?
|
||||||
|
link_to t('documents.form.delete_button'),
|
||||||
|
document_path(document, index: index, nested_document: true),
|
||||||
|
method: :delete,
|
||||||
|
remote: true,
|
||||||
|
data: { confirm: t('documents.actions.destroy.confirm') },
|
||||||
|
class: "delete float-right"
|
||||||
|
elsif !document.persisted? && document.cached_attachment.present?
|
||||||
|
link_to t('documents.form.delete_button'),
|
||||||
|
destroy_upload_documents_path(path: document.cached_attachment,
|
||||||
|
nested_document: true,
|
||||||
|
index: index,
|
||||||
|
documentable_type: document.documentable_type,
|
||||||
|
documentable_id: document.documentable_id),
|
||||||
|
method: :delete,
|
||||||
|
remote: true,
|
||||||
|
class: "delete float-right"
|
||||||
|
else
|
||||||
|
link_to t('documents.form.delete_button'),
|
||||||
|
"#",
|
||||||
|
class: "delete float-right remove-document"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_attachment(document, index)
|
||||||
|
html = file_field_tag :attachment,
|
||||||
|
accept: accepted_content_types_extensions(document.documentable_type.constantize),
|
||||||
|
class: 'document_ajax_attachment',
|
||||||
|
data: {
|
||||||
|
url: document_direct_upload_url(document),
|
||||||
|
cached_attachment_input_field: document_nested_field_id(document, index, :cached_attachment),
|
||||||
|
multiple: false,
|
||||||
|
index: index,
|
||||||
|
nested_document: true
|
||||||
|
},
|
||||||
|
name: document_nested_field_name(document, index, :attachment),
|
||||||
|
id: document_nested_field_id(document, index, :attachment)
|
||||||
|
if document.attachment.blank? && document.cached_attachment.blank?
|
||||||
|
klass = document.errors[:attachment].any? ? "error" : ""
|
||||||
|
html += label_tag document_nested_field_id(document, index, :attachment),
|
||||||
|
t("documents.form.attachment_label"),
|
||||||
|
class: "button hollow #{klass}"
|
||||||
|
if document.errors[:attachment].any?
|
||||||
|
html += content_tag :small, class: "error" do
|
||||||
|
errors_on_attachment(document)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
html
|
||||||
|
end
|
||||||
|
|
||||||
|
def document_direct_upload_url(document)
|
||||||
|
upload_documents_url(
|
||||||
|
documentable_type: document.documentable_type,
|
||||||
|
documentable_id: document.documentable_id,
|
||||||
|
format: :js
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -73,6 +73,7 @@ module Abilities
|
|||||||
can [:manage], ::Legislation::Question
|
can [:manage], ::Legislation::Question
|
||||||
cannot :comment_as_moderator, [::Legislation::Question, Legislation::Annotation]
|
cannot :comment_as_moderator, [::Legislation::Question, Legislation::Annotation]
|
||||||
|
|
||||||
|
can [:create, :destroy], Document
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ module Abilities
|
|||||||
|
|
||||||
can [:create, :destroy], Follow
|
can [:create, :destroy], Follow
|
||||||
|
|
||||||
|
can [:create, :destroy, :new], Document, documentable: { author_id: user.id }
|
||||||
|
can [:new_nested, :upload, :destroy_upload], Document
|
||||||
|
|
||||||
unless user.organization?
|
unless user.organization?
|
||||||
can :vote, Debate
|
can :vote, Debate
|
||||||
can :vote, Comment
|
can :vote, Comment
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
class Budget
|
class Budget
|
||||||
class Investment < ActiveRecord::Base
|
class Investment < ActiveRecord::Base
|
||||||
|
|
||||||
include Measurable
|
include Measurable
|
||||||
include Sanitizable
|
include Sanitizable
|
||||||
include Taggable
|
include Taggable
|
||||||
include Searchable
|
include Searchable
|
||||||
include Reclassification
|
include Reclassification
|
||||||
include Followable
|
include Followable
|
||||||
|
include Documentable
|
||||||
|
documentable max_documents_allowed: 3,
|
||||||
|
max_file_size: 3.megabytes,
|
||||||
|
accepted_content_types: [ "application/pdf" ]
|
||||||
|
accepts_nested_attributes_for :documents, allow_destroy: true
|
||||||
|
|
||||||
acts_as_votable
|
acts_as_votable
|
||||||
acts_as_paranoid column: :hidden_at
|
acts_as_paranoid column: :hidden_at
|
||||||
|
|||||||
20
app/models/concerns/documentable.rb
Normal file
20
app/models/concerns/documentable.rb
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
module Documentable
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
has_many :documents, as: :documentable, dependent: :destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
attr_reader :max_documents_allowed, :max_file_size, :accepted_content_types
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def documentable(options= {})
|
||||||
|
@max_documents_allowed = options[:max_documents_allowed]
|
||||||
|
@max_file_size = options[:max_file_size]
|
||||||
|
@accepted_content_types = options[:accepted_content_types]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
81
app/models/document.rb
Normal file
81
app/models/document.rb
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
class Document < ActiveRecord::Base
|
||||||
|
include DocumentsHelper
|
||||||
|
include DocumentablesHelper
|
||||||
|
has_attached_file :attachment, path: ":rails_root/public/system/:class/:prefix/:style/:filename"
|
||||||
|
attr_accessor :cached_attachment
|
||||||
|
|
||||||
|
belongs_to :user
|
||||||
|
belongs_to :documentable, polymorphic: true
|
||||||
|
|
||||||
|
# Disable paperclip security validation due to polymorphic configuration
|
||||||
|
# Paperclip do not allow to user Procs on valiations definition
|
||||||
|
do_not_validate_attachment_file_type :attachment
|
||||||
|
validate :attachment_presence
|
||||||
|
validate :validate_attachment_content_type, if: -> { attachment.present? }
|
||||||
|
validate :validate_attachment_size, if: -> { attachment.present? }
|
||||||
|
validates :title, presence: true
|
||||||
|
validates :user_id, presence: true
|
||||||
|
validates :documentable_id, presence: true, if: -> { persisted? }
|
||||||
|
validates :documentable_type, presence: true, if: -> { persisted? }
|
||||||
|
|
||||||
|
after_save :remove_cached_document, if: -> { valid? && persisted? && cached_attachment.present? }
|
||||||
|
|
||||||
|
def set_cached_attachment_from_attachment(prefix)
|
||||||
|
self.cached_attachment = if Paperclip::Attachment.default_options[:storage] == :filesystem
|
||||||
|
attachment.path
|
||||||
|
else
|
||||||
|
prefix + attachment.url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_attachment_from_cached_attachment
|
||||||
|
self.attachment = if Paperclip::Attachment.default_options[:storage] == :filesystem
|
||||||
|
File.open(cached_attachment)
|
||||||
|
else
|
||||||
|
URI.parse(cached_attachment)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Paperclip.interpolates :prefix do |attachment, style|
|
||||||
|
attachment.instance.prefix(attachment, style)
|
||||||
|
end
|
||||||
|
|
||||||
|
def prefix(attachment, style)
|
||||||
|
if !attachment.instance.persisted?
|
||||||
|
"cached_attachments/user/#{attachment.instance.user_id}"
|
||||||
|
else
|
||||||
|
":attachment/:id_partition"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def validate_attachment_size
|
||||||
|
if documentable.present? &&
|
||||||
|
attachment_file_size > documentable.class.max_file_size
|
||||||
|
errors[:attachment] = I18n.t("documents.errors.messages.in_between",
|
||||||
|
min: "0 Bytes",
|
||||||
|
max: "#{max_file_size(documentable)} MB")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_attachment_content_type
|
||||||
|
if documentable.present? &&
|
||||||
|
!accepted_content_types(documentable).include?(attachment_content_type)
|
||||||
|
errors[:attachment] = I18n.t("documents.errors.messages.wrong_content_type",
|
||||||
|
content_type: attachment_content_type,
|
||||||
|
accepted_content_types: humanized_accepted_content_types(documentable))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def attachment_presence
|
||||||
|
if attachment.blank? && cached_attachment.blank?
|
||||||
|
errors[:attachment] = I18n.t("errors.messages.blank")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_cached_document
|
||||||
|
File.delete(cached_attachment) if File.exists?(cached_attachment)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -9,6 +9,11 @@ class Proposal < ActiveRecord::Base
|
|||||||
include HasPublicAuthor
|
include HasPublicAuthor
|
||||||
include Graphqlable
|
include Graphqlable
|
||||||
include Followable
|
include Followable
|
||||||
|
include Documentable
|
||||||
|
documentable max_documents_allowed: 3,
|
||||||
|
max_file_size: 3.megabytes,
|
||||||
|
accepted_content_types: [ "application/pdf" ]
|
||||||
|
accepts_nested_attributes_for :documents, allow_destroy: true
|
||||||
|
|
||||||
acts_as_votable
|
acts_as_votable
|
||||||
acts_as_paranoid column: :hidden_at
|
acts_as_paranoid column: :hidden_at
|
||||||
|
|||||||
@@ -17,6 +17,14 @@
|
|||||||
</h3>
|
</h3>
|
||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="tabs-title">
|
||||||
|
<%= link_to "#tab-documents" do %>
|
||||||
|
<h3>
|
||||||
|
<%= t("documents.tab") %>
|
||||||
|
(<%= @investment.documents.count %>)
|
||||||
|
</h3>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,6 +21,10 @@
|
|||||||
<%= f.text_field :external_url %>
|
<%= f.text_field :external_url %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="documents small-12 column" data-max-documents="<%= Budget::Investment.max_documents_allowed %>">
|
||||||
|
<%= render 'documents/nested_documents', documentable: @investment %>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="small-12 column">
|
<div class="small-12 column">
|
||||||
<%= f.text_field :location %>
|
<%= f.text_field :location %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,6 +4,12 @@
|
|||||||
<div class="small-12 medium-9 column">
|
<div class="small-12 medium-9 column">
|
||||||
<%= back_link_to budget_investments_path(investment.budget, heading_id: investment.heading) %>
|
<%= back_link_to budget_investments_path(investment.budget, heading_id: investment.heading) %>
|
||||||
|
|
||||||
|
<% if can?(:create, @document) && investment.documents.size < Budget::Investment.max_documents_allowed %>
|
||||||
|
<%= link_to t("documents.upload_document"),
|
||||||
|
new_document_path(documentable_id:investment, documentable_type: investment.class.name, from: request.url),
|
||||||
|
class: 'button hollow float-right' %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<h1><%= investment.title %></h1>
|
<h1><%= investment.title %></h1>
|
||||||
|
|
||||||
<div class="budget-investment-info">
|
<div class="budget-investment-info">
|
||||||
@@ -14,7 +20,6 @@
|
|||||||
<span class="bullet"> • </span>
|
<span class="bullet"> • </span>
|
||||||
<%= investment.heading.name %>
|
<%= investment.heading.name %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<p id="investment_code">
|
<p id="investment_code">
|
||||||
<%= t("budgets.investments.show.code_html", code: investment.id) %>
|
<%= t("budgets.investments.show.code_html", code: investment.id) %>
|
||||||
@@ -51,6 +56,7 @@
|
|||||||
<h2><%= t('budgets.investments.show.price_explanation') %></h2>
|
<h2><%= t('budgets.investments.show.price_explanation') %></h2>
|
||||||
<p><%= investment.price_explanation %></p>
|
<p><%= investment.price_explanation %></p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<aside class="small-12 medium-3 column">
|
<aside class="small-12 medium-3 column">
|
||||||
|
|||||||
@@ -19,4 +19,11 @@
|
|||||||
comment_flags: @comment_flags,
|
comment_flags: @comment_flags,
|
||||||
display_comments_count: false } %>
|
display_comments_count: false } %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="tabs-panel" id="tab-documents">
|
||||||
|
<%= render 'documents/documents',
|
||||||
|
documents: @investment.documents,
|
||||||
|
max_documents_allowed: Budget::Investment.max_documents_allowed %>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
20
app/views/documents/_document.html.erb
Normal file
20
app/views/documents/_document.html.erb
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<tr id="<%= dom_id(document)%>">
|
||||||
|
<td class="document-link">
|
||||||
|
<%= document.title %>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<%= link_to t('documents.buttons.download_document'),
|
||||||
|
document.attachment.url,
|
||||||
|
target: "_blank",
|
||||||
|
rel: "nofollow",
|
||||||
|
class: 'button hollow' %>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<% if can? :destroy, Document %>
|
||||||
|
<%= link_to t('documents.buttons.destroy_document'),
|
||||||
|
document_path(document, from: request.url), method: :delete,
|
||||||
|
data: { confirm: t('documents.actions.destroy.confirm') },
|
||||||
|
class: 'button hollow alert' %>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
35
app/views/documents/_documents.html.erb
Normal file
35
app/views/documents/_documents.html.erb
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<% if documents.any? %>
|
||||||
|
|
||||||
|
<% if documents.size == max_documents_allowed && can?(:create, Document) %>
|
||||||
|
<div class="row documents-list">
|
||||||
|
<div class="small-12 column">
|
||||||
|
<div class="callout warning text-center">
|
||||||
|
<%= t "documents.max_documents_allowed_reached_html" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div class="row documents-list">
|
||||||
|
<div class="small-12 column">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<% documents.each do |document| %>
|
||||||
|
<%= render "documents/document", document: document %>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% else %>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="small-12 column">
|
||||||
|
<div class="callout primary text-center">
|
||||||
|
<%= t('documents.no_documents') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% end %>
|
||||||
20
app/views/documents/_form.html.erb
Normal file
20
app/views/documents/_form.html.erb
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<%= form_for @document,
|
||||||
|
url: documents_path(
|
||||||
|
documentable_type: @document.documentable_type,
|
||||||
|
documentable_id: @document.documentable_id,
|
||||||
|
from: params[:from]
|
||||||
|
),
|
||||||
|
html: { multipart: true, class: "documentable"},
|
||||||
|
data: { direct_upload_url: upload_documents_url(documentable_type: @document.documentable_type, documentable_id: @document.documentable_id) } do |f| %>
|
||||||
|
|
||||||
|
<%= render 'shared/errors', resource: @document %>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<%= render 'plain_fields', document: @document %>
|
||||||
|
|
||||||
|
<div class="actions small-12 medium-6 large-4 end column">
|
||||||
|
<%= f.submit(t("documents.form.submit_button"), class: "button expanded") %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
22
app/views/documents/_nested_documents.html.erb
Normal file
22
app/views/documents/_nested_documents.html.erb
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<div class="documents-list">
|
||||||
|
<%= label_tag :documents, t("documents.form.title") %>
|
||||||
|
<p class="help-text"><%= documentables_note(documentable) %></p>
|
||||||
|
|
||||||
|
<% documentable.documents.each_with_index do |document, index| %>
|
||||||
|
<%= render 'documents/nested_fields', document: document, index: index, documentable: documentable %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% unless max_documents_allowed?(documentable) %>
|
||||||
|
<%= link_to t("documents.form.add_new_document"),
|
||||||
|
new_nested_documents_path(documentable_type: documentable.class.name, index: documentable.documents.size),
|
||||||
|
remote: true,
|
||||||
|
id: "new_document_link",
|
||||||
|
class: "button hollow" %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div class="max-documents-notice callout warning text-center <%= "hide" unless max_documents_allowed?(documentable) %>">
|
||||||
|
<%= t "documents.max_documents_allowed_reached_html" %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
31
app/views/documents/_nested_fields.html.erb
Normal file
31
app/views/documents/_nested_fields.html.erb
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<div id="<%= document_nested_field_wrapper_id(index) %>" class="document">
|
||||||
|
<%= hidden_field_tag :id,
|
||||||
|
document.id,
|
||||||
|
name: document_nested_field_name(document, index, :id),
|
||||||
|
id: document_nested_field_id(document, index, :id) if document.persisted? %>
|
||||||
|
<%= hidden_field_tag :user_id,
|
||||||
|
current_user.id,
|
||||||
|
name: document_nested_field_name(document, index, :user_id),
|
||||||
|
id: document_nested_field_id(document, index, :user_id) %>
|
||||||
|
<%= hidden_field_tag :cached_attachment,
|
||||||
|
document.cached_attachment,
|
||||||
|
name: document_nested_field_name(document, index, :cached_attachment),
|
||||||
|
id: document_nested_field_id(document, index, :cached_attachment) %>
|
||||||
|
|
||||||
|
<%= label_tag :title, t("activerecord.attributes.document.title") %>
|
||||||
|
<%= text_field_tag :title,
|
||||||
|
document.title,
|
||||||
|
name: document_nested_field_name(document, index, :title),
|
||||||
|
id: document_nested_field_id(document, index, :title),
|
||||||
|
class: "document-title" %>
|
||||||
|
<% if document.errors[:title].any? %>
|
||||||
|
<small class="error"><%= document.errors[:title].join(", ") %></small>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= render_attachment(document, index) %>
|
||||||
|
|
||||||
|
<%= render_destroy_document_link(document, index) %>
|
||||||
|
<p class="file-name"><%= document_attachment_file_name(document) %></p>
|
||||||
|
<div class="progress-bar-placeholder"><div class="loading-bar"></div></div>
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
50
app/views/documents/_plain_fields.html.erb
Normal file
50
app/views/documents/_plain_fields.html.erb
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<div id="plain_document_fields" class="document">
|
||||||
|
|
||||||
|
<div class="small-12 column">
|
||||||
|
<%= label_tag :document_title, t("activerecord.attributes.document.title") %>
|
||||||
|
<%= text_field_tag :document_title, document.title, name: "document[title]", class: "document-title" %>
|
||||||
|
<% if document.errors.has_key?(:title) %>
|
||||||
|
<small class="error"><%= document.errors[:title].join(", ") %></small>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="small-12 column">
|
||||||
|
<%= hidden_field_tag :cached_attachment, document.cached_attachment, name: "document[cached_attachment]" %>
|
||||||
|
<%= file_field_tag :attachment,
|
||||||
|
accept: accepted_content_types_extensions(document.documentable.class),
|
||||||
|
label: false,
|
||||||
|
class: 'document_ajax_attachment',
|
||||||
|
data: {
|
||||||
|
url: upload_documents_url(documentable_type: document.documentable_type, documentable_id: document.documentable_id),
|
||||||
|
cached_attachment_input_field: "document_cached_attachment",
|
||||||
|
multiple: false,
|
||||||
|
nested_document: false
|
||||||
|
},
|
||||||
|
id: "document_attachment",
|
||||||
|
name: "document[attachment]" %>
|
||||||
|
|
||||||
|
<% if document.cached_attachment.blank? %>
|
||||||
|
<%= label_tag :document_attachment, t("documents.form.attachment_label"), class: 'button hollow' %>
|
||||||
|
<% else %>
|
||||||
|
<%= link_to t('documents.form.delete_button'),
|
||||||
|
destroy_upload_documents_path(path: document.cached_attachment,
|
||||||
|
nested_document: false,
|
||||||
|
documentable_type: document.documentable_type,
|
||||||
|
documentable_id: document.documentable_id),
|
||||||
|
method: :delete,
|
||||||
|
remote: true,
|
||||||
|
class: "delete float-right" %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if document.errors.has_key?(:attachment) %>
|
||||||
|
<div class="small-12 column source-option-file">
|
||||||
|
<div class="attachment-errors">
|
||||||
|
<small class="error"><%= errors_on_attachment(document) %></small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<p class="file-name"><%= document_attachment_file_name(document) %></p>
|
||||||
|
<div class="progress-bar-placeholder"><div class="loading-bar"></div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
17
app/views/documents/destroy.js.erb
Normal file
17
app/views/documents/destroy.js.erb
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<% if params[:nested_document] == "true" %>
|
||||||
|
|
||||||
|
App.Documentable.destroyNestedDocument("<%= document_nested_field_wrapper_id(params[:index]) %>", "<%= j render('layouts/flash') %>")
|
||||||
|
<% new_document_link = link_to t("documents.form.add_new_document"),
|
||||||
|
new_nested_documents_path(documentable_type: @document.documentable_type, index: params[:index]),
|
||||||
|
remote: true,
|
||||||
|
id: "new_document_link",
|
||||||
|
class: "button hollow" %>
|
||||||
|
App.Documentable.updateNewDocumentButton("<%= j new_document_link %>")
|
||||||
|
|
||||||
|
<% else %>
|
||||||
|
|
||||||
|
App.Documentable.replacePlainDocument("plain_document_fields",
|
||||||
|
"<%= j render('layouts/flash') %>",
|
||||||
|
"<%= j render('plain_fields', document: @document) %>")
|
||||||
|
|
||||||
|
<% end %>
|
||||||
27
app/views/documents/new.html.erb
Normal file
27
app/views/documents/new.html.erb
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<div class="document-form <%= documentable_class(@document.documentable) %> row">
|
||||||
|
|
||||||
|
<div class="small-12 medium-9 column">
|
||||||
|
<%= back_link_to params[:from] %>
|
||||||
|
<h1><%= t("documents.new.title") %></h1>
|
||||||
|
<%= render "documents/form", form_url: documents_url %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="small-12 medium-3 column">
|
||||||
|
<span class="icon-documents float-right"></span>
|
||||||
|
<h2><%= t("documents.recommendations_title") %></h2>
|
||||||
|
<ul class="recommendations">
|
||||||
|
<li>
|
||||||
|
<%= t "documents.recommendation_one_html",
|
||||||
|
max_documents_allowed: max_documents_allowed(@document.documentable) %>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= t "documents.recommendation_two_html",
|
||||||
|
accepted_content_types: humanized_accepted_content_types(@document.documentable) %>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= t "documents.recommendation_three_html",
|
||||||
|
max_file_size: max_file_size(@document.documentable) %>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
9
app/views/documents/new_nested.js.erb
Normal file
9
app/views/documents/new_nested.js.erb
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<%
|
||||||
|
new_document_link = link_to t("documents.form.add_new_document"),
|
||||||
|
new_nested_documents_path(documentable_type: params[:documentable_type], index: params[:index].to_i + 1),
|
||||||
|
remote: true,
|
||||||
|
id: "new_document_link",
|
||||||
|
class: "button hollow"
|
||||||
|
%>
|
||||||
|
App.Documentable.new("<%= j render('documents/nested_fields', document: @document, index: params[:index]) %>")
|
||||||
|
App.Documentable.updateNewDocumentButton("<%= j new_document_link %>")
|
||||||
12
app/views/documents/upload.js.erb
Normal file
12
app/views/documents/upload.js.erb
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<% if params[:nested_document] == "true" %>
|
||||||
|
|
||||||
|
App.Documentable.uploadNestedDocument("<%= document_nested_field_wrapper_id(params[:index]) %>",
|
||||||
|
"<%= j render('documents/nested_fields', document: @document, index: params[:index]) %>",
|
||||||
|
<%= @document.cached_attachment.present? %>)
|
||||||
|
<% else %>
|
||||||
|
|
||||||
|
App.Documentable.uploadPlainDocument("plain_document_fields",
|
||||||
|
"<%= j render('documents/plain_fields', document: @document) %>",
|
||||||
|
<%= @document.cached_attachment.present? %>)
|
||||||
|
|
||||||
|
<% end %>
|
||||||
@@ -17,6 +17,14 @@
|
|||||||
</h3>
|
</h3>
|
||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="tabs-title">
|
||||||
|
<%= link_to "#tab-documents" do %>
|
||||||
|
<h3>
|
||||||
|
<%= t("documents.tab") %>
|
||||||
|
(<%= @proposal.documents.count %>)
|
||||||
|
</h3>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -34,7 +34,6 @@
|
|||||||
<%= f.cktext_area :description, maxlength: Proposal.description_max_length, ckeditor: { language: I18n.locale }, label: false %>
|
<%= f.cktext_area :description, maxlength: Proposal.description_max_length, ckeditor: { language: I18n.locale }, label: false %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="small-12 column">
|
<div class="small-12 column">
|
||||||
<%= f.label :video_url, t("proposals.form.proposal_video_url") %>
|
<%= f.label :video_url, t("proposals.form.proposal_video_url") %>
|
||||||
<p class="help-text" id="video-url-help-text"><%= t("proposals.form.proposal_video_url_note") %></p>
|
<p class="help-text" id="video-url-help-text"><%= t("proposals.form.proposal_video_url_note") %></p>
|
||||||
@@ -47,6 +46,10 @@
|
|||||||
<%= f.text_field :external_url, placeholder: t("proposals.form.proposal_external_url"), label: false %>
|
<%= f.text_field :external_url, placeholder: t("proposals.form.proposal_external_url"), label: false %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="documents small-12 column" data-max-documents="<%= Proposal.max_documents_allowed %>">
|
||||||
|
<%= render 'documents/nested_documents', documentable: @proposal %>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="small-12 medium-6 column">
|
<div class="small-12 medium-6 column">
|
||||||
<%= f.label :geozone_id, t("proposals.form.geozone") %>
|
<%= f.label :geozone_id, t("proposals.form.geozone") %>
|
||||||
<%= f.select :geozone_id, geozone_select_options, {include_blank: t("geozones.none"), label: false} %>
|
<%= f.select :geozone_id, geozone_select_options, {include_blank: t("geozones.none"), label: false} %>
|
||||||
|
|||||||
@@ -16,6 +16,12 @@
|
|||||||
<div class="small-12 medium-9 column">
|
<div class="small-12 medium-9 column">
|
||||||
<%= back_link_to %>
|
<%= back_link_to %>
|
||||||
|
|
||||||
|
<% if can?(:create, @document) && @proposal.documents.size < Proposal.max_documents_allowed %>
|
||||||
|
<%= link_to t("documents.upload_document"),
|
||||||
|
new_document_path(documentable_id: @proposal, documentable_type: @proposal.class.name, from: request.url),
|
||||||
|
class: 'button hollow float-right' %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<% if author_of?(@proposal, current_user) %>
|
<% if author_of?(@proposal, current_user) %>
|
||||||
<%= link_to t("proposals.show.send_notification"), new_proposal_notification_path(proposal_id: @proposal.id),
|
<%= link_to t("proposals.show.send_notification"), new_proposal_notification_path(proposal_id: @proposal.id),
|
||||||
class: 'button hollow float-right' %>
|
class: 'button hollow float-right' %>
|
||||||
@@ -165,4 +171,10 @@
|
|||||||
<div class="tabs-panel is-active" id="tab-comments">
|
<div class="tabs-panel is-active" id="tab-comments">
|
||||||
<%= render "proposals/comments" %>
|
<%= render "proposals/comments" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="tabs-panel" id="tab-documents">
|
||||||
|
<%= render 'documents/documents',
|
||||||
|
documents: @proposal.documents,
|
||||||
|
max_documents_allowed: Proposal.max_documents_allowed %>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ data:
|
|||||||
- config/locales/%{locale}/officing.yml
|
- config/locales/%{locale}/officing.yml
|
||||||
- config/locales/%{locale}/budgets.yml
|
- config/locales/%{locale}/budgets.yml
|
||||||
- config/locales/%{locale}/legislation.yml
|
- config/locales/%{locale}/legislation.yml
|
||||||
|
- config/locales/%{locale}/documents.yml
|
||||||
|
|
||||||
# Locale files to write new keys to, based on a list of key pattern => file rules. Matched from top to bottom:
|
# Locale files to write new keys to, based on a list of key pattern => file rules. Matched from top to bottom:
|
||||||
# `i18n-tasks normalize -p` will force move the keys according to these rules
|
# `i18n-tasks normalize -p` will force move the keys according to these rules
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ en:
|
|||||||
legislation/answers:
|
legislation/answers:
|
||||||
one: "Answer"
|
one: "Answer"
|
||||||
other: "Answers"
|
other: "Answers"
|
||||||
|
documents:
|
||||||
|
one: "Document"
|
||||||
|
other: "Documents"
|
||||||
attributes:
|
attributes:
|
||||||
budget:
|
budget:
|
||||||
name: "Name"
|
name: "Name"
|
||||||
@@ -197,6 +200,9 @@ en:
|
|||||||
value: Value
|
value: Value
|
||||||
legislation/annotation:
|
legislation/annotation:
|
||||||
text: Comment
|
text: Comment
|
||||||
|
document:
|
||||||
|
title: Title
|
||||||
|
attachment: Attachment
|
||||||
errors:
|
errors:
|
||||||
models:
|
models:
|
||||||
user:
|
user:
|
||||||
|
|||||||
35
config/locales/en/documents.yml
Normal file
35
config/locales/en/documents.yml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
en:
|
||||||
|
documents:
|
||||||
|
tab: Documents
|
||||||
|
no_documents: Don't have uploaded documents
|
||||||
|
upload_document: Upload document
|
||||||
|
max_documents_allowed_reached_html: You have reached the maximum number of documents allowed! <strong>You have to delete one before you can upload another.</strong>
|
||||||
|
form:
|
||||||
|
title: Documents
|
||||||
|
attachment_label: Choose document
|
||||||
|
submit_button: Upload document
|
||||||
|
delete_button: Remove document
|
||||||
|
note: "You can upload up to a maximum of %{max_documents_allowed} documents of following content types: %{accepted_content_types}, up to %{max_file_size} MB per file."
|
||||||
|
add_new_document: Add new document
|
||||||
|
new:
|
||||||
|
title: Upload document
|
||||||
|
recommendations_title: File upload tips
|
||||||
|
recommendation_one_html: You can upload up to a <strong>maximum of %{max_documents_allowed} documents</strong>.
|
||||||
|
recommendation_two_html: You can upload <strong>%{accepted_content_types}</strong> files.
|
||||||
|
recommendation_three_html: You can upload files up to <strong>%{max_file_size} MB</strong>.
|
||||||
|
|
||||||
|
actions:
|
||||||
|
create:
|
||||||
|
notice: Document was created successfully.
|
||||||
|
alert: Cannot create document. Check form errors and try again.
|
||||||
|
destroy:
|
||||||
|
notice: Document was deleted successfully.
|
||||||
|
alert: Cannot destroy document.
|
||||||
|
confirm: Are you sure you want to delete the document? This action cannot be undone!
|
||||||
|
buttons:
|
||||||
|
download_document: Dowload file
|
||||||
|
destroy_document: Destroy
|
||||||
|
errors:
|
||||||
|
messages:
|
||||||
|
in_between: must be in between %{min} and %{max}
|
||||||
|
wrong_content_type: content type %{content_type} does not match any of accepted content types %{accepted_content_types}
|
||||||
@@ -181,6 +181,7 @@ en:
|
|||||||
user: Account
|
user: Account
|
||||||
verification/sms: phone
|
verification/sms: phone
|
||||||
signature_sheet: Signature sheet
|
signature_sheet: Signature sheet
|
||||||
|
document: Document
|
||||||
geozones:
|
geozones:
|
||||||
none: All city
|
none: All city
|
||||||
all: All scopes
|
all: All scopes
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ es:
|
|||||||
legislation/answers:
|
legislation/answers:
|
||||||
one: "Respuesta"
|
one: "Respuesta"
|
||||||
other: "Respuestas"
|
other: "Respuestas"
|
||||||
|
documents:
|
||||||
|
one: "Documento"
|
||||||
|
other: "Documentos"
|
||||||
attributes:
|
attributes:
|
||||||
budget:
|
budget:
|
||||||
name: "Nombre"
|
name: "Nombre"
|
||||||
@@ -192,6 +195,9 @@ es:
|
|||||||
value: Valor
|
value: Valor
|
||||||
legislation/annotation:
|
legislation/annotation:
|
||||||
text: Comentario
|
text: Comentario
|
||||||
|
document:
|
||||||
|
title: Título
|
||||||
|
attachment: Archivo adjunto
|
||||||
errors:
|
errors:
|
||||||
models:
|
models:
|
||||||
user:
|
user:
|
||||||
|
|||||||
34
config/locales/es/documents.yml
Normal file
34
config/locales/es/documents.yml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
es:
|
||||||
|
documents:
|
||||||
|
tab: Documentos
|
||||||
|
no_documents: No hay documentos subidos
|
||||||
|
upload_document: Subir documento
|
||||||
|
max_documents_allowed_reached_html: ¡Has alcanzado el número máximo de documentos permitidos! <strong>Tienes que eliminar uno antes de poder subir otro.</strong>
|
||||||
|
form:
|
||||||
|
title: Documentos
|
||||||
|
attachment_label: Selecciona un documento
|
||||||
|
submit_button: Subir documento
|
||||||
|
delete_button: Eliminar documento
|
||||||
|
note: "Puedes subir hasta un máximo de %{max_documents_allowed} documentos en los formatos: %{accepted_content_types}, y de hasta %{max_file_size} MB por archivo."
|
||||||
|
add_new_document: Añadir nuevo documento
|
||||||
|
new:
|
||||||
|
title: Subir un documento
|
||||||
|
recommendations_title: Consejos para subir archivos
|
||||||
|
recommendation_one_html: Puedes subir hasta un máximo de <strong>%{max_documents_allowed} documentos</strong>
|
||||||
|
recommendation_two_html: Sólo puedes subir <strong>archivos %{accepted_content_types}</strong>.
|
||||||
|
recommendation_three_html: Puedes subir archivos de hasta <strong>%{max_file_size} MB</strong>
|
||||||
|
actions:
|
||||||
|
create:
|
||||||
|
notice: "El documento se ha creado correctamente."
|
||||||
|
alert: "El documento no se ha podido crear. Revise los errores del formulario."
|
||||||
|
destroy:
|
||||||
|
notice: "El documento se ha eliminado correctamente."
|
||||||
|
alert: "El documento no se ha podido eliminar."
|
||||||
|
confirm: "¿Está seguro de que desea eliminar el documento? Esta acción no se puede deshacer!"
|
||||||
|
buttons:
|
||||||
|
download_document: Descargar archivo
|
||||||
|
destroy_document: Eliminar
|
||||||
|
errors:
|
||||||
|
messages:
|
||||||
|
in_between: debe estar entre %{min} y %{max}
|
||||||
|
wrong_content_type: El tipo de contenido %{content_type} del archivo no coincide con ninguno de los tipos de contenido aceptados %{accepted_content_types}
|
||||||
@@ -181,6 +181,7 @@ es:
|
|||||||
user: la cuenta
|
user: la cuenta
|
||||||
verification/sms: el teléfono
|
verification/sms: el teléfono
|
||||||
signature_sheet: la hoja de firmas
|
signature_sheet: la hoja de firmas
|
||||||
|
document: el documento
|
||||||
geozones:
|
geozones:
|
||||||
none: Toda la ciudad
|
none: Toda la ciudad
|
||||||
all: Todos los ámbitos de actuación
|
all: Todos los ámbitos de actuación
|
||||||
|
|||||||
@@ -95,6 +95,14 @@ Rails.application.routes.draw do
|
|||||||
|
|
||||||
resources :follows, only: [:create, :destroy]
|
resources :follows, only: [:create, :destroy]
|
||||||
|
|
||||||
|
resources :documents, only: [:new, :create, :destroy] do
|
||||||
|
collection do
|
||||||
|
get :new_nested
|
||||||
|
delete :destroy_upload
|
||||||
|
post :upload
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
resources :stats, only: [:index]
|
resources :stats, only: [:index]
|
||||||
|
|
||||||
resources :legacy_legislations, only: [:show], path: 'legislations'
|
resources :legacy_legislations, only: [:show], path: 'legislations'
|
||||||
|
|||||||
14
db/migrate/20170720092638_create_documents.rb
Normal file
14
db/migrate/20170720092638_create_documents.rb
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
class CreateDocuments < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :documents do |t|
|
||||||
|
t.string :title
|
||||||
|
t.attachment :attachment
|
||||||
|
t.references :user, index: true, foreign_key: true
|
||||||
|
t.references :documentable, polymorphic: true, index: true
|
||||||
|
|
||||||
|
t.timestamps null: false
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :documents, [:user_id, :documentable_type, :documentable_id], name: "access_documents"
|
||||||
|
end
|
||||||
|
end
|
||||||
20
db/schema.rb
20
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: 20170719174326) do
|
ActiveRecord::Schema.define(version: 20170720092638) 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"
|
||||||
@@ -298,6 +298,23 @@ ActiveRecord::Schema.define(version: 20170719174326) do
|
|||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "documents", force: :cascade do |t|
|
||||||
|
t.string "title"
|
||||||
|
t.string "attachment_file_name"
|
||||||
|
t.string "attachment_content_type"
|
||||||
|
t.integer "attachment_file_size"
|
||||||
|
t.datetime "attachment_updated_at"
|
||||||
|
t.integer "user_id"
|
||||||
|
t.integer "documentable_id"
|
||||||
|
t.string "documentable_type"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "documents", ["documentable_type", "documentable_id"], name: "index_documents_on_documentable_type_and_documentable_id", using: :btree
|
||||||
|
add_index "documents", ["user_id", "documentable_type", "documentable_id"], name: "access_documents", using: :btree
|
||||||
|
add_index "documents", ["user_id"], name: "index_documents_on_user_id", using: :btree
|
||||||
|
|
||||||
create_table "failed_census_calls", force: :cascade do |t|
|
create_table "failed_census_calls", force: :cascade do |t|
|
||||||
t.integer "user_id"
|
t.integer "user_id"
|
||||||
t.string "document_number"
|
t.string "document_number"
|
||||||
@@ -1014,6 +1031,7 @@ ActiveRecord::Schema.define(version: 20170719174326) do
|
|||||||
add_foreign_key "administrators", "users"
|
add_foreign_key "administrators", "users"
|
||||||
add_foreign_key "annotations", "legacy_legislations"
|
add_foreign_key "annotations", "legacy_legislations"
|
||||||
add_foreign_key "annotations", "users"
|
add_foreign_key "annotations", "users"
|
||||||
|
add_foreign_key "documents", "users"
|
||||||
add_foreign_key "failed_census_calls", "poll_officers"
|
add_foreign_key "failed_census_calls", "poll_officers"
|
||||||
add_foreign_key "failed_census_calls", "users"
|
add_foreign_key "failed_census_calls", "users"
|
||||||
add_foreign_key "flags", "users"
|
add_foreign_key "flags", "users"
|
||||||
|
|||||||
@@ -368,6 +368,20 @@ FactoryGirl.define do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
factory :document do
|
||||||
|
sequence(:title) { |n| "Document title #{n}" }
|
||||||
|
association :user, factory: :user
|
||||||
|
attachment { File.new("spec/fixtures/files/empty.pdf") }
|
||||||
|
|
||||||
|
trait :proposal_document do
|
||||||
|
association :documentable, factory: :proposal
|
||||||
|
end
|
||||||
|
|
||||||
|
trait :budget_investment_document do
|
||||||
|
association :documentable, factory: :budget_investment
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
factory :comment do
|
factory :comment do
|
||||||
association :commentable, factory: :debate
|
association :commentable, factory: :debate
|
||||||
user
|
user
|
||||||
|
|||||||
@@ -429,7 +429,17 @@ feature 'Budget Investments' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like "followable", "budget_investment", "budget_investment_path", {"budget_id": "budget_id", "id": "id"}
|
it_behaves_like "followable", "budget_investment", "budget_investment_path", { "budget_id": "budget_id", "id": "id" }
|
||||||
|
|
||||||
|
it_behaves_like "documentable", "budget_investment", "budget_investment_path", {"budget_id": "budget_id", "id": "id"}
|
||||||
|
|
||||||
|
it_behaves_like "nested documentable",
|
||||||
|
"budget_investment",
|
||||||
|
"new_budget_investment_path",
|
||||||
|
{ "budget_id": "budget_id" },
|
||||||
|
"fill_new_valid_budget_investment",
|
||||||
|
"Create Investment",
|
||||||
|
"Budget Investment created successfully."
|
||||||
|
|
||||||
context "Destroy" do
|
context "Destroy" do
|
||||||
|
|
||||||
|
|||||||
@@ -1317,6 +1317,24 @@ feature 'Proposals' do
|
|||||||
|
|
||||||
it_behaves_like "followable", "proposal", "proposal_path", { "id": "id" }
|
it_behaves_like "followable", "proposal", "proposal_path", { "id": "id" }
|
||||||
|
|
||||||
|
it_behaves_like "documentable", "proposal", "proposal_path", { "id": "id" }
|
||||||
|
|
||||||
|
it_behaves_like "nested documentable",
|
||||||
|
"proposal",
|
||||||
|
"new_proposal_path",
|
||||||
|
{ },
|
||||||
|
"fill_new_valid_proposal",
|
||||||
|
"Create proposal",
|
||||||
|
"Proposal created successfully"
|
||||||
|
|
||||||
|
it_behaves_like "nested documentable",
|
||||||
|
"proposal",
|
||||||
|
"edit_proposal_path",
|
||||||
|
{ "id": "id" },
|
||||||
|
nil,
|
||||||
|
"Save changes",
|
||||||
|
"Proposal updated successfully"
|
||||||
|
|
||||||
scenario 'Erased author' do
|
scenario 'Erased author' do
|
||||||
user = create(:user)
|
user = create(:user)
|
||||||
proposal = create(:proposal, author: user)
|
proposal = create(:proposal, author: user)
|
||||||
|
|||||||
BIN
spec/fixtures/files/empty.pdf
vendored
Normal file
BIN
spec/fixtures/files/empty.pdf
vendored
Normal file
Binary file not shown.
@@ -12,8 +12,12 @@ describe "Abilities::Administrator" do
|
|||||||
let(:debate) { create(:debate) }
|
let(:debate) { create(:debate) }
|
||||||
let(:comment) { create(:comment) }
|
let(:comment) { create(:comment) }
|
||||||
let(:proposal) { create(:proposal) }
|
let(:proposal) { create(:proposal) }
|
||||||
|
let(:budget_investment) { create(:budget_investment) }
|
||||||
let(:legislation_question) { create(:legislation_question) }
|
let(:legislation_question) { create(:legislation_question) }
|
||||||
|
|
||||||
|
let(:proposal_document) { build(:document, documentable: proposal) }
|
||||||
|
let(:budget_investment_document) { build(:document, documentable: budget_investment) }
|
||||||
|
|
||||||
let(:hidden_debate) { create(:debate, :hidden) }
|
let(:hidden_debate) { create(:debate, :hidden) }
|
||||||
let(:hidden_comment) { create(:comment, :hidden) }
|
let(:hidden_comment) { create(:comment, :hidden) }
|
||||||
let(:hidden_proposal) { create(:proposal, :hidden) }
|
let(:hidden_proposal) { create(:proposal, :hidden) }
|
||||||
@@ -71,4 +75,12 @@ describe "Abilities::Administrator" do
|
|||||||
|
|
||||||
it { should be_able_to(:valuate, create(:budget_investment, budget: create(:budget, phase: 'valuating'))) }
|
it { should be_able_to(:valuate, create(:budget_investment, budget: create(:budget, phase: 'valuating'))) }
|
||||||
it { should be_able_to(:valuate, create(:budget_investment, budget: create(:budget, phase: 'finished'))) }
|
it { should be_able_to(:valuate, create(:budget_investment, budget: create(:budget, phase: 'finished'))) }
|
||||||
|
|
||||||
|
it { should be_able_to(:new, proposal_document) }
|
||||||
|
it { should be_able_to(:create, proposal_document) }
|
||||||
|
it { should be_able_to(:destroy, proposal_document) }
|
||||||
|
|
||||||
|
it { should be_able_to(:new, budget_investment_document) }
|
||||||
|
it { should be_able_to(:create, budget_investment_document) }
|
||||||
|
it { should be_able_to(:destroy, budget_investment_document) }
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ describe "Abilities::Common" do
|
|||||||
let(:incoming_poll_question_from_other_geozone) { create(:poll_question, poll: incoming_poll_from_other_geozone) }
|
let(:incoming_poll_question_from_other_geozone) { create(:poll_question, poll: incoming_poll_from_other_geozone) }
|
||||||
let(:incoming_poll_question_from_all_geozones) { create(:poll_question, poll: incoming_poll) }
|
let(:incoming_poll_question_from_all_geozones) { create(:poll_question, poll: incoming_poll) }
|
||||||
|
|
||||||
|
let(:own_proposal_document) { build(:document, documentable: own_proposal) }
|
||||||
|
let(:proposal_document) { build(:document, documentable: proposal) }
|
||||||
|
let(:own_budget_investment_document) { build(:document, documentable: own_investment_in_accepting_budget) }
|
||||||
|
let(:budget_investment_document) { build(:document, documentable: investment_in_accepting_budget) }
|
||||||
|
|
||||||
it { should be_able_to(:index, Debate) }
|
it { should be_able_to(:index, Debate) }
|
||||||
it { should be_able_to(:show, debate) }
|
it { should be_able_to(:show, debate) }
|
||||||
it { should be_able_to(:vote, debate) }
|
it { should be_able_to(:vote, debate) }
|
||||||
@@ -82,6 +87,25 @@ describe "Abilities::Common" do
|
|||||||
it { should_not be_able_to(:create, DirectMessage) }
|
it { should_not be_able_to(:create, DirectMessage) }
|
||||||
it { should_not be_able_to(:show, DirectMessage) }
|
it { should_not be_able_to(:show, DirectMessage) }
|
||||||
|
|
||||||
|
it { should be_able_to(:new_nested, Document) }
|
||||||
|
it { should be_able_to(:destroy_upload, Document) }
|
||||||
|
|
||||||
|
it { should be_able_to(:new, own_proposal_document) }
|
||||||
|
it { should be_able_to(:create, own_proposal_document) }
|
||||||
|
it { should be_able_to(:destroy, own_proposal_document) }
|
||||||
|
|
||||||
|
it { should_not be_able_to(:new, proposal_document) }
|
||||||
|
it { should_not be_able_to(:create, proposal_document) }
|
||||||
|
it { should_not be_able_to(:destroy, proposal_document) }
|
||||||
|
|
||||||
|
it { should be_able_to(:new, own_budget_investment_document) }
|
||||||
|
it { should be_able_to(:create, own_budget_investment_document) }
|
||||||
|
it { should be_able_to(:destroy, own_budget_investment_document) }
|
||||||
|
|
||||||
|
it { should_not be_able_to(:new, budget_investment_document) }
|
||||||
|
it { should_not be_able_to(:create, budget_investment_document) }
|
||||||
|
it { should_not be_able_to(:destroy, budget_investment_document) }
|
||||||
|
|
||||||
describe 'flagging content' do
|
describe 'flagging content' do
|
||||||
it { should be_able_to(:flag, debate) }
|
it { should be_able_to(:flag, debate) }
|
||||||
it { should be_able_to(:unflag, debate) }
|
it { should be_able_to(:unflag, debate) }
|
||||||
|
|||||||
8
spec/models/document_spec.rb
Normal file
8
spec/models/document_spec.rb
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Document do
|
||||||
|
|
||||||
|
it_behaves_like "document validations", "budget_investment_document"
|
||||||
|
it_behaves_like "document validations", "proposal_document"
|
||||||
|
|
||||||
|
end
|
||||||
404
spec/shared/features/documentable.rb
Normal file
404
spec/shared/features/documentable.rb
Normal file
@@ -0,0 +1,404 @@
|
|||||||
|
shared_examples "documentable" do |documentable_factory_name, documentable_path, documentable_path_arguments|
|
||||||
|
include ActionView::Helpers
|
||||||
|
include DocumentsHelper
|
||||||
|
include DocumentablesHelper
|
||||||
|
|
||||||
|
let!(:administrator) { create(:user) }
|
||||||
|
let!(:user) { create(:user) }
|
||||||
|
let!(:arguments) { {} }
|
||||||
|
let!(:documentable) { create(documentable_factory_name, author: user) }
|
||||||
|
let!(:documentable_dom_name) { documentable_factory_name.parameterize }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:administrator, user: administrator)
|
||||||
|
|
||||||
|
documentable_path_arguments.each do |argument_name, path_to_value|
|
||||||
|
arguments.merge!("#{argument_name}": documentable.send(path_to_value))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "Show" do
|
||||||
|
|
||||||
|
scenario "Should not display upload document button when there is no logged user" do
|
||||||
|
visit send(documentable_path, arguments)
|
||||||
|
|
||||||
|
within "##{dom_id(documentable)}" do
|
||||||
|
expect(page).not_to have_link("Upload document")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should not display upload document button when maximum number of documents reached " do
|
||||||
|
create_list(:document, 3, documentable: documentable)
|
||||||
|
visit send(documentable_path, arguments)
|
||||||
|
|
||||||
|
within "##{dom_id(documentable)}" do
|
||||||
|
expect(page).not_to have_link("Upload document")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should display upload document button when user is logged in and is documentable owner" do
|
||||||
|
login_as(user)
|
||||||
|
|
||||||
|
visit send(documentable_path, arguments)
|
||||||
|
|
||||||
|
within "##{dom_id(documentable)}" do
|
||||||
|
expect(page).to have_link("Upload document")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should display upload document button when admin is logged in" do
|
||||||
|
login_as(administrator)
|
||||||
|
|
||||||
|
visit send(documentable_path, arguments)
|
||||||
|
|
||||||
|
within "##{dom_id(documentable)}" do
|
||||||
|
expect(page).to have_link("Upload document")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should navigate to new document page when click un upload button" do
|
||||||
|
login_as(user)
|
||||||
|
|
||||||
|
visit send(documentable_path, arguments)
|
||||||
|
click_link "Upload document"
|
||||||
|
|
||||||
|
expect(page).to have_selector("h1", text: "Upload document")
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Documents tab" do
|
||||||
|
|
||||||
|
let!(:document) { create(:document, documentable: documentable, user: documentable.author)}
|
||||||
|
|
||||||
|
scenario "Should not display maximum number of documents alert when reached for users without document creation permission" do
|
||||||
|
create_list(:document, 2, documentable: documentable)
|
||||||
|
visit send(documentable_path, arguments)
|
||||||
|
|
||||||
|
within "#tab-documents" do
|
||||||
|
expect(page).not_to have_content "You have reached the maximum number of documents allowed! You have to delete one before you can upload another."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should display maximum number of documents alert when reached and when current user has document creation permission" do
|
||||||
|
login_as documentable.author
|
||||||
|
create_list(:document, 2, documentable: documentable)
|
||||||
|
visit send(documentable_path, arguments)
|
||||||
|
|
||||||
|
within "#tab-documents" do
|
||||||
|
expect(page).to have_content "You have reached the maximum number of documents allowed! You have to delete one before you can upload another."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Download action should be able to anyone" do
|
||||||
|
visit send(documentable_path, arguments)
|
||||||
|
|
||||||
|
within "#tab-documents" do
|
||||||
|
expect(page).to have_link("Dowload file")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Download file link should have blank target attribute" do
|
||||||
|
visit send(documentable_path, arguments)
|
||||||
|
|
||||||
|
within "#tab-documents" do
|
||||||
|
expect(page).to have_selector("a[target=_blank]", text: "Dowload file")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Download file links should have rel attribute setted to no follow" do
|
||||||
|
visit send(documentable_path, arguments)
|
||||||
|
|
||||||
|
within "#tab-documents" do
|
||||||
|
expect(page).to have_selector("a[rel=nofollow]", text: "Dowload file")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Destroy action" do
|
||||||
|
|
||||||
|
scenario "Should not be able when no user logged in" do
|
||||||
|
visit send(documentable_path, arguments)
|
||||||
|
|
||||||
|
within "#tab-documents" do
|
||||||
|
expect(page).not_to have_link("Destroy")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should be able when documentable author is logged in" do
|
||||||
|
login_as documentable.author
|
||||||
|
visit send(documentable_path, arguments)
|
||||||
|
|
||||||
|
within "#tab-documents" do
|
||||||
|
expect(page).to have_link("Destroy")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should be able when any administrator logged in" do
|
||||||
|
login_as administrator
|
||||||
|
visit send(documentable_path, arguments)
|
||||||
|
|
||||||
|
within "#tab-documents" do
|
||||||
|
expect(page).to have_link("Destroy")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
context "New" do
|
||||||
|
|
||||||
|
scenario "Should not be able for unathenticated users" do
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id)
|
||||||
|
|
||||||
|
expect(page).to have_content("You must sign in or register to continue.")
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should not be able for other users" do
|
||||||
|
login_as create(:user)
|
||||||
|
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id)
|
||||||
|
|
||||||
|
expect(page).to have_content("You do not have permission to carry out the action 'new' on document. ")
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should be able to documentable author" do
|
||||||
|
login_as documentable.author
|
||||||
|
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id)
|
||||||
|
|
||||||
|
expect(page).to have_selector("h1", text: "Upload document")
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should display file name after file selection", :js do
|
||||||
|
login_as documentable.author
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id)
|
||||||
|
|
||||||
|
attach_file :document_attachment, "spec/fixtures/files/empty.pdf", make_visible: true
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
expect(page).to have_content "empty.pdf"
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should not display file name after file selection", :js do
|
||||||
|
login_as documentable.author
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id)
|
||||||
|
|
||||||
|
attach_file :document_attachment, "spec/fixtures/files/logo_header.png", make_visible: true
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
expect(page).not_to have_content "logo_header.jpg"
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should update loading bar style after valid file upload", :js do
|
||||||
|
login_as documentable.author
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id)
|
||||||
|
|
||||||
|
attach_file :document_attachment, "spec/fixtures/files/empty.pdf", make_visible: true
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
expect(page).to have_selector ".loading-bar.complete"
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should update loading bar style after unvalid file upload", :js do
|
||||||
|
login_as documentable.author
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id)
|
||||||
|
|
||||||
|
attach_file :document_attachment, "spec/fixtures/files/logo_header.png", make_visible: true
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
expect(page).to have_selector ".loading-bar.errors"
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should update document title with attachment original file name after file selection if no title defined by user", :js do
|
||||||
|
login_as documentable.author
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id)
|
||||||
|
|
||||||
|
attach_file :document_attachment, "spec/fixtures/files/empty.pdf", make_visible: true
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
expect(find("input[name='document[title]']").value).to eq("empty.pdf")
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should not update document title with attachment original file name after file selection when title already defined by user", :js do
|
||||||
|
login_as documentable.author
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id)
|
||||||
|
|
||||||
|
fill_in :document_title, with: "My custom title"
|
||||||
|
attach_file :document_attachment, "spec/fixtures/files/empty.pdf", make_visible: true
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
expect(find("input[name='document[title]']").value).to eq("My custom title")
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should update document cached_attachment field after valid file upload", :js do
|
||||||
|
login_as documentable.author
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id)
|
||||||
|
|
||||||
|
attach_file :document_attachment, "spec/fixtures/files/empty.pdf", make_visible: true
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
expect(find("input[name='document[cached_attachment]']", visible: false).value).to include("empty.pdf")
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should not update document cached_attachment field after unvalid file upload", :js do
|
||||||
|
login_as documentable.author
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id)
|
||||||
|
|
||||||
|
attach_file :document_attachment, "spec/fixtures/files/logo_header.png", make_visible: true
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
expect(find("input[name='document[cached_attachment]']", visible: false).value).to eq ""
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should show documentable custom recomentations" do
|
||||||
|
login_as documentable.author
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id,
|
||||||
|
from: send(documentable_path, arguments))
|
||||||
|
|
||||||
|
expect(page).to have_content "You can upload up to a maximum of #{max_file_size(documentable)} documents."
|
||||||
|
expect(page).to have_content "You can upload #{humanized_accepted_content_types(documentable)} files."
|
||||||
|
expect(page).to have_content "You can upload files up to #{max_file_size(documentable)} MB."
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
context "Create" do
|
||||||
|
|
||||||
|
scenario "Should show validation errors" do
|
||||||
|
login_as documentable.author
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id)
|
||||||
|
|
||||||
|
click_on "Upload document"
|
||||||
|
|
||||||
|
expect(page).to have_content "2 errors prevented this Document from being saved: "
|
||||||
|
expect(page).to have_selector "small.error", text: "can't be blank", count: 2
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should show error notice after unsuccessfull document upload" do
|
||||||
|
login_as documentable.author
|
||||||
|
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id,
|
||||||
|
from: send(documentable_path, arguments))
|
||||||
|
attach_file :document_attachment, "spec/fixtures/files/empty.pdf"
|
||||||
|
sleep 1
|
||||||
|
click_on "Upload document"
|
||||||
|
|
||||||
|
expect(page).to have_content "Cannot create document. Check form errors and try again."
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should show success notice after successfull document upload" do
|
||||||
|
login_as documentable.author
|
||||||
|
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id,
|
||||||
|
from: send(documentable_path, arguments))
|
||||||
|
fill_in :document_title, with: "Document title"
|
||||||
|
attach_file :document_attachment, "spec/fixtures/files/empty.pdf"
|
||||||
|
sleep 1
|
||||||
|
click_on "Upload document"
|
||||||
|
|
||||||
|
expect(page).to have_content "Document was created successfully."
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should redirect to documentable path after successfull document upload" do
|
||||||
|
login_as documentable.author
|
||||||
|
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id,
|
||||||
|
from: send(documentable_path, arguments))
|
||||||
|
fill_in :document_title, with: "Document title"
|
||||||
|
attach_file :document_attachment, "spec/fixtures/files/empty.pdf"
|
||||||
|
sleep 1
|
||||||
|
click_on "Upload document"
|
||||||
|
|
||||||
|
within "##{dom_id(documentable)}" do
|
||||||
|
expect(page).to have_selector "h1", text: documentable.title
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should show new document on documentable documents tab after successfull document upload" do
|
||||||
|
login_as documentable.author
|
||||||
|
|
||||||
|
visit new_document_path(documentable_type: documentable.class.name,
|
||||||
|
documentable_id: documentable.id,
|
||||||
|
from: send(documentable_path, arguments))
|
||||||
|
fill_in :document_title, with: "Document title"
|
||||||
|
attach_file :document_attachment, "spec/fixtures/files/empty.pdf"
|
||||||
|
sleep 1
|
||||||
|
click_on "Upload document"
|
||||||
|
|
||||||
|
expect(page).to have_link "Documents (1)"
|
||||||
|
within "#tab-documents" do
|
||||||
|
within "#document_#{Document.last.id}" do
|
||||||
|
expect(page).to have_content "Document title"
|
||||||
|
expect(page).to have_link "Dowload file"
|
||||||
|
expect(page).to have_link "Destroy"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
context "Destroy" do
|
||||||
|
|
||||||
|
let!(:document) { create(:document, documentable: documentable, user: documentable.author) }
|
||||||
|
|
||||||
|
scenario "Should show success notice after successfull document upload" do
|
||||||
|
login_as documentable.author
|
||||||
|
|
||||||
|
visit send(documentable_path, arguments)
|
||||||
|
within "#tab-documents" do
|
||||||
|
within "#document_#{document.id}" do
|
||||||
|
click_on "Destroy"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(page).to have_content "Document was deleted successfully."
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should update documents tab count after successful deletion" do
|
||||||
|
login_as documentable.author
|
||||||
|
|
||||||
|
visit send(documentable_path, arguments)
|
||||||
|
within "#tab-documents" do
|
||||||
|
within "#document_#{document.id}" do
|
||||||
|
click_on "Destroy"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(page).to have_link "Documents (0)"
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should redirect to documentable path after successful deletion" do
|
||||||
|
login_as documentable.author
|
||||||
|
|
||||||
|
visit send(documentable_path, arguments)
|
||||||
|
within "#tab-documents" do
|
||||||
|
within "#document_#{document.id}" do
|
||||||
|
click_on "Destroy"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
within "##{dom_id(documentable)}" do
|
||||||
|
expect(page).to have_selector "h1", text: documentable.title
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
265
spec/shared/features/nested_documentable.rb
Normal file
265
spec/shared/features/nested_documentable.rb
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
shared_examples "nested documentable" do |documentable_factory_name, path, documentable_path_arguments, fill_resource_method_name, submit_button, documentable_success_notice|
|
||||||
|
include ActionView::Helpers
|
||||||
|
include DocumentsHelper
|
||||||
|
include DocumentablesHelper
|
||||||
|
|
||||||
|
let!(:administrator) { create(:user) }
|
||||||
|
let!(:user) { create(:user, :level_two) }
|
||||||
|
let!(:arguments) { {} }
|
||||||
|
let!(:documentable) { create(documentable_factory_name, author: user) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:administrator, user: administrator)
|
||||||
|
|
||||||
|
if documentable_path_arguments
|
||||||
|
documentable_path_arguments.each do |argument_name, path_to_value|
|
||||||
|
arguments.merge!("#{argument_name}": documentable.send(path_to_value))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "at #{path}" do
|
||||||
|
|
||||||
|
scenario "Should show new document link when max documents allowed limit is not reached" do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
|
||||||
|
expect(page).to have_selector "#new_document_link", visible: true
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should not show new document link when documentable max documents allowed limit is reached", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
|
||||||
|
click_link "Add new document"
|
||||||
|
sleep 1
|
||||||
|
click_link "Add new document"
|
||||||
|
sleep 1
|
||||||
|
click_link "Add new document"
|
||||||
|
|
||||||
|
expect(page).to have_selector "#new_document_link", visible: false
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should not show max documents warning when no documents added", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
|
||||||
|
expect(page).to have_selector ".max-documents-notice", visible: false
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should show max documents warning when max documents allowed limit is reached", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
|
||||||
|
click_link "Add new document"
|
||||||
|
sleep 1
|
||||||
|
click_link "Add new document"
|
||||||
|
sleep 1
|
||||||
|
click_link "Add new document"
|
||||||
|
|
||||||
|
expect(page).to have_selector ".max-documents-notice", visible: true
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should hide max documents warning after any document removal", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
|
||||||
|
click_link "Add new document"
|
||||||
|
sleep 1
|
||||||
|
click_link "Add new document"
|
||||||
|
sleep 1
|
||||||
|
click_link "Add new document"
|
||||||
|
sleep 1
|
||||||
|
within "#document_0" do
|
||||||
|
find("a", text: "Remove document").click
|
||||||
|
end
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
expect(page).to have_selector ".max-documents-notice", visible: false
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should update nested document file name after choosing a file", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
|
||||||
|
attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/empty.pdf")
|
||||||
|
|
||||||
|
expect(page).to have_selector ".file-name", text: "empty.pdf"
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should update nested document file title with file name after choosing a file when no title defined", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
|
||||||
|
attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/empty.pdf")
|
||||||
|
|
||||||
|
expect(find("##{documentable_factory_name}_documents_attributes_0_title").value).to eq "empty.pdf"
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should not update nested document file title with file name after choosing a file when title already defined", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
|
||||||
|
click_link "Add new document"
|
||||||
|
sleep 1
|
||||||
|
fill_in "#{documentable_factory_name}[documents_attributes][0][title]", with: "Title"
|
||||||
|
attach_file("#{documentable_factory_name}[documents_attributes][0][attachment]", "spec/fixtures/files/empty.pdf", make_visible: true)
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
expect(find("##{documentable_factory_name}_documents_attributes_0_title").value).to eq "Title"
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should update loading bar style after valid file upload", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
|
||||||
|
attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/empty.pdf")
|
||||||
|
fill_in "#{documentable_factory_name}[documents_attributes][0][title]", with: "Title"
|
||||||
|
|
||||||
|
expect(page).to have_selector ".loading-bar.complete"
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should update loading bar style after unvalid file upload", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
|
||||||
|
attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/logo_header.png")
|
||||||
|
|
||||||
|
expect(page).to have_selector ".loading-bar.errors"
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should update document cached_attachment field after valid file upload", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
|
||||||
|
attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/empty.pdf")
|
||||||
|
|
||||||
|
expect(find("input[name='#{documentable_factory_name}[documents_attributes][0][cached_attachment]']", visible: false).value).to include("empty.pdf")
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should not update document cached_attachment field after unvalid file upload", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
|
||||||
|
attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/logo_header.png")
|
||||||
|
|
||||||
|
expect(find("input[name='#{documentable_factory_name}[documents_attributes][0][cached_attachment]']", visible: false).value).to eq ""
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should show document errors after unvalid file upload", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
|
||||||
|
click_link "Add new document"
|
||||||
|
sleep 1
|
||||||
|
click_on submit_button
|
||||||
|
|
||||||
|
within "#document_0" do
|
||||||
|
expect(page).to have_content("can't be blank", count: 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should delete document after valid file upload and click on remove button", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
|
||||||
|
attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/empty.pdf")
|
||||||
|
within "#document_0" do
|
||||||
|
click_link "Remove document"
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(page).not_to have_selector("#document_0")
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should delete document after valid file upload and click on remove button", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
|
||||||
|
attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/empty.pdf")
|
||||||
|
within "#document_0" do
|
||||||
|
click_link "Remove document"
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(page).to have_content "Document was deleted successfully."
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should show successful notice when resource filled correctly without any nested documents", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
send(fill_resource_method_name) if fill_resource_method_name
|
||||||
|
|
||||||
|
click_on submit_button
|
||||||
|
expect(page).to have_content documentable_success_notice
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should show successful notice when resource filled correctly and after valid file uploads", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
send(fill_resource_method_name) if fill_resource_method_name
|
||||||
|
|
||||||
|
attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/empty.pdf")
|
||||||
|
|
||||||
|
click_on submit_button
|
||||||
|
expect(page).to have_content documentable_success_notice
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should show new document after successful creation with one uploaded file", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
send(fill_resource_method_name) if fill_resource_method_name
|
||||||
|
|
||||||
|
attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/empty.pdf")
|
||||||
|
|
||||||
|
click_on submit_button
|
||||||
|
redirected_to_resource_show_or_navigate_to
|
||||||
|
|
||||||
|
expect(page).to have_content "Documents (1)"
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Should show resource with new document after successful creation with maximum allowed uploaded files", :js do
|
||||||
|
login_as user
|
||||||
|
visit send(path, arguments)
|
||||||
|
send(fill_resource_method_name) if fill_resource_method_name
|
||||||
|
|
||||||
|
documentable.class.max_documents_allowed.times.each do |index|
|
||||||
|
attach_new_file(documentable_factory_name, index , "spec/fixtures/files/empty.pdf")
|
||||||
|
end
|
||||||
|
|
||||||
|
click_on submit_button
|
||||||
|
redirected_to_resource_show_or_navigate_to
|
||||||
|
|
||||||
|
expect(page).to have_content "Documents (#{documentable.class.max_documents_allowed})"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def redirected_to_resource_show_or_navigate_to
|
||||||
|
find("a", text: "Not now, go to my proposal")
|
||||||
|
click_on "Not now, go to my proposal"
|
||||||
|
rescue
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
def attach_new_file(documentable_factory_name, index, path)
|
||||||
|
click_link "Add new document"
|
||||||
|
sleep 1
|
||||||
|
attach_file("#{documentable_factory_name}[documents_attributes][#{index}][attachment]", path, make_visible: true)
|
||||||
|
sleep 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def fill_new_valid_proposal
|
||||||
|
fill_in :proposal_title, with: "Proposal title"
|
||||||
|
fill_in :proposal_summary, with: "Proposal summary"
|
||||||
|
fill_in :proposal_question, with: "Proposal question?"
|
||||||
|
check :proposal_terms_of_service
|
||||||
|
end
|
||||||
|
|
||||||
|
def fill_new_valid_budget_investment
|
||||||
|
page.select documentable.heading.name_scoped_by_group, from: :budget_investment_heading_id
|
||||||
|
fill_in :budget_investment_title, with: "Budget investment title"
|
||||||
|
fill_in_ckeditor "budget_investment_description", with: "Budget investment description"
|
||||||
|
check :budget_investment_terms_of_service
|
||||||
|
end
|
||||||
|
|
||||||
62
spec/shared/models/document_validations.rb
Normal file
62
spec/shared/models/document_validations.rb
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
shared_examples "document validations" do |documentable_factory|
|
||||||
|
include DocumentsHelper
|
||||||
|
include DocumentablesHelper
|
||||||
|
|
||||||
|
let!(:document) { build(:document, documentable_factory.to_sym) }
|
||||||
|
let!(:documentable) { document.documentable }
|
||||||
|
let!(:maxfilesize) { max_file_size(document.documentable) }
|
||||||
|
let!(:acceptedcontenttypes) { accepted_content_types(document.documentable) }
|
||||||
|
|
||||||
|
it "should be valid" do
|
||||||
|
expect(document).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not be valid without a title" do
|
||||||
|
document.title = nil
|
||||||
|
|
||||||
|
expect(document).to_not be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not be valid without an attachment" do
|
||||||
|
document.attachment = nil
|
||||||
|
|
||||||
|
expect(document).to_not be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be valid for all accepted content types" do
|
||||||
|
acceptedcontenttypes.each do |content_type|
|
||||||
|
extension = content_type.split("/").last
|
||||||
|
document.attachment = File.new("spec/fixtures/files/empty.#{extension}")
|
||||||
|
|
||||||
|
expect(document).to be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not be valid for attachments larger than documentable max_file_size definition" do
|
||||||
|
document.stub(:attachment_file_size).and_return(maxfilesize.megabytes + 1.byte)
|
||||||
|
|
||||||
|
expect(document).to_not be_valid
|
||||||
|
expect(document.errors[:attachment]).to include "must be in between 0 Bytes and #{maxfilesize} MB"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not be valid without a user_id" do
|
||||||
|
document.user_id = nil
|
||||||
|
|
||||||
|
expect(document).to_not be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not be valid without a documentable_id" do
|
||||||
|
document.save
|
||||||
|
document.documentable_id = nil
|
||||||
|
|
||||||
|
expect(document).to_not be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not be valid without a documentable_type" do
|
||||||
|
document.save
|
||||||
|
document.documentable_type = nil
|
||||||
|
|
||||||
|
expect(document).to_not be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user