Skip to content

[ELI_38] Combine 'Fix selected' and 'Replace by Pattern' in one button. #58

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
/**
* Validates external links via sending HEAD requests concurrently using {@link PoolingHttpClientConnectionManager}
*/
@Component(service = LinkResolver.class)
@Component(service = {LinkResolver.class, ExternalLinkResolverImpl.class}, immediate = true)
@Designate(ocd = ExternalLinkResolverImpl.Configuration.class)
public class ExternalLinkResolverImpl implements LinkResolver {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
/**
* Validates external links via sending HEAD requests concurrently using {@link PoolingHttpClientConnectionManager}
*/
@Component(service = LinkResolver.class)
@Component(service = LinkResolver.class )
@Designate(ocd = InternalLinkResolverImpl.Config.class)
public class InternalLinkResolverImpl implements LinkResolver {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@ public class ReplaceByPatternServlet extends SlingAllMethodsServlet {
private static final String DRY_RUN_PARAM = "isDryRun";
private static final String BACKUP_PARAM = "isBackup";
private static final String OUTPUT_AS_CSV_PARAM = "isOutputAsCsv";
private static final String ADVANCED_MODE_PARAM = "advancedMode";
private static final String ITEMS_COUNT_RESP_PARAM = "updatedItemsCount";
private static final String BACKUP_PACKAGE_GROUP = "EToolbox_Link_Inspector";
private static final String BACKUP_PACKAGE_NAME = "replace_by_pattern_backup_%s";
private static final String BACKUP_PACKAGE_VERSION = "1.0";
private static final String PAGE_PARAM = "page";
private static final String SELECTED_PARAM = "selected";

private static final String[] CSV_COLUMNS = {
Expand Down Expand Up @@ -136,18 +136,24 @@ void activate(Configuration configuration) {
protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) {
String linkPattern = ServletUtil.getRequestParamString(request, LINK_PATTERN_PARAM);
String replacement = ServletUtil.getRequestParamString(request, REPLACEMENT_PARAM);
boolean isAdvancedMode = ServletUtil.getRequestParamBoolean(request, ADVANCED_MODE_PARAM);
boolean isDryRun = ServletUtil.getRequestParamBoolean(request, DRY_RUN_PARAM);
boolean isBackup = ServletUtil.getRequestParamBoolean(request, BACKUP_PARAM);
boolean isOutputAsCsv = ServletUtil.getRequestParamBoolean(request, OUTPUT_AS_CSV_PARAM);
List<String> selectedItems = ServletUtil.getRequestParamStringList(request, SELECTED_PARAM);

if (StringUtils.isAnyBlank(linkPattern, replacement)) {
if (StringUtils.isBlank(replacement)) {
response.setStatus(HttpStatus.SC_BAD_REQUEST);
LOG.warn("Any (or all) request params are empty: linkPattern - {}, replacement - {}",
linkPattern, replacement);
LOG.warn("Request params is empty: replacement - {}", replacement);
return;
}
if (linkPattern.equals(replacement)) {

if (isAdvancedMode && StringUtils.isBlank(linkPattern)) {
response.setStatus(HttpStatus.SC_BAD_REQUEST);
LOG.warn("Request params is empty: linkPattern - {}", linkPattern);
return;
}
if (isAdvancedMode && linkPattern.equals(replacement)) {
response.setStatus(HttpStatus.SC_ACCEPTED);
LOG.debug("linkPattern and replacement are equal, no processing is required");
return;
Expand All @@ -162,7 +168,7 @@ protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse
.format("%s@%s", gridResource.getResourcePath(), gridResource.getPropertyName())))
.collect(Collectors.toList());
List<UpdatedItem> updatedItems =
processResources(filteredGridResources, isDryRun, isBackup, linkPattern, replacement, resourceResolver);
processResources(filteredGridResources, isDryRun, isBackup, isAdvancedMode, linkPattern, replacement, resourceResolver);
if (CollectionUtils.isEmpty(updatedItems)) {
LOG.info("No links were updated, linkPattern: {}, replacement: {}", linkPattern, replacement);
response.setStatus(HttpStatus.SC_NO_CONTENT);
Expand All @@ -184,6 +190,7 @@ protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse
private List<UpdatedItem> processResources(Collection<GridResource> gridResources,
boolean isDryRun,
boolean isBackup,
boolean isAdvancedMode,
String linkPattern,
String replacement,
ResourceResolver resourceResolver)
Expand All @@ -197,7 +204,7 @@ private List<UpdatedItem> processResources(Collection<GridResource> gridResource
if (isBackup && !isDeactivated) {
createBackupPackage(filteredGridResources, session.get());
}
return replaceByPattern(filteredGridResources, isDryRun, linkPattern, replacement, resourceResolver);
return replaceByPattern(filteredGridResources, isDryRun, isAdvancedMode, linkPattern, replacement, resourceResolver);
}

private List<GridResource> filterGridResources(Collection<GridResource> gridResources,
Expand All @@ -216,6 +223,7 @@ private List<GridResource> filterGridResources(Collection<GridResource> gridReso

private List<UpdatedItem> replaceByPattern(Collection<GridResource> gridResources,
boolean isDryRun,
boolean isAdvancedMode,
String linkPattern,
String replacement,
ResourceResolver resourceResolver) throws PersistenceException {
Expand All @@ -227,7 +235,7 @@ private List<UpdatedItem> replaceByPattern(Collection<GridResource> gridResource
String currentLink = gridResource.getHref();
String path = gridResource.getResourcePath();
String propertyName = gridResource.getPropertyName();
Optional<String> updated = Optional.of(currentLink.replaceAll(linkPattern, replacement))
Optional<String> updated = Optional.of(isAdvancedMode ? currentLink.replaceAll(linkPattern, replacement) : replacement)
.filter(updatedLink -> !updatedLink.equals(currentLink))
.filter(updatedLink ->
linkHelper.replaceLink(resourceResolver, path, propertyName, currentLink, updatedLink)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class FixBrokenLinkServletTest {
private static final String TEST_CURRENT_LINK = "/content/link-for-replacement";
private static final String TEST_NEW_LINK = "/content/replacement-link";
private static final String TEST_EXCEPTION_MSG = "Test exception message";
private static final String ADVANCED_MODE = "advancedMode";

private final AemContext context = new AemContext(ResourceResolverType.JCR_MOCK);

Expand Down Expand Up @@ -100,6 +101,7 @@ void testCurrentLinkEqualToReplacement() {
request.addRequestParameter(PROPERTY_NAME_PARAM, TEST_PROPERTY_NAME);
request.addRequestParameter(CURRENT_LINK_PARAM, TEST_CURRENT_LINK);
request.addRequestParameter(NEW_LINK_PARAM, TEST_CURRENT_LINK);
request.addRequestParameter(ADVANCED_MODE, Boolean.TRUE.toString());
fixture.doPost(request, response);

assertEquals(HttpStatus.SC_ACCEPTED, response.getStatus());
Expand All @@ -119,6 +121,7 @@ void testValidateNewLink_notValid() {
request.addRequestParameter(CURRENT_LINK_PARAM, TEST_CURRENT_LINK);
request.addRequestParameter(NEW_LINK_PARAM, TEST_NEW_LINK);
request.addRequestParameter(IS_SKIP_VALIDATION_PARAM, Boolean.FALSE.toString());
request.addRequestParameter(ADVANCED_MODE, Boolean.TRUE.toString());
fixture.doPost(request, response);

assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatus());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ class ReplaceByPatternServletTest {
private static final String TEST_RESOURCES_TREE_PATH = "/com/exadel/etoolbox/linkinspector/core/servlets/resources.json";
private static final String TEST_FOLDER_PATH = "/content/test-folder";
private static final String TEST_EXCEPTION_MSG = "Test exception message";
private static final String ADVANCED_MODE = "advancedMode";

private final AemContext context = new AemContext(ResourceResolverType.JCR_MOCK);

Expand Down Expand Up @@ -153,6 +154,7 @@ void testEmptyParams() {
void testCurrentLinkEqualToReplacement() {
request.addRequestParameter(LINK_PATTERN_PARAM, TEST_LINK_PATTERN);
request.addRequestParameter(REPLACEMENT_PARAM, TEST_LINK_PATTERN);
request.addRequestParameter(ADVANCED_MODE, Boolean.TRUE.toString());
linkHelper = mock(LinkHelper.class);
repositoryHelper = mock(RepositoryHelper.class);
packageHelper = mock(PackageHelper.class);
Expand Down Expand Up @@ -433,6 +435,7 @@ private void setUpRequestParamsWithBackup() throws IOException {
private void setUpRequestParamsLinks() throws IOException {
request.addRequestParameter(LINK_PATTERN_PARAM, TEST_LINK_PATTERN);
request.addRequestParameter(REPLACEMENT_PARAM, TEST_REPLACEMENT);
request.addRequestParameter(ADVANCED_MODE, Boolean.TRUE.toString());
Arrays.stream(loadSelectedValues()).forEach(value ->
request.addRequestParameter(SELECTED_PARAM, value)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,7 @@ tr.elc-card {
font-size: 12px;
}
}

.elc-filter-active, .elc-filter-active:hover {
background-color: #d3d3d3 !important;
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,9 @@
$(document).ready(function () {
let searchParams = new URL(document.location).searchParams;
if (searchParams != null && searchParams.get('type') != null || searchParams.get('substring') != null) {
$('#elc-filter-options').attr('variant', 'primary');
$('#elc-filter-options').addClass('elc-filter-active');
} else {
$('#elc-filter-options').removeClass('elc-filter-active');
}
initFiltersDialog(searchParams);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
isDryRun: data.isDryRun,
isBackup: data.isBackup,
isOutputAsCsv: data.isOutputAsCsv,
advancedMode: data.advancedMode,
selected: data.selected
}].filter(function (item) {
return item.pattern && item.replacement && item.pattern !== item.replacement;
Expand Down Expand Up @@ -145,13 +146,22 @@
$cancelBtn.appendTo(el.footer);
$updateBtn.appendTo(el.footer);

buildConfirmationMessage(selection).appendTo(el.content);
buildConfirmationMessage(confirmationMessageSelectionItems(selection)).appendTo(el.content);

// Pattern input group
var $advancedOptionsSwitch =
$('<coral-switch data-dialog-advanced class="coral3-Switch" aria-disabled="false" aria-required="false" aria-invalid="false" aria-readonly="false">' +
'<input class="coral3-Switch-input" handle="input" type="checkbox">' +
'</coral-switch>');
$('<p>').text('Advanced Options').appendTo(el.content);
$advancedOptionsSwitch.appendTo(el.content);

let $patternFieldGroup = $('<div class="elc-pattern-field-group" hidden>');
var $patternTextField =
$('<input is="coral-textfield" class="elc-pattern-input" name="pattern" value="" required>');
$('<p>').text(PATTERN_LABEL).appendTo(el.content);
$patternTextField.appendTo(el.content);
$('<input is="coral-textfield" class="elc-pattern-input" name="pattern" value="" placeholder=".+" required>');
$('<p>').text(PATTERN_LABEL).appendTo($patternFieldGroup);
$patternTextField.appendTo($patternFieldGroup);
$patternFieldGroup.appendTo(el.content);

// Replacement input group
var $replacementTextField =
Expand All @@ -161,11 +171,11 @@

// Dry run checkbox group
var $isDryRunCheckbox =
$('<coral-checkbox name="$isDryRun" title="' + DRY_RUN_TOOLTIP + '" checked>').text(DRY_RUN_CHECKBOX_LABEL);
$('<coral-checkbox data-dialog-dry-run name="$isDryRun" title="' + DRY_RUN_TOOLTIP + '" checked>').text(DRY_RUN_CHECKBOX_LABEL);
$isDryRunCheckbox.appendTo(el.content);

// Backup checkbox group
var $isBackupCheckbox = $('<coral-checkbox name="isBackup">').text(BACKUP_CHECKBOX_LABEL);
var $isBackupCheckbox = $('<coral-checkbox name="isBackup" disabled>').text(BACKUP_CHECKBOX_LABEL);
$isBackupCheckbox.appendTo(el.content);

// CSV output checkbox group
Expand All @@ -178,15 +188,18 @@
function onValidate() {
var replVal = $replacementTextField.val();
var patternVal = $patternTextField.val();
var advanced = $advancedOptionsSwitch.prop("checked");
$replacementTextField.each(function () {
this.setCustomValidity(replVal === patternVal ? VALIDATION_MSG : '');
});
$updateBtn.attr('disabled', !replVal || !patternVal || replVal === patternVal);
$updateBtn.attr('disabled', !replVal || (advanced && !patternVal || replVal === patternVal));
}

/** @param {Event} e */
function onResolve(e) {
var data = {
pattern: $patternTextField.val(),
advancedMode: $advancedOptionsSwitch.prop("checked"),
replacement: $replacementTextField.val(),
isDryRun: $isDryRunCheckbox.prop("checked"),
isBackup: $isBackupCheckbox.prop("checked"),
Expand All @@ -198,11 +211,26 @@
deferred.resolve(data);
}

/** @param {Event} e */
function onChangeAdvanced(e) {
$patternFieldGroup.attr('hidden', !$advancedOptionsSwitch.prop("checked"));
onValidate();
}

/** @param {Event} e */
function onDryRunChange(e) {
$isBackupCheckbox.attr('disabled', $isDryRunCheckbox.prop('checked'));
}

el.on('input', 'input', onValidate);
el.on('click', '[data-dialog-action]', onResolve);
el.on('change', '[data-dialog-advanced]', onChangeAdvanced);
el.on('change', '[data-dialog-dry-run]', onDryRunChange);
el.on('coral-overlay:close', function () {
el.off('input', 'input', onValidate);
el.off('click', '[data-dialog-action]', onResolve);
el.off('change', '[data-dialog-advanced]', onChangeAdvanced);
el.off('change', '[data-dialog-dry-run]', onDryRunChange);
deferred.reject();
});

Expand All @@ -214,7 +242,7 @@

function buildConfirmationMessage(selections) {
let list = selections.slice(0, 12).map(function (row) {
return '<li>' + row.currentLink + '</li>';
return '<li>' + row.currentLink + ' (' + row.count + ')' + '</li>';
});
if (selections.length > 12) {
list.push('<li>&#8230;</li>'); // &#8230; is ellipsis
Expand All @@ -226,6 +254,21 @@
return $msg;
}

function confirmationMessageSelectionItems(selections) {
let valuesMap = {};
selections.map(function (row) {
return row.currentLink;
}).forEach(function (item) {
valuesMap[item] = (valuesMap[item]||0) + 1;
});

let items = [];
for (const [key, value] of Object.entries(valuesMap)) {
items.push({currentLink: key, count: value});
}
return items;
}

function buildSelectionItems(selections) {
return selections.map(function (v) {
let row = $(v);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,19 +114,7 @@
sling:resourceType="etoolbox-link-inspector/components/footer"/>
<actions jcr:primaryType="nt:unstructured">
<primary jcr:primaryType="nt:unstructured">
<fixBroken
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/collection/action"
action="cq-admin.etoolbox.linkinspector.action.fix-broken-link"
activeCondition="cq-admin.etoolbox.linkinspector.actioncondition.fix-broken-link"
disabled="{Boolean}true"
icon="linkCheck"
rel="etoolbox-link-inspector-fix-broken-link"
target=".etoolbox-link-inspector"
text="Fix Selected"
title="Fix Selected"
variant="primary"/>
<replaceByPattern
<fixSelected
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/collection/action"
action="cq-admin.etoolbox.linkinspector.action.replace-by-pattern"
Expand All @@ -135,10 +123,10 @@
icon="findAndReplace"
rel="etoolbox-link-inspector-fix-by-pattern"
target=".etoolbox-link-inspector"
text="Replace By Pattern"
title="Replace By Pattern"
text="Fix Selected"
title="Fix Selected"
granite:id="elc-replace-by-pattern"
variant="minimal"/>
variant="primary"/>
<filterOptions
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/collection/action"
Expand Down