diff --git a/.github/workflows/phpcs.yml b/.github/workflows/phpcs.yml deleted file mode 100644 index 09e8fe6..0000000 --- a/.github/workflows/phpcs.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: PHPCS -on: - pull_request: - push: - branches: [ main, master ] -jobs: - phpcs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.2' - tools: phpcs - - name: Install WPCS - run: | - git clone https://github.com/WordPress/WordPress-Coding-Standards.git wpcs - phpcs --config-set installed_paths wpcs - - name: Run PHPCS - run: phpcs - diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 08f1530..0000000 --- a/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -# Node/Composer caches (in case added later) -node_modules/ -vendor/ -composer.lock -package-lock.json -# OS/editor junk -.DS_Store -Thumbs.db -.idea/ -.vscode/ - diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/README.md b/README.md index 6781975..7f590dc 100644 --- a/README.md +++ b/README.md @@ -1,185 +1,41 @@ -# WordPress FOSSASIA Event Plugin (WPFAevent) +# FOSSASIA Event Plugin -The **FOSSASIA Event Plugin** provides WordPress integrations for [Eventyay](https://eventyay.com)-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](https://fossasia.org) and is compatible with the **eventyay** platform. +A WordPress plugin for managing and displaying event and speaker data for FOSSASIA using Custom Post Types. ## Features +- **Custom Post Types:** `Events` and `Speakers` for structured, easy-to-manage data. +- **Frontend Rendering:** A simple `[wpfa_speakers]` shortcode to display a styled grid of speakers on any page. +- **Admin Interface:** Cleanly integrated into the WordPress dashboard, with settings and import/export tools. +- **Developer Tooling:** WP-CLI commands to quickly seed sample data for testing. -- Display **speakers**, **sessions**, and **event schedules** from Eventyay or other compatible APIs. -- Works with the unified **Eventyay (Django + Vue 3)** architecture. -- Includes an **admin settings page** to configure JSON API endpoints and cache duration. -- Supports **shortcodes** for embedding event data anywhere on your site. -- Built with modern WordPress development practices: - - Class-based structure - - Hooks and actions - - Internationalization (translation-ready) -- Includes **placeholder data** for local development and testing. -- Easily extendable with custom templates, endpoints, or additional shortcodes. - ---- - -## Requirements - -- WordPress **5.8** or higher -- PHP **7.4** or newer -- HTTPS-enabled server (for API calls) -- The WordPress REST API and `wp_remote_get()` must be available - -## Installation - -1. Download or clone this plugin into your WordPress `wp-content/plugins/` directory: - ```bash - git clone https://github.com/fossasia/WPFAevent.git event-plugin - - -2. Activate **Event Plugin** in your WordPress Admin under - `Plugins → Installed Plugins → Event Plugin → Activate`. - -3. Configure your API endpoints: - - * Go to **Settings → Event Plugin** in the WordPress Admin. - * Enter the URLs of your Eventyay API endpoints for **Speakers**, **Sessions**, and **Schedule**. - * Optionally adjust the **cache time (TTL)** in seconds. - -4. Add shortcodes to your pages or posts, for example: - - ```text - [event_speakers] - [event_sessions] - [event_schedule] - ``` - - These will automatically display data fetched from your configured endpoints. - If no API data is available, placeholder content will appear instead. - -## Directory Structure - -``` -event-plugin/ -│ -├─ event-plugin.php → main plugin file (entry point) -│ -├─ includes/ -│ ├─ class-event-loader.php → initializes hooks and shortcodes -│ ├─ class-event-api.php → handles remote API fetching with caching -│ ├─ class-event-admin.php → admin settings page (API config, cache) -│ ├─ class-event-speakers.php → logic for speakers shortcode -│ ├─ class-event-sessions.php → logic for sessions shortcode -│ └─ class-event-schedule.php → logic for schedule shortcode -│ -├─ public/ -│ ├─ partials/ -│ │ ├─ event-speakers.php → speaker display template -│ │ ├─ event-sessions.php → sessions display template -│ │ └─ event-schedule.php → schedule display template -│ ├─ css/ -│ │ └─ event-public.css → public-facing styles -│ └─ js/ -│ ├─ event-public.js → public-facing scripts -│ └─ event-admin.js → admin JS for “Test Connection” buttons -│ -├─ assets/ -│ └─ img/ -│ └─ speaker-placeholder.jpg → placeholder image (no real data) -│ -├─ languages/ -│ └─ event-plugin.pot → base translation template -│ -└─ README.md -``` - - -## Shortcodes Overview +## Getting Started -| Shortcode | Description | Output Source | -| ------------------ | ------------------------------------------------------- | --------------------------- | -| `[event_speakers]` | Displays the list of speakers. | API endpoint or placeholder | -| `[event_sessions]` | Displays event sessions with title, time, and abstract. | API endpoint or placeholder | -| `[event_schedule]` | Displays daily schedule in a table format. | API endpoint or placeholder | +The plugin uses WordPress Custom Post Types (CPTs) to store data, which is the standard and most robust way to handle structured content. -Each shortcode can accept optional attributes — for example: +1. **Install and Activate** the plugin. +2. You will see a new **"FOSSASIA Events"** menu item in your WordPress dashboard. -```text -[event_schedule profile="summit2026"] -``` - -if multiple event profiles are configured in settings. - - -## Settings Page - -Navigate to **Settings → Event Plugin** to configure: +### Managing Content -* **Speakers Endpoint:** `https://example.org/api/v1/events/{id}/speakers` -* **Sessions Endpoint:** `https://example.org/api/v1/events/{id}/sessions` -* **Schedule Endpoint:** `https://example.org/api/v1/events/{id}/schedule` -* **Cache TTL (seconds):** Duration for transient caching of API results -* **Test Buttons:** Verify that endpoints respond with valid JSON data +* **To Add an Event:** Go to **FOSSASIA Events -> Add New**. Fill in the title, description, and event-specific details like date and venue. +* **To Add a Speaker:** Go to **FOSSASIA Events -> Speakers -> Add New**. Fill in the speaker's name, bio, and their associated role and organization. -If the fields are left empty, the plugin falls back to placeholder content for development. +### Displaying Speakers -## Development Notes +To display the list of speakers on the front end of your site: -* Core logic resides in `includes/`, presentation templates in `public/partials/`. -* All user-facing text should use translation functions `__()` or `_e()`. -* Load assets using `wp_enqueue_script()` and `wp_enqueue_style()`. -* Use the built-in caching layer via transients in `class-event-api.php`. -* Do **not** commit large demo data or real images — use placeholders only. -* To modify the layout, you can override templates in your theme directory: +1. Create a new Page or Post in WordPress. +2. Add a "Shortcode" block. +3. Enter the following shortcode: `[wpfa_speakers]` +4. Publish the page. The speaker grid will be rendered automatically. - ``` - your-theme/event-plugin/partials/event-speakers.php - ``` +## For Developers: Seeding Data with WP-CLI - WordPress will automatically use the theme’s version if it exists. +The plugin includes a WP-CLI command for developers to quickly seed test data. ---- - -## Local Development - -1. Install WordPress locally (e.g., using LocalWP, Docker, or WP-CLI). -2. Place this plugin in `wp-content/plugins/`. -3. Activate it and navigate to **Settings → Event Plugin**. -4. Test with public Eventyay JSON endpoints or your own mock data. - -To debug API calls, enable WordPress debug logging in `wp-config.php`: - -```php -define( 'WP_DEBUG', true ); -define( 'WP_DEBUG_LOG', true ); +This command creates 2 placeholder speakers and 1 event. +```bash +wp wpfa seed --minimal ``` -Logs can be found in `/wp-content/debug.log`. - -## Translation - -* The plugin is fully internationalization-ready (`Text Domain: event-plugin`). -* Translations are located in the `languages/` directory. -* You can generate `.mo` and `.po` files using tools such as **Poedit** or **Loco Translate**. - -## Contributing - -Contributions are welcome! - -* Fork the repository on GitHub -* Create a feature branch: - - ```bash - git checkout -b feature/my-feature - ``` -* Commit and push your changes, then submit a **Pull Request** -* Follow **WordPress PHP coding standards** - -Before submitting: - -* Run `phpcs` with the WordPress standard -* Avoid committing binary or large files -* Test locally with caching disabled -* Ensure translations are wrapped correctly in `__()` or `_e()` - -## License - -Licensed under the **Apache License, Version 2.0** -Copyright © 2025 [FOSSASIA](https://fossasia.org) +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. \ No newline at end of file diff --git a/Readme.txt b/Readme.txt deleted file mode 100644 index ecb51f8..0000000 --- a/Readme.txt +++ /dev/null @@ -1,19 +0,0 @@ -=== WPFA Event === -Contributors: fossasia -Tags: events -Requires at least: 5.8 -Tested up to: 6.6 -Requires PHP: 7.4 -Stable tag: 0.1.0 -License: Apache2 -License URI: https://www.apache.org/licenses/LICENSE-2.0.txt - -Skeleton plugin for the FOSSASIA Event project. - -== Description == -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. - -== Changelog == -= 0.1.0 = -* Initial skeleton. - diff --git a/admin/class-wpfaevent-admin.php b/admin/class-wpfaevent-admin.php deleted file mode 100644 index 727158b..0000000 --- a/admin/class-wpfaevent-admin.php +++ /dev/null @@ -1,103 +0,0 @@ - - */ -class Wpfaevent_Admin { - - /** - * The ID of this plugin. - * - * @since 1.0.0 - * @access private - * @var string $plugin_name The ID of this plugin. - */ - private $plugin_name; - - /** - * The version of this plugin. - * - * @since 1.0.0 - * @access private - * @var string $version The current version of this plugin. - */ - private $version; - - /** - * Initialize the class and set its properties. - * - * @since 1.0.0 - * @param string $plugin_name The name of this plugin. - * @param string $version The version of this plugin. - */ - public function __construct( $plugin_name, $version ) { - - $this->plugin_name = $plugin_name; - $this->version = $version; - - } - - /** - * Register the stylesheets for the admin area. - * - * @since 1.0.0 - */ - public function enqueue_styles() { - - /** - * This function is provided for demonstration purposes only. - * - * An instance of this class should be passed to the run() function - * defined in Wpfaevent_Loader as all of the hooks are defined - * in that particular class. - * - * The Wpfaevent_Loader will then create the relationship - * between the defined hooks and the functions defined in this - * class. - */ - - wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/wpfaevent-admin.css', array(), $this->version, 'all' ); - - } - - /** - * Register the JavaScript for the admin area. - * - * @since 1.0.0 - */ - public function enqueue_scripts() { - - /** - * This function is provided for demonstration purposes only. - * - * An instance of this class should be passed to the run() function - * defined in Wpfaevent_Loader as all of the hooks are defined - * in that particular class. - * - * The Wpfaevent_Loader will then create the relationship - * between the defined hooks and the functions defined in this - * class. - */ - - wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/wpfaevent-admin.js', array( 'jquery' ), $this->version, false ); - - } - -} diff --git a/admin/css/wpfaevent-admin.css b/admin/css/wpfaevent-admin.css deleted file mode 100644 index 00c8c7f..0000000 --- a/admin/css/wpfaevent-admin.css +++ /dev/null @@ -1,4 +0,0 @@ -/** - * All of the CSS for your admin-specific functionality should be - * included in this file. - */ \ No newline at end of file diff --git a/admin/index.php b/admin/index.php deleted file mode 100644 index 080fcca..0000000 --- a/admin/index.php +++ /dev/null @@ -1 +0,0 @@ - - - diff --git a/assets/images/logo.png b/assets/images/logo.png new file mode 100644 index 0000000..b734021 Binary files /dev/null and b/assets/images/logo.png differ diff --git a/class-wpfa-import-export.php b/class-wpfa-import-export.php new file mode 100644 index 0000000..d801260 --- /dev/null +++ b/class-wpfa-import-export.php @@ -0,0 +1,65 @@ + +
+

