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') 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() instance.gc()
}) })
} }

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

@ -35,6 +35,7 @@ function textoMultiVigente (item, diff) {
_$(item).addClass('active') _$(item).addClass('active')
_$('.dptt.desativado').removeClass('displaynone') _$('.dptt.desativado').removeClass('displaynone')
_$('.desativado > .dn').removeClass('displaynone') _$('.desativado > .dn').removeClass('displaynone')
_$('.desativado > .dpt-img').removeClass('displaynone')
_$('.dptt.revogado').removeClass('displaynone') _$('.dptt.revogado').removeClass('displaynone')
_$('.dtxt').removeClass('displaynone') _$('.dtxt').removeClass('displaynone')
_$('.dtxt.diff').remove() _$('.dtxt.diff').remove()
@ -133,6 +134,7 @@ function textoVigente (item, link) {
_$(item).addClass('active') _$(item).addClass('active')
_$('.dptt.desativado').addClass('displaynone') _$('.dptt.desativado').addClass('displaynone')
_$('.desativado > .dn').addClass('displaynone') _$('.desativado > .dn').addClass('displaynone')
_$('.desativado > .dpt-img').addClass('displaynone')
_$('.nota-alteracao').removeClass('displaynone') _$('.nota-alteracao').removeClass('displaynone')
_$('.dptt.revogado').removeClass('displaynone') _$('.dptt.revogado').removeClass('displaynone')
if (!link) _$('.nota-alteracao').addClass('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; margin: -5px auto 0;
border-radius: 50%; border-radius: 50%;
} }
& > a { & > a {
position: absolute; position: absolute;
white-space: nowrap; white-space: nowrap;
@ -219,10 +219,10 @@ a:link:after, a:visited:after {
margin-bottom: 5px; margin-bottom: 5px;
} }
} }
ul { ul {
z-index: 1; z-index: 1;
position: absolute; position: absolute;
display: none; display: none;
background: white; background: white;
margin: 30px 0; margin: 30px 0;
@ -252,7 +252,7 @@ a:link:after, a:visited:after {
&:hover { &:hover {
background: #eee; background: #eee;
} }
} }
} }
&.active { &.active {
@ -329,6 +329,14 @@ a:link:after, a:visited:after {
border: 1px dotted #ccc; border: 1px dotted #ccc;
} }
} }
.dpt-img {
img {
filter: grayscale(100%) contrast(110%);
opacity: 0.5;
}
}
} }
a { a {
@ -353,6 +361,12 @@ a:link:after, a:visited:after {
&.indent { &.indent {
padding-left: 1em; padding-left: 1em;
} }
.dpt-img {
text-align: center;
img {
max-width: 100%;
}
}
.ementa { .ementa {
padding: 2em 0em 2em 35%; padding: 2em 0em 2em 35%;
font-weight: bold; font-weight: bold;
@ -417,8 +431,8 @@ a:link:after, a:visited:after {
margin-top: 0.3333em; margin-top: 0.3333em;
font-size: 1.15em; font-size: 1.15em;
} }
.texto_n_estruturado{ .texto_n_estruturado{
margin-top: 0.3333em; margin-top: 0.3333em;
font-size: 1.15em; font-size: 1.15em;
} }
@ -443,16 +457,16 @@ a:link:after, a:visited:after {
font-size: 1.0em; font-size: 1.0em;
margin-top: 2px; margin-top: 2px;
} }
.assinatura { .assinatura {
margin-top: 0.6em; margin-top: 0.6em;
font-size: 1.15em; font-size: 1.15em;
} }
.fecho_lei { .fecho_lei {
margin-top: 0.6em; margin-top: 0.6em;
font-size: 1.15em; font-size: 1.15em;
} }
.page-break { page-break-before: always; } .page-break { page-break-before: always; }
.bloco_alteracao { .bloco_alteracao {
@ -714,6 +728,7 @@ a:link:after, a:visited:after {
table, table td { table, table td {
border: 1px dotted #ccc; border: 1px dotted #ccc;
} }
} }
a.nota-alteracao { a.nota-alteracao {
@ -1620,7 +1635,7 @@ a:link:after, a:visited:after {
} }
} }
.cp-nav-parents { .cp-nav-parents {
.dropdown-menu { .dropdown-menu {
left: 0; left: 0;
right: auto; right: auto;
@ -1810,7 +1825,7 @@ a:link:after, a:visited:after {
height: 8px; height: 8px;
margin: -4px auto 0; margin: -4px auto 0;
} }
& > a { & > a {
font-size: 0.75rem; font-size: 0.75rem;
} }
@ -1824,22 +1839,22 @@ a:link:after, a:visited:after {
margin-bottom: 4px; margin-bottom: 4px;
} }
} }
ul { ul {
a { a {
line-height: 1.3rem; line-height: 1.3rem;
font-size: 0.7rem; font-size: 0.7rem;
background: #fff; background: #fff;
} }
} }
&.active { &.active {
.circle { .circle {
width: 14px; width: 14px;
height: 14px; height: 14px;
margin: -7px auto 0; margin: -7px auto 0;
} }
&:not(:last-child) { &:not(:last-child) {
& > a { & > a {
margin-bottom: 15px; margin-bottom: 15px;
@ -1857,13 +1872,13 @@ a:link:after, a:visited:after {
} }
@media print { @media print {
.cp .vigencias, .cp .vigencias,
.toggle-topbar, .toggle-topbar,
.menu-icon, .menu-icon,
.button, .button,
.tipo-vigencias, .tipo-vigencias,
.dne, .dne,
.cp-linha-vigencias, .cp-linha-vigencias,
.tipo-vigencias { .tipo-vigencias {
display:none !important; 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 image_cropping = (function ($) {
var jcrop = {} var jcrop = {}
function init() { function init() {
@ -184,7 +184,7 @@ var image_cropping = (function ($) {
} }
})(jQuery) })(jQuery)
window.image_cropping = image_cropping
jQuery(function() { jQuery(function() {
/*var image_cropping_jquery_url = jQuery('.image-ratio:first').data('jquery-url') /*var image_cropping_jquery_url = jQuery('.image-ratio:first').data('jquery-url')
if (image_cropping_jquery_url == "None") { 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.forms.models import ModelForm
from django.template import defaultfilters from django.template import defaultfilters
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from image_cropping.widgets import CropWidget, ImageCropWidget,\
get_attrs
from model_utils.choices import Choices from model_utils.choices import Choices
from prompt_toolkit.key_binding.bindings.named_commands import self_insert
from sapl import utils from sapl import utils
from sapl.compilacao.models import (NOTAS_PUBLICIDADE_CHOICES, from sapl.compilacao.models import (NOTAS_PUBLICIDADE_CHOICES,
@ -24,9 +27,20 @@ from sapl.compilacao.models import (NOTAS_PUBLICIDADE_CHOICES,
VeiculoPublicacao, Vide) VeiculoPublicacao, Vide)
from sapl.compilacao.utils import DISPOSITIVO_SELECT_RELATED from sapl.compilacao.utils import DISPOSITIVO_SELECT_RELATED
from sapl.crispy_layout_mixin import SaplFormHelper from sapl.crispy_layout_mixin import SaplFormHelper
from sapl.crispy_layout_mixin import SaplFormLayout, to_column, to_row,\ from sapl.crispy_layout_mixin import SaplFormLayout, to_column, to_row
form_actions from sapl.utils import YES_NO_CHOICES, FileFieldCheckMixin
from sapl.utils import YES_NO_CHOICES
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 = { error_messages = {
@ -513,11 +527,16 @@ class DispositivoIntegerField(forms.IntegerField):
min_value=0, *args, **kwargs) min_value=0, *args, **kwargs)
class DispositivoEdicaoBasicaForm(ModelForm): class DispositivoEdicaoBasicaForm(FileFieldCheckMixin, ModelForm):
class Meta: class Meta:
model = Dispositivo model = Dispositivo
fields = [] fields = ['imagem', 'imagem_cropping']
widgets = {
'imagem': CustomImageCropWidget(),
'imagem_cropping': CropWidget(),
}
error_messages = { error_messages = {
NON_FIELD_ERRORS: { NON_FIELD_ERRORS: {
@ -670,13 +689,24 @@ class DispositivoEdicaoBasicaForm(ModelForm):
DispositivoEdicaoBasicaForm.Meta.fields.remove( DispositivoEdicaoBasicaForm.Meta.fields.remove(
'visibilidade') 'visibilidade')
fields = DispositivoEdicaoBasicaForm.Meta.fields if 'texto' in DispositivoEdicaoBasicaForm.Meta.fields and\
if fields: not editor_type:
self.base_fields.clear() layout.append(
for f in fields: 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)}) 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 = SaplFormHelper()
self.helper.include_media = False
if not editor_type: if not editor_type:
cancel_label = _('Ir para o Editor Sequencial') cancel_label = _('Ir para o Editor Sequencial')
@ -689,6 +719,14 @@ class DispositivoEdicaoBasicaForm(ModelForm):
super(DispositivoEdicaoBasicaForm, self).__init__(*args, **kwargs) 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, def actions_get_form_base(self, layout,
inst, inst,
texto_articulado_do_editor=None): 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 import messages
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType 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.decorators import classonlymethod
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from image_cropping.fields import ImageCropField, ImageRatioField
import reversion import reversion
from sapl.compilacao.utils import (get_integrations_view_names, int_to_letter, from sapl.compilacao.utils import (get_integrations_view_names, int_to_letter,
int_to_roman) 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() @reversion.register()
@ -965,6 +965,10 @@ class Publicacao(TimestampedMixin):
self.ta) self.ta)
def imagem_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath='')
@reversion.register() @reversion.register()
class Dispositivo(BaseModel, TimestampedMixin): class Dispositivo(BaseModel, TimestampedMixin):
TEXTO_PADRAO_DISPOSITIVO_REVOGADO = force_text(_('(Revogado)')) TEXTO_PADRAO_DISPOSITIVO_REVOGADO = force_text(_('(Revogado)'))
@ -1186,6 +1190,17 @@ class Dispositivo(BaseModel, TimestampedMixin):
verbose_name=_('Contagem contínua') 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: class Meta:
verbose_name = _('Dispositivo') verbose_name = _('Dispositivo')
verbose_name_plural = _('Dispositivos') verbose_name_plural = _('Dispositivos')

1
sapl/parlamentares/models.py

@ -271,6 +271,7 @@ class Parlamentar(models.Model):
verbose_name=_('Ativo na Casa?')) verbose_name=_('Ativo na Casa?'))
biografia = models.TextField( biografia = models.TextField(
blank=True, verbose_name=_('Biografia')) blank=True, verbose_name=_('Biografia'))
fotografia = ImageCropField( fotografia = ImageCropField(
verbose_name=_('Fotografia'), upload_to=foto_upload_path, verbose_name=_('Fotografia'), upload_to=foto_upload_path,
validators=[restringe_tipos_de_arquivo_img], null=True, blank=True) 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 i18n %}
{% load compilacao_filters %} {% load compilacao_filters cropping%}
{% load common_tags %} {% load common_tags %}
{% dispositivotree dispositivos_list %} {% dispositivotree dispositivos_list %}
@ -53,11 +53,18 @@
</a> </a>
{% endif %} {% endif %}
</div> </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-form clearfix"></div>
<div class="dpt-actions-bottom btn-group"></div> <div class="dpt-actions-bottom btn-group"></div>
{% if node.alts or node.td.dispositivo_de_alteracao and node.td.dispositivo_de_articulacao %} {% if node.alts or node.td.dispositivo_de_alteracao and node.td.dispositivo_de_articulacao %}
<div class="dpt-alts">{{ alts }} <div class="dpt-alts">{{ alts }}
</div> </div>
{% if node.td.dispositivo_de_alteracao %} {% if node.td.dispositivo_de_alteracao %}
<div class="dpt-actions-fixed bottom"> <div class="dpt-actions-fixed bottom">

10
sapl/templates/compilacao/text_list_bloco.html

@ -1,6 +1,7 @@
{% load i18n %} {% load i18n %}
{% load compilacao_filters %} {% load compilacao_filters %}
{% load common_tags %} {% load common_tags %}
{% load cropping %}
{% for dpt in object_list %} {% for dpt in object_list %}
{% if dpt.tipo_dispositivo.dispositivo_de_alteracao and not dpt.tipo_dispositivo.dispositivo_de_articulacao%} {% 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 }} {{ 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 }} <a class="dpt-link" name="{{dpt.pk}}" title="{{dpt.pk}}">{{ dpt.rotulo }}</a>{{ dpt.tipo_dispositivo.rotulo_sufixo_html|safe }}
{% endif %} {% endif %}
{% 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> <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> </ul>
</div> </div>
{% endif%} {% endif%}
{% if dpt.imagem %}
<div class="dpt-img">
<img src="{% cropped_thumbnail dpt 'imagem_cropping' %}">
</div>
{% endif %}
</div> </div>
{% if dpt.ta_publicado_id %} {% if dpt.ta_publicado_id %}
<div class="clearfix"></div> <div class="clearfix"></div>

10
sapl/templates/compilacao/text_list_blocoalteracao.html

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

8
sapl/utils.py

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

Loading…
Cancel
Save