From 443da2592b0344a76248d1efd364d0d6759408b8 Mon Sep 17 00:00:00 2001 From: Nishil Hoogar Date: Fri, 24 Oct 2025 01:49:46 +0530 Subject: [PATCH 1/2] =?UTF-8?q?PR=205=20=E2=80=94=20Admin=20UI=20and=20Set?= =?UTF-8?q?tings=20Page=20Implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/phpcs.yml | 22 - .gitignore | 11 - LICENSE | 201 -- README.md | 199 +- Readme.txt | 19 - admin/class-wpfa-admin.php | 68 + admin/class-wpfaevent-admin.php | 103 - admin/css/wpfaevent-admin.css | 4 - admin/index.php | 1 - admin/js/wpfaevent-admin.js | 32 - admin/partials/wpfaevent-admin-display.php | 16 - assets/images/logo.png | Bin 0 -> 9238 bytes fossasia-landing.php | 99 + includes/class-fossasia-uninstaller.php | 73 + includes/class-wpfa-cli.php | 311 +++ includes/class-wpfa-cpt.php | 94 + includes/class-wpfa-settings.php | 13 + includes/class-wpfaevent-activator.php | 36 - includes/class-wpfaevent-deactivator.php | 36 - includes/class-wpfaevent-i18n.php | 47 - includes/class-wpfaevent-loader.php | 129 - includes/class-wpfaevent.php | 218 -- includes/index.php | 1 - index.php | 1 - phpcs.xml | 18 - .../class-wpfa-admin.php | 0 public/class-wpfa-public.php | 96 + public/class-wpfa-settings.php | 0 public/class-wpfaevent-public.php | 103 - public/css/wpfaevent-public.css | 4 - public/index.php | 1 - public/js/wpfaevent-public.js | 32 - public/partials/wpfaevent-public-display.php | 16 - public/uninstall.php | 0 public/wpfa-public.css | 23 + templates/admin-dashboard.php | 2266 +++++++++++++++++ ...est38(1)_templates_events-listing-page.php | 1356 ++++++++++ ...s_test38(1)_templates_past-events-page.php | 184 ++ templates/code-of-conduct-page.php | 91 + templates/events-listing-page.php | 1354 ++++++++++ templates/fossasia-landing-content.php | 0 templates/fossasia-landing-template.php | 1421 +++++++++++ templates/minimal.json | 32 + templates/past-events-page.php | 375 +++ templates/schedule-page.php | 196 ++ templates/speakers-page-content.php | 0 templates/speakers-page.php | 1066 ++++++++ uninstall.php | 40 +- wpfaevent.php | 85 - 49 files changed, 9163 insertions(+), 1330 deletions(-) delete mode 100644 .github/workflows/phpcs.yml delete mode 100644 .gitignore delete mode 100644 LICENSE delete mode 100644 Readme.txt create mode 100644 admin/class-wpfa-admin.php delete mode 100644 admin/class-wpfaevent-admin.php delete mode 100644 admin/css/wpfaevent-admin.css delete mode 100644 admin/index.php delete mode 100644 admin/js/wpfaevent-admin.js delete mode 100644 admin/partials/wpfaevent-admin-display.php create mode 100644 assets/images/logo.png create mode 100644 fossasia-landing.php create mode 100644 includes/class-fossasia-uninstaller.php create mode 100644 includes/class-wpfa-cli.php create mode 100644 includes/class-wpfa-cpt.php create mode 100644 includes/class-wpfa-settings.php delete mode 100644 includes/class-wpfaevent-activator.php delete mode 100644 includes/class-wpfaevent-deactivator.php delete mode 100644 includes/class-wpfaevent-i18n.php delete mode 100644 includes/class-wpfaevent-loader.php delete mode 100644 includes/class-wpfaevent.php delete mode 100644 includes/index.php delete mode 100644 index.php delete mode 100644 phpcs.xml rename languages/wpfaevent.pot => public/class-wpfa-admin.php (100%) create mode 100644 public/class-wpfa-public.php create mode 100644 public/class-wpfa-settings.php delete mode 100644 public/class-wpfaevent-public.php delete mode 100644 public/css/wpfaevent-public.css delete mode 100644 public/index.php delete mode 100644 public/js/wpfaevent-public.js delete mode 100644 public/partials/wpfaevent-public-display.php create mode 100644 public/uninstall.php create mode 100644 public/wpfa-public.css create mode 100644 templates/admin-dashboard.php create mode 100644 templates/c_Users_Nishil_Downloads_test38(1)_templates_events-listing-page.php create mode 100644 templates/c_Users_Nishil_Downloads_test38(1)_templates_past-events-page.php create mode 100644 templates/code-of-conduct-page.php create mode 100644 templates/events-listing-page.php create mode 100644 templates/fossasia-landing-content.php create mode 100644 templates/fossasia-landing-template.php create mode 100644 templates/minimal.json create mode 100644 templates/past-events-page.php create mode 100644 templates/schedule-page.php create mode 100644 templates/speakers-page-content.php create mode 100644 templates/speakers-page.php delete mode 100644 wpfaevent.php 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..b95fbca 100644 --- a/README.md +++ b/README.md @@ -1,185 +1,40 @@ -# WordPress FOSSASIA Event Plugin (WPFAevent) +# FOSSASIA Event WordPress 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 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. -- 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 - -| 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 | - -Each shortcode can accept optional attributes — for example: - -```text -[event_schedule profile="summit2026"] -``` - -if multiple event profiles are configured in settings. - - -## Settings Page - -Navigate to **Settings → Event Plugin** to configure: - -* **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 - -If the fields are left empty, the plugin falls back to placeholder content for development. - -## Development Notes - -* 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: - - ``` - your-theme/event-plugin/partials/event-speakers.php - ``` - - WordPress will automatically use the theme’s version if it exists. - ---- - -## 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 ); -``` - -Logs can be found in `/wp-content/debug.log`. - -## Translation +## Getting Started -* 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**. +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. -## Contributing -Contributions are welcome! +## For Developers: Seeding Data with WP-CLI -* Fork the repository on GitHub -* Create a feature branch: +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. - ```bash - git checkout -b feature/my-feature - ``` -* Commit and push your changes, then submit a **Pull Request** -* Follow **WordPress PHP coding standards** +**Prerequisites:** +- WP-CLI must be installed. +- The plugin must be activated. -Before submitting: +**Commands:** -* 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()` +1. **Seed Minimal Data:** + This command creates 2 placeholder speakers and 1 event in the database. + ```bash + wp wpfa seed --minimal + ``` -## License +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 + ``` -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-wpfa-admin.php b/admin/class-wpfa-admin.php new file mode 100644 index 0000000..2449f28 --- /dev/null +++ b/admin/class-wpfa-admin.php @@ -0,0 +1,68 @@ + 'esc_url_raw', + ) ); + + register_setting( 'wpfa_settings_group', 'wpfa_default_placeholder', array( + 'sanitize_callback' => 'sanitize_text_field', + ) ); + + register_setting( 'wpfa_settings_group', 'wpfa_feature_toggle', array( + 'sanitize_callback' => 'absint', + ) ); + } + + public function render_settings_page() { + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + ?> +
+

