web-dev-qa-db-fra.com

Un mot de passe protégé affiche les redirections

J'ai une page associée à un modèle personnalisé qui répertorie tous les messages protégés par mot de passe.

Appelons-le MyPage .

Ce que j'essaie de faire, c'est lorsqu'un utilisateur affiche MyPage pour afficher une seule fois le formulaire mot de passe protégé et lorsqu'il/elle saisit un mot de passe valide -> pour rediriger vers MyPage so il peut voir la liste des messages protégés.

Je veux sur le modèle de liste pour afficher qu'un seul formulaire et lorsque l'utilisateur a entré son mot de passe pour afficher uniquement les messages avec le même mot de passe (ou les messages dans la même catégorie) qui sont déverrouillés.

Le mot de passe est le mot de passe de post-protection par défaut de la méta-boîte de publication:

WordPress post publish meta box

4
NoSense

TL; TR? alerte Spoiler - survolez la citation suivante pour l'exposer.

Construire une archive qui est facilement filtrable par le mot de passe actuellement entré n’a rien de facile et ne peut sûrement pas être élégant. La solution implique une requête secondaire et un petit plugin, mais cela fonctionne. Toutes les erreurs et tous les mauvais chemins sont également affichés pour vous éviter de commettre les mêmes erreurs.

Structure de la base de données

Le mot de passe fait partie de la table principale $wpdb->posts. Il est enregistré dans texte brut (juste pour que ce soit clair. N'ayez pas peur de cela. Il n'est connecté à aucun login d'utilisateur, vous n'avez donc pas à craindre d'ouvrir une sécurité. trou quand quelqu'un le trouve par accident.

Construire/modifier la requête principale

Pour réduire les résultats aux publications protégées par mot de passe, nous interceptons la clause WHERE pour:

  • Une feuille
  • Tout ce qui est public
  • Toutes les pages avec un modèle dont le nom de fichier est password-protected.php

Voici le code réel en tant que plugin. Veuillez noter que cela fonctionneranot. is_page_template() et is_page() avec un argument slug extrairont leurs données de get_queried_object(). Et l'objet interrogé est présent lorsque nous avons déjà récupéré la requête finale résultat.

<?php
/** Plugin Name: (#135443) Password Protected Posts Page Template Query */

add_action( 'pre_get_posts', 'wpse135443ExcludeProtectedAction' );
function wpse135443ExcludeProtectedAction( $query )
{
    if (
            is_page( 'slug-of-protected-page' )
            AND is_page_template( 'password-protected.php' )
            AND ! is_admin()
    )
        add_filter( 'posts_where', 'wpse135443ExcludeProtected' );
}
// Callback for the posts WHERE clause that only queries for password protected posts
function wpse135443ExcludeProtected( $where )
{
    return $where .= " AND NOT {$GLOBALS['wpdb']->posts}.post_password = '' ";
}

Modèle de page

Il y a plusieurs façons de procéder. On utilise un filtre:

add_filter( 'the_excerpt', 'wpse135443PasswordProtectedExcerpt' );
function wpse135443PasswordProtectedExcerpt( $excerpt )
{
    return post_password_required()
        ? get_the_password_form()
        : $excerpt;

}

Il serait beaucoup plus facile de simplement lister le titre puis d’ajouter l’extrait ou le contenu directement dans le modèle dédié. post_password_required() renvoie un résultat booléen, il est donc facile de le vérifier. La fonction accepte un argument, qui est l'identifiant de publication. Il est déjà extrait du global à l'intérieur de la boucle, mais vous pouvez également l'utiliser en dehors de la boucle avec un mot de passe.

post_password_required() AND print get_the_password_form();

Récupérer le mot de passe et l'ajouter à la requête:

La fonction suivante ajoute le mot de passe à la chaîne de requête (publique). Comme vous pouvez le constater, cette solution n’est pas sécurisée: elle expose le mot de passe dans la requête HTTP.

add_action( 'login_form_postpass', 'wpse135443PasswordToQueryString' );
function wpse135443PasswordToQueryString()
{
    require_once ABSPATH . 'wp-includes/class-phpass.php';
    $hasher = new PasswordHash( 8, true );

    $expire = apply_filters( 'post_password_expires', time() + 10 * DAY_IN_SECONDS );
    setcookie( 'wp-postpass_' . COOKIEHASH, $hasher->HashPassword( wp_unslash( $_POST['post_password'] ) ), $expire, COOKIEPATH );

    exit( wp_safe_redirect( add_query_arg(
        'post_password',
        esc_attr( $_POST['post_password'] ),
        wp_get_referer()
    ) ) );
} );

