1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.module.site;
35
36 import info.magnolia.cms.security.JCRSessionOp;
37 import info.magnolia.config.registry.DefinitionMetadata;
38 import info.magnolia.config.registry.DefinitionProvider;
39 import info.magnolia.config.registry.DefinitionProviderBuilder;
40 import info.magnolia.config.registry.DefinitionRawView;
41 import info.magnolia.config.source.ConfigurationSourceFactory;
42 import info.magnolia.context.MgnlContext;
43 import info.magnolia.jcr.node2bean.Node2BeanException;
44 import info.magnolia.jcr.node2bean.Node2BeanProcessor;
45 import info.magnolia.jcr.util.PropertyUtil;
46 import info.magnolia.module.ModuleLifecycle;
47 import info.magnolia.module.ModuleLifecycleContext;
48 import info.magnolia.module.site.theme.Theme;
49 import info.magnolia.module.site.theme.registry.ThemeRegistry;
50 import info.magnolia.objectfactory.Components;
51 import info.magnolia.observation.WorkspaceEventListenerRegistration;
52 import info.magnolia.repository.RepositoryConstants;
53
54 import java.util.ArrayList;
55 import java.util.Collection;
56 import java.util.HashMap;
57 import java.util.Map;
58
59 import javax.inject.Inject;
60 import javax.inject.Singleton;
61 import javax.jcr.Node;
62 import javax.jcr.RepositoryException;
63 import javax.jcr.Session;
64 import javax.jcr.observation.EventIterator;
65 import javax.jcr.observation.EventListener;
66
67 import org.apache.commons.lang3.StringUtils;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70
71
72
73
74 @Singleton
75 public class SiteModule implements ModuleLifecycle {
76
77 private static final Logger log = LoggerFactory.getLogger(SiteModule.class);
78
79 public static final String SITE_MODULE_PATH = "/modules/site/config/site";
80
81 private static final long DELAY = 5000;
82
83 private static final long MAX_DELAY = 30000;
84
85 private static final String EXTENDS_PROPERTY = "extends";
86
87 private static final String EXTENDS_OVERRIDE = "override";
88
89 private final EventListener siteModuleEventListener = new SiteModuleEventListener();
90
91 private final Node2BeanProcessor node2BeanProcessor;
92
93 private Site site;
94
95 private final ConfigurationSourceFactory configSourceFactory;
96
97 private final ThemeRegistry themeRegistry;
98
99 private final Collection<DefinitionMetadata> oldLocationThemes = new ArrayList<>();
100
101
102
103
104 protected static final Site NULL_SITE = new NullSite();
105
106 private WorkspaceEventListenerRegistration.Handle eventListenerRegistration;
107
108 @Inject
109 public SiteModule(Node2BeanProcessor node2BeanProcessor, ConfigurationSourceFactory configSourceFactory, ThemeRegistry themeRegistry) {
110 this.node2BeanProcessor = node2BeanProcessor;
111 this.configSourceFactory = configSourceFactory;
112 this.themeRegistry = themeRegistry;
113 }
114
115
116
117
118 @Deprecated
119 public SiteModule(Node2BeanProcessor node2BeanProcessor) {
120 this(Components.getComponent(Node2BeanProcessor.class), Components.getComponent(ConfigurationSourceFactory.class), Components.getComponent(ThemeRegistry.class));
121 }
122
123
124
125
126 @Deprecated
127 public SiteModule() {
128 this(Components.getComponent(Node2BeanProcessor.class), Components.getComponent(ConfigurationSourceFactory.class), Components.getComponent(ThemeRegistry.class));
129 }
130
131
132
133
134
135 public Site getSite() {
136 if (this.site == null) {
137 return NULL_SITE;
138 }
139 return this.site;
140 }
141
142 public void setSite(Site site) {
143 this.site = site;
144 }
145
146
147
148
149 @Deprecated
150 public Map<String, Theme> getThemes() {
151 Map<String, Theme> result = new HashMap<>();
152 Collection<Theme> themes = themeRegistry.getAllDefinitions();
153 for (Theme theme : themes) {
154 result.put(theme.getName(), theme);
155 }
156 return result;
157 }
158
159
160
161
162 @Deprecated
163 public void setThemes(Map<String, Theme> themes) {
164 themeRegistry.unregisterAndRegister(oldLocationThemes, new ArrayList<DefinitionProvider<Theme>>());
165 oldLocationThemes.clear();
166 for (String key : themes.keySet()) {
167 this.addTheme(key, themes.get(key));
168 }
169 }
170
171
172
173
174 @Deprecated
175 public void addTheme(String name, Theme theme) {
176 log.warn("Deprecated location ( [/modules/site/config/themes/{}]) for theme configuration used. Please move your theme under [/modules/<module-name>/themes].", name);
177 final String moduleName = "site";
178 final String relativeLocation = "/config/themes/" + name;
179 final String location = "/modules/" + moduleName + relativeLocation;
180 final DefinitionProvider<Theme> definitionProvider = DefinitionProviderBuilder.<Theme>newBuilder()
181 .metadata(themeRegistry.newMetadataBuilder().type(themeRegistry.type()).module(moduleName).location(location).relativeLocation(relativeLocation).name(name))
182 .rawView(DefinitionRawView.EMPTY)
183 .definition(theme)
184 .build();
185 themeRegistry.register(definitionProvider);
186 oldLocationThemes.add(definitionProvider.getMetadata());
187 }
188
189
190
191
192 @Deprecated
193 public Theme getTheme(String name) {
194 return themeRegistry.getProvider(name).get();
195 }
196
197 @Override
198 public void start(ModuleLifecycleContext moduleLifecycleContext) {
199 if (site == null) {
200 log.warn("Currently there is no default site specified in the site module. Make sure to add a site configuration to [{}].", SITE_MODULE_PATH);
201 }
202 String extendedNodePath = getExtendedNodePath();
203 if (StringUtils.isNotEmpty(extendedNodePath)) {
204 try {
205 eventListenerRegistration = WorkspaceEventListenerRegistration.observe(RepositoryConstants.CONFIG, extendedNodePath, siteModuleEventListener).withDelay(DELAY, MAX_DELAY).register();
206 } catch (RepositoryException e) {
207 log.warn("Unable to register event listener for [{}:{}]", RepositoryConstants.CONFIG, extendedNodePath, e);
208 }
209 }
210
211 if (moduleLifecycleContext.getPhase() == ModuleLifecycleContext.PHASE_SYSTEM_STARTUP) {
212 configSourceFactory.jcr().bindWithDefaults(themeRegistry);
213 configSourceFactory.yaml().bindWithDefaults(themeRegistry);
214 }
215 }
216
217 @Override
218 public void stop(ModuleLifecycleContext moduleLifecycleContext) {
219 if (eventListenerRegistration != null) {
220 try {
221 eventListenerRegistration.unregister();
222 } catch (RepositoryException e) {
223 log.warn("Unable to unregister event listener for [{}:{}]", RepositoryConstants.CONFIG, getExtendedNodePath(), e);
224 }
225 }
226 }
227
228
229
230
231
232
233 private String getExtendedNodePath() {
234 try {
235 final Session session = MgnlContext.getJCRSession(RepositoryConstants.CONFIG);
236 final Node siteNode = session.getNode(SITE_MODULE_PATH);
237 final String extendingNodePath = PropertyUtil.getString(siteNode, EXTENDS_PROPERTY, null);
238 if (StringUtils.isNotBlank(extendingNodePath) && !EXTENDS_OVERRIDE.equals(extendingNodePath)) {
239 final Node extendedNode;
240 if (extendingNodePath.startsWith("/")) {
241 extendedNode = session.getNode(extendingNodePath);
242 } else {
243 extendedNode = siteNode.getNode(extendingNodePath);
244 }
245 return extendedNode.getPath();
246 }
247 } catch (RepositoryException e) {
248 log.debug("Unable to obtain extended node path", e);
249 }
250 return null;
251 }
252
253
254
255
256
257
258 private Node getSiteNode() {
259
260 try {
261 return MgnlContext.doInSystemContext(new JCRSessionOp<Node>(RepositoryConstants.CONFIG) {
262 @Override
263 public Node exec(final Session session) throws RepositoryException {
264 return session.getNode(SITE_MODULE_PATH);
265 }
266 });
267 } catch (RepositoryException e) {
268 log.debug("Unable to obtain site node");
269 }
270 return null;
271 }
272
273
274
275
276 private class SiteModuleEventListener implements EventListener {
277 @Override
278 public void onEvent(final EventIterator events) {
279 Node siteNode = getSiteNode();
280 if (siteNode != null) {
281 try {
282 site = (Site) node2BeanProcessor.toBean(siteNode, Site.class);
283 } catch (Node2BeanException | RepositoryException e) {
284 log.debug("Error occurred while transforming site node to bean class", e);
285 }
286 }
287 }
288 }
289
290 }