├── index.php ├── public ├── partials │ ├── speakers-page-content.php │ ├── schedule-page.php │ ├── past-events-page.php │ ├── speakers-page.php │ ├── events-listing-page.php │ ├── code-of-conduct-page.php │ ├── wpfaevent-public-display.php │ └── wpfaevent-landing-template.php ├── index.php ├── css │ └── wpfaevent-public.css ├── js │ └── wpfaevent-public.js └── class-wpfaevent-public.php ├── admin ├── index.php ├── css │ └── wpfaevent-admin.css ├── partials │ └── wpfaevent-admin-display.php ├── js │ └── wpfaevent-admin.js └── class-wpfaevent-admin.php ├── includes ├── index.php ├── wpfaevent-public.css ├── class-wpfaevent-activator.php ├── class-wpfaevent-deactivator.php ├── class-wpfaevent-i18n.php ├── class-wpfaevent-uninstaller.php ├── class-wpfaevent-landing.php ├── class-wpfaevent.php ├── class-wpfaevent-loader.php └── class-wpfa-cli.php ├── assets ├── images │ └── logo.png └── data │ └── minimal.json ├── .gitignore ├── .github └── workflows │ └── phpcs.yml ├── phpcs.xml ├── readme.txt ├── uninstall.php ├── wpfaevent.php ├── README.md └── LICENSE /index.php: -------------------------------------------------------------------------------- 1 | '; 9 | } 10 | -------------------------------------------------------------------------------- /public/partials/past-events-page.php: -------------------------------------------------------------------------------- 1 | '; 9 | } 10 | -------------------------------------------------------------------------------- /public/partials/speakers-page.php: -------------------------------------------------------------------------------- 1 | '; 9 | } 10 | -------------------------------------------------------------------------------- /public/partials/events-listing-page.php: -------------------------------------------------------------------------------- 1 | '; 9 | } 10 | -------------------------------------------------------------------------------- /public/partials/code-of-conduct-page.php: -------------------------------------------------------------------------------- 1 | '; 9 | } 10 | -------------------------------------------------------------------------------- /admin/partials/wpfaevent-admin-display.php: -------------------------------------------------------------------------------- 1 |

Admin Dashboard

Admin template not found.

