Browse Source

Adiciona ImageField em compilacao.Dispositivo

pull/3359/head
Leandro Roberto 4 years ago
parent
commit
ce924e68fd
  1. 4
      frontend/src/__apps/compilacao/js/old/compilacao_edit.js
  2. 2
      frontend/src/__apps/compilacao/js/old/compilacao_view.js
  3. 57
      frontend/src/__apps/compilacao/scss/compilacao.scss
  4. 4
      frontend/src/__global/js/image_cropping/image_cropping.js
  5. 56
      sapl/compilacao/forms.py
  6. 25
      sapl/compilacao/migrations/0017_auto_20210225_1127.py
  7. 21
      sapl/compilacao/models.py
  8. 1
      sapl/parlamentares/models.py
  9. 11
      sapl/templates/compilacao/text_edit_bloco.html
  10. 10
      sapl/templates/compilacao/text_list_bloco.html
  11. 10
      sapl/templates/compilacao/text_list_blocoalteracao.html
  12. 8
      sapl/utils.py

4
frontend/src/__apps/compilacao/js/old/compilacao_edit.js

@ -228,6 +228,10 @@ window.DispositivoEdit = function () {
dpt.find('.btn-group-inserts').removeClass('open show')
})
// Para ativar image_cropping dentro do editor dinâmico descolmentar linha abaixo
// e retirar restrição do backend que adiciona input image apenas o editor avançado
// window.image_cropping.init()
instance.gc()
})
}

2
frontend/src/__apps/compilacao/js/old/compilacao_view.js

