Configurar o WordPress Multisite, também conhecido como WordPress MU ou simplesmente WordPress Multiblog, pode em alguns casos ser uma dor de cabeça devido principalmente às configurações dos servidores web que servem os pedidos ao WordPress. Nesta primeira parte da série iremos abordar como facilmente ligar e instalar a opção Multisite do WordPress tentando abordar todo o tipo de configurações que o seu servidor possa deter incluindo diferentes ambientes e servidores. Nesta parte não vamos ainda olhar as soluções para montar ou escalar uma rede com muitos blogs – estamos ainda a dar os passos iniciais!

Durante esta série de tutoriais iremos:

  • Ensinar como ligar facilmente a opção de Multisite no WordPress e o que fazer após a instalação do Multisite concluída;
  • Abordar soluções de plugins testados que podem ajudá-lo no desenvolvimento do seu projecto para criar facilmente a sua rede de blogues;
  • Desenvolver snippets de códigos e mostrar o “canivete suíço” de funções exclusivas do WordPress Multisite;
  • Abordar várias soluções de configuração e ambientes de trabalho: desde o servidor web Apache ao servidor Nginx, passando por outras soluções de routeamento estático tais como o servidor lighttpd;
  • Abordar as soluções de cache em memória, usando memcached ou APC, assim como soluções de cache persistente estáticas em disco, para acelerar a sua rede e evitar ligações perdidas;
  • Aumentar o poder da sua instalação WordPress Multisite de modo a poder aceitar mais ligações simultâneas sem perda de performance dos seus sites;
  • Escalar a sua rede para usar multiplas bases de dados e aumentar o poder da sua rede;
  • Como estruturar e manter uma rede com mais de 1 milhão de blogues;
  • Mostrar soluções encontradas para problemas com o Multisite;
  • Desenvolver teoricamente configurações de rede Multisite para várias soluções de mercado;

PARTE IV – PLUGINS, SNIPETS DE CÓDIGO E FUNÇÕES WPMU

Na primeira parte desta série abordámos questões relacionadas com a instalação do WordPress multisite em vários ambientes e configurações. Na segunda parte falámos sobre como aumentar a performance do servidor e na terceira parte falámos sobre o banco de dados do multisite.

Nesta quarta e penúltima parte desta série, iremos abordar soluções de plugins, snippets de código e desvendar várias funções nativas do WordPress Multisite para uso no desenvolvimento da sua rede.

LISTA DE FUNÇÕES NATIVAS E SNIPPETS DE CÓDIGO

Uma instalação multisite requer que por vezes o desenvolvedor tenha que criar algo específico para a rede. Em muitos casos é necessário conhecer-se algumas das funções mais importantes nativas do WordPress Multisite. Deixamos uma lista de funções importantes.

Uma nota de extrema importância é que, visto que a maior parte destas funções consomem imensos recursos, você deve ter sempre atenção no seu uso pois pode fazer diminuir bastante a performance do site quando usadas em excesso.

Variável global com o identificador do blog

global $blog_id;

Esta variável mostra o ID do blog atual que estamos a aceder. É de extrema importância pois na maior parte dos casos é com esta variável que vamos ter que lidar.

global $current_site;

Esta variável global é um objeto com várias propriedades do site atual em que você está trabalhando.

Obter e atualizar detalhes de um blog da rede

$blog = get_blog_details( $blog_id );

Obtém-se todos os detalhes do blog com o ID que se pretende. Neste caso estamos a pedir detalhes do blog atual. A função retorna um array com os campos presentes nas tabelas wp_blogs e wp_blogmeta.

E como esta função cria um array que permanece na cache, podemos usar a função seguinte para limpar a cache de detalhes do blog, possibilitando-nos obter detalhes mais atualizados.

refresh_blog_details( $blog_id );

Esta função, em combinação com a anterior, pode ser usada quando, por exemplo, um processo automático atualiza os detalhes de um blog e você necessita confirmá-los:

update_blog_details( $blog_id, array(
	'site_id' => 1,
	'domain' => 'dominio-qualquer.com,
	'path' => '/',
	'registered' => '2012-06-21 22:03',
	'last_updated' => '2012-06-21 22:03',
	'public' => '1',
	'archived' => '0',
	'mature' => '0',
	'spam' => '0',
	'deleted' => '0',
	'lang_id' => ''
	)
);

Marcando archived como true, faz com que o blog não seja visualizável.

