SISTEMA USADO: Koha 25.05.05 || Fevereiro de 2026.Tudo (ou quase tudo) no Koha pode ser modificado. E o guia de hoje pretende criar um sistema de alteração do Modal de citações. Parece confuso? Calma que vamos explicar.
A primeira coisa que eu fiz foi pesquisar por “Machado de Assis”, isto, porque me renderia um sem fim de opções de títulos.

E a seguir vou clicar no exemplar de “O alienista” para checarmos seus detalhes.
Estes detalhes são padrão e vão mostrar o koha assim:

Neste recorte existem incongruências de catalogação, este não é o foco de nossa busca então peço a gentileza de relevarem essa questão. O que vamos nos focar aqui é em personalizar aquele pequeno menu lateral direto onde lemos “CITAR”

Clicando aqui, um pop-up se abre. (como eu já tinha realizado a alteração, a imagem a seguir será de um recorte de outra obra, mas a visualização deve ser como a que aparece para você no Koha 25.05.05

Como você nota, e se seu usuário precisar citar no Brasil? E se a pessoa em questão tiver dúvidas? (Quem nunca sofreu com a ABNT que atire a primeira pedra!). Nosso papel como bibliotecários (as) é também ajudar nessa questão. É facilitar essa busca. E se o koha pudesse mostrar pra nós a ABNT? Nosso objetivo aqui é justamente essa automatização.
Vamos customizar o modal de citações bibliográficas no OPAC do Koha para exibir formatadas corretamente segundo as normas:
- ABNT NBR 6023:2018 (padrão brasileiro).
- APA 7ª edição.
- Chicago 17ª edição.
- Harvard.
- MLA 9ª edição.
O modal de citações original do Koha apresentava a formatação incorreta para todas as normas, ausência do local de publicação, títulos com capitalização errada, subtítulos não separados corretamente e ausência de elementos essenciais da ABNT.
Exemplo de problema:
❌ Itice Y., . (2024). Do Contra: Herança. Barueri, SP: Panini Brasil.
Mas o que precisávamos que aparecesse?
✅ YOSHI, Itice. Do contra: herança. Barueri: Panini Brasil, 2024.
Do que vamos precisar?
- Acesso à parte administrativa do Koha.
- Permissão de Administrador ou acesso à Preferências do Sistema com todas as permissões necessárias.
- Um navegador (!)
- Conexão estável com a internet (!)
Tenha a prática de backup. Faça backup do conteúdo atual. Vá em Ferramentas > Personalização HTML > OpacUserJs
Copie tudo que tiver ali. Salve num bloco de notas. GUARDE. É seu backup. Se você não souber o que está fazendo, não altere rs. (inclusive fica aqui a minha dica de boa prática de nome para um arquivo de backup no formato backup-opacuserjs-AAAA-MM-DD.txt
Repita o processo para OPACUserCSS e salve como backup-opacusercss-AAAA-MM-DD.txt
Vamos editar o JavaScript que é como faremos nossa mágica. Cole em OpacUserJs.
$(document).ready(function(){
// Interceptar quando o modal de citações for aberto
$(document).on('show.bs.modal', '#citeModal', function (e) {
setTimeout(function() {
substituirCitacoes();
}, 200);
});
function substituirCitacoes() {
var dados = {
autor: extrairAutor(),
titulo: extrairTitulo(),
subtitulo: extrairSubtitulo(),
edicao: extrairEdicao(),
local: extrairLocal(),
editora: extrairEditora(),
ano: extrairAno(),
tradutor: extrairTradutor(),
isbn: extrairISBN(),
paginas: extrairPaginas()
};
console.log('Dados extraídos:', dados);
var htmlCitacoes = `
<div class="citation-block abnt">
<div class="citation-header">
<h4>ABNT <span class="citation-badge">NBR 6023:2018</span></h4>
<button class="btn-copy-citation" data-citation="abnt">
<i class="fa fa-copy"></i> Copiar
</button>
</div>
<p class="citation-text" id="citation-abnt">${gerarABNT(dados)}</p>
</div>
<div class="citation-block">
<div class="citation-header">
<h4>APA <span class="citation-badge">7ª edição</span></h4>
<button class="btn-copy-citation" data-citation="apa">
<i class="fa fa-copy"></i> Copiar
</button>
</div>
<p class="citation-text" id="citation-apa">${gerarAPA(dados)}</p>
</div>
<div class="citation-block">
<div class="citation-header">
<h4>Chicago <span class="citation-badge">17ª edição</span></h4>
<button class="btn-copy-citation" data-citation="chicago">
<i class="fa fa-copy"></i> Copiar
</button>
</div>
<p class="citation-text" id="citation-chicago">${gerarChicago(dados)}</p>
</div>
<div class="citation-block">
<div class="citation-header">
<h4>Harvard</h4>
<button class="btn-copy-citation" data-citation="harvard">
<i class="fa fa-copy"></i> Copiar
</button>
</div>
<p class="citation-text" id="citation-harvard">${gerarHarvard(dados)}</p>
</div>
<div class="citation-block">
<div class="citation-header">
<h4>MLA <span class="citation-badge">9ª edição</span></h4>
<button class="btn-copy-citation" data-citation="mla">
<i class="fa fa-copy"></i> Copiar
</button>
</div>
<p class="citation-text" id="citation-mla">${gerarMLA(dados)}</p>
</div>
`;
$('#citeModal .modal-body').html(htmlCitacoes);
$('.btn-copy-citation').on('click', function() {
var tipo = $(this).data('citation');
var textoElemento = $('#citation-' + tipo);
var texto = textoElemento.text();
navigator.clipboard.writeText(texto).then(function() {
var botao = $('[data-citation="' + tipo + '"]');
var textoOriginal = botao.html();
botao.addClass('copied').html('<i class="fa fa-check"></i> Copiado!');
setTimeout(function() {
botao.removeClass('copied').html(textoOriginal);
}, 2000);
}).catch(function(err) {
console.error('Erro ao copiar:', err);
alert('Erro ao copiar citação');
});
});
}
// ====================================
// FUNÇÕES AUXILIARES DE LIMPEZA
// ====================================
function limparAutor(autor) {
autor = autor.replace(/\d{4}-\d{4}/g, '').trim();
autor = autor.replace(/\([^)]+\)/g, '').trim();
autor = autor.replace(/\[AUTOR\],?\s*/i, '').trim();
autor = autor.replace(/\s+/g, ' ').trim();
autor = autor.replace(/,\s*$/, '');
return autor;
}
function limparTitulo(titulo) {
titulo = titulo.replace(/\[[^\]]+\]/g, '').trim();
if (titulo.includes(';')) {
titulo = titulo.split(';')[0].trim();
}
return titulo;
}
function capitalizarNomeProprio(texto) {
return texto.split(' ').map(palavra => {
if (['de', 'da', 'do', 'das', 'dos', 'e'].includes(palavra.toLowerCase())) {
return palavra.toLowerCase();
}
return palavra.charAt(0).toUpperCase() + palavra.slice(1).toLowerCase();
}).join(' ');
}
// ====================================
// FUNÇÕES DE EXTRAÇÃO DE DADOS
// ====================================
function extrairAutor() {
var autor = $('.author a').first().text().trim();
if (!autor) {
autor = $('span[property="author"]').text().trim();
}
if (!autor) {
autor = $('h5.author').text().replace('Por:', '').trim();
}
autor = limparAutor(autor);
return autor || 'Autor não identificado';
}
function extrairTitulo() {
var tituloCompleto = $('h1.title').first().text().trim();
if (!tituloCompleto) {
tituloCompleto = $('#catalogue_detail_biblio h1').first().text().trim();
}
tituloCompleto = limparTitulo(tituloCompleto);
var titulo = tituloCompleto;
if (tituloCompleto.includes(':')) {
titulo = tituloCompleto.split(':')[0].trim();
}
titulo = titulo.replace(/\s+(graphic|msp|dvd|cd|audiobook).*$/i, '').trim();
return titulo;
}
function extrairSubtitulo() {
var tituloCompleto = $('h1.title').first().text().trim();
tituloCompleto = limparTitulo(tituloCompleto);
if (tituloCompleto.includes(':')) {
var subtitulo = tituloCompleto.split(':').slice(1).join(':').trim();
subtitulo = subtitulo.replace(/\s+(graphic|msp|dvd|cd|audiobook).*$/i, '').trim();
return subtitulo;
}
return '';
}
function extrairTradutor() {
var detalhes = $('#catalogue_detail_biblio').text();
var match = detalhes.match(/tradução[^:]*:\s*([^;]+)/i);
if (match) {
var tradutor = match[1].trim();
tradutor = tradutor.replace(/para\s+[^:]+:\s*/i, '');
return tradutor;
}
var statement = $('span.results_summary.author').text();
match = statement.match(/tradução[^:]*:\s*([^;]+)/i);
if (match) {
return match[1].trim();
}
return '';
}
function extrairEdicao() {
var edicaoTexto = $('span.results_summary.edition').text();
if (!edicaoTexto) {
edicaoTexto = $('#catalogue_detail_biblio').text();
}
var match = edicaoTexto.match(/Edição:\s*(\d+)/i);
if (match) {
return match[1];
}
match = edicaoTexto.match(/(\d+)ª\s*ed/i);
if (match) {
return match[1];
}
return '';
}
function extrairLocal() {
var detalhes = $('span.results_summary.publisher').text();
if (!detalhes) {
detalhes = $('.results_summary:contains("Detalhes da publicação")').text();
}
var localLink = $('a[href*="q=pl:"]').first().text().trim();
if (localLink) {
localLink = localLink.replace(/,\s*(SP|RJ|MG|RS|PR|SC|BA|PE|CE|PA|AM|GO|DF|MT|MS|RO|AC|RR|AP|TO|MA|PI|RN|PB|AL|SE|ES).*$/i, '').trim();
return localLink;
}
var match = detalhes.match(/\[([^\]]+),\s*[A-Z]{2}\]/);
if (match) {
return match[1];
}
return 'Local não identificado';
}
function extrairEditora() {
var editoraLink = $('a[href*="Provider:"]').first().text().trim();
if (editoraLink) {
editoraLink = editoraLink.replace(/^Editora\s+/i, '');
return editoraLink;
}
var detalhes = $('span.results_summary.publisher').text();
var match = detalhes.match(/\]\s*([^\[]+)\s*\[/);
if (match) {
return match[1].trim();
}
return 'Editora não identificada';
}
function extrairAno() {
var anoLink = $('a[href*="copydate:"]').first().text().trim();
if (anoLink) {
return anoLink;
}
var detalhes = $('span.results_summary.publisher').text();
var match = detalhes.match(/\[(\d{4})\]/);
if (match) {
return match[1];
}
return 's.d.';
}
function extrairISBN() {
var isbn = $('span.results_summary.isbn').text().trim();
var match = isbn.match(/\d+[- ]?\d+[- ]?\d+[- ]?\d+/);
return match ? match[0] : '';
}
function extrairPaginas() {
var detalhes = $('span.results_summary.extent, span.results_summary.physical').text();
var match = detalhes.match(/(\d+)\s*(p\.|páginas|pages)/i);
return match ? match[1] : '';
}
// ====================================
// FUNÇÕES DE FORMATAÇÃO
// ====================================
function gerarABNT(d) {
var autor = formatarAutorABNT(d.autor);
var titulo = d.titulo.toLowerCase();
titulo = titulo.charAt(0).toUpperCase() + titulo.slice(1);
var citacao = autor + '. ';
citacao += '<strong>' + titulo;
if (d.subtitulo) {
var subtitulo = d.subtitulo.toLowerCase();
citacao += ': ' + subtitulo;
}
citacao += '</strong>. ';
// Adicionar tradutor se houver
if (d.tradutor) {
var tradutorFormatado = capitalizarNomeProprio(d.tradutor);
citacao += 'Tradução: ' + tradutorFormatado + '. ';
}
if (d.edicao && d.edicao !== '1') {
citacao += d.edicao + '. ed. ';
}
citacao += d.local + ': ' + d.editora + ', ' + d.ano + '.';
if (d.paginas) {
citacao += ' ' + d.paginas + ' p.';
}
return citacao;
}
function formatarAutorABNT(autor) {
if (!autor || autor === 'Autor não identificado') {
return 'AUTOR NÃO IDENTIFICADO';
}
autor = autor.replace(/,+/g, '').trim();
var partes = autor.split(/\s+/);
if (partes.length === 1) {
return partes[0].toUpperCase();
}
var sobrenome = partes[partes.length - 1].toUpperCase();
var nomes = partes.slice(0, -1).join(' ');
return sobrenome + ', ' + nomes;
}
function gerarAPA(d) {
var autorLimpo = d.autor.replace(/,+/g, '').trim();
var partes = autorLimpo.split(/\s+/);
var sobrenome = partes[partes.length - 1];
var iniciais = partes.slice(0, -1).map(n => n.charAt(0).toUpperCase() + '.').join(' ');
var citacao = sobrenome + ', ' + iniciais + ' (' + d.ano + '). ';
citacao += '<em>' + d.titulo.charAt(0).toUpperCase() + d.titulo.slice(1).toLowerCase();
if (d.subtitulo) {
citacao += ': ' + d.subtitulo.charAt(0).toUpperCase() + d.subtitulo.slice(1).toLowerCase();
}
citacao += '</em>';
if (d.edicao && d.edicao !== '1') {
citacao += ' (' + d.edicao + 'ª ed.)';
}
citacao += '. ' + d.editora + '.';
return citacao;
}
function gerarChicago(d) {
var autorLimpo = d.autor.replace(/,+/g, '').trim();
var partes = autorLimpo.split(/\s+/);
var sobrenome = partes[partes.length - 1];
var nomes = partes.slice(0, -1).join(' ');
var citacao = sobrenome + ', ' + nomes + '. ';
citacao += '<em>' + d.titulo.charAt(0).toUpperCase() + d.titulo.slice(1).toLowerCase();
if (d.subtitulo) {
citacao += ': ' + d.subtitulo.charAt(0).toUpperCase() + d.subtitulo.slice(1).toLowerCase();
}
citacao += '</em>. ';
if (d.edicao && d.edicao !== '1') {
citacao += d.edicao + 'ª ed. ';
}
citacao += d.local + ': ' + d.editora + ', ' + d.ano + '.';
return citacao;
}
function gerarHarvard(d) {
var autorLimpo = d.autor.replace(/,+/g, '').trim();
var partes = autorLimpo.split(/\s+/);
var sobrenome = partes[partes.length - 1];
var iniciais = partes.slice(0, -1).map(n => n.charAt(0).toUpperCase()).join('');
var citacao = sobrenome + ', ' + iniciais + ' (' + d.ano + ') ';
citacao += '<em>' + d.titulo.charAt(0).toUpperCase() + d.titulo.slice(1).toLowerCase();
if (d.subtitulo) {
citacao += ': ' + d.subtitulo.charAt(0).toUpperCase() + d.subtitulo.slice(1).toLowerCase();
}
citacao += '</em>';
if (d.edicao && d.edicao !== '1') {
citacao += ', ' + d.edicao + 'ª edn';
}
citacao += '. ' + d.local + ': ' + d.editora + '.';
return citacao;
}
function gerarMLA(d) {
var autorLimpo = d.autor.replace(/,+/g, '').trim();
var partes = autorLimpo.split(/\s+/);
var sobrenome = partes[partes.length - 1];
var nomes = partes.slice(0, -1).join(' ');
var citacao = sobrenome + ', ' + nomes + '. ';
citacao += '<em>' + d.titulo.charAt(0).toUpperCase() + d.titulo.slice(1).toLowerCase();
if (d.subtitulo) {
citacao += ': ' + d.subtitulo.charAt(0).toUpperCase() + d.subtitulo.slice(1).toLowerCase();
}
citacao += '</em>. ';
if (d.edicao && d.edicao !== '1') {
citacao += d.edicao + 'ª ed., ';
}
citacao += d.editora + ', ' + d.ano + '.';
return citacao;
}
});Eu não vou explicar linha por linha o que nosso código faz, mas de maneira mais suscita pra você entender a lógica. No bloco acima você está estruturando como você quer que a citação seja formatada e o que você quer que o código interprete como cada parte do item (título, sobrenome do autor, edição,etc). Como se cada parte fosse um item marc. E aqui nós formatamos porque a ABNT é só esse “como” apresentar a informação.
Então aqui ele vai ficar “seco”. Se você parar por aqui, já terá o código funcional. Mas se quiser deixar com cores melhores, visualmente mais agradável, então precisaremos de uma regra de css para melhorar isso. Vou deixar como fizemos a nossa no bloco a seguir. Cole no OpacUserCSS.
/*ABNT */
/* Modal de citações - Melhorias visuais */
#citeModal .modal-body {
padding: 20px 25px;
max-height: 500px;
overflow-y: auto;
}
/* Cada bloco de citação */
#citeModal .citation-block {
margin-bottom: 20px;
padding-bottom: 18px;
border-bottom: 1px solid #e8e8e8;
}
#citeModal .citation-block:last-child {
border-bottom: none;
margin-bottom: 0;
}
/* Cabeçalho da citação */
#citeModal .citation-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
#citeModal .modal-body h4 {
color: #2c3e50;
font-size: 15px;
font-weight: 600;
margin: 0;
padding: 0;
border: none;
}
/* Texto da citação */
#citeModal .citation-text {
font-size: 14px;
line-height: 1.7;
color: #444;
background: #f8f9fa;
padding: 12px 15px;
border-radius: 6px;
border-left: 3px solid #007bff;
margin: 0;
}
/* Botão copiar */
.btn-copy-citation {
background: #007bff;
color: white;
border: none;
padding: 4px 12px;
font-size: 12px;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
font-weight: 500;
}
.btn-copy-citation:hover {
background: #0056b3;
transform: translateY(-1px);
}
.btn-copy-citation:active {
transform: translateY(0);
}
.btn-copy-citation.copied {
background: #28a745;
}
/* Destaque para ABNT */
#citeModal .citation-block.abnt .citation-text {
border-left-color: #28a745;
background: #f0f8f4;
}
#citeModal .citation-block.abnt h4 {
color: #28a745;
}
/* Badge com nome da norma */
.citation-badge {
display: inline-block;
background: #007bff;
color: white;
padding: 2px 8px;
border-radius: 3px;
font-size: 11px;
font-weight: 600;
margin-left: 8px;
text-transform: uppercase;
}
.citation-block.abnt .citation-badge {
background: #28a745;
}
/* Scrollbar customizada */
#citeModal .modal-body::-webkit-scrollbar {
width: 8px;
}
#citeModal .modal-body::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
#citeModal .modal-body::-webkit-scrollbar-thumb {
background: #888;
border-radius: 10px;
}
#citeModal .modal-body::-webkit-scrollbar-thumb:hover {
background: #555;
}
/* Responsivo */
@media (max-width: 576px) {
#citeModal .citation-text {
font-size: 13px;
padding: 10px 12px;
}
#citeModal .citation-header {
flex-direction: column;
align-items: flex-start;
}
.btn-copy-citation {
margin-top: 8px;
}
}
Deve fornecer uma alteração visual assim:


Boa sorte por aí.