first commit: gustavoo-portfolio theme + gustavoo-portfolio-core plugin
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
/**
|
||||
* Native WordPress Customizer integration and selective-refresh shortcuts.
|
||||
*
|
||||
* @package Gustavo_Portfolio
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/** Sanitize a URL or an in-page anchor used by theme calls to action. */
|
||||
function gustavoo_portfolio_customize_link( $value ) {
|
||||
$value = trim( sanitize_text_field( (string) $value ) );
|
||||
|
||||
if ( preg_match( '/^#[A-Za-z][A-Za-z0-9_:.-]*$/', $value ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return esc_url_raw( $value, array( 'http', 'https', 'mailto', 'tel' ) );
|
||||
}
|
||||
|
||||
/** Render a template part for a selective-refresh partial. */
|
||||
function gustavoo_portfolio_customize_render_part( $slug ) {
|
||||
ob_start();
|
||||
get_template_part( $slug );
|
||||
return (string) ob_get_clean();
|
||||
}
|
||||
|
||||
/** Register the portfolio content controls in Appearance > Customize. */
|
||||
function gustavoo_portfolio_customize_register( $wp_customize ) {
|
||||
$wp_customize->add_panel(
|
||||
'gustavoo_portfolio_content',
|
||||
array(
|
||||
'title' => __( 'Conteúdo do tema', 'gustavoo-portfolio' ),
|
||||
'description' => __( 'Edite os conteúdos exibidos no site e use os lápis na prévia para ir direto à seção desejada.', 'gustavoo-portfolio' ),
|
||||
'priority' => 30,
|
||||
)
|
||||
);
|
||||
|
||||
$sections = array(
|
||||
'hero' => __( 'Hero', 'gustavoo-portfolio' ),
|
||||
'about' => __( 'Sobre', 'gustavoo-portfolio' ),
|
||||
'services' => __( 'Serviços', 'gustavoo-portfolio' ),
|
||||
'projects' => __( 'Projetos', 'gustavoo-portfolio' ),
|
||||
'blog' => __( 'Blog', 'gustavoo-portfolio' ),
|
||||
'contact' => __( 'Contato', 'gustavoo-portfolio' ),
|
||||
'newsletter' => __( 'Newsletter', 'gustavoo-portfolio' ),
|
||||
'social' => __( 'Redes sociais', 'gustavoo-portfolio' ),
|
||||
);
|
||||
|
||||
foreach ( $sections as $id => $title ) {
|
||||
$wp_customize->add_section(
|
||||
'gustavoo_portfolio_' . $id,
|
||||
array(
|
||||
'title' => $title,
|
||||
'panel' => 'gustavoo_portfolio_content',
|
||||
'priority' => 10,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$fields = array(
|
||||
'hero_eyebrow' => array( 'hero', __( 'Sobretítulo', 'gustavoo-portfolio' ), 'text' ),
|
||||
'hero_title' => array( 'hero', __( 'Título', 'gustavoo-portfolio' ), 'text' ),
|
||||
'hero_description' => array( 'hero', __( 'Descrição', 'gustavoo-portfolio' ), 'textarea' ),
|
||||
'hero_primary_label' => array( 'hero', __( 'Botão principal — texto', 'gustavoo-portfolio' ), 'text' ),
|
||||
'hero_primary_url' => array( 'hero', __( 'Botão principal — link', 'gustavoo-portfolio' ), 'link' ),
|
||||
'hero_secondary_label' => array( 'hero', __( 'Botão secundário — texto', 'gustavoo-portfolio' ), 'text' ),
|
||||
'hero_secondary_url' => array( 'hero', __( 'Botão secundário — link', 'gustavoo-portfolio' ), 'link' ),
|
||||
'about_eyebrow' => array( 'about', __( 'Sobretítulo', 'gustavoo-portfolio' ), 'text' ),
|
||||
'about_title' => array( 'about', __( 'Título', 'gustavoo-portfolio' ), 'text' ),
|
||||
'about_text' => array( 'about', __( 'Texto principal', 'gustavoo-portfolio' ), 'textarea' ),
|
||||
'about_secondary_text' => array( 'about', __( 'Texto complementar', 'gustavoo-portfolio' ), 'textarea' ),
|
||||
'services_eyebrow' => array( 'services', __( 'Sobretítulo', 'gustavoo-portfolio' ), 'text' ),
|
||||
'services_title' => array( 'services', __( 'Título', 'gustavoo-portfolio' ), 'text' ),
|
||||
'services_description' => array( 'services', __( 'Descrição', 'gustavoo-portfolio' ), 'textarea' ),
|
||||
'projects_eyebrow' => array( 'projects', __( 'Sobretítulo', 'gustavoo-portfolio' ), 'text' ),
|
||||
'projects_title' => array( 'projects', __( 'Título', 'gustavoo-portfolio' ), 'text' ),
|
||||
'projects_text' => array( 'projects', __( 'Descrição', 'gustavoo-portfolio' ), 'textarea' ),
|
||||
'projects_limit' => array( 'projects', __( 'Quantidade de projetos', 'gustavoo-portfolio' ), 'number' ),
|
||||
'blog_eyebrow' => array( 'blog', __( 'Sobretítulo', 'gustavoo-portfolio' ), 'text' ),
|
||||
'blog_title' => array( 'blog', __( 'Título', 'gustavoo-portfolio' ), 'text' ),
|
||||
'blog_text' => array( 'blog', __( 'Descrição', 'gustavoo-portfolio' ), 'textarea' ),
|
||||
'blog_limit' => array( 'blog', __( 'Quantidade de posts', 'gustavoo-portfolio' ), 'number' ),
|
||||
'contact_eyebrow' => array( 'contact', __( 'Sobretítulo', 'gustavoo-portfolio' ), 'text' ),
|
||||
'contact_heading' => array( 'contact', __( 'Título', 'gustavoo-portfolio' ), 'text' ),
|
||||
'contact_text' => array( 'contact', __( 'Descrição', 'gustavoo-portfolio' ), 'textarea' ),
|
||||
'email' => array( 'contact', __( 'E-mail', 'gustavoo-portfolio' ), 'email' ),
|
||||
'whatsapp' => array( 'contact', __( 'WhatsApp — número', 'gustavoo-portfolio' ), 'text' ),
|
||||
'whatsapp_message' => array( 'contact', __( 'WhatsApp — mensagem inicial', 'gustavoo-portfolio' ), 'textarea' ),
|
||||
'newsletter_eyebrow' => array( 'newsletter', __( 'Sobretítulo', 'gustavoo-portfolio' ), 'text' ),
|
||||
'newsletter_heading' => array( 'newsletter', __( 'Título', 'gustavoo-portfolio' ), 'text' ),
|
||||
'newsletter_text' => array( 'newsletter', __( 'Descrição', 'gustavoo-portfolio' ), 'textarea' ),
|
||||
'github_url' => array( 'social', __( 'GitHub', 'gustavoo-portfolio' ), 'url' ),
|
||||
'linkedin_url' => array( 'social', __( 'LinkedIn', 'gustavoo-portfolio' ), 'url' ),
|
||||
'instagram_url' => array( 'social', __( 'Instagram', 'gustavoo-portfolio' ), 'url' ),
|
||||
'availability_label' => array( 'social', __( 'Status de disponibilidade', 'gustavoo-portfolio' ), 'text' ),
|
||||
);
|
||||
|
||||
foreach ( $fields as $key => $field ) {
|
||||
$setting_id = 'gustavoo_portfolio_' . $key;
|
||||
$sanitize = 'textarea' === $field[2] ? 'sanitize_textarea_field' : ( 'number' === $field[2] ? 'absint' : ( in_array( $field[2], array( 'url', 'link' ), true ) ? 'gustavoo_portfolio_customize_link' : ( 'email' === $field[2] ? 'sanitize_email' : 'sanitize_text_field' ) ) );
|
||||
|
||||
$wp_customize->add_setting(
|
||||
$setting_id,
|
||||
array(
|
||||
'default' => gustavoo_portfolio_get_setting( $key ),
|
||||
'sanitize_callback' => $sanitize,
|
||||
// Each front-page section is refreshed by its native Customizer
|
||||
// partial, allowing WordPress to expose its own edit shortcut.
|
||||
'transport' => 'postMessage',
|
||||
)
|
||||
);
|
||||
|
||||
$control_args = array(
|
||||
'label' => $field[1],
|
||||
'section' => 'gustavoo_portfolio_' . $field[0],
|
||||
'type' => in_array( $field[2], array( 'textarea', 'number', 'email', 'url' ), true ) ? $field[2] : 'text',
|
||||
);
|
||||
|
||||
if ( 'number' === $field[2] ) {
|
||||
$control_args['input_attrs'] = array( 'min' => 1, 'max' => 24 );
|
||||
}
|
||||
|
||||
$wp_customize->add_control( $setting_id, $control_args );
|
||||
}
|
||||
|
||||
if ( ! isset( $wp_customize->selective_refresh ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$partials = array(
|
||||
'hero' => array( '.hero-stage', 'template-parts/front/hero', array( 'hero_eyebrow', 'hero_title', 'hero_description', 'hero_primary_label', 'hero_primary_url', 'hero_secondary_label', 'hero_secondary_url' ) ),
|
||||
'about' => array( '#sobre', 'template-parts/front/about', array( 'about_eyebrow', 'about_title', 'about_text', 'about_secondary_text' ) ),
|
||||
'services' => array( '#servicos', 'template-parts/front/services', array( 'services_eyebrow', 'services_title', 'services_description' ) ),
|
||||
'projects' => array( '#projetos', 'template-parts/front/projects', array( 'projects_eyebrow', 'projects_title', 'projects_text', 'projects_limit' ) ),
|
||||
'blog' => array( '#blog', 'template-parts/front/posts', array( 'blog_eyebrow', 'blog_title', 'blog_text', 'blog_limit' ) ),
|
||||
'contact' => array( '#contato', 'template-parts/front/contact', array( 'contact_eyebrow', 'contact_heading', 'contact_text', 'email', 'whatsapp', 'whatsapp_message', 'github_url', 'linkedin_url', 'instagram_url' ) ),
|
||||
);
|
||||
|
||||
foreach ( $partials as $id => $partial ) {
|
||||
$settings = array_map(
|
||||
static function ( $key ) {
|
||||
return 'gustavoo_portfolio_' . $key;
|
||||
},
|
||||
$partial[2]
|
||||
);
|
||||
|
||||
/*
|
||||
* A native edit shortcut is tied to the partial's primary setting.
|
||||
* Using the first actual setting as the partial ID makes the pencil
|
||||
* focus a real control instead of an unresolvable synthetic partial.
|
||||
*/
|
||||
$wp_customize->selective_refresh->add_partial(
|
||||
$settings[0],
|
||||
array(
|
||||
'selector' => $partial[0],
|
||||
'settings' => $settings,
|
||||
'primary_setting' => $settings[0],
|
||||
'container_inclusive' => true,
|
||||
'render_callback' => static function () use ( $partial ) {
|
||||
return gustavoo_portfolio_customize_render_part( $partial[1] );
|
||||
},
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$wp_customize->selective_refresh->add_partial(
|
||||
'gustavoo_portfolio_whatsapp_shortcut',
|
||||
array(
|
||||
'selector' => '.whatsapp-float-wrap',
|
||||
'settings' => array( 'gustavoo_portfolio_whatsapp', 'gustavoo_portfolio_whatsapp_message' ),
|
||||
'primary_setting' => 'gustavoo_portfolio_whatsapp',
|
||||
'container_inclusive' => true,
|
||||
'render_callback' => static function () {
|
||||
return gustavoo_portfolio_customize_render_part( 'template-parts/global/whatsapp-float' );
|
||||
},
|
||||
)
|
||||
);
|
||||
}
|
||||
add_action( 'customize_register', 'gustavoo_portfolio_customize_register' );
|
||||
|
||||
/** Register native Customizer shortcuts for the theme's menu locations. */
|
||||
function gustavoo_portfolio_customize_menu_shortcuts( $wp_customize ) {
|
||||
if ( ! isset( $wp_customize->selective_refresh ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$menus = array(
|
||||
'primary' => '#site-navigation',
|
||||
'news_categories' => '.news-category-nav',
|
||||
);
|
||||
|
||||
foreach ( $menus as $location => $selector ) {
|
||||
$setting = 'nav_menu_locations[' . $location . ']';
|
||||
|
||||
if ( ! $wp_customize->get_setting( $setting ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$wp_customize->selective_refresh->add_partial(
|
||||
'gustavoo_portfolio_menu_shortcut_' . $location,
|
||||
array(
|
||||
'selector' => $selector,
|
||||
'settings' => array( $setting ),
|
||||
'primary_setting' => $setting,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
add_action( 'customize_register', 'gustavoo_portfolio_customize_menu_shortcuts', 20 );
|
||||
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
/**
|
||||
* Theme setting defaults and reusable portfolio content.
|
||||
*
|
||||
* @package Gustavo_Portfolio
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return default settings without writing options to the database.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
function gustavoo_portfolio_get_defaults() {
|
||||
$defaults = array(
|
||||
'hero_eyebrow' => __( 'Desenvolvimento de ponta a ponta', 'gustavoo-portfolio' ),
|
||||
'hero_title' => __( 'Transformo ideias em produtos digitais sólidos.', 'gustavoo-portfolio' ),
|
||||
'hero_description' => __( 'Gustavo, Desenvolvedor Full Stack Sênior. Web, mobile, infraestrutura e automação para tirar projetos do papel e fazê-los crescer.', 'gustavoo-portfolio' ),
|
||||
'hero_primary_label' => __( 'Ver projetos', 'gustavoo-portfolio' ),
|
||||
'hero_primary_url' => '#projetos',
|
||||
'hero_secondary_label' => __( 'Fale comigo', 'gustavoo-portfolio' ),
|
||||
'hero_secondary_url' => '#contato',
|
||||
'about_eyebrow' => __( 'Sobre mim', 'gustavoo-portfolio' ),
|
||||
'about_title' => __( 'Responsabilidade técnica do planejamento à produção.', 'gustavoo-portfolio' ),
|
||||
'about_text' => __( 'Mais do que escrever código, meu objetivo é garantir que seu projeto saia do papel e funcione exatamente como planejado. Assumo a responsabilidade técnica de ponta a ponta para que você foque no que importa: crescer.', 'gustavoo-portfolio' ),
|
||||
'about_secondary_text' => __( 'Já participei do desenvolvimento e lançamento de múltiplos projetos digitais, atuando da arquitetura técnica à divulgação e ao crescimento das plataformas. Essa combinação de visão técnica e entendimento de mercado orienta cada entrega.', 'gustavoo-portfolio' ),
|
||||
'services_eyebrow' => __( 'Como posso ajudar', 'gustavoo-portfolio' ),
|
||||
'services_title' => __( 'Soluções completas para produtos digitais.', 'gustavoo-portfolio' ),
|
||||
'services_description' => __( 'Estratégia, código e infraestrutura trabalhando juntos para criar experiências rápidas, seguras e preparadas para evoluir.', 'gustavoo-portfolio' ),
|
||||
'projects_eyebrow' => __( 'Portfólio', 'gustavoo-portfolio' ),
|
||||
'projects_title' => __( 'Projetos selecionados', 'gustavoo-portfolio' ),
|
||||
'projects_text' => __( 'Uma seleção de produtos, experiências e soluções que ajudei a colocar no mundo.', 'gustavoo-portfolio' ),
|
||||
'projects_limit' => 6,
|
||||
'blog_eyebrow' => __( 'Blog', 'gustavoo-portfolio' ),
|
||||
'blog_title' => __( 'Código, produto e bastidores', 'gustavoo-portfolio' ),
|
||||
'blog_text' => __( 'Análises práticas sobre desenvolvimento, infraestrutura, automação e crescimento de produtos digitais.', 'gustavoo-portfolio' ),
|
||||
'blog_limit' => 4,
|
||||
'contact_eyebrow' => __( 'Vamos conversar', 'gustavoo-portfolio' ),
|
||||
'contact_heading' => __( 'Tem um projeto em mente?', 'gustavoo-portfolio' ),
|
||||
'contact_text' => __( 'Conte o que você precisa construir ou melhorar. Responderei com os próximos passos para transformar a ideia em uma entrega concreta.', 'gustavoo-portfolio' ),
|
||||
'contact_form_id' => 0,
|
||||
'newsletter_eyebrow' => __( 'Newsletter', 'gustavoo-portfolio' ),
|
||||
'newsletter_heading' => __( 'Ideias úteis, direto na sua caixa de entrada.', 'gustavoo-portfolio' ),
|
||||
'newsletter_text' => __( 'Receba conteúdos sobre desenvolvimento, produto, infraestrutura e automação. Sem ruído e sem spam.', 'gustavoo-portfolio' ),
|
||||
'newsletter_form_id' => 0,
|
||||
'email' => (string) get_option( 'admin_email', '' ),
|
||||
'whatsapp' => '',
|
||||
'whatsapp_message' => __( 'Olá! Gostaria de falar sobre um projeto.', 'gustavoo-portfolio' ),
|
||||
'github_url' => '',
|
||||
'linkedin_url' => '',
|
||||
'instagram_url' => '',
|
||||
'projects_archive_url' => '',
|
||||
'availability_label' => __( 'Disponível para novos projetos', 'gustavoo-portfolio' ),
|
||||
);
|
||||
|
||||
/**
|
||||
* Filter theme defaults before saved settings are merged.
|
||||
*
|
||||
* @param array<string, mixed> $defaults Default settings.
|
||||
*/
|
||||
return apply_filters( 'gustavoo_portfolio_defaults', $defaults );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the saved theme settings merged with safe local defaults.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
function gustavoo_portfolio_get_settings() {
|
||||
$settings = get_option( 'gustavoo_portfolio_settings', array() );
|
||||
|
||||
if ( ! is_array( $settings ) ) {
|
||||
$settings = array();
|
||||
}
|
||||
|
||||
return wp_parse_args( $settings, gustavoo_portfolio_get_defaults() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one theme setting. A few aliases preserve compatibility with early builds.
|
||||
*
|
||||
* @param string $key Setting key.
|
||||
* @param mixed $fallback Optional fallback.
|
||||
* @return mixed
|
||||
*/
|
||||
function gustavoo_portfolio_get_setting( $key, $fallback = null ) {
|
||||
$customizer_key = 'gustavoo_portfolio_' . $key;
|
||||
$customizer_values = get_theme_mods();
|
||||
|
||||
if ( is_array( $customizer_values ) && array_key_exists( $customizer_key, $customizer_values ) ) {
|
||||
return $customizer_values[ $customizer_key ];
|
||||
}
|
||||
|
||||
$settings = gustavoo_portfolio_get_settings();
|
||||
$aliases = array(
|
||||
'hero_eyebrow' => array( 'hero_kicker' ),
|
||||
'hero_description' => array( 'hero_text' ),
|
||||
'contact_form_id' => array( 'fluent_contact_form_id', 'lead_form_id' ),
|
||||
'newsletter_form_id' => array( 'fluent_newsletter_form_id', 'fluentcrm_form_id' ),
|
||||
'contact_heading' => array( 'contact_title' ),
|
||||
'newsletter_heading' => array( 'newsletter_title' ),
|
||||
);
|
||||
|
||||
if ( array_key_exists( $key, $settings ) && '' !== $settings[ $key ] && null !== $settings[ $key ] ) {
|
||||
return $settings[ $key ];
|
||||
}
|
||||
|
||||
if ( isset( $aliases[ $key ] ) ) {
|
||||
foreach ( $aliases[ $key ] as $alias ) {
|
||||
if ( array_key_exists( $alias, $settings ) && '' !== $settings[ $alias ] && null !== $settings[ $alias ] ) {
|
||||
return $settings[ $alias ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( null !== $fallback ) {
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
$defaults = gustavoo_portfolio_get_defaults();
|
||||
|
||||
return $defaults[ $key ] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the service cards used by the front page.
|
||||
*
|
||||
* @return array<int, array<string, string>>
|
||||
*/
|
||||
function gustavoo_portfolio_get_services() {
|
||||
$services = array(
|
||||
array(
|
||||
'number' => '01',
|
||||
'title' => __( 'Web, Mobile e E-commerce', 'gustavoo-portfolio' ),
|
||||
'description' => __( 'Sites institucionais, aplicações para iOS e Android, softwares desktop, lojas e marketplaces personalizados com foco em experiência e conversão.', 'gustavoo-portfolio' ),
|
||||
),
|
||||
array(
|
||||
'number' => '02',
|
||||
'title' => __( 'WordPress avançado', 'gustavoo-portfolio' ),
|
||||
'description' => __( 'Temas e plugins sob medida, integrações específicas, recuperação de sites e otimização para Core Web Vitals.', 'gustavoo-portfolio' ),
|
||||
),
|
||||
array(
|
||||
'number' => '03',
|
||||
'title' => __( 'Infraestrutura e servidores', 'gustavoo-portfolio' ),
|
||||
'description' => __( 'Ambientes Linux, VPS e servidores dedicados configurados para manter aplicações estáveis, rápidas, seguras e escaláveis.', 'gustavoo-portfolio' ),
|
||||
),
|
||||
array(
|
||||
'number' => '04',
|
||||
'title' => __( 'Automação e integrações', 'gustavoo-portfolio' ),
|
||||
'description' => __( 'APIs e fluxos com n8n para conectar sistemas, organizar dados e eliminar tarefas operacionais repetitivas.', 'gustavoo-portfolio' ),
|
||||
),
|
||||
array(
|
||||
'number' => '05',
|
||||
'title' => __( 'Games e experiências interativas', 'gustavoo-portfolio' ),
|
||||
'description' => __( 'Jogos 2D e 3D para celulares, computadores e navegadores, incluindo ranking, multiplayer e compras dentro do jogo.', 'gustavoo-portfolio' ),
|
||||
),
|
||||
array(
|
||||
'number' => '06',
|
||||
'title' => __( 'Divulgação e crescimento', 'gustavoo-portfolio' ),
|
||||
'description' => __( 'Estratégias de conteúdo, comunidades, parcerias e mídia paga para aproximar produtos digitais do público certo.', 'gustavoo-portfolio' ),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Filter the service cards shown on the front page.
|
||||
*
|
||||
* @param array<int, array<string, string>> $services Service cards.
|
||||
*/
|
||||
return apply_filters( 'gustavoo_portfolio_services', $services );
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* Front-end assets.
|
||||
*
|
||||
* @package Gustavo_Portfolio
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a cache-busting asset version.
|
||||
*
|
||||
* @param string $relative_path Theme-relative path.
|
||||
* @return string
|
||||
*/
|
||||
function gustavoo_portfolio_asset_version( $relative_path ) {
|
||||
$release = '1.1.0';
|
||||
$path = get_theme_file_path( '/' . ltrim( $relative_path, '/' ) );
|
||||
|
||||
if ( file_exists( $path ) ) {
|
||||
return $release . '-' . (string) filemtime( $path );
|
||||
}
|
||||
|
||||
return $release;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue only local, versioned assets.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_enqueue_assets() {
|
||||
wp_enqueue_style(
|
||||
'gustavoo-portfolio-style',
|
||||
get_stylesheet_uri(),
|
||||
array(),
|
||||
gustavoo_portfolio_asset_version( 'style.css' )
|
||||
);
|
||||
|
||||
if ( file_exists( get_theme_file_path( '/assets/css/main.css' ) ) ) {
|
||||
wp_enqueue_style(
|
||||
'gustavoo-portfolio-main',
|
||||
get_theme_file_uri( '/assets/css/main.css' ),
|
||||
array( 'gustavoo-portfolio-style' ),
|
||||
gustavoo_portfolio_asset_version( 'assets/css/main.css' )
|
||||
);
|
||||
}
|
||||
|
||||
if ( file_exists( get_theme_file_path( '/assets/js/navigation.js' ) ) ) {
|
||||
wp_enqueue_script(
|
||||
'gustavoo-portfolio-navigation',
|
||||
get_theme_file_uri( '/assets/js/navigation.js' ),
|
||||
array(),
|
||||
gustavoo_portfolio_asset_version( 'assets/js/navigation.js' ),
|
||||
true
|
||||
);
|
||||
|
||||
wp_script_add_data( 'gustavoo-portfolio-navigation', 'strategy', 'defer' );
|
||||
wp_localize_script(
|
||||
'gustavoo-portfolio-navigation',
|
||||
'gustavooPortfolioNavigation',
|
||||
array(
|
||||
'expand' => __( 'Abrir menu', 'gustavoo-portfolio' ),
|
||||
'collapse' => __( 'Fechar menu', 'gustavoo-portfolio' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
|
||||
wp_enqueue_script( 'comment-reply' );
|
||||
}
|
||||
}
|
||||
add_action( 'wp_enqueue_scripts', 'gustavoo_portfolio_enqueue_assets' );
|
||||
@@ -0,0 +1,247 @@
|
||||
<?php
|
||||
/**
|
||||
* Fluent Forms presentation helpers.
|
||||
*
|
||||
* @package Gustavo_Portfolio
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the configured contact form contains the fields required by the site.
|
||||
*
|
||||
* This is intentionally idempotent so forms created by an older seed version
|
||||
* are repaired even when the seeder state is already marked complete.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_ensure_contact_form_fields() {
|
||||
$form_id = absint( gustavoo_portfolio_get_setting( 'contact_form_id' ) );
|
||||
|
||||
if ( ! $form_id || ! class_exists( '\FluentForm\App\Models\Form' ) || ! function_exists( 'fluentformLoadFile' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$form = \FluentForm\App\Models\Form::find( $form_id );
|
||||
|
||||
if ( ! $form ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$structure = json_decode( (string) $form->form_fields, true );
|
||||
|
||||
if ( ! is_array( $structure ) || ! isset( $structure['fields'] ) || ! is_array( $structure['fields'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$defaults = fluentformLoadFile( 'Services/FormBuilder/DefaultElements.php' );
|
||||
$message = $defaults['general']['textarea'] ?? array();
|
||||
|
||||
if ( ! $message ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields_by_name = array();
|
||||
$changed = false;
|
||||
|
||||
foreach ( $structure['fields'] as $field ) {
|
||||
$name = (string) ( $field['attributes']['name'] ?? '' );
|
||||
|
||||
if ( ! in_array( $name, array( 'email', 'whatsapp', 'message' ), true ) ) {
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isset( $fields_by_name[ $name ] ) ) {
|
||||
$changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
$fields_by_name[ $name ] = $field;
|
||||
}
|
||||
|
||||
if ( isset( $fields_by_name['email'] ) ) {
|
||||
$fields_by_name['email']['settings']['validation_rules']['required']['value'] = true;
|
||||
}
|
||||
|
||||
if ( isset( $fields_by_name['whatsapp'] ) ) {
|
||||
$whatsapp = $fields_by_name['whatsapp'];
|
||||
$whatsapp['settings']['validation_rules']['required']['value'] = true;
|
||||
$whatsapp['settings']['temp_mask'] = 'custom';
|
||||
$whatsapp['settings']['data-mask-reverse'] = 'no';
|
||||
$whatsapp['attributes']['data-mask'] = '(00) 00000-0000';
|
||||
$fields_by_name['whatsapp'] = $whatsapp;
|
||||
}
|
||||
|
||||
if ( ! isset( $fields_by_name['whatsapp'] ) ) {
|
||||
$whatsapp = $defaults['general']['input_mask'] ?? array();
|
||||
if ( $whatsapp ) {
|
||||
$whatsapp['uniqElKey'] = 'el_gso_contact_whatsapp';
|
||||
$whatsapp['attributes']['name'] = 'whatsapp';
|
||||
$whatsapp['attributes']['placeholder'] = '(31) 99999-9999';
|
||||
$whatsapp['attributes']['data-mask'] = '(00) 00000-0000';
|
||||
$whatsapp['settings']['label'] = __( 'WhatsApp', 'gustavoo-portfolio' );
|
||||
$whatsapp['settings']['admin_field_label'] = __( 'WhatsApp', 'gustavoo-portfolio' );
|
||||
$whatsapp['settings']['mobile_keyboard_type'] = 'tel';
|
||||
$whatsapp['settings']['temp_mask'] = 'custom';
|
||||
$whatsapp['settings']['data-mask-reverse'] = 'no';
|
||||
$whatsapp['settings']['validation_rules']['required']['value'] = true;
|
||||
$fields_by_name['whatsapp'] = $whatsapp;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! isset( $fields_by_name['message'] ) || 'textarea' !== (string) ( $fields_by_name['message']['element'] ?? '' ) ) {
|
||||
$message['uniqElKey'] = 'el_gso_contact_message';
|
||||
$message['attributes']['name'] = 'message';
|
||||
$message['attributes']['placeholder'] = __( 'Descreva o motivo do contato e o que você quer construir.', 'gustavoo-portfolio' );
|
||||
$message['attributes']['rows'] = 5;
|
||||
$message['settings']['label'] = __( 'Como posso ajudar?', 'gustavoo-portfolio' );
|
||||
$message['settings']['admin_field_label'] = __( 'Motivo do contato', 'gustavoo-portfolio' );
|
||||
$message['settings']['validation_rules']['required']['value'] = true;
|
||||
$fields_by_name['message'] = $message;
|
||||
}
|
||||
|
||||
$fields = array();
|
||||
foreach ( array( 'email', 'whatsapp', 'message' ) as $required_name ) {
|
||||
if ( isset( $fields_by_name[ $required_name ] ) ) {
|
||||
$fields[] = $fields_by_name[ $required_name ];
|
||||
}
|
||||
}
|
||||
$changed = wp_json_encode( $structure['fields'] ) !== wp_json_encode( $fields );
|
||||
|
||||
if ( ! $changed ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$structure['fields'] = $fields;
|
||||
$form_fields = wp_json_encode( $structure, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
|
||||
|
||||
if ( class_exists( '\FluentForm\App\Services\Form\FormService' ) ) {
|
||||
( new \FluentForm\App\Services\Form\FormService() )->update(
|
||||
array(
|
||||
'form_id' => $form->id,
|
||||
'title' => $form->title,
|
||||
'status' => $form->status,
|
||||
'formFields' => $form_fields,
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$form->form_fields = $form_fields;
|
||||
$form->save();
|
||||
}
|
||||
}
|
||||
add_action( 'init', 'gustavoo_portfolio_ensure_contact_form_fields', 30 );
|
||||
|
||||
/**
|
||||
* Preserve contact fields in submissions while an older form schema is being
|
||||
* migrated. Fluent Forms only whitelists fields known by its saved schema.
|
||||
*
|
||||
* @param array $form_data Sanitized submission data.
|
||||
* @param int $form_id Submitted form ID.
|
||||
* @param array $input_configs Fluent Forms input configuration.
|
||||
* @return array
|
||||
*/
|
||||
function gustavoo_portfolio_preserve_contact_submission_fields( $form_data, $form_id, $input_configs ) {
|
||||
$contact_form_id = absint( gustavoo_portfolio_get_setting( 'contact_form_id' ) );
|
||||
|
||||
if ( ! $contact_form_id || $contact_form_id !== absint( $form_id ) ) {
|
||||
return $form_data;
|
||||
}
|
||||
|
||||
if ( isset( $_POST['whatsapp'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Fluent Forms validates the submission.
|
||||
$form_data['whatsapp'] = sanitize_text_field( wp_unslash( $_POST['whatsapp'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
}
|
||||
|
||||
if ( isset( $_POST['message'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Fluent Forms validates the submission.
|
||||
$form_data['message'] = sanitize_textarea_field( wp_unslash( $_POST['message'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
}
|
||||
|
||||
return $form_data;
|
||||
}
|
||||
add_filter( 'fluentform/insert_response_data', 'gustavoo_portfolio_preserve_contact_submission_fields', 10, 3 );
|
||||
|
||||
/**
|
||||
* Render a Fluent Forms form from a numeric, administrator-controlled ID.
|
||||
*
|
||||
* The theme deliberately never stores or executes arbitrary shortcodes. Contact
|
||||
* synchronization is configured through the FluentCRM feed inside Fluent Forms.
|
||||
*
|
||||
* @param int $form_id Form ID.
|
||||
* @param string $context Form context, used for classes and filters.
|
||||
* @return string
|
||||
*/
|
||||
function gustavoo_portfolio_render_fluent_form( $form_id, $context = 'default' ) {
|
||||
$form_id = absint( $form_id );
|
||||
$context = sanitize_html_class( $context );
|
||||
|
||||
if ( $form_id && shortcode_exists( 'fluentform' ) ) {
|
||||
if ( 'contact' === $context ) {
|
||||
gustavoo_portfolio_ensure_contact_form_fields();
|
||||
}
|
||||
|
||||
$shortcode = sprintf( '[fluentform id="%d"]', $form_id );
|
||||
$markup = do_shortcode( $shortcode );
|
||||
|
||||
// Older persisted forms may still contain only the e-mail field. Keep the
|
||||
// requested fields visible inside the real Fluent Forms <form>.
|
||||
if ( 'contact' === $context && false !== stripos( $markup, '</form>' ) ) {
|
||||
$missing_fields = '';
|
||||
|
||||
if ( false === stripos( $markup, 'name="whatsapp"' ) ) {
|
||||
$missing_fields .= '<div class="ff-el-group"><div class="ff-el-input--label"><label for="gso-contact-whatsapp">' . esc_html__( 'WhatsApp', 'gustavoo-portfolio' ) . '</label></div><div class="ff-el-input--content"><input id="gso-contact-whatsapp" name="whatsapp" class="ff-el-form-control" type="tel" placeholder="(31) 99999-9999" inputmode="tel" data-mask="(00) 00000-0000" data-mask-reverse="false"></div></div>';
|
||||
}
|
||||
|
||||
if ( false === stripos( $markup, 'name="message"' ) ) {
|
||||
$missing_fields .= '<div class="ff-el-group"><div class="ff-el-input--label"><label for="gso-contact-message">' . esc_html__( 'Como posso ajudar?', 'gustavoo-portfolio' ) . '</label></div><div class="ff-el-input--content"><textarea id="gso-contact-message" name="message" class="ff-el-form-control" rows="5" placeholder="' . esc_attr__( 'Descreva o motivo do contato e o que você quer construir.', 'gustavoo-portfolio' ) . '"></textarea></div></div>';
|
||||
}
|
||||
|
||||
if ( $missing_fields ) {
|
||||
$markup = preg_replace( '#<button\b#i', $missing_fields . '<button', $markup, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter trusted Fluent Forms output before it is returned.
|
||||
*
|
||||
* @param string $markup Form markup generated by Fluent Forms.
|
||||
* @param int $form_id Form ID.
|
||||
* @param string $context Presentation context.
|
||||
*/
|
||||
return (string) apply_filters( 'gustavoo_portfolio_fluent_form_markup', $markup, $form_id, $context );
|
||||
}
|
||||
|
||||
$email = sanitize_email( (string) gustavoo_portfolio_get_setting( 'email' ) );
|
||||
$contact_url = gustavoo_portfolio_section_url( 'contato' );
|
||||
$button_url = $email ? 'mailto:' . $email : $contact_url;
|
||||
$button_label = 'newsletter' === $context
|
||||
? __( 'Quero receber novidades', 'gustavoo-portfolio' )
|
||||
: __( 'Enviar uma mensagem', 'gustavoo-portfolio' );
|
||||
|
||||
$fallback = '<div class="form-fallback form-fallback--' . esc_attr( $context ) . '">';
|
||||
$fallback .= '<p>' . esc_html__( 'O formulário está sendo configurado. Você ainda pode entrar em contato diretamente.', 'gustavoo-portfolio' ) . '</p>';
|
||||
$fallback .= '<a class="button button--primary" href="' . esc_url( $button_url ) . '">' . esc_html( $button_label ) . '</a>';
|
||||
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
$fallback .= '<p class="form-fallback__admin"><a href="' . esc_url( admin_url( 'edit.php?post_type=gso_project&page=gso-portfolio-settings' ) ) . '">';
|
||||
$fallback .= esc_html__( 'Configure o ID do Fluent Forms nas opções do portfólio.', 'gustavoo-portfolio' );
|
||||
$fallback .= '</a></p>';
|
||||
}
|
||||
|
||||
$fallback .= '</div>';
|
||||
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a Fluent Forms form. Output originates either from the trusted plugin
|
||||
* shortcode or from fully escaped fallback markup built above.
|
||||
*
|
||||
* @param int $form_id Form ID.
|
||||
* @param string $context Form context.
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_the_fluent_form( $form_id, $context = 'default' ) {
|
||||
echo gustavoo_portfolio_render_fluent_form( $form_id, $context ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Trusted plugin output or escaped local fallback.
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
/**
|
||||
* Theme setup and core WordPress integrations.
|
||||
*
|
||||
* @package Gustavo_Portfolio
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register theme supports, menus, and editor features.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_setup() {
|
||||
load_theme_textdomain( 'gustavoo-portfolio', get_template_directory() . '/languages' );
|
||||
|
||||
add_theme_support( 'automatic-feed-links' );
|
||||
add_theme_support( 'title-tag' );
|
||||
add_theme_support( 'post-thumbnails' );
|
||||
add_theme_support( 'responsive-embeds' );
|
||||
add_theme_support( 'align-wide' );
|
||||
add_theme_support( 'editor-styles' );
|
||||
add_editor_style( 'assets/css/editor.css' );
|
||||
|
||||
add_theme_support(
|
||||
'html5',
|
||||
array(
|
||||
'comment-form',
|
||||
'comment-list',
|
||||
'gallery',
|
||||
'caption',
|
||||
'script',
|
||||
'search-form',
|
||||
'style',
|
||||
)
|
||||
);
|
||||
|
||||
add_theme_support(
|
||||
'custom-logo',
|
||||
array(
|
||||
'height' => 160,
|
||||
'width' => 160,
|
||||
'flex-height' => true,
|
||||
'flex-width' => true,
|
||||
)
|
||||
);
|
||||
|
||||
register_nav_menus(
|
||||
array(
|
||||
'primary' => __( 'Menu principal', 'gustavoo-portfolio' ),
|
||||
'news_categories' => __( 'Menu de categorias de notícias', 'gustavoo-portfolio' ),
|
||||
'footer' => __( 'Menu do rodapé', 'gustavoo-portfolio' ),
|
||||
)
|
||||
);
|
||||
|
||||
add_image_size( 'gustavoo-project-card', 720, 480, true );
|
||||
add_image_size( 'gustavoo-post-card', 720, 460, true );
|
||||
}
|
||||
add_action( 'after_setup_theme', 'gustavoo_portfolio_setup' );
|
||||
|
||||
/**
|
||||
* Define a comfortable content width for embeds and media.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_content_width() {
|
||||
$GLOBALS['content_width'] = apply_filters( 'gustavoo_portfolio_content_width', 780 );
|
||||
}
|
||||
add_action( 'after_setup_theme', 'gustavoo_portfolio_content_width', 0 );
|
||||
|
||||
/**
|
||||
* Add useful state classes without coupling templates to presentation logic.
|
||||
*
|
||||
* @param string[] $classes Existing body classes.
|
||||
* @return string[]
|
||||
*/
|
||||
function gustavoo_portfolio_body_classes( $classes ) {
|
||||
if ( is_front_page() ) {
|
||||
$classes[] = 'has-overlay-header';
|
||||
}
|
||||
|
||||
if ( ! is_front_page() && is_active_sidebar( 'sidebar-blog' ) ) {
|
||||
$classes[] = 'has-blog-sidebar';
|
||||
}
|
||||
|
||||
if ( ! is_singular() ) {
|
||||
$classes[] = 'is-list-view';
|
||||
}
|
||||
|
||||
return array_unique( $classes );
|
||||
}
|
||||
add_filter( 'body_class', 'gustavoo_portfolio_body_classes' );
|
||||
|
||||
/** Set the posts page browser title to the editorial section name. */
|
||||
function gustavoo_portfolio_blog_document_title( $title ) {
|
||||
if ( is_home() ) {
|
||||
$title['title'] = __( 'Últimas notícias', 'gustavoo-portfolio' );
|
||||
}
|
||||
|
||||
return $title;
|
||||
}
|
||||
add_filter( 'document_title_parts', 'gustavoo_portfolio_blog_document_title' );
|
||||
|
||||
/** Keep the editorial top grid focused on the five newest posts. */
|
||||
function gustavoo_portfolio_news_posts_per_page( $query ) {
|
||||
if ( is_admin() || ! $query->is_main_query() || ! ( $query->is_home() || $query->is_category() ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$query->set( 'posts_per_page', 5 );
|
||||
}
|
||||
add_action( 'pre_get_posts', 'gustavoo_portfolio_news_posts_per_page' );
|
||||
|
||||
/** Refresh category rewrite rules once after the editorial templates are installed. */
|
||||
function gustavoo_portfolio_refresh_editorial_rewrites() {
|
||||
$version = '1.0.0';
|
||||
|
||||
if ( $version === get_option( 'gustavoo_portfolio_editorial_rewrite_version' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
flush_rewrite_rules( false );
|
||||
update_option( 'gustavoo_portfolio_editorial_rewrite_version', $version, false );
|
||||
}
|
||||
add_action( 'init', 'gustavoo_portfolio_refresh_editorial_rewrites', 99 );
|
||||
|
||||
/**
|
||||
* Print a fallback icon only when WordPress has no configured Site Icon.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_fallback_site_icon() {
|
||||
if ( has_site_icon() || ! function_exists( 'gustavoo_portfolio_get_image_source' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$icon_url = gustavoo_portfolio_get_image_source( 'logo-mark', 'png' );
|
||||
|
||||
if ( $icon_url ) {
|
||||
printf( '<link rel="icon" href="%s" sizes="192x192">' . "\n", esc_url( $icon_url ) );
|
||||
}
|
||||
}
|
||||
add_action( 'wp_head', 'gustavoo_portfolio_fallback_site_icon', 2 );
|
||||
@@ -0,0 +1,569 @@
|
||||
<?php
|
||||
/**
|
||||
* Reusable template helpers.
|
||||
*
|
||||
* @package Gustavo_Portfolio
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an image source, preferring an existing format and falling back safely.
|
||||
*
|
||||
* @param string $basename File basename without extension.
|
||||
* @param string $preferred Preferred extension.
|
||||
* @return string
|
||||
*/
|
||||
function gustavoo_portfolio_get_image_source( $basename, $preferred = 'webp' ) {
|
||||
$basename = preg_replace( '/[^a-z0-9-]/', '', strtolower( (string) $basename ) );
|
||||
$preferred = in_array( $preferred, array( 'webp', 'png' ), true ) ? $preferred : 'webp';
|
||||
$formats = array_unique( array( $preferred, 'webp', 'png' ) );
|
||||
|
||||
if ( ! $basename ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
foreach ( $formats as $format ) {
|
||||
$relative_path = '/assets/images/' . $basename . '.' . $format;
|
||||
|
||||
if ( file_exists( get_theme_file_path( $relative_path ) ) ) {
|
||||
return get_theme_file_uri( $relative_path );
|
||||
}
|
||||
}
|
||||
|
||||
// Keep a deterministic URI while assets are being provisioned by deployment.
|
||||
return get_theme_file_uri( '/assets/images/' . $basename . '.' . $preferred );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return image URLs for a picture element.
|
||||
*
|
||||
* @param string $basename File basename without extension.
|
||||
* @return array{webp:string,png:string,fallback:string}
|
||||
*/
|
||||
function gustavoo_portfolio_get_picture_sources( $basename ) {
|
||||
$webp_path = '/assets/images/' . $basename . '.webp';
|
||||
$png_path = '/assets/images/' . $basename . '.png';
|
||||
$has_webp = file_exists( get_theme_file_path( $webp_path ) );
|
||||
$has_png = file_exists( get_theme_file_path( $png_path ) );
|
||||
|
||||
return array(
|
||||
'webp' => $has_webp ? get_theme_file_uri( $webp_path ) : '',
|
||||
'png' => $has_png ? get_theme_file_uri( $png_path ) : '',
|
||||
'fallback' => $has_png ? get_theme_file_uri( $png_path ) : get_theme_file_uri( $webp_path ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the configured logo or the bundled brand mark.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_the_brand() {
|
||||
$site_name = get_bloginfo( 'name' );
|
||||
|
||||
if ( has_custom_logo() ) {
|
||||
echo get_custom_logo(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Core-generated custom logo markup.
|
||||
return;
|
||||
}
|
||||
|
||||
$sources = gustavoo_portfolio_get_picture_sources( 'logo-mark' );
|
||||
?>
|
||||
<a class="custom-logo-link" href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home">
|
||||
<picture>
|
||||
<?php if ( $sources['webp'] ) : ?>
|
||||
<source srcset="<?php echo esc_url( $sources['webp'] ); ?>" type="image/webp">
|
||||
<?php endif; ?>
|
||||
<img class="custom-logo" src="<?php echo esc_url( $sources['fallback'] ); ?>" width="64" height="64" alt="<?php echo esc_attr( $site_name ); ?>">
|
||||
</picture>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the appropriate URL for a front-page section.
|
||||
*
|
||||
* @param string $section_id Section anchor without hash.
|
||||
* @return string
|
||||
*/
|
||||
function gustavoo_portfolio_section_url( $section_id ) {
|
||||
$section_id = sanitize_html_class( $section_id );
|
||||
|
||||
if ( is_front_page() ) {
|
||||
return '#' . $section_id;
|
||||
}
|
||||
|
||||
return home_url( '/#' . $section_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured posts-page URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function gustavoo_portfolio_get_blog_url() {
|
||||
$posts_page_id = absint( get_option( 'page_for_posts' ) );
|
||||
|
||||
if ( $posts_page_id ) {
|
||||
$url = get_permalink( $posts_page_id );
|
||||
|
||||
if ( $url ) {
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
|
||||
return home_url( '/blog/' );
|
||||
}
|
||||
|
||||
/** Render the editorial category navigation used on news pages. */
|
||||
function gustavoo_portfolio_news_category_menu() {
|
||||
if ( has_nav_menu( 'news_categories' ) ) {
|
||||
?>
|
||||
<nav class="news-category-nav" aria-label="<?php esc_attr_e( 'Categorias de notícias', 'gustavoo-portfolio' ); ?>">
|
||||
<div class="news-category-nav__inner site-shell">
|
||||
<?php
|
||||
wp_nav_menu(
|
||||
array(
|
||||
'theme_location' => 'news_categories',
|
||||
'menu_class' => 'news-category-nav__list',
|
||||
'container' => false,
|
||||
'fallback_cb' => false,
|
||||
'depth' => 1,
|
||||
)
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
</nav>
|
||||
<?php
|
||||
return;
|
||||
}
|
||||
|
||||
$categories = get_categories(
|
||||
array(
|
||||
'hide_empty' => true,
|
||||
'exclude' => array( absint( get_option( 'default_category' ) ) ),
|
||||
'orderby' => 'name',
|
||||
'order' => 'ASC',
|
||||
)
|
||||
);
|
||||
|
||||
if ( empty( $categories ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$current_category_id = is_category() ? (int) get_queried_object_id() : 0;
|
||||
?>
|
||||
<nav class="news-category-nav" aria-label="<?php esc_attr_e( 'Categorias de notícias', 'gustavoo-portfolio' ); ?>">
|
||||
<div class="news-category-nav__inner site-shell">
|
||||
<a class="news-category-nav__link<?php echo is_home() ? ' is-current' : ''; ?>" href="<?php echo esc_url( gustavoo_portfolio_get_blog_url() ); ?>"><?php esc_html_e( 'Últimas notícias', 'gustavoo-portfolio' ); ?></a>
|
||||
<?php foreach ( $categories as $category ) : ?>
|
||||
<a class="news-category-nav__link<?php echo $current_category_id === (int) $category->term_id ? ' is-current' : ''; ?>" href="<?php echo esc_url( get_category_link( $category ) ); ?>"><?php echo esc_html( $category->name ); ?></a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</nav>
|
||||
<?php
|
||||
}
|
||||
|
||||
/** Print editorial categories inside the mobile hamburger panel. */
|
||||
function gustavoo_portfolio_mobile_news_category_menu() {
|
||||
if ( has_nav_menu( 'news_categories' ) ) {
|
||||
?>
|
||||
<div class="mobile-news-categories">
|
||||
<p class="mobile-news-categories__title"><?php esc_html_e( 'Categorias', 'gustavoo-portfolio' ); ?></p>
|
||||
<?php
|
||||
wp_nav_menu(
|
||||
array(
|
||||
'theme_location' => 'news_categories',
|
||||
'menu_class' => 'mobile-news-categories__list',
|
||||
'container' => false,
|
||||
'fallback_cb' => false,
|
||||
'depth' => 1,
|
||||
)
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
return;
|
||||
}
|
||||
|
||||
$categories = get_categories(
|
||||
array(
|
||||
'hide_empty' => true,
|
||||
'exclude' => array( absint( get_option( 'default_category' ) ) ),
|
||||
'orderby' => 'name',
|
||||
'order' => 'ASC',
|
||||
)
|
||||
);
|
||||
|
||||
if ( empty( $categories ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$current_category_id = is_category() ? (int) get_queried_object_id() : 0;
|
||||
?>
|
||||
<div class="mobile-news-categories">
|
||||
<p class="mobile-news-categories__title"><?php esc_html_e( 'Categorias', 'gustavoo-portfolio' ); ?></p>
|
||||
<ul class="mobile-news-categories__list">
|
||||
<li><a class="<?php echo is_home() ? 'is-current' : ''; ?>" href="<?php echo esc_url( gustavoo_portfolio_get_blog_url() ); ?>"><?php esc_html_e( 'Últimas notícias', 'gustavoo-portfolio' ); ?></a></li>
|
||||
<?php foreach ( $categories as $category ) : ?>
|
||||
<li><a class="<?php echo $current_category_id === (int) $category->term_id ? 'is-current' : ''; ?>" href="<?php echo esc_url( get_category_link( $category ) ); ?>"><?php echo esc_html( $category->name ); ?></a></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the image used by an editorial card.
|
||||
*
|
||||
* Seeded demo posts use a local theme image until an editor assigns a
|
||||
* featured image in WordPress.
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @param string $size Registered image size.
|
||||
* @return string
|
||||
*/
|
||||
function gustavoo_portfolio_get_news_image_url( $post_id = 0, $size = 'gustavoo-post-card' ) {
|
||||
$post_id = $post_id ? absint( $post_id ) : get_the_ID();
|
||||
$image = get_the_post_thumbnail_url( $post_id, $size );
|
||||
|
||||
if ( $image ) {
|
||||
return $image;
|
||||
}
|
||||
|
||||
return (string) get_post_meta( $post_id, '_gso_news_image', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessible fallback menu for a site that has not assigned a WordPress menu yet.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_primary_menu_fallback() {
|
||||
?>
|
||||
<ul id="primary-menu" class="primary-menu">
|
||||
<li class="menu-item"><a href="<?php echo esc_url( gustavoo_portfolio_section_url( 'sobre' ) ); ?>"><?php esc_html_e( 'Sobre', 'gustavoo-portfolio' ); ?></a></li>
|
||||
<li class="menu-item"><a href="<?php echo esc_url( gustavoo_portfolio_section_url( 'servicos' ) ); ?>"><?php esc_html_e( 'Serviços', 'gustavoo-portfolio' ); ?></a></li>
|
||||
<li class="menu-item"><a href="<?php echo esc_url( gustavoo_portfolio_section_url( 'projetos' ) ); ?>"><?php esc_html_e( 'Projetos', 'gustavoo-portfolio' ); ?></a></li>
|
||||
<li class="menu-item"><a href="<?php echo esc_url( gustavoo_portfolio_get_blog_url() ); ?>"><?php esc_html_e( 'Blog', 'gustavoo-portfolio' ); ?></a></li>
|
||||
<li class="menu-item"><a href="<?php echo esc_url( gustavoo_portfolio_section_url( 'contato' ) ); ?>"><?php esc_html_e( 'Contato', 'gustavoo-portfolio' ); ?></a></li>
|
||||
</ul>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Return supported social profile links.
|
||||
*
|
||||
* @return array<string, array{label:string,url:string}>
|
||||
*/
|
||||
function gustavoo_portfolio_get_social_links() {
|
||||
$profiles = array(
|
||||
'linkedin' => array(
|
||||
'label' => __( 'LinkedIn', 'gustavoo-portfolio' ),
|
||||
'url' => (string) gustavoo_portfolio_get_setting( 'linkedin_url' ),
|
||||
),
|
||||
'instagram' => array(
|
||||
'label' => __( 'Instagram', 'gustavoo-portfolio' ),
|
||||
'url' => (string) gustavoo_portfolio_get_setting( 'instagram_url' ),
|
||||
),
|
||||
);
|
||||
|
||||
return array_filter(
|
||||
$profiles,
|
||||
static function ( $profile ) {
|
||||
return ! empty( $profile['url'] );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print social links with explicit new-window text for assistive technology.
|
||||
*
|
||||
* @param string $class Optional list class.
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_social_links( $class = 'social-links' ) {
|
||||
$profiles = gustavoo_portfolio_get_social_links();
|
||||
|
||||
if ( empty( $profiles ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf( '<ul class="%s">', esc_attr( $class ) );
|
||||
|
||||
foreach ( $profiles as $slug => $profile ) {
|
||||
printf(
|
||||
'<li class="social-links__item social-links__item--%1$s"><a href="%2$s" target="_blank" rel="noopener noreferrer external"><span>%3$s</span><span class="screen-reader-text"> %4$s</span></a></li>',
|
||||
esc_attr( $slug ),
|
||||
esc_url( $profile['url'] ),
|
||||
esc_html( $profile['label'] ),
|
||||
esc_html__( '(abre em uma nova guia)', 'gustavoo-portfolio' )
|
||||
);
|
||||
}
|
||||
|
||||
echo '</ul>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Print share links for a blog post using inline brand SVG icons.
|
||||
*
|
||||
* @param string $class Optional wrapper class.
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_post_share_links( $class = 'entry-share' ) {
|
||||
$share_url = rawurlencode( get_permalink() );
|
||||
$share_title = rawurlencode( get_the_title() );
|
||||
$links = array(
|
||||
'WhatsApp' => array(
|
||||
'url' => 'https://api.whatsapp.com/send?text=' . $share_title . '%20' . $share_url,
|
||||
'icon' => '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M20.5 3.5A11.8 11.8 0 0 0 12.1 0C5.6 0 .3 5.3.3 11.8c0 2.1.5 4.1 1.6 5.9L.2 23.8l6.3-1.7a11.8 11.8 0 0 0 5.6 1.4h.1c6.5 0 11.8-5.3 11.8-11.8 0-3.1-1.2-6-3.5-8.2Zm-8.4 17.9c-1.8 0-3.5-.5-5-1.4l-.4-.2-3.7 1 1-3.6-.2-.4a9.6 9.6 0 1 1 8.3 4.6Zm5.3-7.2c-.3-.2-1.7-.8-2-.9-.3-.1-.5-.2-.7.2-.2.3-.8.9-.9 1.1-.2.2-.3.2-.6.1-1.7-.8-2.8-1.5-3.9-3.4-.3-.5.3-.5.8-1.7.1-.2 0-.4 0-.6l-.9-2.1c-.2-.6-.5-.5-.7-.5h-.6c-.2 0-.5.1-.8.4-.3.3-1 1-1 2.4s1 2.8 1.1 3c.1.2 2 3.1 4.9 4.4 1.8.8 2.5.9 3.4.8.5-.1 1.7-.7 1.9-1.4.2-.7.2-1.3.1-1.4-.1-.2-.3-.3-.6-.4Z" fill="currentColor"/></svg>',
|
||||
),
|
||||
'Facebook' => array(
|
||||
'url' => 'https://www.facebook.com/sharer/sharer.php?u=' . $share_url,
|
||||
'icon' => '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M13.5 22v-8h2.7l.4-3h-3.1V9.1c0-.9.3-1.5 1.6-1.5h1.7V4.9c-.3 0-1.3-.1-2.5-.1-2.5 0-4.2 1.5-4.2 4.3V11H7.3v3h2.8v8h3.4Z" fill="currentColor"/></svg>',
|
||||
),
|
||||
'X' => array(
|
||||
'url' => 'https://twitter.com/intent/tweet?text=' . $share_title . '&url=' . $share_url,
|
||||
'icon' => '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M18.9 2h3.7l-8.1 9.3L24 22h-7.4l-5.8-7.6L4.2 22H.5l8.4-9.7L0 2h7.6l5.2 6.9L18.9 2Zm-1.3 17.9h2.1L6.4 4H4.1l13.5 15.9Z" fill="currentColor"/></svg>',
|
||||
),
|
||||
'LinkedIn' => array(
|
||||
'url' => 'https://www.linkedin.com/sharing/share-offsite/?url=' . $share_url,
|
||||
'icon' => '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M5.2 3.4A2.2 2.2 0 1 1 .8 3.4a2.2 2.2 0 0 1 4.4 0ZM1.1 7h4.2v13.5H1.1V7Zm6.8 0h4v1.8h.1c.6-1.1 2-2.2 4.1-2.2 4.4 0 5.2 2.9 5.2 6.7v7.2h-4.2v-6.4c0-1.5 0-3.5-2.1-3.5s-2.4 1.7-2.4 3.4v6.5H7.9V7Z" fill="currentColor"/></svg>',
|
||||
),
|
||||
);
|
||||
|
||||
printf( '<div class="%1$s" aria-label="%2$s">', esc_attr( $class ), esc_attr__( 'Compartilhar postagem', 'gustavoo-portfolio' ) );
|
||||
|
||||
foreach ( $links as $label => $link ) {
|
||||
printf(
|
||||
'<a href="%1$s" target="_blank" rel="noopener noreferrer" aria-label="%2$s">%3$s<span class="screen-reader-text">%2$s</span></a>',
|
||||
esc_url( $link['url'] ),
|
||||
esc_attr( $label ),
|
||||
$link['icon'] // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Static SVG markup.
|
||||
);
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a configured phone number to a WhatsApp URL.
|
||||
*
|
||||
* @param string $phone Raw phone number.
|
||||
* @param string $message Optional pre-filled conversation message.
|
||||
* @return string
|
||||
*/
|
||||
function gustavoo_portfolio_get_whatsapp_url( $phone, $message = '' ) {
|
||||
$digits = preg_replace( '/\D+/', '', (string) $phone );
|
||||
|
||||
if ( ! $digits ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( in_array( strlen( $digits ), array( 10, 11 ), true ) ) {
|
||||
$digits = '55' . $digits;
|
||||
}
|
||||
|
||||
$url = 'https://wa.me/' . $digits;
|
||||
$message = trim( wp_strip_all_tags( (string) $message ) );
|
||||
|
||||
if ( '' !== $message ) {
|
||||
$url .= '?text=' . rawurlencode( $message );
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/** Print the configured floating WhatsApp contact link. */
|
||||
function gustavoo_portfolio_the_whatsapp_float() {
|
||||
$phone = (string) gustavoo_portfolio_get_setting( 'whatsapp' );
|
||||
$message = (string) gustavoo_portfolio_get_setting( 'whatsapp_message' );
|
||||
$url = gustavoo_portfolio_get_whatsapp_url( $phone, $message );
|
||||
|
||||
if ( ! $url ) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<div class="whatsapp-float-wrap"<?php echo is_customize_preview() ? ' data-customize-partial-id="gustavoo_portfolio_whatsapp_shortcut"' : ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Static attribute. ?>>
|
||||
<a class="whatsapp-float" href="<?php echo esc_url( $url ); ?>" target="_blank" rel="noopener noreferrer external" aria-label="<?php esc_attr_e( 'Conversar pelo WhatsApp (abre em uma nova guia)', 'gustavoo-portfolio' ); ?>">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path fill="currentColor" d="M20.5 3.5A11.8 11.8 0 0 0 12.1 0C5.6 0 .3 5.3.3 11.8c0 2.1.5 4.1 1.6 5.9L.2 23.8l6.3-1.7a11.8 11.8 0 0 0 5.6 1.4h.1c6.5 0 11.8-5.3 11.8-11.8 0-3.1-1.2-6-3.5-8.2ZM12.1 21.4c-1.8 0-3.5-.5-5-1.4l-.4-.2-3.7 1 1-3.6-.2-.4a9.6 9.6 0 1 1 8.3 4.6Zm5.3-7.2c-.3-.2-1.7-.8-2-.9-.3-.1-.5-.2-.7.2-.2.3-.8.9-.9 1.1-.2.2-.3.2-.6.1-1.7-.8-2.8-1.5-3.9-3.4-.3-.5.3-.5.8-1.7.1-.2 0-.4 0-.6l-.9-2.1c-.2-.6-.5-.5-.7-.5h-.6c-.2 0-.5.1-.8.4-.3.3-1 1-1 2.4s1 2.8 1.1 3c.1.2 2 3.1 4.9 4.4 1.8.8 2.5.9 3.4.8.5-.1 1.7-.7 1.9-1.4.2-.7.2-1.3.1-1.4-.1-.2-.3-.3-.6-.4Z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an external project URL without making a network request.
|
||||
*
|
||||
* @param string $url Candidate URL.
|
||||
* @return string Empty string when invalid.
|
||||
*/
|
||||
function gustavoo_portfolio_validate_project_url( $url ) {
|
||||
$url = esc_url_raw( (string) $url, array( 'http', 'https' ) );
|
||||
$scheme = wp_parse_url( $url, PHP_URL_SCHEME );
|
||||
$host = wp_parse_url( $url, PHP_URL_HOST );
|
||||
|
||||
if ( ! $url || ! $host || ! in_array( strtolower( (string) $scheme ), array( 'http', 'https' ), true ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return featured projects first, then fill the requested limit by menu order.
|
||||
*
|
||||
* @param int $limit Maximum number of projects.
|
||||
* @return WP_Post[]
|
||||
*/
|
||||
function gustavoo_portfolio_get_home_projects( $limit = 6 ) {
|
||||
$limit = max( 1, min( 24, absint( $limit ) ) );
|
||||
|
||||
if ( ! post_type_exists( 'gso_project' ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$base_args = array(
|
||||
'post_type' => 'gso_project',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => $limit * 3,
|
||||
'orderby' => array(
|
||||
'menu_order' => 'ASC',
|
||||
'date' => 'DESC',
|
||||
),
|
||||
'ignore_sticky_posts' => true,
|
||||
'no_found_rows' => true,
|
||||
'update_post_term_cache' => true,
|
||||
);
|
||||
|
||||
$featured_args = $base_args;
|
||||
$featured_args['meta_query'] = array(
|
||||
array(
|
||||
'key' => '_gso_project_featured',
|
||||
'value' => array( '1', 'yes', 'on', 'true' ),
|
||||
'compare' => 'IN',
|
||||
),
|
||||
);
|
||||
|
||||
$featured = get_posts( $featured_args );
|
||||
$others = get_posts( $base_args );
|
||||
$projects = array();
|
||||
$seen = array();
|
||||
|
||||
foreach ( array_merge( $featured, $others ) as $project ) {
|
||||
if ( isset( $seen[ $project->ID ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$url = gustavoo_portfolio_validate_project_url( get_post_meta( $project->ID, '_gso_project_url', true ) );
|
||||
|
||||
if ( ! $url ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$seen[ $project->ID ] = true;
|
||||
$projects[] = $project;
|
||||
|
||||
if ( count( $projects ) >= $limit ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $projects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print post publication metadata.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_posted_on() {
|
||||
$published = sprintf(
|
||||
'<time class="entry-date published" datetime="%1$s">%2$s</time>',
|
||||
esc_attr( get_the_date( DATE_W3C ) ),
|
||||
esc_html( get_the_date() )
|
||||
);
|
||||
|
||||
printf(
|
||||
'<span class="posted-on"><span class="screen-reader-text">%1$s </span>%2$s</span>',
|
||||
esc_html__( 'Publicado em', 'gustavoo-portfolio' ),
|
||||
$published // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaped immediately above.
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the post author link.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_posted_by() {
|
||||
printf(
|
||||
'<span class="byline"><span class="screen-reader-text">%1$s </span><a class="url fn n" href="%2$s">%3$s</a></span>',
|
||||
esc_html__( 'Por', 'gustavoo-portfolio' ),
|
||||
esc_url( get_author_posts_url( (int) get_the_author_meta( 'ID' ) ) ),
|
||||
esc_html( get_the_author() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate reading time from the current post content.
|
||||
*
|
||||
* @param int|null $post_id Optional post ID.
|
||||
* @return string
|
||||
*/
|
||||
function gustavoo_portfolio_reading_time( $post_id = null ) {
|
||||
$post_id = $post_id ? absint( $post_id ) : get_the_ID();
|
||||
$content = wp_strip_all_tags( strip_shortcodes( (string) get_post_field( 'post_content', $post_id ) ) );
|
||||
$words = preg_split( '/\s+/u', trim( $content ), -1, PREG_SPLIT_NO_EMPTY );
|
||||
$minutes = max( 1, (int) ceil( count( is_array( $words ) ? $words : array() ) / 220 ) );
|
||||
|
||||
return sprintf(
|
||||
/* translators: %d: reading time in minutes. */
|
||||
_n( '%d min de leitura', '%d min de leitura', $minutes, 'gustavoo-portfolio' ),
|
||||
$minutes
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print category and tag links for a post.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_entry_terms() {
|
||||
$categories = get_the_category_list( esc_html_x( ', ', 'category list separator', 'gustavoo-portfolio' ) );
|
||||
$tags = get_the_tag_list( '', esc_html_x( ', ', 'tag list separator', 'gustavoo-portfolio' ) );
|
||||
|
||||
if ( $categories ) {
|
||||
printf(
|
||||
'<div class="entry-terms entry-terms--categories"><span>%1$s</span> %2$s</div>',
|
||||
esc_html__( 'Categorias:', 'gustavoo-portfolio' ),
|
||||
wp_kses_post( $categories )
|
||||
);
|
||||
}
|
||||
|
||||
if ( $tags ) {
|
||||
printf(
|
||||
'<div class="entry-terms entry-terms--tags"><span>%1$s</span> %2$s</div>',
|
||||
esc_html__( 'Tags:', 'gustavoo-portfolio' ),
|
||||
wp_kses_post( $tags )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print main-query pagination with accessible labels.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_pagination() {
|
||||
the_posts_pagination(
|
||||
array(
|
||||
'mid_size' => 1,
|
||||
'prev_text' => sprintf( '<span aria-hidden="true">←</span> %s', esc_html__( 'Anteriores', 'gustavoo-portfolio' ) ),
|
||||
'next_text' => sprintf( '%s <span aria-hidden="true">→</span>', esc_html__( 'Próximos', 'gustavoo-portfolio' ) ),
|
||||
'screen_reader_text' => __( 'Navegação entre páginas', 'gustavoo-portfolio' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
/**
|
||||
* Widget areas and the reusable newsletter widget.
|
||||
*
|
||||
* @package Gustavo_Portfolio
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register sidebars used by blog and footer templates.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_register_sidebars() {
|
||||
$shared = array(
|
||||
'before_widget' => '<section id="%1$s" class="widget %2$s">',
|
||||
'after_widget' => '</section>',
|
||||
'before_title' => '<h2 class="widget__title">',
|
||||
'after_title' => '</h2>',
|
||||
);
|
||||
|
||||
register_sidebar(
|
||||
array_merge(
|
||||
$shared,
|
||||
array(
|
||||
'name' => __( 'Barra lateral do blog', 'gustavoo-portfolio' ),
|
||||
'id' => 'sidebar-blog',
|
||||
'description' => __( 'Widgets exibidos ao lado de posts, arquivos e buscas.', 'gustavoo-portfolio' ),
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
register_sidebar(
|
||||
array_merge(
|
||||
$shared,
|
||||
array(
|
||||
'name' => __( 'Rodapé — coluna 1', 'gustavoo-portfolio' ),
|
||||
'id' => 'footer-1',
|
||||
'description' => __( 'Primeira coluna de widgets do rodapé.', 'gustavoo-portfolio' ),
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
register_sidebar(
|
||||
array_merge(
|
||||
$shared,
|
||||
array(
|
||||
'name' => __( 'Rodapé — coluna 2', 'gustavoo-portfolio' ),
|
||||
'id' => 'footer-2',
|
||||
'description' => __( 'Segunda coluna de widgets do rodapé.', 'gustavoo-portfolio' ),
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
add_action( 'widgets_init', 'gustavoo_portfolio_register_sidebars' );
|
||||
|
||||
/**
|
||||
* Newsletter widget backed by the same safe Fluent Forms renderer as the CTA.
|
||||
*/
|
||||
class Gustavao_Portfolio_Newsletter_Widget extends WP_Widget {
|
||||
/**
|
||||
* Register the widget with WordPress.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'gustavoo_portfolio_newsletter',
|
||||
__( 'Gustavo — Newsletter', 'gustavoo-portfolio' ),
|
||||
array(
|
||||
'classname' => 'widget_newsletter',
|
||||
'description' => __( 'Exibe a chamada de newsletter integrada ao Fluent Forms e FluentCRM.', 'gustavoo-portfolio' ),
|
||||
'customize_selective_refresh' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render widget front end.
|
||||
*
|
||||
* @param array<string, string> $args Sidebar wrappers.
|
||||
* @param array<string, mixed> $instance Saved instance.
|
||||
* @return void
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
$title = ! empty( $instance['title'] ) ? $instance['title'] : gustavoo_portfolio_get_setting( 'newsletter_heading' );
|
||||
$text = ! empty( $instance['text'] ) ? $instance['text'] : gustavoo_portfolio_get_setting( 'newsletter_text' );
|
||||
$form_id = ! empty( $instance['form_id'] ) ? absint( $instance['form_id'] ) : absint( gustavoo_portfolio_get_setting( 'newsletter_form_id' ) );
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Registered sidebar wrapper.
|
||||
|
||||
if ( $title ) {
|
||||
echo $args['before_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Registered sidebar wrapper.
|
||||
echo esc_html( $title );
|
||||
echo $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Registered sidebar wrapper.
|
||||
}
|
||||
|
||||
if ( $text ) {
|
||||
printf( '<p class="widget_newsletter__text">%s</p>', esc_html( $text ) );
|
||||
}
|
||||
|
||||
gustavoo_portfolio_the_fluent_form( $form_id, 'newsletter' );
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Registered sidebar wrapper.
|
||||
}
|
||||
|
||||
/**
|
||||
* Render widget controls.
|
||||
*
|
||||
* @param array<string, mixed> $instance Saved instance.
|
||||
* @return void
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$title = isset( $instance['title'] ) ? (string) $instance['title'] : '';
|
||||
$text = isset( $instance['text'] ) ? (string) $instance['text'] : '';
|
||||
$form_id = isset( $instance['form_id'] ) ? absint( $instance['form_id'] ) : 0;
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Título:', 'gustavoo-portfolio' ); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>">
|
||||
</p>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'text' ) ); ?>"><?php esc_html_e( 'Texto:', 'gustavoo-portfolio' ); ?></label>
|
||||
<textarea class="widefat" rows="4" id="<?php echo esc_attr( $this->get_field_id( 'text' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'text' ) ); ?>"><?php echo esc_textarea( $text ); ?></textarea>
|
||||
</p>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( $this->get_field_id( 'form_id' ) ); ?>"><?php esc_html_e( 'ID do Fluent Forms (vazio usa o global):', 'gustavoo-portfolio' ); ?></label>
|
||||
<input class="tiny-text" id="<?php echo esc_attr( $this->get_field_id( 'form_id' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'form_id' ) ); ?>" type="number" min="0" step="1" value="<?php echo esc_attr( $form_id ); ?>">
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize widget settings.
|
||||
*
|
||||
* @param array<string, mixed> $new_instance New values.
|
||||
* @param array<string, mixed> $old_instance Previous values.
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) {
|
||||
unset( $old_instance );
|
||||
|
||||
return array(
|
||||
'title' => isset( $new_instance['title'] ) ? sanitize_text_field( $new_instance['title'] ) : '',
|
||||
'text' => isset( $new_instance['text'] ) ? sanitize_textarea_field( $new_instance['text'] ) : '',
|
||||
'form_id' => isset( $new_instance['form_id'] ) ? absint( $new_instance['form_id'] ) : 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the newsletter widget after core has loaded WP_Widget.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_register_widgets() {
|
||||
register_widget( 'Gustavao_Portfolio_Newsletter_Widget' );
|
||||
}
|
||||
add_action( 'widgets_init', 'gustavoo_portfolio_register_widgets' );
|
||||
|
||||
/**
|
||||
* Add one newsletter widget to the blog sidebar on first theme activation.
|
||||
*
|
||||
* Existing widget arrangements are preserved and the operation is idempotent.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function gustavoo_portfolio_seed_newsletter_widget() {
|
||||
$widget_id_base = 'gustavoo_portfolio_newsletter';
|
||||
$instances = get_option( 'widget_' . $widget_id_base, array() );
|
||||
$instances = is_array( $instances ) ? $instances : array();
|
||||
$sidebars = wp_get_sidebars_widgets();
|
||||
$sidebars = is_array( $sidebars ) ? $sidebars : array();
|
||||
|
||||
foreach ( $sidebars as $widgets ) {
|
||||
foreach ( (array) $widgets as $widget_id ) {
|
||||
if ( str_starts_with( (string) $widget_id, $widget_id_base . '-' ) ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$indexes = array_filter( array_keys( $instances ), 'is_int' );
|
||||
$index = $indexes ? max( $indexes ) + 1 : 1;
|
||||
|
||||
$instances[ $index ] = array(
|
||||
'title' => __( 'Newsletter', 'gustavoo-portfolio' ),
|
||||
'text' => __( 'Receba novos artigos sobre desenvolvimento, infraestrutura e automação.', 'gustavoo-portfolio' ),
|
||||
'form_id' => 0,
|
||||
);
|
||||
$instances['_multiwidget'] = 1;
|
||||
|
||||
$sidebars['sidebar-blog'] = array_values(
|
||||
array_merge(
|
||||
array( $widget_id_base . '-' . $index ),
|
||||
isset( $sidebars['sidebar-blog'] ) ? (array) $sidebars['sidebar-blog'] : array()
|
||||
)
|
||||
);
|
||||
|
||||
update_option( 'widget_' . $widget_id_base, $instances, false );
|
||||
update_option( 'sidebars_widgets', $sidebars, false );
|
||||
}
|
||||
add_action( 'after_switch_theme', 'gustavoo_portfolio_seed_newsletter_widget' );
|
||||
Reference in New Issue
Block a user