From 0d67f51ed9b47a354171708bd57c902a48ec7d69 Mon Sep 17 00:00:00 2001 From: Kevin Roast Date: Wed, 3 May 2006 12:01:21 +0000 Subject: [PATCH] . Rhino JavaScript integration checkpoint: - Added a new action to the repository for executing JavaScript files - Added script action UI to rule framework, means we can execute a JavaScript file as part of a rule - Lucene search and Saved Search functionality added to default data-model for scripts - Added Scripts folder to Data Dictionary (created during bootstrap) - Created patch to add the Scripts folder to existing schemas - Added ScriptService to ServiceRegistry bean git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2740 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/messages/webclient.properties | 4 + config/alfresco/web-client-config-actions.xml | 11 ++ .../org/alfresco/web/app/Application.java | 36 ++++ .../web/bean/CheckinCheckoutBean.java | 3 +- .../web/bean/DocumentDetailsBean.java | 5 +- .../web/bean/TemplateSupportBean.java | 64 +++++-- .../web/bean/actions/BaseActionWizard.java | 57 +++++-- .../web/bean/rules/CreateRuleWizard.java | 5 + .../web/bean/rules/EditRuleWizard.java | 6 + source/web/jsp/actions/script.jsp | 157 ++++++++++++++++++ 10 files changed, 309 insertions(+), 39 deletions(-) create mode 100644 source/web/jsp/actions/script.jsp diff --git a/config/alfresco/messages/webclient.properties b/config/alfresco/messages/webclient.properties index 3200138f61..3d389c0c9f 100644 --- a/config/alfresco/messages/webclient.properties +++ b/config/alfresco/messages/webclient.properties @@ -689,6 +689,8 @@ action_check_in=Check in content as ''{0}'' with comment ''{1}'' action_check_out=Check out content to action_set_property_value=Sets property action_import=Imports to +action_script=Executes script +action_script_select=Select a script to execute not_condition_result=Check the item does not match the criteria above space=Space import_to=Import To @@ -720,6 +722,7 @@ creating_from=Creating From save_as_template=Save As Template template_name=Template Name select_a_template=Select a template... +select_a_script=Select a script... starting_space=Starting Space space_options=Space Options space_details=Space Details @@ -857,6 +860,7 @@ title_action_checkin=Check In Action title_action_checkout=Check Out Action title_action_copy=Copy Action title_action_move=Move Action +title_action_script=Script Action title_action_import=Import Action title_action_email=Email Action title_action_link_category=Link Category Action diff --git a/config/alfresco/web-client-config-actions.xml b/config/alfresco/web-client-config-actions.xml index 25390e3ae1..0b54950bec 100644 --- a/config/alfresco/web-client-config-actions.xml +++ b/config/alfresco/web-client-config-actions.xml @@ -372,6 +372,16 @@ + + + + Write + + manage_deleted_items + /images/icons/trashcan.gif + dialog:manageDeletedItems + + @@ -525,6 +535,7 @@ + diff --git a/source/java/org/alfresco/web/app/Application.java b/source/java/org/alfresco/web/app/Application.java index d6661fae5c..2812dec7bd 100644 --- a/source/java/org/alfresco/web/app/Application.java +++ b/source/java/org/alfresco/web/app/Application.java @@ -71,6 +71,7 @@ public class Application private static String contentTemplatesFolderName; private static String emailTemplatesFolderName; private static String savedSearchesFolderName; + private static String scriptsFolderName; /** * Private constructor to prevent instantiation of this class @@ -379,6 +380,23 @@ public class Application return getSavedSearchesFolderName(FacesContextUtils.getRequiredWebApplicationContext(context)); } + + /** + * @return Return the JavaScript scripts folder name + */ + public static String getScriptsFolderName(ServletContext context) + { + return getScriptsFolderName(WebApplicationContextUtils.getRequiredWebApplicationContext(context)); + } + + /** + * @return Return the JavaScript scripts folder name + */ + public static String getScriptsFolderName(FacesContext context) + { + return getScriptsFolderName(FacesContextUtils.getRequiredWebApplicationContext(context)); + } + /** * Set the language locale for the current user context * @@ -728,6 +746,24 @@ public class Application return savedSearchesFolderName; } + /** + * Returns the JavaScript scripts folder name + * + * @param context The spring context + * @return The scripts folder name + */ + private static String getScriptsFolderName(WebApplicationContext context) + { + if (scriptsFolderName == null) + { + ImporterBootstrap bootstrap = (ImporterBootstrap)context.getBean(BEAN_IMPORTER_BOOTSTRAP); + Properties configuration = bootstrap.getConfiguration(); + scriptsFolderName = configuration.getProperty("spaces.scripts.childname"); + } + + return scriptsFolderName; + } + /** * Retrieves the configured error page for the application * diff --git a/source/java/org/alfresco/web/bean/CheckinCheckoutBean.java b/source/java/org/alfresco/web/bean/CheckinCheckoutBean.java index 9370d8c499..c40d85ac97 100644 --- a/source/java/org/alfresco/web/bean/CheckinCheckoutBean.java +++ b/source/java/org/alfresco/web/bean/CheckinCheckoutBean.java @@ -549,7 +549,8 @@ public class CheckinCheckoutBean // calculate which editor screen to display if (MimetypeMap.MIMETYPE_TEXT_PLAIN.equals(mimetype) || MimetypeMap.MIMETYPE_XML.equals(mimetype) || - MimetypeMap.MIMETYPE_TEXT_CSS.equals(mimetype)) + MimetypeMap.MIMETYPE_TEXT_CSS.equals(mimetype) || + MimetypeMap.MIMETYPE_JAVASCRIPT.equals(mimetype)) { // make content available to the editing screen if (reader != null) diff --git a/source/java/org/alfresco/web/bean/DocumentDetailsBean.java b/source/java/org/alfresco/web/bean/DocumentDetailsBean.java index c168596c07..09f9e7f039 100644 --- a/source/java/org/alfresco/web/bean/DocumentDetailsBean.java +++ b/source/java/org/alfresco/web/bean/DocumentDetailsBean.java @@ -839,11 +839,12 @@ public class DocumentDetailsBean extends BaseDetailsBean } if (contentType != null) { - // set the property to true by default if the filetype is text/HTML content + // set the property to true by default if the filetype is a known content type if (MimetypeMap.MIMETYPE_HTML.equals(contentType) || MimetypeMap.MIMETYPE_TEXT_PLAIN.equals(contentType) || MimetypeMap.MIMETYPE_XML.equals(contentType) || - MimetypeMap.MIMETYPE_TEXT_CSS.equals(contentType)) + MimetypeMap.MIMETYPE_TEXT_CSS.equals(contentType) || + MimetypeMap.MIMETYPE_JAVASCRIPT.equals(contentType)) { props.put(ContentModel.PROP_EDITINLINE, true); } diff --git a/source/java/org/alfresco/web/bean/TemplateSupportBean.java b/source/java/org/alfresco/web/bean/TemplateSupportBean.java index 24a14ad454..01f5e59519 100644 --- a/source/java/org/alfresco/web/bean/TemplateSupportBean.java +++ b/source/java/org/alfresco/web/bean/TemplateSupportBean.java @@ -37,7 +37,7 @@ import org.alfresco.web.data.IDataContainer; import org.alfresco.web.data.QuickSort; /** - * Provide access to commonly used lists of templates. + * Provide access to commonly used lists of templates and script files. *

* The lists are cached for a small period of time to help performance in the client, * as generally the contents of the template folders are not changed frequently. @@ -46,6 +46,9 @@ import org.alfresco.web.data.QuickSort; */ public class TemplateSupportBean { + private static final String MSG_SELECT_TEMPLATE = "select_a_template"; + private static final String MSG_SELECT_SCRIPT = "select_a_script"; + /** "no selection" marker for SelectItem lists */ public static final String NO_SELECTION = "none"; @@ -55,12 +58,15 @@ public class TemplateSupportBean /** The SearchService instance */ private SearchService searchService; - /** cache of content templates that last 10 seconds - enough for a couple of page refreshes */ - private ExpiringValueCache> contentTemplates = new ExpiringValueCache>(1000*10); + /** cache of content templates that lasts 30 seconds - enough for a couple of page refreshes */ + private ExpiringValueCache> contentTemplates = new ExpiringValueCache>(1000*30); - /** cache of email templates that last 30 seconds - enough for a few page refreshes */ + /** cache of email templates that lasts 30 seconds - enough for a few page refreshes */ private ExpiringValueCache> emailTemplates = new ExpiringValueCache>(1000*30); + /** cache of JavaScript files that lasts 30 seconds - enough for a few page refreshes */ + private ExpiringValueCache> scriptFiles = new ExpiringValueCache>(1000*30); + /** * @param nodeService The NodeService to set. @@ -92,7 +98,7 @@ public class TemplateSupportBean Application.getGlossaryFolderName(fc) + "/" + Application.getContentTemplatesFolderName(fc) + "//*"; - templates = selectTemplateNodes(fc, xpath); + templates = selectDictionaryNodes(fc, xpath, MSG_SELECT_TEMPLATE); contentTemplates.put(templates); } @@ -114,23 +120,45 @@ public class TemplateSupportBean Application.getGlossaryFolderName(fc) + "/" + Application.getEmailTemplatesFolderName(fc) + "//*"; - templates = selectTemplateNodes(fc, xpath); + templates = selectDictionaryNodes(fc, xpath, MSG_SELECT_TEMPLATE); emailTemplates.put(templates); } return templates; } + + /** + * @return the list of available JavaScript files that can be applied to the current document. + */ + public List getScriptFiles() + { + List scripts = this.scriptFiles.get(); + if (scripts == null) + { + // get the scripts from the special Scripts folder + FacesContext fc = FacesContext.getCurrentInstance(); + String xpath = Application.getRootPath(fc) + "/" + + Application.getGlossaryFolderName(fc) + "/" + + Application.getScriptsFolderName(fc) + "//*"; + + scripts = selectDictionaryNodes(fc, xpath, MSG_SELECT_SCRIPT); + + scriptFiles.put(scripts); + } + + return scripts; + } /** * @param context FacesContext - * @param xpath XPath to the template nodes to select + * @param xpath XPath to the nodes to select * - * @return List of SelectItem object from the template nodes found at the XPath + * @return List of SelectItem wrapper objects for the nodes found at the XPath */ - private List selectTemplateNodes(FacesContext fc, String xpath) + private List selectDictionaryNodes(FacesContext fc, String xpath, String noSelectionLabel) { - List templates = null; + List wrappers = null; try { @@ -138,7 +166,7 @@ public class TemplateSupportBean NamespaceService resolver = Repository.getServiceRegistry(fc).getNamespaceService(); List results = this.searchService.selectNodes(rootNodeRef, xpath, null, resolver, false); - templates = new ArrayList(results.size() + 1); + wrappers = new ArrayList(results.size() + 1); if (results.size() != 0) { DictionaryService dd = Repository.getServiceRegistry(fc).getDictionaryService(); @@ -149,13 +177,13 @@ public class TemplateSupportBean Node childNode = new Node(ref); if (dd.isSubClass(childNode.getType(), ContentModel.TYPE_CONTENT)) { - templates.add(new SelectItem(childNode.getId(), childNode.getName())); + wrappers.add(new SelectItem(childNode.getId(), childNode.getName())); } } } // make sure the list is sorted by the label - QuickSort sorter = new QuickSort(templates, "label", true, IDataContainer.SORT_CASEINSENSITIVE); + QuickSort sorter = new QuickSort(wrappers, "label", true, IDataContainer.SORT_CASEINSENSITIVE); sorter.sort(); } } @@ -164,13 +192,13 @@ public class TemplateSupportBean // ignore the result if we cannot access the root } - // add an entry (at the start) to instruct the user to select a template - if (templates == null) + // add an entry (at the start) to instruct the user to select an item + if (wrappers == null) { - templates = new ArrayList(1); + wrappers = new ArrayList(1); } - templates.add(0, new SelectItem(NO_SELECTION, Application.getMessage(FacesContext.getCurrentInstance(), "select_a_template"))); + wrappers.add(0, new SelectItem(NO_SELECTION, Application.getMessage(FacesContext.getCurrentInstance(), noSelectionLabel))); - return templates; + return wrappers; } } diff --git a/source/java/org/alfresco/web/bean/actions/BaseActionWizard.java b/source/java/org/alfresco/web/bean/actions/BaseActionWizard.java index dd72d62abb..cca1ed275f 100644 --- a/source/java/org/alfresco/web/bean/actions/BaseActionWizard.java +++ b/source/java/org/alfresco/web/bean/actions/BaseActionWizard.java @@ -29,6 +29,7 @@ import org.alfresco.repo.action.executer.LinkCategoryActionExecuter; import org.alfresco.repo.action.executer.MailActionExecuter; import org.alfresco.repo.action.executer.MoveActionExecuter; import org.alfresco.repo.action.executer.RemoveFeaturesActionExecuter; +import org.alfresco.repo.action.executer.ScriptActionExecutor; import org.alfresco.repo.action.executer.SimpleWorkflowActionExecuter; import org.alfresco.repo.action.executer.SpecialiseTypeActionExecuter; import org.alfresco.repo.action.executer.TransformActionExecuter; @@ -94,6 +95,7 @@ public abstract class BaseActionWizard extends BaseWizardBean public static final String PROP_MIMETYPE = "mimetype"; public static final String PROP_MODEL_ASPECT = "modelaspect"; public static final String PROP_TYPE_OR_ASPECT = "typeoraspect"; + public static final String PROP_SCRIPT = "script"; protected ActionService actionService; protected DictionaryService dictionaryService; @@ -154,6 +156,7 @@ public abstract class BaseActionWizard extends BaseWizardBean this.currentActionProperties.put(PROP_CHECKIN_MINOR, new Boolean(true)); } + // ------------------------------------------------------------------------------ // Bean Getters and Setters @@ -520,6 +523,7 @@ public abstract class BaseActionWizard extends BaseWizardBean return this.imageTransformers; } + // ------------------------------------------------------------------------------ // Action event handlers @@ -750,6 +754,7 @@ public abstract class BaseActionWizard extends BaseWizardBean usingTemplate = null; } + // ------------------------------------------------------------------------------ // Service Injection @@ -799,6 +804,7 @@ public abstract class BaseActionWizard extends BaseWizardBean this.authorityService = authorityService; } + // ------------------------------------------------------------------------------ // Helper methods @@ -1045,11 +1051,18 @@ public abstract class BaseActionWizard extends BaseWizardBean NodeRef destNodeRef = (NodeRef)this.currentActionProperties.get(PROP_DESTINATION); actionParams.put(ImporterActionExecuter.PARAM_DESTINATION_FOLDER, destNodeRef); } - else if (this.action.equals(SpecialiseTypeActionExecuter.NAME) == true) + else if (this.action.equals(SpecialiseTypeActionExecuter.NAME)) { - // add the specialisation type - String objectType = (String)this.currentActionProperties.get(PROP_OBJECT_TYPE); - actionParams.put(SpecialiseTypeActionExecuter.PARAM_TYPE_NAME, QName.createQName(objectType)); + // add the specialisation type + String objectType = (String)this.currentActionProperties.get(PROP_OBJECT_TYPE); + actionParams.put(SpecialiseTypeActionExecuter.PARAM_TYPE_NAME, QName.createQName(objectType)); + } + else if (this.action.equals(ScriptActionExecutor.NAME)) + { + // add the selected script noderef to the action properties + String id = (String)this.currentActionProperties.get(PROP_SCRIPT); + NodeRef scriptRef = new NodeRef(Repository.getStoreRef(), id); + actionParams.put(ScriptActionExecutor.PARAM_SCRIPTREF, scriptRef); } return actionParams; @@ -1221,22 +1234,29 @@ public abstract class BaseActionWizard extends BaseWizardBean String spaceName = Repository.getNameForNode(this.nodeService, space); summary.append("'").append(spaceName).append("'"); } - else if (SpecialiseTypeActionExecuter.NAME.equals(actionName) == true) + else if (SpecialiseTypeActionExecuter.NAME.equals(actionName)) { - String label = null; - String objectType = (String)this.currentActionProperties.get(PROP_OBJECT_TYPE); - for (SelectItem item : getObjectTypes()) - { - if (item.getValue().equals(objectType) == true) - { - label = item.getLabel(); - break; - } - } - - summary.append("'").append(label).append("'"); + String label = null; + String objectType = (String)this.currentActionProperties.get(PROP_OBJECT_TYPE); + for (SelectItem item : getObjectTypes()) + { + if (item.getValue().equals(objectType) == true) + { + label = item.getLabel(); + break; + } + } + + summary.append("'").append(label).append("'"); } - + else if (ScriptActionExecutor.NAME.equals(actionName)) + { + String id = (String)this.currentActionProperties.get(PROP_SCRIPT); + NodeRef scriptRef = new NodeRef(Repository.getStoreRef(), id); + String scriptName = Repository.getNameForNode(this.nodeService, scriptRef); + summary.append("'").append(scriptName).append("'"); + } + summaryResult = summary.toString(); } @@ -1270,6 +1290,7 @@ public abstract class BaseActionWizard extends BaseWizardBean return ACTION_PAGES_LOCATION + actionId + ".jsp"; } + // ------------------------------------------------------------------------------ // Inner classes diff --git a/source/java/org/alfresco/web/bean/rules/CreateRuleWizard.java b/source/java/org/alfresco/web/bean/rules/CreateRuleWizard.java index 39b55761d2..ddf0e64876 100644 --- a/source/java/org/alfresco/web/bean/rules/CreateRuleWizard.java +++ b/source/java/org/alfresco/web/bean/rules/CreateRuleWizard.java @@ -76,6 +76,7 @@ public class CreateRuleWizard extends BaseActionWizard private static final Log logger = LogFactory.getLog(CreateRuleWizard.class); + // ------------------------------------------------------------------------------ // Wizard implementation @@ -260,6 +261,7 @@ public class CreateRuleWizard extends BaseActionWizard return outcome; } + // ------------------------------------------------------------------------------ // Bean Getters and Setters @@ -515,6 +517,7 @@ public class CreateRuleWizard extends BaseActionWizard this.condition = condition; } + // ------------------------------------------------------------------------------ // Action event handlers @@ -646,6 +649,7 @@ public class CreateRuleWizard extends BaseActionWizard goToPage(FacesContext.getCurrentInstance(), this.returnViewId); } + // ------------------------------------------------------------------------------ // Service Injection @@ -667,6 +671,7 @@ public class CreateRuleWizard extends BaseActionWizard this.rulesBean = rulesBean; } + // ------------------------------------------------------------------------------ // Helper methods diff --git a/source/java/org/alfresco/web/bean/rules/EditRuleWizard.java b/source/java/org/alfresco/web/bean/rules/EditRuleWizard.java index 4a2e598f41..8e63de05c1 100644 --- a/source/java/org/alfresco/web/bean/rules/EditRuleWizard.java +++ b/source/java/org/alfresco/web/bean/rules/EditRuleWizard.java @@ -23,6 +23,7 @@ import org.alfresco.repo.action.executer.LinkCategoryActionExecuter; import org.alfresco.repo.action.executer.MailActionExecuter; import org.alfresco.repo.action.executer.MoveActionExecuter; import org.alfresco.repo.action.executer.RemoveFeaturesActionExecuter; +import org.alfresco.repo.action.executer.ScriptActionExecutor; import org.alfresco.repo.action.executer.SimpleWorkflowActionExecuter; import org.alfresco.repo.action.executer.SpecialiseTypeActionExecuter; import org.alfresco.repo.action.executer.TransformActionExecuter; @@ -306,5 +307,10 @@ public class EditRuleWizard extends CreateRuleWizard QName specialiseType = (QName)actionProps.get(SpecialiseTypeActionExecuter.PARAM_TYPE_NAME); this.currentActionProperties.put(PROP_OBJECT_TYPE, specialiseType.toString()); } + else if (this.action.equals(ScriptActionExecutor.NAME)) + { + NodeRef scriptRef = (NodeRef)actionProps.get(ScriptActionExecutor.PARAM_SCRIPTREF); + this.currentActionProperties.put(PROP_SCRIPT, scriptRef.getId()); + } } } diff --git a/source/web/jsp/actions/script.jsp b/source/web/jsp/actions/script.jsp new file mode 100644 index 0000000000..1ec2183223 --- /dev/null +++ b/source/web/jsp/actions/script.jsp @@ -0,0 +1,157 @@ +<%-- + Copyright (C) 2005 Alfresco, Inc. + + Licensed under the Mozilla Public License version 1.1 + with a permitted attribution clause. You may obtain a + copy of the License at + + http://www.alfresco.org/legal/license.txt + + 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. +--%> +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + +<%@ page buffer="32kb" contentType="text/html;charset=UTF-8" %> +<%@ page isELIgnored="false" %> +<%@ page import="org.alfresco.web.ui.common.PanelGenerator" %> + + + + + + <%-- load a bundle of properties with I18N strings --%> + + + + + <%-- Main outer table --%> + + + <%-- Title bar --%> + + + + + <%-- Main area --%> + + <%-- Shelf --%> + + + <%-- Work Area --%> + + +
+ <%@ include file="../parts/titlebar.jsp" %> +
+ <%@ include file="../parts/shelf.jsp" %> + + + <%-- Breadcrumb --%> + <%@ include file="../parts/breadcrumb.jsp" %> + + <%-- Status and Actions --%> + + + + + + + <%-- separator row with gradient shadow --%> + + + + + + + <%-- Details --%> + + + + + + + <%-- separator row with bottom panel graphics --%> + + + + + + +
+ + <%-- Status and Actions inner contents table --%> + <%-- Generally this consists of an icon, textual summary and actions for the current object --%> + + + + + +
+ +
+
+
+ +
+ + + + + + +
+ + + + <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "white", "white"); %> + + + + + + + + + + +
: + <%-- Scripts drop-down selector --%> + + + +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "white"); %> +
+ <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "blue", "#D3E6FE"); %> + + + + + + + +
+ +
+ +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "blue"); %> +
+
+
+ +
+ +
+ +
\ No newline at end of file