Tema-Componente v Plugin: ¿Cuál es la diferencia

¿Alguien puede aclarar la diferencia entre estos tres conceptos de Discourse: theme-component, plugin y pluginAPI?

Especialmente entre theme-component y plugin. Si quiero personalizar mi foro, ¿cómo sé cuál debo desarrollar?

(lo siento si pasé por alto esta distinción en la introducción, pero no la veo allí)

No soy un principiante en Discourse, pero tampoco soy un experto.

  1. Un componente de tema utiliza HTML, CSS y JavaScript para mejorar un tema base. Hago énfasis en “tema base” porque normalmente se le llama simplemente “tema” y a veces la gente no distingue la diferencia, por lo que hay que inferirla. Un tema y/o un componente de tema puede ser instalado por un administrador sin necesidad de tomar el sitio fuera de línea, y si eres cliente de Discourse, también puedes agregarlos (lista). Consulta también: Guía para principiantes sobre el uso de temas en Discourse

  2. Un plugin utiliza Ruby y puede hacer prácticamente cualquier cosa posible. Si eres cliente de Discourse, tienes un conjunto limitado de plugins que puedes activar; sin embargo, si estás alojando el sitio tú mismo, puedes agregar todos los que quieras. Ten en cuenta que he visto muchos posts donde plugins personalizados rompen el sitio durante una actualización. Estos tampoco requieren reinicio al activarse; sospecho que un reinicio podría ser necesario al instalarlos por primera vez. Otros pueden ampliar esta información, ya que mi única experiencia con plugins es activarlos desde los menús de administración (lista). Consulta también: Guía para principiantes sobre la creación de plugins para Discourse - Parte 1

  3. No he desarrollado un plugin, así que supongo que te refieres a Discourse API Ruby Gem. Consulta: Use the Discourse API ruby gem

  4. También existe la API, que incluye webhooks y se utiliza típicamente con curl u otro lenguaje de programación. Esto es conveniente porque te libera de tener que usar Ruby.

  5. Aunque tampoco he probado esto, podrías programar a nivel de la base de datos PostgreSQL, pero no lo recomendaría a menos que seas muy hábil y muy seguro de tus capacidades.

Espero que esto ayude.


EDIT

Ronda extra si quieres comprometerte por completo como desarrollador de Discourse

Consulta: Cómo empezar a crear cosas para Discourse si eres principiante (como yo)

Para complementar la respuesta de @EricGT, que ya explica muy bien lo siguiente:

  • Un tema o componente de tema es esencialmente una forma de modificar cualquier parte de la aplicación front-end EmberJS de Discourse. Esto puede ser tan sencillo como personalizar HTML o CSS, o tan complejo como agregar nueva funcionalidad. Los temas son mucho más elegantes en caso de que algo falle, lo que significa que todo tu sitio no necesariamente se caerá si algo no funciona.
  • Un plugin afecta principalmente a la aplicación del servidor Rails, pero también incluye todo el poder de un tema y la capacidad de modificar la aplicación EmberJS, aunque es mucho más complejo. Los fallos de los plugins suelen ser menos elegantes, por lo que si puedes construir algo en un tema, comienza por ahí. Sin embargo, un plugin es necesario si necesitas una ruta personalizada o almacenar datos.
  • La pluginAPI es una API en el lado del cliente que los temas o componentes de tema pueden usar para modificar más fácilmente partes específicas del cliente de Discourse.

El mejor lugar para comenzar a personalizar tu sitio es con un tema. Aquí tienes algunos recursos:

Guía de diseño para temas de Discourse
Guía de desarrollador para temas de Discourse
Guía para principiantes sobre el uso de Theme Creator y Theme CLI para comenzar a crear un tema de Discourse

Gracias, chicos. Esto es útil. Creo que la distinción clave que estoy captando es:
– Si quieres cambiar algo que solo afecta a la interfaz frontal, crea un tema.
– Si quieres cambiar algo que requiere interacción con el backend, crea un plugin.

¿Suena correcto?

Aquí hay un ejemplo concreto que tengo en mente y que estoy tratando de resolver: quiero que cualquier moderador de categoría pueda fijar temas en esa categoría. Las líneas generales, creo, son:

  1. Evaluar si el usuario es moderador de la categoría (esto requiere acceder al backend para obtener información sobre el usuario y la categoría).

  2. Si el usuario es moderador, mostrar el botón de fijar (esto es frontend).

  3. Si el usuario hace clic en el botón de fijar, llevar ese tema a la parte superior (realmente no estoy seguro de dónde ocurre esto en el código de Discourse, ¿quizás también en frontend y backend?).