+
+ + + + + + + + + + + + + + +
>
+ +
+
+ - */ -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 0000000000000000000000000000000000000000..b73402160321dc0b847707114572a7f847ef1462 GIT binary patch literal 9238 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 + 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 . 'includes/class-wpfa-settings.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'; + } + + if ( is_admin() ) { + require_once WPFA_EVENT_PLUGIN_DIR . 'admin/class-wpfa-admin.php'; + 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' ) ); + + if ( is_admin() ) { + new WPFA_Admin(); + } + } +} + +/** + * 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..c355b58 --- /dev/null +++ b/includes/class-wpfa-cli.php @@ -0,0 +1,311 @@ +] + * : 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=.' ); + } + + /** + * Manage plugin settings. + * + * ## OPTIONS + * + * + * : The command to run. + * --- + * options: + * - get + * - set + * - list + * --- + * + * [] + * : The setting key. Required for 'get' and 'set'. + * + * [] + * : The value to set for the setting. Required for 'set'. + * + * ## EXAMPLES + * + * # List all settings and their values + * wp wpfa settings list + * + * # Get the value of a single setting + * wp wpfa settings get wpfa_image_base_path + * + * # Set the value of a setting + * wp wpfa settings set wpfa_image_base_path https://example.com/images/ + * + * # Enable a feature toggle + * wp wpfa settings set wpfa_feature_toggle 1 + * + * @when after_wp_load + * + * @param array $args Positional arguments. + * @param array $assoc_args Associative arguments. + */ + public static function settings( $args, $assoc_args ) { + $valid_keys = array( 'wpfa_image_base_path', 'wpfa_default_placeholder', 'wpfa_feature_toggle' ); + $command = $args[0] ?? 'list'; + + switch ( $command ) { + case 'list': + $settings_data = array(); + foreach ( $valid_keys as $key ) { + $settings_data[] = array( + 'key' => $key, + 'value' => WPFA_Settings::get( $key, '(not set)' ), + ); + } + WP_CLI\Utils\format_items( 'table', $settings_data, array( 'key', 'value' ) ); + break; + + case 'get': + $key = $args[1] ?? ''; + if ( empty( $key ) || ! in_array( $key, $valid_keys, true ) ) { + WP_CLI::error( 'Please provide a valid setting key. Use `wp wpfa settings list` to see available keys.' ); + } + WP_CLI::line( WPFA_Settings::get( $key, '(not set)' ) ); + break; + + case 'set': + $key = $args[1] ?? ''; + $value = $args[2] ?? null; + if ( empty( $key ) || ! in_array( $key, $valid_keys, true ) || is_null( $value ) ) { + WP_CLI::error( 'Usage: wp wpfa settings set ' ); + } + WPFA_Settings::update( $key, $value ); + WP_CLI::success( "Setting '{$key}' updated." ); + break; + } + } + + /** + * 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..2628711 --- /dev/null +++ b/includes/class-wpfa-cpt.php @@ -0,0 +1,94 @@ + 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' ) ); + } + + /** + * Adds custom columns to the speaker CPT admin list. + * + * @param array $columns Existing columns. + * @return array Modified columns. + */ + public function add_speaker_admin_columns( $columns ) { + $columns['speaker_role'] = __( 'Role', 'wpfa-event' ); + $columns['speaker_org'] = __( 'Organization', 'wpfa-event' ); + return $columns; + } + + /** + * Renders the content for the custom admin columns. + * + * @param string $column The column name. + * @param int $post_id The post ID. + */ + public function render_speaker_admin_columns( $column, $post_id ) { + if ( 'speaker_role' === $column ) { + echo esc_html( get_post_meta( $post_id, 'wpfa_speaker_position', true ) ); + } + if ( 'speaker_org' === $column ) { + echo esc_html( get_post_meta( $post_id, 'wpfa_speaker_org', true ) ); + } + } +} \ No newline at end of file diff --git a/includes/class-wpfa-settings.php b/includes/class-wpfa-settings.php new file mode 100644 index 0000000..35dfc2f --- /dev/null +++ b/includes/class-wpfa-settings.php @@ -0,0 +1,13 @@ + - */ -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/languages/wpfaevent.pot b/public/class-wpfa-admin.php similarity index 100% rename from languages/wpfaevent.pot rename to public/class-wpfa-admin.php diff --git a/public/class-wpfa-public.php b/public/class-wpfa-public.php new file mode 100644 index 0000000..3a21fa9 --- /dev/null +++ b/public/class-wpfa-public.php @@ -0,0 +1,96 @@ +nplugin_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/class-wpfa-settings.php b/public/class-wpfa-settings.php new file mode 100644 index 0000000..e69de29 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/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 '
    '; + foreach ( $rss_items as $item ) : ?> +
  • + get_date('j F Y | g:i a'); ?>' target="_blank" rel="noopener"> + get_title() ); ?> + + get_date('F j, Y'); ?> +
  • + '; + echo ''; + } +} +?> + +> + + + + + + + + + + + + + + + + +> + + +
    +
    + + +
    +
    + +

    FOSSASIA Events

    +

    Discover upcoming community events, local meetups, and partner conferences from the FOSSASIA network.

    +
    + + + +
    +
    +
    +
    +

    Events

    + +
    + +
    + +
    +
    + +
    + +
    + Showing 0 matching events. +
    + +
    + 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_description = get_the_excerpt(); + $featured_img_url = get_the_post_thumbnail_url( get_the_ID(), 'large' ) ?: ''; + + $formatted_date = 'Date not set'; + if (!empty($event_date)) { + $start = date_create($event_date); + if (!empty($event_end_date) && $event_end_date !== $event_date) { + $end = date_create($event_end_date); + $formatted_date = date_format($start, 'M j') . ' - ' . date_format($end, 'M j, Y'); + } else { + $formatted_date = date_format($start, 'F j, Y'); + } + } + ?> +
    + + +
    + + Edit Content + +
    + + + +
    + <?php the_title_attribute(); ?> +
    +
    +

    +

    +

    +
    +
    +
    + No events created yet. Click "Create Event" to add one!

    '; + endif; + ?> +
    + + + + +
    + +
    +
    + + +
    + + +
    +
    +
    +

    Global Settings Dashboard

    + +
    +
    +
    +
    Code of Conduct
    +
    + + +
    +

    This is a preview of the global Code of Conduct. To make changes, please go to the main Admin Dashboard.

    + '']; + ?> +
    + +
    +
    +
    +
    + + + + + + + + + + + +
    +
    + + + + + + + +
    +
    + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/templates/c_Users_Nishil_Downloads_test38(1)_templates_past-events-page.php b/templates/c_Users_Nishil_Downloads_test38(1)_templates_past-events-page.php new file mode 100644 index 0000000..b5d9ea5 --- /dev/null +++ b/templates/c_Users_Nishil_Downloads_test38(1)_templates_past-events-page.php @@ -0,0 +1,184 @@ + 'page', + 'posts_per_page' => -1, + 'meta_key' => '_event_date', + 'orderby' => 'meta_value', + 'order' => 'DESC', // Show most recent past events first + 'meta_query' => [ + 'relation' => 'AND', + [ + 'key' => '_wp_page_template', + 'value' => 'fossasia-landing-template.php', + 'compare' => '=', + ], + [ + // This logic finds events where the end date is in the past. + // If no end date, it checks if the start date is in the past. + 'relation' => 'OR', + [ + 'key' => '_event_end_date', + 'value' => $today, + 'compare' => '<', + 'type' => 'DATE' + ], + [ + 'relation' => 'AND', + [ + 'key' => '_event_end_date', + 'compare' => 'NOT EXISTS' + ], + [ + 'key' => '_event_date', + 'value' => $today, + 'compare' => '<', + 'type' => 'DATE' + ] + ] + ] + ], +]); +?> + +> + + + + + + + +> + + +
    + + +
    +
    +

    Past FOSSASIA Events

    +

    A look back at our community events, meetups, and conferences.

    + +
    + +
    +
    +
    +

    Event Archive

    +
    + +
    + have_posts() ) : + while ( $past_events_query->have_posts() ) : $past_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 ); + $featured_img_url = get_the_post_thumbnail_url( get_the_ID(), 'large' ) ?: plugins_url('../images/hero-image.jpg', __FILE__); + + $formatted_date = 'Date not set'; + if (!empty($event_date)) { + $start = date_create($event_date); + if (!empty($event_end_date) && $event_end_date !== $event_date) { + $end = date_create($event_end_date); + $formatted_date = date_format($start, 'M j') . ' - ' . date_format($end, 'M j, Y'); + } else { + $formatted_date = date_format($start, 'F j, Y'); + } + } + ?> + + No past events found in the archive.

    '; + endif; + ?> +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/templates/code-of-conduct-page.php b/templates/code-of-conduct-page.php new file mode 100644 index 0000000..2f18b78 --- /dev/null +++ b/templates/code-of-conduct-page.php @@ -0,0 +1,91 @@ +Placeholder CoC content.

    "}'); } + +$theme_settings_data = json_decode(file_get_contents($global_theme_settings_file), true); +$coc_content_data = json_decode(file_get_contents($coc_content_file), true); +$coc_content = $coc_content_data['content'] ?? '

    The Code of Conduct has not been set. Please add it in the admin dashboard.

    '; +?> + +> + + + + + + + +> + + +
    + + +
    +
    +

    Code of Conduct

    +

    Our commitment to a safe, respectful, and harassment-free event experience for everyone.

    +
    + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/templates/events-listing-page.php b/templates/events-listing-page.php new file mode 100644 index 0000000..bafec67 --- /dev/null +++ b/templates/events-listing-page.php @@ -0,0 +1,1354 @@ + ($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 '
      '; + foreach ( $rss_items as $item ) : ?> +
    • + get_date('j F Y | g:i a'); ?>' target="_blank" rel="noopener"> + get_title() ); ?> + + get_date('F j, Y'); ?> +
    • + '; + echo ''; + } +} +?> + +> + + + + + + + + + + + + + + + + +> + + +
      +
      + + +
      +
      + +

      FOSSASIA Events

      +

      Discover upcoming community events, local meetups, and partner conferences from the FOSSASIA network.

      +
      + + + +
      +
      +
      +
      +

      Events

      + +
      + +
      + +
      +
      + +
      + +
      + Showing 0 matching events. +
      + +
      + 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_description = get_the_excerpt(); + $featured_img_url = get_the_post_thumbnail_url( get_the_ID(), 'large' ) ?: ''; + + $formatted_date = 'Date not set'; + if (!empty($event_date)) { + $start = date_create($event_date); + if (!empty($event_end_date) && $event_end_date !== $event_date) { + $end = date_create($event_end_date); + $formatted_date = date_format($start, 'M j') . ' - ' . date_format($end, 'M j, Y'); + } else { + $formatted_date = date_format($start, 'F j, Y'); + } + } + ?> +
      + + +
      + + Edit Content + +
      + + + +
      + <?php the_title_attribute(); ?> +
      +
      +

      +

      +

      +
      +
      +
      + No events created yet. Click "Create Event" to add one!

      '; + endif; + ?> +
      + + + + +
      + +
      +
      + + +
      + + +
      +
      +
      +

      Global Settings Dashboard

      + +
      +
      +
      +
      Code of Conduct
      +
      + + +
      +

      This is a preview of the global Code of Conduct. To make changes, please go to the main Admin Dashboard.

      + '']; + ?> +
      + +
      +
      +
      +
      + + + + + + + + + + + +
      +
      + + + + + + + +
      +
      + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/templates/fossasia-landing-content.php b/templates/fossasia-landing-content.php new file mode 100644 index 0000000..e69de29 diff --git a/templates/fossasia-landing-template.php b/templates/fossasia-landing-template.php new file mode 100644 index 0000000..6ed5f59 --- /dev/null +++ b/templates/fossasia-landing-template.php @@ -0,0 +1,1421 @@ + ($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($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 = ''; + + ?> +
      + +
      + + +
      + 'About', 'href' => '#about', 'order' => 20]; +} +if ($section_visibility['speakers'] ?? true) { + $dynamic_nav_items['speakers'] = ['text' => 'Speakers', 'href' => '#speakers', 'order' => 30]; +} +if ($section_visibility['schedule'] ?? true) { + $dynamic_nav_items['schedule-overview'] = ['text' => 'Schedule', 'href' => '#schedule-overview', 'order' => 40]; +} +if ($section_visibility['sponsors'] ?? true) { + $dynamic_nav_items['sponsors'] = ['text' => 'Sponsors', 'href' => '#sponsors', 'order' => 50]; +} + +// Add active custom sections that have a title +if (!empty($custom_sections_data) && is_array($custom_sections_data)) { + $active_custom_sections = array_filter($custom_sections_data, function($section) { + return !empty($section['is_active']) && !empty($section['title']); + }); + + foreach ($active_custom_sections as $section) { + $nav_item = [ + 'text' => $section['title'], + 'href' => '#custom-section-' . $section['id'], + 'order' => $section['order'] ?? 100 // Use the section's order for sorting + ]; + $dynamic_nav_items['custom-' . $section['id']] = $nav_item; + } +} + +// Sort all navigation items by their order +uasort($dynamic_nav_items, fn($a, $b) => ($a['order'] ?? 100) <=> ($b['order'] ?? 100)); +?> + + +
      + + + + +
      + + + + +
      + + +
      + + + + + +
      + +

      + + + +

      + + + +

      + +

      + + + +
      + + + + + + +
      + + +
      + + + +
      + <?php echo esc_attr($event_title_string); ?> hero image +
      + +
      + +
      + + + + +
      + +
      +
      + About content has not been set for this event.

      '); ?> +
      +
      +
      + + + + +
      + +
      + +
      + +

      Featured Speakers

      + + +

      Curated invited & selected speakers

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

      Schedule Overview

      + + +

      Event timeline and sessions

      + +
      + + + + '; // Wrapper to match theme style + echo '

      ' . esc_html($table['name']) . '

      '; + echo '
      '; + echo ''; + + $is_first_row = true; + foreach ($table['data'] as $row_data) { + echo ''; + foreach ($row_data as $cell_content) { + $cell_html = preg_replace('/\*\*(.*?)\*\*/', '$1', esc_html($cell_content)); + $cell_html = preg_replace('/\*(.*?)\*/', '$1', $cell_html); + $cell_html = nl2br($cell_html); + $tag = $is_first_row ? 'th' : 'td'; + echo "<{$tag}>" . wp_kses_post($cell_html) . ""; + } + echo ''; + $is_first_row = false; + } + echo '
      '; + } + } + ?> + + + +
      + + + + +
      + + + + +
      + + + + + +
      + +
      + +
      + +

      Sponsors

      + + +

      Our valued partners and supporters

      + +
      + + + + ' . esc_html( $group['group_name'] ) . ''; + echo '
      '; + foreach ( $group['sponsors'] as $sponsor ) { + ?> + + '; + } + } + ?> + +
      +
      + + + + + +
      + +
      + +
      + +
      + +

      Join the open-source future

      + + +

      Register now and submit a talk — help shape the program.

      + + +
      + + + +
      + + +
      + +
      + + +
      + + Follow updates on @fossasia + + +
      + +
      + +
      + +
      + + + + + + +^ + + + +
      + + + + + +
      + + + + + + + +
      + + + + \ No newline at end of file diff --git a/templates/minimal.json b/templates/minimal.json new file mode 100644 index 0000000..8ab4171 --- /dev/null +++ b/templates/minimal.json @@ -0,0 +1,32 @@ +{ + "speakers": [ + { + "title": "Alex Example", + "content": "Open source contributor and community speaker.", + "slug": "alex-example", + "org": "FOSSASIA", + "position": "Developer Advocate", + "photo": "https://via.placeholder.com/300x300.png?text=Alex" + }, + { + "title": "Bao Nguyen", + "content": "Engineer focusing on event platforms and accessibility.", + "slug": "bao-nguyen", + "org": "Eventyay", + "position": "Software Engineer", + "photo": "https://via.placeholder.com/300x300.png?text=Bao" + } + ], + "events": [ + { + "title": "FOSSASIA Community Meetup", + "content": "A casual meetup to discuss the roadmap and OSS collaboration.", + "slug": "fossasia-community-meetup", + "start_date": "2026-01-15", + "end_date": "2026-01-16", + "location": "Online", + "url": "https://eventyay.com/", + "speakers": ["alex-example", "bao-nguyen"] + } + ] +} \ No newline at end of file diff --git a/templates/past-events-page.php b/templates/past-events-page.php new file mode 100644 index 0000000..2639a9c --- /dev/null +++ b/templates/past-events-page.php @@ -0,0 +1,375 @@ + 'page', + 'posts_per_page' => -1, + 'meta_key' => '_event_date', + 'orderby' => 'meta_value', + 'order' => 'DESC', // Show most recent past events first + 'meta_query' => [ + 'relation' => 'AND', + [ + 'key' => '_wp_page_template', + 'value' => 'fossasia-landing-template.php', + 'compare' => '=', + ], + [ + // This logic finds events where the end date is in the past. + // If no end date, it checks if the start date is in the past. + 'relation' => 'OR', + [ + 'key' => '_event_end_date', + 'value' => $today, + 'compare' => '<', + 'type' => 'DATE' + ], + [ + 'relation' => 'AND', + [ + 'key' => '_event_end_date', + 'compare' => 'NOT EXISTS' + ], + [ + 'key' => '_event_date', + 'value' => $today, + 'compare' => '<', + 'type' => 'DATE' + ] + ] + ] + ], +]); +?> + +> + + + + + + +> + + +
      + + +
      +
      +

      Past FOSSASIA Events

      +

      A look back at our community events, meetups, and conferences.

      + +
      + +
      +
      +
      +

      Event Archive

      +
      + +
      + have_posts() ) : + while ( $past_events_query->have_posts() ) : $past_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(); + $event_lead_text = get_post_meta( get_the_ID(), '_event_lead_text', true ); + $event_registration_link = get_post_meta( get_the_ID(), '_event_registration_link', true ); + $event_cfs_link = get_post_meta( get_the_ID(), '_event_cfs_link', true ); + $featured_img_url = get_the_post_thumbnail_url( get_the_ID(), 'large' ) ?: ''; + + $formatted_date = 'Date not set'; + if (!empty($event_date)) { + $start = date_create($event_date); + if (!empty($event_end_date) && $event_end_date !== $event_date) { + $end = date_create($event_end_date); + $formatted_date = date_format($start, 'M j') . ' - ' . date_format($end, 'M j, Y'); + } else { + $formatted_date = date_format($start, 'F j, Y'); + } + } + ?> +
      + + +
      + + Edit Content + +
      + + +
      + <?php the_title_attribute(); ?> +
      +
      +

      +

      +

      +
      +
      +
      + No past events found in the archive.

      '; + endif; + ?> +
      +
      +
      +
      +
      + + + + + + + + + \ No newline at end of file diff --git a/templates/schedule-page.php b/templates/schedule-page.php new file mode 100644 index 0000000..b1f82f7 --- /dev/null +++ b/templates/schedule-page.php @@ -0,0 +1,196 @@ + + +> + + + + + + +> + + +
      + + +
      + + +
      + '; + echo '

      Schedule Coming Soon

      '; + echo '

      The detailed schedule will be announced shortly. Please check back later!

      '; + echo '
      '; + } else { + $table = $schedule_tables; + echo '
      '; + echo '

      ' . esc_html($table['name']) . '

      '; + echo '
      '; + echo ''; + + $is_first_row = true; + foreach ($table['data'] as $row_data) { + echo ''; + foreach ($row_data as $cell_content) { + // Basic markdown to HTML conversion + $cell_html = preg_replace('/\*\*(.*?)\*\*/', '$1', esc_html($cell_content)); + $cell_html = preg_replace('/\*(.*?)\*/', '$1', $cell_html); + $cell_html = nl2br($cell_html); + + $tag = $is_first_row ? 'th' : 'td'; + echo "<{$tag}>" . wp_kses_post($cell_html) . ""; + } + echo ''; + $is_first_row = false; + } + echo '
      '; + echo '
      '; + echo '
      '; + } + ?> +
      + + + + + +
      + © FOSSASIA • FOSSASIA Summit — Mar 13–15, 2025 • True Digital Park West, Bangkok +
      + + + + + \ No newline at end of file diff --git a/templates/speakers-page-content.php b/templates/speakers-page-content.php new file mode 100644 index 0000000..e69de29 diff --git a/templates/speakers-page.php b/templates/speakers-page.php new file mode 100644 index 0000000..85be397 --- /dev/null +++ b/templates/speakers-page.php @@ -0,0 +1,1066 @@ + + "#D51007", "background_color" => "#f8f9fa", "text_color" => "#0b0b0b"]; } +$navigation_data = json_decode(file_get_contents($navigation_file), true); +$custom_sections_data = file_exists($sections_file) ? json_decode(file_get_contents($sections_file), true) : []; + +// --- Dynamically build navigation based on visible sections --- +$dynamic_nav_items = []; +$settings_file = $event_id ? $data_dir . '/site-settings-' . $event_id . '.json' : ''; +$site_settings_data = $event_id && file_exists($settings_file) ? json_decode(file_get_contents($settings_file), true) : []; +$section_visibility = $site_settings_data['section_visibility'] ?? []; + +// Add default sections if they are visible +if ($section_visibility['about'] ?? true) { + $dynamic_nav_items['about'] = ['text' => 'About', 'href' => get_permalink($event_id) . '#about', 'order' => 20]; +} +if ($section_visibility['speakers'] ?? true) { + $dynamic_nav_items['speakers'] = ['text' => 'Speakers', 'href' => get_permalink($event_id) . '#speakers', 'order' => 30]; +} +if ($section_visibility['schedule'] ?? true) { + $dynamic_nav_items['schedule-overview'] = ['text' => 'Schedule', 'href' => get_permalink($event_id) . '#schedule-overview', 'order' => 40]; +} +if ($section_visibility['sponsors'] ?? true) { + $dynamic_nav_items['sponsors'] = ['text' => 'Sponsors', 'href' => get_permalink($event_id) . '#sponsors', 'order' => 50]; +} + +// Add active custom sections that have a title +if (!empty($custom_sections_data) && is_array($custom_sections_data)) { + foreach ($custom_sections_data as $section) { + if (!empty($section['is_active']) && !empty($section['title'])) { + $dynamic_nav_items['custom-' . $section['id']] = ['text' => $section['title'], 'href' => get_permalink($event_id) . '#custom-section-' . $section['id'], 'order' => $section['order'] ?? 100]; + } + } +} + +// Sort all navigation items by their order +uasort($dynamic_nav_items, fn($a, $b) => ($a['order'] ?? 100) <=> ($b['order'] ?? 100)); +?> + +> + + + + + + + + +> + + +
      +
      + + +
      +
      +
      +

      FOSSASIA Summit Speakers

      +

      Discover all the amazing speakers joining us at FOSSASIA Summit 2025

      + + + +
      + +
      + +
      + + +
      +
      +
      + +
      + Showing 0 speakers +
      + +
      +

      No speakers found

      +

      Try adjusting your search or filters

      +
      + +
      + +
      +
      + +
      + © FOSSASIA • FOSSASIA Summit — Mar 13–15, 2025 • True Digital Park West, Bangkok +
      + + + + + + + + + + + + diff --git a/uninstall.php b/uninstall.php index 18392b5..3123496 100644 --- a/uninstall.php +++ b/uninstall.php @@ -1,31 +1,27 @@ -1, + 'post_type' => $cpt_slug, + 'post_status' => 'any', + ) + ); + // Delete each post. + foreach ( $posts as $post ) { + wp_delete_post( $post->ID, true ); + } +} +// Note: This simple uninstaller does not remove the `fossasia-data` directory +// from wp-content/uploads, as that was part of the old implementation. +// A more robust uninstaller could be added here if needed. \ No newline at end of file diff --git a/wpfaevent.php b/wpfaevent.php deleted file mode 100644 index d810945..0000000 --- a/wpfaevent.php +++ /dev/null @@ -1,85 +0,0 @@ -run(); - -} -run_wpfaevent(); From bcca8fa5395052aacef5852387fdffd906b2aba4 Mon Sep 17 00:00:00 2001 From: Nishil Hoogar <90044992+NishilHoogar@users.noreply.github.com> Date: Tue, 28 Oct 2025 17:57:58 +0530 Subject: [PATCH 2/2] Update public/class-wpfa-public.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- public/class-wpfa-public.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/class-wpfa-public.php b/public/class-wpfa-public.php index 3a21fa9..cff307b 100644 --- a/public/class-wpfa-public.php +++ b/public/class-wpfa-public.php @@ -1,4 +1,4 @@ -n