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>
16 KiB
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