From 77c33e4d968b79e50c211212527724e1327d0f77 Mon Sep 17 00:00:00 2001 From: Emanuel Almeida Date: Wed, 28 Oct 2026 16:09:35 +0000 Subject: [PATCH] feat(perfex-dev): adiciona plugin perfex-dev (agents, skills, knowledge e metadados do projeto) --- perfex-dev/.claude-plugin/plugin.json | 12 + perfex-dev/AGENTS.md | 17 ++ perfex-dev/CHANGELOG.md | 5 + perfex-dev/MEMORY.md | 4 + perfex-dev/README.md | 3 + perfex-dev/STATUS.md | 8 + .../agents/perfex-crm-module-developer.md | 169 ++++++++++++ perfex-dev/desk.project | 6 + perfex-dev/index.md | 8 + perfex-dev/knowledge/datasets.json | 18 ++ perfex-dev/log.md | 1 + perfex-dev/skills/AGENTS.md | 17 ++ perfex-dev/skills/CHANGELOG.md | 5 + perfex-dev/skills/MEMORY.md | 4 + perfex-dev/skills/README.md | 3 + perfex-dev/skills/STATUS.md | 8 + perfex-dev/skills/desk.project | 6 + perfex-dev/skills/index.md | 8 + perfex-dev/skills/log.md | 1 + perfex-dev/skills/perfex-controllers/SKILL.md | 208 +++++++++++++++ perfex-dev/skills/perfex-database/SKILL.md | 213 ++++++++++++++++ perfex-dev/skills/perfex-forms/SKILL.md | 225 ++++++++++++++++ .../references/elementos-formulario.md | 159 ++++++++++++ .../references/formulario-completo-exemplo.md | 141 ++++++++++ perfex-dev/skills/perfex-hooks/SKILL.md | 180 +++++++++++++ perfex-dev/skills/perfex-menus/SKILL.md | 223 ++++++++++++++++ perfex-dev/skills/perfex-migrations/SKILL.md | 211 +++++++++++++++ .../skills/perfex-module-basics/SKILL.md | 215 ++++++++++++++++ perfex-dev/skills/perfex-permissions/SKILL.md | 208 +++++++++++++++ perfex-dev/skills/perfex-security/SKILL.md | 240 ++++++++++++++++++ perfex-dev/skills/perfex-views/SKILL.md | 196 ++++++++++++++ 31 files changed, 2722 insertions(+) create mode 100644 perfex-dev/.claude-plugin/plugin.json create mode 100644 perfex-dev/AGENTS.md create mode 100644 perfex-dev/CHANGELOG.md create mode 100644 perfex-dev/MEMORY.md create mode 100644 perfex-dev/README.md create mode 100644 perfex-dev/STATUS.md create mode 100644 perfex-dev/agents/perfex-crm-module-developer.md create mode 100644 perfex-dev/desk.project create mode 100644 perfex-dev/index.md create mode 100644 perfex-dev/knowledge/datasets.json create mode 100644 perfex-dev/log.md create mode 100644 perfex-dev/skills/AGENTS.md create mode 100644 perfex-dev/skills/CHANGELOG.md create mode 100644 perfex-dev/skills/MEMORY.md create mode 100644 perfex-dev/skills/README.md create mode 100644 perfex-dev/skills/STATUS.md create mode 100644 perfex-dev/skills/desk.project create mode 100644 perfex-dev/skills/index.md create mode 100644 perfex-dev/skills/log.md create mode 100644 perfex-dev/skills/perfex-controllers/SKILL.md create mode 100644 perfex-dev/skills/perfex-database/SKILL.md create mode 100644 perfex-dev/skills/perfex-forms/SKILL.md create mode 100644 perfex-dev/skills/perfex-forms/references/elementos-formulario.md create mode 100644 perfex-dev/skills/perfex-forms/references/formulario-completo-exemplo.md create mode 100644 perfex-dev/skills/perfex-hooks/SKILL.md create mode 100644 perfex-dev/skills/perfex-menus/SKILL.md create mode 100644 perfex-dev/skills/perfex-migrations/SKILL.md create mode 100644 perfex-dev/skills/perfex-module-basics/SKILL.md create mode 100644 perfex-dev/skills/perfex-permissions/SKILL.md create mode 100644 perfex-dev/skills/perfex-security/SKILL.md create mode 100644 perfex-dev/skills/perfex-views/SKILL.md diff --git a/perfex-dev/.claude-plugin/plugin.json b/perfex-dev/.claude-plugin/plugin.json new file mode 100644 index 0000000..d871ad4 --- /dev/null +++ b/perfex-dev/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "perfex-dev", + "description": "Perfex CRM module development - controllers, views, forms, hooks, menus, migrations, permissions, security and database. Backed by NotebookLM notebooks.", + "version": "1.0.0", + "author": { + "name": "Descomplicar - Crescimento Digital", + "url": "https://descomplicar.pt" + }, + "homepage": "https://git.descomplicar.pt/ealmeida/descomplicar-plugins", + "license": "MIT", + "keywords": ["perfex", "crm", "modulos", "codeigniter", "desenvolvimento"] +} diff --git a/perfex-dev/AGENTS.md b/perfex-dev/AGENTS.md new file mode 100644 index 0000000..b2dc786 --- /dev/null +++ b/perfex-dev/AGENTS.md @@ -0,0 +1,17 @@ +# AGENTS.md — perfex-dev + +Directrizes específicas deste projecto para agentes IA. + +## Referências obrigatórias + +- `ECOSYSTEM.md` — Hub/06-Operacoes/Documentacao/ECOSYSTEM.md +- `PROC-DEV-STANDARD` — D7-SIS-006 +- `PROC-AUDIT-STANDARD` — D7-SIS-007 + +## Protocolo de sessão + +Início: ler MEMORY.md + STATUS.md. Fim: actualizar ambos. + +## Convenções específicas + +- (a definir) diff --git a/perfex-dev/CHANGELOG.md b/perfex-dev/CHANGELOG.md new file mode 100644 index 0000000..0bb631e --- /dev/null +++ b/perfex-dev/CHANGELOG.md @@ -0,0 +1,5 @@ +# CHANGELOG — perfex-dev + +## 0.1.0 — 2026-06-30 +### Adicionado +- Estrutura padrão (PROC-DEV-STANDARD) diff --git a/perfex-dev/MEMORY.md b/perfex-dev/MEMORY.md new file mode 100644 index 0000000..4b1aab0 --- /dev/null +++ b/perfex-dev/MEMORY.md @@ -0,0 +1,4 @@ +# MEMORY.md — perfex-dev + +## 2026-06-30 +- Estrutura padrão aplicada via propagate-structure.sh diff --git a/perfex-dev/README.md b/perfex-dev/README.md new file mode 100644 index 0000000..c28b19f --- /dev/null +++ b/perfex-dev/README.md @@ -0,0 +1,3 @@ +# perfex-dev + +Subprojecto perfex-dev do ecossistema Descomplicar. diff --git a/perfex-dev/STATUS.md b/perfex-dev/STATUS.md new file mode 100644 index 0000000..c79e7a4 --- /dev/null +++ b/perfex-dev/STATUS.md @@ -0,0 +1,8 @@ +# STATUS.md — perfex-dev + +**Versão:** 0.1 | **Data:** 2026-06-30 | **Agente:** — + +## Estado actual +- Em curso: — +- Bloqueios: — +- Próximos passos: — diff --git a/perfex-dev/agents/perfex-crm-module-developer.md b/perfex-dev/agents/perfex-crm-module-developer.md new file mode 100644 index 0000000..99de7fb --- /dev/null +++ b/perfex-dev/agents/perfex-crm-module-developer.md @@ -0,0 +1,169 @@ +--- +name: perfex-crm-module-developer +description: Desenvolvimento de modulos personalizados para Perfex CRM com expertise + em automacao de workflows e integracoes +role: Desenvolvimento de modulos personalizados para Perfex CRM com expertise em automacao + de workflows e integracoes +domain: Dev +model: sonnet +tools: Read, Write, Edit, Bash, Glob, Grep, ToolSearch + +# Dependencies +primary_mcps: + - desk-crm-v3 + - ssh-unified + - gitea +recommended_mcps: + - context7 + - filesystem +skills: +- _core +- perfex-module-basics +- perfex-hooks +- perfex-controllers +- perfex-views +- perfex-menus +- perfex-permissions +- perfex-database +- perfex-security +- perfex-forms +- perfex-migrations +desk_task: 1510 +desk_project: 65 +milestone: 274 +tags: +- agent +- stackworkflow +- claude-code +- perfex +version: '2.0' +status: active +quality_score: 70 +compliance: + sacred_rules: true + excellence_standards: true + data_sources: true + knowledge_first: true +created: '2025-01-13' +updated: '2026-02-04' +author: Descomplicar® +layer: wiki +--- +# Perfex CRM Module Developer Descomplicar + +Especialista em desenvolvimento de modulos customizados, automacao de workflows e criacao de integracoes para sistemas Perfex CRM. + +## Responsabilidades +- Desenvolvimento de modulos Perfex CRM seguindo convencoes do framework +- Design e implementacao de extensoes de schema de base de dados +- Criacao de workflows automatizados para lead management e vendas +- Desenvolvimento de ferramentas de reporting e dashboards customizados +- Implementacao de integracoes com servicos terceiros (pagamentos, comunicacao) + +## Knowledge Sources (Consultar SEMPRE) + +### NotebookLM (Primario - usar PRIMEIRO) + +``` +mcp__notebooklm__notebook_query notebook_id:"df4688bb-c2c0-4aba-98c1-38c3b50a353c" query:"module development hooks customization" +mcp__notebooklm__notebook_query notebook_id:"24947ffa-0019-448a-a340-2f4a275d2eb1" query:"PHP MVC CRM development" +``` + +## System Prompt + +### Papel +Especialista em desenvolvimento de modulos customizados, automacao de workflows e criacao de integracoes para sistemas Perfex CRM. + +### Regras Obrigatórias +1. SEMPRE seguir estrutura de módulos Perfex (hooks, views, controllers) +2. Namespace adequado (`modules/`) +3. Versionamento semântico (CHANGELOG.md) +4. Database migrations em `/install.php` e `/upgrade/` +5. NUNCA modificar core do Perfex +6. Testes em ambiente staging antes de produção + +### Output Format +- Código PHP PSR-4 compliant +- README com instalação e configuração +- CHANGELOG.md actualizado +- Database schema documentado +- Hooks utilizados listados + +## Perfex Skills (Official Documentation Only) + +**Zero assumptions, zero hallucinations** - Todas baseadas em documentação oficial help.perfexcrm.com. + +| Skill | Função | +|-------|--------| +| **/perfex-module-basics** | Estrutura base, init file, file headers | +| **/perfex-hooks** | Actions, filters, lista 80+ hooks | +| **/perfex-controllers** | AdminController, ClientsController, routing | +| **/perfex-views** | Templates, init_head(), init_tail() | +| **/perfex-menus** | Sidebar menus, client menus, posições | +| **/perfex-permissions** | staff_can(), register_staff_capabilities | +| **/perfex-database** | db_prefix(), options, queries seguras | +| **/perfex-security** | CSRF, XSS, input validation | +| **/perfex-forms** | form_open(), AJAX, validation | +| **/perfex-migrations** | Versioning, migrations, upgrades | + +## Workflows + +### Workflow 1: Criar Novo Módulo Perfex +1. Estrutura pastas (`modules/`, views, controllers, models) +2. `install.php` (DB schema, permissions, menu) +3. Hooks registration +4. Views e controllers +5. Testes funcionais +6. Documentação + +### Workflow 2: Integração API Externa +1. Configurar credentials (settings module) +2. Helper class para API calls +3. Error handling e retry logic +4. Webhook receiver (se aplicável) +5. Logging de requests +6. Testes end-to-end + +### Workflow 3: Custom Dashboard Widget +1. Hook `admin_widgets` +2. Controller para data fetching +3. View com gráfico/tabela +4. Permissions check +5. Responsive design +6. Cache layer + +## MCPs Relevantes +- `desk-crm-v3`: Acesso directo à BD Perfex para testes +- `notebooklm`: KB primaria (Gemini 2.5 RAG) | ``: KB PerfexCRM, Desenvolvimento Software +- `ssh-unified`: Deploy de módulos em servidores +- `gitea`: Versionamento de código + +## Colaboracao +- Reports to: Development Lead +- Colabora com: Database specialists, CRM administrators, Business analysts + +## Your Available MCPs + +### Primary MCPs (Your Domain) +✓ **desk-crm-v3** (business) + - Clientes, projectos, facturas, time tracking + - Usage: `mcp__desk-crm-v3__*` + +✓ **gitea** (dev) + - Repositórios Git, issues, PRs + - Usage: `mcp__gitea__*` + +✓ **ssh-unified** (infra) + - SSH, SFTP, servidor management + - Usage: `mcp__ssh-unified__*` + +✓ **notebooklm** (knowledge primaria) +✓ **** (knowledge fallback) + - Knowledge base AI + - Usage: `mcp____*` + +### Recommended for dev +- **context7** - Context documentation +- **n8n** - Workflows automation +- **filesystem** - Ficheiros locais +- **mag \ No newline at end of file diff --git a/perfex-dev/desk.project b/perfex-dev/desk.project new file mode 100644 index 0000000..5678920 --- /dev/null +++ b/perfex-dev/desk.project @@ -0,0 +1,6 @@ +{ + "task_id": null, + "project_id": null, + "customer_id": null, + "gitea_repo": null +} diff --git a/perfex-dev/index.md b/perfex-dev/index.md new file mode 100644 index 0000000..d224cb7 --- /dev/null +++ b/perfex-dev/index.md @@ -0,0 +1,8 @@ +# perfex-dev + +## Documentação + +- [README](README.md) +- [AGENTS](AGENTS.md) +- [STATUS](STATUS.md) +- [MEMORY](MEMORY.md) diff --git a/perfex-dev/knowledge/datasets.json b/perfex-dev/knowledge/datasets.json new file mode 100644 index 0000000..b2efbf1 --- /dev/null +++ b/perfex-dev/knowledge/datasets.json @@ -0,0 +1,18 @@ +{ + "description": "Knowledge sources for Perfex CRM Development domain", + "sources": { + "notebooklm": { + "description": "NotebookLM - conhecimento curado profundo via Gemini 2.5 RAG (PRIMARIO)", + "query_tool": "mcp__notebooklm__notebook_query", + "notebooks": [ + { + "id": "df4688bb-c2c0-4aba-98c1-38c3b50a353c", + "title": "Perfex CRM: Gestao de Clientes e Projetos", + "topics": [ + "perfexcrm" + ] + } + ] + } + } +} diff --git a/perfex-dev/log.md b/perfex-dev/log.md new file mode 100644 index 0000000..66e9c4d --- /dev/null +++ b/perfex-dev/log.md @@ -0,0 +1 @@ +## 2026-06-30 init | Estrutura padrão diff --git a/perfex-dev/skills/AGENTS.md b/perfex-dev/skills/AGENTS.md new file mode 100644 index 0000000..37d90a2 --- /dev/null +++ b/perfex-dev/skills/AGENTS.md @@ -0,0 +1,17 @@ +# AGENTS.md — skills + +Directrizes específicas deste projecto para agentes IA. + +## Referências obrigatórias + +- `ECOSYSTEM.md` — Hub/06-Operacoes/Documentacao/ECOSYSTEM.md +- `PROC-DEV-STANDARD` — D7-SIS-006 +- `PROC-AUDIT-STANDARD` — D7-SIS-007 + +## Protocolo de sessão + +Início: ler MEMORY.md + STATUS.md. Fim: actualizar ambos. + +## Convenções específicas + +- (a definir) diff --git a/perfex-dev/skills/CHANGELOG.md b/perfex-dev/skills/CHANGELOG.md new file mode 100644 index 0000000..5e14603 --- /dev/null +++ b/perfex-dev/skills/CHANGELOG.md @@ -0,0 +1,5 @@ +# CHANGELOG — skills + +## 0.1.0 — 2026-06-30 +### Adicionado +- Estrutura padrão (PROC-DEV-STANDARD) diff --git a/perfex-dev/skills/MEMORY.md b/perfex-dev/skills/MEMORY.md new file mode 100644 index 0000000..4971060 --- /dev/null +++ b/perfex-dev/skills/MEMORY.md @@ -0,0 +1,4 @@ +# MEMORY.md — skills + +## 2026-06-30 +- Estrutura padrão aplicada via propagate-structure.sh diff --git a/perfex-dev/skills/README.md b/perfex-dev/skills/README.md new file mode 100644 index 0000000..2ad232f --- /dev/null +++ b/perfex-dev/skills/README.md @@ -0,0 +1,3 @@ +# skills + +Subprojecto skills do ecossistema Descomplicar. diff --git a/perfex-dev/skills/STATUS.md b/perfex-dev/skills/STATUS.md new file mode 100644 index 0000000..07c1590 --- /dev/null +++ b/perfex-dev/skills/STATUS.md @@ -0,0 +1,8 @@ +# STATUS.md — skills + +**Versão:** 0.1 | **Data:** 2026-06-30 | **Agente:** — + +## Estado actual +- Em curso: — +- Bloqueios: — +- Próximos passos: — diff --git a/perfex-dev/skills/desk.project b/perfex-dev/skills/desk.project new file mode 100644 index 0000000..5678920 --- /dev/null +++ b/perfex-dev/skills/desk.project @@ -0,0 +1,6 @@ +{ + "task_id": null, + "project_id": null, + "customer_id": null, + "gitea_repo": null +} diff --git a/perfex-dev/skills/index.md b/perfex-dev/skills/index.md new file mode 100644 index 0000000..1d7c6b4 --- /dev/null +++ b/perfex-dev/skills/index.md @@ -0,0 +1,8 @@ +# skills + +## Documentação + +- [README](README.md) +- [AGENTS](AGENTS.md) +- [STATUS](STATUS.md) +- [MEMORY](MEMORY.md) diff --git a/perfex-dev/skills/log.md b/perfex-dev/skills/log.md new file mode 100644 index 0000000..66e9c4d --- /dev/null +++ b/perfex-dev/skills/log.md @@ -0,0 +1 @@ +## 2026-06-30 init | Estrutura padrão diff --git a/perfex-dev/skills/perfex-controllers/SKILL.md b/perfex-dev/skills/perfex-controllers/SKILL.md new file mode 100644 index 0000000..127afff --- /dev/null +++ b/perfex-dev/skills/perfex-controllers/SKILL.md @@ -0,0 +1,208 @@ +--- +name: perfex-controllers +description: Controllers para módulos Perfex CRM — AdminController, ClientsController, routing e trait ValidatesContact. Baseado apenas na documentação oficial. +layer: wiki +--- + +# /perfex-controllers - Controllers Perfex CRM + +Controllers para módulos Perfex CRM. **Zero assumptions, zero hallucinations** - apenas documentação oficial. + +--- + +## Documentação Base + +- [Module Controllers](https://help.perfexcrm.com/module-controllers/) +- [CodeIgniter Controllers](https://codeigniter.com/userguide3/general/controllers.html) + +--- + +## Estrutura de Pastas + +``` +modules/meu_modulo/ +└── controllers/ + ├── Meu_modulo.php # Controller principal + ├── Admin_controller.php # Controller admin adicional + └── Client_controller.php # Controller cliente +``` + +--- + +## Classes Base Disponíveis + +| Classe | Uso | Validação Automática | +|--------|-----|----------------------| +| `AdminController` | Área admin/staff | Login staff obrigatório | +| `ClientsController` | Área cliente | Tema cliente aplicado | +| `App_Controller` | Base genérica | Nenhuma | + +--- + +## AdminController (Área Admin) + +Para funcionalidades exclusivas de staff/administradores. + +### Template + +```php +load->model('meu_modulo/meu_modulo_model'); + } + + /** + * Página principal do módulo + * URL: /admin/meu_modulo ou /admin/meu_modulo/index + */ + public function index() + { + // Verificar permissão + if (!staff_can('view', 'meu_modulo')) { + access_denied('meu_modulo'); + } + + // Preparar dados + $data['items'] = $this->meu_modulo_model->get_all(); + $data['title'] = _l('meu_modulo_title'); + + // Carregar view + $this->load->view('meu_modulo/index', $data); + } + + /** + * Formulário de criação + * URL: /admin/meu_modulo/create + */ + public function create() + { + if (!staff_can('create', 'meu_modulo')) { + access_denied('meu_modulo'); + } + + if ($this->input->post()) { + // Processar formulário + $data = $this->input->post(); + $id = $this->meu_modulo_model->add($data); + + if ($id) { + set_alert('success', _l('added_successfully', _l('meu_modulo_item'))); + redirect(admin_url('meu_modulo')); + } + } + + $this->load->view('meu_modulo/form'); + } + + /** + * Editar item + * URL: /admin/meu_modulo/edit/123 + */ + public function edit($id) + { + if (!staff_can('edit', 'meu_modulo')) { + access_denied('meu_modulo'); + } + + $data['item'] = $this->meu_modulo_model->get($id); + + if (!$data['item']) { + show_404(); + } + + if ($this->input->post()) { + $success = $this->meu_modulo_model->update($id, $this->input->post()); + + if ($success) { + set_alert('success', _l('updated_successfully', _l('meu_modulo_item'))); + redirect(admin_url('meu_modulo')); + } + } + + $this->load->view('meu_modulo/form', $data); + } + + /** + * Apagar item + * URL: /admin/meu_modulo/delete/123 + */ + public function delete($id) + { + if (!staff_can('delete', 'meu_modulo')) { + access_denied('meu_modulo'); + } + + if ($this->meu_modulo_model->delete($id)) { + set_alert('success', _l('deleted', _l('meu_modulo_item'))); + } + + redirect(admin_url('meu_modulo')); + } +} +``` + +--- + +## ClientsController (Área Cliente) + +Para funcionalidades na área de clientes. + +### Template + +```php +load->model('meu_modulo/meu_modulo_model'); + } + + /** + * Página principal cliente + * URL: /meu_modulo ou /meu_modulo/index + */ + public function index() + { + // Verificar se cliente logado + if (!is_client_logged_in()) { + redirect(site_url('authentication/login')); + } + + $contact_id = get_contact_user_id(); + $client_id = get_client_user_id(); + + $data = [ + 'items' => $this->meu_modulo_model->get_by_client($client_id), + ]; + + // Métodos específicos de ClientsController + $this->data($data); + $this->title(_l('meu_modulo_title')); + $this->view('meu_modulo/client_index'); + $this->layout(); + } + + /** + * Ver detalhe + * URL: /meu_modulo/view/123 + */ + public function view($id) + { + if (!is_client_logged_in()) { + redirect(site_url('authentication/login')); + } + + $item = $this->meu_mod \ No newline at end of file diff --git a/perfex-dev/skills/perfex-database/SKILL.md b/perfex-dev/skills/perfex-database/SKILL.md new file mode 100644 index 0000000..0ad3ac4 --- /dev/null +++ b/perfex-dev/skills/perfex-database/SKILL.md @@ -0,0 +1,213 @@ +--- +name: perfex-database +description: Operações de base de dados em módulos Perfex CRM — db_prefix(), options, queries e Active Record. Baseado apenas na documentação oficial. +layer: wiki +--- + +# /perfex-database - Base de Dados Perfex CRM + +Operações de base de dados em módulos. **Zero assumptions, zero hallucinations** - apenas documentação oficial. + +--- + +## Documentação Base + +- [Module Basics](https://help.perfexcrm.com/module-basics/) +- [CodeIgniter Query Builder](https://codeigniter.com/userguide3/database/query_builder.html) + +--- + +## Regra Fundamental: db_prefix() + +**SEMPRE usar `db_prefix()` em todas as queries.** + +O Perfex CRM suporta prefixos de tabela customizados. O default é `tbl`, mas pode ser alterado. + +```php +// CORRECTO +$CI->db->get(db_prefix() . 'meu_modulo'); + +// ERRADO - quebra se prefix não for "tbl" +$CI->db->get('tblmeu_modulo'); +``` + +--- + +## Aceder à Base de Dados + +### Em Controllers/Models + +```php +// Acesso directo +$this->db->get(db_prefix() . 'clients'); +``` + +### Fora de Controllers/Models + +```php +$CI = &get_instance(); +$CI->db->get(db_prefix() . 'clients'); +``` + +--- + +## Criar Tabelas (Activation Hook) + +```php +function meu_modulo_activation_hook() +{ + $CI = &get_instance(); + + // Verificar se tabela existe + if (!$CI->db->table_exists(db_prefix() . 'meu_modulo')) { + $CI->db->query(' + CREATE TABLE `' . db_prefix() . 'meu_modulo` ( + `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `client_id` INT(11) UNSIGNED NOT NULL, + `name` VARCHAR(255) NOT NULL, + `description` TEXT, + `amount` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `status` VARCHAR(50) NOT NULL DEFAULT "pending", + `created_at` DATETIME NOT NULL, + `updated_at` DATETIME DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `client_id` (`client_id`), + KEY `status` (`status`) + ) ENGINE=InnoDB DEFAULT CHARSET=' . $CI->db->char_set . '; + '); + } +} +``` + +--- + +## Query Builder (Active Record) + +### SELECT + +```php +// Todos os registos +$result = $this->db->get(db_prefix() . 'meu_modulo')->result(); + +// Com condições +$this->db->where('status', 'active'); +$this->db->where('client_id', $client_id); +$result = $this->db->get(db_prefix() . 'meu_modulo')->result(); + +// Seleccionar campos específicos +$this->db->select('id, name, amount'); +$this->db->where('status', 'active'); +$result = $this->db->get(db_prefix() . 'meu_modulo')->result(); + +// Um registo +$this->db->where('id', $id); +$row = $this->db->get(db_prefix() . 'meu_modulo')->row(); + +// Com LIKE +$this->db->like('name', $search); +$result = $this->db->get(db_prefix() . 'meu_modulo')->result(); + +// Com ORDER BY +$this->db->order_by('created_at', 'DESC'); +$result = $this->db->get(db_prefix() . 'meu_modulo')->result(); + +// Com LIMIT +$this->db->limit(10, 0); // 10 registos, offset 0 +$result = $this->db->get(db_prefix() . 'meu_modulo')->result(); + +// JOIN +$this->db->select('m.*, c.company as client_name'); +$this->db->from(db_prefix() . 'meu_modulo as m'); +$this->db->join(db_prefix() . 'clients as c', 'c.userid = m.client_id', 'left'); +$result = $this->db->get()->result(); +``` + +### INSERT + +```php +$data = [ + 'client_id' => $client_id, + 'name' => $this->input->post('name'), + 'description' => $this->input->post('description'), + 'amount' => $this->input->post('amount'), + 'status' => 'pending', + 'created_at' => date('Y-m-d H:i:s'), +]; + +$this->db->insert(db_prefix() . 'meu_modulo', $data); +$insert_id = $this->db->insert_id(); +``` + +### UPDATE + +```php +$data = [ + 'name' => $this->input->post('name'), + 'description' => $this->input->post('description'), + 'amount' => $this->input->post('amount'), + 'updated_at' => date('Y-m-d H:i:s'), +]; + +$this->db->where('id', $id); +$this->db->update(db_prefix() . 'meu_modulo', $data); + +// Verificar se actualizou +$affected = $this->db->affected_rows(); +``` + +### DELETE + +```php +$this->db->where('id', $id); +$this->db->delete(db_prefix() . 'meu_modulo'); +``` + +### COUNT + +```php +// Contar todos +$count = $this->db->count_all(db_prefix() . 'meu_modulo'); + +// Contar com condições +$this->db->where('status', 'active'); +$count = $this->db->count_all_results(db_prefix() . 'meu_modulo'); +``` + +--- + +## Sistema de Opções + +Para guardar configurações simples (key-value). + +### add_option() + +Cria opção nova. **NÃO sobrescreve se existir.** + +```php +add_option('meu_modulo_enabled', '1'); +add_option('meu_modulo_config', serialize(['key' => 'value'])); + +// Com autoload (0 = não, 1 = sim) +add_option('meu_modulo_setting', 'valor', 0); +``` + +### get_option() + +Obtém valor da opção. + +```php +$enabled = get_option('meu_modulo_enabled'); +$config = unserialize(get_option('meu_modulo_config')); + +// Retorna string vazia se não existir +if (get_option('meu_modulo_setting') === '') { + // Opção não existe +} +``` + +### update_option() + +Actualiza ou cria opção (v2.3.3+). + +```php +update \ No newline at end of file diff --git a/perfex-dev/skills/perfex-forms/SKILL.md b/perfex-dev/skills/perfex-forms/SKILL.md new file mode 100644 index 0000000..e6ebd16 --- /dev/null +++ b/perfex-dev/skills/perfex-forms/SKILL.md @@ -0,0 +1,225 @@ +--- +name: perfex-forms +description: Gestao de formularios em modulos Perfex CRM -- form_open(), tokens CSRF, validacao e AJAX. Baseado apenas na documentacao oficial. +layer: wiki +--- + +# /perfex-forms - Formularios Perfex CRM + +Gestao de formularios em modulos. **Zero assumptions, zero hallucinations** - apenas documentacao oficial. + +--- + +## Documentacao Base + +- [Working With Forms](https://help.perfexcrm.com/working-with-forms/) +- [CodeIgniter Form Helper](https://codeigniter.com/userguide3/helpers/form_helper.html) + +--- + +## Regra Fundamental: form_open() + +**SEMPRE usar `form_open()` para gerar token CSRF automaticamente.** + +```php + + + +``` + +**NUNCA usar `
` HTML directo** - sem CSRF protection. + +--- + +## Sintaxe form_open() + +```php +// Basico +echo form_open(admin_url('meu_modulo/save')); + +// Com atributos +echo form_open(admin_url('meu_modulo/save'), [ + 'id' => 'form-meu-modulo', + 'class' => 'form-horizontal', +]); + +// Com upload de ficheiros +echo form_open_multipart(admin_url('meu_modulo/upload'), [ + 'id' => 'form-upload', +]); + +// Fechar formulario +echo form_close(); +``` + +--- + +## Elementos de Formulario + +Todos os elementos com exemplos completos em: `references/elementos-formulario.md` + +Elementos disponiveis: Input Text, Textarea, Select, Select AJAX, Checkbox, Radio, Date Picker, DateTime Picker, File Upload, Hidden. + +**Padrao geral:** +```php +
+ + +
+``` + +--- + +## Validacao Client-Side + +O Perfex usa jQuery Validation. Usar `appValidateForm()`: + +```php + + + + +``` + +### Regras de Validacao + +| Regra | Descricao | +|-------|-----------| +| `required: true` | Campo obrigatorio | +| `email: true` | Email valido | +| `number: true` | Numero | +| `digits: true` | Apenas digitos | +| `minlength: N` | Minimo N caracteres | +| `maxlength: N` | Maximo N caracteres | +| `min: N` | Valor minimo | +| `max: N` | Valor maximo | +| `equalTo: '#field'` | Igual a outro campo | + +--- + +## Validacao Server-Side + +```php +public function save() +{ + if (!$this->input->post()) { + redirect(admin_url('meu_modulo')); + } + + $this->load->library('form_validation'); + + $this->form_validation->set_rules('name', _l('name'), 'required|min_length[3]'); + $this->form_validation->set_rules('email', _l('email'), 'required|valid_email'); + $this->form_validation->set_rules('amount', _l('amount'), 'required|numeric'); + + if ($this->form_validation->run() === false) { + set_alert('danger', validation_errors()); + redirect(admin_url('meu_modulo/create')); + } + + $data = [ + 'name' => $this->input->post('name'), + 'email' => $this->input->post('email'), + 'amount' => $this->input->post('amount'), + ]; + + // ... guardar +} +``` + +--- + +## AJAX com jQuery + +### POST Simples + +```javascript +$.post(admin_url + 'meu_modulo/ajax_save', { + name: $('#name').val(), + description: $('#description').val() +}).done(function(response) { + response = JSON.parse(response); + if (response.success) { + alert_float('success', response.message); + } else { + alert_float('danger', response.message); + } +}); +``` + +### Controller AJAX + +```php +public function ajax_save() +{ + if (!$this->input->is_ajax_request()) { + show_404(); + } + + if (!staff_can('create', 'meu_modulo')) { + echo json_encode(['success' => false, 'message' => _l('access_denied')]); + return; + } + + $data = [ + 'name' => $this->input->post('name'), + 'description' => $this->input->post('description'), + ]; + + $id = $this->meu_modulo_model->add($data); + + echo json_encode([ + 'success' => (bool) $id, + 'id' => $id, + 'message' => $id ? _l('added_successfully') : _l('error_occurred'), + ]); +} +``` + +--- + +## Excluir CSRF para Webhooks + +Para endpoints que recebem callbacks externos: + +```php +// modules/meu_modulo/config/csrf_exclude_uris.php +` HTML directo | CSRF bypass \ No newline at end of file diff --git a/perfex-dev/skills/perfex-forms/references/elementos-formulario.md b/perfex-dev/skills/perfex-forms/references/elementos-formulario.md new file mode 100644 index 0000000..1445aee --- /dev/null +++ b/perfex-dev/skills/perfex-forms/references/elementos-formulario.md @@ -0,0 +1,159 @@ +# Elementos de Formulario Perfex CRM + +Referencia completa de todos os elementos HTML/PHP para formularios em modulos Perfex. + +--- + +## Input Text + +```php +
+ + +
+``` + +## Textarea + +```php +
+ + +
+``` + +## Select + +```php +
+ + +
+``` + +## Select com AJAX (Clientes) + +```php +
+ + +
+``` + +## Checkbox + +```php +
+ is_active == 1) ? 'checked' : ''; ?>> + +
+``` + +## Radio + +```php +
+ +
+ status == 'pending') ? 'checked' : ''; ?>> + +
+
+ status == 'active') ? 'checked' : ''; ?>> + +
+
+``` + +## Date Picker + +```php +
+ + +
+``` + +## Date Time Picker + +```php +
+ + +
+``` + +## File Upload + +```php + + +
+ + +
+ + +``` + +## Hidden + +```php + +``` diff --git a/perfex-dev/skills/perfex-forms/references/formulario-completo-exemplo.md b/perfex-dev/skills/perfex-forms/references/formulario-completo-exemplo.md new file mode 100644 index 0000000..8e97d00 --- /dev/null +++ b/perfex-dev/skills/perfex-forms/references/formulario-completo-exemplo.md @@ -0,0 +1,141 @@ +# Formulario Completo Exemplo - Perfex CRM + +Exemplo completo de view com formulario, validacao client-side e todos os elementos tipicos. + +--- + +## View + +```php + + + +
+
+
+
+
+
+ +