'; 9 | } 10 | -------------------------------------------------------------------------------- /public/partials/wpfaevent-public-display.php: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.github/workflows/phpcs.yml: -------------------------------------------------------------------------------- 1 | name: PHPCS 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | phpcs: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Setup PHP 15 | uses: shivammathur/setup-php@v2 16 | with: 17 | php-version: '8.0' 18 | - name: Install composer deps 19 | run: composer require --dev squizlabs/php_codesniffer 20 | - name: Run PHPCS 21 | run: vendor/bin/phpcs --standard=WordPress . || true 22 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | WordPress Coding Standards for WPFA Event. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ./ 14 | 15 | vendor/* 16 | node_modules/* 17 | 18 | 19 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === WPFA Event === 2 | Contributors: fossasia 3 | Tags: events 4 | Requires at least: 5.8 5 | Tested up to: 6.6 6 | Requires PHP: 7.4 7 | Stable tag: 0.1.0 8 | License: Apache2 9 | License URI: https://www.apache.org/licenses/LICENSE-2.0.txt 10 | 11 | Skeleton plugin for the FOSSASIA Event project. 12 | 13 | == Description == 14 | The FOSSASIA Event Plugin provides WordPress integrations for Eventyay-based events. It allows you to display event sessions, speakers, and schedules directly on WordPress pages using shortcodes, manual content, or custom templates. This plugin is maintained by FOSSASIA and is compatible with the eventyay platform. 15 | 16 | == Changelog == 17 | = 0.1.0 = 18 | * Initial skeleton. 19 | 20 | -------------------------------------------------------------------------------- /includes/class-wpfaevent-activator.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class Wpfaevent_Activator { 24 | 25 | /** 26 | * Short Description. (use period) 27 | * 28 | * Long Description. 29 | * 30 | * @since 1.0.0 31 | */ 32 | public static function activate() { 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /includes/class-wpfaevent-deactivator.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class Wpfaevent_Deactivator { 24 | 25 | /** 26 | * Short Description. (use period) 27 | * 28 | * Long Description. 29 | * 30 | * @since 1.0.0 31 | */ 32 | public static function deactivate() { 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /includes/class-wpfaevent-i18n.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class Wpfaevent_i18n { 21 | 22 | /** 23 | * Load the plugin text domain for translation. 24 | * 25 | * @since 1.0.0 26 | */ 27 | public function load_plugin_textdomain() { 28 | 29 | load_plugin_textdomain( 30 | 'wpfaevent', 31 | false, 32 | dirname( dirname( plugin_basename( __FILE__ ) ) ) . '/languages/' 33 | ); 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /admin/js/wpfaevent-admin.js: -------------------------------------------------------------------------------- 1 | (function( $ ) { 2 | 'use strict'; 3 | 4 | /** 5 | * All of the code for your admin-facing JavaScript source 6 | * should reside in this file. 7 | * 8 | * Note: It has been assumed you will write jQuery code here, so the 9 | * $ function reference has been prepared for usage within the scope 10 | * of this function. 11 | * 12 | * This enables you to define handlers, for when the DOM is ready: 13 | * 14 | * $(function() { 15 | * 16 | * }); 17 | * 18 | * When the window is loaded: 19 | * 20 | * $( window ).load(function() { 21 | * 22 | * }); 23 | * 24 | * ...and/or other possibilities. 25 | * 26 | * Ideally, it is not considered best practise to attach more than a 27 | * single DOM-ready or window-load handler for a particular page. 28 | * Although scripts in the WordPress core, Plugins and Themes may be 29 | * practising this, we should strive to set a better example in our own work. 30 | */ 31 | 32 | })( jQuery ); 33 | -------------------------------------------------------------------------------- /public/js/wpfaevent-public.js: -------------------------------------------------------------------------------- 1 | (function( $ ) { 2 | 'use strict'; 3 | 4 | /** 5 | * All of the code for your public-facing JavaScript source 6 | * should reside in this file. 7 | * 8 | * Note: It has been assumed you will write jQuery code here, so the 9 | * $ function reference has been prepared for usage within the scope 10 | * of this function. 11 | * 12 | * This enables you to define handlers, for when the DOM is ready: 13 | * 14 | * $(function() { 15 | * 16 | * }); 17 | * 18 | * When the window is loaded: 19 | * 20 | * $( window ).load(function() { 21 | * 22 | * }); 23 | * 24 | * ...and/or other possibilities. 25 | * 26 | * Ideally, it is not considered best practise to attach more than a 27 | * single DOM-ready or window-load handler for a particular page. 28 | * Although scripts in the WordPress core, Plugins and Themes may be 29 | * practising this, we should strive to set a better example in our own work. 30 | */ 31 | 32 | })( jQuery ); 33 | -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 | ID, true ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /assets/data/minimal.json: -------------------------------------------------------------------------------- 1 | { 2 | "speakers": [ 3 | { 4 | "title": "Alex Example", 5 | "content": "Open source contributor and community speaker.", 6 | "slug": "alex-example", 7 | "org": "FOSSASIA", 8 | "position": "Developer Advocate", 9 | "photo": "https://via.placeholder.com/300x300.png?text=Alex" 10 | }, 11 | { 12 | "title": "Bao Nguyen", 13 | "content": "Engineer focusing on event platforms and accessibility.", 14 | "slug": "bao-nguyen", 15 | "org": "Eventyay", 16 | "position": "Software Engineer", 17 | "photo": "https://via.placeholder.com/300x300.png?text=Bao" 18 | } 19 | ], 20 | "events": [ 21 | { 22 | "title": "FOSSASIA Community Meetup", 23 | "content": "A casual meetup to discuss the roadmap and OSS collaboration.", 24 | "slug": "fossasia-community-meetup", 25 | "start_date": "2026-01-15", 26 | "end_date": "2026-01-16", 27 | "location": "Online", 28 | "url": "https://eventyay.com/", 29 | "speakers": ["alex-example", "bao-nguyen"] 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /wpfaevent.php: -------------------------------------------------------------------------------- 1 | ID, true ); // true to force delete and bypass trash. 50 | } 51 | } 52 | 53 | // Query for and delete all dynamic event pages identified by the _event_date meta key. 54 | $event_pages_query = new WP_Query( [ 55 | 'post_type' => 'page', 56 | 'posts_per_page' => -1, 57 | 'meta_key' => '_wp_page_template', 58 | 'compare' => 'EXISTS', 59 | 'fields' => 'ids', // Only get post IDs for efficiency. 60 | ] ); 61 | 62 | if ( $event_pages_query->have_posts() ) { 63 | foreach ( $event_pages_query->posts as $page_id ) { 64 | wp_delete_post( $page_id, true ); 65 | } 66 | } 67 | } 68 | 69 | /** 70 | * Deletes the plugin's data directory using the WP_Filesystem API. 71 | */ 72 | private static function delete_data_directory() { 73 | global $wp_filesystem; 74 | 75 | // Ensure the filesystem is initialized. 76 | if ( empty( $wp_filesystem ) ) { 77 | require_once ABSPATH . '/wp-admin/includes/file.php'; 78 | WP_Filesystem(); 79 | } 80 | 81 | $upload_dir = wp_upload_dir(); 82 | $data_dir = $upload_dir['basedir'] . '/fossasia-data'; 83 | 84 | if ( $wp_filesystem->is_dir( $data_dir ) ) { 85 | $wp_filesystem->rmdir( $data_dir, true ); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /admin/class-wpfaevent-admin.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class Wpfaevent_Admin { 24 | 25 | /** 26 | * The ID of this plugin. 27 | * 28 | * @since 1.0.0 29 | * @access private 30 | * @var string $plugin_name The ID of this plugin. 31 | */ 32 | private $plugin_name; 33 | 34 | /** 35 | * The version of this plugin. 36 | * 37 | * @since 1.0.0 38 | * @access private 39 | * @var string $version The current version of this plugin. 40 | */ 41 | private $version; 42 | 43 | /** 44 | * Initialize the class and set its properties. 45 | * 46 | * @since 1.0.0 47 | * @param string $plugin_name The name of this plugin. 48 | * @param string $version The version of this plugin. 49 | */ 50 | public function __construct( $plugin_name, $version ) { 51 | 52 | $this->plugin_name = $plugin_name; 53 | $this->version = $version; 54 | 55 | } 56 | 57 | /** 58 | * Register the stylesheets for the admin area. 59 | * 60 | * @since 1.0.0 61 | */ 62 | public function enqueue_styles() { 63 | 64 | /** 65 | * This function is provided for demonstration purposes only. 66 | * 67 | * An instance of this class should be passed to the run() function 68 | * defined in Wpfaevent_Loader as all of the hooks are defined 69 | * in that particular class. 70 | * 71 | * The Wpfaevent_Loader will then create the relationship 72 | * between the defined hooks and the functions defined in this 73 | * class. 74 | */ 75 | 76 | wp_enqueue_style( $this->plugin_name, plugin_dir_url( dirname( __FILE__ ) ) . 'admin/css/wpfaevent-admin.css', array(), $this->version, 'all' ); 77 | 78 | } 79 | 80 | /** 81 | * Register the JavaScript for the admin area. 82 | * 83 | * @since 1.0.0 84 | */ 85 | public function enqueue_scripts() { 86 | 87 | /** 88 | * This function is provided for demonstration purposes only. 89 | * 90 | * An instance of this class should be passed to the run() function 91 | * defined in Wpfaevent_Loader as all of the hooks are defined 92 | * in that particular class. 93 | * 94 | * The Wpfaevent_Loader will then create the relationship 95 | * between the defined hooks and the functions defined in this 96 | * class. 97 | */ 98 | 99 | wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/wpfaevent-admin.js', array( 'jquery' ), $this->version, false ); 100 | 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /public/class-wpfaevent-public.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class Wpfaevent_Public { 24 | 25 | /** 26 | * The ID of this plugin. 27 | * 28 | * @since 1.0.0 29 | * @access private 30 | * @var string $plugin_name The ID of this plugin. 31 | */ 32 | private $plugin_name; 33 | 34 | /** 35 | * The version of this plugin. 36 | * 37 | * @since 1.0.0 38 | * @access private 39 | * @var string $version The current version of this plugin. 40 | */ 41 | private $version; 42 | 43 | /** 44 | * Initialize the class and set its properties. 45 | * 46 | * @since 1.0.0 47 | * @param string $plugin_name The name of the plugin. 48 | * @param string $version The version of this plugin. 49 | */ 50 | public function __construct( $plugin_name, $version ) { 51 | 52 | $this->plugin_name = $plugin_name; 53 | $this->version = $version; 54 | 55 | } 56 | 57 | /** 58 | * Register the stylesheets for the public-facing side of the site. 59 | * 60 | * @since 1.0.0 61 | */ 62 | public function enqueue_styles() { 63 | 64 | /** 65 | * This function is provided for demonstration purposes only. 66 | * 67 | * An instance of this class should be passed to the run() function 68 | * defined in Wpfaevent_Loader as all of the hooks are defined 69 | * in that particular class. 70 | * 71 | * The Wpfaevent_Loader will then create the relationship 72 | * between the defined hooks and the functions defined in this 73 | * class. 74 | */ 75 | 76 | wp_enqueue_style( $this->plugin_name, plugin_dir_url( dirname( __FILE__ ) ) . 'public/css/wpfaevent-public.css', array(), $this->version, 'all' ); 77 | 78 | } 79 | 80 | /** 81 | * Register the JavaScript for the public-facing side of the site. 82 | * 83 | * @since 1.0.0 84 | */ 85 | public function enqueue_scripts() { 86 | 87 | /** 88 | * This function is provided for demonstration purposes only. 89 | * 90 | * An instance of this class should be passed to the run() function 91 | * defined in Wpfaevent_Loader as all of the hooks are defined 92 | * in that particular class. 93 | * 94 | * The Wpfaevent_Loader will then create the relationship 95 | * between the defined hooks and the functions defined in this 96 | * class. 97 | */ 98 | 99 | wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/wpfaevent-public.js', array( 'jquery' ), $this->version, false ); 100 | 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /includes/class-wpfaevent-landing.php: -------------------------------------------------------------------------------- 1 | @ -1,97 +1,106 @@ 2 | 52 | For documentation and setup instructions, visit the 53 | 54 | official repository 55 | . 56 |

57 | 58 | /** 59 | * Loads the custom template file when a page with that template is viewed. 60 | * 61 | * @param string $template The path of the template to include. 62 | * @return string The path of the template file. 63 | */ 64 | public function load_template( $template ) { 65 | if ( is_page_template( 'public/partials/wpfaevent-landing-template.php' ) ) { 66 | return plugin_dir_path( __DIR__ ) . 'public/partials/wpfaevent-landing-template.php'; 67 | } 68 | if ( is_page_template( 'templates/events-listing-page.php' ) ) { 69 | return plugin_dir_path( __DIR__ ) . 'public/partials/wpfaevent-landing-template.php'; 70 | } 71 | if ( is_page_template( 'admin/partials/admin-dashboard.php' ) ) { 72 | return plugin_dir_path( __DIR__ ) . 'admin/partials/admin-dashboard.php'; 73 | } 74 | if ( is_page_template( 'public/partials/speakers-page.php' ) ) { 75 | return plugin_dir_path( __DIR__ ) . 'public/partials/speakers-page.php'; 76 | } 77 | if ( is_page_template( 'public/partials/schedule-page.php' ) ) { 78 | return plugin_dir_path( __DIR__ ) . 'public/partials/schedule-page.php'; 79 | } 80 | if ( is_page_template( 'public/partials/past-events-page.php' ) ) { 81 | return plugin_dir_path( __DIR__ ) . 'public/partials/past-events-page.php'; 82 | } 83 | if ( is_page_template( 'templates/code-of-conduct-page.php' ) ) { 84 | return plugin_dir_path( __DIR__ ) . 'public/partials/wpfaevent-landing-template.php'; 85 | } 86 | return $template; 87 | } 88 | 89 | 117 | create_page_if_not_exists( 'FOSSASIA Summit', 'fossasia-summit', 'public/partials/wpfaevent-landing-template.php' ); 124 | $this->create_page_if_not_exists( 'Speakers', 'speakers', 'public/partials/speakers-page.php' ); 125 | $this->create_page_if_not_exists( 'Full Schedule', 'full-schedule', 'public/partials/schedule-page.php' ); 126 | $this->create_page_if_not_exists( 'Admin Dashboard', 'admin-dashboard', 'public/partials/wpfaevent-landing-template.php', 'private' ); 127 | $this->create_page_if_not_exists( 'Events', 'events', 'public/partials/wpfaevent-landing-template.php' ); 128 | $this->create_page_if_not_exists( 'Past Events', 'past-events', 'public/partials/past-events-page.php' ); 129 | $this->create_page_if_not_exists( 'Code of Conduct', 'code-of-conduct', 'public/partials/wpfaevent-landing-template.php' ); 130 | } 131 | } -------------------------------------------------------------------------------- /includes/class-wpfaevent.php: -------------------------------------------------------------------------------- 1 | plugin_name = 'wpfaevent'; 39 | $this->version = WPFAEVENT_VERSION; 40 | 41 | $this->load_dependencies(); 42 | $this->define_admin_hooks(); 43 | $this->define_public_hooks(); 44 | } 45 | 46 | private function load_dependencies() { 47 | // Loader 48 | require_once plugin_dir_path( __FILE__ ) . 'class-wpfaevent-loader.php'; 49 | 50 | // Legacy plugin code (defines FOSSASIA_Landing_Plugin class) 51 | require_once plugin_dir_path( __FILE__ ) . 'class-wpfaevent-landing.php'; 52 | 53 | // Admin and Public classes 54 | require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-wpfaevent-admin.php'; 55 | require_once plugin_dir_path( dirname( __FILE__ ) ) . 'public/class-wpfaevent-public.php'; 56 | 57 | // Optional utilities if present 58 | if ( file_exists( plugin_dir_path( __FILE__ ) . 'class-wpfa-cli.php' ) ) { 59 | require_once plugin_dir_path( __FILE__ ) . 'class-wpfa-cli.php'; 60 | } 61 | if ( file_exists( plugin_dir_path( __FILE__ ) . 'class-wpfaevent-uninstaller.php' ) ) { 62 | require_once plugin_dir_path( __FILE__ ) . 'class-wpfaevent-uninstaller.php'; 63 | } 64 | 65 | $this->loader = new Wpfaevent_Loader(); 66 | } 67 | 68 | private function define_admin_hooks() { 69 | // Instantiate the admin class 70 | $this->plugin_admin = new Wpfaevent_Admin( $this->plugin_name, $this->version ); 71 | 72 | // Register admin-specific stylesheet 73 | $this->loader->add_action( 'admin_enqueue_scripts', $this->plugin_admin, 'enqueue_styles' ); 74 | 75 | // Instantiate the legacy plugin and keep a reference so we can reuse its methods 76 | if ( class_exists( 'Wpfaevent_Landing' ) ) { 77 | $this->legacy = new Wpfaevent_Landing(); 78 | } 79 | 80 | if ( ! $this->legacy ) { return; } 81 | 82 | // Register admin-facing hooks via the loader so tests/tooling can inspect them 83 | $this->loader->add_action( 'admin_enqueue_scripts', $this->legacy, 'enqueue_admin_scripts' ); 84 | 85 | // Register the many AJAX handlers the legacy class provides 86 | $ajax_methods = [ 87 | 'fossasia_manage_speakers' => 'ajax_manage_speakers', 88 | 'fossasia_manage_sponsors' => 'ajax_manage_sponsors', 89 | 'fossasia_manage_site_settings' => 'ajax_manage_site_settings', 90 | 'fossasia_manage_sections' => 'ajax_manage_sections', 91 | 'fossasia_manage_schedule' => 'ajax_manage_schedule', 92 | 'fossasia_manage_navigation' => 'ajax_manage_navigation', 93 | 'fossasia_sync_eventyay' => 'ajax_sync_eventyay', 94 | 'fossasia_create_event_page' => 'ajax_create_event_page', 95 | 'fossasia_edit_event_page' => 'ajax_edit_event_page', 96 | 'fossasia_delete_event_page' => 'ajax_delete_event_page', 97 | 'fossasia_manage_theme_settings' => 'ajax_manage_theme_settings', 98 | 'fossasia_import_sample_data' => 'ajax_import_sample_data', 99 | 'fossasia_add_sample_event' => 'ajax_add_sample_event', 100 | 'fossasia_manage_coc' => 'ajax_manage_coc', 101 | ]; 102 | 103 | foreach ( $ajax_methods as $action => $method ) { 104 | // admin ajax 105 | $this->loader->add_action( 'wp_ajax_' . $action, $this->legacy, $method ); 106 | } 107 | } 108 | 109 | private function define_public_hooks() { 110 | // Instantiate the public class 111 | $this->plugin_public = new Wpfaevent_Public( $this->plugin_name, $this->version ); 112 | 113 | // Register public-specific stylesheet 114 | $this->loader->add_action( 'wp_enqueue_scripts', $this->plugin_public, 'enqueue_styles' ); 115 | 116 | if ( ! $this->legacy ) { return; } 117 | 118 | // Template registration and inclusion 119 | $this->loader->add_filter( 'theme_page_templates', $this->legacy, 'register_template' ); 120 | $this->loader->add_filter( 'template_include', $this->legacy, 'load_template', 99 ); 121 | $this->loader->add_action( 'init', $this->legacy, 'setup_pages' ); 122 | } 123 | 124 | public function run() { 125 | $this->loader->run(); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /includes/class-wpfaevent-loader.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | class Wpfaevent_Loader { 25 | 26 | /** 27 | * The array of actions registered with WordPress. 28 | * 29 | * @since 1.0.0 30 | * @access protected 31 | * @var array $actions The actions registered with WordPress to fire when the plugin loads. 32 | */ 33 | protected $actions; 34 | 35 | /** 36 | * The array of filters registered with WordPress. 37 | * 38 | * @since 1.0.0 39 | * @access protected 40 | * @var array $filters The filters registered with WordPress to fire when the plugin loads. 41 | */ 42 | protected $filters; 43 | 44 | /** 45 | * Initialize the collections used to maintain the actions and filters. 46 | * 47 | * @since 1.0.0 48 | */ 49 | public function __construct() { 50 | 51 | $this->actions = array(); 52 | $this->filters = array(); 53 | 54 | } 55 | 56 | /** 57 | * Add a new action to the collection to be registered with WordPress. 58 | * 59 | * @since 1.0.0 60 | * @param string $hook The name of the WordPress action that is being registered. 61 | * @param object $component A reference to the instance of the object on which the action is defined. 62 | * @param string $callback The name of the function definition on the $component. 63 | * @param int $priority Optional. The priority at which the function should be fired. Default is 10. 64 | * @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1. 65 | */ 66 | public function add_action( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { 67 | $this->actions = $this->add( $this->actions, $hook, $component, $callback, $priority, $accepted_args ); 68 | } 69 | 70 | /** 71 | * Add a new filter to the collection to be registered with WordPress. 72 | * 73 | * @since 1.0.0 74 | * @param string $hook The name of the WordPress filter that is being registered. 75 | * @param object $component A reference to the instance of the object on which the filter is defined. 76 | * @param string $callback The name of the function definition on the $component. 77 | * @param int $priority Optional. The priority at which the function should be fired. Default is 10. 78 | * @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1 79 | */ 80 | public function add_filter( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { 81 | $this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority, $accepted_args ); 82 | } 83 | 84 | /** 85 | * A utility function that is used to register the actions and hooks into a single 86 | * collection. 87 | * 88 | * @since 1.0.0 89 | * @access private 90 | * @param array $hooks The collection of hooks that is being registered (that is, actions or filters). 91 | * @param string $hook The name of the WordPress filter that is being registered. 92 | * @param object $component A reference to the instance of the object on which the filter is defined. 93 | * @param string $callback The name of the function definition on the $component. 94 | * @param int $priority The priority at which the function should be fired. 95 | * @param int $accepted_args The number of arguments that should be passed to the $callback. 96 | * @return array The collection of actions and filters registered with WordPress. 97 | */ 98 | private function add( $hooks, $hook, $component, $callback, $priority, $accepted_args ) { 99 | 100 | $hooks[] = array( 101 | 'hook' => $hook, 102 | 'component' => $component, 103 | 'callback' => $callback, 104 | 'priority' => $priority, 105 | 'accepted_args' => $accepted_args 106 | ); 107 | 108 | return $hooks; 109 | 110 | } 111 | 112 | /** 113 | * Register the filters and actions with WordPress. 114 | * 115 | * @since 1.0.0 116 | */ 117 | public function run() { 118 | 119 | foreach ( $this->filters as $hook ) { 120 | add_filter( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); 121 | } 122 | 123 | foreach ( $this->actions as $hook ) { 124 | add_action( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); 125 | } 126 | 127 | } 128 | 129 | /** 130 | * Initialize WPFA Event Landing setup. 131 | * 132 | * This method loads the logic from class-wpfaevent-landing.php and ensures 133 | * that the plugin initializes correctly without replacing the 134 | * existing boilerplate structure. 135 | * 136 | * @since 1.0.0 137 | */ 138 | public function initialize_wpfaevent_landing() { 139 | // Include the main landing logic file. 140 | require_once plugin_dir_path( __FILE__ ) . 'class-wpfaevent-landing.php'; 141 | 142 | // Initialize the landing setup if the class exists. 143 | if ( class_exists( 'Wpfaevent_Landing' ) ) { 144 | $landing = new Wpfaevent_Landing(); 145 | $landing->init(); 146 | } 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WordPress FOSSASIA Event Plugin (WPFAevent) 2 | 3 | The **FOSSASIA Event Plugin** provides WordPress integrations for [Eventyay](https://eventyay.com)-based events. 4 | It allows you to display event sessions, speakers, and schedules directly on WordPress pages using **shortcodes**, **manual content**, or **custom templates**. 5 | 6 | This plugin is maintained by [FOSSASIA](https://fossasia.org) and is compatible with the **eventyay** platform. 7 | 8 | ## Features 9 | 10 | - Display **speakers**, **sessions**, and **event schedules** from Eventyay or other compatible APIs. 11 | - Works with the unified **Eventyay (Django + Vue 3)** architecture. 12 | - Includes an **admin settings page** to configure JSON API endpoints and cache duration. 13 | - Supports **shortcodes** for embedding event data anywhere on your site. 14 | - Built with modern WordPress development practices: 15 | - Class-based structure 16 | - Hooks and actions 17 | - Internationalization (translation-ready) 18 | - Includes **placeholder data** for local development and testing. 19 | - Easily extendable with custom templates, endpoints, or additional shortcodes. 20 | 21 | --- 22 | 23 | ## Requirements 24 | 25 | - WordPress **5.8** or higher 26 | - PHP **7.4** or newer 27 | - HTTPS-enabled server (for API calls) 28 | - The WordPress REST API and `wp_remote_get()` must be available 29 | 30 | ## Installation 31 | 32 | 1. Download or clone this plugin into your WordPress `wp-content/plugins/` directory: 33 | ```bash 34 | git clone https://github.com/fossasia/WPFAevent.git event-plugin 35 | 36 | 37 | 2. Activate **Event Plugin** in your WordPress Admin under 38 | `Plugins → Installed Plugins → Event Plugin → Activate`. 39 | 40 | 3. Configure your API endpoints: 41 | 42 | * Go to **Settings → Event Plugin** in the WordPress Admin. 43 | * Enter the URLs of your Eventyay API endpoints for **Speakers**, **Sessions**, and **Schedule**. 44 | * Optionally adjust the **cache time (TTL)** in seconds. 45 | 46 | 4. Add shortcodes to your pages or posts, for example: 47 | 48 | ```text 49 | [event_speakers] 50 | [event_sessions] 51 | [event_schedule] 52 | ``` 53 | 54 | These will automatically display data fetched from your configured endpoints. 55 | If no API data is available, placeholder content will appear instead. 56 | 57 | ## Directory Structure 58 | 59 | ``` 60 | event-plugin/ 61 | │ 62 | ├─ event-plugin.php → main plugin file (entry point) 63 | │ 64 | ├─ includes/ 65 | │ ├─ class-event-loader.php → initializes hooks and shortcodes 66 | │ ├─ class-event-api.php → handles remote API fetching with caching 67 | │ ├─ class-event-admin.php → admin settings page (API config, cache) 68 | │ ├─ class-event-speakers.php → logic for speakers shortcode 69 | │ ├─ class-event-sessions.php → logic for sessions shortcode 70 | │ └─ class-event-schedule.php → logic for schedule shortcode 71 | │ 72 | ├─ public/ 73 | │ ├─ partials/ 74 | │ │ ├─ event-speakers.php → speaker display template 75 | │ │ ├─ event-sessions.php → sessions display template 76 | │ │ └─ event-schedule.php → schedule display template 77 | │ ├─ css/ 78 | │ │ └─ event-public.css → public-facing styles 79 | │ └─ js/ 80 | │ ├─ event-public.js → public-facing scripts 81 | │ └─ event-admin.js → admin JS for “Test Connection” buttons 82 | │ 83 | ├─ assets/ 84 | │ └─ img/ 85 | │ └─ speaker-placeholder.jpg → placeholder image (no real data) 86 | │ 87 | ├─ languages/ 88 | │ └─ event-plugin.pot → base translation template 89 | │ 90 | └─ README.md 91 | ``` 92 | 93 | 94 | ## Shortcodes Overview 95 | 96 | | Shortcode | Description | Output Source | 97 | | ------------------ | ------------------------------------------------------- | --------------------------- | 98 | | `[event_speakers]` | Displays the list of speakers. | API endpoint or placeholder | 99 | | `[event_sessions]` | Displays event sessions with title, time, and abstract. | API endpoint or placeholder | 100 | | `[event_schedule]` | Displays daily schedule in a table format. | API endpoint or placeholder | 101 | 102 | Each shortcode can accept optional attributes — for example: 103 | 104 | ```text 105 | [event_schedule profile="summit2026"] 106 | ``` 107 | 108 | if multiple event profiles are configured in settings. 109 | 110 | 111 | ## Settings Page 112 | 113 | Navigate to **Settings → Event Plugin** to configure: 114 | 115 | * **Speakers Endpoint:** `https://example.org/api/v1/events/{id}/speakers` 116 | * **Sessions Endpoint:** `https://example.org/api/v1/events/{id}/sessions` 117 | * **Schedule Endpoint:** `https://example.org/api/v1/events/{id}/schedule` 118 | * **Cache TTL (seconds):** Duration for transient caching of API results 119 | * **Test Buttons:** Verify that endpoints respond with valid JSON data 120 | 121 | If the fields are left empty, the plugin falls back to placeholder content for development. 122 | 123 | ## Development Notes 124 | 125 | * Core logic resides in `includes/`, presentation templates in `public/partials/`. 126 | * All user-facing text should use translation functions `__()` or `_e()`. 127 | * Load assets using `wp_enqueue_script()` and `wp_enqueue_style()`. 128 | * Use the built-in caching layer via transients in `class-event-api.php`. 129 | * Do **not** commit large demo data or real images — use placeholders only. 130 | * To modify the layout, you can override templates in your theme directory: 131 | 132 | ``` 133 | your-theme/event-plugin/partials/event-speakers.php 134 | ``` 135 | 136 | WordPress will automatically use the theme’s version if it exists. 137 | 138 | --- 139 | 140 | ## Local Development 141 | 142 | 1. Install WordPress locally (e.g., using LocalWP, Docker, or WP-CLI). 143 | 2. Place this plugin in `wp-content/plugins/`. 144 | 3. Activate it and navigate to **Settings → Event Plugin**. 145 | 4. Test with public Eventyay JSON endpoints or your own mock data. 146 | 147 | To debug API calls, enable WordPress debug logging in `wp-config.php`: 148 | 149 | ```php 150 | define( 'WP_DEBUG', true ); 151 | define( 'WP_DEBUG_LOG', true ); 152 | ``` 153 | 154 | Logs can be found in `/wp-content/debug.log`. 155 | 156 | ## Translation 157 | 158 | * The plugin is fully internationalization-ready (`Text Domain: event-plugin`). 159 | * Translations are located in the `languages/` directory. 160 | * You can generate `.mo` and `.po` files using tools such as **Poedit** or **Loco Translate**. 161 | 162 | ## Contributing 163 | 164 | Contributions are welcome! 165 | 166 | * Fork the repository on GitHub 167 | * Create a feature branch: 168 | 169 | ```bash 170 | git checkout -b feature/my-feature 171 | ``` 172 | * Commit and push your changes, then submit a **Pull Request** 173 | * Follow **WordPress PHP coding standards** 174 | 175 | Before submitting: 176 | 177 | * Run `phpcs` with the WordPress standard 178 | * Avoid committing binary or large files 179 | * Test locally with caching disabled 180 | * Ensure translations are wrapped correctly in `__()` or `_e()` 181 | 182 | ## License 183 | 184 | Licensed under the **Apache License, Version 2.0** 185 | Copyright © 2025 [FOSSASIA](https://fossasia.org) 186 | -------------------------------------------------------------------------------- /includes/class-wpfa-cli.php: -------------------------------------------------------------------------------- 1 | ] 20 | * : Path to a JSON file with seed data. 21 | * 22 | * ## EXAMPLES 23 | * wp wpfa seed --minimal 24 | * wp wpfa seed --from-json=wp-content/plugins/wpfa-event/assets/demo/minimal.json 25 | * 26 | * @when after_wp_load 27 | * 28 | * @param array $args 29 | * @param array $assoc_args 30 | */ 31 | public static function seed( $args, $assoc_args ) { 32 | if ( isset( $assoc_args['from-json'] ) ) { 33 | self::seed_from_json(plugin_dir_path( __FILE__ ) . $assoc_args['from-json'] ); 34 | return; 35 | } 36 | 37 | if ( isset( $assoc_args['minimal'] ) ) { 38 | self::seed_minimal(); 39 | return; 40 | } 41 | 42 | WP_CLI::error( 'No option provided. Use --minimal or --from-json=.' ); 43 | } 44 | 45 | /** 46 | * Minimal hardcoded seed (2 speakers, 1 event). 47 | */ 48 | private static function seed_minimal() { 49 | $placeholder = 'https://via.placeholder.com/300x300.png?text=Speaker'; 50 | 51 | $speakers = [ 52 | [ 53 | 'post_title' => 'Alex Example', 54 | 'post_content' => 'Open source contributor and community speaker.', 55 | 'meta' => [ 56 | 'wpfa_speaker_org' => 'FOSSASIA', 57 | 'wpfa_speaker_position' => 'Developer Advocate', 58 | 'wpfa_speaker_photo' => $placeholder, 59 | ], 60 | 'slug' => 'alex-example', 61 | ], 62 | [ 63 | 'post_title' => 'Bao Nguyen', 64 | 'post_content' => 'Engineer focusing on event platforms and accessibility.', 65 | 'meta' => [ 66 | 'wpfa_speaker_org' => 'Eventyay', 67 | 'wpfa_speaker_position' => 'Software Engineer', 68 | 'wpfa_speaker_photo' => $placeholder, 69 | ], 70 | 'slug' => 'bao-nguyen', 71 | ], 72 | ]; 73 | 74 | $event = [ 75 | 'post_title' => 'FOSSASIA Community Meetup', 76 | 'post_content' => 'A casual meetup to discuss the roadmap and OSS collaboration.', 77 | 'meta' => [ 78 | 'wpfa_event_start_date' => date( 'Y-m-d', strtotime( '+30 days' ) ), 79 | 'wpfa_event_end_date' => date( 'Y-m-d', strtotime( '+31 days' ) ), 80 | 'wpfa_event_location' => 'Online', 81 | 'wpfa_event_url' => 'https://eventyay.com/', 82 | ], 83 | 'slug' => 'fossasia-community-meetup', 84 | ]; 85 | 86 | // Insert speakers (idempotent by slug). 87 | $speaker_ids = []; 88 | foreach ( $speakers as $s ) { 89 | $speaker_ids[] = self::upsert_post_by_slug( 90 | 'wpfa_speaker', 91 | $s['slug'], 92 | [ 93 | 'post_title' => $s['post_title'], 94 | 'post_content' => $s['post_content'], 95 | 'post_status' => 'publish', 96 | 'post_type' => 'wpfa_speaker', 97 | ], 98 | $s['meta'] 99 | ); 100 | } 101 | 102 | // Insert event (idempotent by slug). 103 | $event_id = self::upsert_post_by_slug( 104 | 'wpfa_event', 105 | $event['slug'], 106 | [ 107 | 'post_title' => $event['post_title'], 108 | 'post_content' => $event['post_content'], 109 | 'post_status' => 'publish', 110 | 'post_type' => 'wpfa_event', 111 | ], 112 | $event['meta'] 113 | ); 114 | 115 | // Relate event ↔ speakers (store both sides; skip if already there). 116 | self::sync_relationships( $event_id, $speaker_ids ); 117 | 118 | WP_CLI::success( 'Seeded minimal data: 2 speakers, 1 event.' ); 119 | } 120 | 121 | /** 122 | * Seed from JSON file. 123 | * 124 | * @param string $path 125 | */ 126 | private static function seed_from_json( $path ) { 127 | if ( ! file_exists( $path ) ) { 128 | WP_CLI::error( "JSON file not found: {$path}" ); 129 | } 130 | $json = file_get_contents( $path ); 131 | if ( false === $json ) { 132 | WP_CLI::error( "Unable to read JSON file: {$path}" ); 133 | } 134 | 135 | $data = json_decode( $json, true ); 136 | if ( ! is_array( $data ) ) { 137 | WP_CLI::error( 'Invalid JSON structure.' ); 138 | } 139 | 140 | // Insert speakers first. 141 | $slug_to_id = []; 142 | if ( ! empty( $data['speakers'] ) && is_array( $data['speakers'] ) ) { 143 | foreach ( $data['speakers'] as $s ) { 144 | $slug = sanitize_title( $s['slug'] ?? $s['title'] ?? wp_generate_uuid4() ); 145 | $title = sanitize_text_field( $s['title'] ?? 'Speaker' ); 146 | $content = wp_kses_post( $s['content'] ?? '' ); 147 | 148 | $meta = [ 149 | 'wpfa_speaker_org' => isset( $s['org'] ) ? sanitize_text_field( $s['org'] ) : '', 150 | 'wpfa_speaker_position' => isset( $s['position'] ) ? sanitize_text_field( $s['position'] ) : '', 151 | 'wpfa_speaker_photo' => isset( $s['photo'] ) ? esc_url_raw( $s['photo'] ) : 'https://via.placeholder.com/300x300.png?text=Speaker', 152 | ]; 153 | 154 | $id = self::upsert_post_by_slug( 'wpfa_speaker', $slug, [ 'post_title' => $title, 'post_content' => $content, 'post_status' => 'publish', 'post_type' => 'wpfa_speaker', ], $meta ); 155 | $slug_to_id[ $slug ] = $id; 156 | } 157 | } 158 | 159 | // Insert events and relate to speakers from slugs. 160 | if ( ! empty( $data['events'] ) && is_array( $data['events'] ) ) { 161 | foreach ( $data['events'] as $e ) { 162 | $slug = sanitize_title( $e['slug'] ?? $e['title'] ?? wp_generate_uuid4() ); 163 | $title = sanitize_text_field( $e['title'] ?? 'Event' ); 164 | $content = wp_kses_post( $e['content'] ?? '' ); 165 | 166 | $meta = [ 167 | 'wpfa_event_start_date' => isset( $e['start_date'] ) ? sanitize_text_field( $e['start_date'] ) : '', 168 | 'wpfa_event_end_date' => isset( $e['end_date'] ) ? sanitize_text_field( $e['end_date'] ) : '', 169 | 'wpfa_event_location' => isset( $e['location'] ) ? sanitize_text_field( $e['location'] ) : '', 170 | 'wpfa_event_url' => isset( $e['url'] ) ? esc_url_raw( $e['url'] ) : '', 171 | ]; 172 | 173 | $event_id = self::upsert_post_by_slug( 'wpfa_event', $slug, [ 'post_title' => $title, 'post_content' => $content, 'post_status' => 'publish', 'post_type' => 'wpfa_event', ], $meta ); 174 | 175 | $event_speaker_slugs = ! empty( $e['speakers'] ) && is_array( $e['speakers'] ) ? $e['speakers'] : []; 176 | $speaker_ids = array_values( array_intersect_key( $slug_to_id, array_flip( $event_speaker_slugs ) ) ); 177 | 178 | self::sync_relationships( $event_id, $speaker_ids ); 179 | } 180 | } 181 | 182 | WP_CLI::success( 'Seeded data from JSON.' ); 183 | } 184 | 185 | /** 186 | * Upsert by slug: create the post if not found; otherwise update meta. 187 | */ 188 | private static function upsert_post_by_slug( $post_type, $slug, $postarr, $meta ) { 189 | $existing = get_page_by_path( $slug, OBJECT, $post_type ); 190 | if ( $existing ) { 191 | $post_id = $existing->ID; 192 | $postarr['ID'] = $post_id; 193 | wp_update_post( $postarr ); 194 | } else { 195 | $postarr['post_name'] = $slug; 196 | $post_id = wp_insert_post( $postarr ); 197 | } 198 | 199 | if ( is_wp_error( $post_id ) || ! $post_id ) { 200 | WP_CLI::warning( "Failed to upsert {$post_type} : {$slug}" ); 201 | return 0; 202 | } 203 | 204 | update_post_meta( $post_id, '_wpfa_seeded', 1 ); 205 | if ( is_array( $meta ) ) { 206 | foreach ( $meta as $k => $v ) { 207 | update_post_meta( $post_id, $k, $v ); 208 | } 209 | } 210 | return $post_id; 211 | } 212 | 213 | /** 214 | * Sync event <-> speakers relationships. 215 | */ 216 | private static function sync_relationships( $event_id, $speaker_ids ) { 217 | $event_id = absint( $event_id ); 218 | $speaker_ids = array_values( array_unique( array_map( 'absint', $speaker_ids ) ) ); 219 | 220 | if ( ! $event_id || empty( $speaker_ids ) ) { 221 | return; 222 | } 223 | 224 | $current = (array) get_post_meta( $event_id, 'wpfa_event_speakers', true ); 225 | $new = array_values( array_unique( array_merge( $current, $speaker_ids ) ) ); 226 | update_post_meta( $event_id, 'wpfa_event_speakers', $new ); 227 | 228 | foreach ( $speaker_ids as $sid ) { 229 | $cur = (array) get_post_meta( $sid, 'wpfa_speaker_events', true ); 230 | if ( ! in_array( $event_id, $cur, true ) ) { 231 | $cur[] = $event_id; 232 | } 233 | update_post_meta( $sid, 'wpfa_speaker_events', array_values( array_unique( $cur ) ) ); 234 | } 235 | } 236 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /public/partials/wpfaevent-landing-template.php: -------------------------------------------------------------------------------- 1 | ($b['order'] ?? 10); 95 | }); 96 | 97 | foreach ($sections_to_render as $section) { 98 | $section_id = esc_attr($section['id']); 99 | $layout = $section['layout'] ?? 'full_width'; 100 | $section_type = $section['type'] ?? 'content'; 101 | ?> 102 |
103 |
104 |
105 | 106 |

107 | 108 | 109 |

110 | 111 |
112 | 113 | ' . esc_attr($section['title']) . ''; 119 | } elseif ($section['mediaType'] === 'video' && !empty($section['video_embed_src'])) { 120 | $embed_url = get_video_embed_url($section['video_embed_src']); 121 | echo '
'; 122 | } elseif ($section['mediaType'] === 'carousel' && !empty($section['carousel_images']) && is_array($section['carousel_images'])) { 123 | $carousel_id = 'carousel-' . esc_attr($section['id']); 124 | $timer = !empty($section['carousel_timer']) ? absint($section['carousel_timer']) * 1000 : 5000; 125 | ?> 126 | 131 | 145 | 150 |
151 | 152 |

153 | 154 | 155 |
156 | 157 | 158 |
159 |
160 | 161 |
162 |
163 | 164 |
165 |
' . esc_attr($section['title']) . ' media
'; 172 | } elseif ($section['mediaType'] === 'map' && !empty($section['map_embed_src'])) { 173 | $media_col_html = '
'; 174 | } 175 | 176 | $content_col_html = ''; 187 | 188 | ?> 189 |
190 | 199 |
200 | 203 | 204 |
205 | 244 | 255 | 256 |
257 | 258 | 259 |