| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| /* $Id$ */ |
| |
| cocoon.load("resource://org/apache/cocoon/forms/flow/javascript/Form.js"); |
| importPackage(Packages.org.apache.lenya.cms.linking) |
| |
| //placeholders for custom flow code: |
| var customLoopFlow = undefined; |
| var customSubmitFlow = undefined; |
| |
| /** |
| * Get usecase. |
| * |
| * @param usecaseName, a string |
| * @return a new org.apache.lenya.cms.usecase.Usecase Avalon component |
| */ |
| function getUsecase(usecaseName) { |
| var flowHelper; |
| var request; |
| var sourceUrl; |
| var usecaseResolver; |
| var usecase; |
| try { |
| flowHelper = cocoon.getComponent("org.apache.lenya.cms.cocoon.flow.FlowHelper"); |
| request = flowHelper.getRequest(cocoon); |
| sourceUrl = Packages.org.apache.lenya.util.ServletHelper.getWebappURI(request); |
| usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver"); |
| usecase = usecaseResolver.resolve(sourceUrl, usecaseName); |
| usecase.setSourceURL(sourceUrl); |
| usecase.setName(usecaseName); |
| } catch (exception) { |
| log("error", "Error in getUsecase(): " + exception); |
| log("debug", "usecaseName = " + usecaseName); |
| log("debug", "flowHelper = " + flowHelper); |
| log("debug", "request = " + request); |
| log("debug", "sourceUrl = " + sourceUrl); |
| log("debug", "usecaseResolver = " + usecaseResolver); |
| log("debug", "usecase = " + usecase); |
| throw exception; |
| } finally { |
| cocoon.releaseComponent(flowHelper); |
| cocoon.releaseComponent(usecaseResolver); |
| } |
| return usecase; |
| } |
| |
| /** |
| * Release usecase. Since usecases are Avalon Components, they must |
| * be released before a continuation is created. |
| * |
| * @param usecase, a org.apache.lenya.cms.usecase.Usecase Avalon component |
| */ |
| function releaseUsecase(usecase) { |
| var usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver"); |
| try { |
| usecaseResolver.release(usecase); |
| } finally { |
| cocoon.releaseComponent(usecaseResolver); |
| } |
| } |
| |
| /** |
| * Pass all parameters from the current request to a usecase |
| * (except lenya.usecase, lenya.continuation and submit). |
| * |
| * @param usecase, a org.apache.lenya.cms.usecase.Usecase Avalon component |
| */ |
| function passRequestParameters(usecase) { |
| var flowHelper = cocoon.getComponent("org.apache.lenya.cms.cocoon.flow.FlowHelper"); |
| var names = cocoon.request.getParameterNames(); |
| while (names.hasMoreElements()) { |
| var name = names.nextElement(); |
| // some parameters are handled elsewhere: |
| if (!name.equals("lenya.usecase") |
| && !name.equals("lenya.continuation") |
| && !name.equals("submit")) { |
| // pass the rest on: |
| var value = flowHelper.getRequest(cocoon).get(name); |
| var string = new Packages.java.lang.String(); |
| var vector = new Packages.java.util.Vector(); |
| if (string.getClass().isInstance(value) || vector.getClass().isInstance(value)) { |
| // use getParameters() to avoid character encoding problems |
| var values = flowHelper.getRequest(cocoon).getParameterValues(name); |
| if (values.length < 2) { |
| usecase.setParameter(name, values[0]); |
| } else { |
| usecase.setParameter(name, values); |
| } |
| } else if (value == null) { |
| value = cocoon.request.getParameter(name); |
| usecase.setParameter(name, value); |
| } else { |
| usecase.setPart(name, value); |
| } |
| } |
| } |
| cocoon.releaseComponent(flowHelper); |
| } |
| |
| /** |
| * Load the custom flow functions as provided in the view |
| * configuration, if any. |
| * |
| * @param view, a org.apache.lenya.cms.usecase.UsecaseView object |
| */ |
| function loadCustomFlow(view) { |
| customLoopFlow = undefined; |
| customSubmitFlow = undefined; |
| |
| var flowUri; |
| if (view != null) { |
| flowUri = view.getCustomFlow(); |
| if (flowUri != null && flowUri != "") { // for some reason, flowUri is not correctly cast into a Boolean, so "if (flowUri)" does not work |
| log("debug", "customFlow uri: [" + flowUri + "]"); |
| cocoon.load(flowUri); |
| } |
| } else { |
| log("debug", "Usecase does not define a view."); |
| } |
| } |
| |
| |
| /** |
| * Log messages via cocoon.log. |
| * |
| * @param level, one of ("debug"|"info"|"warn"|"error") |
| * @param message, a string |
| * @param usecaseName, a string. |
| */ |
| function log(level, message, usecaseName) { |
| var msg = "usecases.js::executeUsecase() " |
| + (usecaseName ? "with lenya.usecase=[" + usecaseName + "]" : "") |
| + ": " |
| + message; |
| switch (level) { |
| case "debug": |
| if (cocoon.log.isDebugEnabled()) |
| cocoon.log.debug(msg); |
| break; |
| case "info": |
| cocoon.log.info(msg); |
| break; |
| case "warn": |
| cocoon.log.warn(msg); |
| break; |
| case "error": |
| cocoon.log.error(msg); |
| break; |
| default: |
| cocoon.log.error(msg + "[Unknown log level " + level + "]"); |
| break; |
| } |
| } |
| |
| /** |
| * The Loop stage of the flow, in which a view is displayed. |
| * <em>Note:</em> All Avalon components should be released before calling |
| * this function! This means that you cannot hold a usecase object, |
| * hence the proxy. |
| * |
| * @param view, a org.apache.lenya.cms.usecase.UsecaseView object |
| * @param proxy, a org.apache.lenya.cms.usecase.UsecaseProxy object |
| * @param generic, a generic Javascript object for custom flow code to preserve state information (currently not used by the default code) |
| * |
| * This function invokes customLoopFlow if it exists. |
| * Otherwise it falls back to defaultLoopFlow. |
| */ |
| function loopFlow(view, proxy, generic) { |
| if (customLoopFlow != undefined) { |
| log("info", "Using customLoopFlow function", proxy.getName()); |
| return customLoopFlow(view, proxy, generic); |
| } else{ |
| return defaultLoopFlow(view, proxy); |
| } |
| } |
| |
| |
| /** |
| * The Submit stage of the flow, in which a user interaction is processed. |
| * and the usecase is advanced. If the user has submitted, the usecase is executed. |
| * |
| * @param usecase, a org.apache.lenya.cms.usecase.Usecase Avalon component |
| * @param generic, a generic Javascript object for custom flow code to preserve state information (currently not used by the default code) |
| * @return a string with the return state ("success"|"cancel"|"continue"). |
| * |
| * This function invokes customSubmitFlow if it exists. |
| * Otherwise it falls back to defaultSubmitFlow. |
| */ |
| function submitFlow(usecase, generic) { |
| if (customSubmitFlow != null) { |
| log("info", "Using customSubmitFlow function", usecase.getName()); |
| return customSubmitFlow(usecase, generic); |
| } else{ |
| |
| return defaultSubmitFlow(usecase); |
| } |
| } |
| |
| /** |
| * @see loopFlow. |
| */ |
| function defaultLoopFlow(view, proxy) { |
| var viewUri = view.getViewURI(); |
| // we used to allow a cocoon:/ and cocoon:// prefix (which sendPageXXX does not handle), |
| // but it is now deprecated! |
| if (viewUri.startsWith("cocoon:/")) { |
| // leave leading / in case of cocoon:// for root sitemap |
| viewUri = viewUri.substring(new Packages.java.lang.String("cocoon:/").length()); |
| log("warn", "The use of the cocoon:/ protocol prefix in the <view uri=\"...\"> attribute is deprecated!"); |
| } |
| if (! viewUri.startsWith("/")) { |
| // a local URI must be handled by usecase.xmap, which assumes a prefix "usecases-view/[menu|nomenu]/ |
| // that determines whether the menu is to be displayed. this mechanism is used by most lenya core usecases. |
| viewUri = "usecases-view/" |
| + (view.showMenu() ? "menu" : "nomenu") |
| + "/" + viewUri; |
| } |
| if (view.createContinuation()) { |
| log("debug", "Creating view and continuation, calling Cocoon with viewUri = [" + viewUri + "]"); |
| cocoon.sendPageAndWait(viewUri, { "usecase" : proxy }); |
| } else { |
| log("debug", "Creating view without continuation (!), calling Cocoon with viewUri = [" + viewUri + "]"); |
| cocoon.sendPage(viewUri, { "usecase" : proxy}); |
| cocoon.exit(); // we're done. |
| } |
| } |
| |
| /** |
| * @see submitFlow |
| */ |
| function defaultSubmitFlow(usecase) { |
| var preconditionsOk = true; |
| if (cocoon.request.getParameter("submit")||cocoon.request.getParameter("lenya.submit")=="ok") { |
| if (usecase.isOptimistic()) { |
| usecase.checkPreconditions(); |
| preconditionsOk = !usecase.hasErrors(); |
| } |
| if (preconditionsOk) { |
| if (usecase.isOptimistic()) { |
| usecase.lockInvolvedObjects(); |
| } |
| usecase.checkExecutionConditions(); |
| if (! usecase.hasErrors()) { |
| return executeFlow(usecase); |
| } |
| } |
| } else if (cocoon.request.getParameter("cancel")) { |
| usecase.cancel(); |
| return "cancel"; |
| } |
| else { |
| usecase.advance(); |
| } |
| return "continue" |
| } |
| |
| /** |
| * The Execute stage of the flow, in which the usecase is finally executed. |
| * |
| * @param a org.apache.lenya.cms.usecase.Usecase object |
| * @return a string with the return state ("success"|"continue"). |
| */ |
| function executeFlow(usecase) { |
| usecase.execute(); |
| if (! usecase.hasErrors()) { |
| usecase.checkPostconditions(); |
| if (! usecase.hasErrors()) { |
| return "success"; |
| } |
| } |
| return "continue"; |
| } |
| |
| /** |
| * Redirect to target URL after finishing the usecase, |
| * taking proxy settings into account. |
| * |
| * @param the webapp-internal target URL |
| */ |
| function redirect(targetUrl) { |
| var flowHelper; // needed to obtain the current objectModel |
| var objectModel; // needed to provide context to the proxyModule |
| var inputModuleSelector; // needed to obtain a proxyModule |
| var proxyModule; // used to rewrite the targetUrl according to the publication's proxy settings |
| var proxyUrl; // the rewritten target Url |
| |
| flowHelper = cocoon.getComponent("org.apache.lenya.cms.cocoon.flow.FlowHelper"); |
| try { |
| objectModel = flowHelper.getObjectModel(cocoon); |
| } finally { |
| cocoon.releaseComponent(flowHelper); |
| } |
| |
| inputModuleSelector = cocoon.getComponent(org.apache.cocoon.components.modules.input.InputModule.ROLE + "Selector"); |
| try { |
| proxyModule = inputModuleSelector.select("proxy"); |
| try { |
| proxyUrl = proxyModule.getAttribute(targetUrl, null, objectModel); |
| } finally { |
| cocoon.releaseComponent(proxyModule); |
| } |
| } finally { |
| cocoon.releaseComponent(inputModuleSelector); |
| } |
| |
| log("debug", "Redirecting to {proxy:" + targetUrl + "} = " + proxyUrl + "."); |
| |
| cocoon.redirectTo(proxyUrl, true); |
| } |
| |
| |
| |
| /** |
| * Main function to execute a usecase. This is called from <map:flow/>. |
| * |
| * Uses request parameter "lenya.usecase" to determine what |
| * usecase to execute. |
| * |
| * Since "usecase" and "flowHelper" are avalon components, |
| * they must be released before a continuation is created. |
| * In order to preserve state information, a "proxy" object |
| * is used. |
| */ |
| function executeUsecase() { |
| |
| var usecaseName; |
| var usecase; // the Usecase object |
| var proxy; // a UsecaseProxy to make the usecase state persistent across continuations |
| var view; // the UsecaseView object that belongs to our usecase. |
| var state; // the state of the usecase ("continue"|"success"|"cancel"); |
| var targetUrl; // URL to redirect to after completion. |
| var generic = new Object; // a generic helper object for custom flow code to preserve state information. |
| |
| var preconditionsOK; |
| |
| try { |
| usecaseName = cocoon.parameters["usecaseName"]; |
| usecase = getUsecase(usecaseName); |
| passRequestParameters(usecase); |
| usecase.checkPreconditions(); |
| preconditionsOK = !usecase.hasErrors(); |
| if (preconditionsOK && !usecase.isOptimistic()) { |
| usecase.lockInvolvedObjects(); |
| } |
| // create proxy object to save usecase state |
| view = usecase.getView(); |
| proxy = new Packages.org.apache.lenya.cms.usecase.impl.UsecaseProxy(usecase); |
| log("debug", "Successfully prepared usecase.", usecaseName); |
| } catch (exception) { |
| log("error", "Could not prepare usecase: " + exception, usecaseName); |
| throw exception; |
| } finally { |
| releaseUsecase(usecase); |
| } |
| loadCustomFlow(view); |
| // If the usecase has a view uri, this means we want to display something |
| // to the user before proceeding. This also means the usecase can consist |
| // of several steps; repeated until the user chooses to submit or cancel. |
| if (view != null && view.getViewURI()) { |
| do { |
| // show the view: |
| try { |
| loopFlow(view, proxy, generic); //usecase must be released here! |
| } catch (exception) { |
| // if something went wrong, try and rollback the usecase: |
| log("error", "Exception during loopFlow(): " + exception, usecaseName); |
| try { |
| usecase = getUsecase(usecaseName); |
| proxy.setup(usecase); |
| usecase.cancel(); |
| throw exception; |
| } finally { |
| releaseUsecase(usecase); |
| } |
| } |
| if (preconditionsOK) { |
| log("debug", "Advancing in usecase.", usecaseName); |
| // restore the usecase state and handle the user input: |
| usecase = getUsecase(usecaseName); |
| proxy.setup(usecase); |
| passRequestParameters(usecase); |
| state = submitFlow(usecase, generic); |
| // create a new proxy with the updated usecase state |
| proxy = new Packages.org.apache.lenya.cms.usecase.impl.UsecaseProxy(usecase); |
| releaseUsecase(usecase); |
| } |
| } while (state == "continue"); |
| // If the usecase does not have a view uri, we can directly jump to |
| // executeFlow(). |
| } else { |
| usecase = getUsecase(usecaseName); |
| proxy.setup(usecase); |
| passRequestParameters(usecase); |
| |
| if (usecase.isOptimistic()) { |
| usecase.checkPreconditions(); |
| preconditionsOK = !usecase.hasErrors(); |
| } |
| |
| if (preconditionsOK) { |
| if (usecase.isOptimistic()) { |
| usecase.lockInvolvedObjects(); |
| } |
| usecase.checkExecutionConditions(); |
| var hasErrors = usecase.hasErrors(); |
| if (!hasErrors) { |
| state = executeFlow(usecase); |
| hasErrors = usecase.hasErrors(); |
| } |
| } |
| releaseUsecase(usecase); |
| if (hasErrors) { |
| proxy = new Packages.org.apache.lenya.cms.usecase.impl.UsecaseProxy(usecase); |
| cocoon.sendPage("usecases-view/nomenu/modules/usecase/templates/error.jx", { "usecase" : proxy}); |
| return; |
| } |
| } |
| //getTargetURL takes a boolean that is true on success: |
| targetUrl = usecase.getTargetURL(state == "success"); |
| log("debug", "Completed, redirecting to url = [" + targetUrl + "]", usecaseName); |
| // jump to the appropriate URL: |
| redirect(targetUrl); |
| } |