Skip to content

Commit

Permalink
Merge branch 'master' into issues/3177-automate-creation-vanity
Browse files Browse the repository at this point in the history
  • Loading branch information
yelouardi committed Sep 14, 2023
2 parents 5a60a64 + a75bb29 commit 8850a78
Show file tree
Hide file tree
Showing 23 changed files with 611 additions and 45 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com)

## Unreleased ([details][unreleased changes details])

## 6.1.0 - 2023-09-08

## Added

- #3159 - Add PageProperty annotation for Sling Models
- #3170 - Added a new MCP tool to bulk tag AEM content pages via an Excel file input.

## Fixed

- #3147 - Fixed setting initial content-type when importing CFs from a spreadsheet
- #3040 - Fixed bug where namespaced multi-fields would have the namespace 2 times
- #3140 - Fixed issue where malformed MCP process nodes can cause a NPE that breaks the entire MPC reporting UI. Now displays more friendly values in UI to help remove the invalid nodes.
- #3150 - Support for case-insensitive redirect rules ( [NC] flag equivalent of apache)
Expand Down
2 changes: 1 addition & 1 deletion all/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.adobe.acs</groupId>
<artifactId>acs-aem-commons</artifactId>
<version>6.0.15-SNAPSHOT</version>
<version>6.1.1-SNAPSHOT</version>
</parent>

<!-- ====================================================================== -->
Expand Down
2 changes: 1 addition & 1 deletion bundle/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.adobe.acs</groupId>
<artifactId>acs-aem-commons</artifactId>
<version>6.0.15-SNAPSHOT</version>
<version>6.1.1-SNAPSHOT</version>
</parent>

