• ¡Bienvenido a XenFácil!

    Estás viendo el sitio como Invitado. Para poder participar en este sitio

    y obtendrás privilegios adicionales, acceso a otras áreas y mucho mas.

    ¡Es gratis!


    ¿Ya eres miembro? Inicia sesión

XF2.1 ¿Qué hay de nuevo para los programadores en XF 2.1?

#1
Cambios en Font Awesome

Debido a un gran numero de cambios en la arquitectura de FA5, en particular, a la capacidad de utilizar diferentes estilos y separar los íconos de marca según su estilo, se ha hecho necesario introducir algunas ayudas en XF 2.1 para llamar a los íconos de la manera más simple posible..

Veremos primero unos cuantos pequeños cambios relativos al uso de los iconos en CSS.

Cambios en el combinado m-faBase()

Ahora viene preparado para manejar los íconos y el estilo que se desee. De forma predeterminada, se mostrará un icono sin marca en el grosor predeterminado, pero hay argumentos nuevos que se pueden pasar.

Less:
 &:before
{
   .m-faBase('Brands');
   .m-faContent(@fa-var-facebook);
}
O, si se desea un icono Pro de un estilo específico:
Less:
&:before
{
   .m-faBase('Pro', @faWeight-solid);
   .m-faContent(@fa-var-bookmark);
}
There are three variables available for FA styles and they are @faWeight-solid, @faWeight-regular and @faWeight-light. These style variables actually correspond to the font weight values 900, 400 and 300 respectively.

Llamar a los iconos con la nueva etiqueta <xf:fa>

Naturalmente, se puede seguir utilizando la denominación anterior (que era muy similar a FA4) aunque para mayor facilidad de uso se utiliza en su lugar la sintaxis de plantillas, especial y generalmente tal y como se querría para un uso correcto del estilo FA5 configurado en el estilo de visitantes.


Por ejemplo, para llamar al icono usuario en el estilo predeterminado, puede utilizarse:
HTML:
<i class="fa{{ fa_weight() }} fa-user"></i>
que ahora aún es más simple de usar:
HTML:
<xf:fa icon="fa-user" />
Si se tiene una razón específica para utilizar un icono con un estilo diferente, también está soportado:
HTML:
<xf:fa icon="fas fa-user" />
Y puede llamarse a los iconos con marca o PRO, algo así como:
HTML:
<xf:fa icon="fab fa-github" />
Accediendo a cualquier icono FA con la etiqueta <xf:button>

Desde hace algún tiempo se viene soportando el atributo icono en la etiquetar button, siendo este enfoque válido y en algunos casos se asignará a una frase predeterminada que utilice ese estilo de. Por ejemplo:
HTML:
 <xf:button icon="save" />
Que genera: save.png

En XF 2.1 esto se ha expandido para soportar cualquier icono FA icon que utilice un nuevo atributo fa. Por ejemplo:
HTML:
<xf:button fa="fa-cloud-upload">Do the thing</xf:button>
lo que produce hacer-algo.png

También puede utilizarse el atributo fa con <xf:submitrow /> para poder adjuntar un icono al botón submit primario.

Agregar contexto extra en las entradas input con iconos

Se recomienda utilizar esto con moderación ya que por regla general no es una convención de la Interfaz de usuario que se utilice mucho en el software, pero ahora es posible mostrar un icono dentro de una entrada en XF 2.1. Por ejemplo:
HTML:
<xf:textboxrow rowtype="fullWidth noLabel" placeholder="Write a comment..." fa="fa-pencil" />
lo que produce:
imput.png

Continúar leyendo...
 
Última edición por un moderador:

lms

Administrador
#2
Nuevo complemento para el controlador XF:Delete

Si alguna vez has creado un complemento que tenga una interfaz de usuario para eliminar registros de la base de datos, puedes estar familiarizado con la tarea bastante repetitiva de escribir el código del controlador y la plantilla para realizar esa acción.

