diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..0e7b5a3
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,15 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.{md,yml}]
+indent_size = 2
diff --git a/Gemfile b/Gemfile
index 8590dbd..e723a41 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,40 +1,40 @@
-source "https://rubygems.org"
-# Hello! This is where you manage which Jekyll version is used to run.
-# When you want to use a different version, change it below, save the
-# file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
-#
-# bundle exec jekyll serve
-#
-# This will help ensure the proper Jekyll version is running.
-# Happy Jekylling!
-gem "jekyll", "~> 4.3.3"
-# This is the default theme for new Jekyll sites. You may change this to anything you like.
-# gem "minima", "~> 2.5"
-# If you want to use GitHub Pages, remove the "gem "jekyll"" above and
-# uncomment the line below. To upgrade, run `bundle update github-pages`.
-# gem "github-pages", group: :jekyll_plugins
-# If you have any plugins, put them here!
-group :jekyll_plugins do
- gem "jekyll-feed"
- gem "jekyll-toc"
- gem "jekyll-sitemap"
- gem "jekyll-minifier"
-end
-
-# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
-# and associated library.
-platforms :mingw, :x64_mingw, :mswin, :jruby do
- gem "tzinfo", ">= 1", "< 3"
- gem "tzinfo-data"
-end
-
-# Performance-booster for watching directories on Windows
-gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin]
-
-# Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem
-# do not have a Java counterpart.
-gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby]
-
-gem "rouge"
-
-gem "webrick", "~> 1.8"
+source "https://rubygems.org"
+# Hello! This is where you manage which Jekyll version is used to run.
+# When you want to use a different version, change it below, save the
+# file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
+#
+# bundle exec jekyll serve
+#
+# This will help ensure the proper Jekyll version is running.
+# Happy Jekylling!
+gem "jekyll", "~> 4.3.3"
+# This is the default theme for new Jekyll sites. You may change this to anything you like.
+# gem "minima", "~> 2.5"
+# If you want to use GitHub Pages, remove the "gem "jekyll"" above and
+# uncomment the line below. To upgrade, run `bundle update github-pages`.
+# gem "github-pages", group: :jekyll_plugins
+# If you have any plugins, put them here!
+group :jekyll_plugins do
+ gem "jekyll-feed"
+ gem "jekyll-toc"
+ gem "jekyll-sitemap"
+ gem "jekyll-minifier"
+end
+
+# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
+# and associated library.
+platforms :mingw, :x64_mingw, :mswin, :jruby do
+ gem "tzinfo", ">= 1", "< 3"
+ gem "tzinfo-data"
+end
+
+# Performance-booster for watching directories on Windows
+gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin]
+
+# Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem
+# do not have a Java counterpart.
+gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby]
+
+gem "rouge"
+
+gem "webrick", "~> 1.8"
diff --git a/_config.yml b/_config.yml
index ec806f1..de646b5 100644
--- a/_config.yml
+++ b/_config.yml
@@ -72,6 +72,8 @@ exclude:
- LICENSE
- rollup.config.js
- package*.json
+ - .gitignore
+ - .editorconfig
include:
- _pages
diff --git a/_posts/2018-03-30-html-purifier-um-pouco-a-mais-da-simples-limpeza.markdown b/_posts/2018-03-30-html-purifier-um-pouco-a-mais-da-simples-limpeza.md
similarity index 96%
rename from _posts/2018-03-30-html-purifier-um-pouco-a-mais-da-simples-limpeza.markdown
rename to _posts/2018-03-30-html-purifier-um-pouco-a-mais-da-simples-limpeza.md
index 7dee3ee..f0f9aea 100644
--- a/_posts/2018-03-30-html-purifier-um-pouco-a-mais-da-simples-limpeza.markdown
+++ b/_posts/2018-03-30-html-purifier-um-pouco-a-mais-da-simples-limpeza.md
@@ -1,567 +1,567 @@
----
-title: "HTML Purifier - Um pouco a mais da simples limpeza"
-description: Efetuando uma limpeza poderosa no HTML para evitar dores de cabeça com conteúdo indesejado e possíveis quebras de layout.
-date: 2018-03-30 19:00:00 -0400
-categories: php tutorial
-tags:
- - desenvolvimento
- - php
- - html
- - purifier
----
-
-Tratar entrada de usuário é obrigação, principalmente se permitirmos que ela seja em HTML. Embora os editores WYSIWYG
-possuam configurações de limpeza nós não podemos escapar dessa árdua tarefa no back-end.
-
-Há alguns anos conheci o [HTML Purifier](http://htmlpurifier.org/) e ele virou a ferramenta que mais utilizo para
-"limpar" o HTML.
-
-Infelizmente quase sempre usei a configuração padrão do HTML Purifier.
-Em partes por que me atendia bem, mas também por que a documentação não é muito atrativa.
-
-> Porém, a vida é uma caixinha de surpresas e numa bela manhã de Sol [...]
-
-Recentemente estava trabalhando numa versão antiga do [Moodle](https://moodle.org/), que está utilizando
-o framework [Bootstrap](https://getbootstrap.com/), e comecei a perceber problemas com conteúdo HTML dos alunos,
-principalmente quando eles copiavam o texto de páginas Web.
-
-Devido ao texto vir com "sujeira" (muito CSS inline e elementos com tamanhos definidos com width e height) o
-layout ficava destoante e em alguns casos ficava quebrado mesmo, impedindo que o grid se ajustasse à largura da tela.
-
-E como a necessidade nos força a sair da zona de conforto resolvi estudar mais a documentação do HTML Purifier
-e aqui estou pra entregar um pouco do que aprendi.
-
-## Funcionamento básico
-
-O exemplo mais simples de uso do HTML Purifier, após incluir o autoload, é:
-
-```php
-purify($html_sujo);
-```
-
-A configuração padrão do HTML Purifier atende muito bem na maioria dos casos.
-Na página de [demonstração](http://htmlpurifier.org/demo.php) é possível testar as opções de configuração.
-
-Mas é na [documentação da configuração](http://htmlpurifier.org/live/configdoc/plain.html) que tudo começa a
-ficar mais interessante.
-
-## Configurando o HTML Purifier
-
-Para definir um item da configuração basta copiar seu nome na página de configuração e passar o valor desejado
-à variável de configuração, que sempre trarei na variável **$config**. Os nomes são bem explicativos.
-
-```php
-set('AutoFormat.RemoveEmpty', true);
-$config->set('AutoFormat.RemoveEmpty.RemoveNbsp', true);
-```
-
-### Uma configuração menos permissiva
-
-Para o meu caso eu precisava remover várias propriedades, classes e até elementos do HTML que o usuário postava. Basicamente eu poderia permitir somente algumas propriedades como cores, negrito, alinhamento etc.
-
-Pensando em tudo que era permitido e proibido eu fiz a seguinte configuração:
-
-```php
-set('CSS.AllowedProperties', $allowedStyleProperties);
-$config->set('Attr.AllowedClasses', $allowedClasses);
-$config->set('HTML.ForbiddenElements', $forbiddenElements);
-$config->set('HTML.ForbiddenAttributes', $forbiddenAttributes);
-$config->set('AutoFormat.RemoveEmpty', true);
-$config->set('AutoFormat.RemoveSpansWithoutAttributes', true);
-$config->set('Output.Newline', "\n");
-$config->set('AutoFormat.RemoveEmpty.RemoveNbsp', true);
-$config->set('HTML.TidyLevel', 'heavy');
-```
-
-## As transformações do HTML Purifier
-
-Se você prestou atenção nas configurações deve ter notado a `HTML.TidyLevel`.
-
-O HTML Purifier utiliza essa configuração para substituir as tags e atributos descontinuados por tags e
-atributos atualizados. Ou seja, ele pega um HTML assim:
-
-```html
-
Centralizado e Branco
-Centralizado!
-```
-
-E deixa assim:
-
-```html
-Centralizado e Branco
-Centralizado!
-```
-
-A ótima notícia é que podemos usar a ferramenta de análise e transformação do HTML Purifier
-para fazer as nossas modificações.
-
-Podemos personalizar as definições do HTML, olha só a
-[documentação de personalização](http://htmlpurifier.org/docs/enduser-customize.html),
-mas isso é tema pra outro post. Por enquanto usar as transformações é o suficiente.
-
-### Transformando Atributos
-
-Para alterar os atributos basta criar uma classe que estenda a `HTMLPurifier_AttrTransform` e
-implemente o método `transform`, o qual recebe os atributos da tag, além da configuração e contexto,
-realiza as mudanças e então retorna os atributos.
-
-No diretório `library/HTMLPurifier/AttrTransform` há várias classes utilizadas pelo HTML Purifier e
-que podemos usar como exemplo para desenvolver as nossas. Todas estendem a classe `HTMLPurifier_AttrTransform` e
-implementam o método `transform`.
-
-### Transformando Elementos
-
-De forma semelhante às transformações de atributos podemos realizar transformações completas nos elementos.
-
-Podemos criar uma classe que estenda a `HTMLPurifier_TagTransform` e que
-implemente o método `transform`, o qual recebe a tag, a configuração e o contexto.
-
-No diretório `library/HTMLPurifier/TagTransform` há duas classes utilizadas pelo HTML Purifier, a classe que
-transforma a tag **font** e uma classe mais "genérica" que trata todas as outras transformações simples,
-a `HTMLPurifier_TagTransform_Simple`.
-
-### Configurando o HTML Purifier para usar as transformações
-
-Toda a configuração das transformações ficam nas definições do HTML.
-Para pegar as definições basta usar o método `getHTMLDefinition` na **$config** e você receberá uma
-instância da `HTMLPurifier_HTMLDefinition`.
-
-Nesse objeto de definições há 3 propriedades públicas que recebem as nossas instâncias de transformações.
-
-- **info_tag_transform** é um array cujo índice é o nome da tag a transformar
-- **info_attr_transform_pre** é um array de índice numérico que transformará os atributos no início do processo
-- **info_attr_transform_post** é um array de índice numérico que transformará os atributos no final do processo
-
-Poderíamos configurar o HTML Purifier para fazer algumas trocas de teste assim:
-
-```php
-getHTMLDefinition();
-
-// Transformará toda tag p em uma tag div com CSS inline para deixar o texto com sublinhado
-$htmlDefinition->info_tag_transform['p'] = new HTMLPurifier_TagTransform_Simple(
- 'div',
- 'text-decoration:underline;'
-);
-
-// Converte o atributo bgcolor em um CSS inline
-$htmlDefinition->info_attr_transform_pre[] = new HTMLPurifier_AttrTransform_BgColor();
-
-// Tudo configurado basta instanciar o HTML Purifier
-$purifier = new HTMLPurifier($config);
-$html_limpo = $purifier->purify($html_sujo);
-```
-
-## Criando as nossas transformações
-
-Para o meu caso eu precisava cumprir esses objetivos:
-
-- Adicionar a classe **img-responsive** às tags **img**
-- Adicionar as classes **table** e **table-condensed** às tags **table**
-- Adicionar a classe **table-bordered** se a tag **table** possuir o atributo **border**
-- Cercar a tabela com uma **div** que possua a classe **table-responsive**
-
-Com isso em mente vamos ao trabalho.
-
-### Adicionando as classes básicas nas tags
-
-Para adicionar as classes padrões que não dependem de alguma verificação criei uma classe genérica baseada na
-classe `HTMLPurifier_TagTransform_Simple`.
-
-```php
-classes = array_unique(array_map('trim', $classes));
- }
-
- /**
- * @param HTMLPurifier_Token_Tag $tag
- * @param HTMLPurifier_Config $config
- * @param HTMLPurifier_Context $context
- * @return HTMLPurifier_Token_Tag
- */
- public function transform($tag, $config, $context)
- {
- // Se for a tag de fim não precisa alterar nada
- if (empty($this->classes) || $tag instanceof HTMLPurifier_Token_End) {
- return $tag;
- }
-
- $new_tag = clone $tag;
-
- if (empty($new_tag->attr['class'])) {
- $new_tag->attr['class'] = '';
- }
-
- $new_tag->attr['class'] = $this->appendClasses($new_tag->attr['class']);
- return $new_tag;
- }
-
- /**
- * Concatena as classes da instância à string de classes já definidas
- * @param string $classes
- * @return string
- */
- protected function appendClasses($classes = '')
- {
- return implode(
- ' ',
- array_unique(
- array_merge(
- array_map('trim', explode(' ', $classes)),
- $this->classes
- )
- )
- );
- }
-}
-```
-
-Então configuramos a definição do HTML com as novas instâncias de transformações.
-
-```php
-info_tag_transform['table'] = new TagClassTransform('table table-condensed');
-$htmlDefinition->info_tag_transform['img'] = new TagClassTransform('img-responsive');
-```
-
-Com essa configuração já podemos converter esse HTML:
-
-```html
-
-
-
-
-
- Hey! |
- Ho! |
- Lets Go! |
-
-
-
-```
-
-Nessa versão quase pronta:
-
-```html
-
-
-
-
-
- Hey! |
- Ho! |
- Lets Go! |
-
-
-
-```
-
-### Trocando o atributo border pela classe table-bordered
-
-Precisamos fazer a troca do atributo **border**, independente do seu valor, pela classe
-**table-bordered**. Para isso basta criar uma classe estendendo a `HTMLPurifier_AttrTransform` e
-então adicionar uma instância dela ao array `$htmlDefinition->info_attr_transform_pre`.
-
-```php
-get('CurrentToken')->name !== 'table') {
- return $attr;
- }
-
- // A intenção é trocar o atributo, então já podemos removê-lo
- unset($attr['border']);
-
- if (empty($attr['class'])) {
- $attr['class'] = '';
- }
-
- $attr['class'] = $this->appendClasses($attr['class']);
-
- return $attr;
- }
-
- /**
- * Concatena as classes da instância à string de classes já definidas
- * Ps: sim, poderíamos fazer uma trait
- * @param string $classes
- * @return string
- */
- protected function appendClasses($classes = '')
- {
- return implode(
- ' ',
- array_unique(
- array_merge(
- array_map('trim', explode(' ', $classes)),
- $this->classes
- )
- )
- );
- }
-}
-
-// Então adicionar nas definições HTML
-$htmlDefinition->info_attr_transform_pre[] = new TableBorderAttrTransform();
-```
-
-E agora aquele HTML sujo:
-
-```html
-
-
-
-
-
- Hey! |
- Ho! |
- Lets Go! |
-
-
-
-```
-
-Fica assim:
-
-```html
-
-
-
-
-
- Hey! |
- Ho! |
- Lets Go! |
-
-
-
-```
-
-## Criando um wrapper para a tabela
-
-Para a tabela não extrapolar os limites da área devemos cercá-la com uma **div.table-responsive**.
-
-Agora é hora de bater um pouco a cabeça. Como usar a transformação para criar um wrapper na nossa tabela?
-
-Simplesmente não vamos utilizar, afinal não queremos transformar uma table e sim inserir algo antes e depois dela.
-
-O HTML Purifier usa a classe `HTMLPurifier_Injector` para fazer inserções no HTML.
-Por exemplo a opção `AutoFormat.Linkify` usa injector para inserir a tag **a** com o atributo **href**
-em torno de um texto que case com a regra de URL.
-
-Utilizar injector é mais complexo do que uma TagTransform por que você analisará os tokens que formam o HTML,
-mas é possível resolver mais fácil do que os quebra-cabeças do novo filme da Lara Croft.
-
-Dê uma olhada no diretório `library\HTMLPurifier\Injector` e ficará surpreso com as inserções que o Purifier já possui.
-
-Basicamente a `HTMLPurifier_Injector` possui 3 métodos para trabalhar com os tokens analisados.
-
-- **handleText**: chamado quando o token é um texto
-- **handleElement**: chamado quando o token é uma tag de início ou vazia
-- **handleEnd**: chamado quando o token é uma tag de fim
-
-Todos os métodos recebem uma referência de `HTMLPurifier_Token` e ela será modificada caso queira adicionar algo.
-Geralmente será convertida em um array e então adicionará tokens neste array na sequência necessária.
-
-É importante observar as propriedades **name** e **needed** da `HTMLPurifier_Injector`.
-
-A propriedade `name` indica o nome do seu injector e a `needed` indica quais tags e atributos são necessários para ela
-funcionar (a `HTMLPurifier_Injector` identifica o `needed` e caso exista alguma regra que proíba as tags e
-atributos já cancela a sua execução).
-
- Nosso injector ficou assim:
-
- ```php
- ['class'],
- ];
-
- /**
- * @param HTMLPurifier_Token_Start $token
- */
- public function handleElement(&$token)
- {
- /*
- * Esse método é executado em todo token que indica um início
- * Se não for uma tag table simplesmente ignoramos
- */
- if (!$token->is_tag || $token->name !== 'table') {
- return;
- }
-
- // Copiando o token para não o perdermos
- $table = clone $token;
-
- /*
- * Transformando o token original em um array e
- * inserindo os tokens na sequência necessária
- */
- $token = [
- new HTMLPurifier_Token_Start('div', ['class' => 'table-responsive']),
- $table,
- ];
- }
-
- /**
- * @param HTMLPurifier_Token_End $token
- */
- public function handleEnd(&$token)
- {
- /*
- * Esse método é executado em todo token que indica um fim
- * Se não for uma tag table simplesmente ignoramos
- */
- if (!$token->is_tag || $token->name !== 'table') {
- return;
- }
-
- // Copiando o token para não o perdermos
- $table = clone $token;
-
- /*
- * Transformando o token original em um array e
- * inserindo os tokens na sequência necessária
- */
- $token = [
- $table,
- new HTMLPurifier_Token_End('div'),
- ];
- }
-}
-```
-
-Vamos adicionar nosso injector na definição HTML:
-
-```php
-info_injector[] = new TableResponsiveWrapperInjector();
-```
-
-E agora nosso HTML limpo ficou pronto:
-
-```html
-
-
-
-
-
- Hey! |
- Ho! |
- Lets Go! |
-
-
-
-```
-
-### Código fonte completo
-
-Você pode visualizar o código completo no meu [Gist](https://gist.github.com/brunogasparetto/63f42e44388f13153b57e4c02df33f27).
-
-## Algumas dicas extras
-
-Use o HTML Purifier antes de persistir os dados (indiferente se utilizar arquivos ou banco de dados), pois o processo de analisar e converter o HTML é custoso e não deve ser executado todas as vezes que for exibir na tela. Então o melhor é já salvar limpo.
-
-Reduza o HTML antes de salvá-lo para economizar espaço. Durante esse processo eu utilizei a [Tiny Html Minifier](https://github.com/jenstornell/tiny-html-minifier) e gostei do resultado.
-
-Caso você permita edição é bom salvar o HTML original.
+---
+title: "HTML Purifier - Um pouco a mais da simples limpeza"
+description: "Efetuando uma limpeza poderosa no HTML para evitar dores de cabeça com conteúdo indesejado e possíveis quebras de layout."
+date: 2018-03-30 19:00:00 -0400
+categories: php tutorial
+tags:
+ - desenvolvimento
+ - php
+ - html
+ - purifier
+---
+
+Tratar entrada de usuário é obrigação, principalmente se permitirmos que ela seja em HTML. Embora os editores WYSIWYG
+possuam configurações de limpeza nós não podemos escapar dessa árdua tarefa no back-end.
+
+Há alguns anos conheci o [HTML Purifier](http://htmlpurifier.org/) e ele virou a ferramenta que mais utilizo para
+"limpar" o HTML.
+
+Infelizmente quase sempre usei a configuração padrão do HTML Purifier.
+Em partes por que me atendia bem, mas também por que a documentação não é muito atrativa.
+
+> Porém, a vida é uma caixinha de surpresas e numa bela manhã de Sol [...]
+
+Recentemente estava trabalhando numa versão antiga do [Moodle](https://moodle.org/), que está utilizando
+o framework [Bootstrap](https://getbootstrap.com/), e comecei a perceber problemas com conteúdo HTML dos alunos,
+principalmente quando eles copiavam o texto de páginas Web.
+
+Devido ao texto vir com "sujeira" (muito CSS inline e elementos com tamanhos definidos com width e height) o
+layout ficava destoante e em alguns casos ficava quebrado mesmo, impedindo que o grid se ajustasse à largura da tela.
+
+E como a necessidade nos força a sair da zona de conforto resolvi estudar mais a documentação do HTML Purifier
+e aqui estou pra entregar um pouco do que aprendi.
+
+## Funcionamento básico
+
+O exemplo mais simples de uso do HTML Purifier, após incluir o autoload, é:
+
+```php
+purify($html_sujo);
+```
+
+A configuração padrão do HTML Purifier atende muito bem na maioria dos casos.
+Na página de [demonstração](http://htmlpurifier.org/demo.php) é possível testar as opções de configuração.
+
+Mas é na [documentação da configuração](http://htmlpurifier.org/live/configdoc/plain.html) que tudo começa a
+ficar mais interessante.
+
+## Configurando o HTML Purifier
+
+Para definir um item da configuração basta copiar seu nome na página de configuração e passar o valor desejado
+à variável de configuração, que sempre trarei na variável **$config**. Os nomes são bem explicativos.
+
+```php
+set('AutoFormat.RemoveEmpty', true);
+$config->set('AutoFormat.RemoveEmpty.RemoveNbsp', true);
+```
+
+### Uma configuração menos permissiva
+
+Para o meu caso eu precisava remover várias propriedades, classes e até elementos do HTML que o usuário postava. Basicamente eu poderia permitir somente algumas propriedades como cores, negrito, alinhamento etc.
+
+Pensando em tudo que era permitido e proibido eu fiz a seguinte configuração:
+
+```php
+set('CSS.AllowedProperties', $allowedStyleProperties);
+$config->set('Attr.AllowedClasses', $allowedClasses);
+$config->set('HTML.ForbiddenElements', $forbiddenElements);
+$config->set('HTML.ForbiddenAttributes', $forbiddenAttributes);
+$config->set('AutoFormat.RemoveEmpty', true);
+$config->set('AutoFormat.RemoveSpansWithoutAttributes', true);
+$config->set('Output.Newline', "\n");
+$config->set('AutoFormat.RemoveEmpty.RemoveNbsp', true);
+$config->set('HTML.TidyLevel', 'heavy');
+```
+
+## As transformações do HTML Purifier
+
+Se você prestou atenção nas configurações deve ter notado a `HTML.TidyLevel`.
+
+O HTML Purifier utiliza essa configuração para substituir as tags e atributos descontinuados por tags e
+atributos atualizados. Ou seja, ele pega um HTML assim:
+
+```html
+Centralizado e Branco
+Centralizado!
+```
+
+E deixa assim:
+
+```html
+Centralizado e Branco
+Centralizado!
+```
+
+A ótima notícia é que podemos usar a ferramenta de análise e transformação do HTML Purifier
+para fazer as nossas modificações.
+
+Podemos personalizar as definições do HTML, olha só a
+[documentação de personalização](http://htmlpurifier.org/docs/enduser-customize.html),
+mas isso é tema pra outro post. Por enquanto usar as transformações é o suficiente.
+
+### Transformando Atributos
+
+Para alterar os atributos basta criar uma classe que estenda a `HTMLPurifier_AttrTransform` e
+implemente o método `transform`, o qual recebe os atributos da tag, além da configuração e contexto,
+realiza as mudanças e então retorna os atributos.
+
+No diretório `library/HTMLPurifier/AttrTransform` há várias classes utilizadas pelo HTML Purifier e
+que podemos usar como exemplo para desenvolver as nossas. Todas estendem a classe `HTMLPurifier_AttrTransform` e
+implementam o método `transform`.
+
+### Transformando Elementos
+
+De forma semelhante às transformações de atributos podemos realizar transformações completas nos elementos.
+
+Podemos criar uma classe que estenda a `HTMLPurifier_TagTransform` e que
+implemente o método `transform`, o qual recebe a tag, a configuração e o contexto.
+
+No diretório `library/HTMLPurifier/TagTransform` há duas classes utilizadas pelo HTML Purifier, a classe que
+transforma a tag **font** e uma classe mais "genérica" que trata todas as outras transformações simples,
+a `HTMLPurifier_TagTransform_Simple`.
+
+### Configurando o HTML Purifier para usar as transformações
+
+Toda a configuração das transformações ficam nas definições do HTML.
+Para pegar as definições basta usar o método `getHTMLDefinition` na **$config** e você receberá uma
+instância da `HTMLPurifier_HTMLDefinition`.
+
+Nesse objeto de definições há 3 propriedades públicas que recebem as nossas instâncias de transformações.
+
+- **info_tag_transform** é um array cujo índice é o nome da tag a transformar
+- **info_attr_transform_pre** é um array de índice numérico que transformará os atributos no início do processo
+- **info_attr_transform_post** é um array de índice numérico que transformará os atributos no final do processo
+
+Poderíamos configurar o HTML Purifier para fazer algumas trocas de teste assim:
+
+```php
+getHTMLDefinition();
+
+// Transformará toda tag p em uma tag div com CSS inline para deixar o texto com sublinhado
+$htmlDefinition->info_tag_transform['p'] = new HTMLPurifier_TagTransform_Simple(
+ 'div',
+ 'text-decoration:underline;'
+);
+
+// Converte o atributo bgcolor em um CSS inline
+$htmlDefinition->info_attr_transform_pre[] = new HTMLPurifier_AttrTransform_BgColor();
+
+// Tudo configurado basta instanciar o HTML Purifier
+$purifier = new HTMLPurifier($config);
+$html_limpo = $purifier->purify($html_sujo);
+```
+
+## Criando as nossas transformações
+
+Para o meu caso eu precisava cumprir esses objetivos:
+
+- Adicionar a classe **img-responsive** às tags **img**
+- Adicionar as classes **table** e **table-condensed** às tags **table**
+- Adicionar a classe **table-bordered** se a tag **table** possuir o atributo **border**
+- Cercar a tabela com uma **div** que possua a classe **table-responsive**
+
+Com isso em mente vamos ao trabalho.
+
+### Adicionando as classes básicas nas tags
+
+Para adicionar as classes padrões que não dependem de alguma verificação criei uma classe genérica baseada na
+classe `HTMLPurifier_TagTransform_Simple`.
+
+```php
+classes = array_unique(array_map('trim', $classes));
+ }
+
+ /**
+ * @param HTMLPurifier_Token_Tag $tag
+ * @param HTMLPurifier_Config $config
+ * @param HTMLPurifier_Context $context
+ * @return HTMLPurifier_Token_Tag
+ */
+ public function transform($tag, $config, $context)
+ {
+ // Se for a tag de fim não precisa alterar nada
+ if (empty($this->classes) || $tag instanceof HTMLPurifier_Token_End) {
+ return $tag;
+ }
+
+ $new_tag = clone $tag;
+
+ if (empty($new_tag->attr['class'])) {
+ $new_tag->attr['class'] = '';
+ }
+
+ $new_tag->attr['class'] = $this->appendClasses($new_tag->attr['class']);
+ return $new_tag;
+ }
+
+ /**
+ * Concatena as classes da instância à string de classes já definidas
+ * @param string $classes
+ * @return string
+ */
+ protected function appendClasses($classes = '')
+ {
+ return implode(
+ ' ',
+ array_unique(
+ array_merge(
+ array_map('trim', explode(' ', $classes)),
+ $this->classes
+ )
+ )
+ );
+ }
+}
+```
+
+Então configuramos a definição do HTML com as novas instâncias de transformações.
+
+```php
+info_tag_transform['table'] = new TagClassTransform('table table-condensed');
+$htmlDefinition->info_tag_transform['img'] = new TagClassTransform('img-responsive');
+```
+
+Com essa configuração já podemos converter esse HTML:
+
+```html
+
+
+
+
+
+ Hey! |
+ Ho! |
+ Lets Go! |
+
+
+
+```
+
+Nessa versão quase pronta:
+
+```html
+
+
+
+
+
+ Hey! |
+ Ho! |
+ Lets Go! |
+
+
+
+```
+
+### Trocando o atributo border pela classe table-bordered
+
+Precisamos fazer a troca do atributo **border**, independente do seu valor, pela classe
+**table-bordered**. Para isso basta criar uma classe estendendo a `HTMLPurifier_AttrTransform` e
+então adicionar uma instância dela ao array `$htmlDefinition->info_attr_transform_pre`.
+
+```php
+get('CurrentToken')->name !== 'table') {
+ return $attr;
+ }
+
+ // A intenção é trocar o atributo, então já podemos removê-lo
+ unset($attr['border']);
+
+ if (empty($attr['class'])) {
+ $attr['class'] = '';
+ }
+
+ $attr['class'] = $this->appendClasses($attr['class']);
+
+ return $attr;
+ }
+
+ /**
+ * Concatena as classes da instância à string de classes já definidas
+ * Ps: sim, poderíamos fazer uma trait
+ * @param string $classes
+ * @return string
+ */
+ protected function appendClasses($classes = '')
+ {
+ return implode(
+ ' ',
+ array_unique(
+ array_merge(
+ array_map('trim', explode(' ', $classes)),
+ $this->classes
+ )
+ )
+ );
+ }
+}
+
+// Então adicionar nas definições HTML
+$htmlDefinition->info_attr_transform_pre[] = new TableBorderAttrTransform();
+```
+
+E agora aquele HTML sujo:
+
+```html
+
+
+
+
+
+ Hey! |
+ Ho! |
+ Lets Go! |
+
+
+
+```
+
+Fica assim:
+
+```html
+
+
+
+
+
+ Hey! |
+ Ho! |
+ Lets Go! |
+
+
+
+```
+
+## Criando um wrapper para a tabela
+
+Para a tabela não extrapolar os limites da área devemos cercá-la com uma **div.table-responsive**.
+
+Agora é hora de bater um pouco a cabeça. Como usar a transformação para criar um wrapper na nossa tabela?
+
+Simplesmente não vamos utilizar, afinal não queremos transformar uma table e sim inserir algo antes e depois dela.
+
+O HTML Purifier usa a classe `HTMLPurifier_Injector` para fazer inserções no HTML.
+Por exemplo a opção `AutoFormat.Linkify` usa injector para inserir a tag **a** com o atributo **href**
+em torno de um texto que case com a regra de URL.
+
+Utilizar injector é mais complexo do que uma TagTransform por que você analisará os tokens que formam o HTML,
+mas é possível resolver mais fácil do que os quebra-cabeças do novo filme da Lara Croft.
+
+Dê uma olhada no diretório `library\HTMLPurifier\Injector` e ficará surpreso com as inserções que o Purifier já possui.
+
+Basicamente a `HTMLPurifier_Injector` possui 3 métodos para trabalhar com os tokens analisados.
+
+- **handleText**: chamado quando o token é um texto
+- **handleElement**: chamado quando o token é uma tag de início ou vazia
+- **handleEnd**: chamado quando o token é uma tag de fim
+
+Todos os métodos recebem uma referência de `HTMLPurifier_Token` e ela será modificada caso queira adicionar algo.
+Geralmente será convertida em um array e então adicionará tokens neste array na sequência necessária.
+
+É importante observar as propriedades **name** e **needed** da `HTMLPurifier_Injector`.
+
+A propriedade `name` indica o nome do seu injector e a `needed` indica quais tags e atributos são necessários para ela
+funcionar (a `HTMLPurifier_Injector` identifica o `needed` e caso exista alguma regra que proíba as tags e
+atributos já cancela a sua execução).
+
+ Nosso injector ficou assim:
+
+ ```php
+ ['class'],
+ ];
+
+ /**
+ * @param HTMLPurifier_Token_Start $token
+ */
+ public function handleElement(&$token)
+ {
+ /*
+ * Esse método é executado em todo token que indica um início
+ * Se não for uma tag table simplesmente ignoramos
+ */
+ if (!$token->is_tag || $token->name !== 'table') {
+ return;
+ }
+
+ // Copiando o token para não o perdermos
+ $table = clone $token;
+
+ /*
+ * Transformando o token original em um array e
+ * inserindo os tokens na sequência necessária
+ */
+ $token = [
+ new HTMLPurifier_Token_Start('div', ['class' => 'table-responsive']),
+ $table,
+ ];
+ }
+
+ /**
+ * @param HTMLPurifier_Token_End $token
+ */
+ public function handleEnd(&$token)
+ {
+ /*
+ * Esse método é executado em todo token que indica um fim
+ * Se não for uma tag table simplesmente ignoramos
+ */
+ if (!$token->is_tag || $token->name !== 'table') {
+ return;
+ }
+
+ // Copiando o token para não o perdermos
+ $table = clone $token;
+
+ /*
+ * Transformando o token original em um array e
+ * inserindo os tokens na sequência necessária
+ */
+ $token = [
+ $table,
+ new HTMLPurifier_Token_End('div'),
+ ];
+ }
+}
+```
+
+Vamos adicionar nosso injector na definição HTML:
+
+```php
+info_injector[] = new TableResponsiveWrapperInjector();
+```
+
+E agora nosso HTML limpo ficou pronto:
+
+```html
+
+
+
+
+
+ Hey! |
+ Ho! |
+ Lets Go! |
+
+
+
+```
+
+### Código fonte completo
+
+Você pode visualizar o código completo no meu [Gist](https://gist.github.com/brunogasparetto/63f42e44388f13153b57e4c02df33f27).
+
+## Algumas dicas extras
+
+Use o HTML Purifier antes de persistir os dados (indiferente se utilizar arquivos ou banco de dados), pois o processo de analisar e converter o HTML é custoso e não deve ser executado todas as vezes que for exibir na tela. Então o melhor é já salvar limpo.
+
+Reduza o HTML antes de salvá-lo para economizar espaço. Durante esse processo eu utilizei a [Tiny Html Minifier](https://github.com/jenstornell/tiny-html-minifier) e gostei do resultado.
+
+Caso você permita edição é bom salvar o HTML original.
diff --git a/_posts/2024-06-01-criando-notificacoes-personalizadas-no-fluig.md b/_posts/2024-06-01-criando-notificacoes-personalizadas-no-fluig.md
new file mode 100644
index 0000000..d9e7f5a
--- /dev/null
+++ b/_posts/2024-06-01-criando-notificacoes-personalizadas-no-fluig.md
@@ -0,0 +1,170 @@
+---
+title: "Criando notificações personalizadas no Fluig"
+description: "Cansado de só enviar notificações por e-mail? Vamos aprender a criar notificações personalizadas no sistema."
+date: 2024-06-01 15:00:00 -0400
+categories: fluig tutorial
+tags:
+ - fluig
+ - desenvolvimento
+ - java
+ - javascript
+---
+
+Notificações personalizadas por e-mail são ótimas (utilizando o método `notifier.notify`), mas há situações nas
+quais queremos que o alerta seja exibido no próprio Fluig, no ícone de notificações.
+
+Tive essa necessidade de notificações no sistema durante o desenvolvimento de um processo para **Gestão de Frotas**,
+pois não queria incomodar demais os gestores avisando sobre CNHs vencidas, afinal os motoristas já receberiam a
+notificação por e-mail, então aos gestores era interessante simplesmente alertar que havia alguma coisa acontecendo.
+
+Após muita pesquisa e testes consegui fazer essa *mágica* no Fluig e compartilho com você para que não sofra tanto
+quanto eu.
+
+## Como criar as notificações personalizadas
+
+O Fluig não possuí, de forma simples, um modo de criar as notificações personalizadas. No máximo temos como configurar
+quais são as notificações padrões dos novos usuários (no menu **Painel de Controle → Pessoas → Notificações padrões para novos usuários**).
+No mesmo lugar que configuramos os processo de limpeza das notificações.
+
+Os usuários também podem clicar no ícone de Notificações, ir em Configurações, e marcar quais notificações receberá por
+e-mail, mas não é possível criar/modificar as permissões.
+
+Felizmente o Fluig possuí métodos no WS REST que permite a criação das notificações personalizadas.
+
+Basicamente temos que executar 2 processos para isso:
+
+1. Criar um módulo de Notificações;
+1. Criar os eventos de Notificação;
+
+O módulo de notificações é como se fosse um agrupador dos tipos das notificações, enquanto os eventos indicam sobre o que
+é a notificação disparada.
+
+
+
+Para facilitar o processo de chamada ao REST farei isso nas **Ferramentas do desenvolvedor** no navegador, já autenticado no Fluig
+com um usuário administrador.
+
+No Chrome basta apertar `F12` ou `Ctrl + Shift + i` ou ir no menu *Mais ferramentas → Ferramentas do desenvolvedor*.
+Quase todos os navegadores modernos possuem alguma ferramenta similar.
+
+Com as Ferramentas do Desenvolvedor aberta temos que ir na aba `Console`, onde podemos digitar código JavaScript, para realizar
+as chamadas ao Fluig.
+
+Tudo isso é possível fazer por qualquer ferramenta de requisições (Postman, Thunder Client, etc.), porém executando no navegador
+com usuário já autenticado não precisará se preocupar com a autenticação.
+
+## Criando o módulo de Notificações Personalizadas
+
+O endpoint `/api/public/alert/module/create` nos permite criar o módulo de notificações no Fluig.
+
+Então, na aba Console da Ferramenta do desenvolvedor basta colar esse código e teclar Enter.
+
+```javascript
+fetch("/api/public/alert/module/create", {
+ method: "POST",
+ redirect: "follow",
+ headers: {
+ "Content-Type": "application/json;charset=utf-8",
+ },
+ body: JSON.stringify({
+ moduleKey: "PERSONALIZADAS", // É o Código do Módulo que estamos criando
+ descriptionKey: "Personalizadas" // Descrição do Módulo
+ }),
+});
+```
+
+As propriedades `moduleKey` e `descriptionKey` identificam o código (como se fosse o userCode de um usuário)
+do módulo e a sua descrição, respectivamente, então você pode colocar o nome que mais agradar.
+
+Agora podemos acessar o endpoint `/api/public/alert/module/findVoList` para verificar se o módulo foi criado
+e pegar o ID do módulo, pois precisaremos dele para adicionar os eventos.
+
+No navegador, em uma aba que está com uma página do Fluig aberta, pode acessar a URL indicada.
+Por exemplo: .
+
+
+
+De posse dessa informação, que o ID é **36**, podemos criar os eventos.
+
+## Criando os eventos de Notificações Personalizadas
+
+O endpoint `/api/public/alert/event/createEvent` permite criar o evento de notificação. Então voltemos ao Console
+da Ferramenta do Desenvolvedor, no Fluig com usuário autenticado, e basta colar o seguinte código:
+
+```javascript
+fetch("/api/public/alert/event/createEvent", {
+ method: "POST",
+ redirect: "follow",
+ headers: {
+ "Content-Type": "application/json;charset=utf-8",
+ },
+ body: JSON.stringify({
+ eventKey: "FROTA_CNH_VENCIDA", // Este Código é o que usaremos para disparar a notificação
+ required: true,
+ descriptionKey: "Frota: CNH Vencida",
+ singleDescriptionKey: "A CNH está vencida.",
+ groupDescriptionKey: "A CNH está vencida.",
+ eventIcon: "/globalalertapi/resources/images/exclamation-sign.png",
+ moduleId: 36, // Este é o ID do módulo que criamos
+ grouped: false,
+ canRemove: false,
+ removeAfterExecAction: true,
+ onlyAdmin: false,
+ }),
+});
+```
+
+Podemos conferir no painel de controle que agora aparece o módulo e evento criado. Aproveito para desmarcar o envio por e-mail,
+pois é justamente um dos motivos pelo qual fiz a notificação personalizada. Caso deixe marcado o usuário também receberá um e-mail.
+
+
+
+## Disparando a Notificação
+
+Para disparar a notificação é necessário que o código seja executado no back-end do Fluig. Isso pode ser feito em algum evento
+ou criar um dataset para isso.
+
+O código para disparar é bem simples. Vou demonstrar em um dataset.
+
+```javascript
+function createDataset(fields, constraints, sorts) {
+ var dataset = DatasetBuilder.newDataset();
+ dataset.addColumn("executado");
+
+ var alertService = fluigAPI.getAlertService();
+
+ var objeto = new com.totvs.technology.foundation.alert.GenericAlertObject(
+ -1,
+ "FROTA_CNH_VENCIDA",
+ "O motorista XPTO foi notificado por e-mail que está com a CNH Vencida!",
+ null,
+ null,
+ null
+ );
+
+ alertService.sendNotification(
+ "FROTA_CNH_VENCIDA",
+ null,
+ "userCode de quem vai receber a notificação",
+ objeto,
+ null,
+ null,
+ null
+ );
+
+ return dataset;
+}
+
+```
+
+E agora podemos ver a notificação disparada.
+
+
+
+## Considerações Finais
+
+A possibilidade de enviar notificações personalizadas auxilia muito em várias atividades, mas tenha cuidado para não
+poluir demais os usuários com notificações e e-mails personalizados ao mesmo tempo.
+
+Para quem estiver curioso de como utilizei na prática pode conferir esse [Gist](https://gist.github.com/brunogasparetto/a56010eaf4bf43e3d29b523ddca9ca0c#exemplo-pr%C3%A1tico). Nele eu demonstro o meu Dataset jornalizado que busca as CNHs inativas, dispara os
+e-mails personalizados e notifica os gestores.
diff --git a/assets/img/2024-06-01-criando-notificacoes-personalizadas-no-fluig/evento_criado.jpg b/assets/img/2024-06-01-criando-notificacoes-personalizadas-no-fluig/evento_criado.jpg
new file mode 100644
index 0000000..ef83a91
Binary files /dev/null and b/assets/img/2024-06-01-criando-notificacoes-personalizadas-no-fluig/evento_criado.jpg differ
diff --git a/assets/img/2024-06-01-criando-notificacoes-personalizadas-no-fluig/modulo_criado.jpg b/assets/img/2024-06-01-criando-notificacoes-personalizadas-no-fluig/modulo_criado.jpg
new file mode 100644
index 0000000..8d1f457
Binary files /dev/null and b/assets/img/2024-06-01-criando-notificacoes-personalizadas-no-fluig/modulo_criado.jpg differ
diff --git a/assets/img/2024-06-01-criando-notificacoes-personalizadas-no-fluig/modulos_eventos_padrao.jpg b/assets/img/2024-06-01-criando-notificacoes-personalizadas-no-fluig/modulos_eventos_padrao.jpg
new file mode 100644
index 0000000..babcb2f
Binary files /dev/null and b/assets/img/2024-06-01-criando-notificacoes-personalizadas-no-fluig/modulos_eventos_padrao.jpg differ
diff --git a/assets/img/2024-06-01-criando-notificacoes-personalizadas-no-fluig/notificacao_disparada.jpg b/assets/img/2024-06-01-criando-notificacoes-personalizadas-no-fluig/notificacao_disparada.jpg
new file mode 100644
index 0000000..b12468f
Binary files /dev/null and b/assets/img/2024-06-01-criando-notificacoes-personalizadas-no-fluig/notificacao_disparada.jpg differ