Skip to content

Commit e610939

Browse files
add white labeling
1 parent a9921ce commit e610939

38 files changed

+8444
-16
lines changed

application/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,10 @@
338338
<artifactId>Java-WebSocket</artifactId>
339339
<scope>test</scope>
340340
</dependency>
341+
<dependency>
342+
<groupId>io.bit3</groupId>
343+
<artifactId>jsass</artifactId>
344+
</dependency>
341345
</dependencies>
342346

343347
<build>
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package org.thingsboard.server.controller;
2+
3+
4+
import com.fasterxml.jackson.databind.JsonNode;
5+
import com.google.common.base.Charsets;
6+
import freemarker.template.Configuration;
7+
import lombok.RequiredArgsConstructor;
8+
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.springframework.core.io.Resource;
10+
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
11+
import org.springframework.http.HttpStatus;
12+
import org.springframework.security.access.prepost.PreAuthorize;
13+
import org.springframework.util.StreamUtils;
14+
import org.springframework.web.bind.annotation.*;
15+
import org.thingsboard.server.common.data.AdminSettings;
16+
import org.thingsboard.server.common.data.exception.ThingsboardException;
17+
import org.thingsboard.server.common.data.id.TenantId;
18+
import org.thingsboard.server.common.data.whitelabel.Palette;
19+
import org.thingsboard.server.common.data.whitelabel.PaletteSettings;
20+
import org.thingsboard.server.dao.settings.AdminSettingsService;
21+
import org.thingsboard.server.queue.util.TbCoreComponent;
22+
import io.bit3.jsass.Compiler;
23+
import io.bit3.jsass.Options;
24+
import io.bit3.jsass.Output;
25+
import io.bit3.jsass.OutputStyle;
26+
import io.bit3.jsass.importer.Import;
27+
28+
import javax.annotation.PostConstruct;
29+
import java.net.URI;
30+
import java.util.*;
31+
32+
@RequiredArgsConstructor
33+
@RestController
34+
@TbCoreComponent
35+
@RequestMapping("/api")
36+
public class WhiteLabelController extends BaseController{
37+
38+
@Autowired
39+
private Configuration freemarkerConfig;
40+
41+
@Autowired
42+
private AdminSettingsService adminSettingsService;
43+
44+
private static final String SCSS_EXTENSION = ".scss";
45+
46+
private static final String SCSS_CLASSPATH_PATTERN = "classpath:scss/*.scss";
47+
48+
private static final String APP_THEME_SCSS = "app-theme.scss";
49+
50+
private static final String LOGIN_THEME_SCSS = "login-theme.scss";
51+
52+
private static final String KEY="whiteLabel";
53+
54+
private Map<String, Import> importMap;
55+
56+
private String scssAppTheme;
57+
58+
private String scssLoginTheme;
59+
60+
private Options options;
61+
62+
63+
private Compiler compiler;
64+
65+
@PostConstruct
66+
public void init() throws Exception {
67+
initCompiler();
68+
}
69+
70+
71+
private void initCompiler() throws Exception {
72+
this.importMap = new HashMap<>();
73+
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
74+
Resource[] scssResources = resolver.getResources(SCSS_CLASSPATH_PATTERN);
75+
for (Resource scssResource : scssResources) {
76+
String scssContent = StreamUtils.copyToString(scssResource.getInputStream(), Charsets.UTF_8);
77+
String fileName = scssResource.getFilename();
78+
if (APP_THEME_SCSS.equals(fileName)) {
79+
this.scssAppTheme = scssContent;
80+
} else if (LOGIN_THEME_SCSS.equals(fileName)) {
81+
this.scssLoginTheme = scssContent;
82+
} else if (fileName != null) {
83+
URI scssFileUri = scssResource.getURI();
84+
Import scssImport = new Import(scssFileUri, scssFileUri, scssContent);
85+
String path = fileName.substring(0, fileName.length() - ".scss".length());
86+
this.importMap.put(path, scssImport);
87+
}
88+
}
89+
this.compiler = new Compiler();
90+
this.options = new Options();
91+
this.options.setImporters(Collections.singleton((url, previous) -> Collections.singletonList(this.importMap.get(url))));
92+
this.options.setOutputStyle(OutputStyle.COMPRESSED);
93+
}
94+
95+
@RequestMapping(value = "/noauth/whiteLabel", method = RequestMethod.GET)
96+
@ResponseStatus(value = HttpStatus.OK)
97+
@ResponseBody
98+
public JsonNode getWhiteLabeling() {
99+
JsonNode jsonNode = null;
100+
AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, KEY);
101+
if(adminSettings != null) {
102+
jsonNode = adminSettings.getJsonValue();
103+
}
104+
return jsonNode;
105+
}
106+
107+
108+
@PreAuthorize("hasAuthority('SYS_ADMIN')")
109+
@RequestMapping(value = "/whiteLabel/whiteLabelParams", method = RequestMethod.POST)
110+
@ResponseStatus(value = HttpStatus.OK)
111+
@ResponseBody
112+
public JsonNode save(@RequestBody JsonNode jsonNode) throws ThingsboardException {
113+
// accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE);
114+
AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, KEY);
115+
if(adminSettings == null) {
116+
adminSettings = new AdminSettings();
117+
adminSettings.setKey(KEY);
118+
}
119+
adminSettings.setJsonValue(jsonNode);
120+
return adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings).getJsonValue();
121+
}
122+
123+
@RequestMapping(value = "/noauth/loginThemeCss", method = RequestMethod.POST,produces = {"plain/text"})
124+
@ResponseStatus(value = HttpStatus.OK)
125+
@ResponseBody
126+
public String loginThemeCss(@RequestBody PaletteSettings paletteSettings) throws ThingsboardException {
127+
try {
128+
return generateThemeCss(paletteSettings, true);
129+
} catch (Exception e) {
130+
throw handleException(e);
131+
}
132+
}
133+
134+
@RequestMapping(value = "/noauth/appThemeCss", method = RequestMethod.POST,produces = {"plain/text"})
135+
@ResponseStatus(value = HttpStatus.OK)
136+
public String appThemeCss(@RequestBody PaletteSettings paletteSettings) throws ThingsboardException {
137+
try {
138+
return generateThemeCss(paletteSettings, false);
139+
} catch (Exception e) {
140+
throw handleException(e);
141+
}
142+
}
143+
144+
145+
146+
private String generateThemeCss(PaletteSettings paletteSettings, boolean loginTheme) throws Exception {
147+
String primaryPaletteName = getPaletteName(paletteSettings.getPrimaryPalette(), true);
148+
String primaryColors = "";
149+
String accentPaletteName = getPaletteName(paletteSettings.getAccentPalette(), false);
150+
String accentColors = "";
151+
String targetTheme = loginTheme ? this.scssLoginTheme : this.scssAppTheme;
152+
targetTheme = targetTheme.replaceAll("\\{\\{primary-palette\\}\\}", primaryPaletteName);
153+
targetTheme = targetTheme.replaceAll("\\{\\{primary-colors\\}\\}", primaryColors);
154+
targetTheme = targetTheme.replaceAll("\\{\\{accent-palette\\}\\}", accentPaletteName);
155+
targetTheme = targetTheme.replaceAll("\\{\\{accent-colors\\}\\}", accentColors);
156+
Output output = this.compiler.compileString(targetTheme, this.options);
157+
return output.getCss();
158+
}
159+
160+
private String getPaletteName(Palette palette, boolean primary) {
161+
if (palette == null || palette.getType() == null){
162+
return primary ? "tb-primary" : "tb-accent";
163+
}
164+
return palette.getType();
165+
}
166+
}

0 commit comments

Comments
 (0)