+ +
+
+ +
+
+
+

+
+

+
+ + + +
+
+
+
+

+
+

+
+ + + + +
+
+
+
+
+
+
+
+
+ includes(); + $this->init_hooks(); + } + + /** + * Include required core files. + */ + public function includes() { + require_once WPFA_PLUGIN_PATH . 'includes/class-wpfa-loader.php'; + } + + /** + * Hook into actions and filters. + */ + private function init_hooks() { + add_action( 'plugins_loaded', [ WPFA_Loader::class, 'init' ] ); + add_action( 'init', [ $this, 'load_textdomain' ] ); + + register_activation_hook( __FILE__, [ $this, 'activate' ] ); + register_deactivation_hook( __FILE__, [ $this, 'deactivate' ] ); + } + + /** + * Load Localization files. + */ + public function load_textdomain() { + load_plugin_textdomain( 'wpfa-event', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); + } + + /** + * Plugin activation. + */ + public function activate() { + // Trigger CPT registration to flush rewrite rules. + require_once WPFA_PLUGIN_PATH . 'includes/class-wpfa-cpt.php'; + $cpt = new WPFA_CPT(); + $cpt->register_post_types(); + $cpt->register_meta_fields(); + flush_rewrite_rules(); + } + + /** + * Plugin deactivation. + */ + public function deactivate() { + flush_rewrite_rules(); + } +} + +/** + * Begins execution of the plugin. + */ +function wpfa_event_plugin() { + return FOSSASIA_Event_Plugin::instance(); +} +wpfa_event_plugin(); diff --git a/includes/class-fossasia-uninstaller.php b/includes/class-fossasia-uninstaller.php new file mode 100644 index 0000000..2c41d06 --- /dev/null +++ b/includes/class-fossasia-uninstaller.php @@ -0,0 +1,88 @@ +ID, true ); // true to force delete and bypass trash. + } + } + + // Query for and delete all dynamic event pages identified by the _event_date meta key. + $event_pages_query = new WP_Query( [ + 'post_type' => 'page', + 'posts_per_page' => -1, + 'meta_key' => '_wp_page_template', + 'compare' => 'EXISTS', + 'fields' => 'ids', // Only get post IDs for efficiency. + ] ); + + if ( $event_pages_query->have_posts() ) { + foreach ( $event_pages_query->posts as $page_id ) { + wp_delete_post( $page_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..9417081 --- /dev/null +++ b/includes/class-wpfa-cli.php @@ -0,0 +1,139 @@ + + * wp wpfa export + */ +class WPFA_CLI { + + /** + * Seed command entry point. + * + * ## OPTIONS + * + * [--minimal] + * : Inserts 2 speakers and 1 event using placeholders. + * + * ## EXAMPLES + * wp wpfa seed --minimal + * + * @alias seed + * @subcommand seed + * + * @when after_wp_load + * + * @param array $args + * @param array $assoc_args + */ + public static function seed( $args, $assoc_args ) { + if ( isset( $assoc_args['minimal'] ) ) { + self::seed_minimal(); + return; + } + + WP_CLI::error( 'Please provide an option, e.g., --minimal.' ); + } + + /** + * Minimal hardcoded seed (2 speakers, 1 event). + */ + private static function seed_minimal() { + $placeholder = WPFA_PLUGIN_URL . 'assets/images/placeholder-speaker.png'; + + $speakers = [ + [ + 'post_title' => 'Alex Example', + 'post_content' => 'Open source contributor and community speaker.', + 'meta' => [ + 'wpfa_speaker_org' => 'FOSSASIA', + 'wpfa_speaker_role' => 'Developer Advocate', + 'wpfa_speaker_photo_url' => $placeholder, + ], + 'slug' => 'alex-example', + ], + [ + 'post_title' => 'Bao Nguyen', + 'post_content' => 'Engineer focusing on event platforms.', + 'meta' => [ + 'wpfa_speaker_org' => 'Eventyay', + 'wpfa_speaker_role' => '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_date' => date( 'Y-m-d', strtotime( '+30 days' ) ), + 'wpfa_event_venue' => 'Online', + 'wpfa_event_link' => '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', + $s['slug'], + [ + 'post_title' => $s['post_title'], + 'post_content' => $s['post_content'], + 'post_status' => 'publish', + 'post_type' => 'wpfa_speaker', + ], + $s['meta'] + ); + } + + // Insert event (idempotent by slug). + $event_id = self::upsert_post_by_slug( + 'wpfa_event', + $event['slug'], + [ + 'post_title' => $event['post_title'], + 'post_content' => $event['post_content'], + 'post_status' => 'publish', + 'post_type' => 'wpfa_event', + ], + $event['meta'] + ); + + WP_CLI::success( 'Seeded minimal data: 2 speakers, 1 event.' ); + } + + /** + * 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; + } +} \ 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..18873d9 --- /dev/null +++ b/includes/class-wpfa-cpt.php @@ -0,0 +1,132 @@ + _x( 'Speakers', 'Post Type General Name', 'wpfa-event' ), + 'singular_name' => _x( 'Speaker', 'Post Type Singular Name', 'wpfa-event' ), + 'menu_name' => __( 'Speakers', 'wpfa-event' ), + ]; + $speaker_args = [ + 'label' => __( 'Speaker', 'wpfa-event' ), + 'labels' => $speaker_labels, + 'supports' => [ 'title', 'editor', 'thumbnail' ], + 'hierarchical' => false, + 'public' => true, + 'show_ui' => true, + 'show_in_menu' => 'edit.php?post_type=wpfa_event', + 'menu_position' => 5, + 'show_in_admin_bar' => true, + 'show_in_nav_menus' => true, + 'can_export' => true, + 'has_archive' => true, + 'exclude_from_search' => false, + 'publicly_queryable' => true, + 'capability_type' => 'post', + 'show_in_rest' => true, + 'menu_icon' => 'dashicons-megaphone', + ]; + register_post_type( 'wpfa_speaker', $speaker_args ); + + $event_labels = [ + 'name' => _x( 'Events', 'Post Type General Name', 'wpfa-event' ), + 'singular_name' => _x( 'Event', 'Post Type Singular Name', 'wpfa-event' ), + 'menu_name' => __( 'FOSSASIA Events', 'wpfa-event' ), + ]; + $event_args = [ + 'label' => __( 'Event', 'wpfa-event' ), + 'labels' => $event_labels, + 'supports' => [ 'title', 'editor', 'thumbnail', 'excerpt' ], + 'hierarchical' => false, + 'public' => true, + 'show_ui' => true, + 'show_in_menu' => true, + 'menu_position' => 20, + 'show_in_admin_bar' => true, + 'show_in_nav_menus' => true, + 'can_export' => true, + 'has_archive' => true, + 'exclude_from_search' => false, + 'publicly_queryable' => true, + 'capability_type' => 'page', + 'show_in_rest' => true, + 'menu_icon' => 'dashicons-calendar-alt', + ]; + register_post_type( 'wpfa_event', $event_args ); + } + + /** + * Register meta fields for CPTs. + */ + public function register_meta_fields() { + // Speaker Meta. + register_post_meta( + 'wpfa_speaker', + 'wpfa_speaker_org', + [ + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + 'sanitize_callback' => 'sanitize_text_field', + ] + ); + register_post_meta( + 'wpfa_speaker', + 'wpfa_speaker_role', + [ + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + 'sanitize_callback' => 'sanitize_text_field', + ] + ); + register_post_meta( + 'wpfa_speaker', + 'wpfa_speaker_photo_url', + [ + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + 'sanitize_callback' => 'esc_url_raw', + ] + ); + + // Event Meta. + register_post_meta( 'wpfa_event', 'wpfa_event_date', [ 'show_in_rest' => true, 'single' => true, 'type' => 'string' ] ); + register_post_meta( 'wpfa_event', 'wpfa_event_venue', [ 'show_in_rest' => true, 'single' => true, 'type' => 'string' ] ); + register_post_meta( + 'wpfa_event', + 'wpfa_event_link', + [ + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + 'sanitize_callback' => 'esc_url_raw', + ] + ); + } +} \ No newline at end of file diff --git a/includes/class-wpfa-import-export.php b/includes/class-wpfa-import-export.php new file mode 100644 index 0000000..9ae953c --- /dev/null +++ b/includes/class-wpfa-import-export.php @@ -0,0 +1,28 @@ + +
+

+

Import and Export functionality will be implemented here.

+
+ [ self::class, 'sanitize' ] ] // Sanitize callback + ); + + add_settings_section( + 'wpfa_general_settings_section', // ID + 'General Settings', // Title + null, // Callback + 'wpfa_settings' // Page + ); + + // Add fields here in the future. + } + + /** + * Sanitize each setting field as needed. + * + * @param array $input Contains all settings fields as array keys. + * @return array + */ + public static function sanitize( $input ) { + $new_input = []; + // Example for a future field: + // if ( isset( $input['some_field'] ) ) { + // $new_input['some_field'] = sanitize_text_field( $input['some_field'] ); + // } + return $input; // Return sanitized input. + } +} \ No newline at end of file diff --git a/includes/class-wpfaevent-activator.php b/includes/class-wpfaevent-activator.php deleted file mode 100644 index 3a36f3b..0000000 --- a/includes/class-wpfaevent-activator.php +++ /dev/null @@ -1,36 +0,0 @@ - - */ -class Wpfaevent_Activator { - - /** - * Short Description. (use period) - * - * Long Description. - * - * @since 1.0.0 - */ - public static function activate() { - - } - -} diff --git a/includes/class-wpfaevent-deactivator.php b/includes/class-wpfaevent-deactivator.php deleted file mode 100644 index 5ef477b..0000000 --- a/includes/class-wpfaevent-deactivator.php +++ /dev/null @@ -1,36 +0,0 @@ - - */ -class Wpfaevent_Deactivator { - - /** - * Short Description. (use period) - * - * Long Description. - * - * @since 1.0.0 - */ - public static function deactivate() { - - } - -} diff --git a/includes/class-wpfaevent-i18n.php b/includes/class-wpfaevent-i18n.php deleted file mode 100644 index 3eac493..0000000 --- a/includes/class-wpfaevent-i18n.php +++ /dev/null @@ -1,47 +0,0 @@ - - */ -class Wpfaevent_i18n { - - - /** - * Load the plugin text domain for translation. - * - * @since 1.0.0 - */ - public function load_plugin_textdomain() { - - load_plugin_textdomain( - 'wpfaevent', - false, - dirname( dirname( plugin_basename( __FILE__ ) ) ) . '/languages/' - ); - - } - - - -} diff --git a/includes/class-wpfaevent-loader.php b/includes/class-wpfaevent-loader.php deleted file mode 100644 index abebcbd..0000000 --- a/includes/class-wpfaevent-loader.php +++ /dev/null @@ -1,129 +0,0 @@ - - */ -class Wpfaevent_Loader { - - /** - * The array of actions registered with WordPress. - * - * @since 1.0.0 - * @access protected - * @var array $actions The actions registered with WordPress to fire when the plugin loads. - */ - protected $actions; - - /** - * The array of filters registered with WordPress. - * - * @since 1.0.0 - * @access protected - * @var array $filters The filters registered with WordPress to fire when the plugin loads. - */ - protected $filters; - - /** - * Initialize the collections used to maintain the actions and filters. - * - * @since 1.0.0 - */ - public function __construct() { - - $this->actions = array(); - $this->filters = array(); - - } - - /** - * Add a new action to the collection to be registered with WordPress. - * - * @since 1.0.0 - * @param string $hook The name of the WordPress action that is being registered. - * @param object $component A reference to the instance of the object on which the action is defined. - * @param string $callback The name of the function definition on the $component. - * @param int $priority Optional. The priority at which the function should be fired. Default is 10. - * @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1. - */ - public function add_action( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { - $this->actions = $this->add( $this->actions, $hook, $component, $callback, $priority, $accepted_args ); - } - - /** - * Add a new filter to the collection to be registered with WordPress. - * - * @since 1.0.0 - * @param string $hook The name of the WordPress filter that is being registered. - * @param object $component A reference to the instance of the object on which the filter is defined. - * @param string $callback The name of the function definition on the $component. - * @param int $priority Optional. The priority at which the function should be fired. Default is 10. - * @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1 - */ - public function add_filter( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { - $this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority, $accepted_args ); - } - - /** - * A utility function that is used to register the actions and hooks into a single - * collection. - * - * @since 1.0.0 - * @access private - * @param array $hooks The collection of hooks that is being registered (that is, actions or filters). - * @param string $hook The name of the WordPress filter that is being registered. - * @param object $component A reference to the instance of the object on which the filter is defined. - * @param string $callback The name of the function definition on the $component. - * @param int $priority The priority at which the function should be fired. - * @param int $accepted_args The number of arguments that should be passed to the $callback. - * @return array The collection of actions and filters registered with WordPress. - */ - private function add( $hooks, $hook, $component, $callback, $priority, $accepted_args ) { - - $hooks[] = array( - 'hook' => $hook, - 'component' => $component, - 'callback' => $callback, - 'priority' => $priority, - 'accepted_args' => $accepted_args - ); - - return $hooks; - - } - - /** - * Register the filters and actions with WordPress. - * - * @since 1.0.0 - */ - public function run() { - - foreach ( $this->filters as $hook ) { - add_filter( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); - } - - foreach ( $this->actions as $hook ) { - add_action( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); - } - - } - -} diff --git a/includes/class-wpfaevent.php b/includes/class-wpfaevent.php deleted file mode 100644 index 2caa521..0000000 --- a/includes/class-wpfaevent.php +++ /dev/null @@ -1,218 +0,0 @@ - - */ -class Wpfaevent { - - /** - * The loader that's responsible for maintaining and registering all hooks that power - * the plugin. - * - * @since 1.0.0 - * @access protected - * @var Wpfaevent_Loader $loader Maintains and registers all hooks for the plugin. - */ - protected $loader; - - /** - * The unique identifier of this plugin. - * - * @since 1.0.0 - * @access protected - * @var string $plugin_name The string used to uniquely identify this plugin. - */ - protected $plugin_name; - - /** - * The current version of the plugin. - * - * @since 1.0.0 - * @access protected - * @var string $version The current version of the plugin. - */ - protected $version; - - /** - * Define the core functionality of the plugin. - * - * Set the plugin name and the plugin version that can be used throughout the plugin. - * Load the dependencies, define the locale, and set the hooks for the admin area and - * the public-facing side of the site. - * - * @since 1.0.0 - */ - public function __construct() { - if ( defined( 'WPFAEVENT_VERSION' ) ) { - $this->version = WPFAEVENT_VERSION; - } else { - $this->version = '1.0.0'; - } - $this->plugin_name = 'wpfaevent'; - - $this->load_dependencies(); - $this->set_locale(); - $this->define_admin_hooks(); - $this->define_public_hooks(); - - } - - /** - * Load the required dependencies for this plugin. - * - * Include the following files that make up the plugin: - * - * - Wpfaevent_Loader. Orchestrates the hooks of the plugin. - * - Wpfaevent_i18n. Defines internationalization functionality. - * - Wpfaevent_Admin. Defines all hooks for the admin area. - * - Wpfaevent_Public. Defines all hooks for the public side of the site. - * - * Create an instance of the loader which will be used to register the hooks - * with WordPress. - * - * @since 1.0.0 - * @access private - */ - private function load_dependencies() { - - /** - * The class responsible for orchestrating the actions and filters of the - * core plugin. - */ - require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-wpfaevent-loader.php'; - - /** - * The class responsible for defining internationalization functionality - * of the plugin. - */ - require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-wpfaevent-i18n.php'; - - /** - * The class responsible for defining all actions that occur in the admin area. - */ - require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-wpfaevent-admin.php'; - - /** - * The class responsible for defining all actions that occur in the public-facing - * side of the site. - */ - require_once plugin_dir_path( dirname( __FILE__ ) ) . 'public/class-wpfaevent-public.php'; - - $this->loader = new Wpfaevent_Loader(); - - } - - /** - * Define the locale for this plugin for internationalization. - * - * Uses the Wpfaevent_i18n class in order to set the domain and to register the hook - * with WordPress. - * - * @since 1.0.0 - * @access private - */ - private function set_locale() { - - $plugin_i18n = new Wpfaevent_i18n(); - - $this->loader->add_action( 'plugins_loaded', $plugin_i18n, 'load_plugin_textdomain' ); - - } - - /** - * Register all of the hooks related to the admin area functionality - * of the plugin. - * - * @since 1.0.0 - * @access private - */ - private function define_admin_hooks() { - - $plugin_admin = new Wpfaevent_Admin( $this->get_plugin_name(), $this->get_version() ); - - $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' ); - $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' ); - - } - - /** - * Register all of the hooks related to the public-facing functionality - * of the plugin. - * - * @since 1.0.0 - * @access private - */ - private function define_public_hooks() { - - $plugin_public = new Wpfaevent_Public( $this->get_plugin_name(), $this->get_version() ); - - $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_styles' ); - $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_scripts' ); - - } - - /** - * Run the loader to execute all of the hooks with WordPress. - * - * @since 1.0.0 - */ - public function run() { - $this->loader->run(); - } - - /** - * The name of the plugin used to uniquely identify it within the context of - * WordPress and to define internationalization functionality. - * - * @since 1.0.0 - * @return string The name of the plugin. - */ - public function get_plugin_name() { - return $this->plugin_name; - } - - /** - * The reference to the class that orchestrates the hooks with the plugin. - * - * @since 1.0.0 - * @return Wpfaevent_Loader Orchestrates the hooks of the plugin. - */ - public function get_loader() { - return $this->loader; - } - - /** - * Retrieve the version number of the plugin. - * - * @since 1.0.0 - * @return string The version number of the plugin. - */ - public function get_version() { - return $this->version; - } - -} diff --git a/includes/index.php b/includes/index.php deleted file mode 100644 index e71af0e..0000000 --- a/includes/index.php +++ /dev/null @@ -1 +0,0 @@ - - - WordPress Coding Standards for WPFA Event. - - - - - - - - - - ./ - - vendor/* - node_modules/* - - diff --git a/public/class-wpfa-admin.php b/public/class-wpfa-admin.php new file mode 100644 index 0000000..1fa57b0 --- /dev/null +++ b/public/class-wpfa-admin.php @@ -0,0 +1,99 @@ +id ) { + return; + } + + $screen->add_help_tab( + [ + 'id' => 'wpfa_overview_help_tab', + 'title' => __( 'Overview', 'wpfa-event' ), + 'content' => '

