001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.util.bridges.scripting;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.portlet.PortletResponseUtil;
020    import com.liferay.portal.kernel.scripting.ScriptingException;
021    import com.liferay.portal.kernel.scripting.ScriptingHelperUtil;
022    import com.liferay.portal.kernel.scripting.ScriptingUtil;
023    import com.liferay.portal.kernel.servlet.SessionErrors;
024    import com.liferay.portal.kernel.util.ContentTypes;
025    import com.liferay.portal.kernel.util.FileUtil;
026    import com.liferay.portal.kernel.util.HtmlUtil;
027    import com.liferay.portal.kernel.util.StringBundler;
028    import com.liferay.portal.kernel.util.StringPool;
029    import com.liferay.portal.kernel.util.StringUtil;
030    import com.liferay.portal.kernel.util.Validator;
031    import com.liferay.portal.kernel.util.WebKeys;
032    import com.liferay.portal.security.permission.ActionKeys;
033    import com.liferay.portal.security.permission.PermissionChecker;
034    import com.liferay.portal.service.permission.PortalPermissionUtil;
035    import com.liferay.portal.theme.ThemeDisplay;
036    
037    import java.io.IOException;
038    import java.io.InputStream;
039    
040    import java.util.Map;
041    
042    import javax.portlet.ActionRequest;
043    import javax.portlet.ActionResponse;
044    import javax.portlet.GenericPortlet;
045    import javax.portlet.PortletConfig;
046    import javax.portlet.PortletContext;
047    import javax.portlet.PortletException;
048    import javax.portlet.PortletRequest;
049    import javax.portlet.PortletResponse;
050    import javax.portlet.RenderRequest;
051    import javax.portlet.RenderResponse;
052    import javax.portlet.ResourceRequest;
053    import javax.portlet.ResourceResponse;
054    
055    /**
056     * @author Jorge Ferrer
057     * @author Brian Wing Shun Chan
058     * @author Alberto Montero
059     */
060    public class ScriptingPortlet extends GenericPortlet {
061    
062            @Override
063            public void doDispatch(
064                            RenderRequest renderRequest, RenderResponse renderResponse)
065                    throws IOException, PortletException {
066    
067                    String fileName = getFileName(renderRequest);
068    
069                    if (fileName != null) {
070                            include(fileName, renderRequest, renderResponse);
071                    }
072                    else {
073                            super.doDispatch(renderRequest, renderResponse);
074                    }
075            }
076    
077            @Override
078            public void doEdit(
079                            RenderRequest renderRequest, RenderResponse renderResponse)
080                    throws IOException, PortletException {
081    
082                    if (renderRequest.getPreferences() == null) {
083                            super.doEdit(renderRequest, renderResponse);
084                    }
085                    else {
086                            include(editFile, renderRequest, renderResponse);
087                    }
088            }
089    
090            @Override
091            public void doHelp(
092                            RenderRequest renderRequest, RenderResponse renderResponse)
093                    throws IOException, PortletException {
094    
095                    include(helpFile, renderRequest, renderResponse);
096            }
097    
098            @Override
099            public void doView(
100                            RenderRequest renderRequest, RenderResponse renderResponse)
101                    throws IOException, PortletException {
102    
103                    include(viewFile, renderRequest, renderResponse);
104            }
105    
106            @Override
107            public void init() throws PortletException {
108                    super.init();
109    
110                    filePath = getInitParameter("file-path");
111    
112                    if (Validator.isNull(filePath)) {
113                            throw new PortletException("file-path parameter is not set");
114                    }
115                    else if (filePath.contains(StringPool.BACK_SLASH) ||
116                                     filePath.contains(StringPool.DOUBLE_SLASH) ||
117                                     filePath.contains(StringPool.PERIOD) ||
118                                     filePath.contains(StringPool.SPACE)) {
119    
120                            throw new PortletException(
121                                    "template-path " + filePath + " has invalid characters");
122                    }
123                    else if (!filePath.startsWith(StringPool.SLASH) ||
124                                     !filePath.endsWith(StringPool.SLASH)) {
125    
126                            throw new PortletException(
127                                    "template-path " + filePath +
128                                            " must start and end with a /");
129                    }
130    
131                    actionFile = getInitParameter("action-file");
132                    editFile = getInitParameter("edit-file");
133                    helpFile = getInitParameter("help-file");
134                    resourceFile = getInitParameter("resource-file");
135                    viewFile = getInitParameter("view-file");
136    
137                    language = getInitParameter("scripting-language");
138                    globalFiles = StringUtil.split(getInitParameter("global-files"));
139            }
140    
141            @Override
142            public void processAction(
143                            ActionRequest actionRequest, ActionResponse actionResponse)
144                    throws IOException, PortletException {
145    
146                    include(actionFile, actionRequest, actionResponse);
147            }
148    
149            @Override
150            public void render(
151                            RenderRequest renderRequest, RenderResponse renderResponse)
152                    throws IOException, PortletException {
153    
154                    try {
155                            doRender(renderRequest, renderResponse);
156                    }
157                    catch (IOException ioe) {
158                            throw ioe;
159                    }
160                    catch (PortletException pe) {
161                            throw pe;
162                    }
163                    catch (Exception e) {
164                            throw new PortletException(e);
165                    }
166            }
167    
168            @Override
169            public void serveResource(
170                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
171                    throws IOException, PortletException {
172    
173                    include(resourceFile, resourceRequest, resourceResponse);
174            }
175    
176            protected void checkPath(String path) throws PortletException {
177                    if (Validator.isNotNull(path) &&
178                            (!path.startsWith(filePath) ||
179                             !Validator.isFilePath(path, false))) {
180    
181                            throw new PortletException(
182                                    "Path " + path + " is not accessible by this portlet");
183                    }
184            }
185    
186            protected void declareBeans(
187                            InputStream is, PortletRequest portletRequest,
188                            PortletResponse portletResponse)
189                    throws IOException, ScriptingException {
190    
191                    String script = new String(FileUtil.getBytes(is));
192    
193                    declareBeans(script, portletRequest, portletResponse);
194            }
195    
196            protected void declareBeans(
197                            String script, PortletRequest portletRequest,
198                            PortletResponse portletResponse)
199                    throws IOException, ScriptingException {
200    
201                    script = getGlobalScript() + script;
202    
203                    PortletConfig portletConfig = getPortletConfig();
204                    PortletContext portletContext = getPortletContext();
205    
206                    Map<String, Object> portletObjects =
207                            ScriptingHelperUtil.getPortletObjects(
208                                    portletConfig, portletContext, portletRequest, portletResponse);
209    
210                    ScriptingUtil.exec(
211                            null, portletObjects, language, script, StringPool.EMPTY_ARRAY);
212            }
213    
214            protected void doRender(
215                            RenderRequest renderRequest, RenderResponse renderResponse)
216                    throws Exception {
217    
218                    Object error = SessionErrors.get(renderRequest, _ERROR);
219    
220                    if (error != null) {
221                            Exception e = (Exception)error;
222    
223                            writeErrorMessage(renderRequest, renderResponse, e.getMessage());
224    
225                            return;
226                    }
227    
228                    super.render(renderRequest, renderResponse);
229    
230                    error = SessionErrors.get(renderRequest, _ERROR);
231    
232                    if (error != null) {
233                            Exception e = (Exception)error;
234    
235                            writeErrorMessage(renderRequest, renderResponse, e.getMessage());
236                    }
237            }
238    
239            protected String getFileName(RenderRequest renderRequest) {
240                    return renderRequest.getParameter("file");
241            }
242    
243            protected String getGlobalScript() throws IOException {
244                    if (globalScript != null) {
245                            return globalScript;
246                    }
247    
248                    if (globalFiles.length == 0) {
249                            globalScript = StringPool.BLANK;
250    
251                            return globalScript;
252                    }
253    
254                    StringBundler sb = new StringBundler();
255    
256                    for (String globalFile : globalFiles) {
257                            PortletContext portletContext = getPortletContext();
258    
259                            InputStream inputStream = portletContext.getResourceAsStream(
260                                    globalFile);
261    
262                            if (inputStream == null) {
263                                    if (_log.isWarnEnabled()) {
264                                            _log.warn("Global file " + globalFile + " does not exist");
265                                    }
266                            }
267    
268                            if (inputStream != null) {
269                                    String script = new String(FileUtil.getBytes(inputStream));
270    
271                                    sb.append(script);
272                                    sb.append(StringPool.NEW_LINE);
273                            }
274                    }
275    
276                    globalScript = sb.toString();
277    
278                    return globalScript;
279            }
280    
281            protected void include(
282                            String path, PortletRequest portletRequest,
283                            PortletResponse portletResponse)
284                    throws IOException, PortletException {
285    
286                    checkPath(path);
287    
288                    PortletContext portletContext = getPortletContext();
289    
290                    InputStream inputStream = portletContext.getResourceAsStream(path);
291    
292                    if (inputStream == null) {
293                            _log.error(path + " is not a valid " + language + " file");
294    
295                            return;
296                    }
297    
298                    try {
299                            declareBeans(inputStream, portletRequest, portletResponse);
300                    }
301                    catch (ScriptingException se) {
302                            SessionErrors.add(portletRequest, _ERROR, se);
303                    }
304                    finally {
305                            inputStream.close();
306                    }
307            }
308    
309            protected void writeErrorMessage(
310                            RenderRequest renderRequest, RenderResponse renderResponse,
311                            String errorMessage)
312                    throws Exception {
313    
314                    ThemeDisplay themeDisplay = (ThemeDisplay)renderRequest.getAttribute(
315                            WebKeys.THEME_DISPLAY);
316    
317                    PermissionChecker permissionChecker =
318                            themeDisplay.getPermissionChecker();
319    
320                    StringBundler sb = new StringBundler(6);
321    
322                    sb.append("<div class=\"alert alert-error\">");
323                    sb.append(themeDisplay.translate("an-unexpected-error-occurred"));
324                    sb.append("</div>");
325    
326                    if (PortalPermissionUtil.contains(
327                                    permissionChecker, ActionKeys.CONFIGURATION)) {
328    
329                            sb.append("<pre>");
330                            sb.append(HtmlUtil.escape(errorMessage));
331                            sb.append("</pre>");
332                    }
333    
334                    renderResponse.setContentType(ContentTypes.TEXT_HTML);
335    
336                    PortletResponseUtil.write(renderResponse, sb.toString());
337            }
338    
339            protected String actionFile;
340            protected String editFile;
341            protected String filePath;
342            protected String[] globalFiles;
343            protected String globalScript;
344            protected String helpFile;
345            protected String language;
346            protected String resourceFile;
347            protected String viewFile;
348    
349            private static final String _ERROR = ScriptingPortlet.class + ".ERROR";
350    
351            private static Log _log = LogFactoryUtil.getLog(ScriptingPortlet.class);
352    
353    }