• R/O
  • SSH
  • HTTPS

mantisbtmonitor: 提交


Commit MetaInfo

修訂92 (tree)
時間2022-09-10 05:18:17
作者derekwildstar

Log Message

Atualização do plugin do MantisBT Monitor

Change Summary

差異

--- trunk/plugins/MantisBTMonitor/pages/js/mbtmTinyMCE.php (nonexistent)
+++ trunk/plugins/MantisBTMonitor/pages/js/mbtmTinyMCE.php (revision 92)
@@ -0,0 +1,215 @@
1+<?php
2+header("Content-Type: application/javascript; charset=utf-8");
3+$scriptName = $_GET["sn"]?:"";
4+?>
5+/**
6+* Desabilita os tags não editáveis, removendo deles o atributo "class"
7+* responsável por torná-los não editáveis no TinyMCE. Isso é necessário porque o
8+* mantis não exibe corretamente os elementos que contém atribtutos, então,
9+* salvar elementos com um atributo class definido é ruim
10+*/
11+function disableNonEditable(aContent) {
12+ if (aContent == "") return aContent;
13+
14+ let html = (new DOMParser()).parseFromString(aContent,"text/html");
15+ let nodes = null;
16+
17+<?php
18+// Obtém a instância do plugin atualmente em execução. No caso, só pode ser o
19+// Plugin do MantisBT Monitor, se não for, alguém está tentando fazer alguma
20+// merda. A função plugin_get pode receber de parâmetro o nome base do plugin,
21+// que pode ser obtido com plugin_get_current(). Caso algo aconteça, use esta
22+// abordagem
23+$mbtmp = plugin_get();
24+
25+$nonEditableTags = explode(",",$mbtmp->clearTagsList(plugin_config_get("specialnoneditabletags")));
26+
27+foreach($nonEditableTags as $tag) {
28+ echo <<<EOS
29+ nodes = html.getElementsByTagName("$tag");
30+ for (let node of nodes) {
31+ node.removeAttribute("class");
32+ }
33+
34+
35+EOS;
36+}
37+?>
38+ return html.body.innerHTML;
39+}
40+
41+/**
42+* Habilita os tags não editáveis, adicionando a eles o atributo "class" com o
43+* valor "mceNonEditable"
44+*/
45+function enableNonEditable(aContent) {
46+ if (aContent == "") return aContent;
47+
48+ let html = (new DOMParser()).parseFromString(aContent,"text/html");
49+ let nodes = null;
50+
51+<?php
52+$nonEditableTags = explode(",",$mbtmp->clearTagsList(plugin_config_get("specialnoneditabletags")));
53+
54+foreach($nonEditableTags as $tag) {
55+ echo <<<EOS
56+ nodes = html.getElementsByTagName("$tag");
57+ for (let node of nodes) {
58+ node.classList.add("mceNonEditable");
59+ }
60+
61+
62+EOS;
63+}
64+?>
65+ return html.body.innerHTML;
66+}
67+
68+function isPastingAttachmentReference(aPastedText) {
69+ const regex = /<atta class="mceNonEditable"><atna>(.*)<\/atna><atnn>(.*)<\/atnn>/i;
70+ return regex.exec(aPastedText) != null;
71+}
72+<?php
73+// ATENÇÃO: Não use o plugin paste, pois ele altera a forma como o texto é
74+// colado no editor, inviabilizando o uso do evento onPaste para
75+// colocar código-fonte externo!
76+if ($scriptName == "mbtmTinyMCEEditor") {
77+ $toolBar1 = plugin_config_get("tinymcealternatetoolbar1");
78+ $toolBar2 = plugin_config_get("tinymcealternatetoolbar2");
79+} else {
80+ $toolBar1 = plugin_config_get("tinymcetoolbar1");
81+ $toolBar2 = plugin_config_get("tinymcetoolbar2");
82+}
83+
84+$customElements = $mbtmp->getTinyMCECustomElements();
85+$extendedValidElements = $mbtmp->getTinyMCEExtendedValidElements();
86+?>
87+
88+/*******************************************************************************
89+Se você chegou aqui porque está tentando resolver algum bug relacionado ao
90+funcionamento do TinyMCE, saiba que este componente não é bugado. A forma como
91+ele está configurado o faz funcionar muito fora do padrão e é isso que o faz ter
92+certos comportamentos bizarros. Ele precisou ser configurado desta forma para se
93+adequar ao MantisBT, este sim, cheio de bizarrices
94+*******************************************************************************/
95+
96+tinyMCE.init({selector: "textarea"
97+ ,height: 300
98+ ,entity_encoding: "numeric"
99+ ,plugins: "legacyoutput visualblocks lists insertdatetime charmap hr preview fullscreen code searchreplace noneditable tjpe"
100+ ,menubar: false
101+ ,toolbar1: "<?=$toolBar1;?>"
102+ ,toolbar2: "<?=$toolBar2;?>"
103+ ,browser_spellcheck: true
104+ ,contextmenu: false
105+ ,insertdatetime_formats: ["%H:%M:%S", "%d/%m/%Y", "%d/%m/%Y %H:%M:%S", "%d/%m/%Y às %H:%M:%S", "%Y-%m-%d", "%Y-%m-%dT%H:%M:%S"]
106+ ,content_css: ["/mantis/css/default.css"
107+ ,"/mantis/css/common_config.php"
108+ ,"/mantis/css/status_config.php"
109+ ,"/mantis/css/dropzone-4.3.0.min.css"
110+ ,"/mantis/css/bootstrap-3.3.6.min.css"
111+ ,"/mantis/css/font-awesome-4.6.3.min.css"
112+ ,"/mantis/css/open-sans.css"
113+ ,"/mantis/css/bootstrap-datetimepicker-4.17.43.min.css"
114+ ,"/mantis/css/ace.min.css"
115+ ,"/mantis/css/ace-mantis.css"
116+ ,"/mantis/css/ace-skins.min.css"
117+ ,"<?=plugin_file("css/MantisBTMonitor.css");?>"]
118+ ,invalid_elements: "div"
119+ // Elementos que não existem no HTML5, mas precisam ser aceitos
120+ // pelo TinyMCE. Os nomes de elementos são separados por vírgulas.
121+ // Qualquer elemento que não esteja defindo no HTML5 e não esteja
122+ // nesta lista será removido do TinyMCE automaticamente e sem
123+ // aviso. Por padrão elementos são tratados pelo TinyMCE como
124+ // elementos de bloco. Para definir um elemento "inline", coloque
125+ // um til (~) antes do nome do elemento. Ao aparecer nesta lista,
126+ // um elemento já pode ser manipulado no TinyMCE, não sendo
127+ // necessário adicioná-lo novamente em extended_valid_elements,
128+ // contudo, como estas listas são geradas automaicamente pelo
129+ // plugin do MantisBT Monitor, você deve estar vendo elementos
130+ // listdos tanto aqui como em extended_valid_elements sem qualquer
131+ // especificação de atributo (apenas o nome do atributo). Isso não
132+ // causará problemas
133+ ,custom_elements: "<?=$customElements;?>"
134+ // O atributo valid_elements por padrão contém um conjunto de
135+ // elementos (e seus atributos) que são aceitos pelo TinyMCE. Este
136+ // atributo não deve ser alterado normalmente. Ao invés disso,
137+ // usa-se o atributo extended_valid_elements como forma de
138+ // sobrescrever configurações de atributos para elementos
139+ // específicos ou definir (adicionar) configurações de atributos
140+ // para estes elementos. Aqui, estamos apenas definindo as
141+ // configurações dos elementos que foram definidos em
142+ // custom_elements. Tags customizados listados aqui também não
143+ // serão preenchidos com &#160;
144+ ,extended_valid_elements: "<?=$extendedValidElements;?>"
145+ ,valid_children: ""
146+ // A configuração abaixo foi removida porque o Mantis não gosta de
147+ // tags auto-fechantes (<tag/>). Outra forma de manter os elementos
148+ // que são auto-fechantes é usando o exemplo deste fiddle
149+ // https://fiddle.tiny.cloud/ehhaab
150+ // ,protect: [/\<(teap|tere|hoap|hore) *\/\>/g] // isso é usado para manter os tags sem qualquer alteração pelo TinyMCE
151+ ,block_formats: "Parágrafo simples=p;Texto preformatado=pre;Código \"inline\"=code;Cabeçalho de primeiro nível=h1;Cabeçalho de segundo nível=h2;Cabeçalho de terceiro nível=h3"
152+ ,setup: function(aEditor) {
153+ // Pós processamento executado apenas quando o texto está sendo
154+ // confirmado, isto é, passado do editor para o <textarea>
155+ aEditor.on("GetContent",function(aEventData) {
156+ if (aEventData.format == "html" && aEventData.hasOwnProperty("save") && aEventData.save) {
157+ if (aEventData.content.trim() != "") {
158+ aEventData.content = "<mbtm></mbtm>" + aEventData.content;
159+ }
160+
161+ aEventData.content = disableNonEditable(aEventData.content);
162+ }
163+ });
164+ // Pré processamento executado quando o texto está sendo
165+ // carregado no editor, isto é, passado do <textarea> para o
166+ // editor
167+ aEditor.on("BeforeSetContent",function(aEventData){
168+ aEventData.content = aEventData.content.replaceAll("<mbtm></mbtm>","");
169+ aEventData.content = enableNonEditable(aEventData.content);
170+ });
171+ // Verifica se estamos tentando colar no editor algum tag
172+ // especial. Neste caso, não há a colagem padrão, mas sim um
173+ // inserção ou substituição do conteúdo do editor. Este código
174+ // só funciona quando o plugin Paste do TinyMCE não está
175+ // ativado. Este código só atua em colagens de código-fonte no
176+ // editor do TinyMCE. Quando, por exemplo, um tag especial é
177+ // copiado e colado de e para o próprio editor, a expressão
178+ // regular que detecta o conteúdo sendo colado não vai enxergar
179+ // o código-fonte do tag especial, sendo realizada a colagem
180+ // original, que já lida com o tag especial
181+ aEditor.on("paste",function(aEventData) {
182+ let pastedText = (aEventData.clipboardData || window.clipboardData).getData("text/plain");
183+
184+ if (isPastingAttachmentReference(pastedText)) {
185+ aEventData.preventDefault();
186+ aEditor.execCommand("mceInsertContent",false,pastedText);
187+ }
188+ });
189+ }
190+ });
191+
192+/*******************************************************************************
193+O Dropzone interfere na forma como o TinyMCE serializa o texto do editor.
194+Normalmente o TinyMCE serializa o texto do editor no momento em que o usuário
195+executa o submit do <form>, porém em forms que contém o Dropzone, quando se
196+adiciona algum arquivo a ser anexado, o envio efetivo dos dados ocorre ANTES que
197+o TinyMCE serialize seu texto no <textarea>, fazendo com que o texto não seja
198+enviado ao servidor. As linhas abaixo forçam a serialização do conteúdo do
199+TinyMCE. Abaixo, primeiramente obtém-se a instância do objeto dropzone
200+diretamente a partir do form que foi usado durante sua instanciação e em seguida
201+se aplica a solução, a qual foi obtida em:
202+https://stackoverflow.com/questions/41481758/include-textarea-tynimce-with-dropzone
203+*******************************************************************************/
204+
205+window.onload = function() {
206+ let dropZone = $("div.dropzone").length > 0?$("div.dropzone").closest("form").length > 0?$("div.dropzone").closest("form")[0].dropzone != undefined?$("div.dropzone").closest("form")[0].dropzone:null:null:null;
207+
208+ if (dropZone != null) {
209+ dropZone.on("sending", function(file, xhr, formData) {
210+ for (let i = 0;i < tinyMCE.editors.length;i++) {
211+ formData.append(tinyMCE.editors[i].targetElm.name, tinyMCE.editors[i].save());
212+ }
213+ });
214+ }
215+}
\ No newline at end of file
--- trunk/plugins/MantisBTMonitor/pages/mbtmAbout.php (revision 91)
+++ trunk/plugins/MantisBTMonitor/pages/mbtmAbout.php (revision 92)
@@ -11,8 +11,8 @@
1111 </div>
1212 <div class="space-2"></div>
1313 <ul class="nav nav-tabs padding-18">
14+ <li><a href="<?=plugin_page("mbtmConfig");?>"><i class="blue ace-icon fa fa-gears"></i> <?=lang_get("plugin_mbtm_configtabtitle"); ?></a></li>
1415 <li class="active"><a><i class="blue ace-icon fa fa-info-circle"></i> <?=lang_get("plugin_mbtm_abouttabtitle"); ?></a></li>
15- <li><a href="<?=plugin_page("mbtmConfig");?>"><i class="blue ace-icon fa fa-gears"></i> <?=lang_get("plugin_mbtm_configtabtitle"); ?></a></li>
1616 </ul>
1717 <div class="widget-body">
1818 <div class="widget-main no-padding">
--- trunk/plugins/MantisBTMonitor/pages/mbtmBasePage.php (revision 91)
+++ trunk/plugins/MantisBTMonitor/pages/mbtmBasePage.php (revision 92)
@@ -16,6 +16,7 @@
1616 <?php
1717 event_signal( 'EVENT_LAYOUT_BODY_BEGIN' );
1818 echo($bodyContents);
19+layout_body_javascript();
1920 html_body_end();
2021 ?>
2122 </html>
\ No newline at end of file
--- trunk/plugins/MantisBTMonitor/pages/mbtmConfig.php (revision 91)
+++ trunk/plugins/MantisBTMonitor/pages/mbtmConfig.php (revision 92)
@@ -6,6 +6,13 @@
66 $tinymcemode = plugin_config_get("tinymcemode");
77 $tinymceenabledfor = plugin_config_get("tinymceenabledfor");
88 $tinymcedisabledfor = plugin_config_get("tinymcedisabledfor");
9+$tinymcetoolbar1 = plugin_config_get("tinymcetoolbar1");
10+$tinymcetoolbar2 = plugin_config_get("tinymcetoolbar2");
11+$tinymcealternatetoolbar1 = plugin_config_get("tinymcealternatetoolbar1");
12+$tinymcealternatetoolbar2 = plugin_config_get("tinymcealternatetoolbar2");
13+$additionaltags = plugin_config_get("additionaltags");
14+$specialtags = plugin_config_get("specialtags");
15+$specialnoneditabletags = plugin_config_get("specialnoneditabletags");
916 ?>
1017 <div class="col-md-12 col-xs-12">
1118 <div class="space-10"></div>
@@ -15,8 +22,8 @@
1522 </div>
1623 <div class="space-2"></div>
1724 <ul class="nav nav-tabs padding-18">
25+ <li class="active"><a><i class="blue ace-icon fa fa-gears"></i> <?=lang_get("plugin_mbtm_configtabtitle"); ?></a></li>
1826 <li><a href="<?=plugin_page("mbtmAbout");?>"><i class="blue ace-icon fa fa-info-circle"></i> <?=lang_get("plugin_mbtm_abouttabtitle"); ?></a></li>
19- <li class="active"><a><i class="blue ace-icon fa fa-gears"></i> <?=lang_get("plugin_mbtm_configtabtitle"); ?></a></li>
2027 </ul>
2128 <div class="form-container">
2229 <form id="formatting-config-form" action="<?=plugin_page("mbtmConfigSave");?>" method="post">
@@ -26,6 +33,11 @@
2633 <div class="table-responsive">
2734 <table class="table table-bordered table-condensed table-striped">
2835 <tr>
36+ <td colspan="2" class="widget-header bold center">
37+ Configurações relacionadas ao TinyMCE
38+ </td>
39+ </tr>
40+ <tr>
2941 <th class="category width-30">
3042 Modo de uso do TinyMCE<br />
3143 <span class="small">Informe como o TinyMCE será integrado ao MantisBT</span>
@@ -67,6 +79,74 @@
6779 <input name="tinymcedisabledfor" id="tinymcedisabledfor" class="form-control" value="<?=string_attribute($tinymcedisabledfor); ?>" />
6880 </td>
6981 </tr>
82+ <tr>
83+ <th class="category width-30">
84+ Primeira barra de ferramentas do TinyMCE<br />
85+ <span class="small">Estrutura da primeira barra de ferramentas do TinyMCE</span>
86+ </th>
87+ <td>
88+ <input name="tinymcetoolbar1" id="tinymcetoolbar1" class="form-control" value="<?=string_attribute($tinymcetoolbar1); ?>" />
89+ </td>
90+ </tr>
91+ <tr>
92+ <th class="category width-30">
93+ Segunda barra de ferramentas do TinyMCE<br />
94+ <span class="small">Estrutura da segunda barra de ferramentas do TinyMCE</span>
95+ </th>
96+ <td>
97+ <input name="tinymcetoolbar2" id="tinymcetoolbar2" class="form-control" value="<?=string_attribute($tinymcetoolbar2); ?>" />
98+ </td>
99+ </tr>
100+ <tr>
101+ <th class="category width-30">
102+ Primeira barra de ferramentas alternativa do TinyMCE<br />
103+ <span class="small">Estrutura da primeira barra de ferramentas alternativa do TinyMCE</span>
104+ </th>
105+ <td>
106+ <input name="tinymcealternatetoolbar1" id="tinymcealternatetoolbar1" class="form-control" value="<?=string_attribute($tinymcealternatetoolbar1); ?>" />
107+ </td>
108+ </tr>
109+ <tr>
110+ <th class="category width-30">
111+ Segunda barra de ferramentas alternativa do TinyMCE<br />
112+ <span class="small">Estrutura da segunda barra de ferramentas alternativa do TinyMCE</span>
113+ </th>
114+ <td>
115+ <input name="tinymcealternatetoolbar2" id="tinymcealternatetoolbar2" class="form-control" value="<?=string_attribute($tinymcealternatetoolbar2); ?>" />
116+ </td>
117+ </tr>
118+ <tr>
119+ <td colspan="2" class="widget-header bold center">
120+ Tags que precisam ser aceitos pelo MantisBT
121+ </td>
122+ </tr>
123+ <tr>
124+ <th class="category width-30">
125+ Tags adicionais<br />
126+ <span class="small">Tags adicionais aceitos pelo MantisBT</span>
127+ </th>
128+ <td>
129+ <input name="additionaltags" id="additionaltags" class="form-control" value="<?=string_attribute($additionaltags); ?>" />
130+ </td>
131+ </tr>
132+ <tr>
133+ <th class="category width-30">
134+ Tags especiais<br />
135+ <span class="small">Tags especiais aceitos pelo MantisBT e requeridos pelo MantisBT Monitor</span>
136+ </th>
137+ <td>
138+ <input name="specialtags" id="specialtags" class="form-control" value="<?=string_attribute($specialtags); ?>" />
139+ </td>
140+ </tr>
141+ <tr>
142+ <th class="category width-30">
143+ Tags especiais não editáveis<br />
144+ <span class="small">Tags especiais que não podem ser editados via TinyMCE</span>
145+ </th>
146+ <td>
147+ <input name="specialnoneditabletags" id="specialnoneditabletags" class="form-control" value="<?=string_attribute($specialnoneditabletags); ?>" />
148+ </td>
149+ </tr>
70150 </table>
71151 </div>
72152 </div>
--- trunk/plugins/MantisBTMonitor/pages/mbtmConfigSave.php (revision 91)
+++ trunk/plugins/MantisBTMonitor/pages/mbtmConfigSave.php (revision 92)
@@ -18,7 +18,7 @@
1818 foreach ($defaultConfigs as $key => $defaultValue) {
1919 $value = gpc_get_string($key);
2020 // Remove espaços em campos específicos
21- if ($key == "tinymceenabledfor" || $key == "tinymcedisabledfor") {
21+ if ($key == "tinymceenabledfor" || $key == "tinymcedisabledfor" || $key == "additionaltags" || $key == "specialtags" || $key == "specialnoneditabletags") {
2222 $value = str_replace(" ","",$value);
2323 }
2424
@@ -31,6 +31,6 @@
3131
3232 layout_page_header("Configurações do Plugin do MantisBT Monitor",$mbtmConfigPage);
3333 layout_page_begin();
34-html_operation_successful($mbtmConfigPage,"As configurações do Plugin do MantisBT Monitor foram salvas com sucesso! Aguarde ser redirecionado ou clique o botão abaixo para voltar a tela de configurações do plugin imediatamente");
34+html_operation_successful($mbtmConfigPage,"<center>As configurações do Plugin do MantisBT Monitor foram salvas com sucesso! Aguarde ser redirecionado ou clique o botão abaixo para voltar a tela de configurações do plugin imediatamente</center>");
3535 layout_page_end();
3636 ?>
--- trunk/plugins/MantisBTMonitor/pages/mbtmTinyMCEEditor.php (revision 91)
+++ trunk/plugins/MantisBTMonitor/pages/mbtmTinyMCEEditor.php (revision 92)
@@ -43,6 +43,7 @@
4343 }
4444 /* ]]> */
4545 </script>
46+
4647 EOS;
4748
4849 require(__DIR__ . "/mbtmBasePage.php");
--- trunk/plugins/MantisBTMonitor/MantisBTMonitor.php (revision 91)
+++ trunk/plugins/MantisBTMonitor/MantisBTMonitor.php (revision 92)
@@ -1,5 +1,16 @@
11 <?php
22 class MantisBTMonitorPlugin extends MantisPlugin {
3+ const DEFAULT_TINYMCE_MODE = "EP";
4+ const DEFAULT_TINYMCE_ENABLEDFOR = "cbff";
5+ const DEFAULT_TINYMCE_DISABLEDFOR = "";
6+ const DEFAULT_TINYMCE_TOOLBAR1 = "fullscreen preview searchreplace | undo redo | selectall | bold italic underline strikethrough subscript superscript removeformat | blockquote h1 h2 h3 formatselect";
7+ const DEFAULT_TINYMCE_TOOLBAR2 = "visualblocks insertdatetime charmap hr | bullist numlist | code | tjpeaard";
8+ const DEFAULT_TINYMCE_ALTERNATIVETOOLBAR1 = "searchreplace | undo redo | selectall | bold italic underline strikethrough subscript superscript removeformat | blockquote h1 h2 h3 formatselect";
9+ const DEFAULT_TINYMCE_ALTERNATIVETOOLBAR2 = "visualblocks insertdatetime charmap hr | bullist numlist | tjpeaard";
10+ const DEFAULT_ADDITIONAL_TAGS = "hr,sup,sub,strike,blockquote,code,h1,h2,h3";
11+ const DEFAULT_SPECIAL_TAGS = "~vers,~stat,~fwte,~fwho,~atna,~atnn,reit[class],num[class]";
12+ const DEFAULT_SPECIAL_NONEDITABLE_TAGS = "~atta[class],teap[class],tere[class],hoap[class],hore[class],uds1[class],uds2[class],uds3[class],uds4[class],uds5[class],unts[class],une1[class],une2[class],une3[class],une4[class],une5[class],ungc[class]";
13+
314 // Código de registro (obrigatório!)
415 function register() {
516 $this->name = lang_get("plugin_mbtm_name");
@@ -6,7 +17,7 @@
617 $this->description = lang_get("plugin_mbtm_description");
718 $this->version = "1.0.0";
819 $this->requires = array("MantisCore" => "2.5.0");
9- $this->page = "mbtmAbout";
20+ $this->page = "mbtmConfig";
1021 $this->author = "Carlos Barreto Feitoza Filho";
1122 $this->contact = "carlos.feitoza@tjpe.jus.br";
1223 $this->url = "http://www.tjpe.jus.br";
@@ -14,16 +25,23 @@
1425
1526 // Configurações que este plugin possui e seus valores padrão
1627 function config() {
17- return array("tinymcemode" => "EP"
18- ,"tinymceenabledfor" => "cbff"
19- ,"tinymcedisabledfor" => "");
28+ return array("tinymcemode" => $this::DEFAULT_TINYMCE_MODE
29+ ,"tinymceenabledfor" => $this::DEFAULT_TINYMCE_ENABLEDFOR
30+ ,"tinymcedisabledfor" => $this::DEFAULT_TINYMCE_DISABLEDFOR
31+ ,"tinymcetoolbar1" => $this::DEFAULT_TINYMCE_TOOLBAR1
32+ ,"tinymcetoolbar2" => $this::DEFAULT_TINYMCE_TOOLBAR2
33+ ,"tinymcealternatetoolbar1" => $this::DEFAULT_TINYMCE_ALTERNATIVETOOLBAR1
34+ ,"tinymcealternatetoolbar2" => $this::DEFAULT_TINYMCE_ALTERNATIVETOOLBAR2
35+ ,"additionaltags" => $this::DEFAULT_ADDITIONAL_TAGS
36+ ,"specialtags" => $this::DEFAULT_SPECIAL_TAGS
37+ ,"specialnoneditabletags" => $this::DEFAULT_SPECIAL_NONEDITABLE_TAGS);
2038 }
2139
2240 // Esta função é chamada toda vez que o plugin for inicializado. Aqui é um
2341 // bom lugar para sobrescrever variáveis globais
2442 function init() {
25- config_set_global("html_valid_tags",config_get_global("html_valid_tags") . ", hr, sup, sub, strike, blockquote, code, h1, h2, h3");
26- config_set_global("html_valid_tags",config_get_global("html_valid_tags") . ", mbtm, teap, tere, hoap, hore, vers, stat, fwte, fwho, atta, atna, atnn, uds1, uds2, uds3, uds4, uds5, unts, une1, une2, une3, une4, une5, ungc, stt, sth, reit, num");
43+ config_set_global("html_valid_tags",config_get_global("html_valid_tags") . "," . plugin_config_get("additionaltags"));
44+ config_set_global("html_valid_tags",config_get_global("html_valid_tags") . ",mbtm," . $this->clearTagsList(plugin_config_get("specialtags")) . "," . $this->clearTagsList(plugin_config_get("specialnoneditabletags")));
2745 }
2846
2947 // Eventos que este plugin manipula podem ser definidos aqui
@@ -86,373 +104,25 @@
86104 function onBeforeHeadEnd($aEvent,$aParameters) {
87105 echo("\t<!-- MBTMP -->\n");
88106 html_css_link(plugin_file("css/MantisBTMonitor.css"));
89-
90- $scriptName = basename($_SERVER["SCRIPT_NAME"], ".php");
91-
92- if ($scriptName == "plugin") {
93- $scriptName = basename($_GET["page"]);
94- }
95-
107+
96108 if ($this->isTinyMCEAllowed()) {
109+ $scriptName = basename($_SERVER["SCRIPT_NAME"], ".php");
110+
111+ if ($scriptName == "plugin") {
112+ $scriptName = basename($_GET["page"]);
113+ }
114+
97115 // O TinyMCE e tudo que ele depende, só será adicionada às páginas
98116 // específicadas
99117 if (in_array($scriptName,["bugnote_edit_page"
100- ,"view","bug_update_page"
118+ ,"view"
119+ ,"bug_update_page"
101120 ,"bug_report_page"
102121 ,"mbtmTinyMCEEditor"],true)) {
122+
103123 echo("\t<script src=\"" . plugin_file("js/tinymce/tinymce.min.js") . "\" referrerpolicy=\"origin\"></script>\n");
104-
105- // ATENÇÃO: Não use o plugin paste, pois ele altera a forma como o texto
106- // é colado no editor, inviabilizando o uso do evento onPaste
107- // para colocar código-fonte externo!
108- $toolBar1 = "fullscreen preview searchreplace | undo redo | selectall | bold italic underline strikethrough subscript superscript removeformat | blockquote h1 h2 h3 formatselect";
109- $toolBar2 = "visualblocks insertdatetime charmap hr | bullist numlist | code | tjpeaard";
110-
111- if ($scriptName == "MantisBTMonitorTinyMCEEditor") {
112- $toolBar1 = "searchreplace | undo redo | selectall | bold italic underline strikethrough subscript superscript removeformat | blockquote h1 h2 h3 formatselect";
113- $toolBar2 = "visualblocks insertdatetime charmap hr | bullist numlist | tjpeaard";
114- }
115- ?>
116- <script>
117- /* <![CDATA[ */
118-/*
119-* Se você chegou aqui porque está tentando resolver algum bug relacionado ao
120-* funcionamento do TinyMCE, saiba que este componente não é bugado. A forma como
121-* ele está configurado o faz funcionar muito fora do padrão e é isso que o faz
122-* ter certos comportamentos bizarros. Ele precisou ser configurado desta forma
123-* para se adequar ao mantis.
124-*/
125-
126-function disableNonEditable(aContent) {
127- if (aContent == "") return aContent;
128- // Aqui eu preciso remover o atributo "class" de todos os elementos que
129- // não podem ser editados diretamente no TinyMCE. Isso é necessário porque o
130- // mantis não exibe corretamente os elementos que contém atribtutos, então,
131- // salvar elementos com um atributo class definido é ruim
132- let html = (new DOMParser()).parseFromString(aContent,"text/html");
133-
134- // Removendo o atributo class dos nós de referência a anexos (<atta>)
135- let nodes = html.getElementsByTagName("atta");
136- for (let atta of nodes) {
137- atta.removeAttribute("class");
138- }
139-
140- // Removendo o atributo class dos nós de aprovação em testes (<teap>)
141- nodes = html.getElementsByTagName("teap");
142- for (let teap of nodes) {
143- teap.removeAttribute("class");
144- }
145-
146- // Removendo o atributo class dos nós de rejeição em testes (<tere>)
147- nodes = html.getElementsByTagName("tere");
148- for (let tere of nodes) {
149- tere.removeAttribute("class");
150- }
151-
152- // Removendo o atributo class dos nós de aprovação em homologação (<hoap>)
153- nodes = html.getElementsByTagName("hoap");
154- for (let hoap of nodes) {
155- hoap.removeAttribute("class");
156- }
157-
158- // Removendo o atributo class dos nós de rejeição em homologação (<hore>)
159- nodes = html.getElementsByTagName("hore");
160- for (let hore of nodes) {
161- hore.removeAttribute("class");
162- }
163-
164- // Daqui pra frente você já entendeu, não vou comentar mais...
165-
166- nodes = html.getElementsByTagName("uds1");
167- for (let uds1 of nodes) {
168- uds1.removeAttribute("class");
169- }
170-
171- nodes = html.getElementsByTagName("uds2");
172- for (let uds2 of nodes) {
173- uds2.removeAttribute("class");
174- }
175-
176- nodes = html.getElementsByTagName("uds3");
177- for (let uds3 of nodes) {
178- uds3.removeAttribute("class");
179- }
180-
181- nodes = html.getElementsByTagName("uds4");
182- for (let uds4 of nodes) {
183- uds4.removeAttribute("class");
184- }
185-
186- nodes = html.getElementsByTagName("uds5");
187- for (let uds5 of nodes) {
188- uds5.removeAttribute("class");
189- }
190-
191- nodes = html.getElementsByTagName("unts");
192- for (let unts of nodes) {
193- unts.removeAttribute("class");
194- }
195-
196- nodes = html.getElementsByTagName("une1");
197- for (let une1 of nodes) {
198- une1.removeAttribute("class");
199- }
200-
201- nodes = html.getElementsByTagName("une2");
202- for (let une2 of nodes) {
203- une2.removeAttribute("class");
204- }
205-
206- nodes = html.getElementsByTagName("une3");
207- for (let une3 of nodes) {
208- une3.removeAttribute("class");
209- }
210-
211- nodes = html.getElementsByTagName("une4");
212- for (let une4 of nodes) {
213- une4.removeAttribute("class");
214- }
215-
216- nodes = html.getElementsByTagName("une5");
217- for (let une5 of nodes) {
218- une5.removeAttribute("class");
219- }
220-
221- nodes = html.getElementsByTagName("ungc");
222- for (let ungc of nodes) {
223- ungc.removeAttribute("class");
224- }
225-
226- nodes = html.getElementsByTagName("stt");
227- for (let stt of nodes) {
228- stt.removeAttribute("class");
229- }
230-
231- nodes = html.getElementsByTagName("sth");
232- for (let sth of nodes) {
233- sth.removeAttribute("class");
234- }
235-
236- return html.body.innerHTML;
237-}
238-
239-function enableNonEditable(aContent) {
240- if (aContent == "") return aContent;
241- // Aqui eu preciso adicionar o atributo "class" em todos os elementos que
242- // não podem ser editados diretamente no TinyMCE. A classe a ser aplicada é
243- // "mceNonEditable"
244- let html = (new DOMParser()).parseFromString(aContent,"text/html");
245-
246- // Adicionando o atributo class nos nós de referência a anexos (<atta>)
247- let nodes = html.getElementsByTagName("atta");
248- for (let atta of nodes) {
249- atta.classList.add("mceNonEditable");
250- }
251-
252- // Adicionando o atributo class dos nós de aprovação em testes (<teap>)
253- nodes = html.getElementsByTagName("teap");
254- for (let teap of nodes) {
255- teap.classList.add("mceNonEditable");
256- }
257-
258- // Adicionando o atributo class dos nós de rejeição em testes (<tere>)
259- nodes = html.getElementsByTagName("tere");
260- for (let tere of nodes) {
261- tere.classList.add("mceNonEditable");
262- }
263-
264- // Adicionando o atributo class dos nós de aprovação em homologação (<hoap>)
265- nodes = html.getElementsByTagName("hoap");
266- for (let hoap of nodes) {
267- hoap.classList.add("mceNonEditable");
268- }
269-
270- // Adicionando o atributo class dos nós de rejeição em homologação (<hore>)
271- nodes = html.getElementsByTagName("hore");
272- for (let hore of nodes) {
273- hore.classList.add("mceNonEditable");
274- }
275-
276- // Daqui pra frente você já entendeu, não vou comentar mais...
277-
278- nodes = html.getElementsByTagName("uds1");
279- for (let uds1 of nodes) {
280- uds1.classList.add("mceNonEditable");
281- }
282-
283- nodes = html.getElementsByTagName("uds2");
284- for (let uds2 of nodes) {
285- uds2.classList.add("mceNonEditable");
286- }
287-
288- nodes = html.getElementsByTagName("uds3");
289- for (let uds3 of nodes) {
290- uds3.classList.add("mceNonEditable");
291- }
292-
293- nodes = html.getElementsByTagName("uds4");
294- for (let uds4 of nodes) {
295- uds4.classList.add("mceNonEditable");
296- }
297-
298- nodes = html.getElementsByTagName("uds5");
299- for (let uds5 of nodes) {
300- uds5.classList.add("mceNonEditable");
301- }
302-
303- nodes = html.getElementsByTagName("unts");
304- for (let unts of nodes) {
305- unts.classList.add("mceNonEditable");
306- }
307-
308- nodes = html.getElementsByTagName("une1");
309- for (let une1 of nodes) {
310- une1.classList.add("mceNonEditable");
311- }
312-
313- nodes = html.getElementsByTagName("une2");
314- for (let une2 of nodes) {
315- une2.classList.add("mceNonEditable");
316- }
317-
318- nodes = html.getElementsByTagName("une3");
319- for (let une3 of nodes) {
320- une3.classList.add("mceNonEditable");
321- }
322-
323- nodes = html.getElementsByTagName("une4");
324- for (let une4 of nodes) {
325- une4.classList.add("mceNonEditable");
326- }
327-
328- nodes = html.getElementsByTagName("une5");
329- for (let une5 of nodes) {
330- une5.classList.add("mceNonEditable");
331- }
332-
333- nodes = html.getElementsByTagName("ungc");
334- for (let ungc of nodes) {
335- ungc.classList.add("mceNonEditable");
336- }
337-
338- nodes = html.getElementsByTagName("stt");
339- for (let stt of nodes) {
340- stt.classList.add("mceNonEditable");
341- }
342-
343- nodes = html.getElementsByTagName("sth");
344- for (let sth of nodes) {
345- sth.classList.add("mceNonEditable");
346- }
347-
348- return html.body.innerHTML;
349-}
350-
351-function isPastingAttachmentReference(aPastedText) {
352- const regex = /<atta class="mceNonEditable"><atna>(.*)<\/atna><atnn>(.*)<\/atnn>/i;
353- return regex.exec(aPastedText) != null;
354-}
355-
356-tinyMCE.init({selector: "textarea"
357- ,height: 300
358- ,entity_encoding: "numeric"
359- ,plugins: "legacyoutput visualblocks lists insertdatetime charmap hr preview fullscreen code searchreplace noneditable tjpe"
360- ,menubar: false
361- ,toolbar1: "<?=$toolBar1;?>"
362- ,toolbar2: "<?=$toolBar2;?>"
363- ,browser_spellcheck: true
364- ,contextmenu: false
365- ,insertdatetime_formats: ["%H:%M:%S", "%d/%m/%Y", "%d/%m/%Y %H:%M:%S", "%d/%m/%Y às %H:%M:%S", "%Y-%m-%d", "%Y-%m-%dT%H:%M:%S"]
366- ,content_css: ["/mantis/css/default.css"
367- ,"/mantis/css/common_config.php"
368- ,"/mantis/css/status_config.php"
369- ,"/mantis/css/dropzone-4.3.0.min.css"
370- ,"/mantis/css/bootstrap-3.3.6.min.css"
371- ,"/mantis/css/font-awesome-4.6.3.min.css"
372- ,"/mantis/css/open-sans.css"
373- ,"/mantis/css/bootstrap-datetimepicker-4.17.43.min.css"
374- ,"/mantis/css/ace.min.css"
375- ,"/mantis/css/ace-mantis.css"
376- ,"/mantis/css/ace-skins.min.css"
377- ,"<?=plugin_file("css/MantisBTMonitor.css");?>"]
378- ,invalid_elements: "div"
379- ,extended_valid_elements: "teap[class],tere[class],hoap[class],hore[class],atta[class],atna,atnn,uds1[class],uds2[class],uds3[class],uds4[class],uds5[class],unts[class],une1[class],une2[class],une3[class],une4[class],une5[class],ungc[class],stt[class],sth[class],reit[class],num[class]" // também faz com que os tags customizados não sejam preenchidos com &#160;
380- ,custom_elements: "teap,tere,hoap,hore,~vers,~stat,~fwte,~fwho,~atta,~atna,~atnn,uds1,uds2,uds3,uds4,uds5,unts,une1,une2,une3,une4,une5,ungc,stt,sth,reit,num"
381- ,valid_children: ""
382- // A configuração abaixo foi removida porque o Mantis não gosta de
383- // tags auto-fechantes (<tag/>). Outra forma de manter os elementos
384- // que são auto-fechantes é usando o exemplo deste fiddle
385- // https://fiddle.tiny.cloud/ehhaab
386- // ,protect: [/\<(teap|tere|hoap|hore) *\/\>/g] // isso é usado para manter os tags sem qualquer alteração pelo TinyMCE
387- ,block_formats: "Parágrafo simples=p;Texto preformatado=pre;Código \"inline\"=code;Cabeçalho de primeiro nível=h1;Cabeçalho de segundo nível=h2;Cabeçalho de terceiro nível=h3"
388- ,setup: function(aEditor) {
389- // Pós processamento executado apenas quando o texto está sendo
390- // confirmado, isto é, passado do editor para o <textarea>
391- aEditor.on("GetContent",function(aEventData) {
392- if (aEventData.format == "html" && aEventData.hasOwnProperty("save") && aEventData.save) {
393- if (aEventData.content.trim() != "") {
394- aEventData.content = "<mbtm></mbtm>" + aEventData.content;
395- }
396-
397- aEventData.content = disableNonEditable(aEventData.content);
398- }
399- });
400- // Pré processamento executado quando o texto está sendo
401- // carregado no editor, isto é, passado do <textarea> para o
402- // editor
403- aEditor.on("BeforeSetContent",function(aEventData){
404- aEventData.content = aEventData.content.replaceAll("<mbtm></mbtm>","");
405- aEventData.content = enableNonEditable(aEventData.content);
406- });
407- // Verifica se estamos tentando colar no editor algum tag
408- // especial. Neste caso, não há a colagem padrão, mas sim um
409- // inserção ou substituição do conteúdo do editor. Este código
410- // só funciona quando o plugin Paste do TinyMCE não está
411- // ativado. Este código só atua em colagens de código-fonte no
412- // editor do TinyMCE. Quando, por exemplo, um tag especial é
413- // copiado e colado de e para o próprio editor, a expressão
414- // regular que detecta o conteúdo sendo colado não vai enxergar
415- // o código-fonte do tag especial, sendo realizada a colagem
416- // original, que já lida com o tag especial
417- aEditor.on("paste",function(aEventData) {
418- let pastedText = (aEventData.clipboardData || window.clipboardData).getData("text/plain");
419-
420- if (isPastingAttachmentReference(pastedText)) {
421- aEventData.preventDefault();
422- aEditor.execCommand("mceInsertContent",false,pastedText);
423- }
424- });
425- }
426- });
427-
428-/**
429-* O Dropzone interfere na forma como o TinyMCE serializa o texto do editor.
430-* Normalmente o TinyMCE serializa o texto do editor no momento em que o usuário
431-* executa o submit do <form>, porém em forms que contém o Dropzone, quando se
432-* adiciona algum arquivo a ser anexado, o envio efetivo dos dados ocorre ANTES
433-* que o TinyMCE serialize seu texto no <textarea>, fazendo com que o texto não
434-* seja enviado ao servidor. As linhas abaixo forçam a serialização do conteúdo
435-* do TinyMCE. Abaixo, primeiramente obtém-se a instância do objeto dropzone
436-* diretamente a partir do form que foi usado durante sua instanciação e em
437-* seguida se aplica a solução, a qual foi obtida em:
438-* https://stackoverflow.com/questions/41481758/include-textarea-tynimce-with-dropzone
439-*/
440-
441-window.onload = function() {
442- let dropZone = $("div.dropzone").length > 0?$("div.dropzone").closest("form").length > 0?$("div.dropzone").closest("form")[0].dropzone != undefined?$("div.dropzone").closest("form")[0].dropzone:null:null:null;
443-
444- if (dropZone != null) {
445- dropZone.on("sending", function(file, xhr, formData) {
446- for (let i = 0;i < tinyMCE.editors.length;i++) {
447- formData.append(tinyMCE.editors[i].targetElm.name, tinyMCE.editors[i].save());
124+ echo("\t<script src=\"" . plugin_page("js/mbtmTinyMCE&sn=$scriptName") . "\"></script>\n");
448125 }
449- });
450- }
451-}
452- /* ]]> */
453- </script>
454-<?php
455- }
456126 }
457127
458128 echo("\t<!-- MBTMP -->\n");
@@ -505,6 +175,37 @@
505175 break;
506176 }
507177 return $result;
508- }
178+ }
179+
180+ /**
181+ * Limpa a lista de tags informada, removendo da mesma todos os atributos que
182+ * só fazem sentido para o TinyMCE
183+ * @param string $aTagsList String que contém a lista de tags que se pretende
184+ * limpar, removendo da mesma "~" e elementos entre colchetes
185+ * @return string Lista de tags sem qualquer ocorrência de "~" ou elementos
186+ * entre colchetes
187+ */
188+ function clearTagsList($aTagsList) {
189+ // Esta expressão regular pode não ser suficiente para cobrir todas as
190+ // possibilidade de configuração que o TinyMCE aceita, mas cobre o
191+ // básico
192+ return preg_replace('/~?|\[[\w]*\]/s', "", $aTagsList);
193+ }
194+
195+ /**
196+ * Retorna a lista de elementos customizados que pode ser usada no atributo
197+ * "custom_elements" do TinyMCE
198+ */
199+ function getTinyMCECustomElements() {
200+ return preg_replace('/\[[\w]*\]/s', "", plugin_config_get("specialtags") . "," . plugin_config_get("specialnoneditabletags"));
201+ }
202+
203+ /**
204+ * Retorna a lista estendida de elementos válidos que pode ser usada no
205+ * atributo "extended_valid_elements" do TinyMCE
206+ */
207+ function getTinyMCEExtendedValidElements() {
208+ return preg_replace('/~?/s', "", plugin_config_get("specialtags") . "," . plugin_config_get("specialnoneditabletags"));
209+ }
509210 }
510211 ?>
\ No newline at end of file
Show on old repository browser