Nesse artigo você verá a criação de um plugin de votação Post Ratings com acesso direto ao banco de dados, a criação de um Widget para exibição dos resultados e também o uso de um Shortcode para permitir que o gestor de conteúdo insira informações dos votos em meio a publicação.

A proposta é desenvolver um plugin que permita aos assinantes do site votarem em cada post em um formulário de votação específico. Esse formulário na verdade será constituído apenas de elementos gráficos, imagens de estrelas, que representam o voto em uma escala de 1 a 5 e deverá ser submetido via Ajax.

Plugin de votação de posts via AJAX em funcionamento

Plugin de votação de posts via AJAX em funcionamento

Para gerenciamento dos votos uma nova tabela deverá ser implementada a estrutura do WordPress. Dentro do Dashboard o plugin possuirá página de opções e um Widget próprio; e ainda sobre o modelo de integração, ele deverá fornecer um shortcode e também uma Template Tag a serem aplicadas pelos seus usuários.

Objetivos do plugin

  • Permitir a votação dos posts pelos assinantes do site
  • Computar voto por meio de requisição AJAX
  • Armazenar votos em uma tabela própria
  • Criar Template Tag, Widget e Shortcode para integração do recurso

Estrutura do Plugin

De acordo com o objetivo proposto, será preciso trabalhar com arquivos de diferentes formatos (imagens, CSS e JavaScript) presentes no mesmo diretório do plugin. Aplique portanto a boa prática de manter o caminho absoluto do plugin em uma constante, assim toda vez que você precisar ele poderá ser facilmente recuperado.

define( 'CPR_DIR',  basename( dirname( __FILE__ ) ) );
define( 'CPR_PATH', WP_PLUGIN_DIR . '/' . CPR_DIR . '/' );

Comece a criação do plugin com a classe responsável por agrupar, organizar o código e defina um método para criação de uma página administrativa própria:

add_action( 'plugins_loaded', array( 'KDM_Ratings', 'setup' ) );

class KDM_Ratings
{
    static $cap = 'manage_options';
    function setup()
    {
        add_action( 'admin_menu', array( 'KDM_Ratings', 'menu' ) );
    }
    function menu()
    {
        add_menu_page( 'Post Ratings - Opções', 'Post Ratings', self::$cap, 'pr-menu', array( 'KDM_Ratings', 'admin_page' ) );
    }
}

Repare no uso da capability ‘manage_options’, cuja função é verificar o acesso a usuários que podem gerenciar as opções do WordPress; ela foi declarada dentro da Classe e não diretamente em add_menu_page. Essa prática foi adotada para demonstrar como fazer a verificação do acesso de acordo com as permissões de cada usuário, acompanhe.

Roles e Capabilities

Durante a criação das páginas administrativas ao utilizar add_menu_page você pôde perceber a presença do (terceiro) parâmetro $capability definido como ‘administrator’. Através dessa informação foi definido que apenas usuários do tipo administrador poderiam acessar a página criada.

Dentro do WordPress há um sistema próprio de gerenciamento de usuários que permite estabelecer um sistema de aprovação editorial e também limitar o acesso de usuários a certos recursos. Esse acesso e as ações permitidas a cada usuário dependem diretamente do tipo sob o qual ele está ativo no sistema. O termo correto para designar os ‘tipos’ de usuários são as Roles. Veja abaixo a relação das Roles padrão do sistema e suas respectivas funções dentro do gerenciamento de conteúdo:

  • Administradores: Possuem acesso a todos os recursos
  • Editores: Executam o gerenciamento e publicação de posts
  • Autores: Controlam apenas os próprios conteúdos
  • Colaboradores: Atuação semelhante a dos autores, porém não têem permissão de publicar conteúdo ou realizar uploads de arquivos
  • Assinantes: Capazes apenas de realizarem leituras e publicarem comentários em artigos privados

Ao passo que as ações entre os usuários de diferentes Roles são também distintas, o painel de controle se adequa de modo a exibir ou ocultar os recursos. Esse comportamento se ajusta com o nível de acesso do usuário.

Cada ação permitida a um usuário é denominada Capabilities ou capacidades; e dentro da programação essas capacidades são representadas por um identificador próprio. Tome como exemplo a capacidade de um Administrador adicionar novos usuários ao sistema, ele possui a capability ‘add_users’ atribuída a ele.

