Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add general agenda app #608

Draft
wants to merge 27 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ab80a31
WIP: Add general agenda app
Laykou Oct 6, 2023
b967806
WIP: Add general agenda app
Laykou Oct 6, 2023
7eff79f
Add Datahub::Upvs::ServicesWithForms
Oct 6, 2023
82b6c75
Fix XML for general agenda
Laykou Oct 7, 2023
78191ef
Use ServicesWithForms search in controller
Oct 7, 2023
a8c103c
moved search_recipient to ServicesWithFormsController
Oct 7, 2023
9cc519e
Unify the form (add-general-agenda-app)
Laykou Oct 7, 2023
306ff72
Draft signing
luciajanikova Oct 7, 2023
4147fc7
Merge branch 'master' of https://github.com/slovensko-digital/navody.…
luciajanikova Oct 7, 2023
909a747
WIP: Add file upload to general agenda (add-general-agenda-app)
Laykou Oct 7, 2023
2745f77
added validation to subject and text
Oct 7, 2023
b835e5e
Change Upvs::Submission form to attachment
Oct 7, 2023
cda8574
Complete signing of UPVS Submission (feature/upvs_submissions_signing)
Laykou Oct 7, 2023
4f247df
Merge branch 'feature/upvs_submissions_signing' into add-general-agen…
Laykou Oct 7, 2023
d5e68d5
Add analyzers (add-general-agenda-app)
Oct 7, 2023
5bc7a8d
Add signature together with general agenda (add-general-agenda-app)
Laykou Oct 7, 2023
1a34f91
Remove xml analyzer
Oct 7, 2023
c495f14
Fix submission tests
Oct 7, 2023
a152e34
Fix attachment tempaltes for general agenda form
Laykou Oct 7, 2023
1326b85
fixed query and format of response
Oct 7, 2023
2ff65cb
Update UpvsSubmissions::SktalkMessageBuilder to include attachments
luciajanikova Oct 7, 2023
38c4338
Merge branch 'add-general-agenda-app' of https://github.com/slovensko…
luciajanikova Oct 7, 2023
fc2c9ed
Add attachments uploads + signature for UPVS
Laykou Oct 7, 2023
360f441
Fix validation of placeholders and links to blobs
Laykou Oct 7, 2023
befb48b
Cleanup unnecessary files
Laykou Oct 7, 2023
66316b2
Drop Upvs::Submission.form column
luciajanikova Apr 27, 2024
cefe7f6
Schedule RemoveStaleBlobs job
luciajanikova Apr 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,7 @@ end
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
gem 'hirb'

# signing verification
gem 'pdf-reader'

gem "rails-controller-testing", "~> 1.0"
12 changes: 12 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
Ascii85 (1.1.0)
actioncable (6.1.7.3)
actionpack (= 6.1.7.3)
activesupport (= 6.1.7.3)
Expand Down Expand Up @@ -62,6 +63,7 @@ GEM
zeitwerk (~> 2.3)
addressable (2.8.5)
public_suffix (>= 2.0.2, < 6.0)
afm (0.2.2)
aws-eventstream (1.2.0)
aws-partitions (1.734.0)
aws-record (2.10.1)
Expand Down Expand Up @@ -164,6 +166,7 @@ GEM
activerecord (>= 4.0.0)
globalid (1.1.0)
activesupport (>= 5.0)
hashery (2.1.2)
hashie (5.0.0)
hirb (0.7.3)
htmlentities (4.3.4)
Expand Down Expand Up @@ -270,6 +273,12 @@ GEM
omniauth-rails_csrf_protection (1.0.1)
actionpack (>= 4.2)
omniauth (~> 2.0)
pdf-reader (2.11.0)
Ascii85 (~> 1.0)
afm (~> 0.2.1)
hashery (~> 2.0)
ruby-rc4
ttfunk
pg (1.4.6)
pg_search (2.3.6)
activerecord (>= 5.2)
Expand Down Expand Up @@ -366,6 +375,7 @@ GEM
rspec-support (3.12.0)
rspec_junit_formatter (0.6.0)
rspec-core (>= 2, < 4, != 2.12.0)
ruby-rc4 (0.1.5)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
sass-rails (6.0.0)
Expand Down Expand Up @@ -418,6 +428,7 @@ GEM
thor (1.2.1)
tilt (2.1.0)
timeout (0.3.2)
ttfunk (1.7.0)
turbolinks (5.2.1)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
Expand Down Expand Up @@ -486,6 +497,7 @@ DEPENDENCIES
omniauth
omniauth-google-oauth2
omniauth-rails_csrf_protection
pdf-reader
pg
pg_search
premailer-rails
Expand Down
1 change: 1 addition & 0 deletions app/assets/stylesheets/application.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
*= require static
*= require navody-frontend-fixes
*= require or-sr
*= require spinner
*= require_self
*/

