Pular para o conteúdo

Portabilidade de Plugins do WordPress

Muitos plugins do WordPress podem ser portados para o EmDash. O modelo de plugin é diferente — TypeScript em vez de PHP, hooks em vez de ações/filtros, armazenamento estruturado em vez de wp_options — mas a maior parte da funcionalidade mapeia de forma limpa.

Nem todos os plugins fazem sentido portar. Avalie os candidatos antes de começar.

Bons candidatos

Campos personalizados, plugins de SEO, processadores de conteúdo, extensões de UI administrativa, análises, compartilhamento social, formulários

Maus candidatos

Funcionalidades de Multisite, integrações WooCommerce/Gutenberg, plugins que corrigem os internos do núcleo do WordPress

wp-content/plugins/my-plugin/
├── my-plugin.php # Main file with plugin header
├── includes/
│ ├── class-admin.php
│ └── class-api.php
└── admin/
└── js/

O WordPress usa add_action() e add_filter() com nomes de hook em string. O EmDash usa hooks tipados declarados na definição do plugin.

WordPressEmDashNotas
register_activation_hook()plugin:installExecuta uma vez na primeira instalação
Plugin ativadoplugin:activateExecuta quando ativado
Plugin desativadoplugin:deactivateExecuta quando desativado
register_uninstall_hook()plugin:uninstallevent.deleteData indica escolha do usuário
WordPressEmDashNotas
wp_insert_post_datacontent:beforeSaveRetorna conteúdo modificado ou lança para cancelar
save_postcontent:afterSaveEfeitos colaterais após salvar
before_delete_postcontent:beforeDeleteRetorna false para cancelar
deleted_postcontent:afterDeleteLimpeza após exclusão
php
add_action('save_post', function($post_id, $post, $update) {
if ($post->post_type !== 'product') return;
$price = get_post_meta($post_id, 'price', true);
if ($price > 1000) {
update_post_meta($post_id, 'is_premium', true);
}
}, 10, 3);
WordPressEmDashNotas
wp_handle_upload_prefiltermedia:beforeUploadValidar ou transformar
add_attachmentmedia:afterUploadReagir após upload
php
$api_key = get_option('my_plugin_api_key', '');
update_option('my_plugin_api_key', 'abc123');
delete_option('my_plugin_api_key');

Tabelas Personalizadas → Coleções de Armazenamento

Seção intitulada “Tabelas Personalizadas → Coleções de Armazenamento”
php
global $wpdb;
$table = $wpdb->prefix . 'my_plugin_items';
// Inserir
$wpdb->insert($table, ['name' => 'Item 1', 'status' => 'active']);
// Consultar
$items = $wpdb->get_results(
"SELECT \* FROM $table WHERE status = 'active' LIMIT 10"
);

O WordPress usa a API de Configurações para formulários administrativos. O EmDash usa um esquema declarativo que gera a UI automaticamente.

php
add_action('admin_init', function() {
register_setting('my_plugin', 'my_plugin_api_key');
add_settings_section('main', 'Settings', null, 'my-plugin');
add_settings_field('api_key', 'API Key', function() {
$value = get_option('my_plugin_api_key');
echo '<input type="text" name="my_plugin_api_key"
value="' . esc_attr($value) . '">';
}, 'my-plugin', 'main');
});

As páginas administrativas do WordPress são em PHP. O EmDash usa componentes React.

tsx title="src/admin.tsx"
import { useState, useEffect } from "react";
export const widgets = {
summary: function SummaryWidget() {
const [count, setCount] = useState(0);
useEffect(() => {
fetch("/_emdash/api/plugins/my-plugin/status")
.then((r) => r.json())
.then((data) => setCount(data.count));
}, []);
return <div>Total de itens: {count}</div>
;
},
};
export const pages = {
settings: function SettingsPage() {
// React component for settings page
return <div>Settings content</div>
;
},
};

Registre na definição do plugin:

typescript title="src/index.ts"
admin: {
entry: "@my-org/my-plugin/admin",
pages: [{ path: "/settings", label: "Dashboard" }],
widgets: [{ id: "summary", title: "Summary", size: "half" }],
},
php
register_rest_route('my-plugin/v1', '/items', [
'methods' => 'GET',
'callback' => function($request) {
global $wpdb;
$items = $wpdb->get_results("SELECT * FROM items LIMIT 50");
return new WP_REST_Response($items);
},
]);

As rotas estão disponíveis em /_emdash/api/plugins/{plugin-id}/{route-name}.

  1. Analise o plugin do WordPress

    Documente o que ele faz: hooks, operações de banco de dados, páginas administrativas, endpoints REST.

  2. Mapeie para os conceitos do EmDash

    Hooks do WordPress → Hooks do EmDash. wp_options → ctx.kv. Tabelas personalizadas → Coleções de armazenamento. Páginas administrativas → Componentes React. Endpoints REST → Rotas de plugin.

  3. Crie o esqueleto do plugin

    src/index.ts
    import { definePlugin } from "emdash";
    export function createPlugin() {
    return definePlugin({
    id: "my-ported-plugin",
    version: "1.0.0",
    capabilities: [],
    storage: {},
    hooks: {},
    routes: {},
    admin: {},
    });
    }
  4. Implemente em ordem

    Armazenamento → Hooks → UI Administrativa → Rotas

  5. Teste minuciosamente

    Verifique se os hooks são acionados corretamente, se o armazenamento funciona e se a UI administrativa é renderizada.

php
add_filter('wp_insert_post_data', function($data, $postarr) {
if ($data['post_type'] !== 'post') return $data;
$content = strip_tags($data['post_content']);
$word_count = str_word_count($content);
$read_time = ceil($word_count / 200);
if (!empty($postarr['ID'])) {
update_post_meta($postarr['ID'], '_read_time', $read_time);
}
return $data;
}, 10, 2);

Os plugins devem declarar as capacidades necessárias para o sandbox de segurança:

CapacidadeForneceCaso de Uso
network:fetchctx.http.fetch()Chamadas de API externas
read:contentctx.content.get(), list()Leitura de conteúdo do CMS
write:contentctx.content.create(), etc.Modificação de conteúdo
read:mediactx.media.get(), list()Leitura de mídia
write:mediactx.media.getUploadUrl()Upload de mídia

Sem estado global — Use armazenamento em vez de variáveis globais.

Tudo assíncrono — Sempre use await em chamadas de armazenamento e API.

Sem SQL direto — Use coleções de armazenamento estruturado.

Sem sistema de arquivos — Use a API de mídia para arquivos.