Aquí, como necesito interactuar con el backend (probablemente en el punto 1, ¿quizás también en el 3?), tendría que usar un plugin. ¿Suena correcto?

La parte superficial podría serlo, pero implicará el backend porque se relaciona con los permisos y la seguridad. No se puede dejar que el frontend decida qué privilegios tiene alguien.

¿Te refieres a que responder programáticamente “¿Es este usuario un moderador de esta categoría” implicará múltiples archivos tanto en el frontend como en el backend? Hmm…

En un tema, deberías poder saber si un usuario es moderador y también hacer llamadas al backend si la API de Discourse expone un punto final que puedas llamar desde el frontend (después de todo, el tema puede usar JavaScript), por lo que probablemente no necesites un plugin para [1]. Solo lo necesitarías si necesitas cambiar el comportamiento del backend o exponer una API.

Pero probablemente sí lo necesites para [3], porque, como dijo @merefield, se relaciona con permisos y seguridad (si el backend bloquea a un moderador para fijar un tema, tendrías que cambiarlo para empezar a permitirlo).

Como dije antes, probablemente no se requiera un plugin solo para hacer esa validación (para saber si el usuario es moderador o no), pero probablemente sí se requiera debido a la acción de permitirle fijar la categoría. Si Discourse tiene una opción para permitir que cualquier moderador fije un tema (no sé si la tiene), entonces no necesitarías un plugin (pero probablemente tampoco necesitarías un tema, a menos que el botón de fijar no se muestre a los moderadores, y uses un tema solo para mostrarlo a los moderadores y llamar al punto final en JavaScript cuando haga clic en el botón de fijar).

Eso es muy útil en cuanto a temas vs. plugins y también respecto al ejemplo específico que mencioné. Gracias.

Hasta ahora, mi enfoque para introducir cambios ha sido revisar los detalles del código de Discourse (¿dónde se define este objeto?, ¿qué controla esta plantilla?, ¿dónde está la lógica que maneja esta acción?, etc.). Pero eso ha sido un proceso lento.

Pienso que probablemente una ruta más eficiente sería centrarse en la API. De esa manera, no necesito revisar todos los detalles del código maduro de Discourse y puedo enfocarme en construir un tema en lugar de un plugin, o quizás simplemente introducir cambios en el panel de “personalización”.

Básicamente, entender cómo funciona la API parece mucho más manejable que entender la base de código de Discourse.

Para seguir con el ejemplo que mencioné: si el usuario es moderador de una categoría, permitir que ese usuario fije temas en la página de la categoría.

¿Podría hacerlo sin un plugin? Déjame ver si puedo esbozarlo:

1. ¿Es un usuario moderador de una categoría?

Actualmente no veo nada en la API que me indique si un usuario es moderador de una categoría. Esperaría encontrarlo en una llamada GET para obtener una categoría, pero no veo tal llamada allí. O quizás en la llamada GET para obtener un usuario, pero allí no veo una lista de categorías de las que el usuario es moderador.

¿Podría agregar estas cosas?

O, alternativamente, quizás podría crear un campo personalizado en el usuario o en la categoría para identificar que es moderador, y luego hacer una llamada a la API a ese campo personalizado cuando se cargue la página de la categoría.

2. Si el usuario es moderador, mostrar el botón de fijar.

Si puedo responder a (1), entonces asumo que puedo simplemente agregar este botón con JavaScript y CSS del lado del cliente, mostrándolo si el usuario es moderador.

3. El usuario (que es moderador) hace clic en el botón y eso fija el tema.

En la API, los temas parecen tener una característica “fijado” (un booleano). Asumo que esto se correlaciona con si están fijados en su categoría, ya que parece que cada tema solo tiene una categoría.

Así que aquí, cuando el moderador hace clic en el botón “fijar”, probablemente podría actualizar el estado “fijado” del tema a Verdadero. Si eso no funciona, los campos personalizados también podrían ser una solución aquí (aunque no veo cómo agregar campos personalizados a un tema).


Con eso, o algo similar, parece que podría lograr esta tarea con la API que, si lo hiciera con un plugin, requeriría mucho tiempo revisando los archivos de la base de código de Discourse.

¿Suena bien?

¿Has encontrado: Cómo realizar ingeniería inversa de la API de Discourse

Lo he visto antes, pero ahora lo estoy revisando con más detalle. Gracias por el recordatorio.