@ -35,6 +35,7 @@ function textoMultiVigente (item, diff) {
_$(item).addClass('active')
_$('.dptt.desativado').removeClass('displaynone')
_$('.desativado > .dn').removeClass('displaynone')
_$('.desativado > .dpt-img').removeClass('displaynone')
_$('.dptt.revogado').removeClass('displaynone')
_$('.dtxt').removeClass('displaynone')
_$('.dtxt.diff').remove()
@ -133,6 +134,7 @@ function textoVigente (item, link) {
_$(item).addClass('active')
_$('.dptt.desativado').addClass('displaynone')
_$('.desativado > .dn').addClass('displaynone')
_$('.desativado > .dpt-img').addClass('displaynone')
_$('.nota-alteracao').removeClass('displaynone')
_$('.dptt.revogado').removeClass('displaynone')
if (!link) _$('.nota-alteracao').addClass('displaynone')

57
frontend/src/__apps/compilacao/scss/compilacao.scss

@ -200,7 +200,7 @@ a:link:after, a:visited:after {
margin: -5px auto 0;
border-radius: 50%;
}
& > a {
position: absolute;
white-space: nowrap;
@ -219,10 +219,10 @@ a:link:after, a:visited:after {
margin-bottom: 5px;
}
}
ul {
z-index: 1;
position: absolute;
position: absolute;
display: none;
background: white;
margin: 30px 0;
@ -252,7 +252,7 @@ a:link:after, a:visited:after {
&:hover {
background: #eee;
}
}
}
}
&.active {
@ -329,6 +329,14 @@ a:link:after, a:visited:after {
border: 1px dotted #ccc;
}
}
.dpt-img {
img {
filter: grayscale(100%) contrast(110%);
opacity: 0.5;
}
}
}
a {
@ -353,6 +361,12 @@ a:link:after, a:visited:after {
&.indent {
padding-left: 1em;
}
.dpt-img {
text-align: center;
img {
max-width: 100%;
}
}
.ementa {
padding: 2em 0em 2em 35%;
font-weight: bold;
@ -417,8 +431,8 @@ a:link:after, a:visited:after {
margin-top: 0.3333em;
font-size: 1.15em;
}
.texto_n_estruturado{
.texto_n_estruturado{
margin-top: 0.3333em;
font-size: 1.15em;
}
@ -443,16 +457,16 @@ a:link:after, a:visited:after {
font-size: 1.0em;
margin-top: 2px;
}
.assinatura {
margin-top: 0.6em;
font-size: 1.15em;
}
.fecho_lei {
margin-top: 0.6em;
font-size: 1.15em;
}
}
.page-break { page-break-before: always; }
.bloco_alteracao {
@ -714,6 +728,7 @@ a:link:after, a:visited:after {
table, table td {
border: 1px dotted #ccc;
}
}
a.nota-alteracao {
@ -1620,7 +1635,7 @@ a:link:after, a:visited:after {
}
}
.cp-nav-parents {
.cp-nav-parents {
.dropdown-menu {
left: 0;
right: auto;
@ -1810,7 +1825,7 @@ a:link:after, a:visited:after {
height: 8px;
margin: -4px auto 0;
}
& > a {
font-size: 0.75rem;
}
@ -1824,22 +1839,22 @@ a:link:after, a:visited:after {
margin-bottom: 4px;
}
}
ul {
a {
line-height: 1.3rem;
font-size: 0.7rem;
background: #fff;
}
}
}
&.active {
.circle {
width: 14px;
height: 14px;
margin: -7px auto 0;
}
&:not(:last-child) {
& > a {
margin-bottom: 15px;
@ -1857,13 +1872,13 @@ a:link:after, a:visited:after {
}
@media print {
.cp .vigencias,
.toggle-topbar,
.menu-icon,
.button,
.tipo-vigencias,
.cp .vigencias,
.toggle-topbar,
.menu-icon,
.button,
.tipo-vigencias,
.dne,
.cp-linha-vigencias,
.cp-linha-vigencias,
.tipo-vigencias {
display:none !important;
}

4
frontend/src/__global/js/image_cropping/image_cropping.js

@ -1,4 +1,4 @@
/* eslint-disable */
/* eslint-disable */
var image_cropping = (function ($) {
var jcrop = {}
function init() {
@ -184,7 +184,7 @@ var image_cropping = (function ($) {
}
})(jQuery)
window.image_cropping = image_cropping
jQuery(function() {
/*var image_cropping_jquery_url = jQuery('.image-ratio:first').data('jquery-url')
if (image_cropping_jquery_url == "None") {

56
sapl/compilacao/forms.py

@ -13,7 +13,10 @@ from django.forms.forms import Form
from django.forms.models import ModelForm
from django.template import defaultfilters
from django.utils.translation import ugettext_lazy as _
from image_cropping.widgets import CropWidget, ImageCropWidget,\
get_attrs
from model_utils.choices import Choices
from prompt_toolkit.key_binding.bindings.named_commands import self_insert
from sapl import utils
from sapl.compilacao.models import (NOTAS_PUBLICIDADE_CHOICES,
@ -24,9 +27,20 @@ from sapl.compilacao.models import (NOTAS_PUBLICIDADE_CHOICES,
VeiculoPublicacao, Vide)
from sapl.compilacao.utils import DISPOSITIVO_SELECT_RELATED
from sapl.crispy_layout_mixin import SaplFormHelper
from sapl.crispy_layout_mixin import SaplFormLayout, to_column, to_row,\
form_actions
from sapl.utils import YES_NO_CHOICES
from sapl.crispy_layout_mixin import SaplFormLayout, to_column, to_row
from sapl.utils import YES_NO_CHOICES, FileFieldCheckMixin
class CustomImageCropWidget(ImageCropWidget):
"""
Custom ImageCropWidget that doesn't show the initial value of the field.
We use this trick, and place it right under the CropWidget so that
it looks like the user is seeing the image and clearing the image.
"""
template_with_initial = (
# '%(initial_text)s: <a href="%(initial_url)s">%(initial)s</a> '
'%(clear_template)s<br />%(input_text)s: %(input)s'
)
error_messages = {
@ -513,11 +527,16 @@ class DispositivoIntegerField(forms.IntegerField):
min_value=0, *args, **kwargs)
class DispositivoEdicaoBasicaForm(ModelForm):
class DispositivoEdicaoBasicaForm(FileFieldCheckMixin, ModelForm):
class Meta:
model = Dispositivo
fields = []
fields = ['imagem', 'imagem_cropping']
widgets = {
'imagem': CustomImageCropWidget(),
'imagem_cropping': CropWidget(),
}
error_messages = {
NON_FIELD_ERRORS: {
@ -670,13 +689,24 @@ class DispositivoEdicaoBasicaForm(ModelForm):
DispositivoEdicaoBasicaForm.Meta.fields.remove(
'visibilidade')
fields = DispositivoEdicaoBasicaForm.Meta.fields
if fields:
self.base_fields.clear()
for f in fields:
if 'texto' in DispositivoEdicaoBasicaForm.Meta.fields and\
not editor_type:
layout.append(
Fieldset('Anexar Imagem',
to_row([('imagem', 7), ('imagem_cropping', 5), ]),
css_class="col-md-12"))
DispositivoEdicaoBasicaForm.Meta.fields.append('imagem')
DispositivoEdicaoBasicaForm.Meta.fields.append('imagem_cropping')
for f in DispositivoEdicaoBasicaForm.Meta.fields:
if hasattr(self, f):
self.base_fields.update({f: getattr(self, f)})
for f in set(self.base_fields.keys()) - set(DispositivoEdicaoBasicaForm.Meta.fields):
self.base_fields.pop(f)
self.helper = SaplFormHelper()
self.helper.include_media = False
if not editor_type:
cancel_label = _('Ir para o Editor Sequencial')
@ -689,6 +719,14 @@ class DispositivoEdicaoBasicaForm(ModelForm):
super(DispositivoEdicaoBasicaForm, self).__init__(*args, **kwargs)
#imagem = self.fields['imagem'].widget
# imagem.attrs.update(
# get_attrs(self.instance.imagem, 'imagem')
#)
# if 'class' in imagem.attrs:
# imagem.attrs.pop('class')
def actions_get_form_base(self, layout,
inst,
texto_articulado_do_editor=None):

25
sapl/compilacao/migrations/0017_auto_20210225_1127.py

@ -0,0 +1,25 @@
# Generated by Django 2.2.13 on 2021-02-25 14:27
from django.db import migrations
import image_cropping.fields
import sapl.compilacao.models
class Migration(migrations.Migration):
dependencies = [
('compilacao', '0016_auto_20201013_1159'),
]
operations = [
migrations.AddField(
model_name='dispositivo',
name='imagem',
field=image_cropping.fields.ImageCropField(blank=True, null=True, upload_to=sapl.compilacao.models.imagem_upload_path, verbose_name='Imagem'),
),
migrations.AddField(
model_name='dispositivo',
name='imagem_cropping',
field=image_cropping.fields.ImageRatioField('imagem', '0x0', adapt_rotation=False, allow_fullsize=False, free_crop=False, help_text='O recorte de imagem é possível após a atualização.', hide_image_field=False, size_warning=True, verbose_name='Recorte de Imagem'),
),
]

21
sapl/compilacao/models.py

@ -1,5 +1,3 @@
from bs4 import BeautifulSoup
from django.contrib import messages
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
@ -13,11 +11,13 @@ from django.utils import timezone
from django.utils.decorators import classonlymethod
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
from image_cropping.fields import ImageCropField, ImageRatioField
import reversion
from sapl.compilacao.utils import (get_integrations_view_names, int_to_letter,
int_to_roman)
from sapl.utils import YES_NO_CHOICES, get_settings_auth_user_model
from sapl.utils import YES_NO_CHOICES, get_settings_auth_user_model,\
texto_upload_path, restringe_tipos_de_arquivo_img
@reversion.register()
@ -965,6 +965,10 @@ class Publicacao(TimestampedMixin):
self.ta)
def imagem_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath='')
@reversion.register()
class Dispositivo(BaseModel, TimestampedMixin):
TEXTO_PADRAO_DISPOSITIVO_REVOGADO = force_text(_('(Revogado)'))
@ -1186,6 +1190,17 @@ class Dispositivo(BaseModel, TimestampedMixin):
verbose_name=_('Contagem contínua')
)
imagem = ImageCropField(
verbose_name=_('Imagem'),
upload_to=imagem_upload_path,
validators=[restringe_tipos_de_arquivo_img], null=True, blank=True)
imagem_cropping = ImageRatioField(
'imagem', '100x100', verbose_name=_('Recorte de Imagem'),
free_crop=True, size_warning=True,
help_text=_('O recorte de imagem '
'é possível após a atualização.'))
class Meta:
verbose_name = _('Dispositivo')
verbose_name_plural = _('Dispositivos')

1
sapl/parlamentares/models.py

@ -271,6 +271,7 @@ class Parlamentar(models.Model):
verbose_name=_('Ativo na Casa?'))
biografia = models.TextField(
blank=True, verbose_name=_('Biografia'))
fotografia = ImageCropField(
verbose_name=_('Fotografia'), upload_to=foto_upload_path,
validators=[restringe_tipos_de_arquivo_img], null=True, blank=True)

11
sapl/templates/compilacao/text_edit_bloco.html

@ -1,5 +1,5 @@
{% load i18n %}
{% load compilacao_filters %}
{% load compilacao_filters cropping%}
{% load common_tags %}
{% dispositivotree dispositivos_list %}
@ -53,11 +53,18 @@
</a>
{% endif %}
</div>
{% if node.dpt.imagem %}
<div class="alert alert-info m-2">
<strong>Dispositivo possui imagem anexada.</strong><br>
A visualização junto ao texto é possível no <i>PreView</i> e/ou após publicado. Ou no Editor Avançado do dispositivo acima.
</div>
{% endif %}
<div class="dpt-form clearfix"></div>
<div class="dpt-actions-bottom btn-group"></div>
{% if node.alts or node.td.dispositivo_de_alteracao and node.td.dispositivo_de_articulacao %}
<div class="dpt-alts">{{ alts }}
</div>
{% if node.td.dispositivo_de_alteracao %}
<div class="dpt-actions-fixed bottom">

10
sapl/templates/compilacao/text_list_bloco.html

@ -1,6 +1,7 @@
{% load i18n %}
{% load compilacao_filters %}
{% load common_tags %}
{% load cropping %}
{% for dpt in object_list %}
{% if dpt.tipo_dispositivo.dispositivo_de_alteracao and not dpt.tipo_dispositivo.dispositivo_de_articulacao%}
@ -28,7 +29,7 @@
{{ dpt.tipo_dispositivo.rotulo_prefixo_html|safe }}
<a class="dpt-link" name="{{dpt.pk}}" title="{{dpt.pk}}">{{ dpt.rotulo }}</a>{{ dpt.tipo_dispositivo.rotulo_sufixo_html|safe }}
{% endif %}
{% endif %}
<span class="dtxt" id="d{% if not dpt.dispositivo_subsequente_id and dpt.dispositivo_substituido_id %}a{% endif %}{{dpt.pk}}" pks="{{dpt.dispositivo_substituido_id|default:''}}" pk="{{dpt.pk}}">{{ dpt.tipo_dispositivo.texto_prefixo_html|safe }}{%if dpt.texto %}{{ dpt.texto|safe }}{%else%}{%if not dpt.tipo_dispositivo.dispositivo_de_articulacao %}&nbsp;{% endif %}{% endif %}</span>
@ -177,6 +178,13 @@
</ul>
</div>
{% endif%}
{% if dpt.imagem %}
<div class="dpt-img">
<img src="{% cropped_thumbnail dpt 'imagem_cropping' %}">
</div>
{% endif %}
</div>
{% if dpt.ta_publicado_id %}
<div class="clearfix"></div>

10
sapl/templates/compilacao/text_list_blocoalteracao.html

@ -1,5 +1,4 @@
{% load compilacao_filters %}
{% load common_tags %}
{% load compilacao_filters cropping common_tags %}
{% for ch in dpt.pk|get_bloco_atualizador %}
{% spaceless %}
{% if ch.visibilidade %}
@ -30,6 +29,13 @@
{{ ch.tipo_dispositivo.rotulo_sufixo_html|safe }}
{{ ch.tipo_dispositivo.texto_prefixo_html|safe }}{% if ch.texto_atualizador %}{{ ch.texto_atualizador|safe }}{%else%}{{ ch.texto|safe }}{% endif %}
{% endif %}
{% if ch.imagem %}
<div class="dpt-img">
<img src="{% cropped_thumbnail ch 'imagem_cropping' %}">
</div>
{% endif %}
</div>
</div>
{% endif %}

8
sapl/utils.py

@ -6,7 +6,6 @@ from operator import itemgetter
import os
import platform
import re
import requests
import tempfile
from time import time
from unicodedata import normalize as unicodedata_normalize
@ -35,6 +34,7 @@ import django_filters
from easy_thumbnails import source_generators
from floppyforms import ClearableFileInput
import magic
import requests
from reversion_compare.admin import CompareVersionAdmin
from unipath.path import Path
@ -592,6 +592,7 @@ def fabrica_validador_de_tipos_de_arquivo(lista, nome):
restringe_tipos_de_arquivo_txt = fabrica_validador_de_tipos_de_arquivo(
TIPOS_TEXTO_PERMITIDOS, 'restringe_tipos_de_arquivo_txt')
restringe_tipos_de_arquivo_img = fabrica_validador_de_tipos_de_arquivo(
TIPOS_IMG_PERMITIDOS, 'restringe_tipos_de_arquivo_img')
@ -1025,11 +1026,12 @@ def timing(f):
ts = time()
result = f(*args, **kw)
te = time()
logger.info('funcao:%r args:[%r, %r] took: %2.4f sec' % \
(f.__name__, args, kw, te-ts))
logger.info('funcao:%r args:[%r, %r] took: %2.4f sec' %
(f.__name__, args, kw, te - ts))
return result
return wrap
@timing
def lista_anexados(principal):
from sapl.materia.models import MateriaLegislativa

Loading…
Cancel
Save