Expand Down
22 changes: 0 additions & 22 deletions app/assets/stylesheets/or-sr.scss
Original file line number Diff line number Diff line change
Expand Up @@ -83,28 +83,6 @@ div#or-sr-identifiers, div#acts-submission {
min-height: 40px;
}

@keyframes spinner {
to {transform: rotate(360deg);}
}

.spinner {
position: absolute;
top: 25%;
left: 50%;
}

.spinner:before {
content: '';
box-sizing: border-box;
position: absolute;
width: 20px;
height: 20px;
border-radius: 50%;
border: 2px solid #ccc;
border-top-color: #000;
animation: spinner .6s linear infinite;
}

#identifiers-sr input, #identifiers-foreign input {
width: 100%;

Expand Down
26 changes: 26 additions & 0 deletions app/assets/stylesheets/spinner.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@keyframes spinner {
to {transform: rotate(360deg);}
}

.spinner {
position: absolute;
top: 25%;
left: 50%;
}

.spinner:before {
content: '';
box-sizing: border-box;
position: absolute;
width: 20px;
height: 20px;
border-radius: 50%;
border: 2px solid #ccc;
border-top-color: #000;
animation: spinner .6s linear infinite;
}

.spinner.spinner-lg:before {
width: 60px;
height: 60px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ def form_check

# generates XML to be submitted
def create
xml_form = UpvsSubmissions::FormBuilders::ApplicationForDocumentCopyFormBuilder.build_form(@application_form)
render xml: xml_form.to_xml
render json: { id: UpvsSubmissions::Forms::ApplicationForDocumentCopy.create_form_attachment(@application_form) }.to_json
end

def callback
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
module Apps
module GeneralAgendaApp
class GeneralAgendaController < ApplicationController
skip_forgery_protection only: [:index]

Check failure

Code scanning / CodeQL

CSRF protection weakened or disabled High

Potential CSRF vulnerability due to forgery protection being disabled or weakened.

def index
# Configuration of the form - as API
attributes = {
title: params[:title].presence || 'Všeobecná agenda',
description: params[:description].presence || 'Formulár pre odoslanie podania pre všeobecnú agendu',
subject: params[:subject_placeholder],
text: params[:text_placeholder],
signed_required: params[:signed_required],
attachments_template: params[:attachments_template],
}.with_indifferent_access

# Appending whatever user submitted into recipient, subject, text and attachments
# + remembers the form configuration between submits
attributes.merge!(general_agenda_params || {})

@application_form = Apps::GeneralAgendaApp::GeneralAgenda::ApplicationForm.new(attributes)

return render :invalid_template unless @application_form.valid_template?

redirect_to_upvs_submission if @application_form.is_submitted && @application_form.valid?
end

def callback
end

private

def redirect_to_upvs_submission
@submission_form = UpvsSubmissions::Forms::GeneralAgenda.new(@application_form)

render :redirect_to_upvs_submission
end

def general_agenda_params
params[:apps_general_agenda_app_general_agenda_application_form]&.permit(
:title,
:description,
:subject,
:text,
:signed_required,
:recipient_name,
:recipient_uri,
:is_submitted,
attachments: {},
attachments_template: [:name, :description, :required, :signed_required]
)
end
end
end
end
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
module Datahub::Upvs
class PublicAuthorityEdesksController < ApplicationController

def search
render json: PublicAuthorityEdesk.search(params[:q])
module Datahub
module Upvs
class PublicAuthorityEdesksController < ApplicationController
def search
render json: PublicAuthorityEdesk.search(params[:q])
end
end
end
end
26 changes: 26 additions & 0 deletions app/controllers/datahub/upvs/services_with_forms_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module Datahub
module Upvs
class ServicesWithFormsController < ApplicationController
def search_recipient
result = if testing?
# Datahub::Upvs::ServicesWithForms.search(params[:q])
[{ name: 'Testing - ico://sk/83369507', uri: 'ico://sk/83369507' }]
else
Datahub::Upvs::ServicesWithForms.search(params[:q])
end
render json: { result: result }
end

private

# Testing is determined based on the content of 'SLOVENSKO_SK_API_URL' which looks like e.g.:
# - https://localhost:4000
# - https://fix.slovensko-sk-api.staging.slovensko.digital
#
# If you need to see production list of recipients, change the `SLOVENSKO_SK_API_URL` ENV to something else
def testing?
['staging.', 'localhost'].any? { |e| ENV['SLOVENSKO_SK_API_URL'].to_s.include?(e) }
end
end
end
end
54 changes: 51 additions & 3 deletions app/controllers/upvs/submissions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ class Upvs::SubmissionsController < ApplicationController

before_action :set_metadata
before_action :build_upvs_submission, only: :create
before_action :load_upvs_submission, only: [:show, :submit, :finish]
before_action :load_upvs_submission, only: [:show, :submit, :finish, :signing_data, :update_blob_after_signature]
before_action :load_blob, only: [:signing_data, :update_blob_after_signature]

rescue_from Upvs::Submission::SkApiError, :with => :handle_error

Expand Down Expand Up @@ -52,8 +53,54 @@ def finish
def submission_error
end

def signing_data
response = {
file_name: @blob.filename,
mime_type: @blob.content_type,
content: Base64.strict_encode64(@blob.download)
}

# This mime type is set in `UpvsSubmissions::Forms::GeneralAgenda#create_form_attachment`
if @blob.content_type == 'application/x-eform-xml'
form_template = Upvs::FormTemplateRelatedDocument.find_by!(message_type: 'App.GeneralAgenda')

response.merge!({
identifier: form_template.identifier,
container_xmlns: 'http://data.gov.sk/def/container/xmldatacontainer+xml/1.1',
schema: Base64.strict_encode64(form_template.xsd_schema),
transformation: Base64.strict_encode64(form_template.xslt_transformation)
})
end

render json: response
end

def update_blob_after_signature
render_partial = params[:render_partial].in?(['signed_badge', 'blob_row_content']) ? params[:render_partial] : 'signed_badge'
new_blob = ActiveStorage::Blob.create_and_upload!(
io: StringIO.new(Base64.decode64(params[:content])),
filename: params[:name],
content_type: params[:mimetype],
metadata: { signed: true, signed_required: @blob.metadata[:signed_required] }.compact
)

@blob.signed_blob.attach(new_blob) # 'signed_blob' is defined in `config/initializers/active_storage.rb`

render json: {
success: true,
old_blob_id: @blob.id,
html: render_to_string(partial: "upvs/submissions/#{render_partial}", locals: { blob: @blob })
}
rescue StandardError, ScriptError => e
render json: { success: false, error: "Nastala chyba pri podpisovaní. [#{e.class}]: #{e.message}" }
end

private

def load_blob
@blob = ActiveStorage::Blob.find_signed!(params[:signed_blob_id]) # This is only 'hashed blob ID', not signed blob object nor file
end

def load_upvs_submission
@upvs_submission = current_user.find_upvs_submission!(params[:id] || params[:submission_id])
end
Expand Down Expand Up @@ -87,9 +134,10 @@ def submission_params
:sender_business_reference,
:recipient_business_reference,
:message_subject,
:form,
:form_blob_id,
:attachments,
:callback_url
:callback_url,
attachments_ids: []
).except(:authenticity_token)
end