Validação dos Dados

Criada a página administrativa, verifique se o usuário em questão deve ter o acesso concedido através de current_user_can. Como parâmetro da função será empregada a mesma Capacidade atribuída dentro da classe, porém você pode optar por filtrar as opções ou parte delas de acordo com outras características.

function admin_page() {
    if ( !current_user_can( self::$cap ) )
        wp_die( 'Você não tem permissões suficientes para acessar essa página!' );
}

Elabore agora um formulário para edição das seguintes opções:

  • Texto que antecede a exibição do formulário
  • Checagem de exibição automática dos votos
  • Escolha do local para impressão dos resultados, se deve ser inserido antes ou depois do conteúdo do post
  • Optar por apresentar ou não, logo após o formulário, a quantidade de votos e/ou a média obtida com os votos já computados

No campo de texto que receberá o título do formulário, faça uma validação do valor enviado para evitar que sejam inseridos códigos HTML ou qualquer outra informação que possa afetar negativamente o funcionamento do plugin.

$title = sanitize_text_field( $_POST[ 'cpr_title' ] );

Com essa função você remove quebras de linha, tabulações e tags HTML da string fornecida como parâmetro; o retorno é composto apenas por caracteres alfanuméricos e símbolos especiais como entidades HTML.

Tratamento de Textos

Além dessa validação interna, muitas vezes é preciso verificar uma string em diferentes situações; seja quando ela é obtida através de requisições externas, do envio de formulários por parte dos visitantes do site, da recuperação do banco de dados e tantas outras circunstâncias. Nesses casos de tratamento de dados não-confiáveis, você pode contar com diversas técnicas e funções do WordPress:

<?php esc_attr( $string ) ?>

Ideal para exibir textos dentro de atributos das tags HTML por codificar tanto aspas simples como duplas

<?php esc_html( $string ) ?>

Trata o texto passado como parâmetro de modo que não seja interpretado como tags HTML

<?php wp_kses( $string, $allowed_html, $allowed_protocols ) ?>

Realiza o filtro de $string e retorna apenas as tags e protocolos permitidos que foram informados nos parâmetros

<?php wp_strip_all_tags( $string, $remove_breaks ) ?>

Remove todas as marcações HTML de um texto, inclusive caracteres de retorno \n, \t e \r se desejado.

<?php is_email( $email ) ?>

Verifica se o parâmetro passado é um email válido

Como complemento as verificações propostas é muito importante otimizar sua lógica de programação e trabalhar com estruturas condicionais, de acordo com os objetivos a serem alcançados com o plugin. Desse modo se um referido campo necessita ser preenchido, opte por manter um valor padrão a ele ou force o seu usuário a preenchê-lo. Do contrário, realize o tratamento de modo a evitar erros grotescos.

<?php
if ( !is_admin() ) {
  // limita atuação apenas para o front-end
  if ( isset( $_POST[ 'cpr_count' ] ) ) {
    $valor = (int) $_POST[ 'cpr_count' ];
    if ( !$valor )
      $valor = 7; // ou wp_die( 'O valor deve ser preenchido!' );
    // e em seguida continuam as instruções após validar $valor
  }
}
?>

Nonces

Durante a manipulação de formulários, uma outra técnica de validação, é conhecer e filtrar a origem do envio dos dados. Caso o processamento dos formulários seja feito sem essa verificação, ele facilmente pode ser alvo de envio de spams, sobrecarga no servidor, ponto de acesso para outros ataques; enfim, seu projeto fica vulnerável nessas áreas.

Conheça o Nonce, um verificador único gerado pelo WordPress, com objetivo de evitar ataques mediante requisições oriundas de locais diferentes do desejado. Através desse recurso é possível assegurar que a informação enviada percorreu o caminho definido para chegar até o destino final, filtrando assim códigos mal-intencionados.

A verificação feita através de Nonces geralmente ocorre através de campos ocultos de formulário e parâmetros na URL; no entanto nada impede de ser aplicado de outras maneiras, como por exemplo utilizando sessões.

<?php
// no arquivo de envio das informações
session_start();
$_SESSION[ 'nonce' ] = wp_create_nonce( 'cpr_nonce' );
// no recebimento das informações
$nonce = $_SESSION[ 'nonce' ];
if ( !wp_verify_nonce( $nonce, 'cpr_nonce' ) )
  wp_die( 'Erro ao submeter dados...' );