Mudando entre sites programaticamente

Como mostrar posts de um outro blog que não o atual? Imagine que você está a criar um widget que pretende mostrar os últimos posts do site principal. O WordPress já trás consigo uma função que possibilita fazer isso:

switch_to_blog( $blog_id, false );

Pode agora aceder à variável global $current_site chamando a função:

global $current_site;
$current_site = get_current_site();

Agora você poderia fazer uma query pelos últimos 5 posts do blog que você está trabalhando:

$ultimos_posts = get_posts( array( 'numberposts' => 5 ) );

Para voltar ao blog atual, basta chamar a função para retornar ao processo normal:

restore_current_blog();

Obtendo um post especifico de um determinado blog

Na verdade, você não necessitaria de usar o trecho de código apresentado em cima para obter um post especifico de um blog. Bastava para isso usar:

$blog_post = get_blog_post( $blog_id, $post_id );

Obtendo os blogs em que um usuário está inscrito:

$user_blogs = get_blogs_of_user( $user_id, false );

O último parâmetro desta função serve para você optar se quer ou não que também seja retornado os blogs marcados como spam, deleted e archived.

Comparar se o blog está arquivado

É possível apenas chamar uma função para se saber se um determinado blog da rede está arquivado, ou seja, inacessível ao público.

Isto é de extrema importância se você quiser criar um painel informativo com todos os sites da sua rede, por exemplo:

if ( is_archived( (int) $blog_id ) )
	echo 'Este blog está arquivado!';
else
	echo 'Este blog está acessível';

Com este snippet você poderá fazer o que quiser e adaptá-lo às suas necessidades. Caso queira guardar um blog como arquivado poderá usar a função seguinte para o fazer:

update_archived( $blog_id, '1' );

Metadata e opções do blog

Como o método switch_blog() pode ser bastante consumidor de recursos, existe uma função que nos facilita a vida para tirar valores da tabela de opções de um determinado blog:

$blog_home_url = get_blog_option( $blog_id, 'home_url', false );
$blog_site_url = get_blog_option( $blog_id, 'site_url', false );
$opccao = get_blog_option( $blog_id, 'opccao_qualquer', false );
...

Para além das várias opções disponíveis nativamente, é possível criar um conjunto de opções comuns a todos os blogs. Desta forma é possível manter dados importantes que podem servir para comparar versões dos blogs ou se é necessário realizar um upgrade. Um exemplo prático seria, por exemplo, após se definir um domínio exclusivo para um determinado blog, guardar na tabela de opções se o domínio está ativo ou não:

add_blog_option( $blog_id, 'dominio_ativo', '1' );

Podendo-se também eliminar essa opção:

delete_blog_option( $blog_id, 'dominio_ativo' );

Desta forma podemos manter os dados dos blogs todos atualizados fazendo uma busta massiva por todos os blogs da rede ou por critérios, e atualizar as suas opções sem sair do blog principal.

Recuperar o ID de determinado blog a partir do seu nome

A partir do subdomínio do blog, também é possível saber qual o ID do blog. Para isso usa-se a função:

$blog_id = get_id_from_blogname( 'subdominio.blog-da-rede.com' );

Adicionar um usuário a um blog

$user_id = get_current_user_id();
add_user_to_blog( $blog_id, $user_id, 'administrador' );

Este snipet adiciona um usuário pré-registado na rede a um blog através do ID do usuário. Poderá adicioná-lo em qualquer um dos grupos existentes na rede: administrador, editor, autor ou subscritor.

Adicionar um blog à rede programaticamente

Você pode, no seu código, programar a criação de blogs programaticamente. O WordPress trás algumas funções específicas que ajudam nesse processo:

$domain = 'um-subdominio.rede-de-blogs.com';
$path = '/';
if ( !domain_exists( $domain, $path, 1 ) ) {
	create_empty_blog( $domain, $path, 'Um novo blog' );
}

O trecho em cima instala um novo blog porém sem conteúdos alguns ou opções básicas definidas. Você poderia querer criar um blog pré-preenchido e um usuário ao mesmo tempo. Para isso você poderia usar estas duas funções em vez da função create_empty_blog:

$opcoes = array( ... );
$user_id = wpmu_create_user( $user_name, $password, $email );
wpmu_create_blog( $domain, $path, 'Um novo blog pré-preenchido', $user_id, $opcoes, 1 );

