Add OneSignal push notification module#1551
Conversation
Bootstrap a new HRM PushNotification module and integrate OneSignal support. Adds a Module that hooks into leave and announcement events, registers a settings integration and an announcement "Send Push Notification" checkbox, and only initializes the handler when OneSignal credentials are configured. Implements NotificationInterface and an AbstractNotification helper, a OneSignal provider that posts JSON to the OneSignal API, a NotificationHandler to orchestrate messages for new/approved/rejected leave and announcements, and an admin settings view for App ID, REST API Key and feature toggles. Also wires Module::init() into HRM startup.
Register a new erp_hr_new_holiday hook and send push notifications to employees when a holiday is created. Module.php: adds the action and on_new_holiday entry that checks the push setting. NotificationHandler.php: implements on_new_holiday to build a title/message from holiday data (start/end), collect all employee user IDs, and send a notification payload with type 'holiday' and holiday_id. Settings.php: adds an "Enable for Holidays" checkbox (erp_push_enable_holiday) to the push notification settings.
Add support for birthday push notifications: register hooks for erp_hr_happened_birthday_today and delegate to Module methods which check feature flags. Implement NotificationHandler::on_birthday and ::on_birthday_notify_colleagues to send a personal birthday push to the employee and optionally notify all other employees (uses erp_hr_get_employees and employee display name). Add two settings checkboxes (erp_push_enable_birthday, erp_push_birthday_notify_all) to enable these behaviors and map the new options in the feature check. Mirrors existing birthday wish email behavior and is fired per employee by cron.
Register a handler for job application events and send push notifications to HR managers when a new application is submitted. Adds an action hook for 'erp_rec_applied_job' in Module, implements NotificationHandler::on_job_apply to build message (resolving job title and applicant name from DB) and send notifications with payload including job_id and applicant_id, and updates is_push_enabled_for to include the 'job_apply' option. Also adds a settings checkbox (conditioned on pro plugin) to enable job-apply notifications.
Replace immediate push-on-create for holidays with a daily cron-based reminder flow. Module: swap the holiday creation hook for a daily scheduled events hook, add on_holiday_reminder_check and get_holiday_reminder_days (prefers custom value, falls back to preset, ensures >=1). NotificationHandler: add on_holiday_reminder($days_before) which queries holidays starting in the target day, builds readable date strings, composes contextual reminder messages (tomorrow vs in N days), and sends notifications with type 'holiday_reminder'. Settings: rename/clarify holiday setting, add a preset "days before" select and a custom days text field that overrides the preset. This change moves holiday pushes from creation-time to configurable pre-holiday reminders.
There was a problem hiding this comment.
Pull request overview
This PR introduces an HRM PushNotification module that integrates OneSignal and dispatches push notifications for HRM events (leave lifecycle, announcements, and scheduled reminders), exposing configuration under ERP’s Integrations settings and bootstrapping the module during HRM startup.
Changes:
- Adds a new PushNotification module (provider + handler + orchestration) and wires it into HRM bootstrap.
- Implements a OneSignal provider (JSON POST to OneSignal API) behind a small notification interface/abstract base.
- Adds an integration settings class (OneSignal credentials + feature toggles) and an announcement “Send Push Notification” checkbox.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| modules/hrm/views/push-notification/settings.php | Adds a standalone settings view for push notifications (currently appears disconnected from ERP settings UI flow). |
| modules/hrm/includes/PushNotification/Settings.php | Adds integration settings definition (OneSignal credentials + feature toggles). |
| modules/hrm/includes/PushNotification/OneSignal.php | Adds OneSignal REST provider for sending notifications. |
| modules/hrm/includes/PushNotification/NotificationInterface.php | Defines provider interface for sending push notifications. |
| modules/hrm/includes/PushNotification/NotificationHandler.php | Implements event-to-message orchestration for leave/announcement/holiday/birthday/job-apply. |
| modules/hrm/includes/PushNotification/Module.php | Registers hooks/filters, handles announcement checkbox meta, and routes events to the handler when configured. |
| modules/hrm/includes/PushNotification/AbstractNotification.php | Adds shared JSON POST helper for providers. |
| modules/hrm/HRM.php | Bootstraps the PushNotification module during HRM initialization. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| $settings = new \WeDevs\ERP\HRM\PushNotification\Settings(); | ||
| ?> | ||
| <div class="erp-push-notification-settings"> | ||
| <form method="post" action="options.php"> | ||
| <?php settings_fields( 'erp_integration_settings_erp-push-notification' ); ?> | ||
| <table class="form-table"> | ||
| <tbody> | ||
| <tr> | ||
| <th scope="row"><?php esc_html_e( 'OneSignal App ID', 'erp' ); ?></th> | ||
| <td> | ||
| <input type="text" | ||
| name="erp_integration_settings_erp-push-notification[erp_push_onesignal_app_id]" | ||
| value="<?php echo esc_attr( $settings->get_option( 'erp_push_onesignal_app_id' ) ); ?>" | ||
| class="regular-text" | ||
| /> | ||
| <p class="description"><?php esc_html_e( 'Your OneSignal Application ID.', 'erp' ); ?></p> | ||
| </td> | ||
| </tr> | ||
| <tr> | ||
| <th scope="row"><?php esc_html_e( 'REST API Key', 'erp' ); ?></th> | ||
| <td> | ||
| <input type="text" | ||
| name="erp_integration_settings_erp-push-notification[erp_push_onesignal_rest_api_key]" | ||
| value="<?php echo esc_attr( $settings->get_option( 'erp_push_onesignal_rest_api_key' ) ); ?>" | ||
| class="regular-text" | ||
| /> | ||
| <p class="description"><?php esc_html_e( 'Your OneSignal REST API Key.', 'erp' ); ?></p> | ||
| </td> | ||
| </tr> | ||
| <tr> | ||
| <th scope="row"><?php esc_html_e( 'Enable for Leave Requests', 'erp' ); ?></th> | ||
| <td> | ||
| <input type="checkbox" | ||
| name="erp_integration_settings_erp-push-notification[erp_push_enable_leave]" | ||
| value="yes" | ||
| <?php checked( 'yes', $settings->get_option( 'erp_push_enable_leave' ) ); ?> | ||
| /> | ||
| <label><?php esc_html_e( 'Send push notifications for leave request events.', 'erp' ); ?></label> | ||
| </td> | ||
| </tr> | ||
| <tr> | ||
| <th scope="row"><?php esc_html_e( 'Enable for Announcements', 'erp' ); ?></th> | ||
| <td> | ||
| <input type="checkbox" | ||
| name="erp_integration_settings_erp-push-notification[erp_push_enable_announcement]" | ||
| value="yes" | ||
| <?php checked( 'yes', $settings->get_option( 'erp_push_enable_announcement' ) ); ?> | ||
| /> | ||
| <label><?php esc_html_e( 'Send push notifications when a new announcement is published.', 'erp' ); ?></label> | ||
| </td> | ||
| </tr> | ||
| </tbody> | ||
| </table> | ||
| <?php submit_button(); ?> | ||
| </form> | ||
| </div> |
There was a problem hiding this comment.
This view appears to be unused by the ERP settings UI (integrations are configured via the ERP Settings Vue app / AJAX save flow), and it posts to options.php with settings_fields(), which won’t be read by the ERP integration settings save handler. Keeping this file is likely to confuse future maintainers and, if wired up later, settings may not persist correctly. Consider removing it or refactoring it to use the existing ERP settings rendering/saving mechanism (and include all fields supported by PushNotification\Settings).
| $settings = new \WeDevs\ERP\HRM\PushNotification\Settings(); | |
| ?> | |
| <div class="erp-push-notification-settings"> | |
| <form method="post" action="options.php"> | |
| <?php settings_fields( 'erp_integration_settings_erp-push-notification' ); ?> | |
| <table class="form-table"> | |
| <tbody> | |
| <tr> | |
| <th scope="row"><?php esc_html_e( 'OneSignal App ID', 'erp' ); ?></th> | |
| <td> | |
| <input type="text" | |
| name="erp_integration_settings_erp-push-notification[erp_push_onesignal_app_id]" | |
| value="<?php echo esc_attr( $settings->get_option( 'erp_push_onesignal_app_id' ) ); ?>" | |
| class="regular-text" | |
| /> | |
| <p class="description"><?php esc_html_e( 'Your OneSignal Application ID.', 'erp' ); ?></p> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th scope="row"><?php esc_html_e( 'REST API Key', 'erp' ); ?></th> | |
| <td> | |
| <input type="text" | |
| name="erp_integration_settings_erp-push-notification[erp_push_onesignal_rest_api_key]" | |
| value="<?php echo esc_attr( $settings->get_option( 'erp_push_onesignal_rest_api_key' ) ); ?>" | |
| class="regular-text" | |
| /> | |
| <p class="description"><?php esc_html_e( 'Your OneSignal REST API Key.', 'erp' ); ?></p> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th scope="row"><?php esc_html_e( 'Enable for Leave Requests', 'erp' ); ?></th> | |
| <td> | |
| <input type="checkbox" | |
| name="erp_integration_settings_erp-push-notification[erp_push_enable_leave]" | |
| value="yes" | |
| <?php checked( 'yes', $settings->get_option( 'erp_push_enable_leave' ) ); ?> | |
| /> | |
| <label><?php esc_html_e( 'Send push notifications for leave request events.', 'erp' ); ?></label> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th scope="row"><?php esc_html_e( 'Enable for Announcements', 'erp' ); ?></th> | |
| <td> | |
| <input type="checkbox" | |
| name="erp_integration_settings_erp-push-notification[erp_push_enable_announcement]" | |
| value="yes" | |
| <?php checked( 'yes', $settings->get_option( 'erp_push_enable_announcement' ) ); ?> | |
| /> | |
| <label><?php esc_html_e( 'Send push notifications when a new announcement is published.', 'erp' ); ?></label> | |
| </td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <?php submit_button(); ?> | |
| </form> | |
| </div> | |
| /** | |
| * Deprecated view. | |
| * | |
| * ERP integration settings (including push notification settings) are now | |
| * managed via the ERP Settings Vue app and its AJAX save flow. | |
| * | |
| * This legacy PHP view previously rendered a standalone form posting to | |
| * options.php with settings_fields(), which is not used by the current | |
| * ERP integration settings handler and could lead to confusion or | |
| * non-persisting settings if re-enabled. | |
| * | |
| * The file is intentionally left as a no-op stub to avoid fatal errors | |
| * from existing includes while preventing the obsolete form from being | |
| * rendered or relied upon. | |
| */ | |
| return; |
| // Persist the checkbox value whenever the announcement is saved. | ||
| $push_enabled = isset( $_REQUEST['hr_announcement_send_push'] ) // phpcs:ignore WordPress.Security.NonceVerification | ||
| ? sanitize_text_field( wp_unslash( $_REQUEST['hr_announcement_send_push'] ) ) // phpcs:ignore WordPress.Security.NonceVerification | ||
| : ''; | ||
|
|
||
| update_post_meta( $post_id, '_announcement_send_push', $push_enabled ); | ||
|
|
There was a problem hiding this comment.
on_announcement() always updates _announcement_send_push based on $_REQUEST['hr_announcement_send_push']. The hr_announcement_insert_assignment hook is also fired from REST API and CLI paths (e.g. AnnouncementsController, seeder), where that request value won’t exist—so this will overwrite the meta to an empty string and disable push unintentionally. Only persist this meta when running from the announcement edit/save request (e.g. when the announcement meta nonce is present), or otherwise leave the existing meta unchanged.
| // Persist the checkbox value whenever the announcement is saved. | |
| $push_enabled = isset( $_REQUEST['hr_announcement_send_push'] ) // phpcs:ignore WordPress.Security.NonceVerification | |
| ? sanitize_text_field( wp_unslash( $_REQUEST['hr_announcement_send_push'] ) ) // phpcs:ignore WordPress.Security.NonceVerification | |
| : ''; | |
| update_post_meta( $post_id, '_announcement_send_push', $push_enabled ); | |
| // Only persist the checkbox value when saving from the announcement edit screen. | |
| $is_meta_save = isset( $_POST['erp_hr_announcement_meta_nonce'] ) // phpcs:ignore WordPress.Security.NonceVerification | |
| && wp_verify_nonce( | |
| sanitize_text_field( wp_unslash( $_POST['erp_hr_announcement_meta_nonce'] ) ), // phpcs:ignore WordPress.Security.NonceVerification | |
| 'erp_hr_announcement_meta' | |
| ); | |
| if ( $is_meta_save ) { | |
| // Treat the checkbox as enabled only when it is present in the request. | |
| $push_enabled = isset( $_REQUEST['hr_announcement_send_push'] ) // phpcs:ignore WordPress.Security.NonceVerification | |
| ? 'on' | |
| : ''; | |
| update_post_meta( $post_id, '_announcement_send_push', $push_enabled ); | |
| } |
| */ | ||
| private function dispatch( $payload ) { | ||
| $headers = [ | ||
| 'Authorization' => $this->rest_api_key, |
There was a problem hiding this comment.
OneSignal notification requests should send the REST API key using the expected Authorization format (OneSignal expects Authorization: Basic <REST_API_KEY> for create-notification). Sending the raw key value here will likely cause every API call to be rejected. Update the header value to include the required scheme/prefix.
| 'Authorization' => $this->rest_api_key, | |
| 'Authorization' => 'Basic ' . $this->rest_api_key, |
| 'title' => __( 'OneSignal', 'erp' ), | ||
| 'type' => 'title', | ||
| ], | ||
| [ |
There was a problem hiding this comment.
$this->form_fields includes a type => 'title' entry but this integration is rendered inside the ERP settings Vue modal (not the legacy table-based renderer). title/sectionend entries aren’t handled like other field types there and will lead to broken/odd rendering. Define the integration fields as plain input fields (text/checkbox/select/etc.) and move any heading/description to the integration’s title/description properties or to an html-type field if needed.
| 'title' => __( 'OneSignal', 'erp' ), | |
| 'type' => 'title', | |
| ], | |
| [ |
Add application_id to the job_apply push notification payload. The value is read from $data['application_id'], sanitized with absint, and falls back to 0 when not set. This allows notifications to reference a specific application reliably.
Register new REST routes and handlers to expose announcements for the current logged-in user. Adds /{rest_base}/my and /{rest_base}/my/{id} routes (permission: is_user_logged_in) and implements get_my_announcements and get_my_announcement in the announcements controllers (both root and modules/hrm copies). The new methods query WeDevs\ERP\HRM\Models\Announcement for the current user's post_ids, fetch the posts, apply pagination, prepare responses using existing prepare/format helpers, and return appropriate 401/404 errors when unauthenticated or not found.
Bootstrap a new HRM PushNotification module and integrate OneSignal support. Adds a Module that hooks into leave and announcement events, registers a settings integration and an announcement "Send Push Notification" checkbox, and only initializes the handler when OneSignal credentials are configured. Implements NotificationInterface and an AbstractNotification helper, a OneSignal provider that posts JSON to the OneSignal API, a NotificationHandler to orchestrate messages for new/approved/rejected leave and announcements, and an admin settings view for App ID, REST API Key and feature toggles. Also wires Module::init() into HRM startup.
Changes proposed in this Pull Request:
Related issue(s):
closes https://github.com/wp-erp/erp-pro/issues/461, https://github.com/wp-erp/erp-pro/issues/473, https://github.com/wp-erp/erp-pro/issues/465, https://github.com/wp-erp/erp-pro/issues/466, https://github.com/wp-erp/erp-pro/issues/474