| @@ -8,8 +8,9 @@ | ||
| 8 | 8 | namespace WP_Defender\Component; |
| 9 | 9 | |
| 10 | 10 | use WP_Defender\Component; |
| 11 | 11 | use WP_Defender\Model\Notification as Model_Notification; |
| 12 | +use WP_Defender\Model\Notification\Firewall_Notification; | |
| 12 | 13 | |
| 13 | 14 | /** |
| 14 | 15 | * Handles recipient search and frontend directory data. |
| 15 | 16 | */ |
| @@ -53,9 +54,9 @@ | ||
| 53 | 54 | $results[] = array( |
| 54 | 55 | 'id' => (string) $user->ID, |
| 55 | 56 | 'name' => $user->display_name, |
| 56 | 57 | 'email' => $user->user_email, |
| 57 | - 'role' => ! empty( $user->roles ) ? $user->roles[0] : '', | |
| 58 | + 'role' => isset( $user->roles[0] ) ? $user->roles[0] : '', | |
| 58 | 59 | ); |
| 59 | 60 | } |
| 60 | 61 | |
| 61 | 62 | return $results; |
| @@ -94,8 +95,35 @@ | ||
| 94 | 95 | } |
| 95 | 96 | } |
| 96 | 97 | |
| 97 | 98 | /** |
| 99 | + * Check whether an email already belongs to a recipient in any module. | |
| 100 | + * | |
| 101 | + * @param string $email Email address to look up. | |
| 102 | + * @return bool True when the email is already a recipient. | |
| 103 | + */ | |
| 104 | + public function email_exists( string $email ): bool { | |
| 105 | + $email = strtolower( trim( $email ) ); | |
| 106 | + if ( '' === $email ) { | |
| 107 | + return false; | |
| 108 | + } | |
| 109 | + | |
| 110 | + foreach ( $this->notification->get_modules_as_objects() as $model ) { | |
| 111 | + $recipients = array_merge( | |
| 112 | + array_values( $model->in_house_recipients ), | |
| 113 | + array_values( $model->out_house_recipients ) | |
| 114 | + ); | |
| 115 | + foreach ( $recipients as $recipient ) { | |
| 116 | + if ( strtolower( trim( (string) ( $recipient['email'] ?? '' ) ) ) === $email ) { | |
| 117 | + return true; | |
| 118 | + } | |
| 119 | + } | |
| 120 | + } | |
| 121 | + | |
| 122 | + return false; | |
| 123 | + } | |
| 124 | + | |
| 125 | + /** | |
| 98 | 126 | * Get recipients across all modules. |
| 99 | 127 | * |
| 100 | 128 | * @return array Recipient data. |
| 101 | 129 | */ |
| @@ -122,9 +150,9 @@ | ||
| 122 | 150 | * @param array $avatar_map Avatar URLs keyed by email. |
| 123 | 151 | * @return void |
| 124 | 152 | */ |
| 125 | 153 | private function collect_in_house_recipients( array &$result, array &$seen, Model_Notification $model, array $avatar_map ): void { |
| 126 | - $is_module_active = Model_Notification::STATUS_ACTIVE === $model->status; | |
| 154 | + $counts_as_subscribed = $this->counts_as_subscribed( $model ); | |
| 127 | 155 | foreach ( $model->in_house_recipients as $recipient ) { |
| 128 | 156 | $key = 'u_' . ( $recipient['id'] ?? $recipient['email'] ); |
| 129 | 157 | $base = array( |
| 130 | 158 | 'id' => (string) ( $recipient['id'] ?? $recipient['email'] ), |
| @@ -131,9 +159,9 @@ | ||
| 131 | 159 | 'avatar' => $avatar_map[ $recipient['email'] ] ?? '', |
| 132 | 160 | 'role' => $recipient['role'] ?? '', |
| 133 | 161 | '_inHouse' => true, |
| 134 | 162 | ); |
| 135 | - $this->collect_recipient( $result, $seen, $key, $recipient, $model->slug, $base, $is_module_active ); | |
| 163 | + $this->collect_recipient( $result, $seen, $key, $recipient, $model->slug, $base, $counts_as_subscribed ); | |
| 136 | 164 | } |
| 137 | 165 | } |
| 138 | 166 | |
| 139 | 167 | /** |
| @@ -144,9 +172,9 @@ | ||
| 144 | 172 | * @param Model_Notification $model Notification model. |
| 145 | 173 | * @return void |
| 146 | 174 | */ |
| 147 | 175 | private function collect_out_house_recipients( array &$result, array &$seen, Model_Notification $model ): void { |
| 148 | - $is_module_active = Model_Notification::STATUS_ACTIVE === $model->status; | |
| 176 | + $counts_as_subscribed = $this->counts_as_subscribed( $model ); | |
| 149 | 177 | foreach ( $model->out_house_recipients as $recipient ) { |
| 150 | 178 | $key = 'o_' . $recipient['email']; |
| 151 | 179 | $base = array( |
| 152 | 180 | 'id' => $recipient['email'], |
| @@ -153,13 +181,31 @@ | ||
| 153 | 181 | 'avatar' => '', |
| 154 | 182 | 'role' => '', |
| 155 | 183 | '_inHouse' => false, |
| 156 | 184 | ); |
| 157 | - $this->collect_recipient( $result, $seen, $key, $recipient, $model->slug, $base, $is_module_active ); | |
| 185 | + $this->collect_recipient( $result, $seen, $key, $recipient, $model->slug, $base, $counts_as_subscribed ); | |
| 158 | 186 | } |
| 159 | 187 | } |
| 160 | 188 | |
| 161 | 189 | /** |
| 190 | + * Whether membership in this module counts as an active subscription. | |
| 191 | + * | |
| 192 | + * Reports and the firewall alert are per-recipient preferences, so they count regardless of the | |
| 193 | + * module's on/off state; otherwise a template toggle would mask a recipient's own preference. | |
| 194 | + * Other notifications count only while their status is active. | |
| 195 | + * | |
| 196 | + * @param Model_Notification $model Notification model. | |
| 197 | + * @return bool | |
| 198 | + */ | |
| 199 | + private function counts_as_subscribed( Model_Notification $model ): bool { | |
| 200 | + if ( 'report' === $model->type || Firewall_Notification::SLUG === $model->slug ) { | |
| 201 | + return true; | |
| 202 | + } | |
| 203 | + | |
| 204 | + return Model_Notification::STATUS_ACTIVE === $model->status; | |
| 205 | + } | |
| 206 | + | |
| 207 | + /** | |
| 162 | 208 | * Get avatar URLs for all in-house recipient emails. |
| 163 | 209 | * |
| 164 | 210 | * @param array $modules Notification modules. |
| 165 | 211 | * @return array |
| @@ -167,9 +213,9 @@ | ||
| 167 | 213 | private function get_in_house_avatar_map( array $modules ): array { |
| 168 | 214 | $in_house_emails = array(); |
| 169 | 215 | foreach ( $modules as $model ) { |
| 170 | 216 | foreach ( $model->in_house_recipients as $recipient ) { |
| 171 | - if ( ! empty( $recipient['email'] ) ) { | |
| 217 | + if ( isset( $recipient['email'] ) && '' !== trim( $recipient['email'] ) ) { | |
| 172 | 218 | $in_house_emails[ $recipient['email'] ] = true; |
| 173 | 219 | } |
| 174 | 220 | } |
| 175 | 221 | } |
| @@ -184,21 +230,21 @@ | ||
| 184 | 230 | |
| 185 | 231 | /** |
| 186 | 232 | * Add a recipient entry to the result or append its module slug if already seen. |
| 187 | 233 | * |
| 188 | - * @param array $result Accumulated recipients list. | |
| 189 | - * @param array $seen Deduplication index. | |
| 190 | - * @param string $key Unique recipient key. | |
| 191 | - * @param array $recipient Recipient data from the model. | |
| 192 | - * @param string $slug Notification module slug. | |
| 193 | - * @param array $base Type-specific fields. | |
| 194 | - * @param bool $is_module_active Whether the notification module is enabled. | |
| 234 | + * @param array $result Accumulated recipients list. | |
| 235 | + * @param array $seen Deduplication index. | |
| 236 | + * @param string $key Unique recipient key. | |
| 237 | + * @param array $recipient Recipient data from the model. | |
| 238 | + * @param string $slug Notification module slug. | |
| 239 | + * @param array $base Type-specific fields. | |
| 240 | + * @param bool $counts_as_subscribed Whether membership in the module counts as an active subscription. | |
| 195 | 241 | * @return void |
| 196 | 242 | */ |
| 197 | - private function collect_recipient( array &$result, array &$seen, string $key, array $recipient, string $slug, array $base, bool $is_module_active = true ): void { | |
| 243 | + private function collect_recipient( array &$result, array &$seen, string $key, array $recipient, string $slug, array $base, bool $counts_as_subscribed = true ): void { | |
| 198 | 244 | $status = $recipient['status'] ?? ''; |
| 199 | 245 | $canceled = Model_Notification::USER_SUBSCRIBE_CANCELED === $status; |
| 200 | - $add_slug = $is_module_active && ! $canceled; | |
| 246 | + $add_slug = $counts_as_subscribed && ! $canceled; | |
| 201 | 247 | |
| 202 | 248 | if ( isset( $seen[ $key ] ) ) { |
| 203 | 249 | if ( $add_slug ) { |
| 204 | 250 | $result[ $seen[ $key ] ]['statuses'][] = $slug; |