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

Adding a redacting box to hide the information on the document for in… #109

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
4 changes: 4 additions & 0 deletions app/javascript/application.scss
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ button[disabled] .enabled {
@apply select base-input w-full font-normal;
}

.bg-redact{
background: black;
}

.tooltip-bottom-end:before {
transform: translateX(-95%);
top: var(--tooltip-offset);
Expand Down
47 changes: 44 additions & 3 deletions app/javascript/submission_form/area.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,44 @@
<template>
<div
v-if="field.type === 'redact'"
class="flex absolute"
:style="{ ...computedStyle, backgroundColor: 'black' }"
:class="{ 'cursor-default ': !submittable, 'border ': submittable, 'z-0 ': isActive && submittable, 'bg-opacity-100 ': (isActive || isValueSet) && submittable }"
>
<div
v-if="!isActive && !isValueSet && field.type !== 'checkbox' && submittable"
class="absolute top-0 bottom-0 right-0 left-0 items-center justify-center h-full w-full"
>
<span
v-if="field"
class="flex justify-center items-center h-full opacity-50"
>
<component
:is="fieldIcons[field.type]"
width="100%"
height="100%"
class="max-h-10 text-base-content text-white"
/>
</span>
</div>
<div
v-else
class="flex items-center px-0.5"
>
<span v-if="Array.isArray(modelValue)">
{{ modelValue.join(', ') }}
</span>
<span v-else-if="field.type === 'date'">
{{ formattedDate }}
</span>
<span v-else>
{{ modelValue }}
</span>
</div>
</div>

<div
v-else
class="flex absolute text-[1.5vw] lg:text-base"
:style="computedStyle"
:class="{ 'cursor-default': !submittable, 'bg-red-100 border cursor-pointer ': submittable, 'border-red-100': !isActive && submittable, 'bg-opacity-70': !isActive && !isValueSet && submittable, 'border-red-500 border-dashed z-10': isActive && submittable, 'bg-opacity-30': (isActive || isValueSet) && submittable }"
Expand Down Expand Up @@ -119,7 +158,7 @@
</template>

<script>
import { IconTextSize, IconWritingSign, IconCalendarEvent, IconPhoto, IconCheckbox, IconPaperclip, IconSelect, IconCircleDot, IconChecks, IconCheck, IconColumns3, IconPhoneCheck, IconLetterCaseUpper } from '@tabler/icons-vue'
import { IconTextSize, IconWritingSign, IconCalendarEvent, IconPhoto, IconCheckbox, IconPaperclip, IconSelect, IconCircleDot, IconChecks, IconCheck, IconColumns3, IconPhoneCheck, IconBarrierBlock, IconLetterCaseUpper } from '@tabler/icons-vue'

export default {
name: 'FieldArea',
Expand Down Expand Up @@ -187,7 +226,8 @@ export default {
checkbox: 'Checkbox',
radio: 'Radio',
multiple: 'Multiple Select',
phone: 'Phone'
phone: 'Phone',
redact: 'redact'
}
},
fieldIcons () {
Expand All @@ -203,7 +243,8 @@ export default {
radio: IconCircleDot,
cells: IconColumns3,
multiple: IconChecks,
phone: IconPhoneCheck
phone: IconPhoneCheck,
redact: IconBarrierBlock
}
},
image () {
Expand Down
11 changes: 11 additions & 0 deletions app/javascript/submission_form/form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,15 @@
@focus="$refs.areas.scrollIntoField(currentField)"
@submit="submitStep"
/>
<RedactStep
v-else-if="currentField.type === 'redact'"
ref="currentStep"
v-model="values[currentField.uuid]"
:field="currentField"
:submitter-slug="submitterSlug"
@focus="$refs.areas.scrollIntoField(currentField)"
@submit="submitStep"
/>
</div>
<div class="mt-6 md:mt-8">
<button
Expand Down Expand Up @@ -330,6 +339,7 @@ import InitialsStep from './initials_step'
import AttachmentStep from './attachment_step'
import MultiSelectStep from './multi_select_step'
import PhoneStep from './phone_step'
import RedactStep from './redact_step.vue'
import TextStep from './text_step'
import DateStep from './date_step'
import FormCompleted from './completed'
Expand All @@ -350,6 +360,7 @@ export default {
IconArrowsDiagonal,
TextStep,
PhoneStep,
RedactStep,
IconArrowsDiagonalMinimize2,
FormCompleted
},
Expand Down
9 changes: 9 additions & 0 deletions app/javascript/submission_form/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const en = {
please_check_the_box_to_continue: 'Please check the box to continue',
open_source_documents_software: 'open source documents software',
verified_phone_number: 'Verify Phone Number',
redact: 'redact',
use_international_format: 'Use internatioanl format: +1xxx',
six_digits_code: '6-digit code',
change_phone_number: 'Change phone number',
Expand Down Expand Up @@ -61,6 +62,7 @@ const es = {
please_check_the_box_to_continue: 'Por favor marque la casilla para continuar',
open_source_documents_software: 'software de documentos de código abierto',
verified_phone_number: 'Verificar número de teléfono',
redact: 'redact',
use_international_format: 'Usar formato internacional: +1xxx',
six_digits_code: 'Código de 6 dígitos',
change_phone_number: 'Cambiar número de teléfono',
Expand Down Expand Up @@ -99,6 +101,7 @@ const it = {
please_check_the_box_to_continue: 'Si prega di spuntare la casella per continuare',
open_source_documents_software: 'software di documenti open source',
verified_phone_number: 'Verifica numero di telefono',
redact: 'redact',
use_international_format: 'Usa formato internazionale: +1xxx',
six_digits_code: 'Codice a 6 cifre',
change_phone_number: 'Cambia numero di telefono',
Expand Down Expand Up @@ -137,6 +140,7 @@ const de = {
please_check_the_box_to_continue: 'Bitte setzen Sie das Häkchen, um fortzufahren',
open_source_documents_software: 'Open-Source-Dokumentensoftware',
verified_phone_number: 'Telefonnummer überprüfen',
redact: 'redact',
use_international_format: 'Internationales Format verwenden: +1xxx',
six_digits_code: '6-stelliger Code',
change_phone_number: 'Telefonnummer ändern',
Expand Down Expand Up @@ -175,6 +179,7 @@ const fr = {
please_check_the_box_to_continue: 'Veuillez cocher la case pour continuer',
open_source_documents_software: 'logiciel de documents open source',
verified_phone_number: 'Vérifier le numéro de téléphone',
redact: 'redact',
use_international_format: 'Utiliser le format international : +1xxx',
six_digits_code: 'Code à 6 chiffres',
change_phone_number: 'Changer le numéro de téléphone',
Expand Down Expand Up @@ -213,6 +218,7 @@ const pl = {
please_check_the_box_to_continue: 'Proszę zaznaczyć pole, aby kontynuować',
open_source_documents_software: 'oprogramowanie do dokumentów open source',
verified_phone_number: 'Zweryfikuj numer telefonu',
redact: 'redact',
use_international_format: 'Użyj międzynarodowego formatu: +1xxx',
six_digits_code: '6-cyfrowy kod',
change_phone_number: 'Zmień numer telefonu',
Expand Down Expand Up @@ -251,6 +257,7 @@ const uk = {
please_check_the_box_to_continue: 'Будь ласка, позначте прапорець, щоб продовжити',
open_source_documents_software: 'відкритий програмний засіб для документів',
verified_phone_number: 'Підтвердіть номер телефону',
redact: 'redact',
use_international_format: 'Використовуйте міжнародний формат: +1xxx',
six_digits_code: '6-значний код',
change_phone_number: 'Змінити номер телефону',
Expand Down Expand Up @@ -289,6 +296,7 @@ const cs = {
please_check_the_box_to_continue: 'Prosím, zaškrtněte políčko pro pokračování',
open_source_documents_software: 'open source software pro dokumenty',
verified_phone_number: 'Ověřte telefonní číslo',
redact: 'redact',
use_international_format: 'Použijte mezinárodní formát: +1xxx',
six_digits_code: '6-místný kód',
change_phone_number: 'Změnit telefonní číslo',
Expand Down Expand Up @@ -327,6 +335,7 @@ const pt = {
please_check_the_box_to_continue: 'Por favor, marque a caixa para continuar',
open_source_documents_software: 'software de documentos de código aberto',
verified_phone_number: 'Verificar Número de Telefone',
redact: 'redact',
use_international_format: 'Use formato internacional: +1xxx',
six_digits_code: 'Código de 6 dígitos',
change_phone_number: 'Alterar número de telefone',
Expand Down
5 changes: 5 additions & 0 deletions app/javascript/submission_form/redact_step.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div>
<p class="text-center">This portion is redacted</p>
</div>
</template>
17 changes: 17 additions & 0 deletions app/javascript/template_builder/area.vue
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,23 @@
</button>
</div>
<div
v-if="field.type === 'redact'"
class="opacity-100 flex items-center justify-center h-full w-full bg-redact"
>
<span
v-if="field"
class="flex justify-center items-center space-x-1 h-full"
>
<component
:is="fieldIcons[field.type]"
width="100%"
height="100%"
class="max-h-10 text-white"
/>
</span>
</div>
<div
v-else
class="flex items-center h-full w-full"
:class="[bgColors[submitterIndex], field?.default_value ? '' : 'justify-center']"
>
Expand Down
11 changes: 6 additions & 5 deletions app/javascript/template_builder/field_type.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@
</template>

<script>
import { IconTextSize, IconWritingSign, IconCalendarEvent, IconPhoto, IconCheckbox, IconPaperclip, IconSelect, IconCircleDot, IconChecks, IconColumns3, IconPhoneCheck, IconLetterCaseUpper } from '@tabler/icons-vue'

import { IconTextSize, IconWritingSign, IconCalendarEvent, IconPhoto, IconCheckbox, IconPaperclip, IconSelect, IconCircleDot, IconChecks, IconColumns3, IconPhoneCheck, IconBarrierBlock, IconLetterCaseUpper } from '@tabler/icons-vue'
export default {
name: 'FiledTypeDropdown',
inject: ['withPhone'],
Expand Down Expand Up @@ -92,7 +91,8 @@ export default {
multiple: 'Multiple',
radio: 'Radio',
cells: 'Cells',
phone: 'Phone'
phone: 'Phone',
redact: 'redact'
}
},
fieldIcons () {
Expand All @@ -107,8 +107,9 @@ export default {
checkbox: IconCheckbox,
radio: IconCircleDot,
multiple: IconChecks,
cells: IconColumns3,
phone: IconPhoneCheck
radio: IconCircleDot,
phone: IconPhoneCheck,
redact: IconBarrierBlock
}
}
},
Expand Down
9 changes: 9 additions & 0 deletions app/models/template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ class Template < ApplicationRecord
scope :active, -> { where(deleted_at: nil) }
scope :archived, -> { where.not(deleted_at: nil) }

after_save :create_secure_images

def create_secure_images
documents.each do |doc|
document_data = doc.blob.download
Templates::ProcessDocument.generate_pdf_secured_preview_images(self, doc, document_data)
end
end

private

def maybe_set_default_folder
Expand Down
8 changes: 4 additions & 4 deletions app/views/submit_form/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
</div>
<% (@submitter.submission.template_schema || @submitter.submission.template.schema).each do |item| %>
<% document = @submitter.submission.template_schema_documents.find { |a| a.uuid == item['attachment_uuid'] } %>
<% document_annots_index = document.metadata.dig('pdf', 'annotations')&.group_by { |e| e['page'] } || {} %>
<% preview_images_index = document.preview_images.loaded? ? document.preview_images.index_by { |e| e.filename.base.to_i } : {} %>
<% lazyload_metadata = document.preview_images.last.metadata %>
<% (document.metadata.dig('pdf', 'number_of_pages') || (document.preview_images.loaded? ? preview_images_index.size : document.preview_images.size)).times do |index| %>
<% document_annots_index = document.metadata.dig('pdf', 'annotations')&.group_by { |e| e['page'] } || {} %>
<% preview_images_index = document.preview_secured_images.loaded? ? document.preview_secured_images.index_by { |e| e.filename.base.to_i } : {} %>
<% lazyload_metadata = document.preview_secured_images.last.metadata %>
<% (document.metadata.dig('pdf', 'number_of_pages') || (document.preview_secured_images.loaded? ? preview_images_index.size : document.preview_secured_images.size)).times do |index| %>
<% page = preview_images_index[index] || page_blob_struct.new(metadata: lazyload_metadata, url: preview_document_page_path(document.uuid, "#{index}.jpg")) %>
<div class="relative my-4 shadow-md">
<img loading="lazy" src="<%= page.url %>" width="<%= page.metadata['width'] %>" height="<%= page.metadata['height'] %>">
Expand Down
1 change: 1 addition & 0 deletions config/initializers/active_storage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
attribute :uuid, :string, default: -> { SecureRandom.uuid }

has_many_attached :preview_images
has_many_attached :preview_secured_images
end

ActiveStorage::LogSubscriber.detach_from(:active_storage) if Rails.env.production?
Expand Down
15 changes: 13 additions & 2 deletions lib/submissions/generate_result_attachments.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ def call(submitter)

value = submitter.values[field['uuid']]

next if Array.wrap(value).compact_blank.blank?
if !field['type']=='redact'
next if Array.wrap(value).compact_blank.blank?
end

canvas = page.canvas(type: :overlay)
canvas.font(FONT_NAME, size: font_size)
Expand Down Expand Up @@ -149,6 +151,15 @@ def call(submitter)
.draw(canvas, ((area['x'] * width) + (cell_width * index)),
height - (area['y'] * height))
end
when 'redact'
x = area['x'] * width
y = height - (area['y'] * height) - (area['h'] * height)
w = area['w'] * width
h = area['h'] * height

canvas.fill_color(0, 0, 0) # Set fill color to black
canvas.rectangle(x, y, w, h)
canvas.fill
else
value = I18n.l(Date.parse(value), format: :default, locale: account.locale) if field['type'] == 'date'

Expand Down Expand Up @@ -260,7 +271,7 @@ def build_pdf_from_image(attachment)
page.box.height = attachment.metadata['height'] * scale

page.canvas.image(
StringIO.new(attachment.preview_images.first.download),
StringIO.new(attachment.preview_secured_images.first.download),
at: [0, 0],
width: page.box.width,
height: page.box.height
Expand Down
33 changes: 33 additions & 0 deletions lib/templates/process_document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module ProcessDocument
DPI = 200
FORMAT = '.jpg'
ATTACHMENT_NAME = 'preview_images'
SECURED_ATTACHMENT_NAME = 'preview_secured_images'

PDF_CONTENT_TYPE = 'application/pdf'
Q = 35
Expand Down Expand Up @@ -101,5 +102,37 @@ def generate_pdf_preview_from_file(attachment, file_path, page_number)

io
end

def generate_pdf_secured_preview_images(template, attachment, data)
ActiveStorage::Attachment.where(name: SECURED_ATTACHMENT_NAME, record: attachment).destroy_all
number_of_pages = PDF::Reader.new(StringIO.new(data)).pages.size - 1
(0..number_of_pages).each do |page_number|
pdf = Vips::Image.new_from_buffer(data, '', dpi: DPI, page: page_number)
pdf = pdf.resize(MAX_WIDTH / pdf.width.to_f)
redacted_boxes = template.fields.select { |field| field['type'] == 'redact' && field['areas'][0]['page'] == page_number }
if !redacted_boxes.empty?
redacted_boxes.each do |box|
x = (box['areas'][0]['x'] * pdf.width).to_i
y = (box['areas'][0]['y'] * pdf.height).to_i
w = (box['areas'][0]['w'] * pdf.width).to_i
h = (box['areas'][0]['h'] * pdf.height).to_i
black_rect = Vips::Image.black(w, h)
pdf = pdf.insert(black_rect, x, y)
end
end

io = StringIO.new(pdf.write_to_buffer(FORMAT, Q: Q))

ActiveStorage::Attachment.create!(
blob: ActiveStorage::Blob.create_and_upload!(
io: io, filename: "#{page_number}#{FORMAT}",
metadata: { analyzed: true, identified: true, width: pdf.width, height: pdf.height }
),
name: SECURED_ATTACHMENT_NAME,
record: attachment
)
end
end

end
end
1 change: 1 addition & 0 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module.exports = {
secondary: '#ef9fbc',
accent: '#eeaf3a',
neutral: '#291334',
black: '#000000',
'base-100': '#faf7f5',
'base-200': '#efeae6',
'base-300': '#e7e2df',
Expand Down