En XF 2.0, ese código no diferirá mucho de este:
PHP:
public function actionDelete(ParameterBag $params)
{
   $notice = $this->assertNoticeExists($params['notice_id']);
   if (!$notice->preDelete())
   {
      return $this->error($notice->getErrors());
   }

   if ($this->isPost())
   {
      $notice->delete();
      return $this->redirect($this->buildLink('notices'));
   }
   else
   {
      $viewParams = [
         'notice' => $notice
      ];
      return $this->view('XF:Notice\Delete', 'notice_delete', $viewParams);
   }
}
Y la plantilla lucirá algo así como:
HTML:
<xf:title>{{ phrase('confirm_action') }}</xf:title>

<xf:form action="{{ link('notices/delete', $notice) }}" ajax="true" class="block">
   <div class="block-container">
      <div class="block-body">
         <xf:inforow rowtype="confirm">
            {{ phrase('please_confirm_that_you_want_to_delete_following:') }}
            <strong><a href="{{ link('notices/edit', $notice) }}">{$notice.title}</a></strong>
         </xf:inforow>
      </div>
      <xf:submitrow rowtype="simple" icon="delete" />
   </div>
</xf:form>
En XF 2.1 se ha introducido el controlador Delete para simplificar esto. No habrá que escribir una plantilla completa. En vez, se puede escribir una acción en tu controlador que utilice un código similar a este:
PHP:
public function actionDelete(ParameterBag $params)
{
   $notice = $this->assertNoticeExists($params->notice_id);

   /** @var \XF\ControllerPlugin\Delete $plugin */
   $plugin = $this->plugin('XF:Delete');
   return $plugin->actionDelete(
      $notice,
      $this->buildLink('notices/delete', $notice),
      $this->buildLink('notices/edit', $notice),
      $this->buildLink('notices'),
      $notice->title
   );
}
Los argumentos pasados al controlador son solamente la entidad que se va a eliminar. el enlace a tu acción del controlador, un enlace de editar/ver relevante para el contenido, la URL para redireccionar después de eliminar y el título del contenido. Existe un sexto argumento opcionalo por el que puede pasarse una plantilla personalizada, si fuera preciso.
 

lms

Administrador
#3
Migración de "Me Gusta" a "Reacciones"

Si actualmente SE tiene un complemento que implementa un controlador de contenido "Me gusta", es posible preguntarse cómo migrar a "Reacciones" en XF 2.1 y qué ocurrirá con esos Me gusta.

Nos hemos asegurado de que continúe funcionando normalmente su complemento después de actualizar a XF 2.1. Esto significa que tu código existente de controlador de usuario, tus plantillas Me gusta de usuario, plantillas de alerta de Me gusta, entidades, controladores y todo seguirá apareciendo y funcionará como lo hace ahora sin que tengas que realizar ningún cambio.

De hecho, si así lo deseas, tu complemento puede continuar utilizando "me gusta" en el futuro. Dicho esto, sería seguro asumir que el sistema Me gusta ahora está en desuso y pueda programarse para su eliminación en algún momento futuro. Por lo tanto, se recomienda encarecidamente adoptar e implementar la nueva funcionalidad de reacciones tan pronto como sea posible.

Pero, la buena noticia es que se ha hecho muy sencillo migrar a las reacciones a través de diferentes ayudantes.

[B]AbstractSetup::migrateTableToReactions($tableName, $likesColumn = 'likes', $likeUsersColumn = 'like_users')[/B]

Llamaría a esto para cada uno de sus tipos de contenido y pasaría el nombre de la tabla donde deben aplicarse los cambios. Esto cambia el nombre de la columna de me gusta a reaction_score, la columna de like_users existente a reaction_users y agrega una nueva columna llamada reacciones (que almacena un contador de las reacciones que se han aplicado al contenido y de cuántas veces).

[B]AbstractSetup::renameLikeAlertOptionsToReactions($contentTypes, $oldAction = 'like')[/B]

Puedes pasar una variedad de tipos de contenido (o un solo tipo de contenido) aquí y esto cambia el nombre de cualquier "exclusión de alerta" al nuevo nombre. También deberás actualizar las frases relacionadas con las opciones de exclusión de alertas.