<!-- ====================================================================== -->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
/*-
* #%L
* ACS AEM Commons Bundle
* %%
* Copyright (C) 2013 - 2023 Adobe
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package com.adobe.acs.commons.mcp.impl.processes;

import com.adobe.acs.commons.fam.ActionManager;
import com.adobe.acs.commons.mcp.ProcessDefinition;
import com.adobe.acs.commons.mcp.ProcessInstance;
import com.adobe.acs.commons.mcp.form.FileUploadComponent;
import com.adobe.acs.commons.mcp.form.FormField;
import com.adobe.acs.commons.mcp.model.GenericBlobReport;
import com.adobe.acs.commons.mcp.util.StringUtil;
import com.day.cq.tagging.Tag;
import com.day.cq.wcm.api.Page;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.ModifiableValueMap;
import org.apache.sling.api.resource.PersistenceException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.RepositoryException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Iterator;
import java.util.HashSet;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* The type Bulk page tagger.
*/
public class BulkPageTagger extends ProcessDefinition implements Serializable {
private static final Logger log = LoggerFactory.getLogger(BulkPageTagger.class);
private static final long serialVersionUID = 798823856839772874L;

/**
* The constant NAME.
*/
public static final String NAME = "Bulk Page Tagger";

/**
* The Excel file.
*/
@FormField(
name = "Excel File",
description = "Provide the .xlsx file that defines the content pages and the corresponding cq:tags to be added on the pages",
component = FileUploadComponent.class,
options = {"mimeTypes=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "required"}
)
public transient InputStream excelFile = null;


@Override
public void init() throws RepositoryException {
// Nothing to be done here.

}


@Override
public void buildProcess(ProcessInstance instance, ResourceResolver rr) throws LoginException {
report.setName(instance.getName());
instance.getInfo().setDescription("Bulk Tag AEM content Pages");
instance.defineCriticalAction("Parse Excel File", rr, this::parseExcel);
instance.defineCriticalAction("Add Tags to Content Pages", rr, this::tagPages);

}


/**
* The Page tag mapping dataStructure.
*/
transient volatile HashMap<String, String> pageTagMapping = new LinkedHashMap<>();

/**
* Parse input excel.
*
* @param manager the manager
* @throws Exception the exception
*/
@SuppressWarnings("squid:S112")
public void parseExcel(ActionManager manager) throws Exception {
manager.withResolver(rr -> {
final XSSFWorkbook workbook = new XSSFWorkbook(excelFile);
final XSSFSheet sheet = workbook.getSheetAt(0);

final Iterator<Row> rows = sheet.rowIterator();
final String tagsRootPath = new TagCreator.TagRootResolver(rr).getTagsLocationPath();


if (tagsRootPath == null) {
recordAction(ReportRowSatus.FAILED_TO_PARSE,
"Abandoning Tag parsing. Unable to determine AEM Tags root (/content/cq:tags vs /etc/tags). Please ensure the path exists and is accessible by the user running Tag Creator.", "N/A");
return;
}

while (rows.hasNext()) {
final Row row = rows.next();
if (row.getCell(0) == null) {
break;
}
if (row.getRowNum() != 0 && row.getCell(0) != null) {
pageTagMapping.put(row.getCell(0).getStringCellValue(), row.getCell(1).getStringCellValue());
}
}

});

}


/**
* Tag pages from the excel file with cq:tags.
*
* @param manager the manager
* @throws Exception the exception
*/
@SuppressWarnings("squid:S112")
public void tagPages(ActionManager manager) throws Exception {

manager.withResolver(rr -> {

pageTagMapping.forEach((key, value) -> {
BulkPageTagger.ReportRowSatus status;
Resource resource = rr.getResource(key);
if (resource != null) {
Page page = resource.adaptTo(Page.class);
if (page != null) {
Tag[] existingPageTags = page.getTags();
String[] tagIds = Stream.of(existingPageTags)
.map(Tag::getTagID)
.toArray(String[]::new);
Set<String> updatedTags = Arrays.stream(value.split("[;\n]"))
.map(String::trim)
.collect(Collectors.toSet());
updatedTags.addAll(Arrays.asList(tagIds));
String[] updatedTagsArray = updatedTags.stream().toArray(String[]::new);

ModifiableValueMap properties = page.getContentResource().adaptTo(ModifiableValueMap.class);
properties.put(com.day.cq.tagging.TagConstants.PN_TAGS, updatedTagsArray);
try {
rr.commit();
status = ReportRowSatus.UPDATED_EXISTING;
recordAction(status, page.getPath(), Arrays.toString(updatedTagsArray));
} catch (PersistenceException e) {
status = ReportRowSatus.FAILED_TO_UPDATE;
recordAction(status, page.getPath(), Arrays.toString(updatedTagsArray));
log.error(String.format("Unable to add tags to page with page path - %s ", page.getPath()));
}


}

}

});
});
}


/**
* Reporting
**/


private final transient GenericBlobReport report = new GenericBlobReport();

private final transient ArrayList<EnumMap<BulkPageTagger.ReportColumns, Object>> reportRows = new ArrayList<>();

private enum ReportColumns {
/**
* Status report columns.
*/
STATUS,
/**
* Page path report columns.
*/
PAGE_PATH,
/**
* Tags array report columns.
*/
TAGS_ARRAY
}

/**
* The enum Report row satus.
*/
public enum ReportRowSatus {
/**
* Created report row satus.
*/
CREATED,
/**
* Updated existing report row satus.
*/
UPDATED_EXISTING,
/**
* Failed to parse report row satus.
*/
FAILED_TO_PARSE,
/**
* Failed to update report row satus.
*/
FAILED_TO_UPDATE
}


private void recordAction(BulkPageTagger.ReportRowSatus status, String pagePath, String tags) {
final EnumMap<BulkPageTagger.ReportColumns, Object> row = new EnumMap<>(BulkPageTagger.ReportColumns.class);

row.put(BulkPageTagger.ReportColumns.STATUS, StringUtil.getFriendlyName(status.name()));
row.put(ReportColumns.PAGE_PATH, pagePath);
row.put(ReportColumns.TAGS_ARRAY, tags);

reportRows.add(row);
}


@Override
public void storeReport(ProcessInstance instance, ResourceResolver rr) throws RepositoryException, PersistenceException {
report.setRows(reportRows, BulkPageTagger.ReportColumns.class);
report.persist(rr, instance.getPath() + "/jcr:content/report");
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*-
* #%L
* ACS AEM Commons Bundle
* %%
* Copyright (C) 2013 - 2023 Adobe
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package com.adobe.acs.commons.mcp.impl.processes;

import com.adobe.acs.commons.mcp.ProcessDefinitionFactory;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;

@Component
@Service(ProcessDefinitionFactory.class)
public class BulkPageTaggerFactory extends ProcessDefinitionFactory<BulkPageTagger> {

@Override
public String getName() {
return BulkPageTagger.NAME;
}

@Override
protected BulkPageTagger createProcessDefinitionInstance() {
return new BulkPageTagger();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import com.adobe.cq.dam.cfm.FragmentTemplate;
import com.day.cq.commons.jcr.JcrConstants;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
Expand All @@ -48,22 +47,17 @@
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.apache.sling.api.request.RequestParameter;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Import a series of content fragments from a spreadsheet
*/
public class ContentFragmentImport extends ProcessDefinition {

private static final Logger LOG = LoggerFactory.getLogger(ContentFragmentImport.class);

public enum ReportColumns {
ITEM, ACTION, DESCRIPTION, COUNT
}
Expand Down Expand Up @@ -313,8 +307,14 @@ private void setContentElements(ContentFragment cf, Map<String, CompositeVariant
String value = getString(row, contentElement);
String currentValue = contentElement.getContent();

String contentType;
if(StringUtils.isBlank(currentValue)) { // Workaround issue #3147
contentType = cf.getTemplate().getForElement(contentElement).getInitialContentType();
} else {
contentType = contentElement.getContentType();
}
if (!String.valueOf(value).equals(String.valueOf(currentValue))) {
contentElement.setContent(value, contentElement.getContentType());
contentElement.setContent(value, contentType);
}
}
}
Expand Down Expand Up @@ -351,14 +351,8 @@ private String getString(Map<String, CompositeVariant> row, ContentElement conte
protected ContentFragment getOrCreateFragment(Resource parent, Resource template, String name, String title) throws ContentFragmentException {
Resource fragmentResource = parent.getChild(name);
if (fragmentResource == null) {
try {
FragmentTemplate fragmentTemplate = template.adaptTo(FragmentTemplate.class);
// TODO: Replace this reflection hack with the proper method once ACS Commons doesn't support 6.2 anymore
return (ContentFragment) MethodUtils.invokeMethod(fragmentTemplate, "createFragment", parent, name, title);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
LOG.error("Unable to call createFragment method -- Is this 6.3 or newer?", ex);
return null;
}
FragmentTemplate fragmentTemplate = template.adaptTo(FragmentTemplate.class);
return fragmentTemplate.createFragment(parent, name, title);
} else {
return fragmentResource.adaptTo(ContentFragment.class);
}
Expand Down
Loading

0 comments on commit 8850a78

Please sign in to comment.