Skip to content

Commit b9b9a65

Browse files
committed
feat: enforce unique product per repository mapping and rename settings labels
- Add unique_field support to repeater: product_id dropdown disables already-selected options across sibling rows - Rename Default Status → Article Status, Duplicate Handling → Slug Conflict Handling, Deleted File Handling → When a File is Deleted; expand option labels for clarity - Hide importer submenu page from KB nav (accessed via Tools instead)
1 parent 8f142b4 commit b9b9a65

5 files changed

Lines changed: 62 additions & 13 deletions

File tree

includes/admin/class-plugin-importers.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ public function admin_menu(): void {
9696
$this->page_slug,
9797
array( $this, 'render_page' )
9898
);
99+
100+
remove_submenu_page( 'edit.php?post_type=wz_knowledgebase', $this->page_slug );
99101
}
100102

101103
/**

includes/admin/class-settings.php

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,7 @@ public static function settings_github() {
946946
'add_button_text' => esc_html__( 'Add Repository', 'knowledgebase' ),
947947
'live_update_field' => 'product_id',
948948
'live_update_field_options' => $product_options,
949+
'unique_field' => 'product_id',
949950
'pro' => true,
950951
'fields' => array(
951952
'product_id' => array(
@@ -992,38 +993,38 @@ public static function settings_github() {
992993
'size' => 'large',
993994
),
994995
'default_status' => array(
995-
'name' => esc_html__( 'Default Status', 'knowledgebase' ),
996-
'desc' => esc_html__( 'Override the frontmatter status when set. Leave on "No override" to respect frontmatter.', 'knowledgebase' ),
996+
'name' => esc_html__( 'Article Status', 'knowledgebase' ),
997+
'desc' => esc_html__( 'Force all imported articles to this status, overriding any status set in frontmatter. Leave on "Use frontmatter" to let each article control its own status.', 'knowledgebase' ),
997998
'id' => 'default_status',
998999
'type' => 'select',
9991000
'default' => '',
10001001
'options' => array(
1001-
'' => esc_html__( 'No override', 'knowledgebase' ),
1002+
'' => esc_html__( 'Use frontmatter', 'knowledgebase' ),
10021003
'publish' => esc_html__( 'Published', 'knowledgebase' ),
10031004
'draft' => esc_html__( 'Draft', 'knowledgebase' ),
10041005
),
10051006
),
10061007
'duplicate_handling' => array(
1007-
'name' => esc_html__( 'Duplicate Handling', 'knowledgebase' ),
1008-
'desc' => esc_html__( 'What to do when an imported slug already exists in a non-GitHub article.', 'knowledgebase' ),
1008+
'name' => esc_html__( 'Slug Conflict Handling', 'knowledgebase' ),
1009+
'desc' => esc_html__( 'What to do when a GitHub file\'s slug matches an article that was not imported from GitHub.', 'knowledgebase' ),
10091010
'id' => 'duplicate_handling',
10101011
'type' => 'select',
10111012
'default' => 'overwrite',
10121013
'options' => array(
1013-
'overwrite' => esc_html__( 'Overwrite existing', 'knowledgebase' ),
1014-
'skip' => esc_html__( 'Skip', 'knowledgebase' ),
1015-
'create_new' => esc_html__( 'Create new with suffixed slug', 'knowledgebase' ),
1014+
'overwrite' => esc_html__( 'Overwrite the existing article', 'knowledgebase' ),
1015+
'skip' => esc_html__( 'Skip — leave the existing article unchanged', 'knowledgebase' ),
1016+
'create_new' => esc_html__( 'Create a new article with a suffixed slug', 'knowledgebase' ),
10161017
),
10171018
),
10181019
'delete_removed' => array(
1019-
'name' => esc_html__( 'Deleted File Handling', 'knowledgebase' ),
1020-
'desc' => esc_html__( 'What to do when a tracked file is removed from the repository.', 'knowledgebase' ),
1020+
'name' => esc_html__( 'When a File is Deleted', 'knowledgebase' ),
1021+
'desc' => esc_html__( 'What to do with the linked article when its source file is removed from the repository.', 'knowledgebase' ),
10211022
'id' => 'delete_removed',
10221023
'type' => 'select',
10231024
'default' => 'draft',
10241025
'options' => array(
1025-
'draft' => esc_html__( 'Set to draft', 'knowledgebase' ),
1026-
'delete' => esc_html__( 'Permanently delete', 'knowledgebase' ),
1026+
'draft' => esc_html__( 'Switch to draft', 'knowledgebase' ),
1027+
'delete' => esc_html__( 'Delete permanently', 'knowledgebase' ),
10271028
),
10281029
),
10291030
'enable_push' => array(

includes/admin/settings/class-settings-form.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,9 @@ public function callback_repeater( $args ) {
10161016
<?php if ( ! empty( $live_update_options ) ) : ?>
10171017
data-live-update-field-options="<?php echo esc_attr( wp_json_encode( $live_update_options ) ); ?>"
10181018
<?php endif; ?>
1019+
<?php if ( ! empty( $args['unique_field'] ) ) : ?>
1020+
data-unique-field="<?php echo esc_attr( $args['unique_field'] ); ?>"
1021+
<?php endif; ?>
10191022
<?php echo $attributes; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
10201023

10211024
<div class="<?php echo esc_attr( $args['id'] ); ?>-items wz-repeater-items">

includes/admin/settings/js/settings-admin-scripts.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,49 @@ jQuery(document).ready(function ($) {
139139
}
140140
});
141141

142+
// Enforce unique selection across rows for the field named in data-unique-field.
143+
var uniqueField = wrapper.data('unique-field') || '';
144+
function syncUniqueSelects() {
145+
if (!uniqueField) {
146+
return;
147+
}
148+
var $selects = itemsContainer.find('.wz-repeater-item select[name$="[fields][' + uniqueField + ']"]');
149+
var usedValues = {};
150+
$selects.each(function () {
151+
var val = $(this).val();
152+
if (val) {
153+
usedValues[val] = true;
154+
}
155+
});
156+
$selects.each(function () {
157+
var $sel = $(this);
158+
var ownVal = $sel.val();
159+
$sel.find('option').each(function () {
160+
var optVal = $(this).val();
161+
if (!optVal) {
162+
return;
163+
}
164+
$(this).prop('disabled', optVal !== ownVal && usedValues[optVal]);
165+
});
166+
});
167+
}
168+
169+
if (uniqueField) {
170+
syncUniqueSelects();
171+
wrapper.on('change', '.wz-repeater-item select[name$="[fields][' + uniqueField + ']"]', function () {
172+
syncUniqueSelects();
173+
});
174+
wrapper.on('click', '.remove-item', function () {
175+
// Sync after DOM removal; removal handler fires before remove, so defer.
176+
setTimeout(syncUniqueSelects, 0);
177+
});
178+
document.addEventListener('wz:repeater-item-added', function (e) {
179+
if (wrapper.get(0).contains(e.detail.container)) {
180+
syncUniqueSelects();
181+
}
182+
});
183+
}
184+
142185
// Live update repeater title when the specified field changes.
143186
// Handles text inputs (input event), selects (change event, uses option text),
144187
// and TomSelect-enhanced inputs (change event, uses displayed text or value).

includes/admin/settings/js/settings-admin-scripts.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)