| | |
| | | namespace JVBase\managers; |
| | | |
| | | use Exception; |
| | | use JVBase\Instance; |
| | | use JVBase\managers\queue\executors\InvitationExecutor; |
| | | use JVBase\managers\queue\Queue; |
| | | use JVBase\managers\queue\TypeConfig; |
| | | use JVBase\registrar\Registrar; |
| | | use JVBase\base\Site; |
| | | use JVBase\rest\Rest; |
| | | use JVBase\rest\routes\Invitations; |
| | | use WP_Error; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | |
| | | |
| | | class InvitationsManager |
| | | { |
| | | use Instance; |
| | | protected array $inviteConfig; |
| | | protected CustomTable $table; |
| | | protected int $expiryDays = 14; |
| | | protected Cache $cache; |
| | | public function __construct() |
| | | protected Rest $route; |
| | | |
| | | private function __construct() |
| | | { |
| | | $this->defineTable(); |
| | | if (!isset($this->table)) { |
| | | return; |
| | | } |
| | | if (Site::membership() && Site::membership()->has('can_invite')) { |
| | | $this->route = Invitations::getInstance(); |
| | | } |
| | | $this->setInviteConfig(); |
| | | $this->cache = Cache::for('invitations'); |
| | | $this->cache = Cache::for('invitations')->user(); |
| | | add_action('init', [$this, 'registerInvitationExecutors'], 5); |
| | | |
| | | add_action('user_register', [$this, 'checkInvitation']); |
| | |
| | | |
| | | public function defineTable():void |
| | | { |
| | | $terms = Registrar::getFeatured('invitable', 'term'); |
| | | $terms = Registrar::withFeature('invitable', 'term'); |
| | | $membership = Site::membership(); |
| | | $roles = ($membership) ? Site::membership()->has('can_invite') :[]; |
| | | if (empty($terms) && empty($roles)) { |
| | |
| | | 'name' => 'varchar(255) NOT NULL', |
| | | 'email' => 'varchar(255) NOT NULL', |
| | | 'invitation_token' => 'varchar(255) NOT NULL', |
| | | 'invited_role' => 'varchar(50) NOT NULL COMMENT \'Role being invited to\'', |
| | | 'status' => 'ENUM(\'pending\',\'accepted\',\'rejected\',\'expired\',\'revoked\') DEFAULT \'pending\'', |
| | | 'inviters' => 'JSON NOT NULL COMMENT \'Array of {user_id, invited_at}\'', |
| | | 'invited_role' => 'varchar(50) NOT NULL', //Role being invited to |
| | | 'status' => "ENUM('pending','accepted','rejected','expired','revoked') DEFAULT 'pending'", |
| | | 'inviters' => 'JSON NOT NULL', // Array of {user_id, invited_at} |
| | | 'new_user_id' => $table->getUserIDType().' DEFAULT NULL', |
| | | 'expires_at' => 'datetime NOT NULL', |
| | | 'accepted_at' => 'datetime DEFAULT NULL', |
| | |
| | | |
| | | $table->setKeys([ |
| | | ['key' => 'PRIMARY', 'value' => '(`id`)'], |
| | | ['key' => 'UNIQUE KEY', 'value' => '`unique_email_role` (`email`, `invited_role`)'], |
| | | ['key' => 'UNIQUE', 'value' => '`unique_email_role` (`email`, `invited_role`)'], |
| | | '`token_lookup` (`invitation_token`)', |
| | | '`status_expiry` (`status`, `expires_at`)', |
| | | '`role_status` (`invited_role`, `status`)', |
| | |
| | | */ |
| | | public function registerInvitationExecutors(): void |
| | | { |
| | | $registry = JVB()->queue()->registry(); |
| | | $registry = Queue::getInstance()->registry(); |
| | | $executor = new InvitationExecutor(); |
| | | |
| | | // Create invitations - chunked at 20 |
| | |
| | | /** |
| | | * Build invite types from JVB constants |
| | | * Combines JVB_MEMBERSHIP['can_invite'] and invitable taxonomies |
| | | * TODO: Should be accessible via Site::getInstance()->membership()\ |
| | | * |
| | | */ |
| | | protected function buildInviteTypes(): array |
| | | { |
| | | $invitable = $this->cache->remember( |
| | | return $this->cache->remember( |
| | | 'invitableTypes', |
| | | function () { |
| | | $types = []; |
| | |
| | | } |
| | | |
| | | // Term invitations from invitable content taxonomies |
| | | $invitable = Registrar::getFeatured('invitable', 'term'); |
| | | $content = Registrar::getFeatured('is_content', 'term'); |
| | | $ownable = Registrar::getFeatured('is_ownable', 'term'); |
| | | $invitable = Registrar::withFeature('invitable', 'term'); |
| | | $content = Registrar::withFeature('is_content', 'term'); |
| | | $ownable = Registrar::withFeature('is_ownable', 'term'); |
| | | $taxonomies = array_intersect($invitable, $content, $ownable); |
| | | if (!empty($taxonomies)) { |
| | | $users = Registrar::getRegistered('user'); |
| | |
| | | return $types; |
| | | } |
| | | ); |
| | | return $invitable; |
| | | |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | // User must be owner or manager of the term |
| | | return JVB()->roles()->isManager($userID, $termID); |
| | | return Roles::getInstance()()->isManager($userID, $termID); |
| | | } |
| | | |
| | | /** |
| | |
| | | } catch (Exception $e) { |
| | | $this->table->rollback(); |
| | | |
| | | JVB()->error()->log( |
| | | Errors::getInstance()->log( |
| | | 'invitation_create', |
| | | 'Error processing invitations: ' . $e->getMessage(), |
| | | ['user_id' => $userID], |
| | |
| | | } |
| | | $termText = implode('', $termContent); |
| | | |
| | | $button = JVB()->email()->button($signupUrl, 'Join the Scene!'); |
| | | $link = JVB()->email()->link($signupUrl); |
| | | $signature = JVB()->email()->signature(); |
| | | $button = Emails::getInstance()->button($signupUrl, 'Join the Scene!'); |
| | | $link = Emails::getInstance()->link($signupUrl); |
| | | $signature = Emails::getInstance()->signature(); |
| | | |
| | | $message = sprintf( |
| | | '<p>Hi %s!</p> |
| | |
| | | $signature |
| | | ); |
| | | |
| | | JVB()->email()->sendEmail($email, $subject, $message); |
| | | Emails::getInstance()->sendEmail($email, $subject, $message); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | // Grant capabilities |
| | | $user = get_userdata($userID); |
| | | $user->add_cap('skip_moderation', true); |
| | | $user->add_cap('skip_moderation'); |
| | | |
| | | return true; |
| | | }); |
| | |
| | | $this->notifyInvitersOfAcceptance($invitation, $userID); |
| | | |
| | | // Invalidate cache |
| | | Cache::for('invitations')->forget('user_' . $userID); |
| | | $this->cache->forget('user_' . $userID); |
| | | |
| | | return true; |
| | | } |
| | |
| | | */ |
| | | protected function processTermMembership(int $userID, object $invitation): void |
| | | { |
| | | foreach (JVB()->roles()->getInvitableTaxonomies() as $taxonomy) { |
| | | foreach (Roles::getInstance()()->getInvitableTaxonomies() as $taxonomy) { |
| | | $column = 'to_' . $taxonomy; |
| | | if (isset($invitation->$column) && $invitation->$column) { |
| | | $termID = (int) $invitation->$column; |
| | |
| | | $userData = get_userdata($userID); |
| | | |
| | | foreach ($inviters as $inviter) { |
| | | JVB()->notification()->addNotification( |
| | | NotificationManager::getInstance()->addNotification( |
| | | $inviter['user_id'], |
| | | 'invitation_accepted', |
| | | [ |
| | |
| | | ); |
| | | |
| | | // Clear cache after cleanup |
| | | Cache::for('invitations')->flush(); |
| | | $this->cache->flush(); |
| | | } |
| | | |
| | | /***************************************************************** |
| | |
| | | $name |
| | | ); |
| | | |
| | | JVB()->email()->sendEmail($email, $subject, $content, 'INVITATION REVOKED'); |
| | | Emails::getInstance()->sendEmail($email, $subject, $content, 'INVITATION REVOKED'); |
| | | } |
| | | |
| | | /** |