?>

No tratamento feito através de URL, o parâmetro _wpnonce é gerado automaticamente por:

<?php
echo wp_nonce_url( 'http://www.site.com.br', 'cpr_nonce' );
// verifica-se da mesma forma
$nonce = $_REQUEST[ '_wpnonce' ];
if ( wp_verify_nonce( $nonce, 'cpr_nonce' ) )
  echo 'Envio realizado com sucesso!';
?>

Enquanto que em campos ocultos de formulário utiliza-se:

<?php
wp_nonce_field( 'nonce_action', 'nonce_name' );
// verificando...
$nonce = $_REQUEST[ 'nonce_name' ];
if ( wp_verify_nonce( $nonce, 'nonce_action' ) )
  echo 'Envio realizado com sucesso!';

A função wp_nonce_field aceita um terceiro parâmetro opcional, o valor boolean de _wp_http_referer, que por padrão é verdadeiro. Ele carrega consigo a URL sob a qual o form está sendo submetido. Quando for conveniente, utilize a função wp_get_referer (sem nenhum parâmetro) para recuperar qual a origem do acesso e desse modo minimize ainda mais as chances de ataques externos por essa via de acesso.

Aplique esse conceito de Nonces dentro da página administrativa do plugin de votação. Como tanto origem e destino dos dados serão dentro do Dashboard, você pode fazer a verificação do seguinte modo:

<?php
wp_nonce_field( 'nonce-save', 'nonce-rating' );
// posteriormente
if ( isset( $_POST[ 'nonce-rating' ] ) ) {
    if ( check_admin_referer( 'nonce-save', 'nonce-rating' ) ) {
        // recupera e atualiza as opções
?>

Banco de Dados do WordPress

Desenvolvido o cenário administrativo para configuração do plugin é preciso agora estruturar um modelo de gerenciamento para os votos computados. Acessando o servidor MySQL você pode verificar que o banco de dados do WordPress é composto por onze tabelas. Tais tabelas são responsáveis por armazenar, além do conteúdo gerenciável, informações referentes à manipulação do sistema; tal como foi apresentado na API de opções.

Estrutura de banco de dados do WordPress

Estrutura de banco de dados do WordPress

Na aplicação das metaboxes foi utilizado um modelo de armazenamento de dados auxiliares bem interessante: o uso de tabelas secundárias para complementar as informações tratadas em uma tabela principal. Note a clara relação entre tabelas principais e suas auxiliares:

  • wp_users – wp_usermeta
  • wp_posts – wp_postmeta
  • wp_comments – wp_commentmeta

As tabelas auxiliares são compostas por um identificador único como chave primária, um identificador como chave secundária da tabela principal e os campos meta_key e meta_value que correspondem respectivamente ao nome de um novo campo para tabela e seu respectivo valor.

MetaData API

Retome o contexto visto das metaboxes para lembrar e aprofundar um pouco mais a manipulação dos dados auxiliares pelo WordPress através da MetaData API. Essa API oferece funções de inserir, alterar, excluir e recuperar os dados.

add_metadata( $meta_type, $object_id, $meta_key, $meta_value, $unique );
  • $meta_type: Tipo de conteúdo tratado: post, comment ou user
  • $object_id: Identificador numérico do conteúdo ($meta_type) em questão
  • $meta_key: Nome do campo
  • $meta_value: Valor a ser atribuído ao campo
  • $unique: Parâmetro do tipo boolean que ao ser definido como verdadeiro, não insere os dados caso o campo ($meta_key) existir
update_metadata( $meta_type, $object_id, $meta_key, $meta_value, $prev_value );
  • $prev_value: Valor armazenado antes da atualização para limitar a alteração somente ao registro com tal valor e não a todas suas ocorrências
delete_metadata( $meta_type, $object_id, $meta_key, $meta_value, $delete_all );
  • $delete_all: Caso o parâmetro seja definido como verdadeiro, a função exclui os campos de todos os objetos, do contrário respeita $object_id
get_metadata( $meta_type, $object_id, $meta_key, $single );
  • $single: Recupera apenas o primeiro valor do referido campo quando verdadeiro, quando não retorna um array numérico com os valores

Um detalhe interessante é que a função responsável por alterar os dados também os insere caso não exista um registro com os identificadores passados pela função. Outra caracterísitca relevante dessa API, trata-se da existência de funções específicas para cada tipo de conteúdo que possui tabela auxiliar; por exemplo a get_post_meta vista anteriormente possui o mesmo efeito que get_metadata com o parâmetro $meta_type setado como ‘post’.

Exceto pelo fato que essas funções específicas não usam o primeiro parâmetro que define a tabela auxiliar a ser manipulada, não há diferença entre o uso dessas com as funções da Metadata API. Na realidade as funções específicas são apenas rótulos que visam facilitar a programação e quando acionadas executam as funções genéricas da API dos meta dados.

Análise de Viabilidade

Conhecendo toda estrutura da base de dados e a API de meta-dados, você pode sim optar por desenvolver seu plugin sem a criação de uma nova tabela. No entanto, adiante seu projeto para diversas situações como a elevada quantidade de registros, qual o tipo de informação será apresentada aos visitantes, performance, tempo de resposta e outras questões que possam surgir de acordo com a necessidade apresentada.

No plugin de votação armazenar os votos todos nas tabelas auxiliares criaria muito volume nessa tabela que é bem requisitada pelo WordPress, Tema e também outros plugins. Sem contar que para exibir valores como a quantidade de votos e sua média aritmética seria muito trabalhoso. Portanto prefira a criação de uma tabela própria capaz de armazenar os votos e nos campos auxiliares guarde esses valores mais amplos (total de votos e média) para otimizar suas consultas.

Tabelas Principais x Tabelas Personalizadas

Desse modo você pode observar que a manipulação dos dados através dos recursos do WordPress é algo bem prático de se fazer. De modo geral, assim como existem funções para as tabelas auxiliares, existem também para as demais tabelas. Veja por exemplo o uso de wp_insert_post para inserir um novo post ou wp_insert_user para usuários. Use ainda o mesmo conceito de nomenclatura para apagar (delete), atualizar (update) ou obter (get) resultados; as funções são auto-explicativas.

Porém existem casos que ao fazer uso de certas funções acabamos por executar códigos que não seriam necessários para o objetivo pretendido, sendo o melhor modo de resolver a questão com o uso de instruções SQL próprias. O mesmo ocorre quando é preciso manipular uma tabela própria, não existem funções para isso. No entanto, o WordPress disponibiliza a classe wpdb capaz de manter uma conexão com o banco de dados e nele executar as queries SQL que você pretender.

Classe $wpdb

Essa classe é responsável por controlar a comunicação do sistema com o banco de dados. Através dela o WordPress estabelece uma conexão com o banco de dados e a disponibiliza para ser utilizada sempre que necessário. A classe contém métodos específicos para manipulação dos dados e auxilia na submissão de diferentes tipos de query. O uso da classe é ativado após ela ter sido referenciada ao arquivo pelo escopo global da aplicação.

global $wpdb;

Qualquer tipo de instrução SQL pode ser enviado ao banco através do método query.

$sql = 'DELETE FROM wp_posts WHERE post_type="revision"';
$wpdb->query( $sql );

O problema ao utilizar esse método está no retorno dos dados, ele retorna apenas a quantidade de registros afetados ou então o valor falso caso tenha ocorrido algum erro durante a execução da consulta. Logo seu uso não deve ser realizado para efetuar consultas. Nas situações onde o objetivo é o retorno de informações do database, a classe wpdb oferece alguns métodos específicos; veja abaixo a relação dos métodos e os valores recuperados:

  • get_var: retorna apenas um único valor
  • get_row: recupera um registro completo
  • get_col: obtém os valores de uma coluna da tabela informada
  • get_results: retorna todos os valores de modo genérico
$user_count = (int) $wpdb->get_var( 'SELECT COUNT(ID) FROM wp_users' );
// Retorna a quantidade de usuários cadastrados
$sql = 'SELECT user_email, display_name FROM wp_users';
$row = $wpdb->get_row( $sql ); // traz o primeiro registro
$col = $wpdb->get_col( $sql, 1 ); // traz todos os resultados da consulta apenas para display_name
$results = $wpdb->get_results( $sql ); // traz todos os resultados

Em situações onde é preciso inserir, alterar ou apagar registros; você pode escolher entre o uso do método query ou então aos métodos específicos de cada ação (insert, update e delete respectivamente).

dbDelta

Estabelecido que o recurso em desenvolvimento deverá possuir sua própria tabela, comece a definir o uso da mesma no momento em que o plugin será ativado pelo usuário. Através do gancho de ativação insira a instrução responsável por criar ou verificar a existência de sua tabela; faça isso aplicando dbDelta de modo a garantir que a estrutura proposta da tabela está correta, atualizada.

register_activation_hook( __FILE__, array( 'KDM_Ratings', 'activate' ) );
// definição do método
function activate()
{
    $sql = "CREATE TABLE `{$wpdb->prefix}postratings` (
        `rating_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
        `post_id` BIGINT UNSIGNED NOT NULL ,
        `user_id` BIGINT UNSIGNED NOT NULL ,
        `rating` TINYINT( 1 ) UNSIGNED NOT NULL ,
        `rating_date` DATETIME NOT NULL
    )";
    require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
    dbDelta( $sql );
}

A estrutura proposta define o armazenamento dos principais valores de cada voto; em essência o usuário, o post e o voto em si. A data na qual o voto foi submetido também será armazenada pois através desse campo você tem condições de realizar consultas específicas, filtradas por período de tempo.

Fique a vontade para inserir na função de ativação do plugin os valores iniciais das configurações do plugin; assim você garante o funcionamento do plugin de modo padrão sem a obrigatoriedade de ajustes.

Comunicação com o Tema

O próximo passo é exibir o formulário para obtenção dos votos. De acordo com as configurações do plugin, é possível que ele venha a ser apresentado de modo automático; antes ou depois do conteúdo. A partir da função setup, aplique o filtro de conteúdo ‘the_content’ e a ele atribua a exibição do form.

function setup()
{
    add_action( 'init',         array( 'KDM_Ratings', 'init' ) );
    add_action( 'admin_menu',   array( 'KDM_Ratings', 'menu' ) );
}

function init()
{
    $opt = get_option( self::$prefix . 'ratings' );
    if ( $opt[ 'show' ] )
        add_filter( 'the_content', array( 'KDM_Ratings', 'stars' ), 10, 1 );
}

Como a exibição automática pode ou não ocorrer, apenas é inserido o filtro se assim o usuário escolher. Dentro do filtro aplicado, as rotinas cuidarão de aplicar o recurso apenas dentro dos posts. Recupere a quantidade de votos e a média para exibir quando assim for configurado o plugin.

public static function stars( $content ) {
    if ( !is_single() ) return $content;

    $opt = get_option( 'kdm_ratings' );

    global $post;
    $value = (float) get_post_meta( $post->ID, 'kdm_ratings', true );
    $int_value = round( $value );

    $html = '<p>' . $opt[ 'title' ] .
        '<span id="post-rating">';

    for ( $i=1; $i<=5; $i++ ) {
        $html .= '<span id="star-' . $i . '"';

        if ( $int_value >= $i )
            $html .= '';

        $html .= '></span>';
    }

    if ( $opt[ 'media' ] )
        $html .= ' Média: ' . number_format( $value, 2, ',', '' );

    if ( $opt[ 'votes' ] ) {
        global $wpdb;
        $votes = (int) $wpdb->get_var(
            $wpdb->prepare( 'SELECT COUNT(rating_id) FROM ' . $wpdb->prefix . 'postratings WHERE post_id=%d', $post->ID )
        );
        $html .= ' Total de votos: ' . $votes;
    }

    $html .= '</span></p>';

    if ( $opt[ 'place' ] == 'before' )
        return $html . $content;
    else
        return $content . $html;
}

Você pôde observar que o formulário não se utiliza da tag form do HTML. Essa marcação de parágrafo e span foi assumida em razão da adequação ao recurso, a facilidade em posteriormente se incrementar rich snippets a ele, e também pelo fato da interação ser feita via Ajax; não necessitando obrigatoriamente da referida tag.

Formatação e Interação

Impresso o HTML, você precisa definir um arquivo de estilo a fim de obter uma exibição compreensível do recurso e o script capaz de interagir com as ações do usuário. O CSS no caso trará apenas informações essenciais de posicionamento, apresentação das imagens; enquanto que o JavaScript deve computar o clique dos usuários sobre as imagens, captando assim o voto dado a cada post.

Registre então ambos os arquivos com as devidas funções. A seguir confirme em qual situação os arquivos serão anexados ao site.

function init()
{
    self::$url = plugins_url( CPR_DIR ) . '/';
    $opt = get_option( self::$prefix . 'ratings' );

    if ( $opt[ 'show' ] == '1' )
        add_filter( 'the_content', array( 'KDM_Ratings', 'stars' ), 10, 1 );

    wp_register_script( 'cpr-script', self::$url . 'js/post-ratings.js', array( 'jquery' ), null );
    wp_register_style( 'cpr-style', self::$url . 'css/post-ratings.css', array(), null, 'screen' );

    if ( !is_admin() ) {
        wp_enqueue_style( 'cpr-style' );
        if ( is_user_logged_in() && current_user_can( 'subscriber' ) )
            wp_enqueue_script( 'cpr-script' );
    }
}

Tanto formatação, quanto scripts são exibidos apenas no lado público do site; portanto a checagem inicial com is_admin. No caso da interação promovida pelo arquivo JavaScript somente ocorrerá se o usuário estiver logado e possuir a role ‘subscriber’ atribuída a ele; já que os votos são permitidos apenas aos assinantes do site. A utilização de current_user_can dispensa is_user_logged_in.

O código dos arquivos CSS e JS não serão detalhados pois tratam-se de assuntos paralelos com a criação de plugins WordPress. Ambos os arquivos podem ser obtidos mediante download na referida área de arquivos anexos dessa aula. De qualquer modo caso tenha alguma dúvida a respeito, entre e contato para obter uma melhor orientação.

Ajax de Votação (Inserindo um Post Rating)

A requisição a ser enviada via Ajax deverá conter o voto do usuário e o identificador do post. Como a votação é aberta somente a usuários logados, você consegue obter qual usuário realizou a ação através de get_current_user_id.

Verifique também se o usuário em questão já não submeteu seu voto; se o fez informe-o da situação, pois é permitido apenas um voto por usuário. Caso o voto seja válido, o insira na tabela de votos e atualize a média para atribuí-la a tabela de dados auxiliares dos posts. Com todo o processo executado corretamente, lembre-se de confirmar a ação ao seu usuário.

public static function vote()
{
    if ( !current_user_can( 'subscriber' ) || !isset( $_POST[ 'post_id' ] ) || !isset( $_POST[ 'rating' ] ) )
        die();

    global $wpdb;
    $table = $wpdb->prefix . 'postratings';

    $rating = $_POST[ 'rating' ];
    $post_id = $_POST[ 'post_id' ];
    $user_id = get_current_user_id();
    $vote = (int) $wpdb->get_var(
        $wpdb->prepare( 'SELECT rating_id FROM ' . $table . ' WHERE post_id=%d AND user_id=%d', $post_id, $user_id )
    );

    if ( !$vote ) {
        $values = array(
            'post_id'		=> $post_id,
            'user_id'		=> $user_id,
            'rating'		=> $rating,
            'rating_date'	=> date( 'Y-m-d H:i:s' )
        );
        $wpdb->insert( $table, $values );

        $count = (int) $wpdb->get_var(
            $wpdb->prepare( 'SELECT COUNT(rating_id) FROM ' . $table . ' WHERE post_id=%d', $post_id )
        );
        $votes = $wpdb->get_results(
            $wpdb->prepare( 'SELECT rating FROM ' . $table . ' WHERE post_id=%d', $post_id )
        );
        $total = 0;

        foreach ( $votes as $vote )
            $total += $vote->rating;

        $avg = number_format( $total / $count, 2 );
        update_metadata( 'post', $post_id, 'kdm_ratings', $avg );

        echo round( $avg );
    }

    die();
}

Desativação do Plugin

Seguindo a mesma lógica de verificar a existência da tabela e definir valores default para as configurações do plugin, caso seja do seu interesse, você pode aplicar o gancho de desativação de plugins de modo a remover as alterações propostas pelo seu código. Faça isso no plugin de votação apagando a tabela personalizada, os valores auxiliares dos posts e também as opções criadas:

register_deactivation_hook( __FILE__, array( 'KDM_Ratings', 'deactivate' ) );
// ...
function deactivate()
{
    global $wpdb;
    $table = $wpdb->prefix . 'postratings';

    $wpdb->query(
        $wpdb->prepare( 'DROP TABLE %s', $table )
    );

    $wpdb->query(
        "DELETE FROM {$wpdb->postmeta} WHERE meta_key='kdm_ratings'"
    );

    delete_option( 'kdm_ratings' );
}

Shortcode API

Entre os requisitos do plugin de votação foi determinado que sua utilização poderia se dar de modo automático, assim como você acabou de implementar; ou de diferentes maneiras manuais. Uma delas é através da aplicação de Shortcodes.

Carazteriza-se como Shortcode o uso de palavras específicas delimitadas por colchetes que proporcionam uma alteração de comportamento de um trecho do texto ou então a execução de certas ações relacionadas a ele.

Os shortcodes do WordPress podem ser aplicados no Editor de textos (em ambos os modos, Visual ou HTML), em Widgets ou via programação. O principal exemplo de uso de um shortcode é o recurso galeria de imagens do próprio WordPress, cuja aplicação se dá através de gallery e seus atributos.

[ gallery columns="3" ids="1,2,3" orderby="rand" ]

Um shortcode pode conter atributos que determinam seu funcionamento. No caso da galeria demonstrada, os atributos definem a exibição das imagens com os referidos ids, divididas em três colunas e por ordem aleatória. Além dos atributos, o uso de shortcodes pode ser usado para delimitar conteúdo.

[logged]Texto exibido apenas para usuários logados![/logged]

Através do shortcode fictício logged poderia ser verificado se o visitante do site estaria logado; em caso afirmativo exibiria a mensagem privada, do contrário nenhuma informação seria exibida.

Criação de um Shortcode

O processo de criação de um shortcode é definido pela função add_shortcode e seus dois parâmetros: No primeiro deles você escolhe qual o termo do shortcode e no outro a função de callback.

add_shortcode( 'KDM_Ratings', array( 'KDM_Ratings', 'code' ) );

Desse modo o método code será chamado toda vez que for utilizado o shortcode KDM_Ratings.

[KDM_Ratings]

Definição de atributos

Ofereça aos utilizadores do plugin as mesmas configurações definidas na página de opções, também no shortcode; permita a eles optarem por exibir um título diferenciado, o contador de votos ou a média da votação realizada. Para isso você precisa fazer uso dos atributos do shortcode.

[KDM_Ratings titulo="Avaliação" votos="true" media="true"]

No escopo do método responsável pelo processamento do shortcode recupere os valores dos atributos com shortcode_atts. Passe como primeiro parâmetro os valores padrão de configuração do recurso. A função se encarregará de sobrepor os valores com as informações recebidas dos atributos.

function code( $atts )
{
    extract( shortcode_atts( array(
        'votos' => 'false',
        'media' => 'false',
        'titulo' => ''
    ), $atts ) );
    $opt = array(
        'votes' => $votos,
        'avg' => $media,
        'title' => $titulo
    );
    return self::stars( null, $opt );
}

Conteúdo do Shortcode

Outro modelo para aplicação dos shortcodes, como introduzido acima, é a delimitação de conteúdo. Ao optar por receber o título do formulário através dessa técnica, trabalhe com a passagem de dois parâmetros para a função de retorno do shortcode; essa segunda informação é justamente o conteúdo delimitado.

[KDM_Ratings votos="true" media="true"] Avaliação[/KDM_Ratings]

function code( $atts, $content ) 
{
    extract( shortcode_atts( array(
        'votos' => 'false',
        'media' => 'false'
    ), $atts ) );
    $opt = array(
        'votes' => $votos,
        'avg' => $media,
        'title' => $content
    );
    return self::stars( null, $opt );
}

Execução do shortcode

Há ainda a possibilidade de você desejar o emprego do shortcode em algum lugar no tema, que não seja através de ganchos ou da inserção via editor de textos; mas sim via código mesmo. Através da função do_shortcode você consegue fazer isso.

<?php echo do_shortcode( '[KDM_Ratings]' ); ?>

Widget API

Operar Widgets dentro do WordPress é um método de integração bem aceito, principalmente por aqueles com pouca familiariedade com códigos e que procuram facilidade de uso nos recursos escolhidos. Os Widgets, assim como os shortcodes, podem realizar suas rotinas através de instruções fixas ou com base nas configurações de seus usuários.

Sempre que optar por oferecer seu plugin com esse modelo de integração, escolha um meio para avisar a seu usuário que é necessária a presença de uma área reservada para receber Widgets dentro do Tema. Pode acontecer de um Tema não possuir tal suporte, nesse caso você pode realizar o registro de uma nova área com register_sidebar e posteriormente mostrar os Widgets dessa área no site com dynamic_sidebar.

Widget com opções personalizadas do plugin de votação de posts

Widget com opções personalizadas do plugin de votação de posts

Criação de um Widget

O WordPress disponibiliza a Widget API para manipulação desses recursos. O melhor método para a criação de um Widget é com o uso estendido da classe WP_Widget. Atribua no método de setup da classe um callback para o momento no qual os Widgets são carregados através da action ‘widgets_init’; e a partir do novo método criado, identifique o registro de seu Widget personalizado.

add_action( 'widgets_init', array( 'KDM_Ratings', 'widgets' ) );

function widgets()
{
    register_widget( 'KDM_Ratings_Widget' );
}

Como será preciso a criação de uma nova classe, prefira a inserção de um novo arquivo ao invés de aglomerar todo seu código no index.php do plugin.

require_once( CPR_PATH . 'cpr-widget.php' );

WP_Widget

A nova classe deverá estender a WP_Widget pois ela oferece todos os recursos de opções, atualização e também apresentação das informações de modo prático. Defina um construtor para a classe passando como parâmetros um identificador único para seu Widget e um título que será apresentado no Dashboard.

class KDM_Ratings_Widget extends WP_Widget
{
    public function KDM_Ratings_Widget()
    {
        parent::WP_Widget( 'cpr-widget', 'Post Ratings' );
    }
}

Comece definindo quais opções pretende fornecer, quais fatores serão sucetíveis a configuração. A proposta do Widget consiste em listar os posts mais bem votados, portanto é interessante permitir a alteração de dois itens cruciais: o título da lista e a quantidade de resultados.

public function form( $inst ) {
    if ( $inst )
        $opt = array(
            'title' => esc_attr( $inst[ 'title' ] ),
            'count' => (int) $inst[ 'count' ]
        );
    else
        $opt = array(
            'title' => 'Melhores votados',
            'count' => 5
        );
    ?>
    <p><label>Título: <input name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo $opt[ 'title' ]; ?>" /></label></p>
    <p><label>Qtd. de posts: <input name="<?php echo $this->get_field_name( 'count' ); ?>" type="text" value="<?php echo $opt[ 'count' ]; ?>" /></label></p>
    <?php
}

O método update de atualização das opções de WP_Widget apenas retorna e salva as novas instâncias configuradas. No entanto você pode realizar suas próprias verificações sobrescrevendo esse método na classe que criou.

public function update( $new, $old )
{
    return array_merge( $old, $new );
}

Por fim crie um método denominado widget para executar suas intruções na parte pública do site, ou seja, as rotinas aqui inseridas serão executadas e apresentadas nas áreas de widgets do Tema.

function widget( $args, $inst )
{
    $opt = array(
        'meta_key' 		=> $this->prefix . 'ratings',
        'posts_per_page'=> (int) $inst[ 'count' ],
        'orderby'		=> 'meta_value_num',
        'order'			=> 'DESC'
    );

    echo $args[ 'before_widget' ];
    echo $args[ 'before_title' ] . $inst[ 'title' ] . $args[ 'after_title' ];

    $q = new WP_Query( $opt );
    if ( $q->have_posts() ) {
        while ( $q->have_posts() ) {
            $q->the_post();
            global $post;
            $title = esc_attr( get_the_title() );
            echo '<li><a href="' . get_permalink() . '" title="' . $title . '">' . $title . '</a> ['
                . get_post_meta( $post->ID, $this->prefix . 'ratings', true ) . ']</li>';
        }
    } else {
        echo '<li>Nenhum post foi votado...</li>';
    }

    echo $args[ 'after_widget' ];
}

O parâmetro $args receberá as definições da sidebar registrada, já $inst possui os valores das opções personalizadas do Widget.

Para não esquecer

  • Usuários possuem diferentes níveis de acesso
  • Dados válidos garantem o correto funcionamento do plugin
  • Utilização da base de dados com tabelas existentes e próprias
  • Diferentes métodos de uso do plugin