+ + +

+
+ + id) + : admin_url('meu_modulo/save'); + echo form_open($action, ['id' => 'form-meu-modulo']); + ?> + + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ is_active == 1) ? 'checked' : ''; ?>> + +
+ + +
+ + + + +
+ + + +
+
+
+
+
+
+ + + + + +``` diff --git a/perfex-dev/skills/perfex-hooks/SKILL.md b/perfex-dev/skills/perfex-hooks/SKILL.md new file mode 100644 index 0000000..1fa2e07 --- /dev/null +++ b/perfex-dev/skills/perfex-hooks/SKILL.md @@ -0,0 +1,180 @@ +--- +name: perfex-hooks +description: Sistema de hooks Perfex CRM — actions, filters, add_action, add_filter, do_action, apply_filters com lista completa por categoria. Baseado apenas na documentação oficial. +layer: wiki +--- + +# /perfex-hooks - Sistema de Hooks Perfex CRM + +Sistema de extensibilidade via actions e filters. **Zero assumptions, zero hallucinations** - apenas documentação oficial. + +--- + +## Documentação Base + +- [Action Hooks](https://help.perfexcrm.com/action-hooks/) +- [GitHub Hook List](https://gist.github.com/JamesSimpson/4eae4ba2d6d7072eca9f0fae58b8887c) + +--- + +## Conceitos Fundamentais + +| Tipo | Função | Retorno | Uso | +|------|--------|---------|-----| +| **Action** | Executa lógica após eventos | void | Side effects (logs, emails, etc.) | +| **Filter** | Modifica dados antes de processamento | Dados modificados | Alterar valores | + +--- + +## Sintaxe (v2.3.0+) + +### Registar Action + +```php +hooks()->add_action($tag, $callback, $priority = 10, $accepted_args = 1); +``` + +**Parâmetros:** +- `$tag` (string): Nome do hook +- `$callback` (callable): Função a executar +- `$priority` (int): Ordem de execução (menor = primeiro). Default: 10 +- `$accepted_args` (int): Número de argumentos. Default: 1 + +**Exemplo:** + +```php +// No init file do módulo +hooks()->add_action('after_client_added', 'meu_modulo_after_client_added'); + +function meu_modulo_after_client_added($clientId) +{ + // Lógica após cliente criado + log_activity('Novo cliente criado: ' . $clientId); + + // Enviar notificação + $CI = &get_instance(); + $CI->load->model('clients_model'); + $client = $CI->clients_model->get($clientId); + + // Integração externa, etc. +} +``` + +### Registar Filter + +```php +hooks()->add_filter($tag, $callback, $priority = 10, $accepted_args = 1); +``` + +**Exemplo:** + +```php +hooks()->add_filter('before_client_added', 'meu_modulo_before_client_added'); + +function meu_modulo_before_client_added($data) +{ + // Modificar dados antes de guardar + if (empty($data['country'])) { + $data['country'] = 177; // Portugal + } + + // OBRIGATÓRIO: retornar dados modificados + return $data; +} +``` + +### Executar Action (criar hooks próprios) + +```php +hooks()->do_action($tag, $arg = ''); +``` + +**Exemplo:** + +```php +// No teu módulo, permitir que outros módulos executem código +hooks()->do_action('meu_modulo_after_process', $resultId); +``` + +### Aplicar Filter (criar filters próprios) + +```php +hooks()->apply_filters($tag, $value, $additionalParams = []); +``` + +**Exemplo:** + +```php +// Permitir que outros módulos modifiquem dados +$data = hooks()->apply_filters('meu_modulo_data', $data); +``` + +--- + +## Onde Colocar Hooks + +### Opção 1: Init File do Módulo (Recomendado) + +```php +// modules/meu_modulo/meu_modulo.php +hooks()->add_action('after_client_added', 'meu_modulo_client_handler'); +``` + +### Opção 2: my_functions_helper.php (Customizações Simples) + +```php +// application/helpers/my_functions_helper.php +hooks()->add_action('after_client_added', 'custom_client_handler'); +``` + +--- + +## Lista Completa de Hooks por Categoria + +### Autenticação + +| Hook | Trigger | Argumentos | +|------|---------|------------| +| `admin_auth_init` | Início auth admin | - | +| `after_staff_login` | Após login staff | $staff_id | +| `after_user_logout` | Após logout | - | +| `after_contact_login` | Após login contacto | $contact_id | +| `after_client_register` | Após registo cliente | $client_id | +| `after_client_logout` | Após logout cliente | - | + +### Clientes + +| Hook | Trigger | Argumentos | +|------|---------|------------| +| `before_client_added` | **FILTER** - Antes de criar | $data | +| `after_client_added` | Após criar cliente | $client_id | +| `before_client_updated` | **FILTER** - Antes de actualizar | $data, $client_id | +| `after_client_updated` | Após actualizar | $client_id | +| `before_client_deleted` | Antes de apagar | $client_id | +| `after_client_deleted` | Após apagar | $client_id | +| `contact_created` | Contacto criado | $contact_id | +| `contact_updated` | Contacto actualizado | $contact_id | +| `before_delete_contact` | Antes apagar contacto | $contact_id | +| `contact_deleted` | Contacto apagado | $contact_id | + +### Facturas + +| Hook | Trigger | Argumentos | +|------|---------|------------| +| `before_invoice_added` | **FILTER** - Antes de criar | $data | +| `after_invoice_added` | Após criar | $invoice_id | +| `before_invoice_updated` | **FILTER** - Antes de actualizar | $data, $invoice_id | +| `after_invoice_updated` | Após actualizar | $invoice_id | +| `invoice_sent` | Factura enviada | $invoice_id | +| `invoice_status_changed` | Status alterado | $invoice_id, $status | +| `before_invoice_deleted` | Antes de apagar | $invoice_id | +| `invoice_marked_as_cancelled` | Factura cancelada | $invoice_id | + +### Orçamentos (Estimates) + +| Hook | Trigger | Argumentos | +|------|---------|------------| +| `before_estimate_added` | **FILTER** - Antes de criar | $data | +| `after_estimate_added` | Após criar | $estimate_id | +| `after_estimate_updated` | Após actualizar | $estimate_id | +| `estimate_ \ No newline at end of file diff --git a/perfex-dev/skills/perfex-menus/SKILL.md b/perfex-dev/skills/perfex-menus/SKILL.md new file mode 100644 index 0000000..d18bfda --- /dev/null +++ b/perfex-dev/skills/perfex-menus/SKILL.md @@ -0,0 +1,223 @@ +--- +name: perfex-menus +description: Criação de menus para módulos Perfex CRM — sidebar, menus de cliente, add_sidebar_menu_item(), posições e ícones. Baseado apenas na documentação oficial. +layer: wiki +--- + +# /perfex-menus - Menus Perfex CRM + +Criação de menus para módulos. **Zero assumptions, zero hallucinations** - apenas documentação oficial. + +--- + +## Documentação Base + +- [Create Menu Items](https://help.perfexcrm.com/create-menu-items/) + +--- + +## Menu Admin (Sidebar) + +### Adicionar Item Simples + +```php +// No init file do módulo +hooks()->add_action('admin_init', 'meu_modulo_init_menu'); + +function meu_modulo_init_menu() +{ + $CI = &get_instance(); + + $CI->app_menu->add_sidebar_menu_item('meu-modulo-menu', [ + 'name' => _l('meu_modulo_title'), + 'href' => admin_url('meu_modulo'), + 'position' => 25, + 'icon' => 'fa fa-cube', + ]); +} +``` + +### Parâmetros + +| Parâmetro | Tipo | Descrição | +|-----------|------|-----------| +| `name` | string | Texto do menu (usar _l() para tradução) | +| `href` | string | URL destino | +| `position` | int | Posição no menu (menor = mais acima) | +| `icon` | string | Classe Font Awesome | +| `collapse` | bool | Se tem sub-items (default: false) | +| `badge` | array | Badge com contagem | + +--- + +## Menu com Sub-Items (Collapsible) + +```php +hooks()->add_action('admin_init', 'meu_modulo_init_menu'); + +function meu_modulo_init_menu() +{ + $CI = &get_instance(); + + // Menu pai (collapsible) + $CI->app_menu->add_sidebar_menu_item('meu-modulo-menu', [ + 'name' => _l('meu_modulo_title'), + 'collapse' => true, + 'position' => 25, + 'icon' => 'fa fa-cube', + ]); + + // Sub-item 1 + $CI->app_menu->add_sidebar_children_item('meu-modulo-menu', [ + 'slug' => 'meu-modulo-lista', + 'name' => _l('list'), + 'href' => admin_url('meu_modulo'), + 'position' => 1, + 'icon' => 'fa fa-list', + ]); + + // Sub-item 2 + $CI->app_menu->add_sidebar_children_item('meu-modulo-menu', [ + 'slug' => 'meu-modulo-criar', + 'name' => _l('create'), + 'href' => admin_url('meu_modulo/create'), + 'position' => 2, + 'icon' => 'fa fa-plus', + ]); + + // Sub-item 3 + $CI->app_menu->add_sidebar_children_item('meu-modulo-menu', [ + 'slug' => 'meu-modulo-settings', + 'name' => _l('settings'), + 'href' => admin_url('meu_modulo/settings'), + 'position' => 3, + 'icon' => 'fa fa-cog', + ]); +} +``` + +--- + +## Posições Padrão Admin + +| Posição | Menu Item | +|---------|-----------| +| 1 | Dashboard | +| 5 | Customers | +| 10 | Sales | +| 15 | Subscriptions | +| 20 | Expenses | +| 25 | Contracts | +| 30 | Projects | +| 35 | Tasks | +| 40 | Tickets | +| 45 | Leads | +| 50 | Knowledge Base | +| 55 | Utilities | +| 60 | Reports | + +**Dica:** Usar posições intermédias (ex: 27, 32) para inserir entre items existentes. + +--- + +## Badge (Contador) + +```php +$CI->app_menu->add_sidebar_menu_item('meu-modulo-menu', [ + 'name' => _l('meu_modulo_title'), + 'href' => admin_url('meu_modulo'), + 'position' => 25, + 'icon' => 'fa fa-cube', + 'badge' => [ + 'value' => meu_modulo_count_pending(), + 'type' => 'danger', // primary, success, warning, danger, info + 'show_on_sidebar' => true, + ], +]); + +// Função de contagem +function meu_modulo_count_pending() +{ + $CI = &get_instance(); + $CI->db->where('status', 'pending'); + return $CI->db->count_all_results(db_prefix() . 'meu_modulo'); +} +``` + +--- + +## Menu Condicional (Permissões) + +```php +function meu_modulo_init_menu() +{ + // Só mostrar se staff tem permissão + if (!staff_can('view', 'meu_modulo')) { + return; + } + + $CI = &get_instance(); + + $CI->app_menu->add_sidebar_menu_item('meu-modulo-menu', [ + 'name' => _l('meu_modulo_title'), + 'href' => admin_url('meu_modulo'), + 'position' => 25, + 'icon' => 'fa fa-cube', + ]); +} +``` + +--- + +## Menu Cliente (Área Cliente) + +### Adicionar Item + +```php +hooks()->add_action('clients_init', 'meu_modulo_client_menu'); + +function meu_modulo_client_menu() +{ + // Só mostrar se cliente logado + if (!is_client_logged_in()) { + return; + } + + add_theme_menu_item('meu-modulo', [ + 'name' => _l('meu_modulo_title'), + 'href' => site_url('meu_modulo'), + 'position' => 35, + 'icon' => 'fa fa-cube', + ]); +} +``` + +### Posições Padrão Cliente + +| Posição | Menu Item | +|---------|-----------| +| 5 | Knowledge Base | +| 10 | Projects | +| 15 | Invoices | +| 20 | Contracts | +| 25 | Estimates | +| 30 | Proposals | +| 40 | Subscriptions | +| 45 | Support | +| 99 | Registration | +| 100 | Login | + +--- + +## Menu no Setup (Administração) + +Para adicionar configurações ao menu Setup: + +```php +hooks()->add_action('admin_init', 'meu_modulo_setup_menu'); + +function meu_modulo_setup_menu() +{ + $CI = &get_instance(); + + // Adicionar ao sub-menu de Setup diff --git a/perfex-dev/skills/perfex-migrations/SKILL.md b/perfex-dev/skills/perfex-migrations/SKILL.md new file mode 100644 index 0000000..bcab6d1 --- /dev/null +++ b/perfex-dev/skills/perfex-migrations/SKILL.md @@ -0,0 +1,211 @@ +--- +name: perfex-migrations +description: Migrações e upgrades de módulos Perfex CRM — gestão de versões, ficheiros de migração e actualizações de base de dados. Baseado apenas na documentação oficial. +layer: wiki +--- + +# /perfex-migrations - Migrations Perfex CRM + +Sistema de migrações e upgrades de módulos. **Zero assumptions, zero hallucinations** - apenas documentação oficial. + +--- + +## Documentação Base + +- [Preparing Module Upgrade](https://help.perfexcrm.com/preparing-module-upgrade/) +- [Module Basics](https://help.perfexcrm.com/module-basics/) + +--- + +## Como Funciona + +O Perfex CRM compara: +- **Versão no init file** (header `Version:`) +- **Versão na base de dados** (tabela `tblmodules`) + +Se forem diferentes → Sistema mostra "Database upgrade required". + +--- + +## Estrutura de Migrations + +``` +modules/meu_modulo/ +├── meu_modulo.php # Init file com Version: X.Y.Z +└── migrations/ + ├── 001_version_100.php # v1.0.0 → v1.0.1 + ├── 002_version_101.php # v1.0.1 → v1.0.2 + ├── 100_version_100.php # v1.0.0 inicial (alternativa) + ├── 110_version_110.php # v1.1.0 + └── 200_version_200.php # v2.0.0 +``` + +--- + +## Formato do Ficheiro + +### Nomenclatura (CRÍTICO) + +O Perfex usa **Sequential Migration** (não Timestamp). + +``` +NNN_version_NNN.php +│ │ │ +│ │ └── Versão sem pontos (110 = 1.1.0) +│ └── Literal "version" +└── Número sequencial (3 dígitos, sem gaps) +``` + +**Exemplos:** + +| Versão | Ficheiro | +|--------|----------| +| 1.0.0 | `001_version_100.php` ou `100_version_100.php` | +| 1.0.1 | `002_version_101.php` ou `101_version_101.php` | +| 1.1.0 | `003_version_110.php` ou `110_version_110.php` | +| 2.0.0 | `004_version_200.php` ou `200_version_200.php` | + +**Convenção recomendada:** Usar versão como prefixo (100, 110, 200...). + +--- + +## Template Migration + +```php +db->field_exists('new_column', db_prefix() . 'meu_modulo')) { + $CI->db->query('ALTER TABLE `' . db_prefix() . 'meu_modulo` + ADD COLUMN `new_column` VARCHAR(255) DEFAULT NULL AFTER `name`'); + } + } +} +``` + +### Criar Nova Tabela + +```php +db->table_exists(db_prefix() . 'meu_modulo_items')) { + $CI->db->query(' + CREATE TABLE `' . db_prefix() . 'meu_modulo_items` ( + `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `parent_id` INT(11) UNSIGNED NOT NULL, + `name` VARCHAR(255) NOT NULL, + `quantity` INT(11) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + KEY `parent_id` (`parent_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=' . $CI->db->char_set . '; + '); + } + } +} +``` + +### Modificar Coluna + +```php +db->query('ALTER TABLE `' . db_prefix() . 'meu_modulo` + MODIFY COLUMN `amount` DECIMAL(15,2) NOT NULL DEFAULT 0.00'); + } +} +``` + +### Adicionar Índice + +```php +db->query('SHOW INDEX FROM `' . db_prefix() . 'meu_modulo` + WHERE Key_name = "idx_status"')->result(); + + if (count($indexes) == 0) { + $CI->db->query('ALTER TABLE `' . db_prefix() . 'meu_modulo` + ADD INDEX `idx_status` (`status`)'); + } + } +} +``` + +### Migração de Dados + +```php +db->table_exists(db_prefix() . 'meu_modulo')) { + $CI->db->query('CREATE TABLE `' . db_prefix() . 'meu_modulo` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `created_at` DATETIME NOT NULL, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=' . $CI->db->char_set . ';'); + } + + // Adicionar opções + add_option('meu_modulo_version', '1.0.0'); +} + +/** + * Executado quando o módulo é desactivado + */ +function meu_modulo_deactivation_hook() +{ + // Limpeza temporária (não apagar dados) +} + +/** + * Executado quando o módulo é removido + */ +function meu_modulo_uninstall_hook() +{ + $CI = &get_instance(); + + // Remover tabelas + $CI->db->query('DROP TABLE IF EXISTS `' . db_prefix() . 'meu_modulo`'); + + // Remover opções + delete_option('meu_modulo_version'); +} +``` + +--- + +## File Headers (Meta-Informação) + +### Headers Obrigatórios + +| Header | Descrição | +|--------|-----------| +| `Module Name` | **ÚNICO OBRIGATÓRIO** - Nome do módulo | + +### Headers Opcionais (Recomendados) + +| Header | Descrição | Exemplo | +|--------|-----------|---------| +| `Description` | Descrição funcional | `CRM integration module` | +| `Version` | Versão semântica | `1.0.0` | +| `Requires at least` | Versão mínima Perfex | `2.3.*` | +| `Author` | Nome do autor | `Descomplicar` | +| `Author URI` | Website do autor | `https://descomplicar.pt` | +| `Module URI` | Página do módulo | `https://...` | + +### Exemplo Completo + +```php +/* +Module Name: Customer Portal +Description: Extended customer self-service portal +Version: 2.1.0 +Requires at least: 2.3.* +Author: Descomplicar +Author URI: https://descomplicar.pt +Module URI: https://descomplicar.pt/modules/customer-portal +*/ +``` + +--- + +## Aceder ao CodeIgniter + +Fora de controllers/models, usar `get_instance()`: + +```php +$CI = &get_instance(); + +// Carregar helper +$CI->load->helper('meu_modulo/meu_helper'); + +// Carregar library +$CI->load->library('meu_modulo/minha_library'); + +// Carregar model +$CI->load->model('meu_modulo/meu_modulo_model'); + +// Aceder à BD +$CI->db->get(db_prefix() . 'meu_modulo'); +``` + +--- + +## Funções de Módulo Essenciais + +### Paths e URLs + +```php +// URL do módulo +$url = module_dir_url('meu_modulo'); +// Resultado: https://crm.exemplo.pt/modules/meu_modulo/ + +// Path absoluto +$path = module_dir_path('meu_modulo'); +// Resultado: /var/www/html/modules/meu_modulo/ + +// Path das libraries +$libs_path = module_libs_path('meu_modulo'); +// Resultado: /var/www/html/modules/meu_modulo/libraries/ +``` + +### Opções (Persistência) + +```php +// Criar opção (não sobrescreve se existir) +add_option('meu_modulo_setting', 'valor', 0); + +// Ler opção +$valor = get_option('meu_modulo_setting'); + +// Actualizar opção (cria se não existir - v2.3.3+) +update_option('meu_modulo_setting', 'novo_valor'); + +// Apagar opção +delete_option('meu_modulo_setting'); +``` + +### Database Prefix + +**SEMPRE usar `db_prefix()` em queries:** + +```php +// CORRECTO +$CI->db->get(db_prefix() . 'meu_modulo \ No newline at end of file diff --git a/perfex-dev/skills/perfex-permissions/SKILL.md b/perfex-dev/skills/perfex-permissions/SKILL.md new file mode 100644 index 0000000..9a5b6df --- /dev/null +++ b/perfex-dev/skills/perfex-permissions/SKILL.md @@ -0,0 +1,208 @@ +--- +name: perfex-permissions +description: Sistema de permissões Perfex CRM — staff_can(), register_staff_capabilities(), controlo de acesso e roles. Baseado apenas na documentação oficial. +layer: wiki +--- + +# /perfex-permissions - Permissões Perfex CRM + +Sistema de permissões e controlo de acesso. **Zero assumptions, zero hallucinations** - apenas documentação oficial. + +--- + +## Documentação Base + +- [Staff Capabilities and Access](https://help.perfexcrm.com/staff-capabilities-and-access/) +- [Roles](https://help.perfexcrm.com/roles/) + +--- + +## Conceitos Fundamentais + +| Conceito | Descrição | +|----------|-----------| +| **Feature** | Área funcional (invoices, projects, meu_modulo) | +| **Capability** | Permissão específica (view, create, edit, delete) | +| **Role** | Conjunto de permissões pré-definido | + +--- + +## Registar Permissões do Módulo + +### No Init File + +```php +hooks()->add_action('admin_init', 'meu_modulo_register_permissions'); + +function meu_modulo_register_permissions() +{ + $capabilities = []; + + $capabilities['capabilities'] = [ + 'view' => _l('permission_view'), // Ver + 'create' => _l('permission_create'), // Criar + 'edit' => _l('permission_edit'), // Editar + 'delete' => _l('permission_delete'), // Apagar + ]; + + register_staff_capabilities( + 'meu_modulo', // Feature ID (único) + $capabilities, // Array de capabilities + _l('meu_modulo_title') // Nome do módulo (mostrado em Settings) + ); +} +``` + +### Parâmetros register_staff_capabilities() + +| Parâmetro | Tipo | Descrição | +|-----------|------|-----------| +| `$feature` | string | ID único da feature | +| `$capabilities` | array | Array com key 'capabilities' | +| `$name` | string | Nome mostrado na UI | + +--- + +## Verificar Permissões: staff_can() + +### Sintaxe + +```php +staff_can($capability, $feature = null, $staff_id = ''); +``` + +### Parâmetros + +| Parâmetro | Tipo | Descrição | +|-----------|------|-----------| +| `$capability` | string | Nome da capability (view, create, etc.) | +| `$feature` | string | Feature ID (recomendado SEMPRE passar) | +| `$staff_id` | int | ID do staff (default: staff logado) | + +### Retorno + +- `true` - Staff tem permissão +- `false` - Staff não tem permissão + +**NOTA:** Administradores SEMPRE retornam `true` (bypass total). + +--- + +## Exemplos de Uso + +### No Controller + +```php +class Meu_modulo extends AdminController +{ + public function index() + { + // Verificar permissão de ver + if (!staff_can('view', 'meu_modulo')) { + access_denied('meu_modulo'); + } + + // ... código + } + + public function create() + { + // Verificar permissão de criar + if (!staff_can('create', 'meu_modulo')) { + access_denied('meu_modulo'); + } + + // ... código + } + + public function edit($id) + { + // Verificar permissão de editar + if (!staff_can('edit', 'meu_modulo')) { + access_denied('meu_modulo'); + } + + // ... código + } + + public function delete($id) + { + // Verificar permissão de apagar + if (!staff_can('delete', 'meu_modulo')) { + access_denied('meu_modulo'); + } + + // ... código + } +} +``` + +### Na View + +```php + + + + + + + + + + + + + + + + + + + + + +``` + +### No Menu + +```php +function meu_modulo_init_menu() +{ + // Só mostrar menu se tem permissão + if (!staff_can('view', 'meu_modulo')) { + return; + } + + $CI = &get_instance(); + $CI->app_menu->add_sidebar_menu_item('meu-modulo', [ + 'name' => _l('meu_modulo'), + 'href' => admin_url('meu_modulo'), + 'position' => 25, + 'icon' => 'fa fa-cube', + ]); +} +``` + +--- + +## Capabilities Padrão + +### Recomendadas para CRUD + +| Capability | Descrição | Uso | +|------------|-----------|-----| +| `view` | Ver registos | Listar, ver detalhes | +| `create` | Criar novos | Formulário de criação | +| `edit` | Editar existentes | Formulário de edição | +| `delete` | Apagar registos | Botão de eliminar | + +### Adicionar Capabilities Custom + +```php +$capabilities['capabilities'] = [ + // CRUD básico + 'view' => _l('permission_view'), + 'create' => _l('permission_create'), + 'edit' \ No newline at end of file diff --git a/perfex-dev/skills/perfex-security/SKILL.md b/perfex-dev/skills/perfex-security/SKILL.md new file mode 100644 index 0000000..dfdd423 --- /dev/null +++ b/perfex-dev/skills/perfex-security/SKILL.md @@ -0,0 +1,240 @@ +--- +name: perfex-security +description: Segurança para módulos Perfex CRM — validação de input, CSRF, prevenção XSS e protecção contra directory traversal. Baseado apenas na documentação oficial. +layer: wiki +--- + +# /perfex-security - Segurança Módulos Perfex CRM + +Práticas de segurança para módulos. **Zero assumptions, zero hallucinations** - apenas documentação oficial. + +--- + +## Documentação Base + +- [Module Security](https://help.perfexcrm.com/module-security/) +- [CodeIgniter Security](https://codeigniter.com/userguide3/libraries/security.html) + +--- + +## 1. Prevenção de Acesso Directo + +**OBRIGATÓRIO em todos os ficheiros PHP:** + +```php + + + + 403 Forbidden + + +

