Caso de uso del glosario

Objetivo

Queremos proporcionar una herramienta para administrar una lista de términos y sus definiciones:

../_images/glossary-1.png

Cada vez que uno de estos términos aparezca en una página de nuestro sitio, será envuelto en una etiqueta <abbr>, cuyo título será la definición, por lo que cuando pasamos un término, obtenemos un pequeña mensaje emergente que indica su definición:

../_images/glossary-2.png

Estructura de la aplicación

rapido/
    glossary/
        blocks/
            all.html
            all.py
            all.yaml
            term.html
            term.py
            term.yaml
        glossary.js
rules.xml

El archivo rules.xml

<after css:theme-children="body">
  <script src="/tutorial/++theme++test/rapido/glossary/glossary.js"></script>
</after>

Esta regla inserta en todas nuestras páginas un archivo javascript a cargo de reemplazar las palabras coincidentes con etiquetas <abbr>.

El bloque term

Este bloque es un formulario que permite crear/editar/eliminar un término del glosario. Contiene dos elementos de campo y tres acciones.

  • term.html

    <p><label>Term</label> {term}</p>
    <p><label>Definition</label> {definition}</p>
    {_save} {_delete} {close}
    
  • term.yaml

    target: ajax
    elements:
        term: TEXT
        definition: TEXT
        close:
            type: ACTION
            label: Close
        _save:
            type: ACTION
            label: Save
        _delete:
            type: ACTION
            label: Delete
    
  • term.py

    def close(context):
        return context.app.get_block('all').url
    
    def on_save(context):
        return context.app.get_block('all').url
    
    def on_delete(context):
        return context.app.get_block('all').url
    

Si hacemos clic en cualquier acción en este bloque, queremos ser redirigidos a la página de administración principal. Lo hacemos devolviendo la URL de all bloques (cuando una acción devuelve una cadena, se utiliza como una dirección URL de redirección).

El bloque all

Este bloque lista todos los términos existentes en una tabla. Cuando hacemos clic en un término, lo abrimos en el bloque term en modo de edición, y un botón permite abrir un bloque term en blanco para crear un nuevo término.

  • all.html

    <table class="listing"><tr><th>Term</th><th>Definition</th></tr>
    {list}
    </table>
    {new_term}
    
  • all.yaml

    target: ajax
    view:
        id: glossary
        with_theme: true
    elements:
        list: BASIC
        new_term:
            type: ACTION
            label: Add term
    

    La configuración view permite renderizar el bloque all como la vista de Plone llamada @@glossary, por lo que podemos llamar a http://localhost:8080/Plone/@@glossary para verlo.

  • all.py

    def list(context):
        html = u""
        for record in context.app.records():
            html += """<tr><td><a href="%s/edit" target="ajax">%s</a></td><td>%s</td></tr>""" % (
                record.url,
                record['term'],
                record['definition'],
            )
        return html
    
    def new_term(context):
        return context.app.get_block('term').url
    

La función list crea una fila de tabla para cada registro existente, mostrando el valor del término y el valor de la definición. El enlace que ponemos en el término se dirige a la URL de registro (más /edit para abrirlo en modo de edición) y hemos añadido target=»ajax» por lo que la página resultante no se muestra como una página completa, se acaba de cargar en la actual bloque en modo AJAX.

El Javascript

  • glossary.js

    require(['jquery'], function($) {
        if($('.template-edit').length > 0) {
            return
        }
        $.getJSON('/tutorial/@@rapido/glossary/records', function(data) {
            var keys = [];
            var values = {};
            for(var i=0; i<data.length; i++) {
                term = data[i].items.term;
                definition = data[i].items.definition;
                keys.push(term);
                values[term] = definition;
            }
            var re = RegExp("(\\W)(" + keys.join("|") + ")(\\W)", "g");
            function replaceNodeText() {
             if (this.nodeType === 3) {
                    var parent = $(this).parent();
                    var html = parent.html();
                    if(html) {
                        var newvalue = html.replace(re, function(){
                            var term = arguments[2],
                                before = arguments[1],
                                after = arguments[3];
                            term = '<abbr title="'+values[term]+'">'+term+'</abbr>';
                            return before + term + after;
                        });
                        parent.html(newvalue);
                    }
             } else {
                 $(this).contents().each(replaceNodeText);
             }
            }
            $("#content-core").contents().each(replaceNodeText);
        });
    });
    

Lo primero que hacemos es comprobar si estamos en modo de edición, y si lo estamos, nos detendremos, ya que no queremos manipular el HTML que está siendo editado en TinyMCE o en cualquier campo de entrada.

Luego cargamos los términos del glosario con la siguiente llamada JSON: /tutorial/@@rapido/glossary/records

Usando el término valores que hemos cargado, construimos una expresión regular capaz de emparejar esos términos en cualquier texto.

Luego iteramos en los elementos principales del contenido de la página (#content-core), y cada vez que encontramos un nodo de texto, usamos nuestra expresión regular para reemplazar las palabras coincidentes con una etiqueta <abbr> donde el atributo title es la definición asociada.