]> git.argeo.org Git - gpl/argeo-suite.git/blob - ui/SuiteApp.java
Prepare next development cycle
[gpl/argeo-suite.git] / ui / SuiteApp.java
1 package org.argeo.suite.ui;
2
3 import static org.argeo.cms.ui.CmsView.CMS_VIEW_UID_PROPERTY;
4
5 import java.util.Collections;
6 import java.util.HashMap;
7 import java.util.HashSet;
8 import java.util.Map;
9 import java.util.Set;
10 import java.util.TreeMap;
11 import java.util.TreeSet;
12
13 import javax.jcr.Node;
14 import javax.jcr.RepositoryException;
15 import javax.jcr.Session;
16 import javax.jcr.nodetype.NodeType;
17
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20 import org.argeo.api.NodeUtils;
21 import org.argeo.cms.ui.AbstractCmsApp;
22 import org.argeo.cms.ui.CmsTheme;
23 import org.argeo.cms.ui.CmsUiProvider;
24 import org.argeo.cms.ui.CmsView;
25 import org.argeo.cms.ui.dialogs.CmsFeedback;
26 import org.argeo.cms.ui.util.CmsEvent;
27 import org.argeo.cms.ui.util.CmsUiUtils;
28 import org.argeo.entity.EntityConstants;
29 import org.argeo.entity.EntityNames;
30 import org.argeo.entity.EntityTypes;
31 import org.argeo.jcr.Jcr;
32 import org.argeo.jcr.JcrUtils;
33 import org.argeo.suite.RankedObject;
34 import org.eclipse.swt.SWT;
35 import org.eclipse.swt.widgets.Composite;
36 import org.osgi.framework.Constants;
37 import org.osgi.service.event.Event;
38 import org.osgi.service.event.EventHandler;
39
40 /** The Argeo Suite App. */
41 public class SuiteApp extends AbstractCmsApp implements EventHandler {
42 private final static Log log = LogFactory.getLog(SuiteApp.class);
43
44 public final static String PID_PREFIX = "argeo.suite.ui.";
45 public final static String HEADER_PID = PID_PREFIX + "header";
46 public final static String LEAD_PANE_PID = PID_PREFIX + "leadPane";
47 public final static String LOGIN_SCREEN_PID = PID_PREFIX + "loginScreen";
48 // public final static String DASHBOARD_LAYER_PID = PID_PREFIX +
49 // "dashboardLayer";
50 public final static String DASHBOARD_PID = PID_PREFIX + "dashboard";
51 public final static String RECENT_ITEMS_PID = PID_PREFIX + "recentItems";
52
53 private final static String DEFAULT_UI_NAME = "app";
54 private final static String DEFAULT_THEME_ID = "org.argeo.suite.theme.default";
55
56 private Map<String, RankedObject<CmsUiProvider>> uiProvidersByPid = Collections.synchronizedMap(new HashMap<>());
57 private Map<String, RankedObject<CmsUiProvider>> uiProvidersByType = Collections.synchronizedMap(new HashMap<>());
58 private Map<String, RankedObject<SuiteLayer>> layers = Collections.synchronizedSortedMap(new TreeMap<>());
59
60 // TODO make more optimal or via CmsSession/CmsView
61 private Map<String, SuiteUi> managedUis = new HashMap<>();
62
63 // private CmsUiProvider headerPart = null;
64
65 public void init(Map<String, String> properties) {
66 if (log.isDebugEnabled())
67 log.info("Argeo Suite App started");
68 }
69
70 public void destroy(Map<String, String> properties) {
71 for (SuiteUi ui : managedUis.values())
72 if (!ui.isDisposed())
73 ui.dispose();
74 if (log.isDebugEnabled())
75 log.info("Argeo Suite App stopped");
76
77 }
78
79 @Override
80 public Set<String> getUiNames() {
81 HashSet<String> uiNames = new HashSet<>();
82 uiNames.add(DEFAULT_UI_NAME);
83 return uiNames;
84 }
85
86 @Override
87 public Composite initUi(Composite parent) {
88 String uiName = parent.getData(UI_NAME_PROPERTY) != null ? parent.getData(UI_NAME_PROPERTY).toString() : null;
89 CmsView cmsView = CmsView.getCmsView(parent);
90 if (cmsView == null)
91 throw new IllegalStateException("No CMS view is registered.");
92 CmsTheme theme = getTheme(uiName);
93 if (theme != null)
94 CmsTheme.registerCmsTheme(parent.getShell(), theme);
95 SuiteUi argeoSuiteUi = new SuiteUi(parent, SWT.NONE);
96 String uid = cmsView.getUid();
97 managedUis.put(uid, argeoSuiteUi);
98 argeoSuiteUi.addDisposeListener((e) -> {
99 managedUis.remove(uid);
100 if (log.isDebugEnabled())
101 log.debug("Suite UI " + uid + " has been disposed.");
102 });
103 refreshUi(argeoSuiteUi, null);
104 return argeoSuiteUi;
105 }
106
107 @Override
108 public String getThemeId(String uiName) {
109 // TODO make it configurable
110 return DEFAULT_THEME_ID;
111 }
112
113 @Override
114 public void refreshUi(Composite parent, String state) {
115 try {
116 Node context = null;
117 SuiteUi ui = (SuiteUi) parent;
118 refreshPart(findUiProvider(HEADER_PID), ui.getHeader(), context);
119 CmsView cmsView = CmsView.getCmsView(parent);
120 if (cmsView.isAnonymous()) {
121 ui.refreshBelowHeader(false);
122 refreshPart(findUiProvider(LOGIN_SCREEN_PID), ui.getBelowHeader(), context);
123 } else {
124 try {
125 if (ui.getUserHome() == null)
126 ui.initSessions(getRepository());
127 context = ui.getUserHome();
128
129 } catch (RepositoryException e) {
130 e.printStackTrace();
131 }
132 ui.refreshBelowHeader(true);
133
134 for (String key : layers.keySet()) {
135 SuiteLayer layer = layers.get(key).get();
136 ui.addLayer(key, layer);
137 }
138
139 // ui.addLayer(ArgeoSuiteUi.DASHBOARD_LAYER);
140 // ui.addLayer("documents");
141 // ui.addLayer("locations");
142 // ui.addLayer("people");
143 // ui.switchToLayer(DASHBOARD_LAYER_PID, context);
144
145 // refreshPart(findUiProvider(DASHBOARD_PID), ui.getTabbedArea().getCurrent(), context);
146 refreshPart(findUiProvider(LEAD_PANE_PID), ui.getLeadPane(), context);
147 // refreshPart(findUiProvider(RECENT_ITEMS_PID), ui.getEntryArea(), context);
148 }
149 ui.layout(true, true);
150 } catch (Exception e) {
151 CmsFeedback.show("Unexpected exception", e);
152 }
153 }
154
155 private void refreshPart(CmsUiProvider uiProvider, Composite part, Node context) {
156 CmsUiUtils.clear(part);
157 uiProvider.createUiPart(part, context);
158 }
159
160 private CmsUiProvider findUiProvider(String pid) {
161 if (!uiProvidersByPid.containsKey(pid))
162 throw new IllegalArgumentException("No UI provider registered as " + pid);
163 return uiProvidersByPid.get(pid).get();
164 }
165
166 private CmsUiProvider findUiProvider(Node context) {
167 try {
168 // mixins
169 Set<String> types = new TreeSet<>();
170 for (NodeType nodeType : context.getMixinNodeTypes()) {
171 String typeName = nodeType.getName();
172 if (uiProvidersByType.containsKey(typeName)) {
173 types.add(typeName);
174 }
175 }
176 // primary node type
177 {
178 NodeType nodeType = context.getPrimaryNodeType();
179 String typeName = nodeType.getName();
180 if (uiProvidersByType.containsKey(typeName)) {
181 types.add(typeName);
182 }
183 }
184 // entity type
185 if (context.isNodeType(EntityTypes.ENTITY_ENTITY)) {
186 String typeName =context.getProperty(EntityNames.ENTITY_TYPE).getString();
187 if (uiProvidersByType.containsKey(typeName)) {
188 types.add(typeName);
189 }
190 }
191
192 // if (context.getPath().equals("/")) {// root node
193 // types.add("nt:folder");
194 // }
195 if (NodeUtils.isUserHome(context) && uiProvidersByType.containsKey("nt:folder")) {// home node
196 types.add("nt:folder");
197 }
198
199 if (types.size() == 0)
200 throw new IllegalArgumentException("No UI provider found for " + context);
201 return uiProvidersByType.get(types.iterator().next()).get();
202 } catch (RepositoryException e) {
203 throw new IllegalStateException(e);
204 }
205 }
206 // private CmsUiProvider findUiProvider(String pid, Node context) {
207 // CmsUiProvider found = null;
208 // if (pid != null) {
209 // SortedMap<RankingKey, CmsUiProvider> subMap = uiProvidersByPid.subMap(RankingKey.minPid(pid),
210 // RankingKey.maxPid(pid));
211 // providers: for (RankingKey key : subMap.keySet()) {
212 // if (key.getPid() == null || !key.getPid().equals(pid))
213 // break providers;
214 // found = subMap.get(key);
215 // }
216 // if (found != null)
217 // return found;
218 // }
219 //
220 // if (found == null && context != null) {
221 // SortedMap<RankingKey, CmsUiProvider> subMap = null;
222 // String dataType = null;
223 // if (Jcr.isNodeType(context, EntityTypes.ENTITY_ENTITY)) {
224 // dataType = Jcr.get(context, EntityNames.ENTITY_TYPE);
225 // subMap = uiProvidersByPid.subMap(RankingKey.minDataType(dataType), RankingKey.maxDataType(dataType));
226 // }
227 // providers: for (RankingKey key : subMap.keySet()) {
228 // if (key.getDataType() == null || !key.getDataType().equals(dataType))
229 // break providers;
230 // found = subMap.get(key);
231 // }
232 // if (found == null)
233 // found = uiProvidersByPid.get(new RankingKey(null, null, null, dataType, null));
234 // if (found != null)
235 // return found;
236 // }
237 //
238 // // nothing
239 // if (log.isWarnEnabled())
240 // log.warn("No UI provider found for" + (pid != null ? " pid " + pid : "")
241 // + (context != null ? " " + context : ""));
242 // return new CmsUiProvider() {
243 //
244 // @Override
245 // public Control createUi(Composite parent, Node context) throws RepositoryException {
246 // return parent;
247 // }
248 // };
249 // }
250
251 @Override
252 public void setState(Composite parent, String state) {
253 CmsView cmsView = CmsView.getCmsView(parent);
254 // for the time being we systematically open a session, in order to make sure
255 // that home is initialised
256 Session session = null;
257 try {
258 if (state != null && state.startsWith("/")) {
259 String path = state.substring(1);
260 String workspace;
261 if (path.equals("")) {
262 workspace = null;
263 path = "/";
264 } else {
265 int index = path.indexOf('/');
266 if (index == 0) {
267 log.error("Cannot interpret // " + state);
268 cmsView.navigateTo("~");
269 return;
270 } else if (index > 0) {
271 workspace = path.substring(0, index);
272 path = path.substring(index);
273 } else {// index<0, assuming root node
274 workspace = path;
275 path = "/";
276 }
277 }
278 session = getRepository().login(workspace);
279
280 Node node = session.getNode(path);
281
282 refreshEntityUi(null, node);
283 }
284 } catch (RepositoryException e) {
285 log.error("Cannot load state " + state, e);
286 cmsView.navigateTo("~");
287 } finally {
288 JcrUtils.logoutQuietly(session);
289 }
290 }
291
292 private void refreshEntityUi(Composite parent, Node context) {
293 }
294
295 /*
296 * Dependency injection.
297 */
298
299 public void addUiProvider(CmsUiProvider uiProvider, Map<String, Object> properties) {
300 if (properties.containsKey(Constants.SERVICE_PID)) {
301 String pid = (String) properties.get(Constants.SERVICE_PID);
302 RankedObject.putIfHigherRank(uiProvidersByPid, pid, uiProvider, properties);
303 }
304 if (properties.containsKey(EntityConstants.TYPE)) {
305 // TODO manage String arrays as well
306 String type = (String) properties.get(EntityConstants.TYPE);
307 RankedObject.putIfHigherRank(uiProvidersByType, type, uiProvider, properties);
308 }
309 }
310
311 public void removeUiProvider(CmsUiProvider uiProvider, Map<String, Object> properties) {
312 if (properties.containsKey(Constants.SERVICE_PID)) {
313 String pid = (String) properties.get(Constants.SERVICE_PID);
314 if (uiProvidersByPid.containsKey(pid)) {
315 if (uiProvidersByPid.get(pid).equals(new RankedObject<CmsUiProvider>(uiProvider, properties))) {
316 uiProvidersByPid.remove(pid);
317 }
318 }
319 }
320
321 }
322
323 public void addLayer(SuiteLayer layer, Map<String, Object> properties) {
324 if (properties.containsKey(Constants.SERVICE_PID)) {
325 String pid = (String) properties.get(Constants.SERVICE_PID);
326 RankedObject.putIfHigherRank(layers, pid, layer, properties);
327 }
328 }
329
330 public void removeLayer(SuiteLayer layer, Map<String, Object> properties) {
331 if (properties.containsKey(Constants.SERVICE_PID)) {
332 String pid = (String) properties.get(Constants.SERVICE_PID);
333 if (layers.containsKey(pid)) {
334 if (layers.get(pid).equals(new RankedObject<SuiteLayer>(layer, properties))) {
335 layers.remove(pid);
336 }
337 }
338 }
339 }
340
341 @Override
342 public void handleEvent(Event event) {
343
344 // Specific UI related events
345 SuiteUi ui = getRelatedUi(event);
346 try {
347 String currentLayerId = ui.getCurrentLayerId();
348 SuiteLayer layer = currentLayerId != null ? layers.get(currentLayerId).get() : null;
349 if (isTopic(event, SuiteEvent.refreshPart)) {
350 String nodeId = get(event, SuiteEvent.NODE_ID);
351 String workspace = get(event, SuiteEvent.WORKSPACE);
352 Node node = Jcr.getNodeById(ui.getSession(workspace), nodeId);
353 CmsUiProvider uiProvider = findUiProvider(node);
354 layer.view(uiProvider, ui.getCurrentWorkArea(), node);
355 } else if (isTopic(event, SuiteEvent.openNewPart)) {
356 String nodeId = get(event, SuiteEvent.NODE_ID);
357 String workspace = get(event, SuiteEvent.WORKSPACE);
358 Node node = Jcr.getNodeById(ui.getSession(workspace), nodeId);
359 CmsUiProvider uiProvider = findUiProvider(node);
360 layer.open(uiProvider, ui.getCurrentWorkArea(), node);
361 } else if (isTopic(event, SuiteEvent.switchLayer)) {
362 String layerId = get(event, SuiteEvent.LAYER);
363 ui.switchToLayer(layerId, Jcr.getRootNode(ui.getSession(null)));
364 }
365 } catch (Exception e) {
366 log.error("Cannot handle event " + event, e);
367 // CmsView.getCmsView(ui).exception(e);
368 }
369
370 }
371
372 private SuiteUi getRelatedUi(Event event) {
373 return managedUis.get(get(event, CMS_VIEW_UID_PROPERTY));
374 }
375
376 private static boolean isTopic(Event event, CmsEvent cmsEvent) {
377 return event.getTopic().equals(cmsEvent.topic());
378 }
379
380 private static String get(Event event, String key) {
381 Object value = event.getProperty(key);
382 if (value == null)
383 throw new IllegalArgumentException("Property " + key + " must be set");
384 return value.toString();
385
386 }
387 }