diff --git a/README.md b/README.md index 6781975..b9312fd 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +<<<<<<< Updated upstream # WordPress FOSSASIA Event Plugin (WPFAevent) The **FOSSASIA Event Plugin** provides WordPress integrations for [Eventyay](https://eventyay.com)-based events. @@ -183,3 +184,45 @@ Before submitting: Licensed under the **Apache License, Version 2.0** Copyright © 2025 [FOSSASIA](https://fossasia.org) +======= +# FOSSASIA Event WordPress Plugin + +A plugin to create and manage event landing pages, speakers, schedules, and sponsors within WordPress using Custom Post Types. + +## Features +- Creates `Event` and `Speaker` Custom Post Types (CPTs). +- Provides a clean, database-driven way to manage event data. +- (For Developers) A WP-CLI command to seed test data for development and testing. + +## Getting Started + +1. **Install and Activate** the plugin. +2. You will see new "Events" and "Speakers" menu items in your WordPress admin sidebar. +3. You can now create and manage events and speakers just like standard WordPress posts. +4. To display your content, you will use shortcodes (e.g., `[wpfa_speakers]`) or custom templates that query these new post types. + + +## For Developers: Seeding Data with WP-CLI + +The plugin includes a WP-CLI command for developers to quickly seed test data. This is useful for setting up a development environment, especially when working with the Custom Post Type architecture. + +**Prerequisites:** +- WP-CLI must be installed. +- The plugin must be activated. + +**Commands:** + +1. **Seed Minimal Data:** + This command creates 2 placeholder speakers and 1 event in the database. + ```bash + wp wpfa seed --minimal + ``` + +2. **Seed from JSON file:** + This command populates CPT data from a specified JSON file. A sample fixture is included in the plugin. + ```bash + wp wpfa seed --from-json=wp-content/plugins/wpfa-event/assets/demo/minimal.json + ``` + +The seeder command is **idempotent**, meaning it is safe to re-run. It will update existing posts based on their slugs instead of creating duplicates. +>>>>>>> Stashed changes diff --git a/fossasia-landing.php b/fossasia-landing.php new file mode 100644 index 0000000..7c250e5 --- /dev/null +++ b/fossasia-landing.php @@ -0,0 +1,89 @@ +setup_constants(); + self::$instance->includes(); + } + return self::$instance; + } + + /** + * Setup plugin constants. + */ + private function setup_constants() { + // Plugin version. + define( 'WPFA_EVENT_VERSION', '1.0.0' ); + } + + /** + * Include required files. + */ + private function includes() { + require_once WPFA_EVENT_PLUGIN_DIR . 'includes/class-wpfa-cpt.php'; + require_once WPFA_EVENT_PLUGIN_DIR . 'public/class-wpfa-public.php'; + + if ( defined( 'WP_CLI' ) && WP_CLI ) { + require_once WPFA_EVENT_PLUGIN_DIR . 'includes/class-wpfa-cli.php'; + } + } + + /** + * Run the plugin. + */ + public function run() { + $cpt_handler = new WPFA_CPT(); + $public_handler = new WPFA_Public( 'wpfa-event', WPFA_EVENT_VERSION ); + + add_action( 'init', array( $cpt_handler, 'register_cpts' ) ); + add_action( 'init', array( $cpt_handler, 'register_meta' ) ); + + add_action( 'wp_enqueue_scripts', array( $public_handler, 'enqueue_styles' ) ); + add_action( 'init', array( $public_handler, 'register_shortcodes' ) ); + } +} + +/** + * Begins execution of the plugin. + */ +function run_wpfa_event() { + $plugin = WPFA_Event::instance(); + $plugin->run(); +} + +add_action( 'plugins_loaded', 'run_wpfa_event' ); + +// Register WP-CLI command if in CLI context. +if ( defined( 'WP_CLI' ) && WP_CLI ) { + WP_CLI::add_command( 'wpfa', 'WPFA_CLI' ); +} diff --git a/includes/class-fossasia-uninstaller.php b/includes/class-fossasia-uninstaller.php new file mode 100644 index 0000000..4e04e01 --- /dev/null +++ b/includes/class-fossasia-uninstaller.php @@ -0,0 +1,73 @@ + $post_type, + 'posts_per_page' => -1, + 'fields' => 'ids', + ] ); + + if ( $query->have_posts() ) { + foreach ( $query->posts as $post_id ) { + wp_delete_post( $post_id, true ); + } + } + } + } + + /** + * Deletes the plugin's data directory using the WP_Filesystem API. + */ + private static function delete_data_directory() { + global $wp_filesystem; + + // Ensure the filesystem is initialized. + if ( empty( $wp_filesystem ) ) { + require_once ABSPATH . '/wp-admin/includes/file.php'; + WP_Filesystem(); + } + + $upload_dir = wp_upload_dir(); + $data_dir = $upload_dir['basedir'] . '/fossasia-data'; + + if ( $wp_filesystem->is_dir( $data_dir ) ) { + $wp_filesystem->rmdir( $data_dir, true ); + } + } +} \ No newline at end of file diff --git a/includes/class-wpfa-cli.php b/includes/class-wpfa-cli.php new file mode 100644 index 0000000..5666df4 --- /dev/null +++ b/includes/class-wpfa-cli.php @@ -0,0 +1,236 @@ +] + * : Path to a JSON file with seed data. + * + * ## EXAMPLES + * wp wpfa seed --minimal + * wp wpfa seed --from-json=wp-content/plugins/wpfa-event/assets/demo/minimal.json + * + * @when after_wp_load + * + * @param array $args + * @param array $assoc_args + */ + public static function seed( $args, $assoc_args ) { + if ( isset( $assoc_args['from-json'] ) ) { + self::seed_from_json( $assoc_args['from-json'] ); + return; + } + + if ( isset( $assoc_args['minimal'] ) ) { + self::seed_minimal(); + return; + } + + WP_CLI::error( 'No option provided. Use --minimal or --from-json=.' ); + } + + /** + * Minimal hardcoded seed (2 speakers, 1 event). + */ + private static function seed_minimal() { + $placeholder = 'https://via.placeholder.com/150'; + + $speakers = [ + [ + 'post_title' => 'Alex Example', + 'post_content' => 'Open source contributor and community speaker.', + 'meta' => [ + 'wpfa_speaker_org' => 'FOSSASIA', + 'wpfa_speaker_position' => 'Developer Advocate', + 'wpfa_speaker_photo_url' => $placeholder, + ], + 'slug' => 'alex-example', + ], + [ + 'post_title' => 'Bao Nguyen', + 'post_content' => 'Engineer focusing on event platforms and accessibility.', + 'meta' => [ + 'wpfa_speaker_org' => 'Eventyay', + 'wpfa_speaker_position' => 'Software Engineer', + 'wpfa_speaker_photo_url' => $placeholder, + ], + 'slug' => 'bao-nguyen', + ], + ]; + + $event = [ + 'post_title' => 'FOSSASIA Community Meetup', + 'post_content' => 'A casual meetup to discuss the roadmap and OSS collaboration.', + 'meta' => [ + 'wpfa_event_start_date' => date( 'Y-m-d', strtotime( '+30 days' ) ), + 'wpfa_event_end_date' => date( 'Y-m-d', strtotime( '+31 days' ) ), + 'wpfa_event_location' => 'Online', + 'wpfa_event_url' => 'https://eventyay.com/', + ], + 'slug' => 'fossasia-community-meetup', + ]; + + // Insert speakers (idempotent by slug). + $speaker_ids = []; + foreach ( $speakers as $s ) { + $speaker_ids[] = self::upsert_post_by_slug( + 'wpfa_speaker', + sanitize_title( $s['slug'] ), + [ + 'post_title' => $s['post_title'], + 'post_content' => $s['post_content'], + 'post_status' => 'publish', + 'post_type' => 'wpfa_speaker', // Ensure this matches your CPT slug + ], + $s['meta'] + ); + } + + // Insert event (idempotent by slug). + $event_id = self::upsert_post_by_slug( + 'wpfa_event', + sanitize_title( $event['slug'] ), + [ + 'post_title' => $event['post_title'], + 'post_content' => $event['post_content'], + 'post_status' => 'publish', + 'post_type' => 'wpfa_event', // Ensure this matches your CPT slug + ], + $event['meta'] + ); + + // Relate event ↔ speakers (store both sides; skip if already there). + self::sync_relationships( $event_id, $speaker_ids ); + + WP_CLI::success( 'Seeded minimal data: 2 speakers, 1 event.' ); + } + + /** + * Seed from JSON file. + * + * @param string $path + */ + private static function seed_from_json( $path ) { + if ( ! file_exists( $path ) ) { + WP_CLI::error( "JSON file not found: {$path}" ); + } + $json = file_get_contents( $path ); + if ( false === $json ) { + WP_CLI::error( "Unable to read JSON file: {$path}" ); + } + + $data = json_decode( $json, true ); + if ( ! is_array( $data ) ) { + WP_CLI::error( 'Invalid JSON structure.' ); + } + + // Insert speakers first. + $slug_to_id = []; + if ( ! empty( $data['speakers'] ) && is_array( $data['speakers'] ) ) { + foreach ( $data['speakers'] as $s ) { + $slug = sanitize_title( $s['slug'] ?? $s['title'] ?? wp_generate_uuid4() ); + $title = sanitize_text_field( $s['title'] ?? 'Speaker' ); + $content = wp_kses_post( $s['content'] ?? '' ); + + $meta = [ + 'wpfa_speaker_org' => isset( $s['org'] ) ? sanitize_text_field( $s['org'] ) : '', + 'wpfa_speaker_position' => isset( $s['position'] ) ? sanitize_text_field( $s['position'] ) : '', + 'wpfa_speaker_photo_url' => isset( $s['photo'] ) ? esc_url_raw( $s['photo'] ) : 'https://via.placeholder.com/150', + ]; + + $id = self::upsert_post_by_slug( 'wpfa_speaker', $slug, [ 'post_title' => $title, 'post_content' => $content, 'post_status' => 'publish', 'post_type' => 'wpfa_speaker', ], $meta ); + $slug_to_id[ $slug ] = $id; + } + } + + // Insert events and relate to speakers from slugs. + if ( ! empty( $data['events'] ) && is_array( $data['events'] ) ) { + foreach ( $data['events'] as $e ) { + $slug = sanitize_title( $e['slug'] ?? $e['title'] ?? wp_generate_uuid4() ); + $title = sanitize_text_field( $e['title'] ?? 'Event' ); + $content = wp_kses_post( $e['content'] ?? '' ); + + $meta = [ + 'wpfa_event_start_date' => isset( $e['start_date'] ) ? sanitize_text_field( $e['start_date'] ) : '', + 'wpfa_event_end_date' => isset( $e['end_date'] ) ? sanitize_text_field( $e['end_date'] ) : '', + 'wpfa_event_location' => isset( $e['location'] ) ? sanitize_text_field( $e['location'] ) : '', + 'wpfa_event_url' => isset( $e['url'] ) ? esc_url_raw( $e['url'] ) : '', + ]; + + $event_id = self::upsert_post_by_slug( 'wpfa_event', $slug, [ 'post_title' => $title, 'post_content' => $content, 'post_status' => 'publish', 'post_type' => 'wpfa_event', ], $meta ); + + $event_speaker_slugs = ! empty( $e['speakers'] ) && is_array( $e['speakers'] ) ? $e['speakers'] : []; + $speaker_ids = array_values( array_intersect_key( $slug_to_id, array_flip( $event_speaker_slugs ) ) ); + + self::sync_relationships( $event_id, $speaker_ids ); + } + } + + WP_CLI::success( 'Seeded data from JSON.' ); + } + + /** + * Upsert by slug: create the post if not found; otherwise update meta. + */ + private static function upsert_post_by_slug( $post_type, $slug, $postarr, $meta ) { + $existing = get_page_by_path( $slug, OBJECT, $post_type ); + if ( $existing ) { + $post_id = $existing->ID; + $postarr['ID'] = $post_id; + wp_update_post( $postarr ); + } else { + $postarr['post_name'] = $slug; + $post_id = wp_insert_post( $postarr ); + } + + if ( is_wp_error( $post_id ) || ! $post_id ) { + WP_CLI::warning( "Failed to upsert {$post_type} : {$slug}" ); + return 0; + } + + update_post_meta( $post_id, '_wpfa_seeded', 1 ); + if ( is_array( $meta ) ) { + foreach ( $meta as $k => $v ) { + update_post_meta( $post_id, $k, $v ); + } + } + return $post_id; + } + + /** + * Sync event <-> speakers relationships. + */ + private static function sync_relationships( $event_id, $speaker_ids ) { + $event_id = absint( $event_id ); + $speaker_ids = array_values( array_unique( array_map( 'absint', $speaker_ids ) ) ); + + if ( ! $event_id || empty( $speaker_ids ) ) { + return; + } + + $current = (array) get_post_meta( $event_id, 'wpfa_event_speakers', true ); + $new = array_values( array_unique( array_merge( $current, $speaker_ids ) ) ); + update_post_meta( $event_id, 'wpfa_event_speakers', $new ); + + foreach ( $speaker_ids as $sid ) { + $cur = (array) get_post_meta( $sid, 'wpfa_speaker_events', true ); + if ( ! in_array( $event_id, $cur, true ) ) { + $cur[] = $event_id; + } + update_post_meta( $sid, 'wpfa_speaker_events', array_values( array_unique( $cur ) ) ); + } + } +} \ No newline at end of file diff --git a/includes/class-wpfa-cpt.php b/includes/class-wpfa-cpt.php new file mode 100644 index 0000000..cbe1170 --- /dev/null +++ b/includes/class-wpfa-cpt.php @@ -0,0 +1,59 @@ + array( + 'name' => __( 'Events', 'wpfa-event' ), + 'singular_name' => __( 'Event', 'wpfa-event' ), + ), + 'public' => true, + 'has_archive' => true, + 'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ), + 'show_in_rest' => true, + ) + ); + + // Speakers CPT. + register_post_type( + 'wpfa_speaker', + array( + 'labels' => array( + 'name' => __( 'Speakers', 'wpfa-event' ), + 'singular_name' => __( 'Speaker', 'wpfa-event' ), + ), + 'public' => true, + 'has_archive' => true, + 'supports' => array( 'title', 'editor', 'thumbnail' ), + 'show_in_rest' => true, + ) + ); + } + + /** + * Register meta fields for CPTs. + */ + public function register_meta() { + register_post_meta( 'wpfa_speaker', 'wpfa_speaker_org', array( 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => 'sanitize_text_field' ) ); + register_post_meta( 'wpfa_speaker', 'wpfa_speaker_position', array( 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => 'sanitize_text_field' ) ); + register_post_meta( 'wpfa_speaker', 'wpfa_speaker_photo_url', array( 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => 'esc_url_raw' ) ); + + register_post_meta( 'wpfa_event', 'wpfa_event_start_date', array( 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => 'sanitize_text_field' ) ); + register_post_meta( 'wpfa_event', 'wpfa_event_end_date', array( 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => 'sanitize_text_field' ) ); + register_post_meta( 'wpfa_event', 'wpfa_event_location', array( 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => 'sanitize_text_field' ) ); + register_post_meta( 'wpfa_event', 'wpfa_event_url', array( 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => 'esc_url_raw' ) ); + } +} \ No newline at end of file diff --git a/public/class-wpfa-public.php b/public/class-wpfa-public.php new file mode 100644 index 0000000..cff307b --- /dev/null +++ b/public/class-wpfa-public.php @@ -0,0 +1,96 @@ +plugin_name = $plugin_name; + $this->version = $version; + } + + /** + * Register the stylesheets for the public-facing side of the site. + */ + public function enqueue_styles() { + wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/wpfa-public.css', array(), $this->version, 'all' ); + } + + /** + * Register the shortcodes for the plugin. + */ + public function register_shortcodes() { + add_shortcode( 'wpfa_speakers', array( $this, 'render_speakers_shortcode' ) ); + } + + /** + * Renders the [wpfa_speakers] shortcode. + * + * @param array $atts Shortcode attributes. + * @return string HTML output. + */ + public function render_speakers_shortcode( $atts ) { + $atts = shortcode_atts( + array( + 'limit' => -1, + ), + $atts, + 'wpfa_speakers' + ); + + $query = new WP_Query( + array( + 'post_type' => 'wpfa_speaker', + 'posts_per_page' => intval( $atts['limit'] ), + 'post_status' => 'publish', + ) + ); + + if ( ! $query->have_posts() ) { + return '

