Files
claude-plugins/perfex-dev/skills/perfex-forms/SKILL.md
Emanuel Almeida 2cb3210962 feat: adiciona 12 plugins Descomplicar ao marketplace
Plugins: automacao, crm-ops, design-media, dev-tools, gestao,
infraestrutura, marketing, negocio, perfex-dev, project-manager,
wordpress + hello-plugin (existente).

Totais: 83 skills, 44 agents, 12 datasets.json

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 21:41:24 +00:00

16 KiB

name, description, author, version, quality_score, user_invocable, desk_task
name description author version quality_score user_invocable desk_task
perfex-forms Perfex CRM forms handling. form_open(), CSRF tokens, validation, AJAX. Based on official documentation only. Use when user mentions "perfex form", "form_open", "csrf token", "validation perfex". Descomplicar® Crescimento Digital 1.0.0 70 true null

/perfex-forms - Formulários Perfex CRM

Gestão de formulários em módulos. Zero assumptions, zero hallucinations - apenas documentação oficial.


Documentação Base


Regra Fundamental: form_open()

SEMPRE usar form_open() para gerar token CSRF automaticamente.

<?php echo form_open(admin_url('meu_modulo/save')); ?>
    <!-- Inputs aqui -->
<?php echo form_close(); ?>

NUNCA usar <form> HTML directo:

<!-- ERRADO - Sem CSRF protection -->
<form action="..." method="post">

Sintaxe form_open()

// Básico
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 formulário
echo form_close();

Elementos de Formulário

Input Text

<div class="form-group">
    <label for="name" class="control-label">
        <?php echo _l('name'); ?>
        <span class="text-danger">*</span>
    </label>
    <input type="text"
           id="name"
           name="name"
           class="form-control"
           value="<?php echo isset($item) ? html_escape($item->name) : ''; ?>"
           required>
</div>

Textarea

<div class="form-group">
    <label for="description" class="control-label">
        <?php echo _l('description'); ?>
    </label>
    <textarea id="description"
              name="description"
              class="form-control"
              rows="4"><?php echo isset($item) ? html_escape($item->description) : ''; ?></textarea>
</div>

Select

<div class="form-group">
    <label for="client_id" class="control-label">
        <?php echo _l('client'); ?>
    </label>
    <select id="client_id" name="client_id" class="selectpicker"
            data-live-search="true" data-width="100%">
        <option value=""><?php echo _l('select'); ?></option>
        <?php foreach ($clients as $client): ?>
            <option value="<?php echo $client['userid']; ?>"
                <?php echo (isset($item) && $item->client_id == $client['userid']) ? 'selected' : ''; ?>>
                <?php echo html_escape($client['company']); ?>
            </option>
        <?php endforeach; ?>
    </select>
</div>

Select com AJAX (Clientes)

<div class="form-group">
    <label for="clientid" class="control-label">
        <?php echo _l('client'); ?>
    </label>
    <select id="clientid" name="clientid" data-live-search="true"
            data-width="100%" class="ajax-search"
            data-none-selected-text="<?php echo _l('dropdown_non_selected_tex'); ?>">
        <?php if (isset($item) && $item->clientid): ?>
            <option value="<?php echo $item->clientid; ?>" selected>
                <?php echo html_escape(get_company_name($item->clientid)); ?>
            </option>
        <?php endif; ?>
    </select>
</div>

Checkbox

<div class="checkbox checkbox-primary">
    <input type="checkbox"
           id="is_active"
           name="is_active"
           value="1"
           <?php echo (isset($item) && $item->is_active == 1) ? 'checked' : ''; ?>>
    <label for="is_active"><?php echo _l('active'); ?></label>
</div>

Radio

<div class="form-group">
    <label class="control-label"><?php echo _l('status'); ?></label>
    <div class="radio radio-primary">
        <input type="radio" name="status" id="status_pending" value="pending"
               <?php echo (!isset($item) || $item->status == 'pending') ? 'checked' : ''; ?>>
        <label for="status_pending"><?php echo _l('pending'); ?></label>
    </div>
    <div class="radio radio-primary">
        <input type="radio" name="status" id="status_active" value="active"
               <?php echo (isset($item) && $item->status == 'active') ? 'checked' : ''; ?>>
        <label for="status_active"><?php echo _l('active'); ?></label>
    </div>