Estoy tratando de averiguar:
–dónde en la API puedo obtener información sobre quién es el moderador de una categoría determinada (no lo veo en la información devuelta sobre categorías o usuarios; obviamente debe estar en algún lugar)

–¿se puede usar la API para agregar nuevos campos a una entrada? Por ejemplo, ¿podría usar la API para agregar un campo personalizado a un tema? (No creo que los temas generalmente incluyan campos personalizados)

Yo lo buscaría en la base de datos.

La base de datos tiene una tabla posts y podrías agregar un campo allí, pero entonces te saldrías del camino y no recibirías soporte.

Gracias, esto es muy útil.

¿A qué base de datos te refieres?


Para confirmar, mi idea aquí sería utilizar la API para realizar algunas de estas tareas que de otro modo requerirían un plugin. Por ejemplo, mi propio sitio realizaría una llamada a la API para determinar si un usuario (o grupo, según corresponda) es moderador de una categoría. La llamada estaría codificada en el panel de personalización, en un tema o en un plugin.

Para realizar este tipo de llamadas, necesitaré autenticación, lo que, según creo, requiere crear una clave de API desde el panel de administración. El proceso de creación de la API en el panel de administración requiere una “Descripción” y un “Nivel de usuario”; no estoy seguro de cómo se aplica esto en este caso. En mi situación, quiero que mi aplicación realice la llamada a la API. No se está realizando en nombre de un usuario específico. Por lo tanto, es posible que esté malinterpretando algo.

¿Sabes qué nivel de usuario es adecuado para ese tipo de llamada a la API o qué debería ingresar allí?

Cuando Discourse se instala según lo establecido en la documentación, se ejecuta en un contenedor de Docker. La persistencia de los datos se logra mediante una base de datos PostgreSQL.

Ver: Complemento Data Explorer

Si tienes derechos de administrador, puedes descargar una de las copias de seguridad; está comprimida en SQL dentro de un archivo tar.gz. Puedes usar el SQL para volver a cargar los datos en otra base de datos PostgreSQL e incluso hacer más.

Nunca he creado un tema, un plugin ni he utilizado la API desde Ruby ni con ningún otro lenguaje de programación. Sí tengo acceso de administrador en un sitio de producción, lo cual es como accedí a la copia de seguridad. También programo en Prolog, que es lo que me permite acceder a los datos y utilizarlos para analizar las publicaciones con DCG. Si conoces BNF, las DCG no son muy diferentes, pero debes entender la unificación sintáctica y la encadenación hacia atrás para las partes más sofisticadas.

Gracias. Procederé con este tema por separado.

No, ya que los derechos de acceso para cada acción son aplicados por el servidor.

Deberás revisar la sobrescritura de las funciones en lib/guardian/ y lib/guardian.rb para permitir que los moderadores específicos de una categoría fijen temas, y luego utilizar los mismos mecanismos que Theme JS (excepto en el plugin) para modificar la interfaz de usuario de modo que la opción “fijar tema” aparezca cuando corresponda.

Ahh, tiene sentido. Parece que usar la API para eso no funcionaría (tu respuesta probablemente me ahorra mucho tiempo).

Podría probar algo ligeramente diferente al comportamiento normal de fijación. En lugar de eso, otorgaría derechos de «propiedad» sobre una categoría a ciertos usuarios, lo que les permitiría resaltar ciertos temas dentro de esa categoría.

La forma en que lo haría con la API JSON sería: desde mi panel de personalización, asignar un campo personalizado a los usuarios relevantes (como category-name: owner) o algo similar. Luego, cuando se cargue la página de la categoría, llamaría a la API para evaluar al usuario; si tiene ese campo personalizado, mostraría el botón «resaltar», y si lo pulsa para un tema, asignaría ese tema al grupo de destacados de la categoría (también un campo personalizado para esa categoría).

Este es un bosquejo general; no hace falta revisar paso a paso para confirmar estos pasos (cuando lo programe, probablemente tenga que ajustarlo de todos modos). Pero mi pregunta por ahora es: ¿puede funcionar este enfoque de uso de la API JSON, especialmente para crear y recuperar campos personalizados desde dentro de mi aplicación de Discourse?

Estimado @JQ331,

Pero mi pregunta por ahora: ¿puede funcionar este modo de usar la API de JSON, especialmente para crear y recuperar campos personalizados desde mi aplicación de Discourse?

Ha habido excelentes respuestas a tus preguntas sobre las diferencias entre un componente de tema, un plugin y la API de Discourse, y dudo que pueda aportar mucho más valor; pero aquí tienes otra forma de verlo, que quizás te resulte útil o no:

Tanto los componentes de tema como los plugins utilizan ganchos de plantilla (ganchos de plugin) para ejecutar código en el ciclo de vida de Ember.js (lo cual es bueno aprender, por cierto).

Además, la API de Discourse también está disponible tanto para componentes de tema como para plugins.

La API expone básicamente un subconjunto, pero no todo, de los datos de la base de datos PostgreSQL subyacente.

Cuando estés desarrollando funcionalidad, sería una buena idea empezar por la API y determinar si los datos que necesitas están disponibles a través de ella.

Si hay algún dato que necesitas y que no está en la API, debes examinar la base de datos PostgreSQL para ver si esos datos existen en la base de datos.

Si los datos adicionales que necesitas existen en la base de datos, entonces debes exponerlos, y en general esto significa añadir datos al serializador de datos de Discourse y extender la API.

El serializador de datos es simplemente el proceso de crear el objeto JSON que expone la API. Esto se puede extender para añadir más objetos.

Para examinar la base de datos PostgreSQL, supongo que hay muchas formas de hacerlo (leyendo el código en GitHub, por ejemplo), pero yo lo hago iniciando sesión directamente en la base de datos, revisando las tablas, estudiando su estructura y viendo qué campos tiene cada tabla (usando SQL básico).

Así que, para resumir (y mantenerlo breve), necesitamos tener tanto la API como las tablas de la base de datos como referencias. En general, cuando quieras “extender la API” añadiendo datos que no están proporcionados por defecto (OOTB), creamos un plugin para ello; y entonces esos datos se expondrán junto con la API (extendida), y podremos usarlos tanto en componentes de tema como en plugins.

Espero que esta perspectiva te haya sido útil de alguna pequeña manera.

Explicación excelente. Muchas gracias por esta respuesta. Destaca algo que no había considerado antes:

De estas respuestas, entiendo que (como mencionas) interactuar con la API JSON puede ser un buen punto de partida en muchos casos, lo que podría evitar la necesidad de desarrollar un nuevo tema o plugin. Sin embargo, hay ciertos tipos de datos que no están expuestos por la API. Para acceder a ellos y trabajar con esos tipos de datos, necesitarías utilizar el serializador de datos de Discourse; y para realizar esa serialización, debes usar un plugin.

Parece que un buen ejemplo de datos no disponibles a través de la API son los propietarios de un grupo. Lo digo porque (en relación con el acceso a los propietarios de grupo):

Un punto de confusión: en la API de Discourse, cuando obtienes un grupo específico, una de las propiedades devueltas se indica como "is_group_owner": true, así que no estoy seguro de qué significa eso…

Pero parece que para obtener al propietario del grupo necesitaría serializar la propiedad del propietario del grupo.


¿Existen buenos ejemplos de uso del serializador de Discourse? He visto esto, pero dada su importancia, una guía paso a paso con algunos ejemplos sería extremadamente útil.

El ejemplo más cercano que tengo es:

Esto es útil, pero no del todo correcto (al menos me da errores que dicen “plugin no válido”). No estoy seguro de cómo ajustarlo para que en la página de índice de grupos pueda acceder a los propietarios de cada grupo.

No estoy seguro de cómo intentaste usar el ejemplo del plugin, pero logré ejecutarlo correctamente en una instancia de desarrollo utilizando la estructura de archivos y el código que publiqué en tu otro tema.

is_group_owner se utiliza en el contexto del usuario actual viendo los grupos.

Puedes obtener la información del propietario mediante una llamada AJAX, pero, hasta donde sé, esto tendría que hacerse para cada grupo individual de la lista, lo que potencialmente resultaría en muchas solicitudes. Creo que, en general, sería un desafío hacer que funcione con este método. En cualquier caso, si quieres experimentar, puedes probar el siguiente fragmento de código de concepto en un tema. Simplemente reemplaza GROUP_NAME con el nombre de uno de tus grupos. (EDITO: Aquí también hay un componente de tema con un ejemplo de cómo se puede utilizar una llamada AJAX: discourse-featured-topics/common/head_tag.html at master · awesomerobot/discourse-featured-topics · GitHub)

<script type="text/discourse-plugin" version="0.8.40">
  const { ajax } = require("discourse/lib/ajax");
  ajax(`/groups/GROUP_NAME/members.json`).then(response => {
    console.log(response.owners.map(owner => owner.username))
  });
</script>

Dicho todo esto, usar un plugin sería definitivamente la solución más sencilla y limpia.