' . __( 'This page contains settings for the FOSSASIA Event Plugin. You can configure default image paths and other options.', 'wpfa-event' ) . '

', + ] + ); + } +} \ 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..38eba77 --- /dev/null +++ b/public/class-wpfa-public.php @@ -0,0 +1,79 @@ + -1, + ], $atts, 'wpfa_speakers' ); + + $query = new WP_Query( [ + 'post_type' => 'wpfa_speaker', + 'posts_per_page' => intval( $atts['limit'] ), + 'no_found_rows' => true, + ] ); + + 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 ); + $role = get_post_meta( get_the_ID(), 'wpfa_speaker_role', true ); + $photo_url = get_post_meta( get_the_ID(), 'wpfa_speaker_photo_url', true ); + ?> +
+ + <?php the_title_attribute(); ?> + +

+

,

+
+ '; + wp_reset_postdata(); + return ob_get_clean(); + } +} \ No newline at end of file diff --git a/public/class-wpfaevent-public.php b/public/class-wpfaevent-public.php deleted file mode 100644 index bac7ee5..0000000 --- a/public/class-wpfaevent-public.php +++ /dev/null @@ -1,103 +0,0 @@ - - */ -class Wpfaevent_Public { - - /** - * The ID of this plugin. - * - * @since 1.0.0 - * @access private - * @var string $plugin_name The ID of this plugin. - */ - private $plugin_name; - - /** - * The version of this plugin. - * - * @since 1.0.0 - * @access private - * @var string $version The current version of this plugin. - */ - private $version; - - /** - * Initialize the class and set its properties. - * - * @since 1.0.0 - * @param string $plugin_name The name of the plugin. - * @param string $version The version of this plugin. - */ - public function __construct( $plugin_name, $version ) { - - $this->plugin_name = $plugin_name; - $this->version = $version; - - } - - /** - * Register the stylesheets for the public-facing side of the site. - * - * @since 1.0.0 - */ - public function enqueue_styles() { - - /** - * This function is provided for demonstration purposes only. - * - * An instance of this class should be passed to the run() function - * defined in Wpfaevent_Loader as all of the hooks are defined - * in that particular class. - * - * The Wpfaevent_Loader will then create the relationship - * between the defined hooks and the functions defined in this - * class. - */ - - wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/wpfaevent-public.css', array(), $this->version, 'all' ); - - } - - /** - * Register the JavaScript for the public-facing side of the site. - * - * @since 1.0.0 - */ - public function enqueue_scripts() { - - /** - * This function is provided for demonstration purposes only. - * - * An instance of this class should be passed to the run() function - * defined in Wpfaevent_Loader as all of the hooks are defined - * in that particular class. - * - * The Wpfaevent_Loader will then create the relationship - * between the defined hooks and the functions defined in this - * class. - */ - - wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/wpfaevent-public.js', array( 'jquery' ), $this->version, false ); - - } - -} diff --git a/public/css/wpfaevent-public.css b/public/css/wpfaevent-public.css deleted file mode 100644 index 65bbf96..0000000 --- a/public/css/wpfaevent-public.css +++ /dev/null @@ -1,4 +0,0 @@ -/** - * All of the CSS for your public-facing functionality should be - * included in this file. - */ \ No newline at end of file diff --git a/public/index.php b/public/index.php deleted file mode 100644 index e71af0e..0000000 --- a/public/index.php +++ /dev/null @@ -1 +0,0 @@ - - - diff --git a/languages/wpfaevent.pot b/public/settings-page.php similarity index 100% rename from languages/wpfaevent.pot rename to public/settings-page.php 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..e8528bd --- /dev/null +++ b/public/wpfa-public.css @@ -0,0 +1,33 @@ +.wpfa-speakers-grid { + 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; + box-shadow: 0 2px 5px rgba(0,0,0,0.05); + background: #fff; +} + +.wpfa-speaker-photo { + width: 120px; + height: 120px; + border-radius: 50%; + object-fit: cover; + margin: 0 auto 15px; +} + +.wpfa-speaker-name { + margin: 0 0 5px; + font-size: 1.2em; +} + +.wpfa-speaker-meta { + margin: 0; + color: #555; + font-size: 0.9em; +} \ 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 '