Suppose you have a lot of .doc, .docx, .xls, .xlsx, .gz, .bz2, .pdf and text in general (.csv, .txt etc.) files and want to dump all the (unique) email addresses from them. How would you do it? Here is a simple solution I’ve just implemented (and probably didn’t test enough, so tell me if you find any bug):
I wrote a small WordPress plugin to prevent users from publishing a post without excerpt or thumbnail, or with a too big excerpt, or with a too small thumbnail, or with an uppercase-only title.
This screenshot is showing some of the messages the plugin displays in portuguese.
For some of my websites it’s important to require the editors to fix some stuff before publishing something, and it looks like this is a useful feature for other people as well. There is even a Require Thumbnail plugin in the WordPress Plugin Directory that seems to do one of the things I’ve just implemented.
The plugin works with two different types of requirements: ths first generates errors (i.e., you can’t publish if you don’t fix it) and the second generates warnings (i.e., you will receive a message but you can proceed to publish if you really want to do that).
I thought of not releasing the plugin (because it’s written in Portuguese and you don’t have a cool interface to decide what’s required yet), but in a fashion of overstated bazaar I decided to push the code anyway (without putting in the WordPress plugin directory, of course) so that other people can collaborate if they want to. Take a look :)
O WordPress é um dos meus programas preferidos e um dos que mais consome as madrugadas já faz alguns anos. Escrevi inúmeros temas, alguns plugins (um único genérico o suficiente para ser público) e já modifiquei algumas partes do código (embora hoje seja raro isso ser necessário).
Acho muito simples e me divirto ao programar em PHP para a web (talvez porque eu faça isso há uns dez anos). Gosto muito da forma como o WordPress é escrito e, com efeito, seu lema é Code is poetry — Código é poesia. Sua documentação e seu código são muito didáticos e foram, assim como seus temas e as tabelas no banco de dados, evoluindo de acordo com o tempo: as atualizações sempre têm novas features e formas mais genéricas e mais elegantes de fazer as coisas.
O WordPress é um software livre usado por mais de 15% da web (um número incrível!) e tem uma comunidade que produz várias extensões (temas e plugins). Gosto tanto dele que certa vez (no final de 2007) escrevi um plugin só para ganhar uma camiseta (que é tamanho G e ainda assim uso de vez em quando).
Propaganda e blogagem a parte, me deparei com um problema interessante em um dos sites que administro com ele no último fim de semana (a gente sempre se depara com problemas interessantes no WordPress também): dado uma categoria com várias subcategorias, gostaria de mostrar um link para o último post de cada uma de suas subcategorias na página inicial.
Uma solução trivial seria fazer uma query pedindo os filhos de uma dada categoria (usando a tabela wp_term_taxonomy) e uma query por categoria para descobrir seu último post (usando as tabelas wp_posts e wp_term_relationships). Suponha (até o final desse post) que a categoria-mãe de todas as categorias que eu quero mostrar na página seja a de ID 33. Então, essa solução seria algo como:
<?php$query=mysql_query("SELECT term_id FROM wp_term_taxonomy
WHERE parent = 33 AND taxonomy = 'category'");while(list($cid)=mysql_fetch_row($query)){$posts_query=mysql_query("SELECT p.ID, p.post_name, p.post_title
FROM wp_posts AS p,
wp_term_relationships AS r
WHERE p.ID = r.object_ID AND r.term_taxonomy_id = '$cid'
ORDER BY p.post_date DESC
LIMIT 1");if(mysql_num_rows($posts_query)){list($id,$permalink,$title)=mysql_fetch_row($posts_query);// Faça o que quiser com o post aqui}else{echo"A categoria $cid não tem posts.\n";}}?>
Não deve ser difícil de entender, mas dá pra resolver de forma ainda mais simples que essa. O WordPress é fantástico e usar as funções dele próprio é bem mais simples, genérico e resolve o problema. A função get_categories aceita um monte de parâmetros, mas só precisamos do child_of:
<?phpforeach(get_categories('child_of=33')as$cat){list($post)=get_posts("numberposts=1&category={$cat->term_id}");// Faça o que quiser com o post aqui}?>
(quem entrou no post procurando a solução pro problema pode parar por aqui se não for nerd)
Porém, eu queria resolver o problema com uma única query. Achei que seria mais elegante resolver o problema todo no banco de dados sem escrever em PHP e achei que poderia ficar mais rápido. Acho que não ficou mais elegante e não faço ideia se fica mais rápido (fiquei com a impressão de que seja pior porque faço JOIN de quatro tabelas enormes), nem acho que tenha volume de dados (ainda) no site em que implementei isso pra realmente me preocupar, mas me diverti fazendo. Então segue o resultado:
SELECT p.ID AS id,
CASE WHEN (p.post_date > DATE_SUB(CURDATE(), INTERVAL 1 MONTH)) THEN
p.post_title
ELSE
''
END AS title,
GROUP_CONCAT(c.slug) AS cat
FROM wp_posts AS p
INNER JOIN
(
SELECT MAX(p.post_date) AS post_date, c.term_ID AS cid, COUNT(p.ID) AS count FROM
wp_posts AS p,
wp_term_relationships AS r,
wp_terms AS c
WHERE
p.ID = r.object_ID AND
c.term_ID = r.term_taxonomy_ID AND
p.post_status = 'publish' AND
p.post_type = 'post'
GROUP BY c.term_id
) AS last ON p.post_date = last.post_date
INNER JOIN wp_term_relationships AS r ON p.ID = r.object_ID
INNER JOIN wp_terms AS c ON c.term_id = r.term_taxonomy_ID
INNER JOIN wp_term_taxonomy AS t ON t.term_id = r.term_taxonomy_ID
WHERE
c.term_id = last.cid
AND t.parent = 33
AND t.taxonomy = 'category'
AND p.post_status = 'publish'
AND p.post_type = 'post'
# AND last.count >= 3
GROUP BY p.ID ORDER BY p.post_date DESC, last.count DESC;
A query (que na verdade é duas) ordena o resultado por data, retorna o título vazio caso o post seja de mais de um mês atrás, junta as categorias (separando-as por vírgula) se um mesmo post for o último de mais de uma categoria e neste caso ordena as categorias por ordem decrescente de número de posts na mesma.
(A parte comentada seria para caso eu só quisesse mostrar o último post de categorias com três ou mais posts.)
(e se você quiser vê-lo em prática, entre em juntos.org.br e procure por “Juntos pelo Brasil”)
Não ficou bonitinho? Se por um lado gostei da solução, por outro fiquei imaginando que deva ser um SQL tremendamente ingênuo e digno da minha inexperiência com grandes bancos de dados. O que você acha? Consegue pensar numa forma mais simples, mais eficiente e mais elegante de resolver o mesmo problema? Ou ao menos sem subqueries?
Acho que as relações necessárias já ficaram explícitas na query que eu escrevi, mas segue o diagrama do banco de dados do WordPress pra quem precisar:
I’ve just wrote my first WordPress public plugin, that I’m licensing under GPL v3. This post is to make it public. I’m writing in English because the WordPress plugin directory asks me to provide a plugin page to host the files there and I’ll provide the URL of this post. Update: The plugin is now in the WordPress plugin directory: wordpress.org/extend/plugins/retrospective/
The website of the brazilian newspaper O Estado de São Paulo has a nice way to display news in a retrospective-style (check this screenshot or this link — Flash required).
Wouldn’t it be nice if we could display WordPress posts in our pages and categories in the same way just by using a shortcode? The possibilities are many. That’s why I wrote the Retrospective plugin for WordPress.
It has at least two advantages over the version you just saw:
Does not require Flash (its implementation uses only CSS and JavaScript/jQuery)
Has a option to respect the (time-)scale of the posts.
Its use is very simple. Wherever you add the shortcode [retrospective] the plugin will draw that cool retrospective. The shortcode supports several attributes:
count — limit the number of posts to be displayed (default = 10; use -1 to display all)
cat — display posts with category IDs comma-separated (default = display all posts)
width — the width of the timeline in pixels (default = 600)
delay — the time of the focus change animation in milisseconds (default = 1000)
scale — if set, respect the time scale in the distances between the points in the timeline (default = false)
image_width, image_height — the dimensions of the thumbnail images in pixels (default = 300×180)
image_border_size — the size of the image’s border in pixels (default = 7)
image_border_color — the color of the image’s border in hexa RGB (default = 000000)
image_margin — the space between the images (default = 5)
date_format — the date format in PHP format (default = d/m/Y)
Some screenshots
Here is a screenshot from juntos.org.br with scale=true (in the link you can see it working):
And here is a screenshot from a fresh WordPress install (TwentyEleven theme without modifications):
Customizing
Post thumbnails
For better results, I suggest always adding post thumbnails to your posts and using registered image sizes in image_width and image_height attributes.
Styling retrospectives
The generated HTML is very easy to style (but just be careful with margins and paddings, they’re set with !important attribute — I did it to try not to break with any theme). Here is a sample:
The generated hash takes in consideration all the attributes sent to the shortcode and also how many retrospectives appeared before in the parsing of the actual page. I made it that way to allow users to set up two exactly equal retrospectives in the same page. Because of that, I don’t recommend setting styles for #retro-uniquehash. I think a reasonable solution for this issue is to make add an outer container.
* Warning: Please consider I’m using a bazaar approach here. Be aware that the plugin probably has a lot of bugs (and please tell me if you catch any).
I hope you enjoy it. Have fun and please let me have your feedback! :)
Já faz algum tempo que o Facebook tem um bug nas listas de membros dos grupos. As listas não mostram todos os membros do grupo. Mais: os membros que não aparecem na lista nem mesmo podem ser encontrados pelo formulário de busca de nomes. Quando você é administrador de um grupo, isso pode ser um grande inconveniente, porque na interface padrão do Facebook a lista de membros é o único lugar onde é possível excluir usuários de um grupo. Ou seja, os usuários que não aparecem lá são indeletáveis!
Há alguns meses, escrevi um user script para o GreaseMonkey (extensão para o Firefox que permite que você crie esses pequenos scripts para serem rodados em páginas específicas) que busca ajudar os administradores de grupos a removerem esses membros fantasmas.
Não havia publicado até agora por vergonha (o script é bem feio, tanto o código como o resultado). Porém, acho que não pretendo perfumá-lo tão cedo, então resolvi liberá-lo aqui até pra inspirar quem quiser fazer coisas mais bonitas.
O funcionamento dele é muito simples: quando você entra num grupo (sendo ou não administrador — porque nem distingui isso no código), ele cria botões X do lado dos links para os perfis dos usuários que estão nessa página (e só nesses — outra coisa que nem me preocupei no script). Se você for administrador do grupo em questão, quando clica no X o Facebook abre aquela caixa perguntando se você quer mesmo excluir o membro em questão (e se quer baní-lo permanentemente).
Sem mais enrolação, eis aqui o código para (des-)apreciação e aprimoramentos (por favor! :)):
// ==UserScript==// @name Remove users from Facebook groups// @description Remove usuarios que postam no grupo sem precisar procurar na lista de membros// @author Tiago Madeira <madeira@juntos.org.br>// @include http*://www.facebook.com/groups/*// @version 1.32// ==/UserScript==(function(){varas= document.getElementsByTagName("a");var gid ="";for(var i =0; i <as.length; i++){var a =as[i];var hovercard = a.getAttribute("data-hovercard");if(hovercard !=null&& hovercard !=""){
uid = hovercard.replace(/.*id=/,"");if(gid !=""){var button ='<a class="mhm auxiliaryButton closeButton uiCloseButton" '+'title="Remove" rel="dialog-post" '+'href="/ajax/groups/members/remove.php?group_id='+
gid +"&uid="+
uid +'"></a>';
a.innerHTML = a.innerHTML +" "+ button;}}var pattern =newRegExp("(^| )groupsCleanProfilePic( |$)");if(pattern.test(a.className)){
gid = a.getAttribute("href").replace(/.*id=/,"");}}})();
Ou o link direto para download (ou instalação no GreaseMonkey):