]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/AbstractCmsEntryPoint.java
Does not force header part (lead, center, end) to have the same size
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / AbstractCmsEntryPoint.java
1 package org.argeo.cms;
2
3 import java.util.HashMap;
4 import java.util.Locale;
5 import java.util.Map;
6 import java.util.ResourceBundle;
7
8 import javax.jcr.Node;
9 import javax.jcr.Property;
10 import javax.jcr.Repository;
11 import javax.jcr.RepositoryException;
12 import javax.jcr.Session;
13 import javax.jcr.nodetype.NodeType;
14 import javax.security.auth.Subject;
15 import javax.security.auth.login.LoginException;
16
17 import org.apache.commons.logging.Log;
18 import org.apache.commons.logging.LogFactory;
19 import org.argeo.ArgeoException;
20 import org.argeo.cms.auth.ArgeoLoginContext;
21 import org.argeo.cms.auth.LoginRequiredException;
22 import org.argeo.cms.i18n.Msg;
23 import org.argeo.jcr.JcrUtils;
24 import org.eclipse.rap.rwt.RWT;
25 import org.eclipse.rap.rwt.application.AbstractEntryPoint;
26 import org.eclipse.rap.rwt.client.WebClient;
27 import org.eclipse.rap.rwt.client.service.BrowserNavigation;
28 import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent;
29 import org.eclipse.rap.rwt.client.service.BrowserNavigationListener;
30 import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
31 import org.eclipse.swt.widgets.Composite;
32 import org.eclipse.swt.widgets.Display;
33 import org.eclipse.swt.widgets.Shell;
34
35 /** Manages history and navigation */
36 public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint
37 implements CmsSession {
38 private final Log log = LogFactory.getLog(AbstractCmsEntryPoint.class);
39
40 private final Subject subject = new Subject();
41
42 private final Repository repository;
43 private final String workspace;
44 private final String defaultPath;
45 private final Map<String, String> factoryProperties;
46
47 // Current state
48 private Session session;
49 private Node node;
50 private String state;
51 private String page;
52 private Throwable exception;
53
54 // Client services
55 private final JavaScriptExecutor jsExecutor;
56 private final BrowserNavigation browserNavigation;
57
58 public AbstractCmsEntryPoint(Repository repository, String workspace,
59 String defaultPath, Map<String, String> factoryProperties) {
60 this.repository = repository;
61 this.workspace = workspace;
62 this.defaultPath = defaultPath;
63 this.factoryProperties = new HashMap<String, String>(factoryProperties);
64
65 // Initial login
66 try {
67 new ArgeoLoginContext(KernelHeader.LOGIN_CONTEXT_USER, subject)
68 .login();
69 } catch (LoginException e) {
70 // if (log.isTraceEnabled())
71 // log.trace("Cannot authenticate user", e);
72 try {
73 new ArgeoLoginContext(KernelHeader.LOGIN_CONTEXT_ANONYMOUS,
74 subject).login();
75 } catch (LoginException eAnonymous) {
76 throw new ArgeoException("Cannot initialize subject",
77 eAnonymous);
78 }
79 }
80 authChange();
81
82 jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class);
83 browserNavigation = RWT.getClient().getService(BrowserNavigation.class);
84 if (browserNavigation != null)
85 browserNavigation
86 .addBrowserNavigationListener(new CmsNavigationListener());
87 }
88
89 @Override
90 protected Shell createShell(Display display) {
91 Shell shell = super.createShell(display);
92 shell.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_SHELL);
93 display.disposeExec(new Runnable() {
94
95 @Override
96 public void run() {
97 if (log.isTraceEnabled())
98 log.trace("Logging out " + session);
99 JcrUtils.logoutQuietly(session);
100 }
101 });
102 return shell;
103 }
104
105 @Override
106 protected final void createContents(Composite parent) {
107 try {
108 getShell().getDisplay().setData(CmsSession.KEY, this);
109
110 createUi(parent);
111 } catch (Exception e) {
112 throw new CmsException("Cannot create entrypoint contents", e);
113 }
114 }
115
116 /** Create UI */
117 protected abstract void createUi(Composite parent);
118
119 /** Recreate UI after navigation or auth change */
120 protected abstract void refresh();
121
122 /**
123 * The node to return when no node was found (for authenticated users and
124 * anonymous)
125 */
126 protected Node getDefaultNode(Session session) throws RepositoryException {
127 if (!session.hasPermission(defaultPath, "read")) {
128 if (session.getUserID().equals("anonymous"))
129 throw new LoginRequiredException();
130 else
131 throw new CmsException("Unauthorized");
132 }
133 return session.getNode(defaultPath);
134 }
135
136 protected String getBaseTitle() {
137 return factoryProperties.get(WebClient.PAGE_TITLE);
138 }
139
140 public void navigateTo(String state) {
141 exception = null;
142 String title = setState(state);
143 refresh();
144 if (browserNavigation != null)
145 browserNavigation.pushState(state, title);
146 }
147
148 @Override
149 public Subject getSubject() {
150 return subject;
151 }
152
153 @Override
154 public void authChange() {
155 try {
156 String currentPath = null;
157 if (node != null)
158 currentPath = node.getPath();
159 JcrUtils.logoutQuietly(session);
160
161 session = repository.login(workspace);
162 if (currentPath != null)
163 try {
164 node = session.getNode(currentPath);
165 } catch (Exception e) {
166 try {
167 // TODO find a less hacky way to log out
168 new ArgeoLoginContext(
169 KernelHeader.LOGIN_CONTEXT_ANONYMOUS, subject)
170 .logout();
171 new ArgeoLoginContext(
172 KernelHeader.LOGIN_CONTEXT_ANONYMOUS, subject)
173 .login();
174 } catch (LoginException eAnonymous) {
175 throw new ArgeoException("Cannot reset to anonymous",
176 eAnonymous);
177 }
178 JcrUtils.logoutQuietly(session);
179 session = repository.login(workspace);
180 navigateTo("~");
181 throw e;
182 }
183
184 // refresh UI
185 refresh();
186 } catch (RepositoryException e) {
187 throw new CmsException("Cannot perform auth change", e);
188 }
189
190 }
191
192 @Override
193 public void exception(Throwable e) {
194 this.exception = e;
195 log.error("Unexpected exception in CMS", e);
196 refresh();
197 }
198
199 @Override
200 public Object local(Msg msg) {
201 String key = msg.getId();
202 int lastDot = key.lastIndexOf('.');
203 String className = key.substring(0, lastDot);
204 String fieldName = key.substring(lastDot + 1);
205 Locale locale = RWT.getLocale();
206 ResourceBundle rb = ResourceBundle.getBundle(className, locale,
207 msg.getClassLoader());
208 return rb.getString(fieldName);
209 }
210
211 /** Sets the state of the entry point and retrieve the related JCR node. */
212 protected synchronized String setState(String newState) {
213 String previousState = this.state;
214
215 node = null;
216 page = null;
217 this.state = newState;
218 if (newState.equals("~"))
219 this.state = "";
220
221 try {
222 int firstSlash = state.indexOf('/');
223 if (firstSlash == 0) {
224 if (session.nodeExists(state))
225 node = session.getNode(state);
226 else
227 throw new CmsException("Data " + state + " does not exist");
228 page = "";
229 } else if (firstSlash > 0) {
230 String prefix = state.substring(0, firstSlash);
231 String path = state.substring(firstSlash);
232 if (session.nodeExists(path))
233 node = session.getNode(path);
234 else
235 throw new CmsException("Data " + path + " does not exist");
236 page = prefix;
237 } else {
238 node = getDefaultNode(session);
239 page = state;
240 }
241
242 // Title
243 String title;
244 if (node.isNodeType(NodeType.MIX_TITLE)
245 && node.hasProperty(Property.JCR_TITLE))
246 title = node.getProperty(Property.JCR_TITLE).getString()
247 + " - " + getBaseTitle();
248 else
249 title = getBaseTitle();
250 jsExecutor.execute("document.title = \"" + title + "\"");
251
252 if (log.isTraceEnabled())
253 log.trace("node=" + node + ", state=" + state + " (page="
254 + page + ", title=" + title + ")");
255
256 return title;
257 } catch (Exception e) {
258 log.error("Cannot set state '" + state + "'", e);
259 if (previousState.equals(""))
260 previousState = "~";
261 navigateTo(previousState);
262 throw new CmsException("Unexpected issue when accessing #"
263 + newState, e);
264 }
265 }
266
267 protected Node getNode() {
268 return node;
269 }
270
271 protected String getState() {
272 return state;
273 }
274
275 protected Throwable getException() {
276 return exception;
277 }
278
279 protected void resetException() {
280 exception = null;
281 }
282
283 protected Session getSession() {
284 return session;
285 }
286
287 private class CmsNavigationListener implements BrowserNavigationListener {
288 private static final long serialVersionUID = -3591018803430389270L;
289
290 @Override
291 public void navigated(BrowserNavigationEvent event) {
292 setState(event.getState());
293 refresh();
294 }
295 }
296 }