Expand Down
30 changes: 30 additions & 0 deletions app/helpers/app_form_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,36 @@ def text_field(method, options = {})
end
end

def text_area(method, options = {})
group_classes = ['govuk-form-group']
field_classes = ['govuk-textarea', options[:class]]
described_by = []

label = options.delete(:label)
label = label(method, label, class: 'govuk-label') if label

hint = options.delete(:hint)
if hint
hint = @template.content_tag(:span, hint, id: hint_id(method), class: 'govuk-hint')
described_by << hint_id(method)
end

if @object.errors[method].present?
group_classes << 'govuk-form-group--error'
field_classes << 'govuk-input--error'
described_by << error_id(method)
end

options = options.merge({'aria-describedby': described_by.join(' ')}) unless described_by.empty?

@template.content_tag(:div, class: group_classes) do
@template.concat label
@template.concat hint
@template.concat error_message(method)
@template.concat super(method, objectify_options(options.merge({class: field_classes})))
end
end

def select(method, values, options = {}, html_options = {}, &block)
group_classes = ['govuk-form-group']
field_classes = ['govuk-select', html_options[:class]]
Expand Down
6 changes: 6 additions & 0 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,10 @@ def sanitize_description(description, length: 500)
def dont_show_small_search_bar?
current_page?(root_path) || current_page?(search_path)
end

def localize_boolean(value)
value.to_s.in?(['1', 'true']) ? 'Áno' : 'Nie'
end

alias :lb :localize_boolean
end
11 changes: 11 additions & 0 deletions app/jobs/remove_stale_blobs_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class RemoveStaleBlobs < ApplicationJob
queue_as :default

# TODO: Setup this as a cron job and run once a week
def perform
# In `GeneralAgenda` user can upload a file which creates an `ActiveStorage::Blob`
# if this form is not converted into `Uvps::Submission`, the file remains uploaded, but not attached anywhere
# These files should be regularly purged
ActiveStorage::Blob.unattached.where('created_at < ?', 3.days.ago).each(&:purge_later)
end
end
Loading
Loading