public function ajax_vote() {
$this->verify_request();
if (!is_user_logged_in()) {
wp_send_json_error(['message' => esc_html__('Sign in to vote.', 'cine-movie-engine')], 401);
}
$playlist_id = isset($_POST['playlist_id']) ? absint($_POST['playlist_id']) : 0;
$playlist = get_post($playlist_id);
if (!$playlist || $playlist->post_type !== self::POST_TYPE || $playlist->post_status !== 'publish' || get_post_meta($playlist_id, '_me_cp_visibility', true) !== 'public') {
wp_send_json_error(['message' => esc_html__('This playlist cannot be voted on.', 'cine-movie-engine')], 404);
}
$vote_key = '_me_cp_voted_' . $playlist_id;
if (!add_user_meta(get_current_user_id(), $vote_key, '1', true)) {
wp_send_json_error(['message' => esc_html__('You have already voted for this playlist.', 'cine-movie-engine')], 409);
}
$votes = absint(get_post_meta($playlist_id, '_me_cp_vote_count', true)) + 1;
update_post_meta($playlist_id, '_me_cp_vote_count', $votes);
wp_send_json_success(['votes' => $votes]);
}
public function ajax_move_title() {
$this->verify_request();
if (!is_user_logged_in()) {
wp_send_json_error(['message' => esc_html__('Sign in to manage this playlist.', 'cine-movie-engine')], 401);
}
$playlist_id = isset($_POST['playlist_id']) ? absint($_POST['playlist_id']) : 0;
$post_id = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
$direction = isset($_POST['direction']) ? sanitize_key(wp_unslash($_POST['direction'])) : '';
$playlist = get_post($playlist_id);
if (!$playlist || $playlist->post_type !== self::POST_TYPE || $playlist->post_status !== 'publish' || (int) $playlist->post_author !== get_current_user_id()) {
wp_send_json_error(['message' => esc_html__('You cannot manage this playlist.', 'cine-movie-engine')], 403);
}
if (!$post_id || !in_array($direction, ['up', 'down'], true)) {
wp_send_json_error(['message' => esc_html__('Invalid playlist item.', 'cine-movie-engine')], 400);
}
$items = $this->get_valid_items($playlist_id);
$index = array_search($post_id, $items, true);
$target_index = $direction === 'up' ? $index - 1 : $index + 1;
if ($index === false || !isset($items[$target_index])) {
wp_send_json_error(['message' => esc_html__('This title cannot be moved further.', 'cine-movie-engine')], 400);
}
$moving_item = $items[$index];
$items[$index] = $items[$target_index];
$items[$target_index] = $moving_item;
update_post_meta($playlist_id, '_me_cp_items', $items);
$source_user_id = absint(get_post_meta($playlist_id, '_me_cp_source_user_id', true));
$source_playlist_id = get_post_meta($playlist_id, '_me_cp_source_playlist_id', true);
if ($source_user_id === get_current_user_id() && $source_playlist_id !== '') {
$source_playlists = \\MovieEngine\\Frontend\\Users\\UserLibrary::get_playlists($source_user_id);
if (isset($source_playlists[$source_playlist_id])) {
$source_playlists[$source_playlist_id]['items'] = $items;
update_user_meta($source_user_id, '_movie_engine_playlists', $source_playlists);
}
}
wp_send_json_success(['items' => $items]);
}
}
private function render_listing($atts) {
self::migrate_personal_playlists_batch();
$sort = isset($_GET['playlist_sort']) ? sanitize_key(wp_unslash($_GET['playlist_sort'])) : 'trending';
$sort = $sort === 'new' ? 'new' : 'trending';
$orderby = $sort === 'new' ? 'date' : 'meta_value_num';
$query_args = [
'post_type' => self::POST_TYPE,
'post_status' => 'publish',
'posts_per_page' => max(1, min(48, absint($atts['limit']))),
'orderby' => $orderby,
'order' => 'DESC',
'meta_query' => [['key' => '_me_cp_visibility', 'value' => 'public']],
];
if ($sort !== 'new') {
$query_args['meta_key'] = '_me_cp_vote_count';
}
$query = new \WP_Query($query_args);
echo '
';
echo '';
if ($query->have_posts()) {
echo '';
wp_reset_postdata();
} else {
echo '';
}
}
private function render_card($playlist_id, $rank) {
$cover = get_post_meta($playlist_id, '_me_cp_cover', true);
$items = $this->get_valid_items($playlist_id);
$votes = absint(get_post_meta($playlist_id, '_me_cp_vote_count', true));
$views = absint(get_post_meta($playlist_id, '_me_cp_view_count', true));
$url = add_query_arg('community_playlist', $playlist_id, remove_query_arg('community_playlist'));
$cover_posters = [];
if (!$cover) {
foreach (array_slice($items, 0, 4) as $item_id) {
$poster = \MovieEngine\Utils\Helpers::get_poster_url($item_id, 'medium');
if ($poster) {
$cover_posters[] = $poster;
}
}
}
echo '';
}
public static function get_personal_playlist_post_id($user_id, $playlist_id) {
$matches = get_posts([
'post_type' => self::POST_TYPE,
'post_status' => ['publish', 'draft', 'private', 'pending'],
'numberposts' => 1,
'fields' => 'ids',
'meta_query' => [
'relation' => 'AND',
['key' => '_me_cp_source_user_id', 'value' => absint($user_id)],
['key' => '_me_cp_source_playlist_id', 'value' => sanitize_text_field($playlist_id)],
],
]);
return $matches ? absint($matches[0]) : 0;
}
private static function find_legacy_personal_playlist_post_id($user_id, $playlist_name) {
$matches = get_posts([
'post_type' => self::POST_TYPE,
'post_status' => 'publish',
'author' => absint($user_id),
'title' => sanitize_text_field($playlist_name),
'numberposts' => 1,
'fields' => 'ids',
'meta_query' => [
'relation' => 'AND',
['key' => '_me_cp_visibility', 'value' => 'public'],
['key' => '_me_cp_source_playlist_id', 'compare' => 'NOT EXISTS'],
],
]);
return $matches ? absint($matches[0]) : 0;
}
public static function sync_personal_playlist($user_id, $playlist_id, $playlist = null) {
$community_id = self::get_personal_playlist_post_id($user_id, $playlist_id);
if (!is_array($playlist)) {
if (!$community_id) {
return 0;
}
wp_trash_post($community_id);
return $community_id;
}
$items = array_values(array_filter(array_map('absint', (array) ($playlist['items'] ?? [])), function ($item_id) {
return in_array(get_post_type($item_id), ['movie_engine_movie', 'movie_engine_series'], true) && get_post_status($item_id) === 'publish';
}));
if (!$community_id) {
$community_id = self::find_legacy_personal_playlist_post_id($user_id, $playlist['name'] ?? '');
}
if (!$community_id && !$items) {
return 0;
}
$description = sanitize_textarea_field($playlist['description'] ?? '');
if ($description === '' && $items) {
$description = sprintf(
/* translators: %d: number of playlist titles */
_n('A curated collection of %d title.', 'A curated collection of %d titles.', count($items), 'cine-movie-engine'),
count($items)
);
}
if (!$community_id) {
$community_id = wp_insert_post([
'post_type' => self::POST_TYPE,
'post_status' => 'publish',
'post_title' => sanitize_text_field($playlist['name'] ?? ''),
'post_author' => absint($user_id),
], true);
if (is_wp_error($community_id)) {
return 0;
}
update_post_meta($community_id, '_me_cp_vote_count', 0);
update_post_meta($community_id, '_me_cp_category', __('Community playlist', 'cine-movie-engine'));
update_post_meta($community_id, '_me_cp_language', '');
update_post_meta($community_id, '_me_cp_cover', '');
}
update_post_meta($community_id, '_me_cp_source_user_id', absint($user_id));
update_post_meta($community_id, '_me_cp_source_playlist_id', sanitize_text_field($playlist_id));
wp_update_post([
'ID' => $community_id,
'post_title' => sanitize_text_field($playlist['name'] ?? ''),
'post_content' => $description,
'post_status' => $items ? 'publish' : 'draft',
'post_author' => absint($user_id),
]);
update_post_meta($community_id, '_me_cp_items', $items);
update_post_meta($community_id, '_me_cp_visibility', $items ? 'public' : 'private');
return $community_id;
}
public static function migrate_personal_playlists_batch() {
if (get_option('me_community_personal_playlists_migrated')) {
return;
}
$batch_size = 100;
$offset = absint(get_option('me_community_personal_playlists_offset', 0));
$user_ids = get_users([
'fields' => 'ID',
'number' => $batch_size,
'offset' => $offset,
'meta_key' => '_movie_engine_playlists',
]);
foreach ($user_ids as $user_id) {
$playlists = \MovieEngine\Frontend\Users\UserLibrary::get_playlists($user_id);
foreach ($playlists as $playlist_id => $playlist) {
if (!empty($playlist['items'])) {
self::sync_personal_playlist($user_id, $playlist_id, $playlist);
}
}
}
if (count($user_ids) < $batch_size) {
update_option('me_community_personal_playlists_migrated', 1, false);
delete_option('me_community_personal_playlists_offset');
} else {
update_option('me_community_personal_playlists_offset', $offset + $batch_size, false);
}
}
private function render_detail($playlist_id) {
$playlist = get_post($playlist_id);
if (!$playlist || $playlist->post_type !== self::POST_TYPE || $playlist->post_status !== 'publish') {
echo '';
return;
}
$visibility = get_post_meta($playlist_id, '_me_cp_visibility', true) ?: 'public';
if ($visibility === 'private' && (int) $playlist->post_author !== get_current_user_id() && !current_user_can('edit_post', $playlist_id)) {
echo '';
return;
}
$items = $this->get_valid_items($playlist_id);
$votes = absint(get_post_meta($playlist_id, '_me_cp_vote_count', true));
$views = absint(get_post_meta($playlist_id, '_me_cp_view_count', true)) + 1;
update_post_meta($playlist_id, '_me_cp_view_count', $views);
$back_url = remove_query_arg('community_playlist');
$can_manage = is_user_logged_in() && (int) $playlist->post_author === get_current_user_id();
$cover = get_post_meta($playlist_id, '_me_cp_cover', true);
$hero_posters = [];
if (!$cover) {
foreach (array_slice($items, 0, 5) as $item_id) {
$poster = \MovieEngine\Utils\Helpers::get_poster_url($item_id, 'large');
if ($poster) {
$hero_posters[] = $poster;
}
}
}
$author_id = absint($playlist->post_author);
$author_avatar = get_user_meta($author_id, 'movie_engine_avatar_url', true);
if (!$author_avatar) {
$author_avatar = get_avatar_url($author_id, ['size' => 72]);
}
$social_whatsapp = preg_replace('/\D/', '', (string) get_user_meta($author_id, 'movie_engine_social_whatsapp', true));
$social_instagram = get_user_meta($author_id, 'movie_engine_social_instagram', true);
$social_facebook = get_user_meta($author_id, 'movie_engine_social_facebook', true);
$created_date = get_the_date(get_option('date_format'), $playlist_id);
echo '';
echo '