Files
gustavoo-me/wp-content/plugins/gustavoo-portfolio-core/includes/class-gso-seeder.php
T

620 lines
21 KiB
PHP

<?php
/**
* Idempotent starter content and Fluent Forms integration.
*
* @package GustavoPortfolioCore
*/
defined( 'ABSPATH' ) || exit;
/**
* Creates useful starter content once, without overwriting later admin edits.
*/
final class GSO_Seeder {
const VERSION = '1.6.0';
const STATE_OPTION = 'gso_portfolio_seed_state';
const LOCK_OPTION = 'gso_portfolio_seed_lock';
const SITE_OPTION = 'gso_portfolio_site_state';
const PROJECT_META = '_gso_seed_key';
const FORM_META = '_gustavoo_seed_key';
const FEED_META = '_gustavoo_seed_fluentcrm_feed_id';
/**
* Register deferred integration setup.
*
* @return void
*/
public static function init() {
add_action( 'init', array( __CLASS__, 'maybe_seed' ), 20 );
add_action( 'after_switch_theme', array( __CLASS__, 'maybe_seed' ), 20 );
}
/**
* Seed content available during activation. Fluent integrations run on init.
*
* @return void
*/
public static function activate() {
self::seed_site_configuration();
self::seed_projects();
$state = self::get_state();
$state['projects_seeded'] = true;
$state['status'] = 'pending';
update_option( self::STATE_OPTION, $state, false );
}
/**
* Return a stable status contract for the admin screen.
*
* @return array<string, mixed>
*/
public static function get_state() {
$state = get_option( self::STATE_OPTION, array() );
return wp_parse_args(
is_array( $state ) ? $state : array(),
array(
'version' => '',
'status' => 'pending',
'projects_seeded' => false,
'contact_form_id' => 0,
'newsletter_form_id' => 0,
'crm_feeds' => false,
'message' => '',
)
);
}
/**
* Complete integration setup when Fluent Forms and FluentCRM are ready.
*
* @return void
*/
public static function maybe_seed() {
self::seed_site_configuration();
$state = self::get_state();
if ( self::VERSION === $state['version'] && 'complete' === $state['status'] ) {
self::maybe_migrate_contact_form();
return;
}
if ( ! post_type_exists( GSO_Projects::POST_TYPE ) ) {
GSO_Projects::register_content_types();
}
self::seed_projects();
if ( ! self::fluent_is_ready() ) {
$state['projects_seeded'] = true;
$state['status'] = 'waiting';
$state['message'] = __( 'Aguardando Fluent Forms e FluentCRM ativos.', 'gustavoo-portfolio-core' );
update_option( self::STATE_OPTION, $state, false );
return;
}
if ( ! add_option( self::LOCK_OPTION, time(), '', false ) ) {
return;
}
try {
$contact_id = self::upsert_form( 'contact', __( 'Site — Contato e orçamento', 'gustavoo-portfolio-core' ), true );
$newsletter_id = self::upsert_form( 'newsletter', __( 'Site — Newsletter', 'gustavoo-portfolio-core' ), false );
self::upsert_crm_feed( $contact_id, 'contact' );
self::upsert_crm_feed( $newsletter_id, 'newsletter' );
self::save_form_ids( $contact_id, $newsletter_id );
update_option(
self::STATE_OPTION,
array(
'version' => self::VERSION,
'status' => 'complete',
'projects_seeded' => true,
'contact_form_id' => $contact_id,
'newsletter_form_id' => $newsletter_id,
'crm_feeds' => true,
'message' => __( 'Formulários e feeds do FluentCRM configurados.', 'gustavoo-portfolio-core' ),
),
false
);
} catch ( Throwable $error ) {
$state['status'] = 'error';
$state['message'] = sanitize_text_field( $error->getMessage() );
update_option( self::STATE_OPTION, $state, false );
} finally {
delete_option( self::LOCK_OPTION );
}
}
/**
* Configure the WordPress pieces that make the bundled theme work on a
* fresh installation.
*
* Existing sites are left untouched: each value is written only when the
* corresponding WordPress setting still has its fresh-install value.
*
* @return void
*/
private static function seed_site_configuration() {
$state = get_option( self::SITE_OPTION, array() );
if ( ! is_array( $state ) || empty( $state['pages'] ) ) {
$home_id = self::ensure_seed_page( 'inicio', __( 'Início', 'gustavoo-portfolio-core' ) );
$blog_id = self::ensure_seed_page( 'blog', __( 'Blog', 'gustavoo-portfolio-core' ) );
if ( $home_id && $blog_id && 'posts' === get_option( 'show_on_front' ) ) {
update_option( 'show_on_front', 'page' );
update_option( 'page_on_front', $home_id );
update_option( 'page_for_posts', $blog_id );
}
if ( $home_id && $blog_id ) {
$state = is_array( $state ) ? $state : array();
$state['pages'] = true;
update_option( self::SITE_OPTION, $state, false );
}
}
if ( empty( $state['widgets'] ) ) {
self::seed_theme_widgets();
$state = is_array( $state ) ? $state : array();
$state['widgets'] = true;
update_option( self::SITE_OPTION, $state, false );
}
}
/**
* Create a required empty page once and identify it by a private marker.
*
* @param string $slug Page slug.
* @param string $title Page title.
* @return int
*/
private static function ensure_seed_page( $slug, $title ) {
$existing = get_posts(
array(
'post_type' => 'page',
'post_status' => 'any',
'posts_per_page' => 1,
'fields' => 'ids',
'meta_key' => '_gso_seed_page',
'meta_value' => $slug,
)
);
if ( $existing ) {
return absint( $existing[0] );
}
$page = get_page_by_path( $slug, OBJECT, 'page' );
if ( $page ) {
update_post_meta( $page->ID, '_gso_seed_page', $slug );
return absint( $page->ID );
}
$page_id = wp_insert_post(
array(
'post_type' => 'page',
'post_status' => 'publish',
'post_title' => $title,
'post_name' => $slug,
),
true
);
if ( is_wp_error( $page_id ) ) {
return 0;
}
update_post_meta( $page_id, '_gso_seed_page', $slug );
return absint( $page_id );
}
/**
* Reproduce the sidebar and footer widget arrangement from this site.
* Widgets are added only to empty areas, so a user's arrangement wins.
*
* @return void
*/
private static function seed_theme_widgets() {
$sidebars = wp_get_sidebars_widgets();
$sidebars = is_array( $sidebars ) ? $sidebars : array();
$blog_widgets = isset( $sidebars['sidebar-blog'] ) ? (array) $sidebars['sidebar-blog'] : array();
$can_seed_blog = empty( $blog_widgets ) || array( 'gustavoo_portfolio_newsletter-1' ) === $blog_widgets;
if ( $can_seed_blog ) {
$newsletter = get_option( 'widget_gustavoo_portfolio_newsletter', array() );
$newsletter = is_array( $newsletter ) ? $newsletter : array();
$newsletter[1] = array(
'title' => __( 'Newsletter', 'gustavoo-portfolio-core' ),
'text' => __( 'Receba novos artigos sobre desenvolvimento, infraestrutura e automação.', 'gustavoo-portfolio-core' ),
'form_id' => 0,
);
$newsletter['_multiwidget'] = 1;
update_option( 'widget_gustavoo_portfolio_newsletter', $newsletter, false );
$blocks = get_option( 'widget_block', array() );
$blocks = is_array( $blocks ) ? $blocks : array();
$blocks[2] = array( 'content' => '<!-- wp:search /-->' );
$blocks[3] = array( 'content' => '<!-- wp:group --><div class="wp-block-group"><!-- wp:heading --><h2>Posts recentes</h2><!-- /wp:heading --><!-- wp:latest-posts /--></div><!-- /wp:group -->' );
$blocks[4] = array( 'content' => '<!-- wp:group --><div class="wp-block-group"><!-- wp:heading --><h2>Comentários</h2><!-- /wp:heading --><!-- wp:latest-comments {"displayAvatar":false,"displayDate":false,"displayExcerpt":false} /--></div><!-- /wp:group -->' );
$blocks[5] = array( 'content' => '<!-- wp:group --><div class="wp-block-group"><!-- wp:heading --><h2>Arquivos</h2><!-- /wp:heading --><!-- wp:archives /--></div><!-- /wp:group -->' );
$blocks[6] = array( 'content' => '<!-- wp:group --><div class="wp-block-group"><!-- wp:heading --><h2>Categorias</h2><!-- /wp:heading --><!-- wp:categories /--></div><!-- /wp:group -->' );
$blocks['_multiwidget'] = 1;
update_option( 'widget_block', $blocks, false );
$sidebars['sidebar-blog'] = array( 'gustavoo_portfolio_newsletter-1', 'block-2', 'block-3', 'block-4' );
}
if ( empty( $sidebars['footer-1'] ) ) {
$sidebars['footer-1'] = array( 'block-5', 'block-6' );
}
update_option( 'sidebars_widgets', $sidebars, false );
}
/**
* Repair the seeded contact form when an earlier migration did not persist.
*
* @return void
*/
private static function maybe_migrate_contact_form() {
if ( ! self::fluent_is_ready() ) {
return;
}
$form = self::find_seeded_form( 'contact' );
if ( ! $form ) {
return;
}
$fields = json_decode( (string) $form->form_fields, true );
$has_message = false;
foreach ( (array) ( $fields['fields'] ?? array() ) as $field ) {
if ( 'message' === (string) ( $field['attributes']['name'] ?? '' ) && 'textarea' === (string) ( $field['element'] ?? '' ) ) {
$has_message = true;
break;
}
}
if ( ! $has_message ) {
try {
self::upsert_form( 'contact', __( 'Site — Contato e orçamento', 'gustavoo-portfolio-core' ), true );
} catch ( Throwable $error ) {
// Keep the front end available if Fluent Forms is temporarily unavailable.
}
}
}
/**
* Check the optional integrations without triggering autoload errors.
*
* @return bool
*/
private static function fluent_is_ready() {
return defined( 'FLUENTFORM' )
&& defined( 'FLUENTCRM' )
&& function_exists( 'fluentformLoadFile' )
&& function_exists( 'FluentCrmApi' )
&& class_exists( '\\FluentForm\\App\\Models\\Form' )
&& class_exists( '\\FluentForm\\App\\Models\\FormMeta' )
&& class_exists( '\\FluentForm\\App\\Services\\Form\\FormService' )
&& class_exists( '\\FluentForm\\App\\Services\\Integrations\\FormIntegrationService' );
}
/**
* Create or reuse a seed-owned Fluent Form.
*
* @param string $key Deterministic form key.
* @param string $title Form title.
* @param bool $include_whatsapp Include the contact message field.
* @return int
* @throws Exception When Fluent Forms cannot create the form.
*/
private static function upsert_form( $key, $title, $include_whatsapp ) {
$form = self::find_seeded_form( $key );
$defaults = fluentformLoadFile( 'Services/FormBuilder/DefaultElements.php' );
$blank = \FluentForm\App\Models\Form::resolvePredefinedForm(
array(
'predefined' => 'blank_form',
'type' => 'form',
)
);
$structure = json_decode( (string) $blank['form_fields'], true );
if ( ! is_array( $defaults ) || ! is_array( $structure ) ) {
throw new Exception( 'Não foi possível carregar a estrutura padrão do Fluent Forms.' );
}
$email = $defaults['general']['input_email'];
$email['uniqElKey'] = 'el_gso_' . $key . '_email';
$email['attributes']['name'] = 'email';
$email['attributes']['placeholder'] = __( 'Seu melhor e-mail', 'gustavoo-portfolio-core' );
$email['settings']['label'] = __( 'E-mail', 'gustavoo-portfolio-core' );
$email['settings']['admin_field_label'] = __( 'E-mail', 'gustavoo-portfolio-core' );
$email['settings']['validation_rules']['required']['value'] = true;
$fields = array( $email );
if ( $include_whatsapp ) {
$whatsapp = $defaults['general']['input_mask'];
$whatsapp['uniqElKey'] = 'el_gso_' . $key . '_whatsapp';
$whatsapp['attributes']['name'] = 'whatsapp';
$whatsapp['attributes']['placeholder'] = '(31) 99999-9999';
$whatsapp['attributes']['data-mask'] = '(00) 00000-0000';
$whatsapp['settings']['label'] = __( 'WhatsApp', 'gustavoo-portfolio-core' );
$whatsapp['settings']['admin_field_label'] = __( 'WhatsApp', 'gustavoo-portfolio-core' );
$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[] = $whatsapp;
$message = $defaults['general']['textarea'];
$message['uniqElKey'] = 'el_gso_' . $key . '_message';
$message['attributes']['name'] = 'message';
$message['attributes']['placeholder'] = __( 'Descreva o motivo do contato e o que você quer construir.', 'gustavoo-portfolio-core' );
$message['attributes']['rows'] = 5;
$message['settings']['label'] = __( 'Como posso ajudar?', 'gustavoo-portfolio-core' );
$message['settings']['admin_field_label'] = __( 'Motivo do contato', 'gustavoo-portfolio-core' );
$message['settings']['validation_rules']['required']['value'] = true;
$fields[] = $message;
}
$structure['fields'] = $fields;
$structure['submitButton']['settings']['button_ui']['text'] = $include_whatsapp
? __( 'Solicitar contato', 'gustavoo-portfolio-core' )
: __( 'Quero receber', 'gustavoo-portfolio-core' );
$service = new \FluentForm\App\Services\Form\FormService();
if ( $form ) {
$service->update(
array(
'form_id' => $form->id,
'title' => $title,
'status' => 'published',
'formFields' => wp_json_encode( $structure, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ),
)
);
self::localize_form_confirmation( $form->id, $include_whatsapp );
return absint( $form->id );
}
$form = $service->store(
array(
'predefined' => 'blank_form',
'type' => 'form',
)
);
$form = $service->update(
array(
'form_id' => $form->id,
'title' => $title,
'status' => 'published',
'formFields' => wp_json_encode( $structure, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ),
)
);
\FluentForm\App\Models\FormMeta::persist( $form->id, self::FORM_META, $key );
self::localize_form_confirmation( $form->id, $include_whatsapp );
return absint( $form->id );
}
/**
* Locate a form owned by this seed.
*
* @param string $key Seed key.
* @return object|null
*/
private static function find_seeded_form( $key ) {
$markers = \FluentForm\App\Models\FormMeta::where( 'meta_key', self::FORM_META )
->where( 'value', $key )
->get();
foreach ( $markers as $marker ) {
$form = \FluentForm\App\Models\Form::find( absint( $marker->form_id ) );
if ( $form ) {
return $form;
}
}
return null;
}
/**
* Use a concise Portuguese success message.
*
* @param int $form_id Form ID.
* @param bool $is_contact_form Whether this is the lead form.
* @return void
*/
private static function localize_form_confirmation( $form_id, $is_contact_form ) {
$settings = \FluentForm\App\Models\FormMeta::retrieve( 'formSettings', $form_id, array() );
$settings = is_array( $settings ) ? $settings : array();
$settings['confirmation'] = wp_parse_args(
array(
'redirectTo' => 'samePage',
'messageToShow' => $is_contact_form
? __( 'Recebi seus dados. Entrarei em contato em breve.', 'gustavoo-portfolio-core' )
: __( 'Inscrição recebida. Confira seu e-mail para confirmar.', 'gustavoo-portfolio-core' ),
'samePageFormBehavior' => 'hide_form',
),
isset( $settings['confirmation'] ) && is_array( $settings['confirmation'] ) ? $settings['confirmation'] : array()
);
\FluentForm\App\Models\FormMeta::persist( $form_id, 'formSettings', $settings );
}
/**
* Create the FluentCRM feed for one seed-owned form.
*
* @param int $form_id Form ID.
* @param string $context Contact or newsletter.
* @return void
*/
private static function upsert_crm_feed( $form_id, $context ) {
$existing_id = absint( \FluentForm\App\Models\FormMeta::retrieve( self::FEED_META, $form_id, 0 ) );
$defaults = apply_filters( 'fluentform/get_integration_defaults_fluentcrm', array(), $form_id );
$defaults = is_array( $defaults ) ? $defaults : array();
$is_contact = 'contact' === $context;
$feed = array_replace(
$defaults,
array(
'name' => $is_contact ? 'Site / Contato' : 'Site / Newsletter',
'email' => 'email',
'enabled' => true,
'double_opt_in' => ! $is_contact,
'other_fields' => $is_contact
? array(
array(
'label' => 'phone',
'item_value' => '{inputs.whatsapp}',
),
)
: array(),
'conditionals' => array(
'status' => false,
'type' => 'all',
'conditions' => array(),
),
)
);
$result = ( new \FluentForm\App\Services\Integrations\FormIntegrationService() )->update(
array(
'form_id' => $form_id,
'integration_id' => $existing_id,
'integration_name' => 'fluentcrm',
'data_type' => 'array',
'status' => true,
'integration' => $feed,
)
);
if ( ! empty( $result['integration_id'] ) ) {
\FluentForm\App\Models\FormMeta::persist( $form_id, self::FEED_META, absint( $result['integration_id'] ) );
}
}
/**
* Share form IDs with the theme settings option.
*
* @param int $contact_id Contact form ID.
* @param int $newsletter_id Newsletter form ID.
* @return void
*/
private static function save_form_ids( $contact_id, $newsletter_id ) {
$settings = GSO_Portfolio_Settings::get_settings();
$settings['contact_form_id'] = absint( $contact_id );
$settings['newsletter_form_id'] = absint( $newsletter_id );
update_option( GSO_Portfolio_Settings::OPTION_NAME, $settings, false );
}
/**
* Add starter projects only when their deterministic markers are absent.
*
* @return void
*/
private static function seed_projects() {
$projects = array(
'aurelia-online' => array(
'title' => 'Aurelia Online',
'excerpt' => __( 'Plataforma online desenvolvida com foco em performance, experiência do usuário e evolução contínua.', 'gustavoo-portfolio-core' ),
'type' => 'Plataforma web',
'technologies' => array( 'Web', 'APIs', 'Infraestrutura' ),
'featured' => true,
),
'lumenix-engine' => array(
'title' => 'Lumenix Engine',
'excerpt' => __( 'Engine e ecossistema técnico para experiências interativas e produtos digitais.', 'gustavoo-portfolio-core' ),
'type' => 'Games',
'technologies' => array( 'C++', 'Games', 'Performance' ),
'featured' => true,
),
'aurabet' => array(
'title' => 'AuraBet',
'excerpt' => __( 'Produto digital com arquitetura, integrações e infraestrutura preparadas para escala.', 'gustavoo-portfolio-core' ),
'type' => 'Produto digital',
'technologies' => array( 'Full Stack', 'Docker', 'APIs' ),
'featured' => true,
),
'visionforge' => array(
'title' => 'VisionForge',
'excerpt' => __( 'Solução criada para transformar processos complexos em uma experiência clara e eficiente.', 'gustavoo-portfolio-core' ),
'type' => 'Software',
'technologies' => array( 'Automação', 'Dados', 'Web' ),
'featured' => true,
),
'papel-e-cor' => array(
'title' => 'Papel & Cor',
'excerpt' => __( 'Experiência de e-commerce otimizada para catálogo, compra e conversão.', 'gustavoo-portfolio-core' ),
'type' => 'E-commerce',
'technologies' => array( 'WordPress', 'WooCommerce', 'UX' ),
'featured' => false,
),
'lumina-hub' => array(
'title' => 'Lumina Hub',
'excerpt' => __( 'Hub digital que reúne conteúdo, serviços e integrações em uma única plataforma.', 'gustavoo-portfolio-core' ),
'type' => 'Plataforma web',
'technologies' => array( 'WordPress', 'APIs', 'Automação' ),
'featured' => false,
),
);
$order = 0;
foreach ( $projects as $key => $project ) {
$existing = get_posts(
array(
'post_type' => GSO_Projects::POST_TYPE,
'post_status' => 'any',
'posts_per_page' => 1,
'fields' => 'ids',
'meta_key' => self::PROJECT_META,
'meta_value' => $key,
)
);
if ( $existing ) {
++$order;
continue;
}
$post_id = wp_insert_post(
array(
'post_type' => GSO_Projects::POST_TYPE,
'post_status' => 'publish',
'post_title' => $project['title'],
'post_excerpt' => $project['excerpt'],
'menu_order' => $order,
),
true
);
if ( is_wp_error( $post_id ) ) {
++$order;
continue;
}
update_post_meta( $post_id, self::PROJECT_META, $key );
update_post_meta( $post_id, GSO_Projects::META_URL, 'https://gustavoo.me/#projetos' );
update_post_meta( $post_id, GSO_Projects::META_CLIENT, __( 'Projeto selecionado', 'gustavoo-portfolio-core' ) );
update_post_meta( $post_id, GSO_Projects::META_YEAR, (int) gmdate( 'Y' ) );
update_post_meta( $post_id, GSO_Projects::META_LINK_LABEL, __( 'Conhecer projeto', 'gustavoo-portfolio-core' ) );
update_post_meta( $post_id, GSO_Projects::META_FEATURED, $project['featured'] ? '1' : '' );
wp_set_object_terms( $post_id, $project['type'], GSO_Projects::TAX_TYPE );
wp_set_object_terms( $post_id, $project['technologies'], GSO_Projects::TAX_TECH );
++$order;
}
}
}