</div>

Date Picker

<div class="form-group">
    <label for="date" class="control-label">
        <?php echo _l('date'); ?>
    </label>
    <input type="text"
           id="date"
           name="date"
           class="form-control datepicker"
           value="<?php echo isset($item) ? _d($item->date) : _d(date('Y-m-d')); ?>"
           autocomplete="off">
</div>

Date Time Picker

<div class="form-group">
    <label for="datetime" class="control-label">
        <?php echo _l('date_time'); ?>
    </label>
    <input type="text"
           id="datetime"
           name="datetime"
           class="form-control datetimepicker"
           value="<?php echo isset($item) ? _dt($item->datetime) : ''; ?>"
           autocomplete="off">
</div>

File Upload

<?php echo form_open_multipart(admin_url('meu_modulo/upload')); ?>

<div class="form-group">
    <label for="attachment" class="control-label">
        <?php echo _l('attachment'); ?>
    </label>
    <input type="file" id="attachment" name="attachment" class="form-control">
</div>

<?php echo form_close(); ?>

Hidden

<input type="hidden" name="id" value="<?php echo $item->id; ?>">

Validação Client-Side

O Perfex usa jQuery Validation. Usar appValidateForm():

<?php init_tail(); ?>
<script>
$(function(){
    appValidateForm($('#form-meu-modulo'), {
        name: {
            required: true,
            minlength: 3
        },
        email: {
            required: true,
            email: true
        },
        amount: {
            required: true,
            number: true,
            min: 0
        }
    });
});
</script>
</body>
</html>

Regras de Validação

Regra Descrição
required: true Campo obrigatório
email: true Email válido
number: true Número
digits: true Apenas dígitos
minlength: N Mínimo N caracteres
maxlength: N Máximo N caracteres
min: N Valor mínimo
max: N Valor máximo
equalTo: '#field' Igual a outro campo

Validação Server-Side

public function save()
{
    if (!$this->input->post()) {
        redirect(admin_url('meu_modulo'));
    }

    // Validação
    $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) {
        // Erro de validação
        set_alert('danger', validation_errors());
        redirect(admin_url('meu_modulo/create'));
    }

    // Dados válidos, processar
    $data = [
        'name'   => $this->input->post('name'),
        'email'  => $this->input->post('email'),
        'amount' => $this->input->post('amount'),
    ];

    // ... guardar
}

AJAX com jQuery

POST Simples

$.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);
    }
});

Com $.ajax()

$.ajax({
    url: admin_url + 'meu_modulo/ajax_save',
    type: 'POST',
    data: $('#form-meu-modulo').serialize(),
    dataType: 'json',
    success: function(response) {
        if (response.success) {
            alert_float('success', response.message);
            // Redirecionar ou actualizar UI
        } else {
            alert_float('danger', response.message);
        }
    },
    error: function() {
        alert_float('danger', 'Erro de comunicação');
    }
});

Controller AJAX

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:

// modules/meu_modulo/config/csrf_exclude_uris.php

<?php

defined('BASEPATH') or exit('No direct script access allowed');

return [
    'meu_modulo/webhook',
    'meu_modulo/callback',
    'meu_modulo/api/.*',  // Regex
];

Formulário Completo Exemplo

View

<?php defined('BASEPATH') or exit('No direct script access allowed'); ?>
<?php init_head(); ?>

