Add feature to unlock selected posts per user

This commit is contained in:
Tetrakern 2024-05-08 23:33:12 +02:00
parent 4ac75cbd2f
commit ffbf97644b
11 changed files with 488 additions and 8 deletions

View File

@ -88,6 +88,7 @@ Fires within the Fictioneer user profile section in the WordPress `wp-admin/prof
* `fictioneer_admin_profile_fields_flags( $profile_user )` User flags. Priority 6.
* `fictioneer_admin_profile_fields_oauth( $profile_user )` User OAuth connections. Priority 7.
* `fictioneer_admin_profile_fields_data_nodes( $profile_user )` User data nodes. Priority 8.
* `fictioneer_admin_profile_post_unlocks( $profile_user )` - Unlock password-protected posts. Priority 9.
* `fictioneer_admin_profile_moderation( $profile_user )` Moderation flags and message. Priority 10.
* `fictioneer_admin_profile_author( $profile_user )` Author page select, support message, and support links. Priority 20.
* `fictioneer_admin_profile_oauth( $profile_user )` OAuth 2.0 account binding IDs. Priority 30.

View File

@ -221,13 +221,13 @@ Fictioneer customizes WordPress by using as many standard action and filter hook
| `do_meta_boxes` | `fictioneer_remove_custom_fields_meta_boxes`
| `edit_comment` | `fictioneer_comment_edit`, `fictioneer_edit_comment`
| `edit_user_profile` | `fictioneer_custom_profile_fields`
| `edit_user_profile_update` | `fictioneer_update_admin_user_profile`, `fictioneer_update_my_user_profile`
| `edit_user_profile_update` | `fictioneer_update_admin_user_profile`, `fictioneer_update_my_user_profile`, `fictioneer_update_admin_unlocked_posts`
| `get_header` | `fictioneer_maintenance_mode`
| `init` | `fictioneer_add_character_taxonomy`, `fictioneer_add_content_warning_taxonomy`, `fictioneer_add_epub_download_endpoint`, `fictioneer_add_fandom_taxonomy`, `fictioneer_add_genre_taxonomy`, `fictioneer_add_logout_endpoint`, `fictioneer_add_oauth2_endpoint`, `fictioneer_restrict_admin_panel`, `fictioneer_disable_heartbeat`, `fictioneer_fcn_chapter_post_type`, `fictioneer_fcn_collection_post_type`, `fictioneer_fcn_recommendation_post_type`, `fictioneer_fcn_story_post_type`, `fictioneer_modify_allowed_tags`, `fictioneer_story_rss`, `fictioneer_remove_custom_fields_supports`, `fictioneer_add_sitemap_rewrite_rule`, `fictioneer_fast_ajax`
| `login_form` | `fictioneer_after_logout_cleanup`
| `login_head` | `fictioneer_wp_login_scripts`
| `manage_comments_custom_column` | `fictioneer_add_comments_report_column_content`
| `personal_options_update` | `fictioneer_update_admin_user_profile`, `fictioneer_update_my_user_profile`
| `personal_options_update` | `fictioneer_update_admin_user_profile`, `fictioneer_update_my_user_profile`, `fictioneer_update_admin_unlocked_posts`
| `pre_get_posts` | `fictioneer_extend_search_query`, `fictioneer_read_others_files`, `fictioneer_read_others_files_list_view`, `fictioneer_filter_chapters_by_story`
| `private_to_draft` | `fictioneer_chapter_to_draft`
| `profile_update` | `fictioneer_on_profile_change`
@ -242,7 +242,7 @@ Fictioneer customizes WordPress by using as many standard action and filter hook
| `trashed_post` | `fictioneer_refresh_post_caches`, `fictioneer_track_chapter_and_story_updates`, `fictioneer_update_modified_date_on_story_for_chapter`, `fictioneer_purge_transients`, `fictioneer_remove_chapter_from_story`
| `untrash_post` | `fictioneer_refresh_post_caches`, `fictioneer_track_chapter_and_story_updates`, `fictioneer_update_modified_date_on_story_for_chapter`, `fictioneer_purge_transients`
| `update_option_*` | `fictioneer_update_option_disable_extended_chapter_list_meta_queries`, `fictioneer_update_option_disable_extended_story_list_meta_queries`
| `wp_ajax_*` | `fictioneer_ajax_clear_my_checkmarks`, `fictioneer_ajax_clear_my_comments`, `fictioneer_ajax_clear_my_comment_subscriptions`, `fictioneer_ajax_clear_my_follows`, `fictioneer_ajax_clear_my_reminders`, `fictioneer_ajax_delete_epub`, `fictioneer_ajax_delete_my_account`, `fictioneer_ajax_delete_my_comment`, `fictioneer_ajax_edit_comment`, `fictioneer_ajax_get_avatar`, `fictioneer_ajax_get_comment_form`, `fictioneer_ajax_get_comment_section`, `fictioneer_ajax_get_finished_checkmarks_list`, `fictioneer_ajax_get_follows_list`, `fictioneer_ajax_get_follows_notifications`, `fictioneer_ajax_get_reminders_list`, `fictioneer_ajax_mark_follows_read`, `fictioneer_ajax_moderate_comment`, `fictioneer_ajax_report_comment`, `fictioneer_ajax_save_bookmarks`, `fictioneer_ajax_set_checkmark`, `fictioneer_ajax_submit_comment`, `fictioneer_ajax_toggle_follow`, `fictioneer_ajax_toggle_reminder`, `fictioneer_ajax_unset_my_oauth`, `fictioneer_ajax_get_user_data`, `fictioneer_ajax_get_auth`, `fictioneer_ajax_purge_schema`, `fictioneer_ajax_purge_all_schemas`, `fictioneer_ajax_reset_theme_colors`
| `wp_ajax_*` | `fictioneer_ajax_clear_my_checkmarks`, `fictioneer_ajax_clear_my_comments`, `fictioneer_ajax_clear_my_comment_subscriptions`, `fictioneer_ajax_clear_my_follows`, `fictioneer_ajax_clear_my_reminders`, `fictioneer_ajax_delete_epub`, `fictioneer_ajax_delete_my_account`, `fictioneer_ajax_delete_my_comment`, `fictioneer_ajax_edit_comment`, `fictioneer_ajax_get_avatar`, `fictioneer_ajax_get_comment_form`, `fictioneer_ajax_get_comment_section`, `fictioneer_ajax_get_finished_checkmarks_list`, `fictioneer_ajax_get_follows_list`, `fictioneer_ajax_get_follows_notifications`, `fictioneer_ajax_get_reminders_list`, `fictioneer_ajax_mark_follows_read`, `fictioneer_ajax_moderate_comment`, `fictioneer_ajax_report_comment`, `fictioneer_ajax_save_bookmarks`, `fictioneer_ajax_set_checkmark`, `fictioneer_ajax_submit_comment`, `fictioneer_ajax_toggle_follow`, `fictioneer_ajax_toggle_reminder`, `fictioneer_ajax_unset_my_oauth`, `fictioneer_ajax_get_user_data`, `fictioneer_ajax_get_auth`, `fictioneer_ajax_purge_schema`, `fictioneer_ajax_purge_all_schemas`, `fictioneer_ajax_reset_theme_colors`, `fictioneer_ajax_search_posts_to_unlock`
| `wp_ajax_nopriv_*` | `fictioneer_ajax_get_comment_form`, `fictioneer_ajax_get_comment_section`, `fictioneer_ajax_submit_comment`, `fictioneer_ajax_get_auth`
| `wp_before_admin_bar_render` | `fictioneer_remove_admin_bar_links`, `fictioneer_remove_dashboard_from_admin_bar`, `fictioneer_remove_comments_from_admin_bar`
| `wp_dashboard_setup` | `fictioneer_remove_dashboard_widgets`

View File

@ -596,6 +596,7 @@ The integrated role manager to add and, edit, and remove roles. Not the most sop
* **Read Others Files:** Allows you to see uploaded files from *other* users.
* **Edit Others Files:** Allows you to edit uploaded files from *other* users.
* **Delete Others Files:** Allows you to delete uploaded files from *other* users.
* **Unlock Posts:** Allows you to unlock selected password-protected posts for users.
* **Manage {Taxonomy}:** Lets you see the overview list table of the taxonomy.
* **Assign {Taxonomy}:** Lets you assign the taxonomy to your posts.
* **Edit {Taxonomy}:** Lets you create and edit taxonomies of this type.

File diff suppressed because one or more lines are too long

View File

@ -34,7 +34,8 @@ define(
'fcn_ignore_page_passwords',
'fcn_ignore_fcn_story_passwords',
'fcn_ignore_fcn_chapter_passwords',
'fcn_ignore_fcn_collection_passwords'
'fcn_ignore_fcn_collection_passwords',
'fcn_unlock_posts'
)
);
@ -95,9 +96,10 @@ function fictioneer_initialize_roles( $force = false ) {
}
// If this capability is missing, the roles need to be updated
if ( $administrator && ! in_array( 'fcn_custom_epub_upload', array_keys( $administrator->capabilities ) ) ) {
if ( $administrator && ! in_array( 'fcn_unlock_posts', array_keys( $administrator->capabilities ) ) ) {
get_role( 'administrator' )->add_cap( 'fcn_custom_page_header' );
get_role( 'administrator' )->add_cap( 'fcn_custom_epub_upload' );
get_role( 'administrator' )->add_cap( 'fcn_unlock_posts' );
if ( $editor = get_role( 'editor' ) ) {
$editor->add_cap( 'fcn_custom_page_header' );
@ -475,6 +477,17 @@ function fictioneer_bypass_password( $required, $post ) {
break;
}
// Check user unlocks
if ( $user && $required ) {
$story_id = get_post_meta( $post->ID, 'fictioneer_chapter_story', true );
$unlocks = get_user_meta( $user->ID, 'fictioneer_post_unlocks', true ) ?: [];
$unlocks = is_array( $unlocks ) ? $unlocks : [];
if ( $unlocks && array_intersect( [ $post->ID, $story_id ], $unlocks ) ) {
$required = false;
}
}
// Check Patreon tiers
if ( $user && $required && get_option( 'fictioneer_enable_patreon_locks' ) && fictioneer_patreon_tiers_valid( $user ) ) {
$patreon_post_data = fictioneer_get_patreon_data( $post );

View File

@ -73,6 +73,7 @@ $admin_caps = array(
'create_users',
'edit_users',
'remove_users',
'fcn_unlock_posts',
'switch_themes',
'edit_theme_options',
'edit_themes',

View File

@ -732,6 +732,140 @@ function fictioneer_admin_profile_fields_data_nodes( $profile_user ) {
}
add_action( 'fictioneer_admin_user_sections', 'fictioneer_admin_profile_fields_data_nodes', 8 );
// =============================================================================
// SHOW UNLOCKS SECTION
// =============================================================================
/**
* Unlock password-protected posts for user
*
* @since 5.16.0
*
* @param WP_User $profile_user The profile user object. Not necessarily the one
* currently editing the profile!
*/
function fictioneer_admin_profile_post_unlocks( $profile_user ) {
// Check permissions
if ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'fcn_unlock_posts' ) ) {
return;
}
if ( ! current_user_can( 'edit_users' ) ) {
return;
}
// Setup
$unlocks = get_user_meta( $profile_user->ID, 'fictioneer_post_unlocks', true ) ?: [];
$unlocks = is_array( $unlocks ) ? $unlocks : [];
// Post data
$posts = [];
$post_type_labels = array(
'post' => _x( 'Post', 'Post type label.', 'fictioneer' ),
'page' => _x( 'Page', 'Post type label.', 'fictioneer' ),
'fcn_story' => _x( 'Story', 'Post type label.', 'fictioneer' ),
'fcn_chapter' => _x( 'Chapter', 'Post type label.', 'fictioneer' ),
'fcn_collection' => _x( 'Collection', 'Post type label.', 'fictioneer' ),
'fcn_recommendation' => _x( 'Rec', 'Post type label.', 'fictioneer' )
);
if ( $unlocks ) {
$query = new WP_Query(
array(
'post_type'=> 'any',
'post_status'=> 'any',
'posts_per_page' => -1,
'post__in' => $unlocks,
'update_post_meta_cache' => false, // Improve performance
'update_post_term_cache' => false, // Improve performance
'no_found_rows' => true // Improve performance
)
);
$posts = $query->posts;
// Prime author cache
if ( function_exists( 'update_post_author_caches' ) ) {
update_post_author_caches( $posts );
}
}
// Start HTML ---> ?>
<tr class="user-unlock-posts-wrap">
<th><?php _e( 'Unlock Posts', 'fictioneer' ); ?></th>
<td class="unlock-posts" data-controller="fcn-unlock-posts">
<fieldset>
<?php wp_nonce_field( 'search_posts', 'unlock_posts_nonce' ); ?>
<template data-target="fcn-unlock-posts-item-template">
<div class="unlock-posts__item" title="" data-target="fcn-unlock-posts-item" data-post-id="0">
<input type="hidden" name="fictioneer_post_unlocks[]" value="">
<span class="unlock-posts__item-title"></span>
<span class="unlock-posts__item-meta"></span>
<button type="button" class="unlock-posts__delete" data-target="fcn-unlock-posts-delete"><span class="dashicons dashicons-no-alt"></span></button>
</div>
</template>
<p>
<?php _e( 'Allow the user to ignore the password of selected posts. Chapters inherit the unlock from the story.', 'fictioneer' ); ?>
</p>
<div class="unlock-posts__search">
<div class="unlock-posts__search-form">
<input type="search" class="unlock-posts__search-input regular-text" name="unlock_search" data-target="fcn-unlock-posts-search" placeholder="<?php _e( 'Search posts to unlock…', 'fictioneer' ); ?>">
<select class="unlock-posts__search-select" name="unlock_select_type" data-target="fcn-unlock-posts-select" >
<option value="0" selected><?php _e( 'Any', 'fictioneer' ); ?></option>
<option value="post"><?php _e( 'Post', 'fictioneer' ); ?></option>
<option value="page"><?php _e( 'Page', 'fictioneer' ); ?></option>
<option value="fcn_story"><?php _e( 'Story', 'fictioneer' ); ?></option>
<option value="fcn_chapter"><?php _e( 'Chapter', 'fictioneer' ); ?></option>
<option value="fcn_collection"><?php _e( 'Collection', 'fictioneer' ); ?></option>
<option value="fcn_recommendation"><?php _e( 'Recommendation', 'fictioneer' ); ?></option>
</select>
<i class="fa-solid fa-spinner fa-spin" style="--fa-animation-duration: .8s;"></i>
</div>
<div class="unlock-posts__search-results" data-target="fcn-unlock-posts-search-results"></div>
</div>
<div class="unlock-posts__unlocked-posts" data-target="fcn-unlock-posts-selected">
<input type="hidden" name="fictioneer_post_unlocks" value="0">
<?php foreach ( $posts as $post ) : ?>
<?php
$type = $post_type_labels[ $post->post_type ] ?? '';
$title = fictioneer_get_safe_title( $post->ID );
$item_title = sprintf(
_x( 'Author: %s | Title: %s', 'Unlock post item.', 'fictioneer' ),
get_the_author_meta( 'display_name', $post->post_author ) ?: __( 'n/a', 'fictioneer' ),
$title
);
?>
<div class="unlock-posts__item" title="<?php echo esc_attr( $item_title ); ?>" data-target="fcn-unlock-posts-item" data-post-id="<?php echo $post->ID; ?>">
<input type="hidden" name="fictioneer_post_unlocks[]" value="<?php echo $post->ID; ?>">
<span class="unlock-posts__item-title"><?php echo $title; ?></span>
<span class="unlock-posts__item-meta"><?php printf( _x( '(%s | %s)', 'Unlock post item meta: Type | ID.', 'fictioneer' ), $type, $post->ID ); ?></span>
<button type="button" class="unlock-posts__delete" data-target="fcn-unlock-posts-delete"><span class="dashicons dashicons-no-alt"></span></button>
</div>
<?php endforeach; ?>
</div>
</fieldset>
</td>
</tr>
<?php // <--- End HTML
}
add_action( 'fictioneer_admin_user_sections', 'fictioneer_admin_profile_post_unlocks', 9 );
// =============================================================================
// SHOW MODERATION SECTION
// =============================================================================

View File

@ -1167,4 +1167,135 @@ function fictioneer_ajax_get_user_data() {
}
add_action( 'wp_ajax_fictioneer_ajax_get_user_data', 'fictioneer_ajax_get_user_data' );
?>
// =============================================================================
// UNLOCK POSTS - AJAX
// =============================================================================
/**
* Searches for posts to unlock via AJAX
*
* @since 5.16.0
*/
function fictioneer_ajax_search_posts_to_unlock() {
// Validations
$user = fictioneer_get_validated_ajax_user( 'nonce', 'search_posts' );
if ( ! $user ) {
wp_send_json_error( array( 'error' => __( 'Request did not pass validation.', 'fictioneer' ) ) );
}
if ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'fcn_unlock_posts' ) ) {
wp_send_json_error( array( 'error' => __( 'Insufficient permissions.', 'fictioneer' ) ) );
}
// Setup
$search = sanitize_text_field( $_REQUEST['search'] ?? '' );
$type = sanitize_key( $_REQUEST['type'] ?? '' );
$output = [];
// Empty?
if ( empty( $search ) ) {
wp_send_json_success( array( 'html' => '' ) );
}
// Query
$posts = new WP_Query(
array(
'post_type' => $type ?: ['post', 'page', 'fcn_story', 'fcn_chapter', 'fcn_collection', 'fcn_recommendation'],
'post_status' => 'any',
'orderby' => 'date',
'order' => 'desc',
'posts_per_page' => 20,
's' => $search,
'update_post_meta_cache' => false, // Improve performance
'update_post_term_cache' => false, // Improve performance
'no_found_rows' => true // Improve performance
)
);
// Prime author cache
if ( function_exists( 'update_post_author_caches' ) ) {
update_post_author_caches( $posts->posts );
}
// Search result
if ( ! $posts->posts ) {
wp_send_json_success( array( 'html' => '' ) );
} else {
// Labels
$post_type_labels = array(
'post' => _x( 'Post', 'Post type label.', 'fictioneer' ),
'page' => _x( 'Page', 'Post type label.', 'fictioneer' ),
'fcn_story' => _x( 'Story', 'Post type label.', 'fictioneer' ),
'fcn_chapter' => _x( 'Chapter', 'Post type label.', 'fictioneer' ),
'fcn_collection' => _x( 'Collection', 'Post type label.', 'fictioneer' ),
'fcn_recommendation' => _x( 'Rec', 'Post type label.', 'fictioneer' )
);
foreach ( $posts->posts as $post ) {
$type = $post_type_labels[ $post->post_type ] ?? '';
$title = fictioneer_get_safe_title( $post->ID );
$item_title = sprintf(
_x( 'Author: %s | Title: %s', 'Unlock post item.', 'fictioneer' ),
get_the_author_meta( 'display_name', $post->post_author ) ?: __( 'n/a', 'fictioneer' ),
$title
);
$output[] = '<div class="unlock-posts__item _searched" title="' . esc_attr( $item_title ) . '" data-target="fcn-unlock-posts-search-item" data-post-id="' . $post->ID . '"><span class="unlock-posts__item-title">' . $title . '</span> <span class="unlock-posts__item-meta">' . sprintf( _x( '(%s | %s)', 'Unlock post item meta: Type | ID.', 'fictioneer' ), $type, $post->ID ) . '</span></div>';
}
}
// Response
wp_send_json_success( array( 'html' => implode( '', $output ) ) );
}
add_action( 'wp_ajax_fictioneer_ajax_search_posts_to_unlock', 'fictioneer_ajax_search_posts_to_unlock' );
/**
* Update unlocked posts for user
*
* @since 5.16.0
*
* @param int $updated_user_id The ID of the updated user.
*/
function fictioneer_update_admin_unlocked_posts( $updated_user_id ) {
// Check permissions
if ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'fcn_unlock_posts' ) ) {
return;
}
if ( ! current_user_can( 'edit_users' ) ) {
return;
}
// Setup
$admin_protection = fictioneer_is_admin( $updated_user_id ) && get_current_user_id() !== $updated_user_id;
// Check if protected, sanitize, validate, and update...
if ( ! $admin_protection && isset( $_POST['fictioneer_post_unlocks'] ) ) {
$post_ids = $_POST['fictioneer_post_unlocks'];
$post_ids = is_array( $post_ids ) ? $post_ids : [];
$post_ids = array_map( 'absint', $post_ids );
$post_ids = array_unique( $post_ids );
$query = new WP_Query(
array(
'post_type'=> ['post', 'page', 'fcn_story', 'fcn_chapter', 'fcn_collection', 'fcn_recommendation'],
'post_status'=> 'any',
'posts_per_page' => -1,
'post__in' => $post_ids ?: [0],
'fields' => 'ids',
'update_post_meta_cache' => false, // Improve performance
'update_post_term_cache' => false, // Improve performance
'no_found_rows' => true // Improve performance
)
);
$post_ids = array_map( 'strval', $query->posts );
fictioneer_update_user_meta( $updated_user_id, 'fictioneer_post_unlocks', $post_ids );
}
}
add_action( 'personal_options_update', 'fictioneer_update_admin_unlocked_posts' );
add_action( 'edit_user_profile_update', 'fictioneer_update_admin_unlocked_posts' );

2
js/admin.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1069,3 +1069,133 @@ function fcn_addRelationship(source, destination) {
// Disable
source.classList.add('disabled');
}
// =============================================================================
// UNLOCK POSTS
// =============================================================================
var /** @type {timeoutID} */ fcn_unlockPostsTimer;
_$('[data-controller="fcn-unlock-posts"]')?.addEventListener('click', event => {
// Evaluate clicks on the interface
fcn_unlockPostsHandleClicks(event.target.closest('[data-target]'));
});
_$('[data-target="fcn-unlock-posts-search"]')?.addEventListener('input', () => {
// Clear previous timer (if any)
clearTimeout(fcn_unlockPostsTimer);
// Trigger search after delay
fcn_unlockPostsTimer = setTimeout(() => {
fcn_unlockPostsSearch();
}, 800);
});
_$('[data-target="fcn-unlock-posts-select"]')?.addEventListener('input', () => {
// Clear previous timer (if any)
clearTimeout(fcn_unlockPostsTimer);
// Start search
fcn_unlockPostsSearch();
});
/**
* Handle clicks on the Unlock Posts interface.
*
* @since 5.16.0
* @param {HTMLElement} target - The element that was clicked.
*/
function fcn_unlockPostsHandleClicks(target) {
if (!target) {
return;
}
// Delegate or perform action...
switch (target.dataset.target) {
case 'fcn-unlock-posts-delete':
target.closest('[data-target="fcn-unlock-posts-item"').remove();
break;
case 'fcn-unlock-posts-search-item':
fcn_unlockPostsAdd(target);
break;
}
}
/**
* Search posts to unlock.
*
* @since 5.16.0
*/
function fcn_unlockPostsSearch() {
// Setup
const container = _$('.unlock-posts');
const search = _$('[data-target="fcn-unlock-posts-search"]');
const results = _$('[data-target="fcn-unlock-posts-search-results"]');
const selected = _$('[data-target="fcn-unlock-posts-selected"]');
// Abort if...
if (!container || !search || (!search.value && !_$('[data-target="fcn-unlock-posts-search-item"]'))) {
return;
}
// Payload
const payload = {
'action': 'fictioneer_ajax_search_posts_to_unlock',
'search': search.value,
'type': _$('[data-target="fcn-unlock-posts-select"]')?.value || 'any',
'nonce': container.querySelector('[name="unlock_posts_nonce"]').value
};
// Indicate process
container.classList.add('ajax-in-progress');
// Request
fcn_ajaxPost(payload)
.then(response => {
if (response.success) {
results.innerHTML = response.data.html;
} else {
results.innerHTML = response.data.error;
}
})
.catch(error => {
results.innerHTML = error;
})
.then(() => {
container.classList.remove('ajax-in-progress');
results.querySelectorAll('[data-target="fcn-unlock-posts-search-item"]').forEach(item => {
if (selected.querySelector(`[data-post-id="${item.dataset.postId}"]`)) {
item.remove();
}
});
});
}
/**
* Add post items to the Unlock Posts selection.
*
* @since 5.16.0
* @param {HTMLElement} target - The element that was clicked.
*/
function fcn_unlockPostsAdd(target) {
// Setup
const template = _$('[data-target="fcn-unlock-posts-item-template"]').content.cloneNode(true);
const item = template.querySelector('.unlock-posts__item');
// Build node
item.dataset.postId = target.dataset.postId;
item.title = target.title;
item.querySelector('input[type="hidden"]').value = target.dataset.postId;
item.querySelector('.unlock-posts__item-title').innerText = target.querySelector('.unlock-posts__item-title').innerText;
item.querySelector('.unlock-posts__item-meta').innerText = target.querySelector('.unlock-posts__item-meta').innerText;
// Append
_$('[data-target="fcn-unlock-posts-selected"]').appendChild(template);
// Remove search node
target.remove();
}

View File

@ -1012,3 +1012,72 @@ td.comment {
}
}
}
.unlock-posts {
&__unlocked-posts,
&__search-results {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 10px;
&:not(:empty) {
margin-top: 15px;
}
}
&__search-form {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 10px 5px;
margin-top: 15px;
}
&__item {
cursor: default;
display: flex;
align-items: center;
gap: 4px;
background: #ddd;
font-size: 12px;
line-height: 1.2;
padding-left: 9px;
border: 1px solid #ddd;
border-radius: 3px;
&:hover {
background: #d6d6d6;
}
&._searched {
cursor: pointer;
background: transparent;
padding: 7px;
border: 1px dashed #ccc;
&:hover {
background: rgb(0 0 0 / 3%);
}
}
> span:first-of-type {
white-space: nowrap;
text-overflow: ellipsis;
max-width: 160px;
overflow: hidden;
}
}
&__delete {
cursor: pointer;
appearance: none;
background: transparent;
padding: 4px;
border: none;
}
&:not(.ajax-in-progress) .fa-spinner {
display: none;
}
}