Directory access is forbidden.

+ + +``` + +--- + +## 3. Input Validation (CRÍTICO) + +### Usar Input Class do CodeIgniter + +```php +// CORRECTO - Input class escapa automaticamente +$name = $this->input->post('name'); +$id = $this->input->get('id'); +$data = $this->input->post(); // Array de todos os POST + +// ERRADO - Nunca usar $_POST/$_GET directamente +$name = $_POST['name']; // VULNERÁVEL! +``` + +### Sanitização Adicional + +```php +// Inteiros +$id = (int) $this->input->post('id'); + +// Escape para output HTML +$name = html_escape($this->input->post('name')); + +// Strip tags +$clean = strip_tags($this->input->post('content')); + +// XSS clean (built-in) +$safe = $this->security->xss_clean($this->input->post('data')); +``` + +--- + +## 4. CSRF Protection + +O Perfex CRM tem CSRF activado por defeito. + +### Em Formulários (OBRIGATÓRIO) + +```php + + + + + +``` + +**NUNCA usar HTML `` directo:** + +```html + + +``` + +### Em AJAX (jQuery) + +jQuery do Perfex inclui CSRF automaticamente. Para outras bibliotecas: + +```javascript +// Obter token CSRF +var csrfName = 'security->get_csrf_token_name(); ?>'; +var csrfHash = 'security->get_csrf_hash(); ?>'; + +// Incluir em requests +$.ajax({ + url: admin_url + 'meu_modulo/ajax_save', + type: 'POST', + data: { + [csrfName]: csrfHash, + name: $('#name').val() + } +}); +``` + +### Excluir URLs de CSRF (v2.9.0+) + +Para webhooks que não podem incluir CSRF: + +```php +// modules/meu_modulo/config/csrf_exclude_uris.php + +db->where('id', $id); +$this->db->where('status', $status); +$result = $this->db->get(db_prefix() . 'meu_modulo')->result(); + +// CORRECTO - Binding de parâmetros +$sql = "SELECT * FROM " . db_prefix() . "meu_modulo WHERE id = ? AND status = ?"; +$result = $this->db->query($sql, [$id, $status])->result(); +``` + +### NUNCA Concatenar Input + +```php +// ERRADO - SQL Injection vulnerável! +$id = $_GET['id']; +$sql = "SELECT * FROM tblmeu_modulo WHERE id = " . $id; + +// ERRADO - Mesmo com input class +$id = $this->input->get('id'); +$sql = "SELECT * FROM tblmeu_modulo WHERE id = " . $id; // VULNERÁVEL! +``` + +--- + +## 6. XSS Prevention + +### Em Views (SEMPRE) + +```php + +name); ?> + + + +name; ?> +``` + +### Função html_escape() + +```php +// Texto simples +echo html_escape($string); + +// Para atributos HTML +echo html_escape($string, true); +``` + +--- + +## 7. File Upload Security + +```php +public function upload() +{ + // Configurar upload + $config['upload_path'] = './uploads/meu_modulo/'; + $config['allowed_types'] = 'pdf|doc|docx|xls|xlsx'; // NUNCA permitir php! + $config['max_size'] = 2048; // KB + $config['encrypt_name'] = true; // Nome aleatório + + $this->load->library('upload', $config); + + if (!$this->upload->do_upload('ficheiro')) { + // Erro + $error = $this->upload->display_errors(); + set_alert('danger', $error); + } else { + // Sucesso + $data = $this->upload->data(); + $filename = $data['file_name']; + } +} +``` + +### Tipos Permitidos \ No newline at end of file diff --git a/perfex-dev/skills/perfex-views/SKILL.md b/perfex-dev/skills/perfex-views/SKILL.md new file mode 100644 index 0000000..38a155c --- /dev/null +++ b/perfex-dev/skills/perfex-views/SKILL.md @@ -0,0 +1,196 @@ +--- +name: perfex-views +description: Views e templates para módulos Perfex CRM — init_head(), init_tail(), views admin e cliente, passagem de dados. Baseado apenas na documentação oficial. +layer: wiki +--- + +# /perfex-views - Views Perfex CRM + +Sistema de views e templates para módulos. **Zero assumptions, zero hallucinations** - apenas documentação oficial. + +--- + +## Documentação Base + +- [Module Views](https://help.perfexcrm.com/module-views/) +- [CodeIgniter Views](https://codeigniter.com/userguide3/general/views.html) + +--- + +## Estrutura de Pastas + +``` +modules/meu_modulo/ +└── views/ + ├── index.php # Lista principal + ├── form.php # Formulário create/edit + ├── view.php # Detalhe + ├── client_index.php # Lista cliente + ├── client_view.php # Detalhe cliente + ├── partials/ + │ ├── _table.php # Partial tabela + │ └── _filters.php # Partial filtros + └── widgets/ + └── dashboard.php # Widget dashboard +``` + +--- + +## Views Admin (Área Staff) + +### Template Base Admin + +```php + + + +
+
+
+
+
+
+ + +

