pull/109/merge
Zee 2 years ago committed by GitHub
commit ac5da42df0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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);

@ -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 }"
@ -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',
@ -187,7 +226,8 @@ export default {
checkbox: 'Checkbox',
radio: 'Radio',
multiple: 'Multiple Select',
phone: 'Phone'
phone: 'Phone',
redact: 'redact'
}
},
fieldIcons () {
@ -203,7 +243,8 @@ export default {
radio: IconCircleDot,
cells: IconColumns3,
multiple: IconChecks,
phone: IconPhoneCheck
phone: IconPhoneCheck,
redact: IconBarrierBlock
}
},
image () {

@ -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
@ -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'
@ -350,6 +360,7 @@ export default {
IconArrowsDiagonal,
TextStep,
PhoneStep,
RedactStep,
IconArrowsDiagonalMinimize2,
FormCompleted
},

@ -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',
@ -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',
@ -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',
@ -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',
@ -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',
@ -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',
@ -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: 'Змінити номер телефону',
@ -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',
@ -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',

@ -0,0 +1,5 @@
<template>
<div>
<p class="text-center">This portion is redacted</p>
</div>
</template>

@ -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']"
>

@ -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'],
@ -92,7 +91,8 @@ export default {
multiple: 'Multiple',
radio: 'Radio',
cells: 'Cells',
phone: 'Phone'
phone: 'Phone',
redact: 'redact'
}
},
fieldIcons () {
@ -107,8 +107,9 @@ export default {
checkbox: IconCheckbox,
radio: IconCircleDot,
multiple: IconChecks,
cells: IconColumns3,
phone: IconPhoneCheck
radio: IconCircleDot,
phone: IconPhoneCheck,
redact: IconBarrierBlock
}
}
},

@ -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

@ -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'] %>">

@ -4,6 +4,7 @@ ActiveSupport.on_load(:active_storage_attachment) do
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?

@ -52,7 +52,9 @@ module Submissions
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)
@ -149,6 +151,15 @@ module Submissions
.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'
@ -260,7 +271,7 @@ module Submissions
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

@ -5,6 +5,7 @@ module Templates
DPI = 200
FORMAT = '.jpg'
ATTACHMENT_NAME = 'preview_images'
SECURED_ATTACHMENT_NAME = 'preview_secured_images'
PDF_CONTENT_TYPE = 'application/pdf'
Q = 35
@ -101,5 +102,37 @@ module Templates
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

@ -11,6 +11,7 @@ module.exports = {
secondary: '#ef9fbc',
accent: '#eeaf3a',
neutral: '#291334',
black: '#000000',
'base-100': '#faf7f5',
'base-200': '#efeae6',
'base-300': '#e7e2df',

Loading…
Cancel
Save