[B]AbstractSetup::renameLikeAlertsToReactions($contentTypes, $renameNewsFeed = true, $oldAction = 'like')[/B]

Esto cambia el nombre de cualquier alerta "me gusta" existente a una alerta de "reacción" con los datos adicionales necesarios, y si está habilitado también cualquier entrada de noticias. Además de esto, deberás actualizar las plantillas de alerta/noticias. Utiliza una plantilla central como alert_post_reaction y news_feed_item_post_reaction como referencia.

[B]AbstractSetup::renameLikePermissionsToReactions(array $permissionGroupIds, $likePermissionId = 'like')[/B]

Dado un conjunto de IDs de grupo de permisos, esto cambiará el nombre de cualquier permiso existente con un ID de Me gusta (o un ID personalizado) a reacción. La matriz que se pasa debe tener el ID del grupo de permisos existente como su clave y un valor verdadero o falso para indicar si es un permiso específico del contenido o solo global, similar a esto:

PHP:
$this->renameLikePermissionsToReactions([
   'forum' => true, // global and content
   'conversation' => false, // global only
   'profile_post' => false // global only
]);
Característica XF\Entity\ReactionTrait

Tras eliminar la mayoría de las referencias a los "me gusta" de tu entidad de contenido, deberás agregar algunas cosas nuevas de reacción específica. Para hacer esto más fácil, se ha añadido un nuevo ReactionTrait. Esto requerirá que implementes un método canReact (que en su mayoría será el mismo que el método canLike existente).


Plantillas y acciones de controlador

Similar to the existing XF:Like controller plugin, there is a new XF:Reaction controller plugin. These will help you very easily start supporting the user interface for likes. In fact, there are no custom templates to create at all for interacting with reactions as the controller plugin will call default templates for this purpose. You can check out XF\Pub\Controller\Post for examples.

Lo único que se precisa cambiar en las plantillas es el enlace existente y el código de resumen. Incluso para estos, tenemos algunas nuevas etiquetas de plantilla XF como ayudar. Echa un vistazo a la plantilla post_macros para ver un ejemplo.
 
Última edición:

lms

Administrador
#4
Nuevo InstallHelperTrait

Principalmente como información, ya que en realidad no se necesita hacer nada con este nuevo rasgo. Este nuevo rasgo se utiliza en la clase AbstractSetup existente. La razón principal por la que se agrega esto es porque hay varios métodos que se duplicaron en la clase de actualización central de XF y en las clases de configuración adicionales. Métodos como la aplicación de permisos, los métodos de migración de reacción mencionados anteriormente, varios métodos de administrador de esquemas y más están disponibles ahora ahí.
 

lms

Administrador
#5
Envío de notificaciones push

Si se tiene un controlador de alertas en tu complemento, cualquier alerta generada por él se enviará automáticamente como una notificación de inserción a los usuarios suscritos. Técnicamente, no necesitas realizar ningún cambio para admitir esto, pero te recomendamos crear una nueva plantilla específica para estas alertas.

La razón es que las notificaciones push no admiten ningún tipo arbitrario de HTML, por lo que hay que convertirlos a texto sin formato e intentar extraer una URL relevante de ellos (a la que se redirigirá a los usuarios cuando hagan clic en sus dispositivos en la notificación) .

Como ejemplo específico, si se tiene una plantilla de alerta que se envía cuando alguien reacciona a un contenido, por ejemplo. alert_post_reaction entonces recomendaríamos crear una plantilla de inserción equivalente, por ejemplo, push_post_reaction. El formato de la plantilla es esencialmente una versión de solo texto sin formato de la misma plantilla:
HTML:
{{ phrase('x_reacted_to_your_post_in_the_thread_y', {
   'name': $user.username ?: $alert.username,
   'reaction': reaction_title($extra.reaction_id),
   'title': prefix('thread', $content.Thread, 'plain') . $content.Thread.title
}) }}
<push:url>{{ link('canonical:posts', $content) }}</push:url>
Esto ahorra un montón de trabajo en las alertas push en cuanto a no tener que convertir nada a texto sin formato o extraer la URL para usar desde el HTML

