HEX
Server: nginx/1.18.0
System: Linux test-ipsremont 5.4.0-214-generic #234-Ubuntu SMP Fri Mar 14 23:50:27 UTC 2025 x86_64
User: ips (1000)
PHP: 8.0.30
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: /var/www/quadcodewordpressapi/public/wp-content/themes/intentionally-blank/lib/api.php
<?php
const GLOSSARY_TYPE = 'glossary';
const POST_TYPE = 'post';

const EVENT_TYPE = 'event';
const DEFAULT_LANG = 'en';
const DEFAULT_POSTS_PER_PAGE = 100;
function getMediaSrcSizes(int $mediaId): array
{
    return [
        'thumbnail' => wp_get_attachment_image_src($mediaId)[0] ?? null,
        'medium' => wp_get_attachment_image_src($mediaId, 'medium')[0] ?? null,
        'mediumLarge' => wp_get_attachment_image_src($mediaId, 'medium_large')[0] ?? null,
        'large' => wp_get_attachment_image_src($mediaId, 'large')[0] ?? null,
    ];
}
function getMediaSrc(?int $mediaId, string $size = 'thumbnail'): ?string
{
    return empty($mediaId) ? null : (is_array(wp_get_attachment_image_src($mediaId, $size)) ? wp_get_attachment_image_src($mediaId, $size)[0] : null);
}
function getAuthorData($post, $attributeName, $request): array
{
    if (is_array($post)) {
        $authorId = $post['author'];
    } else {
        $authorId = $post->post_author;
    }
    $author = get_user_by('ID', $authorId);
    $meta = get_user_meta($authorId);
    $firstName = $meta['first_name'][0];
    $lastName = $meta['last_name'][0];
    $avatarId = (int) $meta['avatar'][0];
    $linkedin = !empty($meta['linkedin']) ? $meta['linkedin'][0] : '';

    $description =  $meta['description'][0];
    $lang = getLangFromRequest($request);
    if ($lang !== DEFAULT_LANG && !empty($meta['description_' . $lang])) {
        $description = $meta['description_' . $lang][0];
    }

    $data = [
        'name' => join(' ', array_filter([$firstName, $lastName])),
        'description' => $description,
        'descriptionEn' => $meta['description'][0],
        'position' => $meta['position'][0],
        'avatar' => getMediaSrcSizes($avatarId),
        'linkedin' => $linkedin,
        'slug' => $author->user_nicename,
        'totalPosts' => countUserPosts($authorId),
    ];

    $lang = getLangFromRequest($request);

    if ($lang !== DEFAULT_LANG) {
        $data = addAuthorTranslations($author, $data, $lang);
    }

    return $data;
}

register_rest_field('post', 'authorData', ['get_callback' => 'getAuthorData']);
register_rest_field('glossary', 'authorData', ['get_callback' => 'getAuthorData']);

function getImageData($post): array
{
    return getMediaSrcSizes($post['featured_media']);
}

register_rest_field('post', 'imageData', ['get_callback' => 'getImageData']);
register_rest_field('event', 'imageData', ['get_callback' => 'getImageData']);
register_rest_field('glossary', 'imageData', ['get_callback' => 'getImageData']);

function getReadAlso($post): array
{
    $result = [];

    $readAlso = get_field('read_also', $post['id']);
    if (empty($readAlso)) {
        return $result;
    }

    foreach ($readAlso as $subPostId) {
        $subPost = get_post($subPostId);
        $tags = get_the_tags($subPost);
        $tag = $tags[0] ?? null;
        if (!empty($tag)) {
            $color = get_field('color', $tag->taxonomy . '_' . $tag->term_id);
        }

        $lang = !empty($_GET['lang']) ? sanitize_text_field($_GET['lang']) : DEFAULT_LANG;
        $postTranslation = getPostTranslation($subPostId, $lang);

        if (!empty($postTranslation[0])) {
            $translation = $postTranslation[0];
            $subPost->post_title = $translation->post_title;
        }

        $result[] = [
            'title' => $subPost->post_title,
            'slug' => $subPost->post_name,
            'color' => $color ?? null,
            'type' => $subPost->post_type,
        ];
    }


    return $result;
}

register_rest_field('post', 'readAlso', ['get_callback' => 'getReadAlso']);
register_rest_field('glossary', 'readAlso', ['get_callback' => 'getReadAlso']);

function getTags($post): array
{
    $result = [];
    $tags = get_the_tags($post['id']);
    if (empty($tags)) {
        return $result;
    }

    $tagTranslations = [];
    if (!empty($_GET['lang'])) {
        $lang = sanitize_text_field($_GET['lang']);
        $tagTranslations = getTagTranslations($lang);
    }

    foreach ($tags as $tag) {
        if (!empty($_GET['lang'])) {
            $translations = pll_get_term_translations($tag->term_id);
            if (!empty($translations[$lang])) {
                $tag = get_term($translations[$lang]);
                $result[] = $tag->name;
                continue;
            }
        }

        if (!empty($tagTranslations[$tag->term_id])) {
            $result[] = $tagTranslations[$tag->term_id];
        } else {
            $result[] = $tag->name;
        }
    }

    return $result;
}

