Initial commit
This commit is contained in:
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
67
README.md
Normal file
67
README.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Gerenciador de Eventos (eventos-manager)
|
||||
|
||||
Plugin WordPress para cadastro, gerenciamento e exibição de eventos com calendário interativo, lista de próximos eventos e integração via shortcodes e widget.
|
||||
|
||||
## Funcionalidades
|
||||
|
||||
- Cadastro de eventos personalizados (CPT: `evento`)
|
||||
- Metabox para data, hora e local do evento
|
||||
- Taxonomia personalizada para tipos de evento
|
||||
- Calendário interativo (FullCalendar.js) com AJAX
|
||||
- Lista de próximos eventos filtrável por tipo e limite
|
||||
- Shortcodes para exibição do calendário, lista e widget
|
||||
- Página única customizada para eventos (`single-evento.php`)
|
||||
- Widget para exibir próximos eventos na sidebar
|
||||
- Página de ajuda no admin
|
||||
|
||||
## Instalação
|
||||
|
||||
1. Faça upload da pasta `eventos-manager` para o diretório `wp-content/plugins/`.
|
||||
2. Ative o plugin no painel do WordPress.
|
||||
3. O tipo de post "Evento" estará disponível no menu lateral.
|
||||
|
||||
## Shortcodes Disponíveis
|
||||
|
||||
- `[mostra-calendario]` — Exibe o calendário de eventos.
|
||||
- `[mostra-prox-eventos limit="5" tipo=""]` — Lista os próximos eventos. Parâmetros:
|
||||
- `limit`: número de eventos (padrão: 5)
|
||||
- `tipo`: slug do tipo de evento (opcional)
|
||||
- `[eventos-completo]` — Exibe calendário e lista de próximos eventos juntos.
|
||||
- `[mostra-calendario-widget]` — Exibe um calendário compacto para sidebar ou widgets.
|
||||
|
||||
## Widget
|
||||
|
||||
- Vá em **Aparência > Widgets** e adicione o widget "Próximos Eventos" à sua sidebar.
|
||||
- Configure o título e o limite de eventos a exibir.
|
||||
|
||||
## Custom Post Type e Taxonomia
|
||||
|
||||
- **Post Type:** `evento`
|
||||
- **Taxonomia:** `tipo_evento` (hierárquica)
|
||||
|
||||
## Templates
|
||||
|
||||
- O plugin inclui o template `single-evento.php` para exibição individual dos eventos.
|
||||
|
||||
## Scripts e Estilos
|
||||
|
||||
- FullCalendar.js e Moment.js são carregados automaticamente.
|
||||
- Estilos customizados para admin e frontend em `/assets/css/`.
|
||||
|
||||
## AJAX
|
||||
|
||||
- Os eventos do calendário são carregados via AJAX para melhor performance.
|
||||
|
||||
## Página de Ajuda
|
||||
|
||||
- Acesse em **Eventos > Ajuda** para instruções e exemplos de uso dos shortcodes.
|
||||
|
||||
## Autor
|
||||
|
||||
Marco Antonio Vivas
|
||||
|
||||
---
|
||||
|
||||
**Observação:**
|
||||
- Para personalizações avançadas, edite os arquivos em `includes/` e os templates conforme necessário.
|
||||
- Compatível com WordPress 5.0+.
|
57
assets/css/admin.css
Normal file
57
assets/css/admin.css
Normal file
@@ -0,0 +1,57 @@
|
||||
/* No changes needed, but ensure this file exists as referenced in class-eventos-admin.php */
|
||||
.evento-metabox {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
padding: 15px 0;
|
||||
}
|
||||
.meta-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
.meta-field label {
|
||||
font-weight: 600;
|
||||
}
|
||||
.meta-field input[type="date"],
|
||||
.meta-field input[type="time"],
|
||||
.meta-field input[type="text"] {
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
max-width: 300px;
|
||||
}
|
||||
#eventos-calendario-widget {
|
||||
min-width: 280px;
|
||||
min-height: 340px;
|
||||
font-size: 15px;
|
||||
max-height: 370px;
|
||||
overflow: hidden;
|
||||
border-radius: 18px; /* Mais arredondado */
|
||||
box-shadow: 0 2px 16px rgba(44,62,80,0.10);
|
||||
border: none;
|
||||
background: #fff;
|
||||
}
|
||||
#eventos-calendario-widget .fc {
|
||||
border-radius: 18px;
|
||||
box-shadow: 0 2px 16px rgba(44,62,80,0.10);
|
||||
background: #fff;
|
||||
padding: 16px 8px 8px 8px;
|
||||
}
|
||||
#eventos-calendario-widget .fc-day-number {
|
||||
border-radius: 50%;
|
||||
}
|
||||
#eventos-calendario-widget .fc-center h2 {
|
||||
color: #3498db;
|
||||
}
|
||||
#eventos-calendario-widget .fc-today .fc-day-number {
|
||||
background: #3498db;
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 8px rgba(52,152,219,0.15);
|
||||
}
|
||||
#eventos-calendario-widget .fc-has-event .fc-day-number {
|
||||
background: #217dbb;
|
||||
color: #fff;
|
||||
border: 2px solid #3498db;
|
||||
box-shadow: 0 2px 8px rgba(52,152,219,0.15);
|
||||
}
|
288
assets/css/eventos-manager.css
Normal file
288
assets/css/eventos-manager.css
Normal file
@@ -0,0 +1,288 @@
|
||||
/* CSS Mínimo para Estrutura do Gerenciador de Eventos */
|
||||
|
||||
.noticias-eventos-wrapper {
|
||||
display: flex;
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.noticias-col {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.eventos-col {
|
||||
flex: 1;
|
||||
min-width: 280px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
.proximos-eventos-lista {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.proximos-eventos-lista li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.evento-titulo a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.evento-tipo {
|
||||
margin-left: auto;
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
.noticias-eventos-wrapper {
|
||||
flex-direction: column;
|
||||
}
|
||||
.eventos-col {
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Estilos do Novo Calendário Offline --- */
|
||||
.em-calendario-wrapper {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
background: #fff;
|
||||
border: 1px solid #e5e5e5;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
||||
}
|
||||
.em-calendario-wrapper[data-view="full"] .em-dia-celula {
|
||||
height: 100px; /* Aumenta a altura para caber eventos */
|
||||
}
|
||||
.em-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
user-select: none;
|
||||
}
|
||||
.em-toolbar-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
.em-toolbar-center {
|
||||
justify-content: center;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.em-toolbar-right {
|
||||
justify-content: flex-end;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.em-mes-ano {
|
||||
font-size: 1.2em;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.em-nav-btn {
|
||||
background: #f7f7f7;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.em-view-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 22px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.em-nav-btn:hover {
|
||||
background: #e9e9e9;
|
||||
}
|
||||
|
||||
.em-today-btn {
|
||||
font-weight: normal;
|
||||
padding: 5px 12px;
|
||||
}
|
||||
|
||||
.em-dias-semana, .em-dias-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.em-dias-semana span {
|
||||
font-weight: 600;
|
||||
color: #777;
|
||||
font-size: 0.85em;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.em-dia-celula {
|
||||
position: relative;
|
||||
padding: 4px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
font-size: 0.9em;
|
||||
border-right: 1px solid #eee;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.em-dia-celula.em-other-month {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.em-dia-celula.today span {
|
||||
background-color: #e74c3c;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
width: 28px;
|
||||
line-height: 26px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.em-event-list {
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.em-event {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 12px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
background-color: #3498db;
|
||||
text-decoration: none;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.em-event:hover {
|
||||
background-color: #217dbb;
|
||||
}
|
||||
|
||||
/* --- Estilos para Tela Cheia --- */
|
||||
body.em-fullscreen-active {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.em-calendario-wrapper.em-fullscreen-mode {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 10000;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.em-calendario-wrapper.em-fullscreen-mode .em-dias-grid {
|
||||
flex-grow: 1; /* Faz a grade de dias ocupar o espaço disponível */
|
||||
}
|
||||
|
||||
.em-calendario-wrapper.em-fullscreen-mode .em-dia-celula {
|
||||
height: auto; /* Altura automática para preencher o espaço */
|
||||
}
|
||||
|
||||
.em-close-fullscreen-btn {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 20px;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 36px;
|
||||
line-height: 1;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
z-index: 10001;
|
||||
}
|
||||
|
||||
.em-close-fullscreen-btn:hover {
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
/* Estilos para single-evento.php */
|
||||
.single-evento-container {
|
||||
max-width: 800px; /* Largura máxima para centralizar */
|
||||
margin: 0 auto; /* Centraliza horizontalmente */
|
||||
padding: 30px 20px;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(44, 62, 80, 0.1);
|
||||
}
|
||||
|
||||
.evento-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.evento-header h1 {
|
||||
font-size: 2rem;
|
||||
color: #2c3e50;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.evento-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.evento-info-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.evento-info-list li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.evento-info-list li strong {
|
||||
min-width: 80px; /* Ajuda a alinhar os rótulos */
|
||||
}
|
||||
|
||||
/* Responsividade */
|
||||
@media (max-width: 600px) {
|
||||
.single-evento-container {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.evento-info-list li {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.evento-info-list li strong {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
18
assets/js/admin.js
Normal file
18
assets/js/admin.js
Normal file
@@ -0,0 +1,18 @@
|
||||
jQuery(document).ready(function($) {
|
||||
$('#data_evento').on('change', function() {
|
||||
var date = $(this).val();
|
||||
if (!date) {
|
||||
alert('Por favor, selecione uma data válida.');
|
||||
$(this).focus();
|
||||
}
|
||||
});
|
||||
|
||||
$('#hora_evento').on('change', function() {
|
||||
var time = $(this).val();
|
||||
if (time && !/^\d{2}:\d{2}$/.test(time)) {
|
||||
alert('Por favor, insira uma hora válida no formato HH:MM.');
|
||||
$(this).val('');
|
||||
$(this).focus();
|
||||
}
|
||||
});
|
||||
});
|
150
assets/js/eventos-manager.js
Normal file
150
assets/js/eventos-manager.js
Normal file
@@ -0,0 +1,150 @@
|
||||
jQuery(document).ready(function($) {
|
||||
// Itera sobre cada instância de calendário na página
|
||||
$('.em-calendario-wrapper').each(function() {
|
||||
const calendarWrapper = $(this);
|
||||
let currentDate = new Date();
|
||||
|
||||
const header = calendarWrapper.find('.em-mes-ano');
|
||||
const weekDaysContainer = calendarWrapper.find('.em-dias-semana');
|
||||
const daysGrid = calendarWrapper.find('.em-dias-grid');
|
||||
|
||||
// Nomes para internacionalização
|
||||
const monthNames = ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"];
|
||||
const weekDayNames = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"];
|
||||
|
||||
function renderCalendar() {
|
||||
daysGrid.html(''); // Usar .html('') é um pouco mais rápido que .empty()
|
||||
weekDaysContainer.empty();
|
||||
|
||||
const month = currentDate.getMonth();
|
||||
const year = currentDate.getFullYear();
|
||||
|
||||
// Define o cabeçalho (Mês Ano)
|
||||
header.text(monthNames[month] + ' ' + year);
|
||||
|
||||
// Renderiza os dias da semana
|
||||
weekDayNames.forEach(day => {
|
||||
weekDaysContainer.append(`<span>${day}</span>`);
|
||||
});
|
||||
|
||||
const firstDayOfMonth = new Date(year, month, 1).getDay();
|
||||
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
||||
|
||||
// Preenche os dias vazios no início do mês
|
||||
for (let i = 0; i < firstDayOfMonth; i++) {
|
||||
daysGrid.append('<div class="em-dia-celula em-other-month"></div>');
|
||||
}
|
||||
|
||||
// Renderiza os dias do mês
|
||||
for (let i = 1; i <= daysInMonth; i++) {
|
||||
const dayCell = $(`<div class="em-dia-celula" data-date="${year}-${String(month + 1).padStart(2, '0')}-${String(i).padStart(2, '0')}"><span>${i}</span></div>`);
|
||||
|
||||
// Marca o dia de hoje
|
||||
const today = new Date();
|
||||
if (i === today.getDate() && month === today.getMonth() && year === today.getFullYear()) {
|
||||
dayCell.addClass('today');
|
||||
}
|
||||
daysGrid.append(dayCell);
|
||||
}
|
||||
|
||||
loadEventsForMonth(year, month);
|
||||
}
|
||||
|
||||
function loadEventsForMonth(year, month) {
|
||||
const startDate = `${year}-${String(month + 1).padStart(2, '0')}-01`;
|
||||
const endDate = new Date(year, month + 1, 0).toISOString().split('T')[0];
|
||||
|
||||
$.ajax({
|
||||
url: eventosManager.ajaxurl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'get_eventos_calendario',
|
||||
security: eventosManager.nonce,
|
||||
start: startDate,
|
||||
end: endDate
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
markEventsOnCalendar(response.data);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
console.error('Erro ao carregar eventos.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function markEventsOnCalendar(events) {
|
||||
// 1. Agrupar eventos por data
|
||||
const eventsByDate = events.reduce((acc, event) => {
|
||||
const eventDate = event.start.split('T')[0];
|
||||
if (!acc[eventDate]) {
|
||||
acc[eventDate] = [];
|
||||
}
|
||||
acc[eventDate].push(event);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// 2. Limpar eventos antigos e renderizar os novos
|
||||
daysGrid.find('.em-dia-celula').each(function() {
|
||||
const cell = $(this);
|
||||
const date = cell.data('date');
|
||||
|
||||
// Limpa conteúdo de eventos anteriores
|
||||
cell.find('.em-event-list').remove();
|
||||
cell.removeClass('has-event');
|
||||
|
||||
if (eventsByDate[date]) {
|
||||
cell.addClass('has-event');
|
||||
const eventList = $('<div class="em-event-list"></div>');
|
||||
eventsByDate[date].forEach(event => {
|
||||
const eventEl = $(`<a href="${event.url}" class="em-event"></a>`);
|
||||
eventEl.text(event.title);
|
||||
eventList.append(eventEl);
|
||||
});
|
||||
cell.append(eventList);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Navegação
|
||||
calendarWrapper.find('.em-nav-btn').on('click', function() {
|
||||
const direction = $(this).data('nav');
|
||||
const currentMonth = currentDate.getMonth();
|
||||
|
||||
if (direction === 'prev') {
|
||||
currentDate.setMonth(currentMonth - 1);
|
||||
} else if (direction === 'next') {
|
||||
currentDate.setMonth(currentMonth + 1);
|
||||
} else if (direction === 'today') {
|
||||
currentDate = new Date();
|
||||
}
|
||||
renderCalendar();
|
||||
});
|
||||
|
||||
// Botão de Tela Cheia
|
||||
calendarWrapper.find('.em-view-btn[data-view="fullscreen"]').on('click', function() {
|
||||
calendarWrapper.toggleClass('em-fullscreen-mode');
|
||||
$('body').toggleClass('em-fullscreen-active');
|
||||
|
||||
if (calendarWrapper.hasClass('em-fullscreen-mode')) {
|
||||
// Adiciona botão de fechar
|
||||
calendarWrapper.append('<button class="em-close-fullscreen-btn">×</button>');
|
||||
} else {
|
||||
// Remove botão de fechar
|
||||
calendarWrapper.find('.em-close-fullscreen-btn').remove();
|
||||
}
|
||||
});
|
||||
|
||||
// Fechar tela cheia com o botão 'X' (evento delegado)
|
||||
calendarWrapper.on('click', '.em-close-fullscreen-btn', function() {
|
||||
calendarWrapper.removeClass('em-fullscreen-mode');
|
||||
$('body').removeClass('em-fullscreen-active');
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
// Renderização inicial
|
||||
renderCalendar();
|
||||
});
|
||||
});
|
41
assets/js/eventos-manager.js.txt
Normal file
41
assets/js/eventos-manager.js.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
jQuery(document).ready(function($) {
|
||||
// Inicializa o calendário se o elemento existir
|
||||
if ($('#eventos-calendario').length) {
|
||||
loadEventosCalendario();
|
||||
}
|
||||
|
||||
function loadEventosCalendario() {
|
||||
$.ajax({
|
||||
url: eventosManager.ajaxurl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'get_eventos_calendario',
|
||||
security: eventosManager.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
initFullCalendar(response.data);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initFullCalendar(eventos) {
|
||||
$('#eventos-calendario').fullCalendar({
|
||||
header: {
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: 'month,agendaWeek,agendaDay'
|
||||
},
|
||||
defaultView: 'month',
|
||||
editable: false,
|
||||
events: eventos,
|
||||
eventColor: '#3498db',
|
||||
eventTextColor: '#ffffff',
|
||||
timeFormat: 'H:mm',
|
||||
eventRender: function(event, element) {
|
||||
element.find('.fc-title').prepend('<span class="evento-tipo-badge">' + event.tipo + '</span> ');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
170
eventos-manager.php
Normal file
170
eventos-manager.php
Normal file
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: Gerenciador de Eventos
|
||||
* Description: Plugin para cadastro e exibição de eventos com calendário.
|
||||
* Shortcodes disponíveis: [mostra-calendario], [mostra-prox-eventos limit="5" tipo=""], [eventos-completo], [mostra-calendario-widget]
|
||||
* Version: 1.0
|
||||
* Author: Marco Antonio Vivas
|
||||
* Text Domain: gerenciador-eventos
|
||||
*/
|
||||
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; // Sai se acessado diretamente
|
||||
}
|
||||
|
||||
define('EVENTOS_MANAGER_VERSION', '1.1.0');
|
||||
define('EVENTOS_MANAGER_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||||
define('EVENTOS_MANAGER_PLUGIN_URL', plugin_dir_url(__FILE__));
|
||||
|
||||
// Carrega as classes do plugin
|
||||
require_once EVENTOS_MANAGER_PLUGIN_DIR . 'includes/class-eventos-post-type.php';
|
||||
require_once EVENTOS_MANAGER_PLUGIN_DIR . 'includes/class-eventos-shortcodes.php';
|
||||
require_once EVENTOS_MANAGER_PLUGIN_DIR . 'includes/class-eventos-admin.php';
|
||||
|
||||
class Eventos_Manager {
|
||||
public function __construct() {
|
||||
new Eventos_Post_Type();
|
||||
new Eventos_Shortcodes();
|
||||
if (is_admin()) {
|
||||
new Eventos_Admin();
|
||||
}
|
||||
add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
|
||||
add_action('wp_ajax_get_eventos_calendario', array($this, 'get_eventos_calendario'));
|
||||
add_action('wp_ajax_nopriv_get_eventos_calendario', array($this, 'get_eventos_calendario'));
|
||||
add_filter('single_template', array($this, 'load_single_template'));
|
||||
}
|
||||
|
||||
public function enqueue_scripts() {
|
||||
wp_enqueue_style(
|
||||
'eventos-manager-css',
|
||||
EVENTOS_MANAGER_PLUGIN_URL . 'assets/css/eventos-manager.css',
|
||||
array(),
|
||||
EVENTOS_MANAGER_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script('jquery');
|
||||
|
||||
wp_enqueue_script(
|
||||
'eventos-manager-js',
|
||||
EVENTOS_MANAGER_PLUGIN_URL . 'assets/js/eventos-manager.js',
|
||||
array('jquery'),
|
||||
EVENTOS_MANAGER_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'eventos-manager-js',
|
||||
'eventosManager',
|
||||
array(
|
||||
'ajaxurl' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('eventos_manager_nonce')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function get_eventos_calendario() {
|
||||
check_ajax_referer('eventos_manager_nonce', 'security');
|
||||
|
||||
$start_date = isset($_POST['start']) ? sanitize_text_field($_POST['start']) : date('Y-m-d', strtotime('-1 month'));
|
||||
$end_date = isset($_POST['end']) ? sanitize_text_field($_POST['end']) : date('Y-m-d', strtotime('+1 month'));
|
||||
|
||||
$args = array(
|
||||
'post_type' => 'evento',
|
||||
'posts_per_page' => -1,
|
||||
'meta_key' => 'data_evento',
|
||||
'orderby' => 'meta_value',
|
||||
'order' => 'ASC',
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => 'data_evento',
|
||||
'value' => array($start_date, $end_date),
|
||||
'compare' => 'BETWEEN',
|
||||
'type' => 'DATE'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$eventos = new WP_Query($args);
|
||||
$calendar_events = array();
|
||||
|
||||
if ($eventos->have_posts()) {
|
||||
while ($eventos->have_posts()) {
|
||||
$eventos->the_post();
|
||||
$data_evento = get_post_meta(get_the_ID(), 'data_evento', true);
|
||||
$hora_evento = get_post_meta(get_the_ID(), 'hora_evento', true);
|
||||
$tipos = get_the_terms(get_the_ID(), 'tipo_evento');
|
||||
$tipo = !empty($tipos) && !is_wp_error($tipos) ? $tipos[0]->name : '';
|
||||
|
||||
$calendar_events[] = array(
|
||||
'title' => get_the_title(),
|
||||
'start' => $data_evento . ($hora_evento ? 'T' . $hora_evento : ''),
|
||||
'tipo' => $tipo,
|
||||
'url' => get_permalink()
|
||||
);
|
||||
}
|
||||
wp_reset_postdata();
|
||||
}
|
||||
|
||||
wp_send_json_success($calendar_events);
|
||||
}
|
||||
|
||||
public function load_single_template($template) {
|
||||
global $post;
|
||||
if ($post->post_type === 'evento') {
|
||||
$plugin_template = EVENTOS_MANAGER_PLUGIN_DIR . 'single-evento.php';
|
||||
if (file_exists($plugin_template)) {
|
||||
return $plugin_template;
|
||||
}
|
||||
}
|
||||
return $template;
|
||||
}
|
||||
}
|
||||
|
||||
new Eventos_Manager();
|
||||
|
||||
// Register widget
|
||||
class Eventos_Upcoming_Widget extends WP_Widget {
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'eventos_upcoming_widget',
|
||||
__('Próximos Eventos', 'eventos-manager'),
|
||||
array('description' => __('Mostra os próximos eventos', 'eventos-manager'))
|
||||
);
|
||||
}
|
||||
|
||||
public function widget($args, $instance) {
|
||||
echo $args['before_widget'];
|
||||
if (!empty($instance['title'])) {
|
||||
echo $args['before_title'] . apply_filters('widget_title', $instance['title']) . $args['after_title'];
|
||||
}
|
||||
echo do_shortcode('[mostra-prox-eventos limit="' . esc_attr($instance['limit']) . '"]');
|
||||
echo $args['after_widget'];
|
||||
}
|
||||
|
||||
public function form($instance) {
|
||||
$title = !empty($instance['title']) ? $instance['title'] : '';
|
||||
$limit = !empty($instance['limit']) ? $instance['limit'] : 5;
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Título:'); ?></label>
|
||||
<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>">
|
||||
</p>
|
||||
<p>
|
||||
<label for="<?php echo $this->get_field_id('limit'); ?>"><?php _e('Número de eventos:'); ?></label>
|
||||
<input class="widefat" id="<?php echo $this->get_field_id('limit'); ?>" name="<?php echo $this->get_field_name('limit'); ?>" type="number" value="<?php echo esc_attr($limit); ?>">
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function update($new_instance, $old_instance) {
|
||||
$instance = array();
|
||||
$instance['title'] = (!empty($new_instance['title'])) ? sanitize_text_field($new_instance['title']) : '';
|
||||
$instance['limit'] = (!empty($new_instance['limit'])) ? intval($new_instance['limit']) : 5;
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
|
||||
add_action('widgets_init', function() {
|
||||
register_widget('Eventos_Upcoming_Widget');
|
||||
});
|
116
includes/class-eventos-admin.php
Normal file
116
includes/class-eventos-admin.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
class Eventos_Admin {
|
||||
public function __construct() {
|
||||
add_action('add_meta_boxes', array($this, 'add_meta_boxes'));
|
||||
add_action('save_post', array($this, 'save_evento_meta'));
|
||||
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
|
||||
add_action('admin_menu', array($this, 'add_help_page'));
|
||||
}
|
||||
|
||||
public function add_meta_boxes() {
|
||||
add_meta_box(
|
||||
'evento_metabox',
|
||||
__('Detalhes do Evento', 'eventos-manager'),
|
||||
array($this, 'render_evento_metabox'),
|
||||
'evento',
|
||||
'normal',
|
||||
'high'
|
||||
);
|
||||
}
|
||||
|
||||
public function render_evento_metabox($post) {
|
||||
wp_nonce_field('evento_meta_nonce', 'evento_meta_nonce');
|
||||
|
||||
$data_evento = get_post_meta($post->ID, 'data_evento', true);
|
||||
$hora_evento = get_post_meta($post->ID, 'hora_evento', true);
|
||||
$local_evento = get_post_meta($post->ID, 'local_evento', true);
|
||||
?>
|
||||
<div class="evento-metabox">
|
||||
<div class="meta-field">
|
||||
<label for="data_evento"><?php _e('Data do Evento', 'eventos-manager'); ?></label>
|
||||
<input type="date" id="data_evento" name="data_evento" value="<?php echo esc_attr($data_evento); ?>" required>
|
||||
</div>
|
||||
<div class="meta-field">
|
||||
<label for="hora_evento"><?php _e('Hora do Evento', 'eventos-manager'); ?></label>
|
||||
<input type="time" id="hora_evento" name="hora_evento" value="<?php echo esc_attr($hora_evento); ?>">
|
||||
</div>
|
||||
<div class="meta-field">
|
||||
<label for="local_evento"><?php _e('Local do Evento', 'eventos-manager'); ?></label>
|
||||
<input type="text" id="local_evento" name="local_evento" value="<?php echo esc_attr($local_evento); ?>">
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function save_evento_meta($post_id) {
|
||||
if (!isset($_POST['evento_meta_nonce']) || !wp_verify_nonce($_POST['evento_meta_nonce'], 'evento_meta_nonce')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!current_user_can('edit_post', $post_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($_POST['data_evento']) && preg_match('/^\d{4}-\d{2}-\d{2}$/', $_POST['data_evento'])) {
|
||||
update_post_meta($post_id, 'data_evento', sanitize_text_field($_POST['data_evento']));
|
||||
}
|
||||
|
||||
if (isset($_POST['hora_evento']) && preg_match('/^\d{2}:\d{2}$/', $_POST['hora_evento'])) {
|
||||
update_post_meta($post_id, 'hora_evento', sanitize_text_field($_POST['hora_evento']));
|
||||
}
|
||||
|
||||
if (isset($_POST['local_evento'])) {
|
||||
update_post_meta($post_id, 'local_evento', sanitize_text_field($_POST['local_evento']));
|
||||
}
|
||||
}
|
||||
|
||||
public function enqueue_admin_scripts() {
|
||||
global $post_type;
|
||||
|
||||
if ('evento' == $post_type) {
|
||||
wp_enqueue_style(
|
||||
'eventos-manager-admin-css',
|
||||
EVENTOS_MANAGER_PLUGIN_URL . 'assets/css/admin.css',
|
||||
array(),
|
||||
EVENTOS_MANAGER_VERSION
|
||||
);
|
||||
wp_enqueue_script(
|
||||
'eventos-manager-admin-js',
|
||||
EVENTOS_MANAGER_PLUGIN_URL . 'assets/js/admin.js',
|
||||
array('jquery'),
|
||||
EVENTOS_MANAGER_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function add_help_page() {
|
||||
add_submenu_page(
|
||||
'edit.php?post_type=evento',
|
||||
__('Ajuda - Gerenciador de Eventos', 'eventos-manager'),
|
||||
__('Ajuda', 'eventos-manager'),
|
||||
'manage_options',
|
||||
'eventos-manager-help',
|
||||
array($this, 'render_help_page')
|
||||
);
|
||||
}
|
||||
|
||||
public function render_help_page() {
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php _e('Ajuda - Gerenciador de Eventos', 'eventos-manager'); ?></h1>
|
||||
<p><?php _e('Este plugin permite gerenciar eventos com um calendário e shortcodes.', 'eventos-manager'); ?></p>
|
||||
<h2><?php _e('Shortcodes Disponíveis', 'eventos-manager'); ?></h2>
|
||||
<ul>
|
||||
<li><code>[mostra-calendario]</code>: <?php _e('Exibe o calendário de eventos.', 'eventos-manager'); ?></li>
|
||||
<li><code>[mostra-prox-eventos limit="5" tipo=""]</code>: <?php _e('Exibe uma lista de próximos eventos. Atributos: limit (número de eventos), tipo (slug do tipo de evento).', 'eventos-manager'); ?></li>
|
||||
<li><code>[eventos-completo]</code>: <?php _e('Exibe o calendário e a lista de próximos eventos juntos.', 'eventos-manager'); ?></li>
|
||||
</ul>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
61
includes/class-eventos-post-type.php
Normal file
61
includes/class-eventos-post-type.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
class Eventos_Post_Type {
|
||||
public function __construct() {
|
||||
add_action('init', array($this, 'register_post_type'));
|
||||
add_action('init', array($this, 'register_taxonomy'));
|
||||
}
|
||||
|
||||
public function register_post_type() {
|
||||
$labels = array(
|
||||
'name' => __('Eventos', 'eventos-manager'),
|
||||
'singular_name' => __('Evento', 'eventos-manager'),
|
||||
'menu_name' => __('Eventos', 'eventos-manager'),
|
||||
'add_new' => __('Adicionar Novo', 'eventos-manager'),
|
||||
'add_new_item' => __('Adicionar Novo Evento', 'eventos-manager'),
|
||||
'edit_item' => __('Editar Evento', 'eventos-manager'),
|
||||
'new_item' => __('Novo Evento', 'eventos-manager'),
|
||||
'view_item' => __('Ver Evento', 'eventos-manager'),
|
||||
'search_items' => __('Buscar Eventos', 'eventos-manager'),
|
||||
'not_found' => __('Nenhum evento encontrado', 'eventos-manager'),
|
||||
'not_found_in_trash' => __('Nenhum evento encontrado na lixeira', 'eventos-manager')
|
||||
);
|
||||
|
||||
$args = array(
|
||||
'labels' => $labels,
|
||||
'public' => true,
|
||||
'has_archive' => true,
|
||||
'menu_icon' => 'dashicons-calendar-alt',
|
||||
'supports' => array('title', 'editor', 'thumbnail'),
|
||||
'rewrite' => array('slug' => 'eventos'),
|
||||
'show_in_rest' => true
|
||||
);
|
||||
|
||||
register_post_type('evento', $args);
|
||||
}
|
||||
|
||||
public function register_taxonomy() {
|
||||
$labels = array(
|
||||
'name' => __('Tipos de Evento', 'eventos-manager'),
|
||||
'singular_name' => __('Tipo de Evento', 'eventos-manager'),
|
||||
'search_items' => __('Buscar Tipos', 'eventos-manager'),
|
||||
'all_items' => __('Todos os Tipos', 'eventos-manager'),
|
||||
'edit_item' => __('Editar Tipo', 'eventos-manager'),
|
||||
'update_item' => __('Atualizar Tipo', 'eventos-manager'),
|
||||
'add_new_item' => __('Adicionar Novo Tipo', 'eventos-manager'),
|
||||
'new_item_name' => __('Novo Nome de Tipo', 'eventos-manager'),
|
||||
'menu_name' => __('Tipos de Evento', 'eventos-manager')
|
||||
);
|
||||
|
||||
$args = array(
|
||||
'hierarchical' => true,
|
||||
'labels' => $labels,
|
||||
'show_ui' => true,
|
||||
'show_admin_column' => true,
|
||||
'query_var' => true,
|
||||
'rewrite' => array('slug' => 'tipo-evento'),
|
||||
'show_in_rest' => true
|
||||
);
|
||||
|
||||
register_taxonomy('tipo_evento', 'evento', $args);
|
||||
}
|
||||
}
|
135
includes/class-eventos-shortcodes.php
Normal file
135
includes/class-eventos-shortcodes.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
class Eventos_Shortcodes {
|
||||
public function __construct() {
|
||||
add_shortcode('mostra-calendario', array($this, 'render_calendario'));
|
||||
add_shortcode('mostra-prox-eventos', array($this, 'render_proximos_eventos'));
|
||||
add_shortcode('eventos-completo', array($this, 'render_eventos_completo'));
|
||||
add_shortcode('mostra-calendario-widget', array($this, 'render_calendario_widget')); // NOVO SHORTCODE
|
||||
}
|
||||
|
||||
public function render_calendario($atts) {
|
||||
ob_start();
|
||||
?>
|
||||
<div id="eventos-calendario" class="em-calendario-wrapper" data-view="full">
|
||||
<div class="em-calendario-header em-toolbar">
|
||||
<div class="em-toolbar-section">
|
||||
<button class="em-nav-btn" data-nav="prev"><</button>
|
||||
<button class="em-nav-btn" data-nav="next">></button>
|
||||
<button class="em-nav-btn em-today-btn" data-nav="today">Hoje</button>
|
||||
</div>
|
||||
<div class="em-toolbar-section em-toolbar-center"><h3 class="em-mes-ano"></h3></div>
|
||||
<div class="em-toolbar-section em-toolbar-right">
|
||||
<button class="em-view-btn" data-view="fullscreen" title="Tela Cheia">⛶</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="em-dias-semana"></div>
|
||||
<div class="em-dias-grid"></div>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
public function render_proximos_eventos($atts) {
|
||||
$atts = shortcode_atts(array(
|
||||
'limit' => 5,
|
||||
'tipo' => ''
|
||||
), $atts, 'mostra-prox-eventos');
|
||||
|
||||
$args = array(
|
||||
'post_type' => 'evento',
|
||||
'posts_per_page' => intval($atts['limit']),
|
||||
'meta_key' => 'data_evento',
|
||||
'orderby' => 'meta_value',
|
||||
'order' => 'ASC',
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => 'data_evento',
|
||||
'value' => date('Y-m-d'),
|
||||
'compare' => '>=',
|
||||
'type' => 'DATE'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (!empty($atts['tipo'])) {
|
||||
$args['tax_query'] = array(
|
||||
array(
|
||||
'taxonomy' => 'tipo_evento',
|
||||
'field' => 'slug',
|
||||
'terms' => sanitize_text_field($atts['tipo'])
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$eventos = new WP_Query($args);
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="proximos-eventos-box">
|
||||
<h4>Próximos Eventos</h4>
|
||||
<ul class="proximos-eventos-lista">
|
||||
<?php if ($eventos->have_posts()) : ?>
|
||||
<?php while ($eventos->have_posts()) : $eventos->the_post(); ?>
|
||||
<?php
|
||||
$data_evento = get_post_meta(get_the_ID(), 'data_evento', true);
|
||||
$hora_evento = get_post_meta(get_the_ID(), 'hora_evento', true);
|
||||
$tipos = get_the_terms(get_the_ID(), 'tipo_evento');
|
||||
?>
|
||||
<li>
|
||||
<div class="evento-data">
|
||||
<?php echo date_i18n('d M', strtotime($data_evento)); ?>
|
||||
</div>
|
||||
<div>
|
||||
<div class="evento-titulo"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></div>
|
||||
<?php if ($hora_evento) : ?>
|
||||
<div class="evento-hora"><?php echo $hora_evento; ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php if ($tipos && !is_wp_error($tipos)) : ?>
|
||||
<span class="evento-tipo"><?php echo $tipos[0]->name; ?></span>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endwhile; wp_reset_postdata(); ?>
|
||||
<?php else : ?>
|
||||
<li>Nenhum evento agendado</li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
public function render_eventos_completo($atts) {
|
||||
ob_start();
|
||||
?>
|
||||
<div class="noticias-eventos-wrapper">
|
||||
<div class="noticias-col">
|
||||
<?php echo $this->render_calendario($atts); ?>
|
||||
</div>
|
||||
<div class="eventos-col">
|
||||
<?php echo $this->render_proximos_eventos($atts); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
public function render_calendario_widget($atts) {
|
||||
ob_start();
|
||||
?>
|
||||
<div id="eventos-calendario-widget" class="em-calendario-wrapper" data-view="widget">
|
||||
<div class="em-calendario-header em-toolbar">
|
||||
<div class="em-toolbar-section">
|
||||
<button class="em-nav-btn" data-nav="prev"><</button>
|
||||
<button class="em-nav-btn" data-nav="next">></button>
|
||||
</div>
|
||||
<div class="em-toolbar-section em-toolbar-center"><h3 class="em-mes-ano"></h3></div>
|
||||
<div class="em-toolbar-section"></div>
|
||||
</div>
|
||||
<div class="em-dias-semana"></div>
|
||||
<div class="em-dias-grid"></div>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
31
single-evento.php
Normal file
31
single-evento.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
get_header();
|
||||
if (have_posts()) : while (have_posts()) : the_post();
|
||||
$data_evento = get_post_meta(get_the_ID(), 'data_evento', true);
|
||||
$hora_evento = get_post_meta(get_the_ID(), 'hora_evento', true);
|
||||
$local_evento = get_post_meta(get_the_ID(), 'local_evento', true);
|
||||
$tipos = get_the_terms(get_the_ID(), 'tipo_evento');
|
||||
?>
|
||||
</br><article class="single-evento-container">
|
||||
<header class="evento-header">
|
||||
<h1><?php the_title(); ?></h1>
|
||||
</header>
|
||||
<div class="evento-details">
|
||||
<ul class="evento-info-list">
|
||||
<li><strong>Data:</strong> <?php echo date_i18n('d/m/Y', strtotime($data_evento)); ?></li>
|
||||
<?php if ($hora_evento) : ?>
|
||||
<li><strong>Hora:</strong> <?php echo esc_html($hora_evento); ?></li>
|
||||
<?php endif; ?>
|
||||
<?php if ($local_evento) : ?>
|
||||
<li><strong>Local:</strong> <?php echo esc_html($local_evento); ?></li>
|
||||
<?php endif; ?>
|
||||
<?php if ($tipos && !is_wp_error($tipos)) : ?>
|
||||
<li><strong>Tipo:</strong> <span class="evento-tipo"><?php echo esc_html($tipos[0]->name); ?></span></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
<div class="evento-content"><?php the_content(); ?></div>
|
||||
</div>
|
||||
</article>
|
||||
<?php
|
||||
endwhile; endif;
|
||||
get_footer();
|
Reference in New Issue
Block a user