' . esc_html__( 'No speakers found.', 'wpfa-event' ) . '

'; + } + + ob_start(); + echo '
'; + while ( $query->have_posts() ) { + $query->the_post(); + $org = get_post_meta( get_the_ID(), 'wpfa_speaker_org', true ); + $position = get_post_meta( get_the_ID(), 'wpfa_speaker_position', true ); + $photo_url = get_the_post_thumbnail_url( get_the_ID(), 'medium' ) ?: 'https://via.placeholder.com/150'; + + echo '
'; + echo '' . esc_attr( get_the_title() ) . ''; + echo '

' . esc_html( get_the_title() ) . '

'; + echo '

' . esc_html( $position ) . ', ' . esc_html( $org ) . '

'; + echo '
'; + } + echo '
'; + wp_reset_postdata(); + return ob_get_clean(); + } + +} \ No newline at end of file diff --git a/public/uninstall.php b/public/uninstall.php new file mode 100644 index 0000000..e69de29 diff --git a/public/wpfa-public.css b/public/wpfa-public.css new file mode 100644 index 0000000..2f13c23 --- /dev/null +++ b/public/wpfa-public.css @@ -0,0 +1,23 @@ +/** + * All of the CSS for your public-facing functionality should be + * included in this file. + */ + +.wpfa-speakers-archive { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 20px; +} + +.wpfa-speaker-card { + border: 1px solid #ddd; + border-radius: 8px; + padding: 15px; + text-align: center; +} + +.wpfa-speaker-card img { + max-width: 150px; + border-radius: 50%; + margin: 0 auto 10px; +} \ No newline at end of file diff --git a/templates/admin-dashboard.php b/templates/admin-dashboard.php new file mode 100644 index 0000000..5b30bd0 --- /dev/null +++ b/templates/admin-dashboard.php @@ -0,0 +1,2266 @@ + 403 ] ); +} + +$event_id = isset($_GET['event_id']) ? absint($_GET['event_id']) : 0; +$event_title = $event_id ? get_the_title($event_id) : ''; + +$upload_dir = wp_upload_dir(); +$data_dir = $upload_dir['basedir'] . '/fossasia-data'; + +// Define file paths based on event ID +$sponsors_file = $event_id ? $data_dir . '/sponsors-' . $event_id . '.json' : ''; +$settings_file = $event_id ? $data_dir . '/site-settings-' . $event_id . '.json' : ''; +$speakers_file = $event_id ? $data_dir . '/speakers-' . $event_id . '.json' : ''; +$schedule_file = $event_id ? $data_dir . '/schedule-' . $event_id . '.json' : ''; +$theme_settings_file = $event_id ? $data_dir . '/theme-settings-' . $event_id . '.json' : ''; + +// Global files that are not event-specific +$global_settings_file = $data_dir . '/site-settings.json'; +if ($event_id) { + $sections_file = $data_dir . '/custom-sections-' . $event_id . '.json'; +} else { + $sections_file = $data_dir . '/custom-sections.json'; // This remains the "global" file for the Events Listing page. +} +$navigation_file = $data_dir . '/navigation.json'; // Assuming navigation is global +$coc_content_file = $data_dir . '/coc-content.json'; + +// Ensure files exist +if ($event_id) { + if (!file_exists($sponsors_file)) { file_put_contents($sponsors_file, '[]'); } + if (!file_exists($settings_file)) { file_put_contents($settings_file, '{"about_section_content": "", "section_visibility": {"about": true, "speakers": true, "schedule": true, "sponsors": true}}'); } + if (!file_exists($speakers_file)) { file_put_contents($speakers_file, '[]'); } + if (!file_exists($schedule_file)) { file_put_contents($schedule_file, '{}'); } + if (!file_exists($theme_settings_file)) { file_put_contents($theme_settings_file, '{"brand_color": "#D51007", "background_color": "#f8f9fa", "text_color": "#0b0b0b"}'); } +} +if (!file_exists($sections_file)) { file_put_contents($sections_file, '[]'); } // This now correctly handles both global and event-specific files. +if (!file_exists($navigation_file)) { file_put_contents($navigation_file, '[]'); } +if (!file_exists($global_settings_file)) { file_put_contents($global_settings_file, '{"hero_image_url": "", "footer_text": ""}'); } +if (!file_exists($coc_content_file)) { file_put_contents($coc_content_file, '{"content": "

Placeholder CoC content.

"}'); } + +// Speaker data is needed for the new "Manage Speakers" tab +$speakers_data = $event_id && file_exists($speakers_file) ? json_decode(file_get_contents($speakers_file), true) : []; +$sponsors_data = $event_id && file_exists($sponsors_file) ? json_decode(file_get_contents($sponsors_file), true) : []; +$site_settings_data = $event_id && file_exists($settings_file) ? json_decode(file_get_contents($settings_file), true) : []; +$custom_sections_data = json_decode(file_get_contents($sections_file), true); +$schedule_data = $event_id && file_exists($schedule_file) ? json_decode(file_get_contents($schedule_file), true) : []; +$navigation_data = json_decode(file_get_contents($navigation_file), true); +$theme_settings_data = $event_id && file_exists($theme_settings_file) ? json_decode(file_get_contents($theme_settings_file), true) : []; +$coc_content_data = json_decode(file_get_contents($coc_content_file), true); + +// Determine the correct "View Site" URL. +$view_site_url = esc_url( home_url( '/fossasia-summit/' ) ); // Default URL. + +if ( isset( $_GET['return_to'] ) ) { + // Priority 1: Use the 'return_to' query parameter if it exists. + $view_site_url = esc_url( urldecode( $_GET['return_to'] ) ); // This is now the event-specific page +} else { + // Priority 2 (Fallback): Use the HTTP referer if it's a valid internal URL. + $referer_url = wp_get_referer(); + if ( $referer_url && strpos( $referer_url, home_url() ) === 0 && strpos( $referer_url, 'admin-dashboard' ) === false ) { + $view_site_url = esc_url( $referer_url ); + } +} +?> + +> + + + + + + +> + + +
+
+
+

Admin Dashboard

+ +

Editing: Global Site Content (Events Page)

+ +

Editing:

+ +
+ +
+ + +
+

No Event Selected

+

Please go to the Events page and click "Edit Content" on an event card to manage its specific content.

+

You can still manage global site settings below.

+
+ + +
+
+ +
Data Sync
+
Manage Speakers
+
Manage Schedule
+
About Section
+
Section Visibility
+
Manage Sponsors
+
Site Settings
+
Theme
+ + +
Custom Sections
+
Manage Navigation
+ +
Code of Conduct
+
+ + + +
+
+

Sync with Eventyay

+

Paste the full Eventyay API URL for sessions below to sync speakers. You can get this from the Eventyay API by inspecting network requests or from the event's API documentation.

+ + +
+ + +
+

+
+
+

Import Sample Data

+

Click this button to populate the current event with sample speakers, sponsors, and settings. This will overwrite existing data for this event.

+ +

+
+ +
+

Manage Speakers

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

Manage Schedule Table

+

Create or edit the single schedule table for the "Full Schedule" page. Only one table can exist at a time.

+
+ +
+
+
+ +
+

Edit "About" Section Content

+

Use the editor below to change the content of the "About" section on the main summit page. You can use formatting like bold, italics, lists, and links.

+
+ 'about_section_content', + 'media_buttons' => false, + 'textarea_rows' => 20, // Fallback for when JS is disabled + 'tinymce' => [ + 'height' => 450, // Set the editor height in pixels + ], + ] ); ?> + +
+
+ +
+

Manage Section Visibility

+

Use these toggles to show or hide the default sections on the event page.

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

Hero Section

+ +

Current Image:

+ Current Hero Image +
+ + + +
+ +

Call for Speakers Button

+ + + + +
+ +

Footer

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

Theme Settings

+

Customize the color palette for this specific event. These settings will override the global defaults.

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

Edit Code of Conduct

+

Use the editor below to change the content of the Code of Conduct page. This content is global and applies to all events.

+
+ 'coc_content', + 'media_buttons' => false, + 'textarea_rows' => 20, + 'tinymce' => [ + 'height' => 450, + ], + ] ); ?> + +
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/templates/c_Users_Nishil_Downloads_test38(1)_templates_events-listing-page.php b/templates/c_Users_Nishil_Downloads_test38(1)_templates_events-listing-page.php new file mode 100644 index 0000000..5c97138 --- /dev/null +++ b/templates/c_Users_Nishil_Downloads_test38(1)_templates_events-listing-page.php @@ -0,0 +1,1356 @@ + ($b['order'] ?? 10); + }); + + foreach ($sections_to_render as $section) { + $section_id = esc_attr($section['id']); + $layout = $section['layout'] ?? 'full_width'; + $section_type = $section['type'] ?? 'content'; + ?> +
+
+
+ +

+ + +

+ +
+ ' . esc_attr($section['title']) . ''; + } elseif ($section['mediaType'] === 'video' && !empty($section['video_embed_src'])) { + $embed_url = get_video_embed_url_for_events($section['video_embed_src']); + echo '
'; + } elseif ($section['mediaType'] === 'carousel' && !empty($section['carousel_images']) && is_array($section['carousel_images'])) { + $carousel_id = 'carousel-' . esc_attr($section['id']); + $timer = !empty($section['carousel_timer']) ? absint($section['carousel_timer']) * 1000 : 5000; + ?> + + + +
+

+
+ +
+ +
+
' . esc_attr($section['title']) . ' media
'; + } elseif ($section['mediaType'] === 'map' && !empty($section['map_embed_src'])) { + $media_col_html = '
'; + } + + $content_col_html = ''; + ?> +
+ +
+ + +
+ 'page', + 'posts_per_page' => -1, + 'meta_key' => '_event_date', + 'orderby' => 'meta_value', + 'order' => 'ASC', + 'meta_query' => [ + 'relation' => 'AND', + [ + 'key' => '_wp_page_template', + 'value' => 'fossasia-landing-template.php' + ], + [ + // This logic finds events where the end date is today or in the future, + // OR if there is no end date, where the start date is today or in the future. + 'relation' => 'OR', + [ + 'relation' => 'AND', + [ + 'key' => '_event_end_date', + 'compare' => 'EXISTS' + ], + [ + 'key' => '_event_end_date', + 'value' => $today, + 'compare' => '>=', + 'type' => 'DATE' + ] + ], + [ + 'key' => '_event_date', + 'value' => $today, + 'compare' => '>=', + 'type' => 'DATE' + ] + ] + ] +]); +$calendar_events = []; +if ($all_events_query->have_posts()) { + while ($all_events_query->have_posts()) { + $all_events_query->the_post(); + $event_date = get_post_meta(get_the_ID(), '_event_date', true); + $event_end_date = get_post_meta(get_the_ID(), '_event_end_date', true); + $event_place = get_post_meta(get_the_ID(), '_event_place', true); + $event_time = get_post_meta(get_the_ID(), '_event_time', true); + $event_description = get_the_excerpt(); + $featured_img_url = get_the_post_thumbnail_url(get_the_ID(), 'large') ?: ''; + + $calendar_events[] = [ + 'id' => get_the_ID(), + 'name' => get_the_title(), + 'date' => $event_date, + 'endDate' => $event_end_date, + 'place' => $event_place, + 'time' => $event_time, + 'description' => $event_description, + 'permalink' => get_the_permalink(), + 'image_url' => $featured_img_url, + 'year' => !empty($event_date) ? date('Y', strtotime($event_date)) : null + ]; + } +} + +/** + * Fetches and renders latest blog posts from FOSSASIA blog. + */ +function render_latest_news() { + include_once( ABSPATH . WPINC . '/feed.php' ); + + // Get a SimplePie feed object from the specified feed source. + $rss = fetch_feed( 'https://blog.fossasia.org/rss/' ); + + if ( is_wp_error( $rss ) ) { + echo '

Could not fetch news. Please try again later.

'; + return; + } + + // Figure out how many total items there are, but limit it to 5. + $maxitems = $rss->get_item_quantity( 5 ); + + // Build an array of all the items, starting with element 0 (first element). + $rss_items = $rss->get_items( 0, $maxitems ); + + if ( $maxitems == 0 ) { + echo '

No news items found.

'; + } else { + // Loop through each feed item and display each item as a hyperlink. + echo '