Sin embargo, es posible que manejes otros modos de envío de notificaciones automáticas y se ha hecho tan agnóstico como ha sido posible a través de un nuevo Rasgo. Todo lo que se necesita hacer es crear un nuevo servicio que use el nuevo XF\Service\PusherTrait y escribir los métodos necesarios para especificar cómo obtener el título y el cuerpo deseados para la notificación.

Para usar el nuevo servicio, simplemente hay que pasar una entidad de usuario (el receptor) y cualquier propiedad adicional y luego llamar al método push. ¡Y eso es todo!
 

lms

Administrador
#6
Compatibilidad mejorada de Composer para complementos

Composer es una herramienta invalorable que permite instalar y actualizar automáticamente las dependencias requeridas por el código (¡y las dependencias requeridas por las dependencias!) Y crear un autocargador para que este código sea accesible instantáneamente dentro de su propio código.

Aunque técnicamente ya es posible que los complementos incluyan las dependencias de composer, hay una serie de pasos necesarios para colocar ese código en el autocargador de XF.

Lo hemos hecho en su mayoría automático en XF 2.1 gracias a la guía de @Sim (enlace arriba). Todo lo que necesitas hacer es agregar una entrada composer_autoload a tu archivo addon.json que apunte al directorio de composers de tu proveedor y XF registrará automáticamente cualquier cargador automático de composers encontrado en nuestro propio autocargador.
 

lms

Administrador
#7
Contextos de caché

Esto fue algo a lo que se hizo referencia en el anuncio de almacenamiento en caché de la página, pero es algo que también se aplica a los desarrolladores que hacen uso del sistema de almacenamiento en caché en sus complementos.

En 2.0, sólo existe una configuración de caché. Si buscas un manejador de caché, se puede llamar a App::cache() que nos devolverá el proveedor de caché (o nulo si no hay proveedor). En 2.1, se han cambiado estos métodos de firma a App::cache($context = '', $fallbackToGlobal = true). El elemento "cache" del contenedor se ha convertido en una factoria en su lugar.

Cuando se solicita un objeto de caché, debe proporcionarse un valor de "contexto" que identifique su modo de uso. Esto permite a los propietarios de foros dirigir tipos particulares de contenido a capas de caché específicas. Si el propietario no ha configurado un valor específico para el contexto, se aplica el valor $fallbackToGlobal. Si es verdadero, se devolverá la configuración global. En la mayoría de los casos, este será el comportamiento deseado. Sin embargo, si se tiene un sistema que podría llevar a almacenar grandes cantidades de datos, es posible que se desee establecer este argumento en falso. Así es como funciona el sistema de almacenamiento en caché de la página.

En términos de configuración de caché, la configuración global es la misma, a través de $config['cache']['provider'] y $config['cache']['config']. Contextos específicos se configuran en $config['cache']['context'][$contextName] (reemplazando $contextName con el nombre de contexto específico).

Los contextos utilizados fuera de caja incluyen:
  • registro
  • sesiones
  • css
  • página (esto no retrocede a lo globall)
 

lms

Administrador
#8
Valores de relación de la Entidad a través de cierres

When defining the conditions that make up a relationship between entities, you're limited to either fixed value comparisons or column-based comparisons. The only slightly more complex option involves basic concatenations. This is because depending on the context, we may be building a direct query to fetch the relationship or we may be building a join to fetch the relationship for a number of entities simultaneously.

