Skip to content

Commit acbcf3b

Browse files
fix(static push): Add implementation to bundle folders (dotCMS#32210)
**Problem** The generation of folder tree when static push publishing was not being considered on the generation of the bundle, so if the folder didn't have any files to push publish then the bundle generated was empty. **Fix** A new `StaticFolderBundler` class was created, this class implements the generation of the folder tree according to the selected folder to push, this way now even if the folder is empty or has files that are not pages, the bundle will be generated.
1 parent 1e2517e commit acbcf3b

File tree

4 files changed

+212
-3
lines changed

4 files changed

+212
-3
lines changed

dotCMS/src/enterprise/java/com/dotcms/enterprise/publishing/staticpublishing/StaticDependencyBundler.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ private void buildPathIncludes() throws DotDataException, DotSecurityException {
8484
List<String> includes = (config.getIncludePatterns() == null) ? new ArrayList<>() : config.getIncludePatterns();
8585
Set<Host> hosts = (config.getHosts() == null) ? new HashSet<>() : new HashSet<>(config.getHosts());
8686
Set<String> languages = config.getLanguages();
87-
87+
Set<String> folders = config.getFolders();
8888
if (languages == null || languages.isEmpty()) {
8989
for (Language l : languageAPI.getLanguages()) {
9090
languages.add(l.getId() + "");
@@ -108,6 +108,7 @@ private void buildPathIncludes() throws DotDataException, DotSecurityException {
108108
hosts.add(h);
109109

110110
includes.add(folder.getPath() + "*");
111+
folders.add(folder.getIdentifier());
111112
//If Asset is CONTENTLET
112113
} else{
113114
// everything care about is going to have an id
@@ -142,6 +143,7 @@ private void buildPathIncludes() throws DotDataException, DotSecurityException {
142143
config.setHosts(Lists.newArrayList(hosts));
143144
config.setIncludePatterns(includes);
144145
config.setLanguages(languages);
146+
config.setFolders(folders);
145147
}
146148

147149
@Override
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package com.dotcms.enterprise.publishing.staticpublishing;
2+
3+
import com.dotcms.enterprise.LicenseUtil;
4+
import com.dotcms.enterprise.license.LicenseLevel;
5+
import com.dotcms.publisher.business.DotPublisherException;
6+
import com.dotcms.publisher.business.PublisherAPI;
7+
import com.dotcms.publisher.pusher.PushPublisherConfig;
8+
import com.dotcms.publisher.pusher.wrapper.FolderWrapper;
9+
import com.dotcms.publishing.*;
10+
import com.dotcms.publishing.output.BundleOutput;
11+
import com.dotmarketing.beans.Host;
12+
import com.dotmarketing.business.APILocator;
13+
import com.dotmarketing.business.UserAPI;
14+
import com.dotmarketing.exception.DotDataException;
15+
import com.dotmarketing.exception.DotSecurityException;
16+
import com.dotmarketing.portlets.contentlet.business.ContentletAPI;
17+
import com.dotmarketing.portlets.folders.business.FolderAPI;
18+
import com.dotmarketing.portlets.folders.model.Folder;
19+
import com.dotmarketing.portlets.languagesmanager.business.LanguageAPI;
20+
import com.dotmarketing.util.Config;
21+
import com.dotmarketing.util.Logger;
22+
import com.dotmarketing.util.PushPublishLogger;
23+
import com.liferay.portal.model.User;
24+
25+
import java.io.File;
26+
import java.io.FileFilter;
27+
import java.io.IOException;
28+
import java.util.ArrayList;
29+
import java.util.Collections;
30+
import java.util.List;
31+
import java.util.Set;
32+
/**
33+
* This bundler will generate the corresponding folder tree for the bundle when trying to Static Publish a folder
34+
*/
35+
public class StaticFolderBundler implements IBundler {
36+
private PublisherConfig config;
37+
private User systemUser;
38+
ContentletAPI contentletAPI = null;
39+
UserAPI userAPI = null;
40+
com.dotcms.publisher.business.PublisherAPI publisherAPI = null;
41+
FolderAPI folderAPI = APILocator.getFolderAPI();
42+
LanguageAPI languageAPI = APILocator.getLanguageAPI();
43+
44+
public final static String FOLDER_EXTENSION = ".folder.xml" ;
45+
46+
@Override
47+
public String getName() {
48+
return "Static Folder bundler";
49+
}
50+
51+
@Override
52+
public void setConfig(PublisherConfig pc) {
53+
config = pc;
54+
contentletAPI = APILocator.getContentletAPI();
55+
userAPI = APILocator.getUserAPI();
56+
publisherAPI = PublisherAPI.getInstance();
57+
58+
try {
59+
systemUser = userAPI.getSystemUser();
60+
} catch (DotDataException e) {
61+
Logger.fatal(StaticFolderBundler.class,e.getMessage(),e);
62+
}
63+
}
64+
65+
@Override
66+
public void setPublisher(IPublisher publisher) {
67+
}
68+
69+
@Override
70+
public void generate(final BundleOutput output, final BundlerStatus status)
71+
throws DotBundleException {
72+
if(LicenseUtil.getLevel() < LicenseLevel.PROFESSIONAL.level)
73+
throw new RuntimeException("need an enterprise pro license to run this bundler");
74+
75+
Set<String> folders = (Set<String>) config.get(PublisherConfig.Config.FOLDERS.name());
76+
77+
try {
78+
for (String folder : folders) {
79+
final long defaultLanguage = this.languageAPI.getDefaultLanguage().getId();
80+
81+
for (final Long langId : getSortedConfigLanguages(this.config, defaultLanguage)) {
82+
83+
writeFolderTree(output, folder, langId);
84+
}
85+
}
86+
} catch (Exception e) {
87+
status.addFailure();
88+
89+
throw new DotBundleException(this.getClass().getName() + " : " + "generate()"
90+
+ e.getMessage() + ": Unable to pull content", e);
91+
}
92+
93+
}
94+
95+
96+
97+
private void writeFolderTree(BundleOutput bundleOutput, String idFolder, Long languageId)
98+
throws DotDataException, DotSecurityException
99+
{
100+
//Get Folder tree
101+
Folder folder = folderAPI.find(idFolder, systemUser, false);
102+
String folderName = folder.getName();
103+
List<String> path = new ArrayList<>();
104+
105+
Host site = folder.getHost();
106+
while(folder != null && !folder.isSystemFolder()) {
107+
path.add(folder.getName());
108+
folder = folderAPI.findParentFolder(folder, systemUser, false);
109+
}
110+
111+
if(path.size() > 0) {
112+
Collections.reverse(path);
113+
StringBuilder b = new StringBuilder(File.separator);
114+
for (String f : path) {
115+
b.append(f);
116+
b.append(File.separator);
117+
118+
// exclude other folders but the one being pushed, when unpublishing
119+
if(config.getOperation().equals(PushPublisherConfig.Operation.UNPUBLISH) && !f.equals(folderName)) {
120+
continue;
121+
}
122+
123+
124+
String myFolderUrl = File.separator + "live" + File.separator + site.getHostname() + File.separator + languageId +
125+
b.toString();
126+
bundleOutput.mkdirs(myFolderUrl);
127+
}
128+
}
129+
130+
if(Config.getBooleanProperty("PUSH_PUBLISHING_LOG_DEPENDENCIES", false)) {
131+
PushPublishLogger.log(getClass(), "Folder bundled for pushing. Operation: "+config.getOperation()+", Id: "+ idFolder, config.getId());
132+
}
133+
}
134+
135+
@Override
136+
public FileFilter getFileFilter(){
137+
return new StaticFolderBundler.FolderBundlerFilter();
138+
}
139+
140+
public class FolderBundlerFilter implements FileFilter{
141+
142+
@Override
143+
public boolean accept(File pathname) {
144+
145+
return (pathname.isDirectory() || pathname.getName().endsWith(FOLDER_EXTENSION));
146+
}
147+
148+
}
149+
150+
}

dotCMS/src/enterprise/java/com/dotcms/enterprise/publishing/staticpublishing/StaticPublisher.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ public List<Class> getBundlers() {
424424
list.add(BinaryExporterBundler.class);
425425
list.add(CSSExporterBundler.class);
426426
list.add(ShortyBundler.class);
427+
list.add(StaticFolderBundler.class);
427428

428429
return list;
429430
} //getBundlers.

dotcms-integration/src/test/java/com/dotcms/enterprise/publishing/staticpublishing/StaticPublisherIntegrationTest.java

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.dotcms.enterprise.publishing.staticpublishing;
22

33
import com.dotcms.datagen.BundleDataGen;
4+
import com.dotcms.datagen.FolderDataGen;
45
import com.dotcms.enterprise.publishing.staticpublishing.StaticPublisherIntegrationTestHelper.FileExpected;
56
import com.dotcms.enterprise.publishing.staticpublishing.StaticPublisherIntegrationTestHelper.TestCase;
67
import com.dotcms.publisher.bundle.bean.Bundle;
@@ -9,6 +10,7 @@
910
import com.dotcms.publisher.business.PublishAuditHistory;
1011
import com.dotcms.publisher.business.PublishAuditStatus;
1112
import com.dotcms.publisher.pusher.PushPublisherConfig;
13+
import com.dotcms.publisher.util.PusheableAsset;
1214
import com.dotcms.publishing.BundlerUtil;
1315
import com.dotcms.publishing.DotPublishingException;
1416
import com.dotcms.publishing.PublishStatus;
@@ -23,6 +25,7 @@
2325
import com.dotmarketing.exception.DotDataException;
2426
import com.dotmarketing.exception.DotSecurityException;
2527
import com.dotmarketing.exception.WebAssetException;
28+
import com.dotmarketing.portlets.folders.model.Folder;
2629
import com.dotmarketing.portlets.htmlpageasset.model.HTMLPageAsset;
2730
import com.dotmarketing.util.UtilMethods;
2831
import com.liferay.util.FileUtil;
@@ -65,8 +68,7 @@
6568
import static com.dotcms.enterprise.publishing.staticpublishing.StaticPublisherIntegrationTestHelper.getWorkingFileAsset;
6669
import static com.dotcms.enterprise.publishing.staticpublishing.StaticPublisherIntegrationTestHelper.getWorkingPage;
6770
import static com.dotcms.util.CollectionsUtils.list;
68-
import static junit.framework.TestCase.assertEquals;
69-
import static junit.framework.TestCase.assertTrue;
71+
import static junit.framework.TestCase.*;
7072

7173
@RunWith(DataProviderRunner.class)
7274
public class StaticPublisherIntegrationTest {
@@ -366,6 +368,60 @@ private static String getXMLPageFileExpectedContent(final HTMLPageAsset page)
366368
return FileTestUtil.removeContent(fileContentExpected, toRemove);
367369
}
368370

371+
/**
372+
* Method to Test: {@link PublisherAPIImpl#publish(PublisherConfig)}
373+
* Given Scenario: A folder without any files is static push published
374+
* Expected Result: A bundle should be generated with the folder info
375+
* */
376+
@Test
377+
public void createStaticBundleWithoutFiles()
378+
throws DotPublishingException, DotPublisherException {
379+
String name = "testFolder" + String.valueOf(System.currentTimeMillis());
380+
381+
final Folder folder = new FolderDataGen().name(name).nextPersisted();
382+
try {
383+
384+
final Class<? extends Publisher> publisher = StaticPublisher.class;
385+
386+
final PublisherAPIImpl publisherAPI = new PublisherAPIImpl();
387+
388+
final PushPublisherConfig config = new PushPublisherConfig();
389+
config.setPublishers(list(publisher));
390+
config.setOperation(PublisherConfig.Operation.PUBLISH);
391+
config.setLuceneQueries(list());
392+
config.setId("StaticPublisher" + System.currentTimeMillis());
393+
config.setStatic(true);
394+
395+
396+
397+
config.setLanguages(Set.of("1", "2"));
398+
399+
final Bundle bundle = new BundleDataGen()
400+
.pushPublisherConfig(config)
401+
.addAssets(list(folder))
402+
.nextPersisted();
403+
404+
final PublishAuditStatus status = new PublishAuditStatus(bundle.getId());
405+
406+
final PublishAuditHistory historyPojo = new PublishAuditHistory();
407+
historyPojo.setAssets(Map.of(folder.getIdentifier(), PusheableAsset.FOLDER.getType()));
408+
status.setStatusPojo(historyPojo);
409+
PublishAuditAPI.getInstance().insertPublishAuditStatus(status);
410+
411+
final PublishStatus publish = publisherAPI.publish(config);
412+
413+
final File bundleRoot = BundlerUtil.getBundleRoot(config);
414+
final boolean isFolderCreated = FileUtil.listFilesRecursively(bundleRoot)
415+
.stream()
416+
.anyMatch(file -> file.getPath().contains(name));
417+
418+
assertTrue(isFolderCreated);
419+
} finally {
420+
FolderDataGen.remove(folder);
421+
}
422+
}
423+
424+
369425
private static List<String> getXMLFileToRemove() {
370426
return list(
371427
"<lockedOn class=\"sql-timestamp\">.*</lockedOn>",

0 commit comments

Comments
 (0)