commit 3151b792ce5ac3687556f7bd77f29a5bd6e01dc8 Author: Marco Antonio Vivas Date: Fri Aug 8 22:14:40 2025 -0300 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/README.md b/README.md new file mode 100644 index 0000000..73c0f41 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# Simple File List + +Um plugin simples para WordPress que permite o envio, organização e listagem de arquivos com categorias. + +--- + +## Instalação + +1. **Faça upload dos arquivos do plugin** para a pasta: + ``` + wp-content/plugins/simple-file-list + ``` +2. **Ative o plugin** no painel do WordPress em **Plugins > Plugins instalados**. + +--- + +## Como Usar + +### Exibir o gerenciador de arquivos no site + +Adicione o shortcode abaixo em qualquer página ou post onde deseja exibir o gerenciador de arquivos: + +``` +[simple_file_list] +``` + +--- + +## Funcionalidades + +- **Upload de arquivos** (restrito a usuários logados com permissão) +- **Campos de descrição e categoria** para cada arquivo enviado +- **Tabela de arquivos** com miniatura, nome, categoria, tamanho, data e ações (abrir/baixar) +- **Filtro por categoria** integrado à tabela +- **Exclusão de arquivos** (apenas para usuários autorizados) +- **Limites configuráveis** de tamanho, quantidade e tipos de arquivos + +--- + +## Configurações + +Acesse **Configurações > File List** no menu do WordPress para: + +- Definir o número máximo de arquivos por envio +- Definir o tamanho máximo de cada arquivo (MB) +- Definir as extensões de arquivos permitidas (ex: `jpg,png,pdf`) + +--- + +## Observações + +- Apenas usuários logados com permissão de upload podem enviar ou excluir arquivos. +- Os arquivos são armazenados em: + `wp-content/uploads/simple-file-list/` +- O plugin utiliza as cores e variáveis CSS do tema para melhor integração visual. + +--- + +## Suporte + +Para dúvidas ou sugestões, abra uma issue ou entre em contato com \ No newline at end of file diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 0000000..0484689 --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1,278 @@ +/* Simple File List Styles */ +.sfl-upload-container { + background: #fff; + border-radius: var(--borda-radius); + box-shadow: var(--sombra); + padding: 25px; + margin-bottom: 30px; +} + +.sfl-upload-container h2 { + color: var(--azul-principal); + margin-top: 0; + font-size: 1.5rem; +} + +.sfl-upload-area { + border: 2px dashed var(--azul-claro); + border-radius: var(--borda-radius); + padding: 30px; + text-align: center; + transition: var(--transicao); + margin-bottom: 20px; +} + +.sfl-upload-area:hover { + border-color: var(--azul-brilhante); + background-color: rgba(0, 166, 251, 0.05); +} + +.sfl-browse-btn { + background: var(--azul-principal); + color: white; + border: none; + padding: 10px 20px; + border-radius: var(--borda-radius); + cursor: pointer; + font-weight: 500; + transition: var(--transicao); + margin-bottom: 15px; +} + +.sfl-browse-btn:hover { + background: var(--azul-brilhante); + transform: translateY(-2px); +} + +.sfl-upload-details { + margin-top: 20px; + color: var(--azul-escuro); + font-size: 0.9rem; +} + +.sfl-upload-details p { + margin: 5px 0; +} + +.sfl-file-meta { + margin-top: 20px; + text-align: left; +} + +.sfl-meta-fields { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 15px; + margin-bottom: 15px; +} + +.sfl-meta-fields label { + display: block; + margin-bottom: 5px; + font-weight: 500; + color: var(--azul-escuro); +} + +.sfl-meta-fields input { + width: 100%; + padding: 8px 12px; + border: 1px solid var(--cinza); + border-radius: var(--borda-radius); + transition: var(--transicao); +} + +.sfl-meta-fields input:focus { + border-color: var(--azul-brilhante); + outline: none; + box-shadow: 0 0 0 2px rgba(0, 166, 251, 0.2); +} + +.sfl-upload-btn { + background: var(--verde); + color: white; + border: none; + padding: 10px 20px; + border-radius: var(--borda-radius); + cursor: pointer; + font-weight: 500; + transition: var(--transicao); +} + +.sfl-upload-btn:hover { + background: #3d8b40; + transform: translateY(-2px); +} + +.sfl-progress { + margin-top: 20px; + background: var(--cinza); + border-radius: 20px; + height: 10px; + position: relative; +} + +.sfl-progress-bar { + background: var(--azul-brilhante); + border-radius: 20px; + height: 100%; + width: 0%; + transition: width 0.3s ease; +} + +.sfl-progress-text { + position: absolute; + top: -25px; + right: 0; + font-size: 0.8rem; + color: var(--azul-escuro); +} + +.sfl-file-list { + width: 100%; + overflow-x: auto; +} + +.sfl-file-list table { + width: 100%; + border-collapse: collapse; + background: white; + border-radius: var(--borda-radius); + box-shadow: var(--sombra); +} + +.sfl-file-list th { + background: var(--azul-principal); + color: white; + padding: 12px 15px; + text-align: left; +} + +.sfl-file-list th, .sfl-file-list td { + vertical-align: middle; +} + +.sfl-file-list td { + padding: 12px 15px; + border-bottom: 1px solid var(--cinza); +} + +.sfl-file-list tr:last-child td { + border-bottom: none; +} + +.sfl-file-list tr:hover { + background-color: rgba(0, 100, 148, 0.05); +} + +.sfl-file-description { + margin: 5px 0 0; + color: var(--azul-claro); + font-size: 0.9rem; +} + +.sfl-file-actions { + display: flex; + gap: 10px; +} + +.sfl-action-link { + display: inline-flex; + align-items: center; + gap: 6px; + background: var(--azul-principal); + color: #fff !important; /* texto branco */ + border: none; + border-radius: var(--borda-radius); + padding: 7px 16px; + font-weight: 500; + font-size: 0.95rem; + text-decoration: none !important; /* remove underline */ + transition: background 0.2s, box-shadow 0.2s, color 0.2s; + box-shadow: 0 1px 3px rgba(0,0,0,0.04); + cursor: pointer; + margin-right: 6px; +} + +.sfl-action-link:last-child { + margin-right: 0; +} + +.sfl-action-link:hover { + background: var(--azul-brilhante); + color: #fff !important; /* mantém texto branco no hover */ + text-decoration: none !important; /* garante sem underline no hover */ + box-shadow: 0 2px 8px rgba(0,166,251,0.08); +} + +/* Opcional: Adicione ícones usando Dashicons */ +.sfl-action-link[data-action="abrir"]::before { + font-family: "Dashicons"; + content: "\f179"; + font-size: 1.1em; + margin-right: 4px; +} +.sfl-action-link[data-action="baixar"]::before { + font-family: "Dashicons"; + content: "\f316"; + font-size: 1.1em; + margin-right: 4px; +} +.sfl-action-link[data-action="copiar"]::before { + font-family: "Dashicons"; + content: "\f481"; + font-size: 1.1em; + margin-right: 4px; +} + +/* Drag and drop styles */ +.sfl-upload-area.drag-over { + background-color: rgba(0, 166, 251, 0.1); + border-color: var(--azul-brilhante); +} + +/* Responsive styles */ +@media (max-width: 768px) { + .sfl-meta-fields { + grid-template-columns: 1fr; + } + + .sfl-file-actions { + flex-direction: column; + gap: 5px; + } +} + +.sfl-file-icon { + display: inline-block; + vertical-align: middle; + width: 40px; + height: 40px; + text-align: center; + line-height: 40px; + font-size: 32px; + color: #0073aa; +} + +.sfl-filter-form { + margin-bottom: 0; + display: flex; + align-items: center; + gap: 10px; + background: none; + padding: 0; +} +.sfl-filter-form label { + margin: 0; + color: var(--azul-principal); /* azul para o label */ + font-weight: 600; +} + +.sfl-filter-form select { + padding: 6px 12px; + border-radius: var(--borda-radius); + border: 1px solid var(--cinza); + margin-left: 0; + color: #222; /* preto para o texto das opções */ + background: #fff; + font-weight: 500; +} \ No newline at end of file diff --git a/assets/js/script.js b/assets/js/script.js new file mode 100644 index 0000000..4aea3f6 --- /dev/null +++ b/assets/js/script.js @@ -0,0 +1,155 @@ +jQuery(document).ready(function($) { + // Handle file selection + $('#sfl-browse-btn').on('click', function(e) { + e.preventDefault(); + $('#sfl-file-input').click(); + }); + + $('#sfl-file-input').on('change', function() { + const files = this.files; + if (files.length > 0) { + const fileNames = Array.from(files).map(file => file.name).join(', '); + $('#sfl-file-info').text(`${files.length} arquivo(s) selecionado(s): ${fileNames}`); + $('.sfl-file-meta').show(); + } else { + $('#sfl-file-info').text('Nenhum arquivo selecionado.'); + $('.sfl-file-meta').hide(); + } + }); + + // Handle drag and drop + const dropZone = $('#sfl-drop-zone')[0]; + + if (dropZone) { + dropZone.addEventListener('dragover', function(e) { + e.preventDefault(); + $(this).addClass('drag-over'); + }); + + dropZone.addEventListener('dragleave', function() { + $(this).removeClass('drag-over'); + }); + + dropZone.addEventListener('drop', function(e) { + e.preventDefault(); + $(this).removeClass('drag-over'); + + const files = e.dataTransfer.files; + if (files.length > 0) { + $('#sfl-file-input')[0].files = files; + const fileNames = Array.from(files).map(file => file.name).join(', '); + $('#sfl-file-info').text(`${files.length} file(s) selected: ${fileNames}`); + $('.sfl-file-meta').show(); + } + }); + } + + // Handle file upload + $('#sfl-upload-btn').on('click', function(e) { + e.preventDefault(); + + const files = $('#sfl-file-input')[0].files; + if (!files || files.length === 0) { + alert('Selecione pelo menos um arquivo para enviar.'); + return; + } + + const maxFiles = parseInt(''); + if (files.length > maxFiles) { + alert(`Você pode enviar no máximo ${maxFiles} arquivos de uma vez.`); + return; + } + + const description = $('#sfl-file-description').val(); + const category = $('#sfl-file-category').val(); + + $('.sfl-progress').show(); + + const formData = new FormData(); + formData.append('action', 'sfl_upload_file'); + formData.append('security', sfl_ajax.nonce); + formData.append('description', description); + formData.append('category', category); + + for (let i = 0; i < files.length; i++) { + formData.append('sfl_file_upload', files[i]); + } + + $.ajax({ + url: sfl_ajax.ajax_url, + type: 'POST', + data: formData, + processData: false, + contentType: false, + xhr: function() { + const xhr = new window.XMLHttpRequest(); + xhr.upload.addEventListener('progress', function(e) { + if (e.lengthComputable) { + const percent = Math.round((e.loaded / e.total) * 100); + $('.sfl-progress-bar').css('width', percent + '%'); + $('.sfl-progress-text').text(percent + '%'); + } + }, false); + return xhr; + }, + success: function(response) { + if (response.success) { + alert('Arquivos enviados com sucesso!'); + location.reload(); + } else { + alert('Erro: ' + response.data); + } + }, + error: function(xhr, status, error) { + alert('Erro: ' + error); + }, + complete: function() { + $('.sfl-progress').hide(); + $('.sfl-progress-bar').css('width', '0%'); + $('.sfl-progress-text').text('0%'); + } + }); + }); + + // Handle file deletion + $('.sfl-delete-file').on('click', function() { + if (!confirm('Tem certeza que deseja excluir este arquivo?')) { + return; + } + + const fileId = $(this).data('file-id'); + + $.ajax({ + url: sfl_ajax.ajax_url, + type: 'POST', + data: { + action: 'sfl_delete_file', + security: sfl_ajax.nonce, + file_id: fileId + }, + success: function(response) { + if (response.success) { + alert('Arquivo excluído com sucesso!'); + location.reload(); + } else { + alert('Erro: ' + response.data); + } + }, + error: function(xhr, status, error) { + alert('Erro: ' + error); + } + }); + }); + + // Handle copy link + $('.sfl-copy-link').on('click', function(e) { + e.preventDefault(); + const fileUrl = $(this).data('file-url'); + + navigator.clipboard.writeText(fileUrl).then(function() { + alert('Link copiado para a área de transferência!'); + }, function() { + alert('Falha ao copiar o link. Tente novamente.'); + }); + }); +}); \ No newline at end of file diff --git a/includes/admin.php b/includes/admin.php new file mode 100644 index 0000000..659d2e4 --- /dev/null +++ b/includes/admin.php @@ -0,0 +1,111 @@ + +
+

