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.groovy.terminal;
35
36 import groovy.lang.Binding;
37 import groovy.lang.GroovySystem;
38 import groovy.lang.MissingPropertyException;
39
40 import info.magnolia.cms.security.User;
41 import info.magnolia.cms.util.NodeDataUtil;
42 import info.magnolia.context.Context;
43 import info.magnolia.context.MgnlContext;
44 import info.magnolia.context.MgnlContext.Op;
45 import info.magnolia.i18nsystem.SimpleTranslator;
46 import info.magnolia.module.groovy.console.MgnlGroovyConsole;
47 import info.magnolia.module.groovy.console.MgnlGroovyConsoleContext;
48
49 import java.io.ByteArrayInputStream;
50 import java.io.FileInputStream;
51 import java.io.FileNotFoundException;
52 import java.io.InputStream;
53 import java.io.Serializable;
54 import java.io.StringWriter;
55 import java.util.Collection;
56
57 import javax.inject.Inject;
58 import javax.jcr.RepositoryException;
59
60 import org.apache.commons.io.IOUtils;
61 import org.apache.commons.lang.StringUtils;
62 import org.codehaus.groovy.control.CompilationFailedException;
63 import org.codehaus.groovy.runtime.InvokerInvocationException;
64 import org.json.JSONArray;
65 import org.json.JSONException;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69 import com.vaadin.annotations.JavaScript;
70 import com.vaadin.annotations.StyleSheet;
71 import com.vaadin.ui.AbstractJavaScriptComponent;
72 import com.vaadin.ui.JavaScriptFunction;
73
74
75
76
77 @JavaScript({ "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js", "jquery.terminal-0.7.7.min.js", "terminal_connector.js" })
78 @StyleSheet("jquery.terminal.css")
79 public class Terminal extends AbstractJavaScriptComponent {
80
81 private static final Logger log = LoggerFactory.getLogger(Terminal.class);
82 private SimpleTranslator simpleTranslator;
83 private boolean useSystemContext;
84
85 public static final String BINDING_SESSION_ATTRIBUTE = "info.magnolia.module.groovy.console.binding.session.attribute";
86
87
88
89
90 @Inject
91 public Terminal(final SimpleTranslator simpleTranslator, final boolean useSystemContext) {
92 this.simpleTranslator = simpleTranslator;
93 this.useSystemContext = useSystemContext;
94 this.getState().greetings = simpleTranslator.translate("console.greetings", GroovySystem.getVersion());
95
96 addFunction("executeCommand", new JavaScriptFunction() {
97 @Override
98 public void call(JSONArray arguments) throws JSONException {
99 getState().command = arguments.getString(0);
100 resetState();
101 try {
102 if (isUseSystemContext()) {
103 MgnlContext.doInSystemContext(new Op<Void, Exception>() {
104
105 @Override
106 public Void exec() throws Exception {
107 execute(getCommand());
108 return null;
109 }
110 });
111 } else {
112 execute(getCommand());
113 }
114 } catch (Exception e) {
115 getState().output = e.getMessage();
116 }
117 }
118 });
119
120 addFunction("saveStatus", new JavaScriptFunction() {
121 @Override
122 public void call(JSONArray arguments) throws JSONException {
123 resetState();
124 getState().view = arguments.getString(0);
125 getState().history = arguments.getString(1);
126 log.debug("saved status [view:{}, history{}]", getState().view, getState().history);
127 }
128 });
129
130 setSizeFull();
131 }
132
133 public boolean isUseSystemContext() {
134 return useSystemContext;
135 }
136
137 public String getCommand() {
138 return getState().command;
139 }
140
141 @Override
142 public TerminalState getState() {
143 return (TerminalState) super.getState();
144 }
145
146 public void execute(String command) throws Exception {
147 User currentUser = MgnlContext.getUser();
148 if (!isAuthorized(currentUser)) {
149 String msg = simpleTranslator.translate("console.user.unauthorized", currentUser.getName());
150 log.warn(msg);
151 getState().output = msg;
152 return;
153 }
154 log.debug("executing command [{}]", command);
155 StringWriter sw = new StringWriter();
156 if (handleCommand(command, sw)) {
157 getState().output = sw.toString();
158 } else {
159 getState().output = evaluate(new ByteArrayInputStream(command.getBytes()));
160 }
161 }
162
163 private String evaluate(final InputStream is) {
164
165 Binding binding = MgnlContext.getAttribute(BINDING_SESSION_ATTRIBUTE, Context.SESSION_SCOPE);
166 MgnlGroovyConsole console = null;
167 if (binding == null) {
168 Binding newBinding = new SerializableBinding();
169 MgnlContext.setAttribute(BINDING_SESSION_ATTRIBUTE, newBinding, Context.SESSION_SCOPE);
170 console = new MgnlGroovyConsole(newBinding);
171 } else {
172 console = new MgnlGroovyConsole(binding);
173 }
174
175 Object lastResult;
176 Context originalCtx = MgnlContext.getInstance();
177 MgnlGroovyConsoleContext groovyCtx = new MgnlGroovyConsoleContext(originalCtx);
178 MgnlContext.setInstance(groovyCtx);
179 StringWriter sw = new StringWriter();
180 try {
181 lastResult = console.evaluate(is, console.generateScriptName(), sw);
182
183 sw.write("===> \n");
184 sw.write(lastResult != null ? lastResult.toString() : "");
185 } catch (CompilationFailedException e) {
186 sw.write(e.getMessage());
187 } catch (Throwable e) {
188
189 if (e instanceof InvokerInvocationException) {
190 e = e.getCause();
191 }
192 if (e instanceof MissingPropertyException) {
193 sw.write(e.getMessage());
194 } else {
195 log.error("Error while evaluating script: ", e);
196 sw.write(e.getClass().getSimpleName() + ": " + e.getMessage());
197 }
198 } finally {
199 MgnlContext.setInstance(originalCtx);
200 }
201 return sw.toString();
202 }
203
204
205
206
207
208
209 public static final class SerializableBinding extends Binding implements Serializable {
210
211 public static final long serialVersionUID = 42L;
212 }
213
214
215
216
217 private boolean handleCommand(final String command, final StringWriter out) throws RepositoryException {
218 if (StringUtils.isEmpty(command)) {
219
220 return true;
221 }
222 String[] tokens = command.trim().split("\\s+");
223 if ("help".equals(tokens[0]) || "?".equals(tokens[0])) {
224 out.write(simpleTranslator.translate("console.help"));
225 return true;
226 } else if ("clear".equals(tokens[0])) {
227 return true;
228 } else if ("clean".equals(tokens[0])) {
229 MgnlContext.setAttribute(BINDING_SESSION_ATTRIBUTE, new SerializableBinding(), Context.SESSION_SCOPE);
230 return true;
231 } else if ("run".equals(tokens[0])) {
232 if (tokens.length != 2) {
233 out.write(simpleTranslator.translate("console.commands.run.usage"));
234 return true;
235 }
236 InputStream is = null;
237 String path = tokens[1];
238
239 String source = NodeDataUtil.getString("scripts", path.endsWith("/") ? path + "text" : path + "/text");
240 try {
241 if (StringUtils.isNotEmpty(source)) {
242 is = new ByteArrayInputStream(source.getBytes());
243 }
244 else {
245
246 is = new FileInputStream(path);
247 }
248 evaluate(is);
249 return true;
250 } catch (FileNotFoundException e) {
251 out.write(e.getMessage());
252 return true;
253 } finally {
254 IOUtils.closeQuietly(is);
255 }
256 }
257 return false;
258 }
259
260 private void resetState() {
261 getState().output = "";
262 getState().view = "";
263 getState().history = "";
264 }
265
266 protected boolean isAuthorized(User currentUser) {
267 final Collection<String> roles = currentUser.getRoles();
268 return roles.contains("superuser") || roles.contains("scripter");
269 }
270
271 }