O array opcoes pode ser usado para incluir na tabela wp_options do blog qualquer opção que você queira colocar.

Confirmar se um determinado blog pertence ao usuário atual:

if ( is_blog_user( (int) $blog_id ) )
	echo 'Este blog pertence ao usuário.';
else
	echo 'AVISO: Este blog NÃO pertence ao usuário';

Este snippet mostra como é fácil implementar um trecho que verifique se o blog que o usuário está a visualizar pertence-lhe ou não.

Retornar os administradores de um determinado blog:

$domain = $_SERVER['SERVER_NAME'];
$path = '/';
$admins = get_admin_users_for_domain( $domain, $path );

Por último e não menos importante, Como saber se a instalação atual do WordPress tem a opção multisite ativa?

Usa-se a função is_multisite:

<?php if( is_multisite() ): ?>

A rede de blogs <?php echo esc_html( get_site_option( 'site_name' ) ); ?>
tem neste momento <?php echo get_blog_count(); ?> blogs e
<?php echo get_user_count(); ?> usuários.

<?php endif; ?>

MOSTRAR TODOS OS POSTS MAIS RECENTES NA REDE

Este snipet é bastante útil para quem quer desenvolver uma rede multisite. À semelhança daquilo que o WordPress.com faz na sua página principal, você também poderá fazer o mesmo, colocando este snipet no tema do seu blog principal.

1. Coloque o seguinte código no functions.php do seu tema:

/**
 * Lista os Posts recentes de uma rede multisite
 * @uses get_blog_list(), get_blog_permalink()
 * @param int $size Número de resultados a retirar
 * @param int $cache_posts_expires Número de segundos até a cache de posts expirar. Por defeito 2 horas.
 * @param int $cache_blogs_expires Número de segundos até a cache dos blogs expirar. Por defeito 24 horas.
 * @return array Array de objectos dos posts
 */ 

function wp_recent_across_network( $size = 10, $cache_posts_expires = 7200, $cache_blogs_expires = 86400 ) {
	if( !is_multisite() ) return false;  

	// Fazer cache dos resultados para acelerar o processo
	if ( ( $recent_across_network = get_site_transient( 'recent_across_network' ) ) === false ) {  

		// Nenhuma cache encontrada, vamos preparar a nossa query
		global $wpdb;
		$base_prefix = $wpdb->get_blog_prefix(0);
		$base_prefix = str_replace( '1_', '' , $base_prefix );  

		// Como a query para obter todos os sites da rede é intensiva, vamos também
		// fazer cache destes resultados
		if ( false === ( $site_list = get_site_transient( 'multisite_site_list' ) ) ) {
			global $wpdb;
			$site_list = $wpdb->get_results( $wpdb->prepare('SELECT * FROM wp_blogs ORDER BY blog_id') ); 

			set_site_transient( 'multisite_site_list', $site_list, $cache_blogs_expires ); 

		}  

		$limit = absint($size);  

		// Vamos juntar os resultados dos posts de cada blog com o seu correspondente ID, através
		// dar ordem UNION nativado MySQL
		foreach ( $site_list as $site ) {
			if ( $site == $site_list[0] ) {
				$posts_table = $base_prefix . "posts"; 

			} else {
				$posts_table = $base_prefix . $site->blog_id . "_posts"; 

			}  

			$posts_table = esc_sql( $posts_table );
			$blogs_table = esc_sql( $base_prefix . 'blogs' );  

			$query .= "(SELECT $posts_table.ID, $blogs_table.blog_id FROM $posts_table, $blogs_table\n";
			$query .= "\tWHERE $posts_table.post_type = 'post'\n";
			$query .= "\tAND $posts_table.post_status = 'publish'\n";
			$query .= "\tAND $blogs_table.blog_id = {$site->blog_id})\n";  

			if ( $site !== end($site_list) )
				$query .= "UNION\n";
			else
				$query .= "ORDER BY post_date DESC LIMIT 0, $limit"; 

		}  

		// Vamos preparar e chamar a query
		$query = $wpdb->prepare( $query );
		$return = $wpdb->get_results( $query ); 

		$recent_across_network = array();
		foreach ( $recent_posts_across_network as $post )
			$recent_across_network[] = get_blog_post( (int) $return[1], (int) $return[0] );

		set_site_transient( 'recent_across_network', $recent_across_network, $cache_posts_expires ); 

	}  

	return $recent_across_network;

}

