| | |
| | | <?php |
| | | namespace JVBase\managers; |
| | | |
| | | use Exception; |
| | | use JVBase\registrar\Registrar; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | |
| | | public function __construct() |
| | | { |
| | | $this->defineTables(); |
| | | $this->registerHooks(); |
| | | } |
| | | |
| | | protected function registerHooks():void |
| | | { |
| | | add_action('before_delete_post', [$this, 'cleanupPostFavourites']); |
| | | add_action('delete_term', [$this, 'cleanupTermFavourites'], 10, 3); |
| | | add_action('jvbUserRegistered', [$this, 'maybeAcceptListInvite'], 10, 3); |
| | | add_action('jvb_cleanupOrphanedFavourites', [$this, 'cleanupOrphanedFavourites']); |
| | | } |
| | | protected function defineTables():void |
| | | { |
| | | $this->defineFavouriteTable(); |
| | |
| | | } |
| | | private function defineFavouriteTable():void |
| | | { |
| | | $table = CustomTable::for('favourites'); |
| | | $table = CustomTable::for('favourites', true); |
| | | |
| | | $table->setColumns([ |
| | | 'id' => 'bigint(20) unsigned NOT NULL AUTO_INCREMENT', |
| | |
| | | 'list_id' => 'bigint(20) unsigned NOT NULL', |
| | | 'user_id' => "{$table->getUserIDType()} NOT NULL", |
| | | 'email' => 'varchar(255) NOT NULL', |
| | | 'invite_token' => 'varchar(255) NOT NULL', |
| | | 'permission' => "ENUM('view', 'edit') NOT NULL DEFAULT 'view'", |
| | | 'status' => "ENUM('pending', 'accepted', 'rejected', 'revoked') NOT NULL DEFAULT 'pending'", |
| | | 'created_at' => 'datetime NOT NULL DEFAULT CURRENT_TIMESTAMP', |
| | |
| | | } elseif ($newUser) { |
| | | $email = sanitize_email($newUser['email']); |
| | | $name = sanitize_text_field($newUser['name']); |
| | | $args['invite_token'] = wp_generate_password(32, false); |
| | | $args['email'] = $email; |
| | | //TODO: Magic Link setup |
| | | } |
| | |
| | | 'status' => $accept ? 'accepted' : 'rejected', |
| | | ], $args); |
| | | } |
| | | |
| | | public function getFavourites(array $args = []):array |
| | | { |
| | | return $this->favourites->getMany($args, false); |
| | | } |
| | | public function getLists(array $args = []):array |
| | | { |
| | | return $this->lists->getMany($args, false); |
| | | } |
| | | public function getAvailableLists(array $args = [], bool $include_shared = true):array |
| | | { |
| | | $lists = []; |
| | | $owned = $this->lists->getMany($args, false); |
| | | foreach ($owned['items'] as &$list) { |
| | | $list['item_count'] = $this->listItems->count(['list_id' => $list['id']]); |
| | | } |
| | | |
| | | $lists['owned'] = $owned; |
| | | |
| | | if ($include_shared) { |
| | | $args['where']['status'] = 'accepted'; |
| | | $sharedLists = $this->listShares->getMany($args); |
| | | $shared = []; |
| | | |
| | | foreach ($sharedLists as $share) { |
| | | $sharedList = $this->lists->get(['id' => $share->list_id]); |
| | | if ($sharedList) { |
| | | $sharedList['owner_name'] = jvbGetUsername($sharedList['user_id']); |
| | | $sharedList['item_count'] = $this->listItems->count(['list_id' => $sharedList['id']]); |
| | | $sharedList['permission_type'] = $sharedList->permission_type; |
| | | $shared[] = $sharedList; |
| | | } |
| | | } |
| | | $lists['shared'] = $shared; |
| | | } |
| | | return $lists; |
| | | } |
| | | |
| | | public function userOwnsList(int $list_id, int $user_id):bool |
| | | { |
| | | return $this->lists->count(['id' => $list_id, 'user_id' => $user_id]) > 0; |
| | | } |
| | | |
| | | public function getListDetails(int $list_id, int $user_id, int $page = 1):array |
| | | { |
| | | $list = JVB()->favourites()->getLists(['id' => $list_id, 'user_id' => $user_id]); |
| | | if (!$list) { |
| | | return []; |
| | | } |
| | | $items = $this->listItems->getMany([ |
| | | 'where' => ['list_id' => $list_id], |
| | | 'order_by' => 'added_at', |
| | | 'order' => 'DESC', |
| | | 'per_page' => 25, |
| | | 'page' => $page, |
| | | ]); |
| | | |
| | | $formatted = []; |
| | | |
| | | foreach($items as $item) { |
| | | //See if we can get the actual favourite record first |
| | | if ($item->favourite_id) { |
| | | $fav = $this->favourites->get(['id' => $item->favourite_id]); |
| | | if ($fav) { |
| | | $formatted[] = $fav; |
| | | continue; |
| | | } |
| | | } |
| | | |
| | | //Create a dummy favourite object otherwise |
| | | $formatted[] = (object) [ |
| | | 'type' => $item->item_type, |
| | | 'target_id' => $item->item_id, |
| | | 'created_at'=> $item->added_at |
| | | ]; |
| | | } |
| | | |
| | | $sharedWith = []; |
| | | $is_owner = $this->userOwnsList($list_id, $user_id); |
| | | if ($is_owner) { |
| | | $shares = $this->listShares->getMany([ |
| | | 'where' => ['list_id' => $list_id], |
| | | 'order_by' => 'created_at', |
| | | 'order' => 'DESC' |
| | | ]); |
| | | foreach ($shares as $share_item) { |
| | | $user = [ |
| | | 'email' => $share_item->email, |
| | | 'status' => $share_item->status, |
| | | 'date_added'=> $share_item->created_at, |
| | | ]; |
| | | if ($share_item->status === 'accepted' && $share_item->user_id) { |
| | | $user['name'] = jvbGetUsername($share_item->user_id); |
| | | $user['permission_type'] = $share_item->permission_type; |
| | | } |
| | | $sharedWith[] = $user; |
| | | } |
| | | } |
| | | |
| | | return [ |
| | | 'id' => (int)$list['id'], |
| | | 'name' => $list['name'], |
| | | 'description' => $list['description']??'', |
| | | 'created_at' => $list['created_at'], |
| | | 'is_owner' => $is_owner, |
| | | 'items' => $formatted, |
| | | 'shared_users' => $sharedWith |
| | | ]; |
| | | } |
| | | |
| | | public function getFavouriteCounts(int $user_id): array |
| | | { |
| | | $results = $this->favourites->queryResults( |
| | | "SELECT type, COUNT(*) as count FROM {table} WHERE user_id = %d GROUP BY type", |
| | | [$user_id] |
| | | ); |
| | | |
| | | $counts = []; |
| | | foreach ($results as $row) { |
| | | $type = str_replace(BASE, '', $row->type); |
| | | $counts[$type] = (int) $row->count; |
| | | } |
| | | $defaults = array_fill_keys( |
| | | array_map(fn($t) => str_replace(BASE, '', $t), Registrar::withFeature('favouritable')), |
| | | 0 |
| | | ); |
| | | return array_merge($defaults, $counts); |
| | | } |
| | | |
| | | public function cleanupOrphanedFavourites(): bool |
| | | { |
| | | global $wpdb; |
| | | |
| | | // Posts - no FK possible since target_id is generic |
| | | $this->favourites->query( |
| | | "DELETE f FROM {table} f |
| | | LEFT JOIN {$wpdb->posts} p ON f.target_id = p.ID |
| | | WHERE f.type IN ( |
| | | SELECT CONCAT('" . BASE . "', post_type) |
| | | FROM {$wpdb->posts} GROUP BY post_type |
| | | ) |
| | | AND p.ID IS NULL" |
| | | ); |
| | | |
| | | // Terms - same reason |
| | | $this->favourites->query( |
| | | "DELETE f FROM {table} f |
| | | LEFT JOIN {$wpdb->term_taxonomy} tt ON f.target_id = tt.term_id |
| | | AND f.type = CONCAT('" . BASE . "', tt.taxonomy) |
| | | WHERE tt.term_id IS NULL |
| | | AND f.type != CONCAT('" . BASE . "', 'user')" |
| | | ); |
| | | |
| | | $this->listItems->query( |
| | | "DELETE li FROM {table} li |
| | | LEFT JOIN {$wpdb->posts} p ON li.target_id = p.ID |
| | | LEFT JOIN {$wpdb->term_taxonomy} tt ON li.target_id = tt.term_id |
| | | WHERE p.ID IS NULL AND tt.term_id IS NULL" |
| | | ); |
| | | |
| | | return true; |
| | | } |
| | | /*************************************************************** |
| | | * UTILITY METHODS |
| | | **************************************************************/ |
| | |
| | | unset($notes[$index]); |
| | | return array_values($notes); //reindexed array |
| | | } |
| | | |
| | | public function cleanupPostFavourites(int $post_id):void |
| | | { |
| | | try { |
| | | $type = get_post_type($post_id); |
| | | if (!$type) return; |
| | | |
| | | $this->favourites->where([ |
| | | 'type' => $type, |
| | | 'target_id' => $post_id |
| | | ])->deleteResults(); |
| | | |
| | | $this->listItems->where([ |
| | | 'item_type' => $type, |
| | | 'item_id' => $post_id |
| | | ])->deleteResults(); |
| | | } catch (Exception $e) { |
| | | JVB()->error()->log('cleanupPostFavourites', $e->getMessage(), [ |
| | | 'post_id' => $post_id |
| | | ]); |
| | | } |
| | | } |
| | | |
| | | public function cleanupTermFavourites(int $term_id, int $tt_id, string $taxonomy):void |
| | | { |
| | | try { |
| | | $registrar = Registrar::getInstance($taxonomy); |
| | | if (!$registrar || !$registrar->hasFeature('favouritable')) { |
| | | return; |
| | | } |
| | | $this->favourites->where([ |
| | | 'type' => $taxonomy, |
| | | 'target_id' => $term_id, |
| | | ])->deleteResults(); |
| | | |
| | | $this->listItems->where([ |
| | | 'item_type' => $taxonomy, |
| | | 'item_id' => $term_id |
| | | ])->deleteResults(); |
| | | } catch (Exception $e) { |
| | | JVB()->error()->log('cleanupTermFavourites', $e->getMessage(), [ |
| | | 'term_id' => $term_id, |
| | | 'taxonomy'=> $taxonomy |
| | | ]); |
| | | } |
| | | } |
| | | } |