2.1 reduces this limit by allowing the value part of a relationship to be defined using a closure. This is hard to explain without an example, so let's look at a relationship for a new 2.1 entity (with a bit of editing to keep the suspense ):
Insertar CODE, HTML o PHP:
'MasterDescription' => [
    'entity' => 'XF:Phrase',
    'type' => self::TO_ONE,
    'conditions' => [
        ['language_id', '=', 0],
        [
            'title', '=', function($context, $arg1)
            {
                if ($context == 'value')
                {
                    /** @var TypeOfEntityClass $entity */
                    $entity = $arg1;
                    return $entity->getPhraseName();
                }
                else
                {
                    $joinTable = $arg1;
                    return "CONCAT('phrase_group.', REPLACE(`$joinTable`.`entity_id`, ':', '_'))";
                }
            }
        ]
    ]
This is the relationship from a new entity type to the master version of the phrase that it creates. This isn't uncommon -- you'll see very similar things in a number of entities (such as options). However, in this case, the unique ID will be written with a ":" in it, and this isn't something that we allow in phrases so for this relationship to be picked up, we need to map the unique ID.

This is where the two contexts come up, "value" and "join".

Value is where we already have the relevant entity; in this case, it'd be $entity->MasterDescription. Internally, XenForo will be building a query to fetch this specific entity. Because we know the entity, we can use a helper method which will return the expected phrase name for us.

In the join context, we haven't actually fetched any entities yet. We'd simply be saying that we want the MasterDescription with the entity. Therefore, what we need to return is an SQL expression that can get the correct value for us. Instead of receiving an entity argument (which we don't have yet), we receive the SQL alias for the table of the entity that "owns" the relationship.

Note that this system only relates to the "value" portion of a relationship. You still need the first 2 arguments ("title" and "=" here) so XenForo can formulate the necessary parts of the query.

In many cases, this extra power isn't needed and there are alternative workarounds. For example, we could have defined another column that stored the "phrase value" of the entity ID and maintained that internally. However, now that isn't necessary.
 

lms

Administrador
#9
withAliases de la Entidad

In 2.0, when you fetched an entity (or a collection of them), you could eagerly fetch specific relationships using the with() finder method. However, this could cause some problems. It could lead to code duplication, where you were naming the same joins in numerous places. Further, it was hard for add-ons to extend. We attempted to workaround this in several entity-specific finders by providing methods specifically for listing eager joins (like in Finder\Thread::forFullView()), but this only covered certain cases.

Enter the withAliases concept. Let's look at an example for threads:
Insertar CODE, HTML o PHP:
$structure->withAliases = [
   'full' => [
      'User',
      function()
      {
         $userId = \XF::visitor()->user_id;
         if ($userId)
         {
            return [
               'Read|' . $userId,
               'UserPosts|' . $userId,
               'Watch|' . $userId,
               'Bookmarks|' . $userId
            ];
         }

         return null;
      }
   ],
   'fullForum' => [
      'full',
      function()
      {
         $with = ['Forum', 'Forum.Node'];

         $userId = \XF::visitor()->user_id;
         if ($userId)
         {
            $with[] = 'Forum.Read|' . $userId;
            $with[] = 'Forum.Watch|' . $userId;
         }

         return $with;
      }
   ]
];
Here we've defined two aliases, "full" and "fullForum". We'll look at a different approach to this in a moment.

When fetching threads, these can be accessed anywhere we expose the "with" concept to. For example, $threadFinder->with('full') or $em->find('XF:Thread', 123, 'full'). Internally, these will be expanded to include any named relationships within them. If we detect a closure, then that code will be run and the closure can return the names of any relationships that should be included.

If we look at the "fullForum" definition, we can see the first line is "full", which refers to the other withAlias we specified.

We can also refer to aliases on other entities. For example, if a "full" alias existed on the User relation, we could refer to User.full to automatically include all of the relationships that are defined in that case.

But let's look at another approach we could take instead of defining two different aliases, using the "with params" concept. XenForo uses the convention of using a vertical pipe to separate the name of a relationship to some sort of parameter to pass into it. That can be seen here with how we refer to fetching the read marking state for a particular user. With aliases support a similar idea, with the params passed into the closure.

Utilizando este concepto se puede definir un solo álias:
Insertar CODE, HTML o PHP:
$structure->withAliases = [
    'full' => [
        'User',
        function()
        {
            $userId = \XF::visitor()->user_id;
            if ($userId)
            {
                return [
                    'Read|' . $userId, 'UserPosts|' . $userId,
                    'Watch|' . $userId,
                    'Bookmarks|' . $userId
                ];
            }

            return null;
        },
        function($withParams)
        {
            if (!empty($withParams['forum']))
            {
                $with = ['Forum', 'Forum.Node'];

                $userId = \XF::visitor()->user_id;
                if ($userId)
                {
                    $with[] = 'Forum.Read|' . $userId;
                    $with[] = 'Forum.Watch|' . $userId;
                }

                return $with;
            }
        }
    ]
];
Make note of the last closure, which takes the $withParams argument. This will be populated based on our use of the pipe. For example, if we call $threadFinder->with('full|forum'), the "forum" key of the $withParams will be set to true. If we just call "full" without the pipe, then no parameters will be passed through.

Multiple parameters can be passed through using syntax like alias|param1+param2, which will set "param1" and "param2" to true. (The closures will also receive the finder and the raw string after the pipe, to allow more specific uses.)

While this may not sound like a particularly significant feature, it should allow add-ons to more consistently include their joins when needed and it has helped pave the way for a feature we'll be discussing soon.
 

lms

Administrador
#10
Soporte Drop para Internet Explorer 9 Internet Explorer 10

While IE11 still attracts a reasonable amount of market share, this can likely be explained by it still being available in even the most recent versions of Windows 10. IE9 and IE10, on the other hand, haven't been around since the days of Windows 7 and Windows 8 respectively. Even Internet Explorer 10 was first released over 6 years ago.

Although we're not doing anything explicit that will totally "break" either of these browsers in XF 2.1, over time you may see fewer attempts at graceful degradation and any issues arising from this in these browsers will no longer be considered as valid bugs.

One such example of this is that we are no longer detecting whether a browser has flexbox support (which IE9 and IE10 do not) which may result in a less optimal experience for the very few users still using these browsers.
 

lms

Administrador
#11
Eliminación de Modernizr

Al inicio de XF 2.0 se incluyó Modernizr como una vía capaz de detectar el soporte del navegador de ciertas características (o no). Modernizr es configurable en términos de que pruebas incluidas y que incluimos una cantidad bastante razonable de ellos, a pesar de que realmente solo se utilizan algunas. Esto suponía un archivo de 18KB.

We aren't actually removing it from the XF download, simply because it's possible that some add-ons are making use of it to detect features beyond what we actually needed, though it's worth noting that it will no longer be loaded by default.

To replace it, we have implemented our own feature detection framework within the XF JS. As with any object within our JS, you should be able to extend it in order to add additional feature tests.

By default we are only detecting support for touch events, passive event listeners and whether the browser/device you're using has "hidden" scrollbars.

Although you can just continue to include Modernizr in your add-on code, we'd strongly encourage you to instead write your own feature tests using this framework instead.
 

lms

Administrador
#12
Actualización de iconos-etiqueta

Sin entrar en muchos detalles, se decidió que el CSS utilizado en XF 2.0 para generar los iconos de Font Awesome utilizados en casillas de verificación, botones de radio, etc. eran innecesariamente complicados y demasiado específicos y cuya personalización era muy dificultosa.

Como consecuencia, se ha abandonado ese código y los iconos-etiqueta utilizan ahora un nuevo CSS.

Probablemente no afecte mucho en el día a día aunque si has incluido botones de radio, casillas de verificación u otras propiedades de iconos-etiqueta en los complementos sin haber utilizado la construcción con las etiquetas estándar <xf:radio... o <xf:checkbox..., o has agregado CSS personalizado a los elementos icono-etiquetas, deberás realizar una doble comprobación de su aspecto en 2.1 y realizar los ajustes necesarios en caso de ser preciso.
 

lms

Administrador
#13
¡Y eso es todo por hoy! Nos vamos acercando al final de la serie Lo que ves actual para 2.1, pero tras hoy todavía hay tres subprocesos más Lo que ves, lo que nos lleva a la primera oportunidad de trastear con XF 2.1 cuando lo implementemos en XF, lo cual, prometemos, va a ser muy pronto!

Hasta entonces, muchas gracias por todo vuestro apoyo y amables palabras.
 
Arriba