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-twitter', '~> 1.4.0'
|
||||
gem 'paperclip', '~> 5.1.0'
|
||||
gem 'jquery-fileupload-rails'
|
||||
gem 'paranoia', '~> 2.3.1'
|
||||
gem 'pg', '~> 0.20.0'
|
||||
gem 'pg_search', '~> 2.0.1'
|
||||
|
||||
86
Gemfile.lock
86
Gemfile.lock
@@ -52,7 +52,7 @@ GEM
|
||||
safely_block (>= 0.1.1)
|
||||
user_agent_parser
|
||||
uuidtools
|
||||
airbrussh (1.2.0)
|
||||
airbrussh (1.3.0)
|
||||
sshkit (>= 1.6.1, != 1.7.0)
|
||||
akami (1.3.1)
|
||||
gyoku (>= 0.4.0)
|
||||
@@ -73,7 +73,7 @@ GEM
|
||||
uniform_notifier (~> 1.10.0)
|
||||
byebug (9.0.6)
|
||||
cancancan (1.16.0)
|
||||
capistrano (3.8.1)
|
||||
capistrano (3.8.2)
|
||||
airbrussh (>= 1.0.0)
|
||||
i18n
|
||||
rake (>= 10.0.0)
|
||||
@@ -87,7 +87,7 @@ GEM
|
||||
capistrano3-delayed-job (1.7.3)
|
||||
capistrano (~> 3.0, >= 3.0.0)
|
||||
daemons (~> 1.2.4)
|
||||
capybara (2.14.0)
|
||||
capybara (2.14.4)
|
||||
addressable
|
||||
mime-types (>= 1.16)
|
||||
nokogiri (>= 1.3.3)
|
||||
@@ -95,17 +95,17 @@ GEM
|
||||
rack-test (>= 0.5.4)
|
||||
xpath (~> 2.0)
|
||||
chronic (0.10.2)
|
||||
ckeditor (4.2.3)
|
||||
ckeditor (4.2.4)
|
||||
cocaine
|
||||
orm_adapter (~> 0.5.0)
|
||||
climate_control (0.1.0)
|
||||
climate_control (0.2.0)
|
||||
cliver (0.3.2)
|
||||
cocaine (0.5.8)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
cocoon (1.2.9)
|
||||
coffee-rails (4.2.1)
|
||||
cocoon (1.2.10)
|
||||
coffee-rails (4.2.2)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (>= 4.0.0, < 5.2.x)
|
||||
railties (>= 4.0.0)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
@@ -120,11 +120,11 @@ GEM
|
||||
daemons (1.2.4)
|
||||
dalli (2.7.6)
|
||||
database_cleaner (1.5.3)
|
||||
debug_inspector (0.0.2)
|
||||
delayed_job (4.1.2)
|
||||
activesupport (>= 3.0, < 5.1)
|
||||
delayed_job_active_record (4.1.1)
|
||||
activerecord (>= 3.0, < 5.1)
|
||||
debug_inspector (0.0.3)
|
||||
delayed_job (4.1.3)
|
||||
activesupport (>= 3.0, < 5.2)
|
||||
delayed_job_active_record (4.1.2)
|
||||
activerecord (>= 3.0, < 5.2)
|
||||
delayed_job (>= 3.0, < 5)
|
||||
devise (3.5.10)
|
||||
bcrypt (~> 3.0)
|
||||
@@ -144,10 +144,10 @@ GEM
|
||||
json
|
||||
thread
|
||||
thread_safe
|
||||
email_spec (2.1.0)
|
||||
email_spec (2.1.1)
|
||||
htmlentities (~> 4.3.3)
|
||||
launchy (~> 2.1)
|
||||
mail (~> 2.6.3)
|
||||
mail (~> 2.6)
|
||||
errbase (0.0.3)
|
||||
erubis (2.7.0)
|
||||
execjs (2.7.0)
|
||||
@@ -158,7 +158,7 @@ GEM
|
||||
railties (>= 3.0.0)
|
||||
faker (1.7.3)
|
||||
i18n (~> 0.5)
|
||||
faraday (0.11.0)
|
||||
faraday (0.12.1)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
foundation-rails (6.2.4.0)
|
||||
railties (>= 3.1.0)
|
||||
@@ -170,12 +170,12 @@ GEM
|
||||
activesupport (>= 4.1)
|
||||
railties (>= 4.1)
|
||||
tzinfo (~> 1.2, >= 1.2.2)
|
||||
geocoder (1.4.3)
|
||||
geocoder (1.4.4)
|
||||
globalid (0.4.0)
|
||||
activesupport (>= 4.2.0)
|
||||
graphiql-rails (1.4.1)
|
||||
graphiql-rails (1.4.2)
|
||||
rails
|
||||
graphql (1.6.3)
|
||||
graphql (1.6.4)
|
||||
groupdate (3.2.0)
|
||||
activesupport (>= 3)
|
||||
gyoku (1.3.1)
|
||||
@@ -183,9 +183,10 @@ GEM
|
||||
hashie (3.5.5)
|
||||
highline (1.7.8)
|
||||
htmlentities (4.3.4)
|
||||
httpi (2.4.1)
|
||||
httpi (2.4.2)
|
||||
rack
|
||||
i18n (0.8.4)
|
||||
socksify
|
||||
i18n (0.8.6)
|
||||
i18n-tasks (0.9.15)
|
||||
activesupport (>= 4.0.2)
|
||||
ast (>= 2.1.0)
|
||||
@@ -200,6 +201,10 @@ GEM
|
||||
railties (>= 3.1, < 6.0)
|
||||
invisible_captcha (0.9.2)
|
||||
rails (>= 3.2.0)
|
||||
jquery-fileupload-rails (0.4.7)
|
||||
actionpack (>= 3.1)
|
||||
railties (>= 3.1)
|
||||
sass (>= 3.2)
|
||||
jquery-rails (4.3.1)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
@@ -224,7 +229,7 @@ GEM
|
||||
knapsack (1.13.3)
|
||||
rake
|
||||
timecop (>= 0.1.0)
|
||||
kramdown (1.13.2)
|
||||
kramdown (1.14.0)
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
letter_opener (1.4.1)
|
||||
@@ -259,9 +264,9 @@ GEM
|
||||
nokogiri (1.8.0)
|
||||
mini_portile2 (~> 2.2.0)
|
||||
nori (2.6.0)
|
||||
oauth (0.5.1)
|
||||
oauth2 (1.3.1)
|
||||
faraday (>= 0.8, < 0.12)
|
||||
oauth (0.5.3)
|
||||
oauth2 (1.4.0)
|
||||
faraday (>= 0.8, < 0.13)
|
||||
jwt (~> 1.0)
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
@@ -328,7 +333,7 @@ GEM
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 4.2.9)
|
||||
sprockets-rails
|
||||
rails-assets-markdown-it (8.2.1)
|
||||
rails-assets-markdown-it (8.2.2)
|
||||
rails-deprecated_sanitizer (1.0.3)
|
||||
activesupport (>= 4.2.0.alpha)
|
||||
rails-dom-testing (1.0.8)
|
||||
@@ -386,7 +391,7 @@ GEM
|
||||
sshkit (>= 1.2)
|
||||
safely_block (0.2.0)
|
||||
errbase
|
||||
sass (3.4.23)
|
||||
sass (3.4.25)
|
||||
sass-rails (5.0.6)
|
||||
railties (>= 4.0.0, < 6)
|
||||
sass (~> 3.1)
|
||||
@@ -408,12 +413,13 @@ GEM
|
||||
docile (~> 1.1.0)
|
||||
json (>= 1.8, < 3)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-html (0.10.0)
|
||||
simplecov-html (0.10.1)
|
||||
sitemap_generator (5.3.1)
|
||||
builder (~> 3.0)
|
||||
social-share-button (0.10.0)
|
||||
coffee-rails
|
||||
spring (2.0.1)
|
||||
socksify (1.7.1)
|
||||
spring (2.0.2)
|
||||
activesupport (>= 4.2)
|
||||
spring-commands-rspec (1.0.4)
|
||||
spring (>= 0.9.1)
|
||||
@@ -428,19 +434,19 @@ GEM
|
||||
actionpack (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
sshkit (1.13.1)
|
||||
sshkit (1.14.0)
|
||||
net-scp (>= 1.1.2)
|
||||
net-ssh (>= 2.8.0)
|
||||
term-ansicolor (1.6.0)
|
||||
tins (~> 1.0)
|
||||
terminal-table (1.7.3)
|
||||
unicode-display_width (~> 1.1.1)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
thor (0.19.4)
|
||||
thread (0.2.2)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.7)
|
||||
timecop (0.8.1)
|
||||
tins (1.13.2)
|
||||
timecop (0.9.1)
|
||||
tins (1.15.0)
|
||||
turbolinks (2.5.3)
|
||||
coffee-rails
|
||||
turnout (2.4.0)
|
||||
@@ -452,14 +458,14 @@ GEM
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (3.2.0)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
unicode-display_width (1.1.3)
|
||||
unicode-display_width (1.3.0)
|
||||
unicorn (5.3.0)
|
||||
kgio (~> 2.6)
|
||||
raindrops (~> 0.7)
|
||||
uniform_notifier (1.10.0)
|
||||
user_agent_parser (2.3.0)
|
||||
user_agent_parser (2.3.1)
|
||||
uuidtools (2.1.5)
|
||||
warden (1.2.6)
|
||||
warden (1.2.7)
|
||||
rack (>= 1.0)
|
||||
wasabi (3.5.0)
|
||||
httpi (~> 2.0)
|
||||
@@ -473,7 +479,7 @@ GEM
|
||||
websocket-extensions (0.1.2)
|
||||
whenever (0.9.7)
|
||||
chronic (>= 0.6.3)
|
||||
xpath (2.0.0)
|
||||
xpath (2.1.0)
|
||||
nokogiri (~> 1.3)
|
||||
|
||||
PLATFORMS
|
||||
@@ -515,6 +521,7 @@ DEPENDENCIES
|
||||
i18n-tasks (~> 0.9.15)
|
||||
initialjs-rails (~> 0.2.0.5)
|
||||
invisible_captcha (~> 0.9.2)
|
||||
jquery-fileupload-rails
|
||||
jquery-rails (~> 4.3.1)
|
||||
jquery-ui-rails (~> 6.0.1)
|
||||
kaminari (~> 1.0.1)
|
||||
@@ -558,5 +565,6 @@ DEPENDENCIES
|
||||
web-console (~> 3.3.0)
|
||||
whenever (~> 0.9.7)
|
||||
|
||||
|
||||
BUNDLED WITH
|
||||
1.15.1
|
||||
1.15.3
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
//= require jquery_ujs
|
||||
//= require jquery-ui/widgets/datepicker
|
||||
//= require jquery-ui/i18n/datepicker-es
|
||||
//= require jquery-fileupload/basic
|
||||
//= require foundation
|
||||
//= require turbolinks
|
||||
//= require ckeditor/loader
|
||||
@@ -59,6 +60,7 @@
|
||||
//= require legislation_annotatable
|
||||
//= require watch_form_changes
|
||||
//= require followable
|
||||
//= require documentable
|
||||
//= require tree_navigator
|
||||
//= require custom
|
||||
|
||||
@@ -94,6 +96,7 @@ var initialize_modules = function() {
|
||||
App.LegislationAnnotatable.initialize();
|
||||
App.WatchFormChanges.initialize();
|
||||
App.TreeNavigator.initialize();
|
||||
App.Documentable.initialize();
|
||||
};
|
||||
|
||||
$(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 'jquery-ui/datepicker';
|
||||
@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';
|
||||
}
|
||||
|
||||
.icon-documents::before {
|
||||
content: '\68';
|
||||
}
|
||||
|
||||
.icon-proposals::before {
|
||||
content: '\68';
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
// 17. Activity
|
||||
// 18. Banners
|
||||
// 19. Recommended Section Home
|
||||
// 20. Documents
|
||||
//
|
||||
|
||||
// 01. Global styles
|
||||
@@ -92,6 +93,11 @@ a {
|
||||
color: $link;
|
||||
}
|
||||
|
||||
.button.hollow.error {
|
||||
border-color: $alert-border;
|
||||
color: $color-alert;
|
||||
}
|
||||
|
||||
.postfix.button {
|
||||
padding: 0;
|
||||
}
|
||||
@@ -2154,7 +2160,6 @@ table {
|
||||
|
||||
.section-recommended {
|
||||
padding: $line-height * 2 0;
|
||||
// padding-bottom: $line-height * 2;
|
||||
|
||||
h2 {
|
||||
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,
|
||||
.proposal-form,
|
||||
.budget-investment-form,
|
||||
.spending-proposal-form {
|
||||
.spending-proposal-form,
|
||||
.document-form {
|
||||
|
||||
.icon-debates,
|
||||
.icon-proposals,
|
||||
.icon-budget {
|
||||
.icon-budget,
|
||||
.icon-documents {
|
||||
font-size: rem-calc(50);
|
||||
line-height: $line-height;
|
||||
opacity: 0.5;
|
||||
@@ -262,7 +264,8 @@
|
||||
color: $debates;
|
||||
}
|
||||
|
||||
.icon-proposals {
|
||||
.icon-proposals,
|
||||
.icon-documents {
|
||||
color: $proposals;
|
||||
}
|
||||
|
||||
@@ -294,7 +297,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.proposal-form {
|
||||
.proposal-form,
|
||||
.document-form {
|
||||
|
||||
.recommendations li::before {
|
||||
color: $proposals;
|
||||
@@ -746,6 +750,12 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.document-form{
|
||||
max-width: 75rem;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.more-info {
|
||||
clear: both;
|
||||
color: $text-medium;
|
||||
|
||||
@@ -44,10 +44,12 @@ module Budgets
|
||||
set_comment_flags(@comment_tree.comments)
|
||||
load_investment_votes(@investment)
|
||||
@investment_ids = [@investment.id]
|
||||
@document = Document.new(documentable: @investment)
|
||||
end
|
||||
|
||||
def create
|
||||
@investment.author = current_user
|
||||
recover_documents_from_cache(@investment)
|
||||
|
||||
if @investment.save
|
||||
Mailer.budget_investment_created(@investment).deliver_later
|
||||
@@ -104,7 +106,8 @@ module Budgets
|
||||
|
||||
def investment_params
|
||||
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
|
||||
|
||||
def load_ballot
|
||||
|
||||
@@ -63,6 +63,8 @@ module CommentableActions
|
||||
|
||||
def update
|
||||
resource.assign_attributes(strong_params)
|
||||
recover_documents_from_cache(resource)
|
||||
|
||||
if resource.save
|
||||
redirect_to resource, notice: t("flash.actions.update.#{resource_name.underscore}")
|
||||
else
|
||||
@@ -115,4 +117,11 @@ module CommentableActions
|
||||
nil
|
||||
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
|
||||
|
||||
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
|
||||
super
|
||||
@notifications = @proposal.notifications
|
||||
@document = Document.new(documentable: @proposal)
|
||||
redirect_to proposal_path(@proposal), status: :moved_permanently if request.path != proposal_path(@proposal)
|
||||
end
|
||||
|
||||
def create
|
||||
@proposal = Proposal.new(proposal_params.merge(author: current_user))
|
||||
recover_documents_from_cache(@proposal)
|
||||
|
||||
if @proposal.save
|
||||
redirect_to share_proposal_path(@proposal), notice: I18n.t('flash.actions.create.proposal')
|
||||
@@ -75,7 +77,8 @@ class ProposalsController < ApplicationController
|
||||
|
||||
def proposal_params
|
||||
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
|
||||
|
||||
def retired_params
|
||||
@@ -121,4 +124,5 @@ class ProposalsController < ApplicationController
|
||||
def load_successful_proposals
|
||||
@proposal_successful_exists = Proposal.successful.exists?
|
||||
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
|
||||
cannot :comment_as_moderator, [::Legislation::Question, Legislation::Annotation]
|
||||
|
||||
can [:create, :destroy], Document
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -36,6 +36,9 @@ module Abilities
|
||||
|
||||
can [:create, :destroy], Follow
|
||||
|
||||
can [:create, :destroy, :new], Document, documentable: { author_id: user.id }
|
||||
can [:new_nested, :upload, :destroy_upload], Document
|
||||
|
||||
unless user.organization?
|
||||
can :vote, Debate
|
||||
can :vote, Comment
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
class Budget
|
||||
class Investment < ActiveRecord::Base
|
||||
|
||||
include Measurable
|
||||
include Sanitizable
|
||||
include Taggable
|
||||
include Searchable
|
||||
include Reclassification
|
||||
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_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 Graphqlable
|
||||
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_paranoid column: :hidden_at
|
||||
|
||||
@@ -17,6 +17,14 @@
|
||||
</h3>
|
||||
<% end %>
|
||||
</li>
|
||||
<li class="tabs-title">
|
||||
<%= link_to "#tab-documents" do %>
|
||||
<h3>
|
||||
<%= t("documents.tab") %>
|
||||
(<%= @investment.documents.count %>)
|
||||
</h3>
|
||||
<% end %>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
<%= f.text_field :external_url %>
|
||||
</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">
|
||||
<%= f.text_field :location %>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
<div class="small-12 medium-9 column">
|
||||
<%= 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>
|
||||
|
||||
<div class="budget-investment-info">
|
||||
@@ -14,7 +20,6 @@
|
||||
<span class="bullet"> • </span>
|
||||
<%= investment.heading.name %>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<p id="investment_code">
|
||||
<%= t("budgets.investments.show.code_html", code: investment.id) %>
|
||||
@@ -51,6 +56,7 @@
|
||||
<h2><%= t('budgets.investments.show.price_explanation') %></h2>
|
||||
<p><%= investment.price_explanation %></p>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
|
||||
<aside class="small-12 medium-3 column">
|
||||
|
||||
@@ -19,4 +19,11 @@
|
||||
comment_flags: @comment_flags,
|
||||
display_comments_count: false } %>
|
||||
</div>
|
||||
|
||||
<div class="tabs-panel" id="tab-documents">
|
||||
<%= render 'documents/documents',
|
||||
documents: @investment.documents,
|
||||
max_documents_allowed: Budget::Investment.max_documents_allowed %>
|
||||
</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>
|
||||
<% end %>
|
||||
</li>
|
||||
<li class="tabs-title">
|
||||
<%= link_to "#tab-documents" do %>
|
||||
<h3>
|
||||
<%= t("documents.tab") %>
|
||||
(<%= @proposal.documents.count %>)
|
||||
</h3>
|
||||
<% end %>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
<%= f.cktext_area :description, maxlength: Proposal.description_max_length, ckeditor: { language: I18n.locale }, label: false %>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="small-12 column">
|
||||
<%= 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>
|
||||
@@ -47,6 +46,10 @@
|
||||
<%= f.text_field :external_url, placeholder: t("proposals.form.proposal_external_url"), label: false %>
|
||||
</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">
|
||||
<%= f.label :geozone_id, t("proposals.form.geozone") %>
|
||||
<%= 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">
|
||||
<%= 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) %>
|
||||
<%= link_to t("proposals.show.send_notification"), new_proposal_notification_path(proposal_id: @proposal.id),
|
||||
class: 'button hollow float-right' %>
|
||||
@@ -165,4 +171,10 @@
|
||||
<div class="tabs-panel is-active" id="tab-comments">
|
||||
<%= render "proposals/comments" %>
|
||||
</div>
|
||||
|
||||
<div class="tabs-panel" id="tab-documents">
|
||||
<%= render 'documents/documents',
|
||||
documents: @proposal.documents,
|
||||
max_documents_allowed: Proposal.max_documents_allowed %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -37,6 +37,7 @@ data:
|
||||
- config/locales/%{locale}/officing.yml
|
||||
- config/locales/%{locale}/budgets.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:
|
||||
# `i18n-tasks normalize -p` will force move the keys according to these rules
|
||||
|
||||
@@ -76,6 +76,9 @@ en:
|
||||
legislation/answers:
|
||||
one: "Answer"
|
||||
other: "Answers"
|
||||
documents:
|
||||
one: "Document"
|
||||
other: "Documents"
|
||||
attributes:
|
||||
budget:
|
||||
name: "Name"
|
||||
@@ -197,6 +200,9 @@ en:
|
||||
value: Value
|
||||
legislation/annotation:
|
||||
text: Comment
|
||||
document:
|
||||
title: Title
|
||||
attachment: Attachment
|
||||
errors:
|
||||
models:
|
||||
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
|
||||
verification/sms: phone
|
||||
signature_sheet: Signature sheet
|
||||
document: Document
|
||||
geozones:
|
||||
none: All city
|
||||
all: All scopes
|
||||
|
||||
@@ -76,6 +76,9 @@ es:
|
||||
legislation/answers:
|
||||
one: "Respuesta"
|
||||
other: "Respuestas"
|
||||
documents:
|
||||
one: "Documento"
|
||||
other: "Documentos"
|
||||
attributes:
|
||||
budget:
|
||||
name: "Nombre"
|
||||
@@ -192,6 +195,9 @@ es:
|
||||
value: Valor
|
||||
legislation/annotation:
|
||||
text: Comentario
|
||||
document:
|
||||
title: Título
|
||||
attachment: Archivo adjunto
|
||||
errors:
|
||||
models:
|
||||
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
|
||||
verification/sms: el teléfono
|
||||
signature_sheet: la hoja de firmas
|
||||
document: el documento
|
||||
geozones:
|
||||
none: Toda la ciudad
|
||||
all: Todos los ámbitos de actuación
|
||||
|
||||
@@ -95,6 +95,14 @@ Rails.application.routes.draw do
|
||||
|
||||
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 :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.
|
||||
|
||||
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
|
||||
enable_extension "plpgsql"
|
||||
@@ -298,6 +298,23 @@ ActiveRecord::Schema.define(version: 20170719174326) do
|
||||
t.datetime "updated_at", null: false
|
||||
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|
|
||||
t.integer "user_id"
|
||||
t.string "document_number"
|
||||
@@ -1014,6 +1031,7 @@ ActiveRecord::Schema.define(version: 20170719174326) do
|
||||
add_foreign_key "administrators", "users"
|
||||
add_foreign_key "annotations", "legacy_legislations"
|
||||
add_foreign_key "annotations", "users"
|
||||
add_foreign_key "documents", "users"
|
||||
add_foreign_key "failed_census_calls", "poll_officers"
|
||||
add_foreign_key "failed_census_calls", "users"
|
||||
add_foreign_key "flags", "users"
|
||||
|
||||
@@ -368,6 +368,20 @@ FactoryGirl.define do
|
||||
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
|
||||
association :commentable, factory: :debate
|
||||
user
|
||||
|
||||
@@ -429,7 +429,17 @@ feature 'Budget Investments' do
|
||||
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
|
||||
|
||||
|
||||
@@ -1317,6 +1317,24 @@ feature 'Proposals' do
|
||||
|
||||
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
|
||||
user = create(: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(:comment) { create(:comment) }
|
||||
let(:proposal) { create(:proposal) }
|
||||
let(:budget_investment) { create(:budget_investment) }
|
||||
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_comment) { create(:comment, :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: '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
|
||||
|
||||
@@ -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_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(:show, 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(: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
|
||||
it { should be_able_to(:flag, 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