Bien que cela puisse fonctionner, c'est vraimentnon recommandé. Si vous voulez vraiment partir de là, vous saurez, espérons-le, comment procéder. Pour des raisons de bon sens, je n'en expose pas plus que cela: les chasseurs de snippets pourraient en faire une copie et l'utiliser dans des thèmes destinés à une diffusion publique et je ne veux pas livrer la racine d'un problème.

Rien ne fonctionne? Autres options?

Le premier gros problème est que, par défaut, WordPress a non "Archives des publications protégées" comme vous pouvez voir dans la hiérarchie des modèles . Et lorsque nous souhaitons modifier la requête principale dans un modèle de page, nous devrons vérifier la chaîne SQL et la comparer. Les plugins peuvent intercepter cela (mal) et il n'y a aucun moyen fiable de réagir avec des clauses SQL interceptées et modifiées dans WP. Un exemple de l'apparence possible de la valeur d'un rappel/d'une action de filtre, lié à posts_clauses. $wpdb->posts est le nom de votre table posts.

array (size=7)
  'where' => string ' AND $wpdb->posts.ID = 1519 AND $wpdb->posts.post_type = 'page'' (length=63)
  'groupby' => string '' (length=0)
  'join' => string '' (length=0)
  'orderby' => string '$wpdb->posts.post_date DESC' (length=27)
  'distinct' => string '' (length=0)
  'fields' => string '$wpdb->posts.*' (length=14)
  'limits' => string '' (length=0)

Rien à voir ici.

Nous essayons ensuite d'ajouter une autre requête. Pour faciliter son identification, nous ajouterons une variable de requête personnalisée supplémentaire.

$ppQuery = new WP_Query( array(
    'password_protected' => TRUE,
    'post_type'          => 'post',
    'posts_per_page'     => get_option( 'posts_per_page' ),
    'post_status'        => 'publish',
) );

L'instruction SQL obtenue ressemblera à ce qui suit:

SELECT SQL_CALC_FOUND_ROWS  $wpdb->posts.*
FROM $wpdb->posts
WHERE 1=1
    AND $wpdb->posts.post_type = 'post' 
    AND ($wpdb->posts.post_status = 'publish')
    AND NOT ($wpdb->posts.post_password = '')
ORDER BY $wpdb->posts.post_date DESC LIMIT 0, 10

Ensuite, nous pouvons le parcourir:

<?php
if ( $ppQuery->have_posts() )
{
    ?>
    <div class="entry-content">
        <?php echo get_the_password_form( $GLOBALS['post'] ); ?>
    </div>
    <?php
    while ( $ppQuery->have_posts() )
    {
        $ppQuery->the_post(); ?>

        <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
            <?php the_title( '<header class="entry-header"><h2>', '</h2></header>' ); ?>
            <div class="entry-content">
            <?php
            post_password_required()
                OR the_excerpt();
            ?>
            </div>
        </article>

    <?php
    }
} ?>

Le formulaire de mot de passe est affiché en haut du modèle. Il prend les données nécessaires de l'objet global $post, qui est la première instance WP_Post détenue par l'objet global $wp_query. Le message utilisé n'a pas vraiment d'importance, car il sert uniquement à générer le <label> pour le formulaire et l'ID du champ input et rien d'autre. Le formulaire sera toujours déclenché pour name="post_password".

L'extrait est affiché uniquement si post_password_required() est TRUE. Lors du déclenchement du formulaire, wp-login.php?action=postpass récupère les données $_POST, définit le cookie et effectue une redirection sécurisée. Par conséquent,tous lespost qui partage le même mot de passe sera "déverrouillé" - WP vérifie simplement la valeur Cookie Hash par rapport à une valeur hachée de tous les mots de passe des publications. Si cela correspond, il est affiché.

Voici le plugin qui aligne ce modèle et qui est nécessaire pour le laisser fonctionner.

<?php
/**
 * Plugin Name: (#135443) Password Protected Posts Page Template Query
 */