Lista de Arquivos Simples

+ +

Configurações salvas com sucesso.

'; + } + ?> + +
+
+

Arquivos Enviados

+ + +
+
+ + +
+

Configurações da Lista de Arquivos

+ +
+ + + + + + + + + + + + + +
+ +

Quantidade máxima de arquivos que podem ser enviados de uma vez.

+
+ +

Tamanho máximo para cada arquivo enviado em megabytes.

+
+ +

Lista separada por vírgulas das extensões permitidas (ex: jpg,png,pdf).

+
+ + +
+
+ prefix . 'simple_file_list'; + + $files = $wpdb->get_results("SELECT * FROM $table_name ORDER BY upload_date DESC"); + + if (empty($files)) { + echo '

Nenhum arquivo foi enviado ainda.

'; + return; + } + + ?> +
+ + + + + + + + + + + + + + + + + + + + + + + +
NomeTamanhoDataDescriçãoCategoriaAções
file_name); ?>file_size); ?>upload_date)); ?>description); ?>category); ?> + Visualizar + Baixar + +
+
+ +
+

Enviar Arquivos

+ +
+ + +

Nenhum arquivo selecionado.

+ +
+

Limite de Arquivos: arquivos

+

Tamanho Máximo: MB por arquivo.

+

Tipos Permitidos:

+

Arraste e solte arquivos aqui ou use o botão Selecionar.

+
+ + + + +
+
+ prefix . 'simple_file_list'; + + // Obtenha todas as categorias distintas + $categorias = $wpdb->get_col("SELECT DISTINCT category FROM $table_name WHERE category IS NOT NULL AND category != '' ORDER BY category ASC"); + $current_cat = isset($_GET['sfl_categoria']) ? sanitize_text_field($_GET['sfl_categoria']) : ''; + if ($current_cat) { + $files = $wpdb->get_results($wpdb->prepare("SELECT * FROM $table_name WHERE category = %s ORDER BY upload_date DESC", $current_cat)); + } else { + $files = $wpdb->get_results("SELECT * FROM $table_name ORDER BY upload_date DESC"); + } + + if (empty($files)) { + echo '

Nenhum arquivo disponível.

'; + return; + } + + ?> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + $value) { + if ($key !== 'sfl_categoria') { + echo ''; + } + } + ?> +
+
MiniaturaNomeCategoriaTamanhoDataAbrirBaixar
+ file_name, PATHINFO_EXTENSION))); + if (in_array($icon, ['file-image'])) { + echo 'thumb'; + } else { + echo ''; + } + ?> + + file_name); ?> + description)): ?> +

description); ?>