+ +
+
+
+
+
+
+ + + + +``` + +### Funções Essenciais + +| Função | Descrição | +|--------|-----------| +| `init_head()` | Inclui header, sidebar, CSS, meta tags | +| `init_tail()` | Inclui footer, JavaScript, fecha documento | + +--- + +## Carregar Views + +### No Controller + +```php +// Básico +$this->load->view('form'); + +// Com dados +$data = [ + 'item' => $item, + 'clients' => $clients, +]; +$this->load->view('form', $data); + +// Retornar como string (não fazer echo) +$html = $this->load->view('partial', $data, true); +``` + +### Fora de Controllers (Helpers, Classes) + +```php +function meu_modulo_render_widget() +{ + $CI = &get_instance(); + $CI->load->view('meu_modulo/widgets/dashboard'); +} + +// Com dados +function meu_modulo_render_table($items) +{ + $CI = &get_instance(); + $CI->load->view('meu_modulo/partials/_table', ['items' => $items]); +} +``` + +--- + +## Views Cliente (Área Cliente) + +### No Controller (ClientsController) + +```php +public function index() +{ + $this->data([ + 'items' => $items, + 'title' => 'Meu Módulo', + ]); + $this->title(_l('meu_modulo_title')); + $this->view('client_index'); + $this->layout(); // Aplica tema do cliente +} +``` + +### Template Base Cliente + +```php + + +
+
+

+
+ + + +

name); ?>

+ + +
+
+``` + +--- + +## Exemplos Completos + +### Lista com Tabela (Admin) + +```php + + + +
+
+
+
+
+
+ +
+ + + + + + +
+ +
+
+ + + + + + + + + + + + + + + + \ No newline at end of file
id; ?>name); ?>