add_action( 'pre_get_posts', 'wpse135443ExcludeProtectedAction' );
function wpse135443ExcludeProtectedAction( $query )
{
    if (
        $query->get( 'password_protected' )
        AND ! is_admin()
    )
        add_filter( 'posts_where', 'wpse135443ExcludeProtected' );
}
// Callback for the posts WHERE clause that only queries for password protected posts
function wpse135443ExcludeProtected( $pieces )
{
    remove_filter( current_filter(), __FUNCTION__ );
    return $where .= " AND NOT ({$GLOBALS['wpdb']->posts}.post_password = '') ";
}

Filtrer les posts

Il serait presque impossible (ou seulement difficilement réalisable) d'interroger des valeurs hachées et d'effectuer un hachage de mots de passe de publication pendant la requête. Le moyen le plus simple consiste donc à filtrer les publications au moment de l'exécution à l'aide d'une variable FilterIterator:

class PasswordProtectedPostsLoop extends \FilterIterator implements \Countable
{
    protected $wp_query;

    protected $allowed = FALSE;

    protected $total = 0;

    protected $counter = 0;

    /**
     * @param \Iterator $iterator
     * @param \WP_Query $wp_query
     */
    public function __construct( \Iterator $iterator, \WP_Query $wp_query )
    {
        # Setup properties
        // Global main query object
        NULL === $this->wp_query AND $this->wp_query = $wp_query;

        // Posts for this request
        $this->total = $this->wp_query->query_vars['posts_per_page'];

        // Internal counter
        0 !== $this->counter AND $this->counter = 0;

        $this->allowed = $this->wp_query->have_posts();

        parent::__construct( $iterator );
    }

    /**
     * @return bool
     */
    public function accept()
    {
        if (
            ! $this->allowed
            OR ! $this->current() instanceof \WP_Post
        )
            return FALSE;

        $this->wp_query->the_post();

        // Rewind posts for next loop
        $this->wp_query->current_post === $this->total -1
            AND $this->wp_query->rewind_posts();

        if ( ! post_password_required() )
            return FALSE;

        $this->counter++;
        return TRUE;
    }

    /**
     * Helper function to retrieve the ID of the currently looped post.
     * @return int
     */
    public function getID()
    {
        return $this->current()->ID;
    }

    /**
     * @return int
     */
    public function count()
    {
        return $this->counter;
    }
}

La boucle serait alors aussi simple que:

$ppArrayObj = new \ArrayObject( $ppQuery->->get_posts() );
$ppLoop = new PasswordProtectedPostsLoop( $ppArrayObj->getIterator(), $ppQuery );
$ppLoop->rewind();
foreach ( $ppyLoop as $post )
{
        ?>
        <article <?php post_class(); ?>>
            <?php the_title( '<header class="entry-header"><h2>', '</h2></header>' ); ?>
            <div class="entry-content">
                <?php the_excerpt(); >
            </div>
        </article>
        <?php
}
6
kaiser

Question interessante :)

Voici une implémentation très simple.

/**
 * Template Name: Pass-Prot
 *
 */

function only_password_protected($where) {
  remove_action('posts_where','only_password_protected');
  return ' AND post_password != ""';
}
add_action('posts_where','only_password_protected');

$protected = new WP_Query(
  array(
    'post_type' => 'post'
  )
);
get_header();

if ($protected->have_posts()) {
  $form = true;
  while ($protected->have_posts()) {
    $protected->the_post();
    if (!post_password_required($post)) {
      $form = false;
      the_title();
      the_content();
      echo '<hr/>';
    }
  }
  if ($form) {
    echo get_the_password_form();
  }
}
get_footer();

Cela devrait fonctionner pour des cas simples. Si vous avez beaucoup de publications protégées par mot de passe, ou si vous avez besoin de pagination, vous aurez besoin de quelque chose de plus compliqué. Vous aurez probablement besoin de rechercher des cookies post-mot de passe et de rechercher spécifiquement ceux-ci.

Vous aurez également besoin d'un moyen de "réinitialiser" pour entrer un nouveau mot de passe.

J'espère que cela vous permet de commencer.

3
s_ha_dum

Le futur/prochain WP version

... comportera déjà l'interrogation par has_password et par post_password. Billet Trac disponible.

2
kaiser