]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms.ui/src/org/argeo/cms/ui/util/BundleCmsTheme.java
Expose servlet context helpers.
[lgpl/argeo-commons.git] / org.argeo.cms.ui / src / org / argeo / cms / ui / util / BundleCmsTheme.java
1 package org.argeo.cms.ui.util;
2
3 import static java.nio.charset.StandardCharsets.UTF_8;
4
5 import java.io.BufferedReader;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.io.InputStreamReader;
9 import java.net.URL;
10 import java.util.ArrayList;
11 import java.util.Enumeration;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Set;
15 import java.util.TreeSet;
16 import java.util.stream.Collectors;
17
18 import org.osgi.framework.Bundle;
19 import org.osgi.framework.BundleContext;
20
21 /**
22 * Simplifies the theming of an app (only RAP is supported at this stage).<br>
23 *
24 * Additional fonts listed in <code>/fonts.txt</code>.<br>
25 * Additional (standard CSS) header in <code>/header.css</code>.<br>
26 * RAP specific CSS files in <code>/rap/*.css</code>.<br>
27 * All images added as additional resources based on extensions
28 * <code>/ ** /*.{png,gif,jpeg,...}</code>.<br>
29 */
30 public class BundleCmsTheme extends AbstractCmsTheme {
31 public final static String DEFAULT_CMS_THEME_BUNDLE = "org.argeo.theme.argeo2";
32
33 public final static String CMS_THEME_PROPERTY = "argeo.cms.theme";
34 public final static String CMS_THEME_BUNDLE_PROPERTY = "argeo.cms.theme.bundle";
35
36 // private final static Log log = LogFactory.getLog(BundleCmsTheme.class);
37
38 private String themeId;
39 private Set<String> webCssPaths = new TreeSet<>();
40 private Set<String> rapCssPaths = new TreeSet<>();
41 private Set<String> swtCssPaths = new TreeSet<>();
42 private Set<String> imagesPaths = new TreeSet<>();
43
44 private String headerCss;
45 private List<String> fonts = new ArrayList<>();
46
47 private String basePath;
48 private String styleCssPath;
49 // private String webCssPath;
50 // private String rapCssPath;
51 // private String swtCssPath;
52 private Bundle themeBundle;
53
54 public BundleCmsTheme() {
55
56 }
57
58 public void init(BundleContext bundleContext, Map<String, String> properties) {
59 initResources(bundleContext, null);
60 }
61
62 public void destroy(BundleContext bundleContext, Map<String, String> properties) {
63
64 }
65
66 @Deprecated
67 public BundleCmsTheme(BundleContext bundleContext) {
68 this(bundleContext, null);
69 }
70
71 @Deprecated
72 public BundleCmsTheme(BundleContext bundleContext, String symbolicName) {
73 initResources(bundleContext, symbolicName);
74 }
75
76 private void initResources(BundleContext bundleContext, String symbolicName) {
77 if (symbolicName == null) {
78 themeBundle = bundleContext.getBundle();
79 // basePath = "/theme/";
80 // cssPath = basePath;
81 } else {
82 themeBundle = findThemeBundle(bundleContext, symbolicName);
83 }
84 basePath = "/";
85 styleCssPath = "/style/";
86 // webCssPath = "/css/";
87 // rapCssPath = "/rap/";
88 // swtCssPath = "/swt/";
89 // this.themeId = RWT.DEFAULT_THEME_ID;
90 this.themeId = themeBundle.getSymbolicName();
91 webCssPaths = addCss(themeBundle, "/css/");
92 rapCssPaths = addCss(themeBundle, "/rap/");
93 swtCssPaths = addCss(themeBundle, "/swt/");
94 addResources("*.png");
95 addResources("*.gif");
96 addResources("*.jpg");
97 addResources("*.jpeg");
98 addResources("*.svg");
99 addResources("*.ico");
100
101 // fonts
102 URL fontsUrl = themeBundle.getEntry(basePath + "fonts.txt");
103 if (fontsUrl != null) {
104 loadFontsUrl(fontsUrl);
105 }
106
107 // common CSS header (plain CSS)
108 URL headerCssUrl = themeBundle.getEntry(basePath + "header.css");
109 if (headerCssUrl != null) {
110 try (BufferedReader buffer = new BufferedReader(new InputStreamReader(headerCssUrl.openStream(), UTF_8))) {
111 headerCss = buffer.lines().collect(Collectors.joining("\n"));
112 } catch (IOException e) {
113 throw new IllegalArgumentException("Cannot read " + headerCssUrl, e);
114 }
115 }
116 }
117
118 public String getHtmlHeaders() {
119 StringBuilder sb = new StringBuilder();
120 if (headerCss != null) {
121 sb.append("<style type='text/css'>\n");
122 sb.append(headerCss);
123 sb.append("\n</style>\n");
124 }
125 for (String link : fonts) {
126 sb.append("<link rel='stylesheet' href='");
127 sb.append(link);
128 sb.append("'/>\n");
129 }
130 if (sb.length() == 0)
131 return null;
132 else
133 return sb.toString();
134 }
135
136 Set<String> addCss(Bundle themeBundle, String path) {
137 Set<String> paths = new TreeSet<>();
138 Enumeration<URL> themeResources = themeBundle.findEntries(path, "*.css", true);
139 if (themeResources == null)
140 return paths;
141 while (themeResources.hasMoreElements()) {
142 String resource = themeResources.nextElement().getPath();
143 // remove first '/' so that RWT registers it
144 resource = resource.substring(1);
145 if (!resource.endsWith("/")) {
146 paths.add(resource);
147 }
148 }
149
150 // common CSS
151 Enumeration<URL> commonResources = themeBundle.findEntries(styleCssPath, "*.css", true);
152 if (commonResources == null)
153 return paths;
154 while (commonResources.hasMoreElements()) {
155 String resource = commonResources.nextElement().getPath();
156 // remove first '/' so that RWT registers it
157 resource = resource.substring(1);
158 if (!resource.endsWith("/")) {
159 paths.add(resource);
160 }
161 }
162 return paths;
163 }
164
165 void loadFontsUrl(URL url) {
166 try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(), UTF_8))) {
167 String line = null;
168 while ((line = in.readLine()) != null) {
169 line = line.trim();
170 if (!line.equals("") && !line.startsWith("#")) {
171 fonts.add(line);
172 }
173 }
174 } catch (IOException e) {
175 throw new IllegalArgumentException("Cannot load URL " + url, e);
176 }
177 }
178
179 void addResources(String pattern) {
180 Enumeration<URL> themeResources = themeBundle.findEntries(basePath, pattern, true);
181 if (themeResources == null)
182 return;
183 while (themeResources.hasMoreElements()) {
184 String resource = themeResources.nextElement().getPath();
185 // remove first '/' so that RWT registers it
186 resource = resource.substring(1);
187 if (!resource.endsWith("/")) {
188 // if (resources.containsKey(resource))
189 // log.warn("Overriding " + resource + " from " + themeBundle.getSymbolicName());
190 // resources.put(resource, themeBRL);
191 imagesPaths.add(resource);
192 }
193
194 }
195
196 }
197
198 @Override
199 public InputStream getResourceAsStream(String resourceName) throws IOException {
200 URL res = themeBundle.getEntry(resourceName);
201 if (res == null) {
202 res = themeBundle.getResource(resourceName);
203 if (res == null)
204 return null;
205 // throw new IllegalArgumentException(
206 // "Resource " + resourceName + " not found in bundle " + themeBundle.getSymbolicName());
207 }
208 return res.openStream();
209 }
210
211 public String getThemeId() {
212 return themeId;
213 }
214
215 // public void setThemeId(String themeId) {
216 // this.themeId = themeId;
217 // }
218 //
219 // public String getBasePath() {
220 // return basePath;
221 // }
222 //
223 // public void setBasePath(String basePath) {
224 // this.basePath = basePath;
225 // }
226 //
227 // public String getRapCssPath() {
228 // return rapCssPath;
229 // }
230 //
231 // public void setRapCssPath(String cssPath) {
232 // this.rapCssPath = cssPath;
233 // }
234
235 @Override
236 public Set<String> getWebCssPaths() {
237 return webCssPaths;
238 }
239
240 @Override
241 public Set<String> getRapCssPaths() {
242 return rapCssPaths;
243 }
244
245 @Override
246 public Set<String> getSwtCssPaths() {
247 return swtCssPaths;
248 }
249
250 @Override
251 public Set<String> getImagesPaths() {
252 return imagesPaths;
253 }
254
255 @Override
256 public InputStream loadPath(String path) throws IOException {
257 URL url = themeBundle.getResource(path);
258 if (url == null)
259 throw new IllegalArgumentException(
260 "Path " + path + " not found in bundle " + themeBundle.getSymbolicName());
261 return url.openStream();
262 }
263
264 private static Bundle findThemeBundle(BundleContext bundleContext, String themeId) {
265 if (themeId == null)
266 return null;
267 // TODO optimize
268 // TODO deal with multiple versions
269 Bundle themeBundle = null;
270 if (themeId != null) {
271 for (Bundle bundle : bundleContext.getBundles())
272 if (themeId.equals(bundle.getSymbolicName())) {
273 themeBundle = bundle;
274 break;
275 }
276 }
277 return themeBundle;
278 }
279
280 }