register_rest_field('post', 'tagsData', ['get_callback' => 'getTags']);
register_rest_field('glossary', 'tagsData', ['get_callback' => 'getTags']);

function getViews($post): int
{
    return (int) get_field('views', $post['id']);
}

register_rest_field('post', 'views', ['get_callback' => 'getViews']);
register_rest_field('glossary', 'views', ['get_callback' => 'getViews']);

function getWordsNumberCallback($post): int
{
    return getWordsNumber($post['content']['rendered']);
}

function getEstReadingTimeCallback($post): int
{
    $lang = getLangFromRequest() ?? DEFAULT_LANG;
    return getEstReadingTime($post['content']['initial'] ?? $post['content']['rendered'], $lang);
}

register_rest_field('post', 'wordsNumber', ['get_callback' => 'getWordsNumberCallback']);
register_rest_field('glossary', 'wordsNumber', ['get_callback' => 'getWordsNumberCallback']);

register_rest_field('post', 'estReadingTime', ['get_callback' => 'getEstReadingTimeCallback']);
register_rest_field('glossary', 'estReadingTime', ['get_callback' => 'getEstReadingTimeCallback']);

add_action('rest_api_init', static function () {
    function filterPostData($post): array
    {
        $clearData = [
            'id' => $post['id'],
            'date' => $post['date'],
            'modified' => $post['modified'],
            'slug' => $post['slug'],
            'status' => $post['status'],
            'title' => $post['title'],
            'content' => $post['content'],
            'excerpt' => $post['excerpt'],
            'authorData' => $post['authorData'],
            'imageData' => $post['imageData'],
            'readAlso' => $post['readAlso'],
            'views' => $post['views'],
            'wordsNumber' => $post['wordsNumber'],
            'estReadingTime' => $post['estReadingTime'],
            'expertData' => $post['expertData'],
            'tagsData' => $post['tagsData'],
        ];

        if (!empty($post['yoast_head_json'])) {
            unset($post['yoast_head_json']['schema']);
            $clearData['yoast_head_json'] = $post['yoast_head_json'];
        }

        if (!empty($post['acf']['faq'])) {
            $clearData['acf']['faq'] = $post['acf']['faq'];
        }

        return $clearData;
    }

    function getLangFromRequest(WP_REST_Request $request = null): string
    {
        if (!empty($_GET['lang'])) {
            return $_GET['lang'];
        }

        $params = [];
        if ($request) {
            $params = $request->get_params();

            if (empty($params['lang'])) {
                $params = $request->get_json_params();
            }
        }

        return !empty($params['lang']) ? $params['lang'] : DEFAULT_LANG;
    }

    register_rest_route('api/v1', 'posts', [
        'methods' => 'GET, POST',
        'permission_callback' => fn(WP_REST_Request $Request) => true, // Required
        'callback' => function (WP_REST_Request $request) {
            $body = $request->get_params();
            if (empty($body)) {
                $body = $request->get_json_params();
            }

            $categorySlug = $body['categorySlug'] ?? null;
            $authorId = $body['authorId'] ?? null;
            $excludeSlugs = $body['excludeSlugs'] ?? null;
            $excludeIds = $body['excludeIds'] ?? null;
            $orderBy = $body['orderBy'] ?? null;
            $order = $body['order'] ?? null;
            $perPage = $body['per_page'] ?? DEFAULT_POSTS_PER_PAGE;

            $lang = getLangFromRequest($request);

            if (empty($body['page'])) {
                $body['page'] = 1;
            }

            $args = [
                'post_type' => POST_TYPE,
                'post_status' => 'publish',
                'posts_per_page' => $perPage ?? -1,
                'offset' => $body['page'] ? $perPage * ($body['page'] - 1) : 0,
                'lang' => DEFAULT_LANG,
            ];
            if (!empty($categorySlug)) {
                $args['category_name'] = $categorySlug;
            }
            if (!empty($authorId)) {
                $args['author'] = $authorId;
            }
            if (!empty($perPage)) {
                $args['per_page'] = $perPage;
            }
            if (!empty($excludeSlugs)) {
                foreach ($excludeSlugs as $excludeSlug) {
                    $excludeIds[] = url_to_postid($excludeSlug);
                }
            }
            if (!empty($excludeIds)) {
                $args['post__not_in'] = $excludeIds;
            }
            if (!empty($orderBy)) {
                $args['orderby'] = $orderBy;
                if ('views' === $orderBy) {
                    $args['meta_key'] = $orderBy;
                    $args['orderby'] = 'meta_value_num';
                }
            }
            if (!empty($order)) {
                $args['order'] = $order;
            }

            $args['fields'] = 'ids';

            $posts = get_posts($args);

            $wpQuery = new WP_Query($args);
            $totalCount = $wpQuery->found_posts;

            $translatedPostIds = [];
            $postsIds = array_map(function ($postId) use ($lang, &$translatedPostIds) {
                $translations = pll_get_post_translations($postId);
                foreach ($translations as $l => $translatedPostId) {
                    $translatedPostStatus = get_post_status($translatedPostId);
                    if ($l === $lang && $translatedPostStatus == 'publish') {
                        $postId = $translatedPostId;
                        $translatedPostIds[] = $translatedPostId;
                    }
                }

                return $postId;
            }, $posts);

            $posts = [];
            if (!empty($postsIds)) {
                foreach ($postsIds as $postId) {
                    $internal_request = new WP_REST_Request('GET', '/wp/v2/posts/' . $postId);
                    $response = rest_do_request($internal_request);
                    if ($response->is_error()) {
                        return new WP_Error('internal_error', 'An error occurred while fetching data.', ['status' => 500]);
                    }
                    $postData = filterPostData($response->get_data());

                    if ($lang !== DEFAULT_LANG) {
                        if (!in_array($postData['id'], $translatedPostIds)) {
                            $postData = add_translation($postData, $lang);
                        }
                    }
                    $posts[] = $postData;
                }
            }

            $max_pages = $perPage > 0 ? (int) ceil($totalCount / $perPage) : 0;
            header('X-WP-Total: ' . (int) $totalCount);
            header('X-WP-TotalPages: ' . $max_pages);

            return compact('posts', 'totalCount');
        },
    ]);

    register_rest_route('api/v1', 'post/(?P<slug>.+)', [
        'methods' => 'GET, POST',
        'permission_callback' => fn(WP_REST_Request $Request) => true, // Required
        'callback' => function (WP_REST_Request $request) {
            $slug = $request['slug'] ?? null;
            if (empty($slug)) {
                return new WP_Error(400, 'Post slug is required', ['status' => 400]);
            }

            $lang = getLangFromRequest($request);
            $args = [
                'name' => $slug,
                'post_type' => POST_TYPE,
                'post_status' => 'publish',
            ];

            $posts = get_posts($args);

            $post = null;
            foreach ($posts as $innerPost) {
                $post = $innerPost;
            }

            if (empty($post)) {
                return new WP_Error(404, 'Post not found', ['status' => 404]);
            }

            $translatedPost = null;
            $translatedPostIds = pll_get_post_translations($post->ID);
            foreach ($translatedPostIds as $translatedPostLang => $translatedPostId) {
                if ($translatedPostLang === $lang) {
                    $translatedPost = get_post($translatedPostId);
                    if ($translatedPost->post_status == 'publish') {
                        $post = $translatedPost;
                    }
                }
            }

            // Get response in old format
            $internal_request = new WP_REST_Request('GET', '/wp/v2/posts/' . $post->ID);
            $response = rest_do_request($internal_request);
            if ($response->is_error()) {
                return new WP_Error('internal_error', 'An error occurred while fetching data.', ['status' => 500]);
            }

            $post = filterPostData($response->get_data());

            if ($lang !== DEFAULT_LANG) {
                if(!$translatedPost || $translatedPost->post_status != 'publish') {
                    $post = add_translation($post, $lang);
                }
            }

            if (!empty($post['content']) && !empty($post['content']['rendered'])) {
                $post['content']['rendered'] = replaceLinks($post['content']['rendered']);
            } elseif (!empty($post['content'])) {
                $post['content'] = replaceLinks($post['content']);
            }

            $result = [$post];

            return $result;
        },
    ]);

    register_rest_route('api/v1', 'view', [
        'methods' => WP_REST_Server::EDITABLE,
        'permission_callback' => fn(WP_REST_Request $Request) => true, // Required
        'callback' => function (WP_REST_Request $request) {
            $postId = $request->get_param('post_id');
            if (empty($postId)) {
                return new WP_Error(400, 'Post ID is required', ['status' => 400]);
            }

            $post = get_post($postId);

            if (empty($post) || !in_array($post->post_type, [POST_TYPE, GLOSSARY_TYPE])) {
                return new WP_Error(404, 'Post not found', ['status' => 404]);
            }

            $count = (int) get_field('views', $postId);
            $count++;
            update_field('views', $count, $postId);

            return ['new_count' => $count];
        },
        'args' => [
            'post_id' => [
                'description' => 'Post ID',
                'type' => 'integer',
            ],
        ],
    ]);

    register_rest_route('api/v1', 'events', [
        'methods' => 'GET, POST',
        'permission_callback' => fn(WP_REST_Request $Request) => true, // Required
        'callback' => function (WP_REST_Request $request) {
            $body = $request->get_json_params();
            if (empty($body) && $request->get_params()) {
                $body = $request->get_params();
            }

            if (empty($body) && $request->get_body()) {
                $body = json_decode($request->get_body(), true);
            }
            $lang = getLangFromRequest($request);

            $isPast = $body['isPast'] ?? null;

            $args = [
                'post_type' => EVENT_TYPE,
                'post_status' => 'publish',
                'posts_per_page' => $body['limit'] ?? 3,
                'offset' => $body['offset'] ?? 0,
                'orderby' => 'period_start_date',
                'order' => 'DESC',
                'lang' => DEFAULT_LANG,
            ];

            $today = date('Ymd');
            if ($isPast) {
                $args['meta_query'] = [
                    ['key' => 'period_end_date', 'compare' => '<=', 'value' => $today],
                ];
            } else {
                $args['meta_query'] = [
                    ['key' => 'period_end_date', 'compare' => '>=', 'value' => $today],
                ];
            }

            $events = get_posts($args);
            $wpQuery = new WP_Query($args);
            $totalCount = $wpQuery->found_posts;
            $max_pages = !empty($body['limit']) && $body['limit'] > 0 ? (int) ceil($totalCount / $body['limit']) : 0;

            header('X-WP-Total: ' . (int) $totalCount);
            header('X-WP-TotalPages: ' . $max_pages);

            $events = array_map(function ($event) use ($lang) {
                return convertEvent($event, $lang);
            }, $events);

            return compact('totalCount', 'events');
        },
    ]);

    register_rest_route('api/v2', 'initial-events', [
        'methods' => 'GET, POST',
        'permission_callback' => fn(WP_REST_Request $request) => true, // Required
        'callback' => function (WP_REST_Request $request) {
            $body = $request->get_json_params();
            if (empty($body) && $request->get_params()) {
                $body = $request->get_params();
            }

            if (empty($body) && $request->get_body()) {
                $body = json_decode($request->get_body(), true);
            }
            $lang = getLangFromRequest($request);

            $args = [
                'post_type' => 'event',
                'post_status' => 'publish',
                'posts_per_page' => $body['limit'] ?? 3,
                'offset' => 0,
                'orderby' => 'period_start_date',
                'order' => 'DESC',
                'lang' => DEFAULT_LANG,
            ];

            $today = date('Ymd');
            $pastEventsArgs = array_merge($args, ['meta_query' => [
                ['key' => 'period_end_date', 'compare' => '<=', 'value' => $today],
            ]]);

            $upcomingEventsArgs = array_merge($args, ['meta_query' => [
                ['key' => 'period_end_date', 'compare' => '>=', 'value' => $today],
            ]]);

            $pastEvents = new WP_Query($pastEventsArgs);
            $totalCountPast = $pastEvents->found_posts;
            $pastEvents = $pastEvents->posts;

            $upcomingEvents = new WP_Query($upcomingEventsArgs);
            $totalCountUpcoming = $upcomingEvents->found_posts;
            $upcomingEvents = $upcomingEvents->posts;

            $response = [
                "past" => [
                    "totalCount" => $totalCountPast,
                    "events" => [],
                ],
                "upcoming" => [
                    "totalCount" => $totalCountUpcoming,
                    "events" => [],
                ],
            ];

            $pastEvents = array_map(function ($event) use ($lang) {
                return convertEvent($event, $lang);
            }, $pastEvents);
            $upcomingEvents = array_map(function ($event) use ($lang) {
                return convertEvent($event, $lang);
            }, $upcomingEvents);

            $response['past']['events'] = $pastEvents;
            $response['upcoming']['events'] = $upcomingEvents;

            return $response;
        },
    ]);

    function convertEvent ($event, $lang) {
        if (!$event) {
            return;
        }
        $translations = pll_get_post_translations($event->ID);
        $eventTranlatedInAdmin = false;
        foreach ($translations as $l => $translatedPostId) {
            $translatedPostStatus = get_post_status($translatedPostId);
            if ($l === $lang && $translatedPostStatus == 'publish') {
                $event = get_post($translatedPostId); // Event translated in admin
                if ($event) {
                    $eventTranlatedInAdmin = true;
                }
            }
        }

        $period = get_field('period', $event->ID);
        $address = get_field('address', $event->ID);
        $link = get_field('external_link', $event->ID);
        $tag = get_field('tag', $event->ID);
        $mediaId = get_post_thumbnail_id($event->ID);

        $linkData = null;
        if (!empty($link['button_text']) && !empty($link['link'])) {
            $linkData = [
                'buttonText' => $link['button_text'],
                'link' => $link['link'],
            ];
        }

        $translatedData = [];
        $translatedAttributes = [];
        if (!$eventTranlatedInAdmin) {
            $translatedData = getPostTranslation($event->ID, $lang);
            if (!empty($translatedData[0])) {
                $translatedData = $translatedData[0];
                if ($translatedData->post_attributes) {
                    if (is_base64($translatedData->post_attributes)) {
                        $translatedData->post_attributes = base64_decode($translatedData->post_attributes);
                    }
                    // Bad serialized strings will be rebuild on translation cron run
                    $translatedAttributes = @unserialize($translatedData->post_attributes);
                }
            }
        }

        return [
            'title' => $translatedData->post_title ?? $event->post_title,
            'description' => !empty($translatedData->post_content) ? trim(strip_tags($translatedData->post_content)) : trim(strip_tags($event->post_content)),
            'startDate' => $period['start_date'],
            'endDate' => $period['end_date'],
            'city' => $address['city'] ?? null,
            'location' => $address['location'] ?? null,
            'linkData' => $linkData,
            'tag' => $translatedAttributes['tag'] ?? ($tag ?? null),
            'image' => getMediaSrcSizes($mediaId),
        ];
    }

    register_rest_route('api/v1', 'webinars', [
        'methods' => 'POST',
        'permission_callback' => fn(WP_REST_Request $Request) => true, // Required
        'callback' => function (WP_REST_Request $request) {
            $body = $request->get_json_params();

            $excludeIds = $body['excludeIds'] ?? null;
            $orderBy = $body['orderBy'] ?? null;
            $order = $body['order'] ?? null;
            $isFeatured = $body['isFeatured'] ?? null;

            $args = [
                'post_type' => 'webinar',
                'post_status' => 'publish',
                'posts_per_page' => $body['limit'] ?? 3,
                'offset' => $body['offset'] ?? 0,
            ];

            if (!empty($excludeIds)) {
                $args['post__not_in'] = $excludeIds;
            }
            if (!empty($orderBy)) {
                $args['orderby'] = $orderBy;
                if ('date' === $orderBy) {
                    $args['meta_key'] = $orderBy;
                    $args['orderby'] = 'meta_value';
                }
            }
            if (!empty($order)) {
                $args['order'] = $order;
            }
            if (!is_null($isFeatured)) {
                $args['meta_query'] = [
                    ['key' => 'is_featured', 'compare' => '=', 'value' => (int) $isFeatured],
                ];
            }

            $webinars = get_posts($args);
            $wpQuery = new WP_Query($args);
            $totalCount = $wpQuery->found_posts;
            $max_pages = !empty($body['limit']) && $body['limit'] > 0 ? (int) ceil($totalCount / $body['limit']) : 0;

            header('X-WP-Total: ' . (int) $totalCount);
            header('X-WP-TotalPages: ' . $max_pages);

            $usersCache = [];
            $webinars = array_map(function ($webinar) use (&$usersCache) {
                $agenda = get_field('agenda', $webinar->ID);
                $date = get_field('date', $webinar->ID);
                $presentersIds = get_field('presenters', $webinar->ID);
                $registrationLink = get_field('registration_link', $webinar->ID);
                $youTubeLink = get_field('youtube_link', $webinar->ID);
                $isFeatured = get_field('is_featured', $webinar->ID);
                $mediaId = get_post_thumbnail_id($webinar->ID);

                $presenters = [];
                foreach ($presentersIds as $presenterId) {
                    if (empty($usersCache[$presenterId])) {
                        $meta = get_user_meta($presenterId);
                        $firstName = $meta['first_name'][0];
                        $lastName = $meta['last_name'][0];
                        $avatarId = (int) $meta['avatar'][0];
                        $usersCache[$presenterId] = [
                            'name' => join(' ', array_filter([$firstName, $lastName])),
                            'description' => $meta['description'][0],
                            'position' => $meta['position'][0],
                            'avatar' => getMediaSrc($avatarId),
                            'linkedin' => get_user_meta($presenterId, 'linkedin', true) ?? '',
                        ];
                    }

                    $presenters[] = $usersCache[$presenterId];
                }

                return [
                    'title' => $webinar->post_title,
                    'agenda' => trim($agenda),
                    'date' => $date,
                    'presenters' => $presenters,
                    'registrationLink' => $registrationLink,
                    'youTubeLink' => $youTubeLink,
                    'image' => getMediaSrcSizes($mediaId),
                    'isFeatured' => (bool) $isFeatured,
                ];
            }, $webinars);

            return compact('totalCount', 'webinars');
        },
    ]);

    register_rest_route('api/v1', GLOSSARY_TYPE, [
        'methods' => 'GET',
        'permission_callback' => fn(WP_REST_Request $Request) => true, // Required
        'callback' => function (WP_REST_Request $request) {
            $lang = getLangFromRequest($request);
            $args = [
                'post_type' => GLOSSARY_TYPE,
                'post_status' => 'publish',
                'posts_per_page' => -1,
                'lang' => DEFAULT_LANG,
            ];
            $glossariesCollection = get_posts($args);

            $noLangArgs = $args;
            unset($noLangArgs['lang']);
            $noLangArgs = array_merge($noLangArgs, [
                'meta_query' => [
                    [
                        'key' => '_pll_language',
                        'compare' => 'NOT EXISTS',
                    ],
                ],
            ]);
            $noLangGlossaries = get_posts($noLangArgs);

            $glossariesCollection = array_merge($glossariesCollection, $noLangGlossaries);

            $glossariesCollection = array_map(function ($glossary) use ($lang) {
                $translations = pll_get_post_translations($glossary->ID);
                foreach ($translations as $l => $translated_post_id) {
                    if ($l === $lang) {
                        $glossary = get_post($translated_post_id);
                    }
                }

                return [
                    'word' => get_field('word', $glossary->ID),
                    'title' => $glossary->post_title,
                    'slug' => $glossary->post_name,
                ];
            }, $glossariesCollection);

            $glossary = [];
            foreach ($glossariesCollection as $word) {
                $key = mb_strtolower(mb_substr($word['word'], 0, 1));
                $glossary[$key][mb_strtolower($word['word'])] = $word;
            }

            ksort($glossary);

            return $glossary;
        },
    ]);

    register_rest_route('api/v1', 'glossary/(?P<slug>.+)', [
        'methods' => 'GET',
        'permission_callback' => fn(WP_REST_Request $Request) => true, // Required
        'callback' => function (WP_REST_Request $request) {
            $slug = $request['slug'] ?? null;
            if (empty($slug)) {
                return new WP_Error(400, 'Glossary slug is required', ['status' => 400]);
            }

            $lang = getLangFromRequest($request);
            $args = [
                'name' => $slug,
                'post_type' => GLOSSARY_TYPE,
                'post_status' => 'publish',
            ];

            $glossariesCollection = get_posts($args);

            $glossaryObject = null;
            foreach ($glossariesCollection as $glossary) {
                $glossaryObject = $glossary;
            }

            if (empty($glossaryObject)) {
                return new WP_Error(404, 'Glossary not found', ['status' => 404]);
            }

            $translatedGlossaryIds = [];
            $translatedGlossaryPosts = pll_get_post_translations($glossaryObject->ID);
            foreach ($translatedGlossaryPosts as $translatedGlossaryLang => $translatedGlossaryId) {
                $translatedGlossaryStatus = get_post_status($translatedGlossaryId);
                if ($translatedGlossaryLang === $lang && $translatedGlossaryStatus === 'publish') {
                    $translatedGlossary = get_post($translatedGlossaryId);
                    $glossaryObject = $translatedGlossary; // Replace post with translated post in database
                    $translatedGlossaryIds[] = $translatedGlossaryId;
                }
            }

            // Get response in old format
            $internal_request = new WP_REST_Request('GET', '/wp/v2/glossaries/' . $glossaryObject->ID);
            $response = rest_do_request($internal_request);
            if ($response->is_error()) {
                return new WP_Error('internal_error', 'An error occurred while fetching data.', ['status' => 500]);
            }

            $glossary = filterPostData($response->get_data());

            if ($lang !== DEFAULT_LANG) {
                if(!in_array($glossary['id'], $translatedGlossaryIds)) {
                    $glossary = add_translation($glossary, $lang);
                }
            }

            if (!empty($glossary['content'])) {
                $glossary['content']['rendered'] = replaceLinks($glossary['content']['rendered']);
            }
            $result = [$glossary];

            return $result;
        },
    ]);

    function calculateReadingTime(string $text, int $wordsPerMinute = 120): float
    {
        $words = str_word_count(strip_tags($text));

        return ceil($words / $wordsPerMinute);
    }

    function authorFormatting(int $authorId): array
    {
        $user = get_user_by('ID', $authorId);
        $meta = get_user_meta($authorId);

        $firstName = $meta['first_name'][0] ?? '';
        $lastName = $meta['last_name'][0] ?? '';

        $langs = get_available_languages();
        $langsArray = [];
        foreach ($langs as $lang) {
            $langParts = explode('_', $lang);
            $langsArray[] = $langParts[0];
        }

        $description = [
            'en' => $meta['description'][0] ?? '',
        ];

        foreach ($langsArray as $lang) {
            $description[$lang] = $meta['description_' . $lang][0] ?? '';
        }

        $avatarId = (int) $meta['avatar'][0];

        $fields = get_fields('user_' . $user->ID);
        $position = $fields['position'];

        return [
            'id' => $user->ID,
            'slug' => $user->user_nicename,
            'name' => join(' ', array_filter([$firstName, $lastName])),
            'position' => $position,
            'description' => $description,
            'avatar' => getMediaSrc($avatarId, 'medium'),
            'instagram' => get_user_meta($user->ID, 'instagram', true) ?? '',
            'linkedin' => get_user_meta($user->ID, 'linkedin', true) ?? '',
            'views' => (int)(get_user_meta($user->ID, 'total_views', true) ?? 0),
        ];
    }

    // Get author page data
    register_rest_route('api/v1', '/author/(?P<slug>.+)', [
        'methods' => 'GET',
        'permission_callback' => fn(WP_REST_Request $Request) => true, // Required
        'callback' => function (WP_REST_Request $request) {
            $slug = $request['slug'] ?? null;
            if (empty($slug)) {
                return new WP_Error(400, 'Author slug is required', ['status' => 400]);
            }

            $user = get_user_by('slug', $slug);
            if (empty($user)) {
                return new WP_Error(404, 'Author not found', ['status' => 404]);
            }

            $authorData = authorFormatting($user->ID);

            $authorData['totalPosts'] = countUserPosts($user->ID);

            $expertise = $user->expertise;
            $expertiseArray = [];
            if (!empty($expertise)) {
                foreach ($expertise as $exp) {
                    $term = get_term_by('id', $exp, 'expertise');
                    $expertiseArray[] = $term->name;
                }
            }

            $authorData['expertise'] = $expertiseArray;

            $fields = get_fields('user_' . $user->ID);
            if (!empty($fields['education'])) {
                $authorData['education'] = $fields['education'];
            }

            if (!empty($fields['experience'])) {
                $authorData['experience'] = $fields['experience'];
            }

            $lang = getLangFromRequest($request);
            if ($lang !== DEFAULT_LANG) {
                $authorData = addAuthorTranslations($user, $authorData, $lang);
            }

            return $authorData;
        },
    ]);

    register_rest_route('api/v2', 'popup/(?P<title>.+)', [
        'methods' => 'GET',
        'permission_callback' => fn(WP_REST_Request $Request) => true, // Required
        'callback' => function (WP_REST_Request $request) {

            $title = $request['title'] ?? null;
            if (empty($title)) {
                return new WP_Error(400, 'Popup title is required', ['status' => 400]);
            }

            $lang = getLangFromRequest($request);

            $args = [
                'title' => $title,
                'post_type' => 'popup',
            ];

            $posts = get_posts($args);

            $post = null;
            foreach ($posts as $innerPost) {
                $post = $innerPost;
            }

            if (empty($post)) {
                return new WP_Error(404, 'Popup not found', ['status' => 404]);
            }

            $translatedPostIds = pll_get_post_translations($post->ID);
            foreach ($translatedPostIds as $translatedPostLang => $translatedPostId) {
                if ($translatedPostLang === $lang) {
                    $translatedPost = get_post($translatedPostId);
                    $post = $translatedPost;
                }
            }
            $fields = get_fields($post->ID);

            if (!empty($fields['date']) && date($fields['date']) < date('Y-m-d')) {
                return new WP_Error(404, 'Popup not found', ['status' => 404]);
            }

            return [
                'title' => $fields['title'],
                'subtitle' => $fields['subtitle'],
                'text' => $fields['text'],
                'bullets' => $fields['bullets'],
                'date' => $fields['date'],
                'buttonText' => $fields['button_text'],
                'buttonLink' => $fields['button_link'],

            ];

        },
    ]);

    register_rest_route('api/v1', 'sitemap', [
        'methods' => 'GET',
        'permission_callback' => fn(WP_REST_Request $Request) => true, // Required
        'callback' => function (WP_REST_Request $request) {
            $args = [
                'post_type' => POST_TYPE,
                'post_status' => 'publish',
                'posts_per_page' => -1,
                'orderby' => 'meta_value_num',
                'order' => 'ASC',
                'lang' => DEFAULT_LANG,
            ];
            $posts = get_posts($args);

            $args = [
                'post_type' => GLOSSARY_TYPE,
                'post_status' => 'publish',
                'posts_per_page' => -1,
                'orderby' => 'meta_value_num',
                'order' => 'ASC',
                'lang' => DEFAULT_LANG,
            ];
            $glossaries = get_posts($args);

            $processedPosts = [];
            $postsData = [];

            foreach ($posts as $post) {
                $postId = $post->ID;
                $postData = [
                    'url' => '/blog/' . $post->post_name,
                    'alternatives' => [],
                ];
                $processedPosts[] = $postId;
                $translations = pll_get_post_translations($postId);
                foreach ($translations as $lang => $translated_post_id) {
                    if (!in_array($translated_post_id, $processedPosts)) {
                        $translated_post = get_post($translated_post_id);
                        $postData['alternatives'][$lang] = '/blog/' . $translated_post->post_name;
                        $processedPosts[] = $translated_post_id;
                    }
                }

                $langs = get_available_languages();
                foreach ($langs as $lang) {
                    $langParts = explode('_', $lang);
                    $lang = $langParts[0];
                    if (empty($postData['alternatives'][$lang])) {
                        $additionalTranslation = getPostTranslation($postId, $lang);
                        if (!empty($additionalTranslation[0])) {
                            $postData['alternatives'][$lang] = '/blog/' . $post->post_name;
                        }
                    }
                }

                if (empty($postData['alternatives'])) {
                    $postData['alternatives'] = null;
                }
                $postsData[] = $postData;
            }

            foreach ($glossaries as $post) {
                $postId = $post->ID;
                $postData = [
                    'url' => '/glossary/' . $post->post_name,
                    'alternatives' => [],
                ];
                $processedPosts[] = $postId;
                $translations = pll_get_post_translations($postId);
                foreach ($translations as $lang => $translated_post_id) {
                    if (!in_array($translated_post_id, $processedPosts)) {
                        $translated_post = get_post($translated_post_id);
                        $postData['alternatives'][$lang] = '/glossary/' . $translated_post->post_name;
                        $processedPosts[] = $translated_post_id;
                    }
                }

                $langs = get_available_languages();
                foreach ($langs as $lang) {
                    $langParts = explode('_', $lang);
                    $lang = $langParts[0];
                    if (empty($postData['alternatives'][$lang])) {
                        $additionalTranslation = getPostTranslation($postId, $lang);
                        if (!empty($additionalTranslation[0])) {
                            $postData['alternatives'][$lang] = '/blog/' . $post->post_name;
                        }
                    }
                }

                if (empty($postData['alternatives'])) {
                    $postData['alternatives'] = null;
                }
                $postsData[] = $postData;
            }

            return $postsData;
        },
    ]);

    register_rest_route('api/v2', 'webinar-banner', [
        'methods' => 'GET',
        'permission_callback' => static fn(WP_REST_Request $Request) => true,
        'callback' => static function (WP_REST_Request $request) {
            $fields = get_fields( 'option');
            return [
                'enabled' => $fields['enabled'],
                'title' => $fields['title'],
                'webinarTime' => $fields['webinar_time_utc'],
                'buttonText' => $fields['button_text'],
                'buttonLink' => $fields['button_link'],
            ];
        },
    ]);

    function getExpertData($post, $attributeName, $request)
    {
        $expert = get_field('expert', $post['id']);

        if (empty($expert)) {
            return null;
        }

        if (is_object($expert)) {
            $expertId = $expert->ID;
        } else {
            $expertId = $expert;
            $expert = get_user_by('id', $expertId);
        }
        $meta = get_user_meta($expertId);
        $firstName = $meta['first_name'][0];
        $lastName = $meta['last_name'][0];
        $avatarId = (int) $meta['avatar'][0];
        $linkedin = !empty($meta['linkedin']) ? $meta['linkedin'][0] : '';

        $description =  $meta['description'][0];
        $lang = getLangFromRequest($request);
        if ($lang !== DEFAULT_LANG && !empty($meta['description_' . $lang])) {
            $description = $meta['description_' . $lang][0];
        }

        $data = [
            'name' => join(' ', array_filter([$firstName, $lastName])),
            'description' => $description,
            'descriptionEn' => $meta['description'][0],
            'company' => $expert->company,
            'position' => $meta['position'][0],
            'avatar' => getMediaSrcSizes($avatarId),
            'linkedin' => $linkedin,
            'slug' => $expert->user_nicename,
            'totalPosts' => countUserPosts($expertId),
        ];

        if ($lang !== DEFAULT_LANG) {
            $data = addAuthorTranslations($expert, $data, $lang);
        }

        return $data;
    }

    register_rest_field('post', 'expertData', ['get_callback' => 'getExpertData']);
    register_rest_field('glossary', 'expertData', ['get_callback' => 'getExpertData']);

});

function countUserPosts(int $userId): int
{
    $args = [
        'author' => $userId,
        'post_type' => POST_TYPE,
        'post_status' => 'publish',
        'posts_per_page' => -1,
    ];

    $posts = get_posts($args);

    $combinations = [];
    foreach ($posts as $key => $post) {
        $translatedPostIds = pll_get_post_translations($post->ID);
        if (!empty($translatedPostIds)) {
            asort($translatedPostIds);
            $combinations[] = implode(',', $translatedPostIds);
        }
    }

    return count(array_unique($combinations));
}

function replaceLinks($content)
{
    $domain = 'quadcode';
    $content = str_replace('http:\/\/' . $domain, 'https:\/\/' . $domain, $content);
    $content = str_replace('http://' . $domain, 'https://' . $domain, $content);

    return $content;
}