+ +
category); ?>file_size); ?>upload_date)); ?> + Abrir + + Baixar +
+
+ 'media-default', + 'doc' => 'media-document', + 'docx' => 'media-document', + 'xls' => 'media-spreadsheet', + 'xlsx' => 'media-spreadsheet', + 'ppt' => 'media-interactive', + 'pptx' => 'media-interactive', + 'jpg' => 'format-image', + 'jpeg' => 'format-image', + 'png' => 'format-image', + 'gif' => 'format-image', + 'mp3' => 'format-audio', + 'wav' => 'format-audio', + 'mp4' => 'format-video', + 'mov' => 'format-video', + 'zip' => 'media-archive', + 'rar' => 'media-archive', + 'txt' => 'media-text', + ); + return isset($icons[$file_type]) ? $icons[$file_type] : 'media-default'; +} + +function sfl_format_file_size($bytes) { + if ($bytes >= 1073741824) { + return number_format($bytes / 1073741824, 2) . ' GB'; + } elseif ($bytes >= 1048576) { + return number_format($bytes / 1048576, 2) . ' MB'; + } elseif ($bytes >= 1024) { + return number_format($bytes / 1024, 2) . ' KB'; + } else { + return $bytes . ' bytes'; + } +} \ No newline at end of file diff --git a/simple-file-list.php b/simple-file-list.php new file mode 100644 index 0000000..2196311 --- /dev/null +++ b/simple-file-list.php @@ -0,0 +1,235 @@ +prefix . 'simple_file_list'; + + $charset_collate = $wpdb->get_charset_collate(); + + $sql = "CREATE TABLE $table_name ( + id mediumint(9) NOT NULL AUTO_INCREMENT, + file_name varchar(255) NOT NULL, + file_path varchar(255) NOT NULL, + file_url varchar(255) NOT NULL, + file_size varchar(20) NOT NULL, + file_type varchar(100) NOT NULL, + description text, + category varchar(100), + upload_date datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, + user_id bigint(20) NOT NULL, + PRIMARY KEY (id) + ) $charset_collate;"; + + require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); + dbDelta($sql); + + // Add default options + add_option('sfl_max_files', 10); + add_option('sfl_max_size', 64); // in MB (alterado para 64MB) + add_option( + 'sfl_allowed_types', + 'jpg,jpeg,png,tif,pdf,mov,mp4,mp3,zip,doc,docx,xls,xlsx,ppt,pptx' + // Adicionadas extensões do Word, Excel e PowerPoint + ); +} + +function sfl_deactivate_plugin() { + // Clean up if needed +} + +// Enqueue scripts and styles +add_action('wp_enqueue_scripts', 'sfl_enqueue_scripts'); +function sfl_enqueue_scripts() { + wp_enqueue_style('sfl-style', SFL_PLUGIN_URL . 'assets/css/style.css'); + wp_enqueue_style('dashicons'); // Adicione esta linha + wp_enqueue_script('sfl-script', SFL_PLUGIN_URL . 'assets/js/script.js', array('jquery'), SFL_VERSION, true); + + // Localize script for AJAX + wp_localize_script('sfl-script', 'sfl_ajax', array( + 'ajax_url' => admin_url('admin-ajax.php'), + 'nonce' => wp_create_nonce('sfl-nonce') + )); +} + +// Add admin menu +add_action('admin_menu', 'sfl_admin_menu'); +function sfl_admin_menu() { + add_menu_page( + 'Simple File List', + 'File List', + 'manage_options', + 'simple-file-list', + 'sfl_admin_page', + 'dashicons-media-default', + 30 + ); + + add_submenu_page( + 'simple-file-list', + 'Settings', + 'Settings', + 'manage_options', + 'simple-file-list-settings', + 'sfl_settings_page' + ); +} + +// Shortcode for frontend display +add_shortcode('simple_file_list', 'sfl_display_file_list'); +function sfl_display_file_list($atts) { + ob_start(); + + if (is_user_logged_in()) { + sfl_render_upload_form(); + } + + sfl_render_file_list(); + + return ob_get_clean(); +} + +// Handle file upload +add_action('wp_ajax_sfl_upload_file', 'sfl_handle_file_upload'); +add_action('wp_ajax_nopriv_sfl_upload_file', 'sfl_handle_file_upload'); +function sfl_handle_file_upload() { + // Verify nonce + check_ajax_referer('sfl-nonce', 'security'); + + if (!is_user_logged_in()) { + wp_send_json_error('You must be logged in to upload files.'); + } + + if (!isset($_FILES['sfl_file_upload'])) { + wp_send_json_error('No file was uploaded.'); + } + + $file = $_FILES['sfl_file_upload']; + $file_name = sanitize_file_name($file['name']); + $file_tmp = $file['tmp_name']; + $file_size = $file['size']; + $file_error = $file['error']; + + // Check for upload errors + if ($file_error !== UPLOAD_ERR_OK) { + wp_send_json_error('Upload error: ' . $file_error); + } + + // Check file size + $max_size = get_option('sfl_max_size') * 1024 * 1024; // Convert MB to bytes + if ($file_size > $max_size) { + wp_send_json_error('File size exceeds maximum allowed size.'); + } + + // Check file type + $allowed_types = explode(',', get_option('sfl_allowed_types')); + $file_ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION)); + + if (!in_array($file_ext, $allowed_types)) { + wp_send_json_error('File type not allowed.'); + } + + // Generate unique filename if file exists + $counter = 1; + $original_name = pathinfo($file_name, PATHINFO_FILENAME); + $new_file_name = $file_name; + + while (file_exists(SFL_UPLOAD_DIR . $new_file_name)) { + $new_file_name = $original_name . '-' . $counter . '.' . $file_ext; + $counter++; + } + + // Move uploaded file + if (move_uploaded_file($file_tmp, SFL_UPLOAD_DIR . $new_file_name)) { + // Save file info to database + global $wpdb; + $table_name = $wpdb->prefix . 'simple_file_list'; + + $data = array( + 'file_name' => $new_file_name, + 'file_path' => SFL_UPLOAD_DIR . $new_file_name, + 'file_url' => SFL_UPLOAD_URL . $new_file_name, + 'file_size' => size_format($file_size, 2), + 'file_type' => $file_ext, + 'description' => sanitize_text_field($_POST['description']), + 'category' => sanitize_text_field($_POST['category']), + 'user_id' => get_current_user_id() + ); + + $wpdb->insert($table_name, $data); + + wp_send_json_success('File uploaded successfully.'); + } else { + wp_send_json_error('Error moving uploaded file.'); + } +} + +// Handle file deletion +add_action('wp_ajax_sfl_delete_file', 'sfl_handle_file_delete'); +function sfl_handle_file_delete() { + // Verify nonce and permissions + check_ajax_referer('sfl-nonce', 'security'); + + if (!is_user_logged_in() || !current_user_can('upload_files')) { + wp_send_json_error('You do not have permission to delete files.'); + } + + $file_id = intval($_POST['file_id']); + + global $wpdb; + $table_name = $wpdb->prefix . 'simple_file_list'; + + // Get file info + $file = $wpdb->get_row($wpdb->prepare( + "SELECT * FROM $table_name WHERE id = %d", + $file_id + )); + + if (!$file) { + wp_send_json_error('File not found.'); + } + + // Delete file from server + if (file_exists($file->file_path)) { + unlink($file->file_path); + } + + // Delete record from database + $wpdb->delete($table_name, array('id' => $file_id)); + + wp_send_json_success('File deleted successfully.'); +} \ No newline at end of file