<div id="wrapper">
    <div class="content">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <div class="panel_s">
                    <div class="panel-body">

                        <h4 class="no-margin">
                            <?php echo isset($item) ? _l('edit') : _l('create'); ?>
                            <?php echo _l('meu_modulo_item'); ?>
                        </h4>
                        <hr class="hr-panel-heading" />

                        <?php
                        $action = isset($item)
                            ? admin_url('meu_modulo/save/' . $item->id)
                            : admin_url('meu_modulo/save');
                        echo form_open($action, ['id' => 'form-meu-modulo']);
                        ?>

                            <!-- Nome -->
                            <div class="form-group">
                                <label for="name" class="control-label">
                                    <?php echo _l('name'); ?>
                                    <span class="text-danger">*</span>
                                </label>
                                <input type="text" id="name" name="name"
                                       class="form-control"
                                       value="<?php echo isset($item) ? html_escape($item->name) : ''; ?>">
                            </div>

                            <!-- Cliente -->
                            <div class="form-group">
                                <label for="client_id" class="control-label">
                                    <?php echo _l('client'); ?>
                                </label>
                                <select id="client_id" name="client_id"
                                        class="selectpicker"
                                        data-live-search="true"
                                        data-width="100%">
                                    <option value=""><?php echo _l('select'); ?></option>
                                    <?php foreach ($clients as $client): ?>
                                        <option value="<?php echo $client['userid']; ?>"
                                            <?php echo (isset($item) && $item->client_id == $client['userid']) ? 'selected' : ''; ?>>
                                            <?php echo html_escape($client['company']); ?>
                                        </option>
                                    <?php endforeach; ?>
                                </select>
                            </div>

                            <!-- Valor -->
                            <div class="form-group">
                                <label for="amount" class="control-label">
                                    <?php echo _l('amount'); ?>
                                </label>
                                <input type="number" id="amount" name="amount"
                                       class="form-control" step="0.01" min="0"
                                       value="<?php echo isset($item) ? $item->amount : '0.00'; ?>">
                            </div>

                            <!-- Data -->
                            <div class="form-group">
                                <label for="date" class="control-label">
                                    <?php echo _l('date'); ?>
                                </label>
                                <input type="text" id="date" name="date"
                                       class="form-control datepicker"
                                       value="<?php echo isset($item) ? _d($item->date) : _d(date('Y-m-d')); ?>"
                                       autocomplete="off">
                            </div>

                            <!-- Descrição -->
                            <div class="form-group">
                                <label for="description" class="control-label">
                                    <?php echo _l('description'); ?>
                                </label>
                                <textarea id="description" name="description"
                                          class="form-control"
                                          rows="4"><?php echo isset($item) ? html_escape($item->description) : ''; ?></textarea>
                            </div>

                            <!-- Activo -->
                            <div class="checkbox checkbox-primary">
                                <input type="checkbox" id="is_active" name="is_active" value="1"
                                       <?php echo (!isset($item) || $item->is_active == 1) ? 'checked' : ''; ?>>
                                <label for="is_active"><?php echo _l('active'); ?></label>
                            </div>

                            <!-- Botões -->
                            <div class="btn-bottom-toolbar text-right">
                                <button type="submit" class="btn btn-primary">
                                    <?php echo _l('save'); ?>
                                </button>
                                <a href="<?php echo admin_url('meu_modulo'); ?>" class="btn btn-default">
                                    <?php echo _l('cancel'); ?>
                                </a>
                            </div>

                        <?php echo form_close(); ?>

                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<?php init_tail(); ?>
<script>
$(function(){
    // Validação
    appValidateForm($('#form-meu-modulo'), {
        name: {
            required: true,
            minlength: 3
        },
        amount: {
            number: true,
            min: 0
        }
    });

    // Inicializar datepicker (já auto via classe)
    // Inicializar selectpicker (já auto via classe)
});
</script>
</body>
</html>

Anti-Patterns (NUNCA FAZER)

Anti-Pattern Risco Alternativa
<form> HTML directo CSRF bypass form_open()
Valores sem html_escape() XSS Escape sempre
Validação só client-side Bypass Validar server também
AJAX sem verificar is_ajax_request() Acesso directo Verificar sempre

Checklist Formulários

1. [ ] form_open() para todos os formulários
2. [ ] html_escape() em todos os values
3. [ ] Validação client-side (appValidateForm)
4. [ ] Validação server-side (form_validation)
5. [ ] Permissões verificadas no controller
6. [ ] CSRF excluído apenas para webhooks
7. [ ] form_open_multipart() para uploads
8. [ ] is_ajax_request() em endpoints AJAX

Versão: 1.0.0 | Autor: Descomplicar® Fonte: help.perfexcrm.com/working-with-forms