2. Agora que temos a nossa função definida, podemos mostrar os posts mais recentes da rede em qualquer lugar:

<?php if ( $recent_network_posts = wp_recent_across_network() ) : foreach ( $recent_across_network as $post ) : ?>

<div id="post-<?php echo $post->ID; ?>" class="post">

	<a href="<?php echo $post->guid; // Mostra o link para o post ?>">
		<div class="entry-title">
			<?php echo $post->post_title; ?>
		</div>
		<div class="entry-date">
			<?php echo the_date( get_option( 'date_format' ), strtotime( $post->post_date ) ); // Retorna a data de criação do post ?>
		</div>
		<div class="entry-content">
			<?php // Caso não haja excerto, mostramos o conteúdo concatenado em 60 letras.
			if ( $post->excerpt ) echo $post->post_excerpt;
			else echo substr( $post->post_content, 0, 60 ) . '...';
			?>
		</div>
	</a>

</div>

<?php endforeach; endif; ?>

SOLUÇÕES DE PLUGINS TESTADOS

Jetpack by WordPress.com

Este plugin é um pack de vários códigos usados no WordPress.com e que facilita qualquer rede multisite a fornecer vários recursos aos usuários do seu blog sem muito esforço. Enumeramos algumas das principais opções: estatísticas do site e blog, subscrições dos posts por email, um widget para mostrar tweets recentes, um encurtador de links e um corretor ortográfico.

Download do plugin Jetpack by WordPress.com

WordPress MU Domain Mapping

Permite aos usuários de um blog colocarem o seu blog a responder por um outro domínio. Este método é chamado de mapeamento de domínios. A partir de agora os seus usuários podem usar um blog na sua rede com um domínio qualquer .com, .com.br, .pt ou qualquer outra extensão mundialmente conhecida.

Download do plugin WordPress MU Domain Mapping

Antispam Bee

Mate o Spam! é o slogan deste plugin. Bastante efetivo e versátil, com este plugin instalado na rede pode ter a certeza que os seus usuários vão adorar! Ele remove o Spam em comentários em quase 100%.

Download do plugin Antispam Bee

Unconfirmed

Numa rede multisite, os usuários pré-inscrevem-se num blog sendo que, depois devem clicar num link de ativação fornecido por email. Acontece que em muitos casos os usuários inscrevem-se e não confirmam, aumentando a informação do banco de dados. Um dos problemas do multisite é que não se consegue nativamente ver os usuários não confirmados.

Com este plugin você terá à disposição na consola de gestão da rede, uma página com a lista de todos os usuários não confirmados, podendo depois ativar ou remover dependendo dos casos.

Download do plugin Unconfirmed

User Photo

Já que estamos a falar de gestão de usuários, que tal permitir que eles personalizem a sua conta com um avatar? Este plugin permite que um usuário faça o upload de uma foto sua para o seu perfil.

Download do plugin User Photo

User Management Tools

Imagine que você pretendia adicionar vários usuários em simultâneo a um blog. Com este plugin você poderá fazer isso muito facilmente, escolhendo de uma lista de usuários quais os que pretende associar, dando a cada um grupo específico.

Download do plugin User Management Tools

Membership

E por último no tema “usuários”, este plugin criado pelos colegas da WPMU-Dev poderá dar-lhe um número bastante grande de possibilidades para gerir e monetizar os seus conteúdos, blogs e usuários. Esta versão é gratuíta, no entanto existe uma versão paga com mais recursos.

Download do plugin Membership

Log Deprecated Notices

Para quem é desenvolvedor de plugins e sites, este plugin pode ser muito útil. Como o WordPress é um software em constante atualização, por vezes funções são substituídas por outras, assim como variáveis globais deixam de ser necessárias. Quando o time do WordPress decide que uma função deve ser removida do software, essa função não é imediatamente apagada mas sim para um ficheiro do WordPress chamado deprecated.php. Com o tempo esse ficheiro vai sendo limpo de funções mais antigas. Com este procedimento, garante-se que uma atualização do WordPress não quebre plugins que estejam instalados e que usam dessas funções.

Este plugin regista numa tabela quais são as funções que estão marcadas para remoção e que estão a ser usadas por plugins ou temas num site. Embora para o comum dos usuários isto não seja muito importante, para os desenvolvedores este plugin mostra-se de bastante importância.

Download do plugin Log Deprecated Noticas

Espero que esta penúltima parte da série seja útil para a sua rede.

Até breve!