mirror of
https://github.com/apache/struts.git
synced 2026-08-08 08:07:17 +00:00
Compare commits
201 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8274f77bc1 | |||
| 87a18db15f | |||
| e58edfba83 | |||
| 2520513310 | |||
| e4db6b720e | |||
| e1209fde05 | |||
| cf1c674feb | |||
| 992ca3a525 | |||
| e7834d4345 | |||
| bb6c186078 | |||
| 88f04fa337 | |||
| 8a46e9313f | |||
| 4e05233cc2 | |||
| e647e5cbfc | |||
| 8d0382c34e | |||
| 00be9803d5 | |||
| e6b9aaba54 | |||
| c495824e56 | |||
| 5c247b38ed | |||
| a7fdf13446 | |||
| 2825fdb41a | |||
| 5e624faa82 | |||
| 377c0cb20b | |||
| 25da849d0a | |||
| 9f14276c00 | |||
| 45ccf7d83b | |||
| 6ae6283f5e | |||
| 53aeafd7a2 | |||
| 3dd751b730 | |||
| 93303b45cb | |||
| 8ce5811fe7 | |||
| dc349dd356 | |||
| 18ff6c1ef4 | |||
| dc579c0ccc | |||
| cae7a5f25c | |||
| 62d46b62da | |||
| 43358dee85 | |||
| d9ba299412 | |||
| 26e1535b58 | |||
| 8d6e26e0fe | |||
| a96dd31be6 | |||
| fe45511712 | |||
| 90ea4d8aa1 | |||
| d04ed76f1b | |||
| f1824b5393 | |||
| 675deff599 | |||
| 256cd14bf0 | |||
| 620b8c3a0c | |||
| 20e4529e35 | |||
| 8a26c0d753 | |||
| b2580e1d76 | |||
| a65b8d5d51 | |||
| 7e912a2985 | |||
| 1b9563d366 | |||
| ce8b75de58 | |||
| 280f25d2fe | |||
| 69669d6cf7 | |||
| 39d70e3944 | |||
| cb5e55b06c | |||
| ea23ce295a | |||
| 4cf9093cc5 | |||
| 46bae3ce68 | |||
| dfa8fb3e4c | |||
| a2df8f9890 | |||
| 7253edeada | |||
| 2efe3cec7a | |||
| 4506d5716d | |||
| 83aff6e470 | |||
| 3138dc7fbd | |||
| 4566734662 | |||
| d591b7e3b9 | |||
| 9c45d6b025 | |||
| 482af41673 | |||
| 0a75d8e8fa | |||
| 1d18e8f9a8 | |||
| f6365e7363 | |||
| 84aeb77011 | |||
| b3a9d82d58 | |||
| 828d02fcbd | |||
| 88b05cdaed | |||
| e60df05c27 | |||
| 1efcc938cf | |||
| 7b1c3b0fca | |||
| 10a834a54c | |||
| abb1ac3c99 | |||
| dcc59937a8 | |||
| 22c88faf38 | |||
| 1191ca3ed0 | |||
| bcfb769005 | |||
| d61be3e4ce | |||
| c7149ab1bc | |||
| 4504e610f1 | |||
| 9b3a23eb28 | |||
| 3220d832d2 | |||
| bd007953ea | |||
| 1b0c743e85 | |||
| 89e02a4fb4 | |||
| 8cb93ad39a | |||
| e8656d5737 | |||
| 7f10ed505f | |||
| 043814b774 | |||
| 0fabde9f97 | |||
| 4457f03c70 | |||
| 3d3512a399 | |||
| 59a6cbf6ca | |||
| 19802b0614 | |||
| e46e662a7a | |||
| 4786fba0d7 | |||
| e32bd7dba9 | |||
| 6e1d2add07 | |||
| 2eeac367fe | |||
| 30b43044a3 | |||
| 5c82f0246e | |||
| fff35cfd9d | |||
| 1526b36dd6 | |||
| 6d6a422db7 | |||
| 5cf57b9132 | |||
| f4c0135878 | |||
| 77cfae3084 | |||
| ec56290056 | |||
| 47c87bc62c | |||
| ce467b7fa5 | |||
| 73eb6ed189 | |||
| 80a91dc75f | |||
| a6edb0d5df | |||
| 3dfc5a4074 | |||
| fdfeb3233a | |||
| ae3ae2be76 | |||
| 8dface4fcb | |||
| 1348971d40 | |||
| dd6d206d78 | |||
| e2b644a4fb | |||
| 13cfba86f8 | |||
| d84d59f53c | |||
| 3651f55869 | |||
| 7ffa750c68 | |||
| 313876aa3c | |||
| 9d7a9f81db | |||
| fe98223724 | |||
| 1de94b2092 | |||
| 706bb560e4 | |||
| 12d4feaf8f | |||
| d88a8382d4 | |||
| 73809eae54 | |||
| 5b4b554d11 | |||
| fcbd29b7f9 | |||
| 220896a0cf | |||
| 632f19eab3 | |||
| 50a145152d | |||
| f7b191f782 | |||
| 4ae1a6a26d | |||
| 79e598f360 | |||
| 9216e8e22c | |||
| 651eac2c57 | |||
| a50af87644 | |||
| 9e01fbd2dd | |||
| d4dd3386cc | |||
| 0999fba8c4 | |||
| 3ac6835c5c | |||
| b0dd7c1c0d | |||
| a63beef1f8 | |||
| e74e5539dd | |||
| 2226fcc168 | |||
| 865b8a20df | |||
| 3c6956116e | |||
| bb9ce7582b | |||
| 11d373f8b4 | |||
| b23bfd42d2 | |||
| f94b681050 | |||
| fb38a919c9 | |||
| e25c826167 | |||
| 4815744eaf | |||
| f5a7776854 | |||
| f45d2752bb | |||
| 0262574095 | |||
| 30cb56a414 | |||
| 63dc85fd7a | |||
| 5b0ec3400d | |||
| d460d71498 | |||
| 53f64cdecf | |||
| aff4d46835 | |||
| a15c12a051 | |||
| e62155c460 | |||
| f7f4fbd47f | |||
| 2d81439553 | |||
| 7afe35cb96 | |||
| fdd1054070 | |||
| 32e88d1a2e | |||
| 6403de4516 | |||
| 13c517fc3c | |||
| 7242a87165 | |||
| 0323795d67 | |||
| 71267a9b4b | |||
| 5524c579d2 | |||
| 873ca8fa20 | |||
| 8988d57644 | |||
| 2e7cf63964 | |||
| 3eb68be674 | |||
| c9f279ec2e | |||
| 881e1b2580 | |||
| cb0ac7c440 |
+4
-3
@@ -1,3 +1,4 @@
|
||||
dist: trusty
|
||||
language: java
|
||||
sudo: false
|
||||
|
||||
@@ -8,12 +9,12 @@ jdk:
|
||||
- oraclejdk11
|
||||
|
||||
install: true
|
||||
script: mvn test -DskipAssembly
|
||||
script: mvn test -DskipAssembly -B
|
||||
|
||||
after_success:
|
||||
# TODO delete following if statement after fix of https://github.com/cobertura/cobertura/issues/271
|
||||
- if [ "$TRAVIS_JDK_VERSION" == "openjdk8" ] || [ "$TRAVIS_JDK_VERSION" == "oraclejdk8" ]; then
|
||||
mvn cobertura:cobertura org.eluder.coveralls:coveralls-maven-plugin:report com.updateimpact:updateimpact-maven-plugin:submit -Ptravis-coveralls,update-impact -DskipAssembly;
|
||||
mvn cobertura:cobertura org.eluder.coveralls:coveralls-maven-plugin:report com.updateimpact:updateimpact-maven-plugin:submit -Ptravis-coveralls,update-impact -DskipAssembly -B;
|
||||
else
|
||||
echo "Not reporting coverage for $TRAVIS_JDK_VERSION due to incompatibility or to save performance";
|
||||
fi;
|
||||
@@ -25,4 +26,4 @@ env:
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
|
||||
|
||||
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
#!groovy
|
||||
pipeline {
|
||||
agent {
|
||||
label 'ubuntu'
|
||||
}
|
||||
options {
|
||||
buildDiscarder logRotator(daysToKeepStr: '14', numToKeepStr: '10')
|
||||
timeout(80)
|
||||
disableConcurrentBuilds()
|
||||
}
|
||||
tools {
|
||||
jdk 'jdk_1.7_latest'
|
||||
maven 'maven_3_latest'
|
||||
}
|
||||
triggers {
|
||||
pollSCM 'H/15 * * * *'
|
||||
}
|
||||
environment {
|
||||
MAVEN_OPTS = '-Xmx1024m -XX:MaxPermSize=256m'
|
||||
}
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh 'mvn --version'
|
||||
sh 'mvn clean source:jar javadoc:jar install deploy -DskipWiki -Dhttps.protocols=TLSv1,TLSv1.1,TLSv1.2'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.31</version>
|
||||
</parent>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.31</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-rest-showcase</artifactId>
|
||||
<packaging>war</packaging>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.31</version>
|
||||
<name>Struts 2 Rest Showcase Webapp</name>
|
||||
<description>Struts 2 Rest Showcase Example</description>
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.31</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-showcase</artifactId>
|
||||
@@ -167,7 +167,7 @@
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<version>5.1.3.Final</version>
|
||||
<version>5.4.3.Final</version>
|
||||
</dependency>
|
||||
|
||||
<!-- The Servlet API mocks in Spring Framework 4.x only supports Servlet 3.0 and higher.
|
||||
|
||||
+8
@@ -84,4 +84,12 @@ public class FileUploadAction extends ActionSupport {
|
||||
public void setCaption(String caption) {
|
||||
this.caption = caption;
|
||||
}
|
||||
|
||||
public long getUploadSize() {
|
||||
if (upload != null) {
|
||||
return upload.length();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@
|
||||
<validators>
|
||||
<field name="upload">
|
||||
<field-validator type="fieldexpression">
|
||||
<param name="expression"><![CDATA[upload.length() > 0]]></param>
|
||||
<param name="expression"><![CDATA[getUploadSize() > 0]]></param>
|
||||
<message>File cannot be empty</message>
|
||||
</field-validator>
|
||||
</field>
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
</action>
|
||||
|
||||
<action name="doMultipleUploadUsingList" class="org.apache.struts2.showcase.fileupload.MultipleFileUploadUsingListAction" method="upload">
|
||||
<result name="input">/WEB-INF/fileupload/multipleUploadUsingList.jsp</result>
|
||||
<result>/WEB-INF/fileupload/multiple-success.jsp</result>
|
||||
</action>
|
||||
|
||||
@@ -49,6 +50,7 @@
|
||||
</action>
|
||||
|
||||
<action name="doMultipleUploadUsingArray" class="org.apache.struts2.showcase.fileupload.MultipleFileUploadUsingArrayAction" method="upload">
|
||||
<result name="input">/WEB-INF/fileupload/multipleUploadUsingArray.jsp</result>
|
||||
<result>/WEB-INF/fileupload/multiple-success.jsp</result>
|
||||
</action>
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<interceptor-ref name="defaultStack"/>
|
||||
<interceptor-ref name="token"/>
|
||||
<result name="invalid.token">/WEB-INF/token/doublePost.jsp</result>
|
||||
<result name="success" type="redirect">/WEB-INF/token/transferDone.jsp</result>
|
||||
<result name="success">/WEB-INF/token/transferDone.jsp</result>
|
||||
</action>
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
<interceptor-ref name="defaultStack"/>
|
||||
<interceptor-ref name="tokenSession"/>
|
||||
<result name="invalid.token">/WEB-INF/token/doublePost.jsp</result>
|
||||
<result name="success" type="redirect">/WEB-INF/token/transferDone.jsp</result>
|
||||
<result name="success">/WEB-INF/token/transferDone.jsp</result>
|
||||
</action>
|
||||
|
||||
|
||||
|
||||
@@ -34,28 +34,28 @@
|
||||
|
||||
<package name="validation" extends="json-default" namespace="/validation">
|
||||
<action name="index">
|
||||
<result>/WEB-INF/validation/index.jsp</result>
|
||||
<result>index.jsp</result>
|
||||
</action>
|
||||
|
||||
<action name="quizBasic" class="org.apache.struts2.showcase.validation.QuizAction">
|
||||
<result name="input">quiz-basic.jsp</result>
|
||||
<result>quiz-success.jsp</result>
|
||||
<result name="input">/WEB-INF/validation/quiz-basic.jsp</result>
|
||||
<result>/WEB-INF/validation/quiz-success.jsp</result>
|
||||
</action>
|
||||
|
||||
<action name="quizClient" class="org.apache.struts2.showcase.validation.QuizAction">
|
||||
<result name="input">quiz-client.jsp</result>
|
||||
<result>quiz-success.jsp</result>
|
||||
<result name="input">/WEB-INF/validation/quiz-client.jsp</result>
|
||||
<result>/WEB-INF/validation/quiz-success.jsp</result>
|
||||
</action>
|
||||
|
||||
<action name="quizClientCss" class="org.apache.struts2.showcase.validation.QuizAction">
|
||||
<result name="input">quiz-client-css.jsp</result>
|
||||
<result>quiz-success.jsp</result>
|
||||
<result name="input">/WEB-INF/validation/quiz-client-css.jsp</result>
|
||||
<result>/WEB-INF/validation/quiz-success.jsp</result>
|
||||
</action>
|
||||
|
||||
<action name="quizAjax" class="org.apache.struts2.showcase.validation.QuizAction">
|
||||
<interceptor-ref name="jsonValidationWorkflowStack"/>
|
||||
<result name="input">quiz-ajax.jsp</result>
|
||||
<result>quiz-success.jsp</result>
|
||||
<result name="input">/WEB-INF/validation/quiz-ajax.jsp</result>
|
||||
<result>/WEB-INF/validation/quiz-success.jsp</result>
|
||||
</action>
|
||||
|
||||
<!-- =========================================== -->
|
||||
|
||||
@@ -22,12 +22,12 @@ ${parameters.after!}<#t/>
|
||||
</td><#lt/>
|
||||
</tr>
|
||||
<#if (parameters.errorposition!"top") == 'bottom'>
|
||||
<#assign hasFieldErrors = parameters.name?? && fieldErrors?? && fieldErrors[parameters.name]??/>
|
||||
<#assign hasFieldErrors = parameters.name?? && fieldErrors?? && fieldErrors.get(parameters.name)??/>
|
||||
<#if hasFieldErrors>
|
||||
<tr errorFor="${parameters.id}">
|
||||
<td class="tdErrorMessage" colspan="2"><#rt/>
|
||||
<#if hasFieldErrors>
|
||||
<#list fieldErrors[parameters.name] as error>
|
||||
<#list fieldErrors.get(parameters.name) as error>
|
||||
<div class="errorMessage">${error?html}</div><#t/>
|
||||
</#list>
|
||||
</#if>
|
||||
|
||||
+8
-8
@@ -21,29 +21,29 @@
|
||||
<#--
|
||||
Always include elements to show errors. They may be filled later via AJAX.
|
||||
-->
|
||||
<#assign hasFieldErrors = parameters.name?? && fieldErrors?? && fieldErrors[parameters.name]??/>
|
||||
<#assign hasFieldErrors = parameters.name?? && fieldErrors?? && fieldErrors.get(parameters.name)??/>
|
||||
<#if (parameters.errorposition!"top") == 'top'>
|
||||
<tr errorFor="${parameters.id}">
|
||||
<td class="tdErrorMessage" colspan="2" data-error-for-fieldname="${parameters.name}"><#rt/>
|
||||
<#if hasFieldErrors>
|
||||
<#list fieldErrors[parameters.name] as error>
|
||||
<#list fieldErrors.get(parameters.name) as error>
|
||||
<div class="errorMessage">${error?html}</div><#t/>
|
||||
</#list>
|
||||
</#if>
|
||||
</td><#lt/>
|
||||
</tr>
|
||||
</#if>
|
||||
<#if !parameters.labelposition?? && (parameters.form.labelposition)??>
|
||||
<#assign labelpos = parameters.form.labelposition/>
|
||||
<#elseif parameters.labelposition??>
|
||||
<#assign labelpos = parameters.labelposition/>
|
||||
<#if !parameters.labelPosition?? && (parameters.form.labelPosition)??>
|
||||
<#assign labelPos = parameters.form.labelPosition/>
|
||||
<#elseif parameters.labelPosition??>
|
||||
<#assign labelpos = parameters.labelPosition/>
|
||||
</#if>
|
||||
<#--
|
||||
if the label position is top,
|
||||
then give the label it's own row in the table
|
||||
-->
|
||||
<tr>
|
||||
<#if (labelpos!"") == 'top'>
|
||||
<#if (labelPos!"") == 'top'>
|
||||
<td class="tdLabelTop" colspan="2"><#rt/>
|
||||
<#else>
|
||||
<td class="tdLabel"><#rt/>
|
||||
@@ -72,7 +72,7 @@ ${parameters.labelseparator!":"?html}<#t/>
|
||||
</#if>
|
||||
</td><#lt/>
|
||||
<#-- add the extra row -->
|
||||
<#if (labelpos!"") == 'top'>
|
||||
<#if (labelPos!"") == 'top'>
|
||||
</tr>
|
||||
<tr>
|
||||
</#if>
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<!--
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
-->
|
||||
<%@ page
|
||||
language="java"
|
||||
contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<%@ taglib prefix="s" uri="/struts-tags" %>
|
||||
<html>
|
||||
<head>
|
||||
<title>Struts2 Showcase - Fileupload sample - Multiple fileupload</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="page-header">
|
||||
<h1>Fileupload sample - Multiple fileupload</h1>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<s:iterator var="counter" begin="1" end="3">
|
||||
<!-- Mechanism for display to work with upload both as an array and list in a single JSP (a bit ugly).
|
||||
Note: Output "index" doesn't always match if the input is "sparse". -->
|
||||
<s:if test="%{#counter <= upload.length}">
|
||||
<s:set var="displayContent" value="true" />
|
||||
</s:if>
|
||||
<s:elseif test="%{#counter <= upload.size}">
|
||||
<s:set var="displayContent" value="true" />
|
||||
</s:elseif>
|
||||
<s:else>
|
||||
<s:set var="displayContent" value="false" />
|
||||
</s:else>
|
||||
<s:if test="%{#displayContent == true}">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<b>File (<s:property value="#counter" />):</b>
|
||||
<ul>
|
||||
<li>ContentType: <s:property value="uploadContentType[#counter - 1]" /></li>
|
||||
<li>FileName: <s:property value="uploadFileName[#counter -1]" /></li>
|
||||
<li>File: <s:property value="upload[#counter - 1]" /></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</s:if>
|
||||
</s:iterator>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -39,7 +39,7 @@
|
||||
<div class="hero-unit">
|
||||
<h1>Welcome!</h1>
|
||||
<p>The Struts Showcase demonstrates a variety of use cases and tag usages. Essentially, the application exercises various framework features in isolation. The Showcase is not meant as a "best practices" example.</p>
|
||||
<p>For more "by example" solutions, see the <a class="btn btn-primary btn-large">Struts Cookbook »</a> pages.</p>
|
||||
<p>For more "by example" solutions, see the <a href="https://github.com/apache/struts-examples" class="btn btn-primary btn-large">Struts Examples »</a> pages.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
-->
|
||||
<%@page import="org.apache.struts2.showcase.hangman.HangmanConstants" %>
|
||||
<%@taglib prefix="s" uri="/struts-tags" %>
|
||||
<html>
|
||||
<head>
|
||||
@@ -635,5 +636,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<p>
|
||||
A secondary test for this JSP visually verifies expression access to a public constant.
|
||||
A direct access to the same public constant via scriptlet provides secondary verification.
|
||||
</p>
|
||||
<br>
|
||||
<div>
|
||||
Test public static (constant) access (expression). Value: <s:property default="unavailable" value="@org.apache.struts2.showcase.hangman.HangmanConstants@HANGMAN_SESSION_KEY" />
|
||||
</div>
|
||||
<div>
|
||||
Test public static (constant) access (scriptlet). Value: <%=org.apache.struts2.showcase.hangman.HangmanConstants.HANGMAN_SESSION_KEY%>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
<@s.form action="transfer4">
|
||||
<@s.token/>
|
||||
<@s.textfield label="Amount" name="amount" required="true" value="400"/>
|
||||
<@s.textfield label="Amount" name="amount" required=true value="400"/>
|
||||
<@s.submit value="Transfer money" cssClass="btn btn-primary"/>
|
||||
</@s.form>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.31</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-assembly</artifactId>
|
||||
|
||||
+6
-3
@@ -30,7 +30,7 @@
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-bom</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.31</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Struts 2 Bill of Materials</name>
|
||||
@@ -45,7 +45,7 @@
|
||||
</licenses>
|
||||
|
||||
<properties>
|
||||
<struts-version.version>2.5.20</struts-version.version>
|
||||
<struts-version.version>2.5.31</struts-version.version>
|
||||
<maven.site.skip>true</maven.site.skip>
|
||||
<maven.site.deploy.skip>true</maven.site.deploy.skip>
|
||||
</properties>
|
||||
@@ -181,6 +181,9 @@
|
||||
</dependencyManagement>
|
||||
|
||||
<scm>
|
||||
<tag>STRUTS_2_5_20</tag>
|
||||
<tag>STRUTS_2_5_31</tag>
|
||||
<connection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</connection>
|
||||
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</developerConnection>
|
||||
<url>https://github.com/apache/struts/</url>
|
||||
</scm>
|
||||
</project>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.31</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-admin-bundle</artifactId>
|
||||
|
||||
+21
-10
@@ -60,6 +60,9 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
|
||||
}
|
||||
|
||||
public String start() throws BundleException {
|
||||
clearErrorsAndMessages();
|
||||
addActionMessage("Start - OSGi Host: " + osgiHost + ", ID: " + id);
|
||||
|
||||
Bundle bundle = osgiHost.getBundles().get(id);
|
||||
try {
|
||||
bundle.start();
|
||||
@@ -69,29 +72,35 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
|
||||
//there no easy way/elegant way to know if the bundle was processed already
|
||||
Thread.sleep(1000);
|
||||
} catch (Exception e) {
|
||||
addActionError(e.toString());
|
||||
addActionError("Exception: " + e.toString() + " (" + e.getMessage() + ")");
|
||||
}
|
||||
|
||||
return view();
|
||||
}
|
||||
|
||||
public String stop() throws BundleException {
|
||||
clearErrorsAndMessages();
|
||||
addActionMessage("Stop - OSGi Host: " + osgiHost + ", ID: " + id);
|
||||
|
||||
Bundle bundle = osgiHost.getBundles().get(id);
|
||||
try {
|
||||
bundle.stop();
|
||||
} catch (Exception e) {
|
||||
addActionError(e.toString());
|
||||
addActionError("Exception: " + e.toString() + " (" + e.getMessage() + ")");
|
||||
}
|
||||
|
||||
return view();
|
||||
}
|
||||
|
||||
public String update() throws BundleException {
|
||||
clearErrorsAndMessages();
|
||||
addActionMessage("Update - OSGi Host: " + osgiHost + ", ID: " + id);
|
||||
|
||||
Bundle bundle = osgiHost.getBundles().get(id);
|
||||
try {
|
||||
bundle.update();
|
||||
} catch (Exception e) {
|
||||
addActionError(e.toString());
|
||||
addActionError("Exception: " + e.toString() + " (" + e.getMessage() + ")");
|
||||
}
|
||||
|
||||
return view();
|
||||
@@ -114,13 +123,14 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
|
||||
}
|
||||
|
||||
public List<PackageConfig> getPackages() {
|
||||
List<PackageConfig> pkgs = new ArrayList<PackageConfig>();
|
||||
List<PackageConfig> pkgs = new ArrayList<>();
|
||||
Bundle bundle = getBundle();
|
||||
if (bundle.getState() == Bundle.ACTIVE) {
|
||||
for (String name : bundleAccessor.getPackagesByBundle(bundle)) {
|
||||
PackageConfig packageConfig = configuration.getPackageConfig(name);
|
||||
if (packageConfig != null)
|
||||
if (packageConfig != null) {
|
||||
pkgs.add(packageConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pkgs;
|
||||
@@ -133,13 +143,14 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
|
||||
public Collection<Bundle> getBundles() {
|
||||
List<Bundle> bundles = new ArrayList(osgiHost.getBundles().values());
|
||||
Collections.sort(bundles, new Comparator<Bundle>() {
|
||||
@Override
|
||||
public int compare(Bundle bundle1, Bundle bundle2) {
|
||||
boolean bundle1StrutsEnabled = isStrutsEnabled(bundle1);
|
||||
boolean bundle2StrutsEnabled = isStrutsEnabled(bundle2);
|
||||
|
||||
if ((bundle1StrutsEnabled && bundle2StrutsEnabled) || (!bundle1StrutsEnabled && !bundle2StrutsEnabled))
|
||||
if ((bundle1StrutsEnabled && bundle2StrutsEnabled) || (!bundle1StrutsEnabled && !bundle2StrutsEnabled)) {
|
||||
return bundle1.getSymbolicName().compareTo(bundle2.getSymbolicName());
|
||||
else {
|
||||
} else {
|
||||
return bundle1StrutsEnabled ? -1 : 1;
|
||||
}
|
||||
}
|
||||
@@ -172,7 +183,7 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
|
||||
try {
|
||||
state = bundle.getState();
|
||||
} catch (Exception e) {
|
||||
addActionError("Unable to determine bundle state: " + e.getMessage());
|
||||
addActionError("Unable to determine bundle state. Exception: " + e.toString() + " (" + e.getMessage() + ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -181,8 +192,7 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
|
||||
} else if ("stop".equals(val)) {
|
||||
return state == Bundle.ACTIVE;
|
||||
} else if ("update".equals(val)) {
|
||||
return state == Bundle.ACTIVE || state == Bundle.INSTALLED
|
||||
|| state == Bundle.RESOLVED;
|
||||
return state == Bundle.ACTIVE || state == Bundle.INSTALLED || state == Bundle.RESOLVED;
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid state");
|
||||
}
|
||||
@@ -197,6 +207,7 @@ public class BundlesAction extends ActionSupport implements ServletContextAware
|
||||
this.bundleAccessor = bundleAccessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServletContext(ServletContext servletContext) {
|
||||
osgiHost = (OsgiHost) servletContext.getAttribute(StrutsOsgiListener.OSGI_HOST);
|
||||
}
|
||||
|
||||
+62
-10
@@ -24,20 +24,29 @@ package org.apache.struts2.osgi.admin.actions;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import org.apache.struts2.osgi.DefaultBundleAccessor;
|
||||
import org.apache.felix.shell.ShellService;
|
||||
import org.apache.struts2.osgi.DefaultBundleAccessor;
|
||||
import org.apache.struts2.osgi.interceptor.BundleContextAware;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
|
||||
import com.opensymphony.xwork2.Action;
|
||||
import com.opensymphony.xwork2.ActionSupport;
|
||||
|
||||
/**
|
||||
* This action executes commands on the Felix Shell
|
||||
* This action executes commands on the Felix Shell.
|
||||
*
|
||||
* The action is BundleContextAware so that if the OSGi interceptor is used the BundleContext
|
||||
* can be provided for configurations where the DefaultBundleAccessor is insufficient.
|
||||
*
|
||||
*/
|
||||
public class ShellAction extends ActionSupport {
|
||||
public class ShellAction extends ActionSupport implements BundleContextAware {
|
||||
private String command;
|
||||
private String output;
|
||||
private BundleContext bundleContext;
|
||||
|
||||
@Override
|
||||
public String execute() {
|
||||
// get service
|
||||
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
|
||||
@@ -52,7 +61,9 @@ public class ShellAction extends ActionSupport {
|
||||
outString = outByteStream.toString().trim();
|
||||
errString = errByteStream.toString().trim();
|
||||
} catch (Exception e) {
|
||||
errString = e.getMessage();
|
||||
outString = outByteStream.toString().trim();
|
||||
errString = "Exception: " + e.toString() + " (" + e.getMessage() + "). Output:" + outString +
|
||||
". Error: " + errByteStream.toString().trim(); // Full details for troubleshooting.
|
||||
} finally {
|
||||
outStream.close();
|
||||
errStream.close();
|
||||
@@ -75,17 +86,58 @@ public class ShellAction extends ActionSupport {
|
||||
}
|
||||
|
||||
public void executeCommand(String commandLine, PrintStream out, PrintStream err) throws Exception {
|
||||
ShellService shellService = getShellService();
|
||||
if (shellService != null)
|
||||
ShellService shellService = getShellService(out);
|
||||
if (shellService != null) {
|
||||
out.println("Attempting to execute command: " + commandLine);
|
||||
shellService.executeCommand(commandLine, out, err);
|
||||
}
|
||||
else
|
||||
err.println("Apache Felix Shell service is not installed");
|
||||
}
|
||||
|
||||
private ShellService getShellService() {
|
||||
private ShellService getShellService(PrintStream out) {
|
||||
//bundle can be de-activated, so keeping a reference aorund is not a good idea
|
||||
DefaultBundleAccessor bundleAcessor = DefaultBundleAccessor.getInstance();
|
||||
ServiceReference ref = bundleAcessor.getServiceReference(ShellService.class.getName());
|
||||
return (ShellService) bundleAcessor.getService(ref);
|
||||
final DefaultBundleAccessor bundleAccessor = DefaultBundleAccessor.getInstance();
|
||||
ServiceReference ref = (bundleAccessor != null ? bundleAccessor.getServiceReference(ShellService.class.getName()) : null);
|
||||
//out.println("DefaultBundleAccessor: " + bundleAcessor + ", ServiceReference [" + ShellService.class.getName() + "]: " + ref); // No logger, for debugging only.
|
||||
|
||||
if (ref == null && this.bundleContext != null) {
|
||||
// Depending on OSGi and Felix bundle configurations, the DefaultBundleAccessor may not be able to locate the ShellService.
|
||||
// In such cases, use the bundleContext (if available) to locate the ShellService in a "brute-force" manner.
|
||||
final Bundle[] bundles = this.bundleContext.getBundles();
|
||||
if (bundles != null && bundles.length > 0) {
|
||||
for (Bundle currentBundle : bundles) {
|
||||
if (currentBundle != null) {
|
||||
//out.println("Bundle [" + index + "], SymbolicName: " + currentBundle.getSymbolicName() + ", Location: " + currentBundle.getLocation() + ", BundleID: " + currentBundle.getBundleId()); // No logger, for debugging only.
|
||||
if (currentBundle.getSymbolicName().startsWith("org.apache.felix.shell")) {
|
||||
BundleContext currentBundleContext = currentBundle.getBundleContext();
|
||||
Object directShellServiceByClass = (currentBundleContext != null ? currentBundleContext.getServiceReference(org.apache.felix.shell.ShellService.class) : null);
|
||||
Object directShellServiceByName = (currentBundleContext != null ? currentBundleContext.getServiceReference("org.apache.felix.shell.ShellService") : null);
|
||||
//out.println(" ShellService reference (via bundle's context) by class: " + directShellServiceByClass); // No logger, for debugging only.
|
||||
//out.println(" ShellService reference (via bundle's context) by name: " + directShellServiceByName); // No logger, for debugging only.
|
||||
if (ref == null) {
|
||||
ref = (directShellServiceByClass != null ? (ServiceReference) directShellServiceByClass : (ServiceReference) directShellServiceByName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//out.println("Bundle [" + index + "] is null"); // No logger, for debugging only.
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//out.println("OSGi Interceptor-provided BundleContext bundle array is null or empty"); // No logger, for debugging only.
|
||||
}
|
||||
}
|
||||
|
||||
if (ref == null) {
|
||||
out.println("ShellService reference cannot be found (null), service lookup will fail.");
|
||||
}
|
||||
|
||||
return (ShellService) (bundleAccessor != null ? bundleAccessor.getService(ref) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBundleContext(BundleContext bundleContext) {
|
||||
//System.out.println("ShellAction - setBundleContext called. BundleContext: " + bundleContext); // No logger, for debugging only.
|
||||
this.bundleContext = bundleContext;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
Copyright (c) 2009 Paul Bakaus, http://jqueryui.com/
|
||||
Copyright jQuery Foundation and other contributors, https://jquery.org/
|
||||
|
||||
This software consists of voluntary contributions made by many
|
||||
individuals (AUTHORS.txt, http://jqueryui.com/about) For exact
|
||||
contribution history, see the revision history and logs, available
|
||||
at http://jquery-ui.googlecode.com/svn/
|
||||
individuals. For exact contribution history, see the revision history
|
||||
available at https://github.com/jquery/jquery-ui
|
||||
|
||||
The following license applies to all parts of this software except as
|
||||
documented below:
|
||||
|
||||
====
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@@ -23,3 +27,17 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
====
|
||||
|
||||
Copyright and related rights for sample code are waived via CC0. Sample
|
||||
code is defined as all source code contained within the demos directory.
|
||||
|
||||
CC0: http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
====
|
||||
|
||||
All files located in the node_modules and external directories are
|
||||
externally maintained libraries used by this software which have their
|
||||
own licenses; we recommend you read them, as their terms may differ from
|
||||
the terms above.
|
||||
|
||||
@@ -2,7 +2,8 @@ Apache Struts
|
||||
Copyright 2000-2011 The Apache Software Foundation
|
||||
|
||||
This product includes software developed by
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
||||
The Apache Software Foundation (https://www.apache.org/).
|
||||
|
||||
The binary distributions includes the following third party software:
|
||||
JQuery (http://jquery.com/).
|
||||
jQuery (https://jquery.com/).
|
||||
jQuery UI (https://jqueryui.com/).
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/shell.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/main.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.7.1.custom.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
|
||||
|
||||
<script src="<@s.url value="/static/js/shell.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-1.3.2.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-ui-1.7.1.custom.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-1.12.4.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-ui-1.12.1.min.js" />"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="menu">
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
<title>${bundle.symbolicName!}</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/main.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.7.1.custom.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
|
||||
|
||||
<script src="<@s.url value="/static/js/jquery-1.3.2.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-ui-1.7.1.custom.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-1.12.4.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-ui-1.12.1.min.js" />"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
@@ -163,5 +163,8 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<@s.actionmessage />
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -23,10 +23,10 @@
|
||||
<title>OSGi Bundles</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/main.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.7.1.custom.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="/static/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
|
||||
|
||||
<script src="<@s.url value="/static/js/jquery-1.3.2.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-ui-1.7.1.custom.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-1.12.4.min.js" />"></script>
|
||||
<script src="<@s.url value="/static/js/jquery-ui-1.12.1.min.js" />"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -44,6 +44,9 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<@s.actionerror />
|
||||
|
||||
<table class="properties" style="clear:both; width:700px">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -87,5 +90,8 @@
|
||||
</#list>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<@s.actionmessage />
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+1311
File diff suppressed because it is too large
Load Diff
-404
@@ -1,404 +0,0 @@
|
||||
/*
|
||||
* jQuery UI CSS Framework
|
||||
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
|
||||
*/
|
||||
|
||||
/* Layout helpers
|
||||
----------------------------------*/
|
||||
.ui-helper-hidden { display: none; }
|
||||
.ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
|
||||
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
|
||||
.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
|
||||
.ui-helper-clearfix { display: inline-block; }
|
||||
/* required comment for clearfix to work in Opera \*/
|
||||
* html .ui-helper-clearfix { height:1%; }
|
||||
.ui-helper-clearfix { display:block; }
|
||||
/* end clearfix */
|
||||
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
|
||||
|
||||
|
||||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-disabled { cursor: default !important; }
|
||||
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
||||
|
||||
/*
|
||||
* jQuery UI CSS Framework
|
||||
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
|
||||
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Lucida%20Grande,%20Lucida%20Sans,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=5px&bgColorHeader=5c9ccc&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=55&borderColorHeader=4297d7&fcHeader=ffffff&iconColorHeader=d8e7f3&bgColorContent=fcfdfd&bgTextureContent=06_inset_hard.png&bgImgOpacityContent=100&borderColorContent=a6c9e2&fcContent=222222&iconColorContent=469bdd&bgColorDefault=dfeffc&bgTextureDefault=02_glass.png&bgImgOpacityDefault=85&borderColorDefault=c5dbec&fcDefault=2e6e9e&iconColorDefault=6da8d5&bgColorHover=d0e5f5&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=79b7e7&fcHover=1d5987&iconColorHover=217bc0&bgColorActive=f5f8f9&bgTextureActive=06_inset_hard.png&bgImgOpacityActive=100&borderColorActive=79b7e7&fcActive=e17009&iconColorActive=f9bd01&bgColorHighlight=fbec88&bgTextureHighlight=01_flat.png&bgImgOpacityHighlight=55&borderColorHighlight=fad42e&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
|
||||
*/
|
||||
|
||||
|
||||
/* Component containers
|
||||
----------------------------------*/
|
||||
.ui-widget { font-family: Lucida Grande, Lucida Sans, Arial, sans-serif; font-size: 1.1em; }
|
||||
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Lucida Grande, Lucida Sans, Arial, sans-serif; font-size: 1em; }
|
||||
.ui-widget-content { border: 1px solid #a6c9e2; background: #fcfdfd url(images/ui-bg_inset-hard_100_fcfdfd_1x100.png) 50% bottom repeat-x; color: #222222; }
|
||||
.ui-widget-content a { color: #222222; }
|
||||
.ui-widget-header { border: 1px solid #4297d7; background: #5c9ccc url(images/ui-bg_gloss-wave_55_5c9ccc_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
|
||||
.ui-widget-header a { color: #ffffff; }
|
||||
|
||||
/* Interaction states
|
||||
----------------------------------*/
|
||||
.ui-state-default, .ui-widget-content .ui-state-default { border: 1px solid #c5dbec; background: #dfeffc url(images/ui-bg_glass_85_dfeffc_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #2e6e9e; outline: none; }
|
||||
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #2e6e9e; text-decoration: none; outline: none; }
|
||||
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus { border: 1px solid #79b7e7; background: #d0e5f5 url(images/ui-bg_glass_75_d0e5f5_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1d5987; outline: none; }
|
||||
.ui-state-hover a, .ui-state-hover a:hover { color: #1d5987; text-decoration: none; outline: none; }
|
||||
.ui-state-active, .ui-widget-content .ui-state-active { border: 1px solid #79b7e7; background: #f5f8f9 url(images/ui-bg_inset-hard_100_f5f8f9_1x100.png) 50% 50% repeat-x; font-weight: bold; color: #e17009; outline: none; }
|
||||
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #e17009; outline: none; text-decoration: none; }
|
||||
|
||||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-highlight, .ui-widget-content .ui-state-highlight {border: 1px solid #fad42e; background: #fbec88 url(images/ui-bg_flat_55_fbec88_40x100.png) 50% 50% repeat-x; color: #363636; }
|
||||
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a { color: #363636; }
|
||||
.ui-state-error, .ui-widget-content .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; }
|
||||
.ui-state-error a, .ui-widget-content .ui-state-error a { color: #cd0a0a; }
|
||||
.ui-state-error-text, .ui-widget-content .ui-state-error-text { color: #cd0a0a; }
|
||||
.ui-state-disabled, .ui-widget-content .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
|
||||
.ui-priority-primary, .ui-widget-content .ui-priority-primary { font-weight: bold; }
|
||||
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_469bdd_256x240.png); }
|
||||
.ui-widget-content .ui-icon {background-image: url(images/ui-icons_469bdd_256x240.png); }
|
||||
.ui-widget-header .ui-icon {background-image: url(images/ui-icons_d8e7f3_256x240.png); }
|
||||
.ui-state-default .ui-icon { background-image: url(images/ui-icons_6da8d5_256x240.png); }
|
||||
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_217bc0_256x240.png); }
|
||||
.ui-state-active .ui-icon {background-image: url(images/ui-icons_f9bd01_256x240.png); }
|
||||
.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); }
|
||||
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
|
||||
|
||||
/* positioning */
|
||||
.ui-icon-carat-1-n { background-position: 0 0; }
|
||||
.ui-icon-carat-1-ne { background-position: -16px 0; }
|
||||
.ui-icon-carat-1-e { background-position: -32px 0; }
|
||||
.ui-icon-carat-1-se { background-position: -48px 0; }
|
||||
.ui-icon-carat-1-s { background-position: -64px 0; }
|
||||
.ui-icon-carat-1-sw { background-position: -80px 0; }
|
||||
.ui-icon-carat-1-w { background-position: -96px 0; }
|
||||
.ui-icon-carat-1-nw { background-position: -112px 0; }
|
||||
.ui-icon-carat-2-n-s { background-position: -128px 0; }
|
||||
.ui-icon-carat-2-e-w { background-position: -144px 0; }
|
||||
.ui-icon-triangle-1-n { background-position: 0 -16px; }
|
||||
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
|
||||
.ui-icon-triangle-1-e { background-position: -32px -16px; }
|
||||
.ui-icon-triangle-1-se { background-position: -48px -16px; }
|
||||
.ui-icon-triangle-1-s { background-position: -64px -16px; }
|
||||
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
|
||||
.ui-icon-triangle-1-w { background-position: -96px -16px; }
|
||||
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
|
||||
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
|
||||
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
|
||||
.ui-icon-arrow-1-n { background-position: 0 -32px; }
|
||||
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
|
||||
.ui-icon-arrow-1-e { background-position: -32px -32px; }
|
||||
.ui-icon-arrow-1-se { background-position: -48px -32px; }
|
||||
.ui-icon-arrow-1-s { background-position: -64px -32px; }
|
||||
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
|
||||
.ui-icon-arrow-1-w { background-position: -96px -32px; }
|
||||
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
|
||||
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
|
||||
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
|
||||
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
|
||||
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
|
||||
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
|
||||
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
|
||||
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
|
||||
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
|
||||
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
|
||||
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
|
||||
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
|
||||
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
|
||||
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
|
||||
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
|
||||
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
|
||||
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
|
||||
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
|
||||
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
|
||||
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
|
||||
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
|
||||
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
|
||||
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
|
||||
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
|
||||
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
|
||||
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
|
||||
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
|
||||
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
|
||||
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
|
||||
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
|
||||
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
|
||||
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
|
||||
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
|
||||
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
|
||||
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
|
||||
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
|
||||
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
|
||||
.ui-icon-arrow-4 { background-position: 0 -80px; }
|
||||
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
|
||||
.ui-icon-extlink { background-position: -32px -80px; }
|
||||
.ui-icon-newwin { background-position: -48px -80px; }
|
||||
.ui-icon-refresh { background-position: -64px -80px; }
|
||||
.ui-icon-shuffle { background-position: -80px -80px; }
|
||||
.ui-icon-transfer-e-w { background-position: -96px -80px; }
|
||||
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
|
||||
.ui-icon-folder-collapsed { background-position: 0 -96px; }
|
||||
.ui-icon-folder-open { background-position: -16px -96px; }
|
||||
.ui-icon-document { background-position: -32px -96px; }
|
||||
.ui-icon-document-b { background-position: -48px -96px; }
|
||||
.ui-icon-note { background-position: -64px -96px; }
|
||||
.ui-icon-mail-closed { background-position: -80px -96px; }
|
||||
.ui-icon-mail-open { background-position: -96px -96px; }
|
||||
.ui-icon-suitcase { background-position: -112px -96px; }
|
||||
.ui-icon-comment { background-position: -128px -96px; }
|
||||
.ui-icon-person { background-position: -144px -96px; }
|
||||
.ui-icon-print { background-position: -160px -96px; }
|
||||
.ui-icon-trash { background-position: -176px -96px; }
|
||||
.ui-icon-locked { background-position: -192px -96px; }
|
||||
.ui-icon-unlocked { background-position: -208px -96px; }
|
||||
.ui-icon-bookmark { background-position: -224px -96px; }
|
||||
.ui-icon-tag { background-position: -240px -96px; }
|
||||
.ui-icon-home { background-position: 0 -112px; }
|
||||
.ui-icon-flag { background-position: -16px -112px; }
|
||||
.ui-icon-calendar { background-position: -32px -112px; }
|
||||
.ui-icon-cart { background-position: -48px -112px; }
|
||||
.ui-icon-pencil { background-position: -64px -112px; }
|
||||
.ui-icon-clock { background-position: -80px -112px; }
|
||||
.ui-icon-disk { background-position: -96px -112px; }
|
||||
.ui-icon-calculator { background-position: -112px -112px; }
|
||||
.ui-icon-zoomin { background-position: -128px -112px; }
|
||||
.ui-icon-zoomout { background-position: -144px -112px; }
|
||||
.ui-icon-search { background-position: -160px -112px; }
|
||||
.ui-icon-wrench { background-position: -176px -112px; }
|
||||
.ui-icon-gear { background-position: -192px -112px; }
|
||||
.ui-icon-heart { background-position: -208px -112px; }
|
||||
.ui-icon-star { background-position: -224px -112px; }
|
||||
.ui-icon-link { background-position: -240px -112px; }
|
||||
.ui-icon-cancel { background-position: 0 -128px; }
|
||||
.ui-icon-plus { background-position: -16px -128px; }
|
||||
.ui-icon-plusthick { background-position: -32px -128px; }
|
||||
.ui-icon-minus { background-position: -48px -128px; }
|
||||
.ui-icon-minusthick { background-position: -64px -128px; }
|
||||
.ui-icon-close { background-position: -80px -128px; }
|
||||
.ui-icon-closethick { background-position: -96px -128px; }
|
||||
.ui-icon-key { background-position: -112px -128px; }
|
||||
.ui-icon-lightbulb { background-position: -128px -128px; }
|
||||
.ui-icon-scissors { background-position: -144px -128px; }
|
||||
.ui-icon-clipboard { background-position: -160px -128px; }
|
||||
.ui-icon-copy { background-position: -176px -128px; }
|
||||
.ui-icon-contact { background-position: -192px -128px; }
|
||||
.ui-icon-image { background-position: -208px -128px; }
|
||||
.ui-icon-video { background-position: -224px -128px; }
|
||||
.ui-icon-script { background-position: -240px -128px; }
|
||||
.ui-icon-alert { background-position: 0 -144px; }
|
||||
.ui-icon-info { background-position: -16px -144px; }
|
||||
.ui-icon-notice { background-position: -32px -144px; }
|
||||
.ui-icon-help { background-position: -48px -144px; }
|
||||
.ui-icon-check { background-position: -64px -144px; }
|
||||
.ui-icon-bullet { background-position: -80px -144px; }
|
||||
.ui-icon-radio-off { background-position: -96px -144px; }
|
||||
.ui-icon-radio-on { background-position: -112px -144px; }
|
||||
.ui-icon-pin-w { background-position: -128px -144px; }
|
||||
.ui-icon-pin-s { background-position: -144px -144px; }
|
||||
.ui-icon-play { background-position: 0 -160px; }
|
||||
.ui-icon-pause { background-position: -16px -160px; }
|
||||
.ui-icon-seek-next { background-position: -32px -160px; }
|
||||
.ui-icon-seek-prev { background-position: -48px -160px; }
|
||||
.ui-icon-seek-end { background-position: -64px -160px; }
|
||||
.ui-icon-seek-first { background-position: -80px -160px; }
|
||||
.ui-icon-stop { background-position: -96px -160px; }
|
||||
.ui-icon-eject { background-position: -112px -160px; }
|
||||
.ui-icon-volume-off { background-position: -128px -160px; }
|
||||
.ui-icon-volume-on { background-position: -144px -160px; }
|
||||
.ui-icon-power { background-position: 0 -176px; }
|
||||
.ui-icon-signal-diag { background-position: -16px -176px; }
|
||||
.ui-icon-signal { background-position: -32px -176px; }
|
||||
.ui-icon-battery-0 { background-position: -48px -176px; }
|
||||
.ui-icon-battery-1 { background-position: -64px -176px; }
|
||||
.ui-icon-battery-2 { background-position: -80px -176px; }
|
||||
.ui-icon-battery-3 { background-position: -96px -176px; }
|
||||
.ui-icon-circle-plus { background-position: 0 -192px; }
|
||||
.ui-icon-circle-minus { background-position: -16px -192px; }
|
||||
.ui-icon-circle-close { background-position: -32px -192px; }
|
||||
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
|
||||
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
|
||||
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
|
||||
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
|
||||
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
|
||||
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
|
||||
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
|
||||
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
|
||||
.ui-icon-circle-zoomin { background-position: -176px -192px; }
|
||||
.ui-icon-circle-zoomout { background-position: -192px -192px; }
|
||||
.ui-icon-circle-check { background-position: -208px -192px; }
|
||||
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
|
||||
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
|
||||
.ui-icon-circlesmall-close { background-position: -32px -208px; }
|
||||
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
|
||||
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
|
||||
.ui-icon-squaresmall-close { background-position: -80px -208px; }
|
||||
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
|
||||
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
|
||||
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
|
||||
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
|
||||
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
|
||||
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Corner radius */
|
||||
.ui-corner-tl { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; }
|
||||
.ui-corner-tr { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; }
|
||||
.ui-corner-bl { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; }
|
||||
.ui-corner-br { -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
|
||||
.ui-corner-top { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; }
|
||||
.ui-corner-bottom { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
|
||||
.ui-corner-right { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
|
||||
.ui-corner-left { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; }
|
||||
.ui-corner-all { -moz-border-radius: 5px; -webkit-border-radius: 5px; }
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
|
||||
.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; }/* Accordion
|
||||
----------------------------------*/
|
||||
.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
|
||||
.ui-accordion .ui-accordion-li-fix { display: inline; }
|
||||
.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
|
||||
.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em 2.2em; }
|
||||
.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
|
||||
.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; }
|
||||
.ui-accordion .ui-accordion-content-active { display: block; }/* Datepicker
|
||||
----------------------------------*/
|
||||
.ui-datepicker { width: 17em; padding: .2em .2em 0; }
|
||||
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
|
||||
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
|
||||
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
|
||||
.ui-datepicker .ui-datepicker-prev { left:2px; }
|
||||
.ui-datepicker .ui-datepicker-next { right:2px; }
|
||||
.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
|
||||
.ui-datepicker .ui-datepicker-next-hover { right:1px; }
|
||||
.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
|
||||
.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
|
||||
.ui-datepicker .ui-datepicker-title select { float:left; font-size:1em; margin:1px 0; }
|
||||
.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
|
||||
.ui-datepicker select.ui-datepicker-month,
|
||||
.ui-datepicker select.ui-datepicker-year { width: 49%;}
|
||||
.ui-datepicker .ui-datepicker-title select.ui-datepicker-year { float: right; }
|
||||
.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
|
||||
.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
|
||||
.ui-datepicker td { border: 0; padding: 1px; }
|
||||
.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
|
||||
.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
|
||||
.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
|
||||
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
|
||||
|
||||
/* with multiple calendars */
|
||||
.ui-datepicker.ui-datepicker-multi { width:auto; }
|
||||
.ui-datepicker-multi .ui-datepicker-group { float:left; }
|
||||
.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
|
||||
.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
|
||||
.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
|
||||
.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
|
||||
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
|
||||
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
|
||||
.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
|
||||
.ui-datepicker-row-break { clear:both; width:100%; }
|
||||
|
||||
/* RTL support */
|
||||
.ui-datepicker-rtl { direction: rtl; }
|
||||
.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
|
||||
.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
|
||||
.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
|
||||
.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
|
||||
.ui-datepicker-rtl .ui-datepicker-group { float:right; }
|
||||
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
|
||||
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
|
||||
|
||||
/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
|
||||
.ui-datepicker-cover {
|
||||
display: none; /*sorry for IE5*/
|
||||
display/**/: block; /*sorry for IE5*/
|
||||
position: absolute; /*must have*/
|
||||
z-index: -1; /*must have*/
|
||||
filter: mask(); /*must have*/
|
||||
top: -4px; /*must have*/
|
||||
left: -4px; /*must have*/
|
||||
width: 200px; /*must have*/
|
||||
height: 200px; /*must have*/
|
||||
}/* Dialog
|
||||
----------------------------------*/
|
||||
.ui-dialog { position: relative; padding: .2em; width: 300px; }
|
||||
.ui-dialog .ui-dialog-titlebar { padding: .5em .3em .3em 1em; position: relative; }
|
||||
.ui-dialog .ui-dialog-title { float: left; margin: .1em 0 .2em; }
|
||||
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
|
||||
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
|
||||
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
|
||||
.ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
|
||||
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
|
||||
.ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; }
|
||||
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
|
||||
.ui-draggable .ui-dialog-titlebar { cursor: move; }
|
||||
/* Progressbar
|
||||
----------------------------------*/
|
||||
.ui-progressbar { height:2em; text-align: left; }
|
||||
.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }/* Resizable
|
||||
----------------------------------*/
|
||||
.ui-resizable { position: relative;}
|
||||
.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
|
||||
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
|
||||
.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0px; }
|
||||
.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0px; }
|
||||
.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0px; height: 100%; }
|
||||
.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0px; height: 100%; }
|
||||
.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
|
||||
.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
|
||||
.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
|
||||
.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Slider
|
||||
----------------------------------*/
|
||||
.ui-slider { position: relative; text-align: left; }
|
||||
.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
|
||||
.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; }
|
||||
|
||||
.ui-slider-horizontal { height: .8em; }
|
||||
.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
|
||||
.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
|
||||
.ui-slider-horizontal .ui-slider-range-min { left: 0; }
|
||||
.ui-slider-horizontal .ui-slider-range-max { right: 0; }
|
||||
|
||||
.ui-slider-vertical { width: .8em; height: 100px; }
|
||||
.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
|
||||
.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
|
||||
.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
|
||||
.ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs
|
||||
----------------------------------*/
|
||||
.ui-tabs { padding: .2em; zoom: 1; }
|
||||
.ui-tabs .ui-tabs-nav { list-style: none; position: relative; padding: .2em .2em 0; }
|
||||
.ui-tabs .ui-tabs-nav li { position: relative; float: left; border-bottom-width: 0 !important; margin: 0 .2em -1px 0; padding: 0; }
|
||||
.ui-tabs .ui-tabs-nav li a { float: left; text-decoration: none; padding: .5em 1em; }
|
||||
.ui-tabs .ui-tabs-nav li.ui-tabs-selected { padding-bottom: 1px; border-bottom-width: 0; }
|
||||
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
|
||||
.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
|
||||
.ui-tabs .ui-tabs-panel { padding: 1em 1.4em; display: block; border-width: 0; background: none; }
|
||||
.ui-tabs .ui-tabs-hide { display: none !important; }
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -29,8 +29,8 @@
|
||||
result_div.appendChild(document.createElement('br'));
|
||||
|
||||
for (var line_index in result_array) {
|
||||
var result_wrap = document.createElement('pre')
|
||||
line = document.createTextNode(result_array[line_index]);
|
||||
var result_wrap = document.createElement('pre');
|
||||
var line = document.createTextNode(result_array[line_index]);
|
||||
result_wrap.appendChild(line);
|
||||
result_div.appendChild(result_wrap);
|
||||
result_div.appendChild(document.createElement('br'));
|
||||
|
||||
@@ -21,28 +21,29 @@
|
||||
-->
|
||||
|
||||
<!DOCTYPE struts PUBLIC
|
||||
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
|
||||
"http://struts.apache.org/dtds/struts-2.0.dtd">
|
||||
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
|
||||
"http://struts.apache.org/dtds/struts-2.5.dtd">
|
||||
|
||||
<struts>
|
||||
<package name="bundle-admin" namespace="/osgi/admin" extends="struts-default">
|
||||
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
|
||||
<package name="bundle-admin" namespace="/osgi/admin" extends="osgi-default" strict-method-invocation="true">
|
||||
<default-action-ref name="bundles" />
|
||||
|
||||
<action name="bundle_*!*" class="org.apache.struts2.osgi.admin.actions.BundlesAction" method="{2}">
|
||||
<param name="id">{1}</param>
|
||||
<result type="freemarker">viewBundle.ftl</result>
|
||||
<allowed-methods>index,view,start,stop,update</allowed-methods>
|
||||
</action>
|
||||
|
||||
<action name="bundles" class="org.apache.struts2.osgi.admin.actions.BundlesAction" method="index">
|
||||
<result type="freemarker">viewBundles.ftl</result>
|
||||
</action>
|
||||
|
||||
|
||||
<action name="execCommand" class="org.apache.struts2.osgi.admin.actions.ShellAction">
|
||||
<result type="freemarker">commandResult.ftl</result>
|
||||
</action>
|
||||
|
||||
<action name="shell">
|
||||
<action name="shell" class="org.apache.struts2.osgi.admin.actions.ShellAction">
|
||||
<result type="freemarker">shell.ftl</result>
|
||||
</action>
|
||||
</package>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.31</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-demo-bundle</artifactId>
|
||||
@@ -67,7 +67,6 @@
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<version>2.1.0</version>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<manifestLocation>META-INF</manifestLocation>
|
||||
|
||||
@@ -22,13 +22,21 @@ package actions.osgi;
|
||||
|
||||
import com.opensymphony.xwork2.ActionSupport;
|
||||
import org.apache.struts2.convention.annotation.Action;
|
||||
import org.apache.struts2.convention.annotation.Actions;
|
||||
import org.apache.struts2.convention.annotation.Namespace;
|
||||
import org.apache.struts2.convention.annotation.Result;
|
||||
import org.apache.struts2.convention.annotation.ResultPath;
|
||||
|
||||
@ResultPath("/content")
|
||||
@Namespace("/osgi")
|
||||
@ResultPath("/content/osgi")
|
||||
public class HelloWorldAction extends ActionSupport {
|
||||
private Message message;
|
||||
private Message message = new Message("Default non-null message");
|
||||
|
||||
@Action("hello-convention")
|
||||
@Override
|
||||
@Actions({
|
||||
@Action(value="hello-convention", results={@Result(name="success", type="freemarker", location="/content/osgi/hello-convention.ftl")}),
|
||||
@Action(value="/osgi/hello-convention", results={@Result(name="success", type="freemarker", location="/content/osgi/hello-convention.ftl")})
|
||||
})
|
||||
public String execute() {
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -45,6 +53,7 @@ public class HelloWorldAction extends ActionSupport {
|
||||
return "Hello!!!";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("{message:");
|
||||
sb.append(message != null ? message.getText() : "null");
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Action mapped by the XML configurationn</title>
|
||||
<title>Action mapped by the XML configuration</title>
|
||||
</head>
|
||||
<body>
|
||||
This is an action mapped by XML configuration, using a <b>FreeMarker</b> result.
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*#
|
||||
<html>
|
||||
<head>
|
||||
<title>Action mapped by the XML configurationn</title>
|
||||
<title>Action mapped by the XML configuration</title>
|
||||
</head>
|
||||
<body>
|
||||
This is an action mapped by XML configuration, using a <b>Velocity</b> result.
|
||||
|
||||
@@ -20,21 +20,36 @@
|
||||
*/
|
||||
-->
|
||||
<!DOCTYPE struts PUBLIC
|
||||
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
|
||||
"http://struts.apache.org/dtds/struts-2.0.dtd">
|
||||
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
|
||||
"http://struts.apache.org/dtds/struts-2.5.dtd">
|
||||
|
||||
<struts>
|
||||
<package name="bundle-demo" namespace="/osgi" extends="osgi-default">
|
||||
<!-- Set some Struts 2 constants relevant to the OSGi Plugin.
|
||||
Note: The constant definitions specified here (within the demo bundle) may also need to be
|
||||
duplicated in the struts.xml configuration of the outer project that packages the demo
|
||||
bundle JAR file within it.
|
||||
-->
|
||||
<constant name="struts.objectFactory" value="osgi" />
|
||||
<constant name="struts.objectFactory.delegate" value="struts" />
|
||||
<constant name="struts.freemarker.manager.classname" value="org.apache.struts2.osgi.BundleFreemarkerManager" />
|
||||
<constant name="struts.velocity.manager.classname" value="org.apache.struts2.osgi.VelocityBundleResourceLoader" />
|
||||
<constant name="struts.staticContentLoader" value="org.apache.struts2.osgi.loaders.StaticContentBundleResourceLoader" />
|
||||
|
||||
<package name="bundle-demo" namespace="/osgi" extends="osgi-default" strict-method-invocation="true">
|
||||
|
||||
<default-action-ref name="home" />
|
||||
<action name="hello-velocity" class="helloWorldAction">
|
||||
|
||||
<action name="hello-velocity" class="actions.osgi.HelloWorldAction">
|
||||
<result type="velocity">/content/osgi/hello.vm</result>
|
||||
</action>
|
||||
<action name="hello-freemarker" class="helloWorldAction">
|
||||
|
||||
<action name="hello-freemarker" class="actions.osgi.HelloWorldAction">
|
||||
<result type="freemarker">/content/osgi/hello.ftl</result>
|
||||
</action>
|
||||
|
||||
<action name="home">
|
||||
<action name="home" class="actions.osgi.HelloWorldAction">
|
||||
<result type="freemarker">/content/osgi/home.ftl</result>
|
||||
</action>
|
||||
|
||||
</package>
|
||||
</struts>
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.31</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>struts2-osgi-bundles</artifactId>
|
||||
|
||||
+3
-2
@@ -24,7 +24,7 @@
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-parent</artifactId>
|
||||
<version>2.5.20</version>
|
||||
<version>2.5.31</version>
|
||||
</parent>
|
||||
<artifactId>struts2-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -190,7 +190,8 @@
|
||||
<dependency>
|
||||
<groupId>com.sun</groupId>
|
||||
<artifactId>tools</artifactId>
|
||||
<version>1.5.0</version>
|
||||
<version>1.7.0</version>
|
||||
<!-- Match version of minimum compatible version -->
|
||||
<scope>system</scope>
|
||||
<systemPath>${java.home}/../lib/tools.jar</systemPath>
|
||||
</dependency>
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.opensymphony.xwork2.test.StubConfigurationProvider;
|
||||
import com.opensymphony.xwork2.util.XWorkTestCaseHelper;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import junit.framework.TestCase;
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
|
||||
/**
|
||||
* Base JUnit TestCase to extend for XWork specific JUnit tests. Uses
|
||||
@@ -78,16 +79,20 @@ public abstract class XWorkTestCase extends TestCase {
|
||||
@Override
|
||||
public void register(ContainerBuilder builder,
|
||||
LocatableProperties props) throws ConfigurationException {
|
||||
builder.factory(type, name, new Factory() {
|
||||
public Object create(Context context) throws Exception {
|
||||
return impl;
|
||||
}
|
||||
if (impl instanceof String || ClassUtils.isPrimitiveOrWrapper(impl.getClass())) {
|
||||
props.setProperty(name, "" + impl);
|
||||
} else {
|
||||
builder.factory(type, name, new Factory() {
|
||||
public Object create(Context context) throws Exception {
|
||||
return impl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class type() {
|
||||
return impl.getClass();
|
||||
}
|
||||
}, Scope.SINGLETON);
|
||||
@Override
|
||||
public Class type() {
|
||||
return impl.getClass();
|
||||
}
|
||||
}, Scope.SINGLETON);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -36,10 +36,8 @@ import java.util.*;
|
||||
* @since 2.1
|
||||
*/
|
||||
public abstract class AbstractMatcher<E> implements Serializable {
|
||||
/**
|
||||
* <p> The logging instance </p>
|
||||
*/
|
||||
private static final Logger log = LogManager.getLogger(AbstractMatcher.class);
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(AbstractMatcher.class);
|
||||
|
||||
/**
|
||||
* <p> Handles all wildcard pattern matching. </p>
|
||||
@@ -50,10 +48,34 @@ public abstract class AbstractMatcher<E> implements Serializable {
|
||||
* <p> The compiled patterns and their associated target objects </p>
|
||||
*/
|
||||
List<Mapping<E>> compiledPatterns = new ArrayList<>();
|
||||
;
|
||||
|
||||
/**
|
||||
* This flag controls if passed named params should be appended
|
||||
* to the map in {@link #replaceParameters(Map, Map)}
|
||||
* and will be accessible in {@link com.opensymphony.xwork2.config.entities.ResultConfig}.
|
||||
* If set to false, the named parameters won't be appended.
|
||||
*
|
||||
* This behaviour is controlled by {@link org.apache.struts2.StrutsConstants#STRUTS_MATCHER_APPEND_NAMED_PARAMETERS}
|
||||
*
|
||||
* @since 2.5.23
|
||||
* See WW-5065
|
||||
*/
|
||||
private final boolean appendNamedParameters;
|
||||
|
||||
public AbstractMatcher(PatternMatcher<?> helper) {
|
||||
public AbstractMatcher(PatternMatcher<?> helper, boolean appendNamedParameters) {
|
||||
this.wildcard = (PatternMatcher<Object>) helper;
|
||||
this.appendNamedParameters = appendNamedParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a matcher with {@link #appendNamedParameters} set to true to keep backward compatibility
|
||||
*
|
||||
* @param helper an instance of {@link PatternMatcher}
|
||||
* @deprecated use @{link {@link AbstractMatcher(PatternMatcher, boolean)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public AbstractMatcher(PatternMatcher<?> helper) {
|
||||
this(helper, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,17 +106,17 @@ public abstract class AbstractMatcher<E> implements Serializable {
|
||||
name = name.substring(1);
|
||||
}
|
||||
|
||||
log.debug("Compiling pattern '{}'", name);
|
||||
LOG.debug("Compiling pattern '{}'", name);
|
||||
|
||||
pattern = wildcard.compilePattern(name);
|
||||
compiledPatterns.add(new Mapping<E>(name, pattern, target));
|
||||
compiledPatterns.add(new Mapping<>(name, pattern, target));
|
||||
|
||||
if (looseMatch) {
|
||||
int lastStar = name.lastIndexOf('*');
|
||||
if (lastStar > 1 && lastStar == name.length() - 1) {
|
||||
if (name.charAt(lastStar - 1) != '*') {
|
||||
pattern = wildcard.compilePattern(name.substring(0, lastStar - 1));
|
||||
compiledPatterns.add(new Mapping<E>(name, pattern, target));
|
||||
compiledPatterns.add(new Mapping<>(name, pattern, target));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,12 +137,12 @@ public abstract class AbstractMatcher<E> implements Serializable {
|
||||
E config = null;
|
||||
|
||||
if (compiledPatterns.size() > 0) {
|
||||
log.debug("Attempting to match '{}' to a wildcard pattern, {} available", potentialMatch, compiledPatterns.size());
|
||||
LOG.debug("Attempting to match '{}' to a wildcard pattern, {} available", potentialMatch, compiledPatterns.size());
|
||||
|
||||
Map<String,String> vars = new LinkedHashMap<String,String>();
|
||||
Map<String,String> vars = new LinkedHashMap<>();
|
||||
for (Mapping<E> m : compiledPatterns) {
|
||||
if (wildcard.match(vars, potentialMatch, m.getPattern())) {
|
||||
log.debug("Value matches pattern '{}'", m.getOriginalPattern());
|
||||
LOG.debug("Value matches pattern '{}'", m.getOriginalPattern());
|
||||
config = convert(potentialMatch, m.getTarget(), vars);
|
||||
break;
|
||||
}
|
||||
@@ -152,20 +174,23 @@ public abstract class AbstractMatcher<E> implements Serializable {
|
||||
*/
|
||||
protected Map<String,String> replaceParameters(Map<String, String> orig, Map<String,String> vars) {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
|
||||
|
||||
//this will set the group index references, like {1}
|
||||
for (Map.Entry<String,String> entry : orig.entrySet()) {
|
||||
map.put(entry.getKey(), convertParam(entry.getValue(), vars));
|
||||
}
|
||||
|
||||
//the values map will contain entries like name->"Lex Luthor" and 1->"Lex Luthor"
|
||||
//now add the non-numeric values
|
||||
for (Map.Entry<String,String> entry: vars.entrySet()) {
|
||||
if (!NumberUtils.isCreatable(entry.getKey())) {
|
||||
map.put(entry.getKey(), entry.getValue());
|
||||
|
||||
if (appendNamedParameters) {
|
||||
LOG.debug("Appending named parameters to the result map");
|
||||
//the values map will contain entries like name->"Lex Luthor" and 1->"Lex Luthor"
|
||||
//now add the non-numeric values
|
||||
for (Map.Entry<String,String> entry: vars.entrySet()) {
|
||||
if (!NumberUtils.isCreatable(entry.getKey())) {
|
||||
map.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -192,7 +217,7 @@ public abstract class AbstractMatcher<E> implements Serializable {
|
||||
c = val.charAt(x);
|
||||
if (x < len - 2 &&
|
||||
c == '{' && '}' == val.charAt(x+2)) {
|
||||
varVal = (String)vars.get(String.valueOf(val.charAt(x + 1)));
|
||||
varVal = vars.get(String.valueOf(val.charAt(x + 1)));
|
||||
if (varVal != null) {
|
||||
ret.append(varVal);
|
||||
}
|
||||
@@ -213,18 +238,18 @@ public abstract class AbstractMatcher<E> implements Serializable {
|
||||
/**
|
||||
* <p> The original pattern. </p>
|
||||
*/
|
||||
private String original;
|
||||
private final String original;
|
||||
|
||||
|
||||
/**
|
||||
* <p> The compiled pattern. </p>
|
||||
*/
|
||||
private Object pattern;
|
||||
private final Object pattern;
|
||||
|
||||
/**
|
||||
* <p> The original object. </p>
|
||||
*/
|
||||
private E config;
|
||||
private final E config;
|
||||
|
||||
/**
|
||||
* <p> Contructs a read-only Mapping instance. </p>
|
||||
|
||||
@@ -58,7 +58,33 @@ public class ActionConfigMatcher extends AbstractMatcher<ActionConfig> implement
|
||||
public ActionConfigMatcher(PatternMatcher<?> patternMatcher,
|
||||
Map<String, ActionConfig> configs,
|
||||
boolean looseMatch) {
|
||||
super(patternMatcher);
|
||||
this(patternMatcher, configs, looseMatch, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Finds and precompiles the wildcard patterns from the ActionConfig
|
||||
* "path" attributes. ActionConfig's will be evaluated in the order they
|
||||
* exist in the config file. Only paths that actually contain a
|
||||
* wildcard will be compiled. </p>
|
||||
*
|
||||
* <p>Patterns can optionally be matched "loosely". When
|
||||
* the end of the pattern matches \*[^*]\*$ (wildcard, no wildcard,
|
||||
* wildcard), if the pattern fails, it is also matched as if the
|
||||
* last two characters didn't exist. The goal is to support the
|
||||
* legacy "*!*" syntax, where the "!*" is optional.</p>
|
||||
*
|
||||
* @param patternMatcher pattern matcher
|
||||
* @param configs An array of ActionConfig's to process
|
||||
* @param looseMatch To loosely match wildcards or not
|
||||
* @param appendNamedParameters To append named parameters or not
|
||||
*
|
||||
* @since 2.5.23
|
||||
* See WW-5065
|
||||
*/
|
||||
public ActionConfigMatcher(PatternMatcher<?> patternMatcher,
|
||||
Map<String, ActionConfig> configs,
|
||||
boolean looseMatch, boolean appendNamedParameters) {
|
||||
super(patternMatcher, appendNamedParameters);
|
||||
for (Map.Entry<String, ActionConfig> entry : configs.entrySet()) {
|
||||
addPattern(entry.getKey(), entry.getValue(), looseMatch);
|
||||
}
|
||||
|
||||
@@ -289,6 +289,8 @@ public class DefaultConfiguration implements Configuration {
|
||||
builder.constant(XWorkConstants.RELOAD_XML_CONFIGURATION, "false");
|
||||
builder.constant(StrutsConstants.STRUTS_I18N_RELOAD, "false");
|
||||
|
||||
builder.constant(StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS, "true");
|
||||
|
||||
return builder.create(true);
|
||||
}
|
||||
|
||||
@@ -338,8 +340,12 @@ public class DefaultConfiguration implements Configuration {
|
||||
}
|
||||
|
||||
PatternMatcher<int[]> matcher = container.getInstance(PatternMatcher.class);
|
||||
boolean appendNamedParameters = Boolean.parseBoolean(
|
||||
container.getInstance(String.class, StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS)
|
||||
);
|
||||
|
||||
return new RuntimeConfigurationImpl(Collections.unmodifiableMap(namespaceActionConfigs),
|
||||
Collections.unmodifiableMap(namespaceConfigs), matcher);
|
||||
Collections.unmodifiableMap(namespaceConfigs), matcher, appendNamedParameters);
|
||||
}
|
||||
|
||||
private void setDefaultResults(Map<String, ResultConfig> results, PackageConfig packageContext) {
|
||||
@@ -417,15 +423,18 @@ public class DefaultConfiguration implements Configuration {
|
||||
|
||||
public RuntimeConfigurationImpl(Map<String, Map<String, ActionConfig>> namespaceActionConfigs,
|
||||
Map<String, String> namespaceConfigs,
|
||||
PatternMatcher<int[]> matcher) {
|
||||
PatternMatcher<int[]> matcher,
|
||||
boolean appendNamedParameters)
|
||||
{
|
||||
this.namespaceActionConfigs = namespaceActionConfigs;
|
||||
this.namespaceConfigs = namespaceConfigs;
|
||||
|
||||
this.namespaceActionConfigMatchers = new LinkedHashMap<>();
|
||||
this.namespaceMatcher = new NamespaceMatcher(matcher, namespaceActionConfigs.keySet());
|
||||
this.namespaceMatcher = new NamespaceMatcher(matcher, namespaceActionConfigs.keySet(), appendNamedParameters);
|
||||
|
||||
for (Map.Entry<String, Map<String, ActionConfig>> entry : namespaceActionConfigs.entrySet()) {
|
||||
namespaceActionConfigMatchers.put(entry.getKey(), new ActionConfigMatcher(matcher, entry.getValue(), true));
|
||||
ActionConfigMatcher configMatcher = new ActionConfigMatcher(matcher, entry.getValue(), true, appendNamedParameters);
|
||||
namespaceActionConfigMatchers.put(entry.getKey(), configMatcher);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,23 @@ import java.util.Set;
|
||||
* @since 2.1
|
||||
*/
|
||||
public class NamespaceMatcher extends AbstractMatcher<NamespaceMatch> {
|
||||
public NamespaceMatcher(PatternMatcher<?> patternMatcher,
|
||||
Set<String> namespaces) {
|
||||
super(patternMatcher);
|
||||
|
||||
public NamespaceMatcher(PatternMatcher<?> patternMatcher, Set<String> namespaces) {
|
||||
this(patternMatcher, namespaces, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches namespace strings against a wildcard pattern matcher
|
||||
*
|
||||
* @param patternMatcher pattern matcher
|
||||
* @param namespaces A set of namespaces to process
|
||||
* @param appendNamedParameters To append named parameters or not
|
||||
*
|
||||
* @since 2.5.23
|
||||
* See WW-5065
|
||||
*/
|
||||
public NamespaceMatcher(PatternMatcher<?> patternMatcher, Set<String> namespaces, boolean appendNamedParameters) {
|
||||
super(patternMatcher, appendNamedParameters);
|
||||
for (String name : namespaces) {
|
||||
if (!patternMatcher.isLiteral(name)) {
|
||||
addPattern(name, new NamespaceMatch(name, null), false);
|
||||
|
||||
+6
@@ -33,6 +33,7 @@ import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.DefaultTextProvider;
|
||||
import com.opensymphony.xwork2.DefaultUnknownHandlerManager;
|
||||
import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.FileManager;
|
||||
import com.opensymphony.xwork2.FileManagerFactory;
|
||||
@@ -88,6 +89,7 @@ import com.opensymphony.xwork2.ognl.accessor.XWorkIteratorPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
|
||||
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.util.CompoundRoot;
|
||||
import com.opensymphony.xwork2.LocalizedTextProvider;
|
||||
import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
|
||||
@@ -108,6 +110,7 @@ import com.opensymphony.xwork2.validator.DefaultValidatorFactory;
|
||||
import com.opensymphony.xwork2.validator.DefaultValidatorFileParser;
|
||||
import com.opensymphony.xwork2.validator.ValidatorFactory;
|
||||
import com.opensymphony.xwork2.validator.ValidatorFileParser;
|
||||
import com.sun.org.apache.xpath.internal.operations.Bool;
|
||||
import ognl.MethodAccessor;
|
||||
import ognl.PropertyAccessor;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
@@ -214,6 +217,8 @@ public class XWorkConfigurationProvider implements ConfigurationProvider {
|
||||
|
||||
.factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class, Scope.PROTOTYPE)
|
||||
.factory(AcceptedPatternsChecker.class, DefaultAcceptedPatternsChecker.class, Scope.PROTOTYPE)
|
||||
.factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class
|
||||
, Scope.SINGLETON)
|
||||
|
||||
.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON)
|
||||
;
|
||||
@@ -227,6 +232,7 @@ public class XWorkConfigurationProvider implements ConfigurationProvider {
|
||||
props.setProperty(XWorkConstants.ENABLE_OGNL_EVAL_EXPRESSION, Boolean.FALSE.toString());
|
||||
props.setProperty(XWorkConstants.RELOAD_XML_CONFIGURATION, Boolean.FALSE.toString());
|
||||
props.setProperty(StrutsConstants.STRUTS_ALLOW_STATIC_METHOD_ACCESS, Boolean.FALSE.toString());
|
||||
props.setProperty(StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS, Boolean.TRUE.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+35
-14
@@ -93,12 +93,12 @@ public class XmlConfigurationProvider implements ConfigurationProvider {
|
||||
private String configFileName;
|
||||
private ObjectFactory objectFactory;
|
||||
|
||||
private Set<String> loadedFileUrls = new HashSet<>();
|
||||
private final Set<String> loadedFileUrls = new HashSet<>();
|
||||
private boolean errorIfMissing;
|
||||
private Map<String, String> dtdMappings;
|
||||
private Configuration configuration;
|
||||
private boolean throwExceptionOnDuplicateBeans = true;
|
||||
private Map<String, Element> declaredPackages = new HashMap<>();
|
||||
private final Map<String, Element> declaredPackages = new HashMap<>();
|
||||
|
||||
private FileManager fileManager;
|
||||
private ValueSubstitutor valueSubstitutor;
|
||||
@@ -874,15 +874,24 @@ public class XmlConfigurationProvider implements ConfigurationProvider {
|
||||
if (allowedMethodsEls.getLength() > 0) {
|
||||
// user defined 'allowed-methods' so used them whatever Strict DMI was enabled or not
|
||||
allowedMethods = new HashSet<>(packageContext.getGlobalAllowedMethods());
|
||||
|
||||
if (allowedMethodsEls.getLength() > 0) {
|
||||
Node n = allowedMethodsEls.item(0).getFirstChild();
|
||||
if (n != null) {
|
||||
String s = n.getNodeValue().trim();
|
||||
if (s.length() > 0) {
|
||||
allowedMethods.addAll(TextParseUtil.commaDelimitedStringToSet(s));
|
||||
// Fix for WW-5029 (concatenate all possible text node children)
|
||||
final Node allowedMethodsNode = allowedMethodsEls.item(0);
|
||||
if (allowedMethodsNode != null) {
|
||||
final NodeList allowedMethodsChildren = allowedMethodsNode.getChildNodes();
|
||||
final StringBuilder allowedMethodsSB = new StringBuilder();
|
||||
for (int i = 0; i < allowedMethodsChildren.getLength(); i++) {
|
||||
Node allowedMethodsChildNode = allowedMethodsChildren.item(i);
|
||||
if (allowedMethodsChildNode != null && allowedMethodsChildNode.getNodeType() == Node.TEXT_NODE) {
|
||||
String childNodeValue = allowedMethodsChildNode.getNodeValue();
|
||||
childNodeValue = (childNodeValue != null ? childNodeValue.trim() : "");
|
||||
if (childNodeValue.length() > 0) {
|
||||
allowedMethodsSB.append(childNodeValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (allowedMethodsSB.length() > 0) {
|
||||
allowedMethods.addAll(TextParseUtil.commaDelimitedStringToSet(allowedMethodsSB.toString()));
|
||||
}
|
||||
}
|
||||
} else if (packageContext.isStrictMethodInvocation()) {
|
||||
// user enabled Strict DMI but didn't defined action specific 'allowed-methods' so we use 'global-allowed-methods' only
|
||||
@@ -937,11 +946,23 @@ public class XmlConfigurationProvider implements ConfigurationProvider {
|
||||
|
||||
if (globalAllowedMethodsElms.getLength() > 0) {
|
||||
Set<String> globalAllowedMethods = new HashSet<>();
|
||||
Node n = globalAllowedMethodsElms.item(0).getFirstChild();
|
||||
if (n != null) {
|
||||
String s = n.getNodeValue().trim();
|
||||
if (s.length() > 0) {
|
||||
globalAllowedMethods = TextParseUtil.commaDelimitedStringToSet(s);
|
||||
// Fix for WW-5029 (concatenate all possible text node children)
|
||||
Node globaAllowedMethodsNode = globalAllowedMethodsElms.item(0);
|
||||
if (globaAllowedMethodsNode != null) {
|
||||
NodeList globaAllowedMethodsChildren = globaAllowedMethodsNode.getChildNodes();
|
||||
final StringBuilder globalAllowedMethodsSB = new StringBuilder();
|
||||
for (int i = 0; i < globaAllowedMethodsChildren.getLength(); i++) {
|
||||
Node globalAllowedMethodsChildNode = globaAllowedMethodsChildren.item(i);
|
||||
if (globalAllowedMethodsChildNode != null && globalAllowedMethodsChildNode.getNodeType() == Node.TEXT_NODE) {
|
||||
String childNodeValue = globalAllowedMethodsChildNode.getNodeValue();
|
||||
childNodeValue = (childNodeValue != null ? childNodeValue.trim() : "");
|
||||
if (childNodeValue.length() > 0) {
|
||||
globalAllowedMethodsSB.append(childNodeValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (globalAllowedMethodsSB.length() > 0) {
|
||||
globalAllowedMethods.addAll(TextParseUtil.commaDelimitedStringToSet(globalAllowedMethodsSB.toString()));
|
||||
}
|
||||
}
|
||||
packageContext.addGlobalAllowedMethods(globalAllowedMethods);
|
||||
|
||||
@@ -164,7 +164,8 @@ class ContainerImpl implements Container {
|
||||
public FieldInjector(ContainerImpl container, Field field, String name)
|
||||
throws MissingDependencyException {
|
||||
this.field = field;
|
||||
if (!field.isAccessible()) {
|
||||
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()))
|
||||
&& !field.isAccessible()) {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
try {
|
||||
if (sm != null) {
|
||||
@@ -256,7 +257,8 @@ class ContainerImpl implements Container {
|
||||
|
||||
public MethodInjector(ContainerImpl container, Method method, String name) throws MissingDependencyException {
|
||||
this.method = method;
|
||||
if (!method.isAccessible()) {
|
||||
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
|
||||
&& !method.isAccessible()) {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
try {
|
||||
if (sm != null) {
|
||||
@@ -306,7 +308,8 @@ class ContainerImpl implements Container {
|
||||
this.implementation = implementation;
|
||||
|
||||
constructor = findConstructorIn(implementation);
|
||||
if (!constructor.isAccessible()) {
|
||||
if ((!Modifier.isPublic(constructor.getModifiers()) || !Modifier.isPublic(constructor.getDeclaringClass().getModifiers()))
|
||||
&& !constructor.isAccessible()) {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
try {
|
||||
if (sm != null) {
|
||||
|
||||
@@ -44,15 +44,17 @@ public enum Scope {
|
||||
@Override
|
||||
<T> InternalFactory<? extends T> scopeFactory(Class<T> type, String name, final InternalFactory<? extends T> factory) {
|
||||
return new InternalFactory<T>() {
|
||||
T instance;
|
||||
volatile T instance;
|
||||
|
||||
public T create(InternalContext context) {
|
||||
synchronized (context.getContainer()) {
|
||||
if (instance == null) {
|
||||
instance = InitializableFactory.wrapIfNeeded(factory).create(context);
|
||||
if (instance == null) {
|
||||
synchronized (context.getContainer()) {
|
||||
if (instance == null) {
|
||||
instance = InitializableFactory.wrapIfNeeded(factory).create(context);
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,6 +23,8 @@ import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.XWorkConstants;
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.util.ClearableValueStack;
|
||||
import com.opensymphony.xwork2.util.Evaluated;
|
||||
import com.opensymphony.xwork2.LocalizedTextProvider;
|
||||
@@ -32,6 +34,7 @@ import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -99,6 +102,9 @@ public class AliasInterceptor extends AbstractInterceptor {
|
||||
protected LocalizedTextProvider localizedTextProvider;
|
||||
protected boolean devMode = false;
|
||||
|
||||
private ExcludedPatternsChecker excludedPatterns;
|
||||
private AcceptedPatternsChecker acceptedPatterns;
|
||||
|
||||
@Inject(XWorkConstants.DEV_MODE)
|
||||
public void setDevMode(String mode) {
|
||||
this.devMode = Boolean.parseBoolean(mode);
|
||||
@@ -114,6 +120,16 @@ public class AliasInterceptor extends AbstractInterceptor {
|
||||
this.localizedTextProvider = localizedTextProvider;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setExcludedPatterns(ExcludedPatternsChecker excludedPatterns) {
|
||||
this.excludedPatterns = excludedPatterns;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setAcceptedPatterns(AcceptedPatternsChecker acceptedPatterns) {
|
||||
this.acceptedPatterns = acceptedPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets the name of the action parameter to look for the alias map.
|
||||
@@ -144,7 +160,7 @@ public class AliasInterceptor extends AbstractInterceptor {
|
||||
ValueStack stack = ac.getValueStack();
|
||||
Object obj = stack.findValue(aliasExpression);
|
||||
|
||||
if (obj != null && obj instanceof Map) {
|
||||
if (obj instanceof Map) {
|
||||
//get secure stack
|
||||
ValueStack newStack = valueStackFactory.createValueStack(stack);
|
||||
boolean clearableStack = newStack instanceof ClearableValueStack;
|
||||
@@ -166,14 +182,23 @@ public class AliasInterceptor extends AbstractInterceptor {
|
||||
for (Object o : aliases.entrySet()) {
|
||||
Map.Entry entry = (Map.Entry) o;
|
||||
String name = entry.getKey().toString();
|
||||
if (isNotAcceptableExpression(name)) {
|
||||
continue;
|
||||
}
|
||||
String alias = (String) entry.getValue();
|
||||
if (isNotAcceptableExpression(alias)) {
|
||||
continue;
|
||||
}
|
||||
Evaluated value = new Evaluated(stack.findValue(name));
|
||||
if (!value.isDefined()) {
|
||||
// workaround
|
||||
HttpParameters contextParameters = ActionContext.getContext().getParameters();
|
||||
|
||||
if (null != contextParameters) {
|
||||
value = new Evaluated(contextParameters.get(name));
|
||||
Parameter param = contextParameters.get(name);
|
||||
if (param.isDefined()) {
|
||||
value = new Evaluated(param.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value.isDefined()) {
|
||||
@@ -202,5 +227,65 @@ public class AliasInterceptor extends AbstractInterceptor {
|
||||
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
|
||||
protected boolean isAccepted(String paramName) {
|
||||
AcceptedPatternsChecker.IsAccepted result = acceptedPatterns.isAccepted(paramName);
|
||||
if (result.isAccepted()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG.warn("Parameter [{}] didn't match accepted pattern [{}]! See Accepted / Excluded patterns at\n" +
|
||||
"https://struts.apache.org/security/#accepted--excluded-patterns",
|
||||
paramName, result.getAcceptedPattern());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isExcluded(String paramName) {
|
||||
ExcludedPatternsChecker.IsExcluded result = excludedPatterns.isExcluded(paramName);
|
||||
if (!result.isExcluded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG.warn("Parameter [{}] matches excluded pattern [{}]! See Accepted / Excluded patterns at\n" +
|
||||
"https://struts.apache.org/security/#accepted--excluded-patterns",
|
||||
paramName, result.getExcludedPattern());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if expression contains vulnerable code
|
||||
*
|
||||
* @param expression of interceptor
|
||||
* @return true|false
|
||||
*/
|
||||
protected boolean isNotAcceptableExpression(String expression) {
|
||||
return isExcluded(expression) || !isAccepted(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* parameters that are allowed in the parameter map (aka whitelist).
|
||||
* <p>
|
||||
* Don't change the default unless you know what you are doing in terms
|
||||
* of security implications.
|
||||
* </p>
|
||||
*
|
||||
* @param commaDelim A comma-delimited list of regular expressions
|
||||
*/
|
||||
public void setAcceptParamNames(String commaDelim) {
|
||||
acceptedPatterns.setAcceptedPatterns(commaDelim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* parameters that should be removed from the parameter map.
|
||||
*
|
||||
* @param commaDelim A comma-delimited list of regular expressions
|
||||
*/
|
||||
public void setExcludeParams(String commaDelim) {
|
||||
excludedPatterns.setExcludedPatterns(commaDelim);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,13 +25,16 @@ import com.opensymphony.xwork2.XWorkConstants;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.util.*;
|
||||
import com.opensymphony.xwork2.util.ClearableValueStack;
|
||||
import com.opensymphony.xwork2.util.MemberAccessValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
@@ -100,8 +103,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
*/
|
||||
static final Comparator<String> rbCollator = new Comparator<String>() {
|
||||
public int compare(String s1, String s2) {
|
||||
int l1 = countOGNLCharacters(s1),
|
||||
l2 = countOGNLCharacters(s2);
|
||||
int l1 = countOGNLCharacters(s1);
|
||||
int l2 = countOGNLCharacters(s2);
|
||||
return l1 < l2 ? -1 : (l2 < l1 ? 1 : s1.compareTo(s2));
|
||||
}
|
||||
|
||||
@@ -185,7 +188,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
if (clearableStack) {
|
||||
//if the stack's context can be cleared, do that to prevent OGNL
|
||||
//from having access to objects in the stack, see XW-641
|
||||
((ClearableValueStack)newStack).clearContextValues();
|
||||
((ClearableValueStack) newStack).clearContextValues();
|
||||
Map<String, Object> context = newStack.getContext();
|
||||
ReflectionContextState.setCreatingNullObjects(context, true);
|
||||
ReflectionContextState.setDenyMethodExecution(context, true);
|
||||
@@ -228,7 +231,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
TextProvider tp = (TextProvider) action;
|
||||
developerNotification = tp.getText("devmode.notification",
|
||||
"Developer Notification:\n{0}",
|
||||
new String[]{ developerNotification }
|
||||
new String[]{developerNotification}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -245,7 +248,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
/**
|
||||
* Checks if name of parameter can be accepted or thrown away
|
||||
*
|
||||
* @param name parameter name
|
||||
* @param name parameter name
|
||||
* @param action current action
|
||||
* @return true if parameter is accepted
|
||||
*/
|
||||
@@ -289,27 +292,45 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
return accepted;
|
||||
}
|
||||
|
||||
protected boolean isWithinLengthLimit( String name ) {
|
||||
protected boolean isWithinLengthLimit(String name) {
|
||||
boolean matchLength = name.length() <= paramNameMaxLength;
|
||||
if (!matchLength) {
|
||||
LOG.debug("Parameter [{}] is too long, allowed length is [{}]", name, String.valueOf(paramNameMaxLength));
|
||||
if (devMode) { // warn only when in devMode
|
||||
LOG.warn("Parameter [{}] is too long, allowed length is [{}]. Use Interceptor Parameter Overriding " +
|
||||
"to override the limit, see more at\n" +
|
||||
"https://struts.apache.org/core-developers/interceptors.html#interceptor-parameter-overriding",
|
||||
name, paramNameMaxLength);
|
||||
} else {
|
||||
LOG.warn("Parameter [{}] is too long, allowed length is [{}]", name, paramNameMaxLength);
|
||||
}
|
||||
}
|
||||
return matchLength;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isAccepted(String paramName) {
|
||||
AcceptedPatternsChecker.IsAccepted result = acceptedPatterns.isAccepted(paramName);
|
||||
if (result.isAccepted()) {
|
||||
return true;
|
||||
} else if (devMode) { // warn only when in devMode
|
||||
LOG.warn("Parameter [{}] didn't match accepted pattern [{}]! See Accepted / Excluded patterns at\n" +
|
||||
"https://struts.apache.org/security/#accepted--excluded-patterns",
|
||||
paramName, result.getAcceptedPattern());
|
||||
} else {
|
||||
LOG.debug("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
|
||||
}
|
||||
LOG.debug("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isExcluded(String paramName) {
|
||||
ExcludedPatternsChecker.IsExcluded result = excludedPatterns.isExcluded(paramName);
|
||||
if (result.isExcluded()) {
|
||||
LOG.debug("Parameter [{}] matches excluded pattern [{}]!", paramName, result.getExcludedPattern());
|
||||
if (devMode) { // warn only when in devMode
|
||||
LOG.warn("Parameter [{}] matches excluded pattern [{}]! See Accepted / Excluded patterns at\n" +
|
||||
"https://struts.apache.org/security/#accepted--excluded-patterns",
|
||||
paramName, result.getExcludedPattern());
|
||||
} else {
|
||||
LOG.debug("Parameter [{}] matches excluded pattern [{}]!", paramName, result.getExcludedPattern());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
+5
-13
@@ -227,21 +227,13 @@ public class StaticParametersInterceptor extends AbstractInterceptor {
|
||||
protected void addParametersToContext(ActionContext ac, Map<String, ?> newParams) {
|
||||
HttpParameters previousParams = ac.getParameters();
|
||||
|
||||
HttpParameters.Builder combinedParams = HttpParameters.create();
|
||||
HttpParameters.Builder combinedParams;
|
||||
if (overwrite) {
|
||||
if (previousParams != null) {
|
||||
combinedParams = combinedParams.withParent(previousParams);
|
||||
}
|
||||
if (newParams != null) {
|
||||
combinedParams = combinedParams.withExtraParams(newParams);
|
||||
}
|
||||
combinedParams = HttpParameters.create().withParent( previousParams);
|
||||
combinedParams = combinedParams.withExtraParams(newParams);
|
||||
} else {
|
||||
if (newParams != null) {
|
||||
combinedParams = combinedParams.withExtraParams(newParams);
|
||||
}
|
||||
if (previousParams != null) {
|
||||
combinedParams = combinedParams.withParent(previousParams);
|
||||
}
|
||||
combinedParams = HttpParameters.create(newParams);
|
||||
combinedParams = combinedParams.withExtraParams(previousParams);
|
||||
}
|
||||
ac.setParameters(combinedParams.build());
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ public class MockResult implements Result {
|
||||
|
||||
public static final String DEFAULT_PARAM = "foo";
|
||||
|
||||
private ActionInvocation invocation;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
@@ -41,7 +43,7 @@ public class MockResult implements Result {
|
||||
}
|
||||
|
||||
public void execute(ActionInvocation invocation) throws Exception {
|
||||
// no op
|
||||
this.invocation = invocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,4 +55,7 @@ public class MockResult implements Result {
|
||||
// no op
|
||||
}
|
||||
|
||||
public ActionInvocation getInvocation() {
|
||||
return invocation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public class OgnlUtil {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(OgnlUtil.class);
|
||||
|
||||
private ConcurrentMap<String, Object> expressions = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<String, Object> expressions = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<Class, BeanInfo> beanInfoCache = new ConcurrentHashMap<>();
|
||||
private TypeConverter defaultConverter;
|
||||
|
||||
@@ -74,6 +74,9 @@ public class OgnlUtil {
|
||||
excludedClasses = new HashSet<>();
|
||||
excludedPackageNamePatterns = new HashSet<>();
|
||||
excludedPackageNames = new HashSet<>();
|
||||
excludedClasses = Collections.unmodifiableSet(excludedClasses);
|
||||
excludedPackageNamePatterns = Collections.unmodifiableSet(excludedPackageNamePatterns);
|
||||
excludedPackageNames = Collections.unmodifiableSet(excludedPackageNames);
|
||||
}
|
||||
|
||||
@Inject
|
||||
@@ -181,10 +184,98 @@ public class OgnlUtil {
|
||||
this.disallowProxyMemberAccess = Boolean.parseBoolean(disallowProxyMemberAccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxLength Injects the Struts OGNL maximum expression length.
|
||||
*/
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_MAX_LENGTH, required = false)
|
||||
protected void applyExpressionMaxLength(String maxLength) {
|
||||
try {
|
||||
if (maxLength == null || maxLength.isEmpty()) {
|
||||
// user is going to disable this functionality
|
||||
Ognl.applyExpressionMaxLength(null);
|
||||
} else {
|
||||
Ognl.applyExpressionMaxLength(Integer.parseInt(maxLength));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Unable to set OGNL Expression Max Length {}.", maxLength); // Help configuration debugging.
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDisallowProxyMemberAccess() {
|
||||
return disallowProxyMemberAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience mechanism to clear the OGNL Runtime Cache via OgnlUtil. May be utilized
|
||||
* by applications that generate many unique OGNL expressions over time.
|
||||
*
|
||||
* Note: This call affects the global OGNL cache, see ({@link ognl.OgnlRuntime#clearCache()} for details.
|
||||
*
|
||||
* Warning: Frequent calling if this method may negatively impact performance, but may be required
|
||||
* to avoid memory exhaustion (resource leak) with too many OGNL expressions being cached.
|
||||
*
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public static void clearRuntimeCache() {
|
||||
OgnlRuntime.clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a mechanism to clear the OGNL expression cache. May be utilized by applications
|
||||
* that generate many unique OGNL expressions over time.
|
||||
*
|
||||
* Note: This call affects the current OgnlUtil instance. For Struts this is often a Singleton
|
||||
* instance so it can be "effectively global".
|
||||
*
|
||||
* Warning: Frequent calling if this method may negatively impact performance, but may be required
|
||||
* to avoid memory exhaustion (resource leak) with too many OGNL expressions being cached.
|
||||
*
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public void clearExpressionCache() {
|
||||
expressions.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the size of the expression cache (current number of elements).
|
||||
*
|
||||
* @return current number of elements in the expression cache.
|
||||
*
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public int expressionCacheSize() {
|
||||
return expressions.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a mechanism to clear the BeanInfo cache. May be utilized by applications
|
||||
* that request BeanInfo and/or PropertyDescriptors for many unique classes or objects over time
|
||||
* (especially dynamic objects).
|
||||
*
|
||||
* Note: This call affects the current OgnlUtil instance. For Struts this is often a Singleton
|
||||
* instance so it can be "effectively global".
|
||||
*
|
||||
* Warning: Frequent calling if this method may negatively impact performance, but may be required
|
||||
* to avoid memory exhaustion (resource leak) with too many BeanInfo elements being cached.
|
||||
*
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public void clearBeanInfoCache() {
|
||||
beanInfoCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the size of the BeanInfo cache (current number of elements).
|
||||
*
|
||||
* @return current number of elements in the BeanInfo cache.
|
||||
*
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public int beanInfoCacheSize() {
|
||||
return beanInfoCache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object's properties using the default type converter, defaulting to not throw
|
||||
* exceptions for problems setting the properties.
|
||||
@@ -682,6 +773,9 @@ public class OgnlUtil {
|
||||
setValue(name, context, o, value);
|
||||
} catch (OgnlException e) {
|
||||
Throwable reason = e.getReason();
|
||||
if (reason instanceof SecurityException) {
|
||||
LOG.warn("Could not evaluate this expression due to security constraints: [{}]", name, e);
|
||||
}
|
||||
String msg = "Caught OgnlException while setting property '" + name + "' on type '" + o.getClass().getName() + "'.";
|
||||
Throwable exception = (reason == null) ? e : reason;
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
|
||||
private void trySetValue(String expr, Object value, boolean throwExceptionOnFailure, Map<String, Object> context) throws OgnlException {
|
||||
context.put(XWorkConverter.CONVERSION_PROPERTY_FULLNAME, expr);
|
||||
context.put(REPORT_ERRORS_ON_NO_PROP, (throwExceptionOnFailure) ? Boolean.TRUE : Boolean.FALSE);
|
||||
context.put(REPORT_ERRORS_ON_NO_PROP, throwExceptionOnFailure || logMissingProperties ? Boolean.TRUE : Boolean.FALSE);
|
||||
ognlUtil.setValue(expr, context, root, value);
|
||||
}
|
||||
|
||||
@@ -204,6 +204,9 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
}
|
||||
|
||||
protected void handleOgnlException(String expr, Object value, boolean throwExceptionOnFailure, OgnlException e) {
|
||||
if (e != null && e.getReason() instanceof SecurityException) {
|
||||
LOG.error("Could not evaluate this expression due to security constraints: [{}]", expr, e);
|
||||
}
|
||||
boolean shouldLog = shouldLogMissingPropertyWarning(e);
|
||||
String msg = null;
|
||||
if (throwExceptionOnFailure || shouldLog) {
|
||||
@@ -246,7 +249,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
}
|
||||
|
||||
protected void setupExceptionOnFailure(boolean throwExceptionOnFailure) {
|
||||
if (throwExceptionOnFailure) {
|
||||
if (throwExceptionOnFailure || logMissingProperties) {
|
||||
context.put(THROW_EXCEPTION_ON_FAILURE, true);
|
||||
}
|
||||
}
|
||||
@@ -326,7 +329,12 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
}
|
||||
|
||||
protected Object handleOgnlException(String expr, boolean throwExceptionOnFailure, OgnlException e) {
|
||||
Object ret = findInContext(expr);
|
||||
Object ret = null;
|
||||
if (e != null && e.getReason() instanceof SecurityException) {
|
||||
LOG.error("Could not evaluate this expression due to security constraints: [{}]", expr, e);
|
||||
} else {
|
||||
ret = findInContext(expr);
|
||||
}
|
||||
if (ret == null) {
|
||||
if (shouldLogMissingPropertyWarning(e)) {
|
||||
LOG.warn("Could not find property [{}]!", expr, e);
|
||||
@@ -339,8 +347,9 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
}
|
||||
|
||||
protected boolean shouldLogMissingPropertyWarning(OgnlException e) {
|
||||
return (e instanceof NoSuchPropertyException || e instanceof MethodFailedException)
|
||||
&& devMode && logMissingProperties;
|
||||
return (e instanceof NoSuchPropertyException ||
|
||||
(e instanceof MethodFailedException && e.getReason() instanceof NoSuchMethodException))
|
||||
&& logMissingProperties;
|
||||
}
|
||||
|
||||
private Object tryFindValue(String expr, Class asType) throws OgnlException {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package com.opensymphony.xwork2.ognl;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.XWorkConstants;
|
||||
import com.opensymphony.xwork2.TextProvider;
|
||||
import com.opensymphony.xwork2.conversion.NullHandler;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
@@ -61,7 +62,7 @@ public class OgnlValueStackFactory implements ValueStackFactory {
|
||||
this.textProvider = textProvider;
|
||||
}
|
||||
|
||||
@Inject(value="allowStaticMethodAccess", required=false)
|
||||
@Inject(value = XWorkConstants.ALLOW_STATIC_METHOD_ACCESS, required = false)
|
||||
protected void setAllowStaticMethodAccess(String allowStaticMethodAccess) {
|
||||
this.allowStaticMethodAccess = BooleanUtils.toBoolean(allowStaticMethodAccess);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class SecurityMemberAccess extends DefaultMemberAccess {
|
||||
/**
|
||||
* SecurityMemberAccess
|
||||
* - access decisions based on whether member is static (or not)
|
||||
* - block or allow access to properties (configureable-after-construction)
|
||||
* - block or allow access to properties (configurable-after-construction)
|
||||
*
|
||||
* @param allowStaticMethodAccess
|
||||
*/
|
||||
|
||||
+16
-12
@@ -217,13 +217,14 @@ public class CompoundRootAccessor implements PropertyAccessor, MethodAccessor, C
|
||||
return null;
|
||||
}
|
||||
|
||||
Throwable reason = null;
|
||||
Class[] argTypes = getArgTypes(objects);
|
||||
for (Object o : root) {
|
||||
if (o == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Class clazz = o.getClass();
|
||||
Class[] argTypes = getArgTypes(objects);
|
||||
|
||||
MethodCall mc = null;
|
||||
|
||||
@@ -233,24 +234,27 @@ public class CompoundRootAccessor implements PropertyAccessor, MethodAccessor, C
|
||||
|
||||
if ((argTypes == null) || !invalidMethods.containsKey(mc)) {
|
||||
try {
|
||||
Object value = OgnlRuntime.callMethod((OgnlContext) context, o, name, objects);
|
||||
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
return OgnlRuntime.callMethod((OgnlContext) context, o, name, objects);
|
||||
} catch (OgnlException e) {
|
||||
// try the next one
|
||||
Throwable reason = e.getReason();
|
||||
reason = e.getReason();
|
||||
|
||||
if (!context.containsKey(OgnlValueStack.THROW_EXCEPTION_ON_FAILURE) && (mc != null) && (reason != null) && (reason.getClass() == NoSuchMethodException.class)) {
|
||||
invalidMethods.put(mc, Boolean.TRUE);
|
||||
} else if (reason != null) {
|
||||
throw new MethodFailedException(o, name, e.getReason());
|
||||
if (reason != null && !(reason instanceof NoSuchMethodException)) {
|
||||
// method has found but thrown an exception
|
||||
break;
|
||||
}
|
||||
|
||||
if ((mc != null) && (reason != null)) {
|
||||
invalidMethods.put(mc, Boolean.TRUE);
|
||||
}
|
||||
// continue and try the next one
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (context.containsKey(OgnlValueStack.THROW_EXCEPTION_ON_FAILURE)) {
|
||||
throw new MethodFailedException(target, name, reason);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -115,6 +115,11 @@ public class XWorkListPropertyAccessor extends ListPropertyAccessor {
|
||||
if (listSize <= index) {
|
||||
Object result;
|
||||
|
||||
if (index > autoGrowCollectionLimit) {
|
||||
throw new OgnlException("Error auto growing collection size to " + index + " which limited to "
|
||||
+ autoGrowCollectionLimit);
|
||||
}
|
||||
|
||||
for (int i = listSize; i < index; i++) {
|
||||
list.add(null);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Used across different interceptors to check if given string matches one of the excluded patterns.
|
||||
* Used across different interceptors to check if given string matches one of the accepted patterns.
|
||||
*/
|
||||
public interface AcceptedPatternsChecker {
|
||||
|
||||
|
||||
+45
-9
@@ -21,10 +21,13 @@ package com.opensymphony.xwork2.security;
|
||||
import com.opensymphony.xwork2.XWorkConstants;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -34,7 +37,11 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
|
||||
private static final Logger LOG = LogManager.getLogger(DefaultAcceptedPatternsChecker.class);
|
||||
|
||||
public static final String[] ACCEPTED_PATTERNS = {
|
||||
"\\w+((\\.\\w+)|(\\[\\d+\\])|(\\(\\d+\\))|(\\['(\\w|[\\u4e00-\\u9fa5])+'\\])|(\\('(\\w|[\\u4e00-\\u9fa5])+'\\)))*"
|
||||
"\\w+((\\.\\w+)|(\\[\\d+])|(\\(\\d+\\))|(\\['(\\w|[\\u4e00-\\u9fa5])+'])|(\\('(\\w|[\\u4e00-\\u9fa5])+'\\)))*"
|
||||
};
|
||||
|
||||
public static final String[] DMI_AWARE_ACCEPTED_PATTERNS = {
|
||||
"\\w+([:]?\\w+)?((\\.\\w+)|(\\[\\d+])|(\\(\\d+\\))|(\\['(\\w|[\\u4e00-\\u9fa5])+'])|(\\('(\\w|[\\u4e00-\\u9fa5])+'\\)))*([!]?\\w+)?"
|
||||
};
|
||||
|
||||
private Set<Pattern> acceptedPatterns;
|
||||
@@ -43,46 +50,74 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
|
||||
setAcceptedPatterns(ACCEPTED_PATTERNS);
|
||||
}
|
||||
|
||||
public DefaultAcceptedPatternsChecker(
|
||||
@Inject(value = StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, required = false) String dmiValue
|
||||
) {
|
||||
if (BooleanUtils.toBoolean(dmiValue)) {
|
||||
LOG.debug("DMI is enabled, adding DMI related accepted patterns");
|
||||
setAcceptedPatterns(DMI_AWARE_ACCEPTED_PATTERNS);
|
||||
} else {
|
||||
setAcceptedPatterns(ACCEPTED_PATTERNS);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.OVERRIDE_ACCEPTED_PATTERNS, required = false)
|
||||
protected void setOverrideAcceptedPatterns(String acceptablePatterns) {
|
||||
LOG.warn("Overriding accepted patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
|
||||
acceptedPatterns, acceptablePatterns);
|
||||
acceptedPatterns, acceptablePatterns);
|
||||
acceptedPatterns = new HashSet<>();
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(acceptablePatterns)) {
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
try {
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(acceptablePatterns)) {
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
acceptedPatterns = Collections.unmodifiableSet(acceptedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.ADDITIONAL_ACCEPTED_PATTERNS, required = false)
|
||||
protected void setAdditionalAcceptedPatterns(String acceptablePatterns) {
|
||||
LOG.warn("Adding additional global patterns [{}] to accepted patterns!", acceptablePatterns);
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(acceptablePatterns)) {
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
acceptedPatterns = new HashSet<>(acceptedPatterns); // Make mutable before adding
|
||||
try {
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(acceptablePatterns)) {
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
acceptedPatterns = Collections.unmodifiableSet(acceptedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAcceptedPatterns(String commaDelimitedPatterns) {
|
||||
setAcceptedPatterns(TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAcceptedPatterns(String[] additionalPatterns) {
|
||||
setAcceptedPatterns(new HashSet<>(Arrays.asList(additionalPatterns)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAcceptedPatterns(Set<String> patterns) {
|
||||
if (acceptedPatterns == null) {
|
||||
// Limit unwanted log entries (for 1st call, acceptedPatterns null)
|
||||
LOG.debug("Sets accepted patterns to [{}], note this impacts the safety of your application!", patterns);
|
||||
} else {
|
||||
LOG.warn("Replacing accepted patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
|
||||
acceptedPatterns, patterns);
|
||||
acceptedPatterns, patterns);
|
||||
}
|
||||
acceptedPatterns = new HashSet<>(patterns.size());
|
||||
for (String pattern : patterns) {
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
try {
|
||||
for (String pattern : patterns) {
|
||||
acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
acceptedPatterns = Collections.unmodifiableSet(acceptedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsAccepted isAccepted(String value) {
|
||||
for (Pattern acceptedPattern : acceptedPatterns) {
|
||||
if (acceptedPattern.matcher(value).matches()) {
|
||||
@@ -93,6 +128,7 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
|
||||
return IsAccepted.no(acceptedPatterns.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Pattern> getAcceptedPatterns() {
|
||||
return acceptedPatterns;
|
||||
}
|
||||
|
||||
+27
-8
@@ -27,6 +27,7 @@ import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -55,36 +56,48 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
|
||||
// Limit unwanted log entries (when excludedPatterns null/empty - usually 1st call)
|
||||
LOG.debug("Overriding excluded patterns with [{}]", excludePatterns);
|
||||
}
|
||||
excludedPatterns = new HashSet<Pattern>();
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(excludePatterns)) {
|
||||
excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
excludedPatterns = new HashSet<>();
|
||||
try {
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(excludePatterns)) {
|
||||
excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
excludedPatterns = Collections.unmodifiableSet(excludedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(value = XWorkConstants.ADDITIONAL_EXCLUDED_PATTERNS, required = false)
|
||||
public void setAdditionalExcludePatterns(String excludePatterns) {
|
||||
LOG.debug("Adding additional global patterns [{}] to excluded patterns!", excludePatterns);
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(excludePatterns)) {
|
||||
excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
excludedPatterns = new HashSet<>(excludedPatterns); // Make mutable before adding
|
||||
try {
|
||||
for (String pattern : TextParseUtil.commaDelimitedStringToSet(excludePatterns)) {
|
||||
excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
excludedPatterns = Collections.unmodifiableSet(excludedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION)
|
||||
protected void setDynamicMethodInvocation(String dmiValue) {
|
||||
if (BooleanUtils.toBoolean(dmiValue) == false) {
|
||||
if (!BooleanUtils.toBoolean(dmiValue)) {
|
||||
LOG.debug("DMI is disabled, adding DMI related excluded patterns");
|
||||
setAdditionalExcludePatterns("^(action|method):.*");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExcludedPatterns(String commaDelimitedPatterns) {
|
||||
setExcludedPatterns(TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExcludedPatterns(String[] patterns) {
|
||||
setExcludedPatterns(new HashSet<>(Arrays.asList(patterns)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExcludedPatterns(Set<String> patterns) {
|
||||
if (excludedPatterns != null && excludedPatterns.size() > 0) {
|
||||
LOG.warn("Replacing excluded patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
|
||||
@@ -94,11 +107,16 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
|
||||
LOG.debug("Sets excluded patterns to [{}]", patterns);
|
||||
}
|
||||
excludedPatterns = new HashSet<>(patterns.size());
|
||||
for (String pattern : patterns) {
|
||||
excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
try {
|
||||
for (String pattern : patterns) {
|
||||
excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
excludedPatterns = Collections.unmodifiableSet(excludedPatterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsExcluded isExcluded(String value) {
|
||||
for (Pattern excludedPattern : excludedPatterns) {
|
||||
if (excludedPattern.matcher(value).matches()) {
|
||||
@@ -109,6 +127,7 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
|
||||
return IsExcluded.no(excludedPatterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Pattern> getExcludedPatterns() {
|
||||
return excludedPatterns;
|
||||
}
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package com.opensymphony.xwork2.security;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DefaultNotExcludedAcceptedPatternsChecker implements NotExcludedAcceptedPatternsChecker {
|
||||
private ExcludedPatternsChecker excludedPatterns;
|
||||
private AcceptedPatternsChecker acceptedPatterns;
|
||||
|
||||
|
||||
@Inject
|
||||
public void setExcludedPatterns(ExcludedPatternsChecker excludedPatterns) {
|
||||
this.excludedPatterns = excludedPatterns;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setAcceptedPatterns(AcceptedPatternsChecker acceptedPatterns) {
|
||||
this.acceptedPatterns = acceptedPatterns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsAllowed isAllowed(String value) {
|
||||
IsExcluded isExcluded = isExcluded(value);
|
||||
if (isExcluded.isExcluded()) {
|
||||
return IsAllowed.no(isExcluded.getExcludedPattern());
|
||||
}
|
||||
|
||||
IsAccepted isAccepted = isAccepted(value);
|
||||
if (!isAccepted.isAccepted()) {
|
||||
return IsAllowed.no(isAccepted.getAcceptedPattern());
|
||||
}
|
||||
|
||||
return IsAllowed.yes(isAccepted.getAcceptedPattern());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsAccepted isAccepted(String value) {
|
||||
return acceptedPatterns.isAccepted(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAcceptedPatterns(String commaDelimitedPatterns) {
|
||||
acceptedPatterns.setAcceptedPatterns(commaDelimitedPatterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAcceptedPatterns(String[] patterns) {
|
||||
acceptedPatterns.setAcceptedPatterns(patterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAcceptedPatterns(Set<String> patterns) {
|
||||
acceptedPatterns.setAcceptedPatterns(patterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Pattern> getAcceptedPatterns() {
|
||||
return acceptedPatterns.getAcceptedPatterns();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsExcluded isExcluded(String value) {
|
||||
return excludedPatterns.isExcluded(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExcludedPatterns(String commaDelimitedPatterns) {
|
||||
excludedPatterns.setExcludedPatterns(commaDelimitedPatterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExcludedPatterns(String[] patterns) {
|
||||
excludedPatterns.setExcludedPatterns(patterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExcludedPatterns(Set<String> patterns) {
|
||||
excludedPatterns.setExcludedPatterns(patterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Pattern> getExcludedPatterns() {
|
||||
return excludedPatterns.getExcludedPatterns();
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package com.opensymphony.xwork2.security;
|
||||
|
||||
/**
|
||||
* Used across different places to check if given string is not excluded and is accepted
|
||||
* @see <a href="https://securitylab.github.com/research/apache-struts-double-evaluation/">here</a>
|
||||
* @since 2.5.27
|
||||
*/
|
||||
public interface NotExcludedAcceptedPatternsChecker extends ExcludedPatternsChecker, AcceptedPatternsChecker {
|
||||
|
||||
/**
|
||||
* Checks if value doesn't match excluded pattern and matches accepted pattern
|
||||
*
|
||||
* @param value to check
|
||||
* @return object containing result of matched pattern and pattern itself
|
||||
*/
|
||||
IsAllowed isAllowed(String value);
|
||||
|
||||
final class IsAllowed {
|
||||
|
||||
private final boolean allowed;
|
||||
private final String allowedPattern;
|
||||
|
||||
public static IsAllowed yes(String allowedPattern) {
|
||||
return new IsAllowed(true, allowedPattern);
|
||||
}
|
||||
|
||||
public static IsAllowed no(String allowedPattern) {
|
||||
return new IsAllowed(false, allowedPattern);
|
||||
}
|
||||
|
||||
private IsAllowed(boolean allowed, String allowedPattern) {
|
||||
this.allowed = allowed;
|
||||
this.allowedPattern = allowedPattern;
|
||||
}
|
||||
|
||||
public boolean isAllowed() {
|
||||
return allowed;
|
||||
}
|
||||
|
||||
public String getAllowedPattern() {
|
||||
return allowedPattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IsAllowed { " +
|
||||
"allowed=" + allowed +
|
||||
", allowedPattern=" + allowedPattern +
|
||||
" }";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,8 +31,6 @@ import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -53,7 +51,7 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
public static final String STRUTS_MESSAGES_BUNDLE = "org/apache/struts2/struts-messages";
|
||||
|
||||
private static final String TOMCAT_RESOURCE_ENTRIES_FIELD = "resourceEntries";
|
||||
private final String RELOADED = "com.opensymphony.xwork2.util.LocalizedTextProvider.reloaded";
|
||||
private static final String RELOADED = "com.opensymphony.xwork2.util.LocalizedTextProvider.reloaded";
|
||||
|
||||
protected final ConcurrentMap<String, ResourceBundle> bundlesMap = new ConcurrentHashMap<>();
|
||||
protected boolean devMode = false;
|
||||
@@ -61,7 +59,7 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
|
||||
private final ConcurrentMap<MessageFormatKey, MessageFormat> messageFormats = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<Integer, List<String>> classLoaderMap = new ConcurrentHashMap<>();
|
||||
private final Set<String> missingBundles = Collections.synchronizedSet(new HashSet<String>());
|
||||
private final ConcurrentMap<String, Boolean> missingBundles = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<Integer, ClassLoader> delegatedClassLoaderMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
@@ -225,10 +223,45 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bundleName Removes the bundle from any cached "misses"
|
||||
* Clear a specific bundle from the <code>bundlesMap</code>
|
||||
*
|
||||
* Warning: This method <b>may be ineffective</b> due to the way the <code>bundlesMap</code>
|
||||
* is used in combination with locale. Descendants should use the method
|
||||
* {@link #clearBundle(java.lang.String, java.util.Locale)} instead.
|
||||
*
|
||||
* @param bundleName The bundle to remove from the bundle map
|
||||
*/
|
||||
public void clearBundle(final String bundleName) {
|
||||
bundlesMap.remove(getCurrentThreadContextClassLoader().hashCode() + bundleName);
|
||||
final ResourceBundle removedBundle = bundlesMap.remove(getCurrentThreadContextClassLoader().hashCode() + bundleName);
|
||||
LOG.debug("Clearing resource bundle [{}], result: [{}].", bundleName, Boolean.valueOf(removedBundle != null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a specific bundle + locale combination from the <code>bundlesMap</code>.
|
||||
* Intended for descendants to use clear a bundle + locale combination.
|
||||
*
|
||||
* @param bundleName The bundle (combined with locale) to remove from the bundle map
|
||||
* @param locale Provides the locale to combine with the bundle to get the key
|
||||
*/
|
||||
protected void clearBundle(final String bundleName, Locale locale) {
|
||||
final String key = createMissesKey(String.valueOf(getCurrentThreadContextClassLoader().hashCode()), bundleName, locale);
|
||||
final ResourceBundle removedBundle = bundlesMap.remove(key);
|
||||
LOG.debug("Clearing resource bundle [{}], locale [{}], result: [{}].", bundleName, locale, Boolean.valueOf(removedBundle != null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the <code>missingBundles</code> contents. This allows descendants to
|
||||
* clear the <b>"missing bundles cache"</b> when desired (or needed).
|
||||
*
|
||||
* Note: This method may be used when the <code>bundlesMap</code> state has changed
|
||||
* in such a way that bundles that were previously "missing" may now be available
|
||||
* (e.g. after calling {@link #addDefaultResourceBundle(java.lang.String)} when the
|
||||
* {@link AbstractLocalizedTextProvider} has already been used for failed bundle
|
||||
* lookups of a given key, or some transitory state made a bundle lookup fail.
|
||||
*/
|
||||
protected void clearMissingBundlesCache() {
|
||||
missingBundles.clear();
|
||||
LOG.debug("Cleared the missing bundles cache.");
|
||||
}
|
||||
|
||||
protected void reloadBundles() {
|
||||
@@ -355,7 +388,7 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
ClassLoader classLoader = getCurrentThreadContextClassLoader();
|
||||
String key = createMissesKey(String.valueOf(classLoader.hashCode()), aBundleName, locale);
|
||||
|
||||
if (missingBundles.contains(key)) {
|
||||
if (missingBundles.containsKey(key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -378,11 +411,11 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
}
|
||||
} catch (MissingResourceException e) {
|
||||
LOG.debug("Missing resource bundle [{}]!", aBundleName, e);
|
||||
missingBundles.add(key);
|
||||
missingBundles.putIfAbsent(key, Boolean.TRUE);
|
||||
}
|
||||
} else {
|
||||
LOG.debug("Missing resource bundle [{}]!", aBundleName);
|
||||
missingBundles.add(key);
|
||||
missingBundles.putIfAbsent(key, Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
return bundle;
|
||||
|
||||
@@ -82,13 +82,14 @@ public class ProxyUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given member is a proxy member of a proxy object.
|
||||
* Check whether the given member is a proxy member of a proxy object or is a static proxy member.
|
||||
* @param member the member to check
|
||||
* @param object the object to check
|
||||
*/
|
||||
public static boolean isProxyMember(Member member, Object object) {
|
||||
if (!isProxy(object))
|
||||
if (!Modifier.isStatic(member.getModifiers()) && !isProxy(object)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Boolean flag = isProxyMemberCache.get(member);
|
||||
if (flag != null) {
|
||||
|
||||
@@ -58,10 +58,25 @@ public class ResourceFinder {
|
||||
this(path, classLoaderInterface, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ResourceFinder instance for looking up resources (via ClassLoader or via specific URLs
|
||||
* specifying resource locations).
|
||||
*
|
||||
* This class was functional in Struts 2.3.x, but broken for Struts 2.5.x (before 2.5.24), when dealing with
|
||||
* JAR resources in certain circumstances. The current logic permits the base path to be "" (empty string),
|
||||
* which is required to also match JAR entries rooted at "" and not just file entries rooted at "/".
|
||||
*
|
||||
* @param path Base path from which to look for resources (typically "xyz/abc/klm" form for file or
|
||||
* jar contents).
|
||||
* @param classLoaderInterface ClassLoader to perform the resource lookup. If null, a default Thread
|
||||
* ClassLoader will be used.
|
||||
* @param urls URLs (typically file: or jar:) within which to search for resources, instead of the
|
||||
* ClassLoader. If null, fallback to a ClassLoader instead.
|
||||
*/
|
||||
public ResourceFinder(String path, ClassLoaderInterface classLoaderInterface, URL... urls) {
|
||||
path = StringUtils.trimToEmpty(path);
|
||||
if (!StringUtils.endsWith(path, "/")) {
|
||||
path += "/";
|
||||
if (!path.isEmpty() && !StringUtils.endsWith(path, "/")) {
|
||||
path += "/"; // Only append terminator to nonempty paths, otherwise JAR entry lookups break.
|
||||
}
|
||||
this.path = path;
|
||||
|
||||
@@ -1015,15 +1030,26 @@ public class ResourceFinder {
|
||||
public Map<URL, Set<String>> findPackagesMap(String uri) throws IOException {
|
||||
String basePath = path + uri;
|
||||
|
||||
LOG.trace(" basePath(initial): " + basePath);
|
||||
|
||||
if (!basePath.endsWith("/")) {
|
||||
basePath += "/";
|
||||
}
|
||||
|
||||
LOG.trace(" basePath(final): " + basePath);
|
||||
|
||||
Enumeration<URL> urls = getResources(basePath);
|
||||
Map<URL, Set<String>> result = new HashMap<>();
|
||||
|
||||
if (! urls.hasMoreElements()) {
|
||||
LOG.debug(" urls enumeration for basePath is empty ?");
|
||||
}
|
||||
|
||||
while (urls.hasMoreElements()) {
|
||||
URL location = urls.nextElement();
|
||||
|
||||
LOG.debug(" url (location): " + location);
|
||||
|
||||
try {
|
||||
if ("jar".equals(location.getProtocol())) {
|
||||
Set<String> resources = new HashSet<>();
|
||||
@@ -1114,12 +1140,20 @@ public class ResourceFinder {
|
||||
jarfile = conn.getJarFile();
|
||||
|
||||
Enumeration<JarEntry> entries = jarfile.entries();
|
||||
|
||||
if (entries == null || ! entries.hasMoreElements()) {
|
||||
LOG.debug(" JAR entries null or empty");
|
||||
}
|
||||
LOG.debug(" Looking for entries matching basePath: " + basePath);
|
||||
|
||||
while (entries != null && entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String name = entry.getName();
|
||||
|
||||
if (entry.isDirectory() && StringUtils.startsWith(name, basePath)) {
|
||||
resources.add(name);
|
||||
} else if (entry.isDirectory()) {
|
||||
LOG.trace(" entry: " + name + " , isDirectory: " + entry.isDirectory() + " but does not start with basepath");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1156,13 +1190,22 @@ public class ResourceFinder {
|
||||
|
||||
private Enumeration<URL> getResources(String fulluri) throws IOException {
|
||||
if (urls == null) {
|
||||
LOG.debug(" urls (member) null, using classLoaderInterface to get resources");
|
||||
|
||||
return classLoaderInterface.getResources(fulluri);
|
||||
}
|
||||
|
||||
LOG.debug(" urls (member) non-null, using findResource to get resources");
|
||||
|
||||
Vector<URL> resources = new Vector<>();
|
||||
for (URL url : urls) {
|
||||
URL resource = findResource(fulluri, url);
|
||||
if (resource != null){
|
||||
|
||||
if (resource != null) {
|
||||
LOG.trace(" resource lookup non-null");
|
||||
resources.add(resource);
|
||||
} else {
|
||||
LOG.trace(" resource lookup is null");
|
||||
}
|
||||
}
|
||||
return resources.elements();
|
||||
|
||||
@@ -96,13 +96,13 @@ public final class StrutsConstants {
|
||||
|
||||
/** Update freemarker templates cache in seconds */
|
||||
public static final String STRUTS_FREEMARKER_TEMPLATES_CACHE_UPDATE_DELAY = "struts.freemarker.templatesCache.updateDelay";
|
||||
|
||||
|
||||
/** Cache model instances at BeanWrapper level */
|
||||
public static final String STRUTS_FREEMARKER_BEANWRAPPER_CACHE = "struts.freemarker.beanwrapperCache";
|
||||
|
||||
|
||||
/** Maximum strong sizing for MruCacheStorage for freemarker */
|
||||
public static final String STRUTS_FREEMARKER_MRU_MAX_STRONG_SIZE = "struts.freemarker.mru.max.strong.size";
|
||||
|
||||
|
||||
/** org.apache.struts2.views.velocity.VelocityManager implementation class */
|
||||
public static final String STRUTS_VELOCITY_MANAGER_CLASSNAME = "struts.velocity.manager.classname";
|
||||
|
||||
@@ -127,6 +127,9 @@ public final class StrutsConstants {
|
||||
/** The maximize size of a multipart request (file upload) */
|
||||
public static final String STRUTS_MULTIPART_MAXSIZE = "struts.multipart.maxSize";
|
||||
|
||||
/** The maximum length of a string parameter in a multipart request. */
|
||||
public static final String STRUTS_MULTIPART_MAX_STRING_LENGTH = "struts.multipart.maxStringLength";
|
||||
|
||||
/** The directory to use for storing uploaded files */
|
||||
public static final String STRUTS_MULTIPART_SAVEDIR = "struts.multipart.saveDir";
|
||||
|
||||
@@ -180,7 +183,7 @@ public final class StrutsConstants {
|
||||
* You can specify different prefixes that will be handled by different mappers
|
||||
*/
|
||||
public static final String PREFIX_BASED_MAPPER_CONFIGURATION = "struts.mapper.prefixMapping";
|
||||
|
||||
|
||||
/** Whether the Struts filter should serve static content or not */
|
||||
public static final String STRUTS_SERVE_STATIC_CONTENT = "struts.serve.static";
|
||||
|
||||
@@ -261,6 +264,9 @@ public final class StrutsConstants {
|
||||
/** Enables evaluation of OGNL expressions */
|
||||
public static final String STRUTS_ENABLE_OGNL_EVAL_EXPRESSION = "struts.ognl.enableOGNLEvalExpression";
|
||||
|
||||
/** The maximum length of an expression (OGNL) */
|
||||
public static final String STRUTS_OGNL_EXPRESSION_MAX_LENGTH = "struts.ognl.expressionMaxLength";
|
||||
|
||||
/** Disables {@link org.apache.struts2.dispatcher.StrutsRequestWrapper} request attribute value stack lookup (JSTL accessibility) */
|
||||
public static final String STRUTS_DISABLE_REQUEST_ATTRIBUTE_VALUE_STACK_LOOKUP = "struts.disableRequestAttributeValueStackLookup";
|
||||
|
||||
@@ -319,6 +325,7 @@ public final class StrutsConstants {
|
||||
/** Dedicated services to check if passed string is excluded/accepted */
|
||||
public static final String STRUTS_EXCLUDED_PATTERNS_CHECKER = "struts.excludedPatterns.checker";
|
||||
public static final String STRUTS_ACCEPTED_PATTERNS_CHECKER = "struts.acceptedPatterns.checker";
|
||||
public static final String STRUTS_NOT_EXCLUDED_ACCEPTED_PATTERNS_CHECKER = "struts.notExcludedAcceptedPatterns.checker";
|
||||
|
||||
/** Constant is used to override framework's default excluded patterns */
|
||||
public static final String STRUTS_OVERRIDE_EXCLUDED_PATTERNS = "struts.override.excludedPatterns";
|
||||
@@ -338,4 +345,7 @@ public final class StrutsConstants {
|
||||
public static final String STRUTS_DISALLOW_PROXY_MEMBER_ACCESS = "struts.disallowProxyMemberAccess";
|
||||
|
||||
public static final String STRUTS_OGNL_AUTO_GROWTH_COLLECTION_LIMIT = "struts.ognl.autoGrowthCollectionLimit";
|
||||
|
||||
/** See {@link com.opensymphony.xwork2.config.impl.AbstractMatcher#appendNamedParameters */
|
||||
public static final String STRUTS_MATCHER_APPEND_NAMED_PARAMETERS = "struts.matcher.appendNamedParameters";
|
||||
}
|
||||
|
||||
@@ -83,4 +83,21 @@ public class Checkbox extends UIBean {
|
||||
this.fieldValue = fieldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated since 2.5.27
|
||||
* @deprecated use {@link #setLabelPosition(String)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
@StrutsTagAttribute(description="(Deprecated) Define label position of form element (top/left), also 'right' is supported when using 'xhtml' theme")
|
||||
public void setLabelposition(String labelPosition) {
|
||||
super.setLabelposition(labelPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
@StrutsTagAttribute(description="Define label position of form element (top/left), also 'right' is supported when using 'xhtml' theme")
|
||||
public void setLabelPosition(String labelPosition) {
|
||||
super.setLabelPosition(labelPosition);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ import com.opensymphony.xwork2.util.ValueStack;
|
||||
allowDynamicAttributes = true)
|
||||
public class CheckboxList extends ListUIBean {
|
||||
final public static String TEMPLATE = "checkboxlist";
|
||||
|
||||
|
||||
public CheckboxList(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
|
||||
super(stack, request, response);
|
||||
}
|
||||
@@ -56,9 +56,19 @@ public class CheckboxList extends ListUIBean {
|
||||
protected String getDefaultTemplate() {
|
||||
return TEMPLATE;
|
||||
}
|
||||
|
||||
|
||||
public void evaluateExtraParams() {
|
||||
super.evaluateExtraParams();
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Checkboxlist tag requires lazy evaluation as list of tags is dynamically generated using <s:iterator/>
|
||||
*
|
||||
* @return boolean true by default
|
||||
*/
|
||||
@Override
|
||||
protected boolean lazyEvaluation() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package org.apache.struts2.components;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
@@ -70,6 +71,8 @@ public class Component {
|
||||
protected boolean throwExceptionOnELFailure;
|
||||
private UrlHelper urlHelper;
|
||||
|
||||
private NotExcludedAcceptedPatternsChecker notExcludedAcceptedPatterns;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -112,6 +115,12 @@ public class Component {
|
||||
public void setUrlHelper(UrlHelper urlHelper) {
|
||||
this.urlHelper = urlHelper;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setNotExcludedAcceptedPatterns(NotExcludedAcceptedPatternsChecker notExcludedAcceptedPatterns) {
|
||||
this.notExcludedAcceptedPatterns = notExcludedAcceptedPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the OGNL value stack associated with this component.
|
||||
* @return the OGNL value stack associated with this component.
|
||||
@@ -276,7 +285,7 @@ public class Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* If altsyntax (%{...}) is applied, simply strip the "%{" and "}" off.
|
||||
* If altsyntax (%{...}) is applied, simply strip the "%{" and "}" off.
|
||||
* @param expr the expression (must be not null)
|
||||
* @return the stripped expression if altSyntax is enabled. Otherwise
|
||||
* the parameter expression is returned as is.
|
||||
@@ -296,11 +305,11 @@ public class Component {
|
||||
/**
|
||||
* Adds the surrounding %{ } to the expression for proper processing.
|
||||
* @param expr the expression.
|
||||
* @return the modified expression if altSyntax is enabled, or the parameter
|
||||
* @return the modified expression if altSyntax is enabled, or the parameter
|
||||
* expression otherwise.
|
||||
*/
|
||||
protected String completeExpressionIfAltSyntax(String expr) {
|
||||
if (altSyntax()) {
|
||||
if (altSyntax() && !ComponentUtils.containsExpression(expr)) {
|
||||
return "%{" + expr + "}";
|
||||
}
|
||||
return expr;
|
||||
@@ -398,7 +407,7 @@ public class Component {
|
||||
* @return the action url.
|
||||
*/
|
||||
protected String determineActionURL(String action, String namespace, String method,
|
||||
HttpServletRequest req, HttpServletResponse res, Map parameters, String scheme,
|
||||
HttpServletRequest req, HttpServletResponse res, Map<String, Object> parameters, String scheme,
|
||||
boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort,
|
||||
boolean escapeAmp) {
|
||||
String finalAction = findString(action);
|
||||
@@ -551,4 +560,22 @@ public class Component {
|
||||
return standardAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if expression doesn't contain vulnerable code
|
||||
*
|
||||
* @param expression of the component
|
||||
* @return true|false
|
||||
* @since 2.5.27
|
||||
*/
|
||||
protected boolean isAcceptableExpression(String expression) {
|
||||
NotExcludedAcceptedPatternsChecker.IsAllowed isAllowed = notExcludedAcceptedPatterns.isAllowed(expression);
|
||||
if (isAllowed.isAllowed()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG.warn("Expression [{}] isn't allowed by pattern [{}]! See Accepted / Excluded patterns at\n" +
|
||||
"https://struts.apache.org/security/", expression, isAllowed.getAllowedPattern());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ public class Form extends ClosingUIBean {
|
||||
@Override
|
||||
protected void populateComponentHtmlId(Form form) {
|
||||
if (id != null) {
|
||||
addParameter("id", escape(id));
|
||||
super.populateComponentHtmlId(null);
|
||||
}
|
||||
|
||||
// if no id given, it will be tried to generate it from the action attribute
|
||||
|
||||
@@ -125,6 +125,7 @@ public abstract class FormButton extends ClosingUIBean {
|
||||
}
|
||||
}
|
||||
addParameter("id", _tmp_id);
|
||||
addParameter("escapedId", escape(_tmp_id));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -188,7 +188,6 @@ public abstract class ListUIBean extends UIBean {
|
||||
this.listTitle = listTitle;
|
||||
}
|
||||
|
||||
|
||||
public void setThrowExceptionOnNullValueAttribute(boolean throwExceptionOnNullValueAttribute) {
|
||||
this.throwExceptionOnNullValueAttribute = throwExceptionOnNullValueAttribute;
|
||||
}
|
||||
|
||||
@@ -125,23 +125,29 @@ public class Param extends Component {
|
||||
if (component instanceof UnnamedParametric) {
|
||||
((UnnamedParametric) component).addParameter(findValue(value));
|
||||
} else {
|
||||
String name = findString(this.name);
|
||||
String translatedName = findString(this.name);
|
||||
|
||||
if (name == null) {
|
||||
if (translatedName == null) {
|
||||
throw new StrutsException("No name found for following expression: " + this.name);
|
||||
}
|
||||
|
||||
Object value = findValue(this.value);
|
||||
boolean evaluated = !translatedName.equals(this.name);
|
||||
boolean reevaluate = !evaluated || isAcceptableExpression(translatedName);
|
||||
if (!reevaluate) {
|
||||
throw new StrutsException("Excluded or not accepted name found: " + translatedName);
|
||||
}
|
||||
|
||||
Object foundValue = findValue(this.value);
|
||||
if (suppressEmptyParameters) {
|
||||
if (value != null && StringUtils.isNotBlank(value.toString())) {
|
||||
component.addParameter(name, value);
|
||||
if (foundValue != null && StringUtils.isNotBlank(foundValue.toString())) {
|
||||
component.addParameter(translatedName, foundValue);
|
||||
} else {
|
||||
component.addParameter(name, null);
|
||||
component.addParameter(translatedName, null);
|
||||
}
|
||||
} else if (value == null || StringUtils.isBlank(value.toString())) {
|
||||
component.addParameter(name, "");
|
||||
} else if (foundValue == null || StringUtils.isBlank(foundValue.toString())) {
|
||||
component.addParameter(translatedName, "");
|
||||
} else {
|
||||
component.addParameter(name, value);
|
||||
component.addParameter(translatedName, foundValue);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -158,7 +164,8 @@ public class Param extends Component {
|
||||
|
||||
return super.end(writer, "");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean usesBody() {
|
||||
return true;
|
||||
}
|
||||
@@ -193,7 +200,7 @@ public class Param extends Component {
|
||||
* Adds the given value as a parameter to the outer tag.
|
||||
* @param value the value
|
||||
*/
|
||||
public void addParameter(Object value);
|
||||
void addParameter(Object value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
allowDynamicAttributes = true)
|
||||
public class Radio extends ListUIBean {
|
||||
final public static String TEMPLATE = "radiomap";
|
||||
|
||||
|
||||
public Radio(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
|
||||
super(stack, request, response);
|
||||
}
|
||||
@@ -65,8 +65,19 @@ public class Radio extends ListUIBean {
|
||||
protected String getDefaultTemplate() {
|
||||
return TEMPLATE;
|
||||
}
|
||||
|
||||
|
||||
public void evaluateExtraParams() {
|
||||
super.evaluateExtraParams();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Radio tag requires lazy evaluation as list of tags is dynamically generated using <s:iterator/>
|
||||
*
|
||||
* @return boolean true by default
|
||||
*/
|
||||
@Override
|
||||
protected boolean lazyEvaluation() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -188,7 +188,9 @@ public class ServletUrlRenderer implements UrlRenderer {
|
||||
|
||||
// if the id isn't specified, use the action name
|
||||
if (formComponent.getId() == null && actionName != null) {
|
||||
formComponent.addParameter("id", formComponent.escape(actionName));
|
||||
String escapedId = formComponent.escape(actionName);
|
||||
formComponent.addParameter("id", escapedId);
|
||||
formComponent.addParameter("escapedId", escapedId);
|
||||
}
|
||||
} else if (action != null) {
|
||||
// Since we can't find an action alias in the configuration, we just
|
||||
@@ -223,7 +225,9 @@ public class ServletUrlRenderer implements UrlRenderer {
|
||||
} else {
|
||||
id = result.substring(slash + 1);
|
||||
}
|
||||
formComponent.addParameter("id", formComponent.escape(id));
|
||||
String escapedId = formComponent.escape(id);
|
||||
formComponent.addParameter("id", escapedId);
|
||||
formComponent.addParameter("escapedId", escapedId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,8 +31,7 @@ import com.opensymphony.xwork2.util.ValueStack;
|
||||
* complex expression and then simply reference that variable each time rather than the complex expression. This is
|
||||
* useful in both cases: when the complex expression takes time (performance improvement) or is hard to read (code
|
||||
* readability improvement).</p>
|
||||
* <p>If the tag is used with body content, the evaluation of the value parameter is omitted. Instead, the String to
|
||||
* which the body evaluates is set as value for the scoped variable.</p>
|
||||
* <p>If the value parameter is omitted, the String to which the body evaluates is set as value for the scoped variable.</p>
|
||||
*
|
||||
* <p>The scopes available are as follows:</p>
|
||||
* <ul>
|
||||
@@ -94,10 +93,10 @@ public class Set extends ContextBean {
|
||||
|
||||
Object o;
|
||||
if (value == null) {
|
||||
if (body != null && !body.equals("")) {
|
||||
o = body;
|
||||
} else {
|
||||
if (body == null) {
|
||||
o = findValue("top");
|
||||
} else {
|
||||
o = body;
|
||||
}
|
||||
} else {
|
||||
o = findValue(value);
|
||||
|
||||
@@ -20,7 +20,9 @@ package org.apache.struts2.components;
|
||||
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -30,6 +32,7 @@ import org.apache.struts2.components.template.Template;
|
||||
import org.apache.struts2.components.template.TemplateEngine;
|
||||
import org.apache.struts2.components.template.TemplateEngineManager;
|
||||
import org.apache.struts2.components.template.TemplateRenderingContext;
|
||||
import org.apache.struts2.util.ComponentUtils;
|
||||
import org.apache.struts2.util.TextProviderHelper;
|
||||
import org.apache.struts2.views.annotations.StrutsTagAttribute;
|
||||
import org.apache.struts2.views.util.ContextUtil;
|
||||
@@ -555,16 +558,13 @@ public abstract class UIBean extends Component {
|
||||
protected abstract String getDefaultTemplate();
|
||||
|
||||
protected Template buildTemplateName(String myTemplate, String myDefaultTemplate) {
|
||||
String template = myDefaultTemplate;
|
||||
String templateName = myDefaultTemplate;
|
||||
|
||||
if (myTemplate != null) {
|
||||
template = findString(myTemplate);
|
||||
templateName = findString(myTemplate);
|
||||
}
|
||||
|
||||
String templateDir = getTemplateDir();
|
||||
String theme = getTheme();
|
||||
|
||||
return new Template(templateDir, theme, template);
|
||||
return new Template(getTemplateDir(), getTheme(), templateName);
|
||||
|
||||
}
|
||||
|
||||
@@ -581,71 +581,70 @@ public abstract class UIBean extends Component {
|
||||
}
|
||||
|
||||
public String getTemplateDir() {
|
||||
String templateDir = null;
|
||||
String result = null;
|
||||
|
||||
if (this.templateDir != null) {
|
||||
templateDir = findString(this.templateDir);
|
||||
result = findString(this.templateDir);
|
||||
}
|
||||
|
||||
// If templateDir is not explicitly given,
|
||||
// try to find attribute which states the dir set to use
|
||||
if (StringUtils.isBlank(templateDir)) {
|
||||
templateDir = stack.findString("#attr.templateDir");
|
||||
if (StringUtils.isBlank(result)) {
|
||||
result = stack.findString("#attr.templateDir");
|
||||
}
|
||||
|
||||
// Default template set
|
||||
if (StringUtils.isBlank(templateDir)) {
|
||||
templateDir = defaultTemplateDir;
|
||||
if (StringUtils.isBlank(result)) {
|
||||
result = defaultTemplateDir;
|
||||
}
|
||||
|
||||
// Defaults to 'template'
|
||||
if (StringUtils.isBlank(templateDir)) {
|
||||
templateDir = "template";
|
||||
if (StringUtils.isBlank(result)) {
|
||||
result = "template";
|
||||
}
|
||||
|
||||
return templateDir;
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getTheme() {
|
||||
String theme = null;
|
||||
String result = null;
|
||||
|
||||
if (this.theme != null) {
|
||||
theme = findString(this.theme);
|
||||
result = findString(this.theme);
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(theme)) {
|
||||
if (StringUtils.isBlank(result)) {
|
||||
Form form = (Form) findAncestor(Form.class);
|
||||
if (form != null) {
|
||||
theme = form.getTheme();
|
||||
result = form.getTheme();
|
||||
}
|
||||
}
|
||||
|
||||
// If theme set is not explicitly given,
|
||||
// try to find attribute which states the theme set to use
|
||||
if (StringUtils.isBlank(theme)) {
|
||||
theme = stack.findString("#attr.theme");
|
||||
if (StringUtils.isBlank(result)) {
|
||||
result = stack.findString("#attr.theme");
|
||||
}
|
||||
|
||||
// Default theme set
|
||||
if (StringUtils.isBlank(theme)) {
|
||||
theme = defaultUITheme;
|
||||
if (StringUtils.isBlank(result)) {
|
||||
result = defaultUITheme;
|
||||
}
|
||||
|
||||
return theme;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void evaluateParams() {
|
||||
String templateDir = getTemplateDir();
|
||||
String theme = getTheme();
|
||||
|
||||
addParameter("templateDir", templateDir);
|
||||
addParameter("theme", theme);
|
||||
String gotTheme = getTheme();
|
||||
|
||||
addParameter("templateDir", getTemplateDir());
|
||||
addParameter("theme", gotTheme);
|
||||
addParameter("template", template != null ? findString(template) : getDefaultTemplate());
|
||||
addParameter("dynamicAttributes", dynamicAttributes);
|
||||
addParameter("themeExpansionToken", uiThemeExpansionToken);
|
||||
addParameter("expandTheme", uiThemeExpansionToken + theme);
|
||||
addParameter("expandTheme", uiThemeExpansionToken + gotTheme);
|
||||
|
||||
String name = null;
|
||||
String translatedName = null;
|
||||
String providedLabel = null;
|
||||
|
||||
if (this.key != null) {
|
||||
@@ -661,8 +660,8 @@ public abstract class UIBean extends Component {
|
||||
}
|
||||
|
||||
if (this.name != null) {
|
||||
name = findString(this.name);
|
||||
addParameter("name", name);
|
||||
translatedName = findString(this.name);
|
||||
addParameter("name", translatedName);
|
||||
}
|
||||
|
||||
if (label != null) {
|
||||
@@ -679,7 +678,9 @@ public abstract class UIBean extends Component {
|
||||
}
|
||||
|
||||
if (labelPosition != null) {
|
||||
addParameter("labelposition", findString(labelPosition));
|
||||
String labelPosition = findString(this.labelPosition);
|
||||
addParameter("labelposition", labelPosition);
|
||||
addParameter("labelPosition", labelPosition);
|
||||
}
|
||||
|
||||
if (requiredPosition != null) {
|
||||
@@ -689,7 +690,7 @@ public abstract class UIBean extends Component {
|
||||
if (errorPosition != null) {
|
||||
addParameter("errorposition", findString(errorPosition));
|
||||
}
|
||||
|
||||
|
||||
if (requiredLabel != null) {
|
||||
addParameter("required", findValue(requiredLabel, Boolean.class));
|
||||
}
|
||||
@@ -784,25 +785,31 @@ public abstract class UIBean extends Component {
|
||||
|
||||
|
||||
// see if the value was specified as a parameter already
|
||||
final String NAME_VALUE = "nameValue";
|
||||
if (parameters.containsKey("value")) {
|
||||
parameters.put("nameValue", parameters.get("value"));
|
||||
parameters.put(NAME_VALUE, parameters.get("value"));
|
||||
} else {
|
||||
if (evaluateNameValue()) {
|
||||
final Class valueClazz = getValueClassType();
|
||||
|
||||
if (valueClazz != null) {
|
||||
if (value != null) {
|
||||
addParameter("nameValue", findValue(value, valueClazz));
|
||||
} else if (name != null) {
|
||||
String expr = completeExpressionIfAltSyntax(name);
|
||||
|
||||
addParameter("nameValue", findValue(expr, valueClazz));
|
||||
addParameter(NAME_VALUE, findValue(value, valueClazz));
|
||||
} else if (translatedName != null) {
|
||||
boolean evaluated = !translatedName.equals(this.name);
|
||||
boolean reevaluate = !evaluated || isAcceptableExpression(translatedName);
|
||||
if (!reevaluate) {
|
||||
addParameter(NAME_VALUE, translatedName);
|
||||
} else {
|
||||
String expr = completeExpressionIfAltSyntax(translatedName);
|
||||
addParameter(NAME_VALUE, findValue(expr, valueClazz));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value != null) {
|
||||
addParameter("nameValue", findValue(value));
|
||||
} else if (name != null) {
|
||||
addParameter("nameValue", findValue(name));
|
||||
addParameter(NAME_VALUE, findValue(value));
|
||||
} else if (translatedName != null) {
|
||||
addParameter(NAME_VALUE, findValue(translatedName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -816,10 +823,10 @@ public abstract class UIBean extends Component {
|
||||
if (form != null ) {
|
||||
addParameter("form", form.getParameters());
|
||||
|
||||
if ( name != null ) {
|
||||
if ( translatedName != null ) {
|
||||
// list should have been created by the form component
|
||||
List<String> tags = (List<String>) form.getParameters().get("tagNames");
|
||||
tags.add(name);
|
||||
tags.add(translatedName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -887,7 +894,7 @@ public abstract class UIBean extends Component {
|
||||
protected String escape(String name) {
|
||||
// escape any possible values that can make the ID painful to work with in JavaScript
|
||||
if (name != null) {
|
||||
return name.replaceAll("[\\/\\.\\[\\]]", "_");
|
||||
return name.replaceAll("[^a-zA-Z0-9_]", "_");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@@ -938,14 +945,14 @@ public abstract class UIBean extends Component {
|
||||
|
||||
protected Map getTooltipConfig(UIBean component) {
|
||||
Object tooltipConfigObj = component.getParameters().get("tooltipConfig");
|
||||
Map<String, String> tooltipConfig = new LinkedHashMap<>();
|
||||
Map<String, String> result = new LinkedHashMap<>();
|
||||
|
||||
if (tooltipConfigObj instanceof Map) {
|
||||
// we get this if its configured using
|
||||
// 1] UI component's tooltipConfig attribute OR
|
||||
// 2] <param name="tooltip" value="" /> param tag value attribute
|
||||
|
||||
tooltipConfig = new LinkedHashMap<>((Map) tooltipConfigObj);
|
||||
result = new LinkedHashMap<>((Map) tooltipConfigObj);
|
||||
} else if (tooltipConfigObj instanceof String) {
|
||||
|
||||
// we get this if its configured using
|
||||
@@ -955,23 +962,23 @@ public abstract class UIBean extends Component {
|
||||
|
||||
for (String aTooltipConfigArray : tooltipConfigArray) {
|
||||
String[] configEntry = aTooltipConfigArray.trim().split("=");
|
||||
String key = configEntry[0].trim();
|
||||
String value;
|
||||
String configKey = configEntry[0].trim();
|
||||
String configValue;
|
||||
if (configEntry.length > 1) {
|
||||
value = configEntry[1].trim();
|
||||
tooltipConfig.put(key, value);
|
||||
configValue = configEntry[1].trim();
|
||||
result.put(configKey, configValue);
|
||||
} else {
|
||||
LOG.warn("component {} tooltip config param {} has no value defined, skipped", component, key);
|
||||
LOG.warn("component {} tooltip config param {} has no value defined, skipped", component, configKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (component.javascriptTooltip != null)
|
||||
tooltipConfig.put("jsTooltipEnabled", component.javascriptTooltip);
|
||||
result.put("jsTooltipEnabled", component.javascriptTooltip);
|
||||
if (component.tooltipIconPath != null)
|
||||
tooltipConfig.put("tooltipIcon", component.tooltipIconPath);
|
||||
result.put("tooltipIcon", component.tooltipIconPath);
|
||||
if (component.tooltipDelay != null)
|
||||
tooltipConfig.put("tooltipDelay", component.tooltipDelay);
|
||||
return tooltipConfig;
|
||||
result.put("tooltipDelay", component.tooltipDelay);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1002,7 +1009,7 @@ public abstract class UIBean extends Component {
|
||||
} else {
|
||||
tryId = generatedId;
|
||||
}
|
||||
|
||||
|
||||
//fix for https://issues.apache.org/jira/browse/WW-4299
|
||||
//do not assign value to id if tryId is null
|
||||
if (tryId != null) {
|
||||
@@ -1021,9 +1028,7 @@ public abstract class UIBean extends Component {
|
||||
|
||||
@StrutsTagAttribute(description="HTML id attribute")
|
||||
public void setId(String id) {
|
||||
if (id != null) {
|
||||
this.id = findString(id);
|
||||
}
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@StrutsTagAttribute(description="The template directory.")
|
||||
@@ -1050,7 +1055,8 @@ public abstract class UIBean extends Component {
|
||||
this.cssClass = cssClass;
|
||||
}
|
||||
|
||||
@StrutsTagAttribute(description="The css class to use for element - it's an alias of cssClass attribute.")
|
||||
@Deprecated
|
||||
@StrutsTagAttribute(description="(Deprecated) The css class to use for element - it's an alias of cssClass attribute.")
|
||||
public void setClass(String cssClass) {
|
||||
this.cssClass = cssClass;
|
||||
}
|
||||
@@ -1095,8 +1101,19 @@ public abstract class UIBean extends Component {
|
||||
this.labelSeparator = labelseparator;
|
||||
}
|
||||
|
||||
@StrutsTagAttribute(description="Define label position of form element (top/left)")
|
||||
/**
|
||||
* Deprecated since 2.5.27
|
||||
* @deprecated use {@link #setLabelPosition(String)} instead
|
||||
*/
|
||||
@StrutsTagAttribute(description="(Deprecated) Define label position of form element (top/left)")
|
||||
@Deprecated
|
||||
public void setLabelposition(String labelPosition) {
|
||||
LOG.warn("\"labelposition\" attribute is deprecated, please use \"labelPosition\" instead!");
|
||||
this.labelPosition = labelPosition;
|
||||
}
|
||||
|
||||
@StrutsTagAttribute(description="Define label position of form element (top/left)")
|
||||
public void setLabelPosition(String labelPosition) {
|
||||
this.labelPosition = labelPosition;
|
||||
}
|
||||
|
||||
@@ -1109,7 +1126,7 @@ public abstract class UIBean extends Component {
|
||||
public void setErrorPosition(String errorPosition) {
|
||||
this.errorPosition = errorPosition;
|
||||
}
|
||||
|
||||
|
||||
@StrutsTagAttribute(description="The name to set for element")
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
@@ -1241,31 +1258,47 @@ public abstract class UIBean extends Component {
|
||||
this.tooltipIconPath = tooltipIconPath;
|
||||
}
|
||||
|
||||
public void setDynamicAttributes(Map<String, Object> tagDynamicAttributes) {
|
||||
for (Map.Entry<String, Object> entry : tagDynamicAttributes.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
public void setDynamicAttributes(Map<String, String> tagDynamicAttributes) {
|
||||
for (Map.Entry<String, String> entry : tagDynamicAttributes.entrySet()) {
|
||||
String attrName = entry.getKey();
|
||||
String attrValue = entry.getValue();
|
||||
|
||||
if (!isValidTagAttribute(key)) {
|
||||
dynamicAttributes.put(key, entry.getValue());
|
||||
if (!isValidTagAttribute(attrName)) {
|
||||
if (ComponentUtils.altSyntax(getStack()) && ComponentUtils.containsExpression(attrValue) && !lazyEvaluation()) {
|
||||
String translated = TextParseUtil.translateVariables('%', attrValue, stack);
|
||||
dynamicAttributes.put(attrName, ObjectUtils.defaultIfNull(translated, attrValue));
|
||||
} else {
|
||||
dynamicAttributes.put(attrName, attrValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* supports dynamic attributes for freemarker ui tags
|
||||
* @see https://issues.apache.org/jira/browse/WW-3174
|
||||
* @see https://issues.apache.org/jira/browse/WW-4166
|
||||
* @see "https://issues.apache.org/jira/browse/WW-3174"
|
||||
* @see "https://issues.apache.org/jira/browse/WW-4166"
|
||||
*/
|
||||
@Override
|
||||
public void copyParams(Map params) {
|
||||
super.copyParams(params);
|
||||
for (Object o : params.entrySet()) {
|
||||
Map.Entry entry = (Map.Entry) o;
|
||||
String key = (String) entry.getKey();
|
||||
if (!isValidTagAttribute(key) && !key.equals("dynamicAttributes")) {
|
||||
dynamicAttributes.put(key, entry.getValue());
|
||||
String entryKey = (String) entry.getKey();
|
||||
if (!isValidTagAttribute(entryKey) && !entryKey.equals("dynamicAttributes")) {
|
||||
dynamicAttributes.put(entryKey, entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to avoid evaluating attributes in {@link #evaluateParams()} or {@link #evaluateExtraParams()}
|
||||
* as evaluation will happen in tag's template
|
||||
*
|
||||
* @return boolean false if evaluation should be performed in ftl
|
||||
*/
|
||||
protected boolean lazyEvaluation() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
-3
@@ -64,7 +64,7 @@ public class FreemarkerTemplateEngine extends BaseTemplateEngine {
|
||||
public void setFreemarkerManager(FreemarkerManager mgr) {
|
||||
this.freemarkerManager = mgr;
|
||||
}
|
||||
|
||||
|
||||
public void renderTemplate(TemplateRenderingContext templateContext) throws Exception {
|
||||
// get the various items required from the stack
|
||||
ValueStack stack = templateContext.getStack();
|
||||
@@ -121,6 +121,10 @@ public class FreemarkerTemplateEngine extends BaseTemplateEngine {
|
||||
ActionInvocation ai = ActionContext.getContext().getActionInvocation();
|
||||
|
||||
Object action = (ai == null) ? null : ai.getAction();
|
||||
if (action == null) {
|
||||
LOG.warn("Rendering tag {} out of Action scope, accessing directly JSPs is not recommended! " +
|
||||
"Please read https://struts.apache.org/security/#never-expose-jsp-files-directly", templateName);
|
||||
}
|
||||
SimpleHash model = freemarkerManager.buildTemplateModel(stack, action, servletContext, req, res, config.getObjectWrapper());
|
||||
|
||||
model.put("tag", templateContext.getTag());
|
||||
@@ -144,10 +148,12 @@ public class FreemarkerTemplateEngine extends BaseTemplateEngine {
|
||||
}
|
||||
};
|
||||
|
||||
LOG.debug("Push tag on top of the stack");
|
||||
stack.push(templateContext.getTag());
|
||||
try {
|
||||
stack.push(templateContext.getTag());
|
||||
template.process(model, writer);
|
||||
} finally {
|
||||
LOG.debug("Removes tag from top of the stack");
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
@@ -155,4 +161,4 @@ public class FreemarkerTemplateEngine extends BaseTemplateEngine {
|
||||
protected String getSuffix() {
|
||||
return "ftl";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ import com.opensymphony.xwork2.factory.ResultFactory;
|
||||
import com.opensymphony.xwork2.factory.ValidatorFactory;
|
||||
import com.opensymphony.xwork2.inject.ContainerBuilder;
|
||||
import com.opensymphony.xwork2.inject.Scope;
|
||||
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.util.PatternMatcher;
|
||||
import com.opensymphony.xwork2.util.TextParser;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
@@ -424,6 +425,8 @@ public class DefaultBeanSelectionProvider extends AbstractBeanSelectionProvider
|
||||
/** Checker is used mostly in interceptors, so there be one instance of checker per interceptor with Scope.PROTOTYPE **/
|
||||
alias(ExcludedPatternsChecker.class, StrutsConstants.STRUTS_EXCLUDED_PATTERNS_CHECKER, builder, props, Scope.PROTOTYPE);
|
||||
alias(AcceptedPatternsChecker.class, StrutsConstants.STRUTS_ACCEPTED_PATTERNS_CHECKER, builder, props, Scope.PROTOTYPE);
|
||||
alias(NotExcludedAcceptedPatternsChecker.class, StrutsConstants.STRUTS_NOT_EXCLUDED_ACCEPTED_PATTERNS_CHECKER
|
||||
, builder, props, Scope.SINGLETON);
|
||||
|
||||
switchDevMode(props);
|
||||
|
||||
|
||||
@@ -97,6 +97,10 @@ public class DefaultDispatcherErrorHandler implements DispatcherErrorHandler {
|
||||
response.sendError(code, e.getMessage());
|
||||
} catch (IOException e1) {
|
||||
// we're already sending an error, not much else we can do if more stuff breaks
|
||||
LOG.warn("Unable to send error response, code: {};", code, e1);
|
||||
} catch (IllegalStateException ise) {
|
||||
// Log illegalstate instead of passing unrecoverable exception to calling thread
|
||||
LOG.warn("Unable to send error response, code: {}; isCommited: {};", code, response.isCommitted(), ise);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +126,10 @@ public class DefaultDispatcherErrorHandler implements DispatcherErrorHandler {
|
||||
response.sendError(code, "Unable to show problem report:\n" + exp + "\n\n" + LocationUtils.getLocation(exp));
|
||||
} catch (IOException ex) {
|
||||
// we're already sending an error, not much else we can do if more stuff breaks
|
||||
LOG.warn("Unable to send error response, code: {};", code, ex);
|
||||
} catch (IllegalStateException ise) {
|
||||
// Log illegalstate instead of passing unrecoverable exception to calling thread
|
||||
LOG.warn("Unable to send error response, code: {}; isCommited: {};", code, response.isCommitted(), ise);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +218,15 @@ public class DefaultStaticContentLoader implements StaticContentLoader {
|
||||
}
|
||||
}
|
||||
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
try {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
} catch (IOException e1) {
|
||||
// we're already sending an error, not much else we can do if more stuff breaks
|
||||
LOG.warn("Unable to send error response, code: {};", HttpServletResponse.SC_NOT_FOUND, e1);
|
||||
} catch (IllegalStateException ise) {
|
||||
// Log illegalstate instead of passing unrecoverable exception to calling thread
|
||||
LOG.warn("Unable to send error response, code: {}; isCommited: {};", HttpServletResponse.SC_NOT_FOUND, response.isCommitted(), ise);
|
||||
}
|
||||
}
|
||||
|
||||
protected void process(InputStream is, String path, HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
|
||||
@@ -85,7 +85,7 @@ public class Dispatcher {
|
||||
*/
|
||||
public static final String REQUEST_POST_METHOD = "POST";
|
||||
|
||||
public static final String MULTIPART_FORM_DATA_REGEX = "^multipart/form-data(; boundary=[0-9a-zA-Z'()+_,\\-./:=?]{1,70})?(;charset=[a-zA-Z\\-0-9]{3,14})?";
|
||||
public static final String MULTIPART_FORM_DATA_REGEX = "^multipart/form-data(?:\\s*;\\s*boundary=[0-9a-zA-Z'()+_,\\-./:=?]{1,70})?(?:\\s*;\\s*charset=[a-zA-Z\\-0-9]{3,14})?";
|
||||
|
||||
/**
|
||||
* Provide a thread local instance.
|
||||
@@ -582,8 +582,10 @@ public class Dispatcher {
|
||||
logConfigurationException(request, e);
|
||||
sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
if (handleException || devMode) {
|
||||
if (devMode) {
|
||||
LOG.debug("Dispatcher serviceAction failed", e);
|
||||
}
|
||||
sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
|
||||
} else {
|
||||
throw new ServletException(e);
|
||||
@@ -694,12 +696,23 @@ public class Dispatcher {
|
||||
try {
|
||||
locale = LocaleUtils.toLocale(defaultLocale);
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warn(new ParameterizedMessage("Cannot convert 'struts.locale' = [{}] to proper locale, defaulting to request locale [{}]",
|
||||
defaultLocale, request.getLocale()), e);
|
||||
locale = request.getLocale();
|
||||
try {
|
||||
locale = request.getLocale();
|
||||
LOG.warn(new ParameterizedMessage("Cannot convert 'struts.locale' = [{}] to proper locale, defaulting to request locale [{}]",
|
||||
defaultLocale, locale), e);
|
||||
} catch (RuntimeException rex) {
|
||||
LOG.warn(new ParameterizedMessage("Cannot convert 'struts.locale' = [{}] to proper locale, and cannot get locale from HTTP Request, falling back to system default locale",
|
||||
defaultLocale), rex);
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
locale = request.getLocale();
|
||||
try {
|
||||
locale = request.getLocale();
|
||||
} catch (RuntimeException rex) {
|
||||
LOG.warn("Cannot get locale from HTTP Request, falling back to system default locale", rex);
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
}
|
||||
return locale;
|
||||
}
|
||||
|
||||
@@ -192,7 +192,34 @@ public class HttpParameters implements Map<String, Parameter>, Cloneable {
|
||||
for (Map.Entry<String, Object> entry : requestParameterMap.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
parameters.put(name, new Parameter.Request(name, value));
|
||||
if (value instanceof Parameter) {
|
||||
parameters.put(name, (Parameter) value);
|
||||
} else {
|
||||
parameters.put(name, new Parameter.Request(name, value));
|
||||
}
|
||||
}
|
||||
|
||||
return new HttpParameters(parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternate Builder method which avoids wrapping any parameters that are already
|
||||
* a {@link Parameter} element within another {@link Parameter} wrapper.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public HttpParameters buildNoNestedWrapping() {
|
||||
Map<String, Parameter> parameters = (parent == null)
|
||||
? new HashMap<String, Parameter>()
|
||||
: new HashMap<>(parent.parameters);
|
||||
|
||||
for (Map.Entry<String, Object> entry : requestParameterMap.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
Parameter parameterValue = (value instanceof Parameter)
|
||||
? (Parameter) value
|
||||
: new Parameter.Request(name, value);
|
||||
parameters.put(name, parameterValue);
|
||||
}
|
||||
|
||||
return new HttpParameters(parameters);
|
||||
|
||||
+12
-2
@@ -54,6 +54,11 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
|
||||
protected long maxSize;
|
||||
protected boolean maxSizeProvided;
|
||||
|
||||
/**
|
||||
* Specifies the maximum length of a string parameter in a multipart request.
|
||||
*/
|
||||
protected Long maxStringLength;
|
||||
|
||||
/**
|
||||
* Specifies the buffer size to use during streaming.
|
||||
*/
|
||||
@@ -88,6 +93,11 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
|
||||
this.maxSize = Long.parseLong(maxSize);
|
||||
}
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_MULTIPART_MAX_STRING_LENGTH)
|
||||
public void setMaxStringLength(String maxStringLength) {
|
||||
this.maxStringLength = Long.parseLong(maxStringLength);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setLocaleProviderFactory(LocaleProviderFactory localeProviderFactory) {
|
||||
defaultLocale = localeProviderFactory.createLocaleProvider().getLocale();
|
||||
@@ -134,9 +144,9 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
|
||||
int forwardSlash = fileName.lastIndexOf('/');
|
||||
int backwardSlash = fileName.lastIndexOf('\\');
|
||||
if (forwardSlash != -1 && forwardSlash > backwardSlash) {
|
||||
fileName = fileName.substring(forwardSlash + 1, fileName.length());
|
||||
fileName = fileName.substring(forwardSlash + 1);
|
||||
} else {
|
||||
fileName = fileName.substring(backwardSlash + 1, fileName.length());
|
||||
fileName = fileName.substring(backwardSlash + 1);
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
+27
-14
@@ -25,6 +25,7 @@ import org.apache.commons.fileupload.RequestContext;
|
||||
import org.apache.commons.fileupload.disk.DiskFileItem;
|
||||
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
|
||||
import org.apache.commons.fileupload.servlet.ServletFileUpload;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.dispatcher.LocalizedMessage;
|
||||
@@ -55,9 +56,8 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
|
||||
*
|
||||
* @param saveDir the directory to save off the file
|
||||
* @param request the request containing the multipart
|
||||
* @throws java.io.IOException is thrown if encoding fails.
|
||||
*/
|
||||
public void parse(HttpServletRequest request, String saveDir) throws IOException {
|
||||
public void parse(HttpServletRequest request, String saveDir) {
|
||||
try {
|
||||
setLocale(request);
|
||||
processUpload(request, saveDir);
|
||||
@@ -126,13 +126,26 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
|
||||
values = new ArrayList<>();
|
||||
}
|
||||
|
||||
// note: see http://jira.opensymphony.com/browse/WW-633
|
||||
// basically, in some cases the charset may be null, so
|
||||
// we're just going to try to "other" method (no idea if this
|
||||
// will work)
|
||||
if (charset != null) {
|
||||
long size = item.getSize();
|
||||
if (size == 0) {
|
||||
values.add(StringUtils.EMPTY);
|
||||
} else if (size > maxStringLength) {
|
||||
String errorKey = "struts.messages.upload.error.parameter.too.long";
|
||||
LocalizedMessage localizedMessage = new LocalizedMessage(this.getClass(), errorKey, null,
|
||||
new Object[] { item.getFieldName(), maxStringLength, size });
|
||||
|
||||
if (!errors.contains(localizedMessage)) {
|
||||
errors.add(localizedMessage);
|
||||
}
|
||||
return;
|
||||
|
||||
} else if (charset != null) {
|
||||
values.add(item.getString(charset));
|
||||
} else {
|
||||
// note: see http://jira.opensymphony.com/browse/WW-633
|
||||
// basically, in some cases the charset may be null, so
|
||||
// we're just going to try to "other" method (no idea if this
|
||||
// will work)
|
||||
values.add(item.getString());
|
||||
}
|
||||
params.put(item.getFieldName(), values);
|
||||
@@ -183,7 +196,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
|
||||
contentTypes.add(fileItem.getContentType());
|
||||
}
|
||||
|
||||
return contentTypes.toArray(new String[contentTypes.size()]);
|
||||
return contentTypes.toArray(new String[0]);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -209,7 +222,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
|
||||
fileList.add(new StrutsUploadedFile(storeLocation));
|
||||
}
|
||||
|
||||
return fileList.toArray(new UploadedFile[fileList.size()]);
|
||||
return fileList.toArray(new UploadedFile[0]);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -227,7 +240,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
|
||||
fileNames.add(getCanonicalName(fileItem.getName()));
|
||||
}
|
||||
|
||||
return fileNames.toArray(new String[fileNames.size()]);
|
||||
return fileNames.toArray(new String[0]);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -245,7 +258,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
|
||||
fileNames.add(((DiskFileItem) fileItem).getStoreLocation().getName());
|
||||
}
|
||||
|
||||
return fileNames.toArray(new String[fileNames.size()]);
|
||||
return fileNames.toArray(new String[0]);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -253,7 +266,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
|
||||
*/
|
||||
public String getParameter(String name) {
|
||||
List<String> v = params.get(name);
|
||||
if (v != null && v.size() > 0) {
|
||||
if (v != null && !v.isEmpty()) {
|
||||
return v.get(0);
|
||||
}
|
||||
|
||||
@@ -272,8 +285,8 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
|
||||
*/
|
||||
public String[] getParameterValues(String name) {
|
||||
List<String> v = params.get(name);
|
||||
if (v != null && v.size() > 0) {
|
||||
return v.toArray(new String[v.size()]);
|
||||
if (v != null && !v.isEmpty()) {
|
||||
return v.toArray(new String[0]);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
+8
-8
@@ -74,6 +74,8 @@ import java.util.Map;
|
||||
public class ActionMappingParametersInterceptor extends ParametersInterceptor {
|
||||
|
||||
/**
|
||||
* Get the parameter map from ActionMapping associated with the provided ActionContext.
|
||||
*
|
||||
* @param ac The action context
|
||||
* @return the parameters from the action mapping in the context. If none found, returns an empty map.
|
||||
*/
|
||||
@@ -81,27 +83,25 @@ public class ActionMappingParametersInterceptor extends ParametersInterceptor {
|
||||
protected HttpParameters retrieveParameters(ActionContext ac) {
|
||||
ActionMapping mapping = (ActionMapping) ac.get(ServletActionContext.ACTION_MAPPING);
|
||||
if (mapping != null) {
|
||||
return HttpParameters.create(mapping.getParams()).build();
|
||||
return HttpParameters.create(mapping.getParams()).buildNoNestedWrapping();
|
||||
} else {
|
||||
return HttpParameters.create().build();
|
||||
return HttpParameters.create().buildNoNestedWrapping();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the parameters into context's ParameterMap
|
||||
* Adds the parameters into the current ActionContext's parameter map.
|
||||
*
|
||||
* Note: The method should avoid re-wrapping values which are already of type Parameter.
|
||||
*
|
||||
* @param ac The action context
|
||||
* @param newParams The parameter map to apply
|
||||
* <p>
|
||||
* In this class this is a no-op, since the parameters were fetched from the same location.
|
||||
* In subclasses both retrieveParameters() and addParametersToContext() should be overridden.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
protected void addParametersToContext(ActionContext ac, Map<String, ?> newParams) {
|
||||
HttpParameters previousParams = ac.getParameters();
|
||||
HttpParameters.Builder combinedParams = HttpParameters.create().withParent(previousParams).withExtraParams(newParams);
|
||||
|
||||
ac.setParameters(combinedParams.build());
|
||||
ac.setParameters(combinedParams.buildNoNestedWrapping());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,6 +343,14 @@ public class FileUploadInterceptor extends AbstractInterceptor {
|
||||
validation.addFieldError(inputName, errMsg);
|
||||
}
|
||||
|
||||
if (LOG.isWarnEnabled()) {
|
||||
LOG.warn(errMsg);
|
||||
}
|
||||
} else if (file.getContent() == null) {
|
||||
String errMsg = getTextMessage(action, "struts.messages.error.uploading", new String[]{filename});
|
||||
if (validation != null) {
|
||||
validation.addFieldError(inputName, errMsg);
|
||||
}
|
||||
if (LOG.isWarnEnabled()) {
|
||||
LOG.warn(errMsg);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import java.util.Map;
|
||||
/**
|
||||
* Just as the CheckboxInterceptor checks that if only the hidden field is present, so too does this interceptor. If
|
||||
* the "__multiselect_" request parameter is present and its visible counterpart is not, set a new request parameter to an
|
||||
* empty Sting.
|
||||
* empty String.
|
||||
*/
|
||||
public class MultiselectInterceptor extends AbstractInterceptor {
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -37,7 +37,7 @@ public class MultiselectInterceptor extends AbstractInterceptor {
|
||||
/**
|
||||
* Just as the CheckboxInterceptor checks that if only the hidden field is present, so too does this interceptor.
|
||||
* If the "__multiselect_" request parameter is present and its visible counterpart is not, set a new request parameter
|
||||
* to an empty Sting.
|
||||
* to an empty String.
|
||||
*
|
||||
* @param ai ActionInvocation
|
||||
* @return the result of the action
|
||||
|
||||
+28
-4
@@ -112,6 +112,20 @@ public class TokenSessionStoreInterceptor extends TokenInterceptor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles processing of invalid tokens. If a previously stored invocation is
|
||||
* available, the method will attempt to return and render its result. Otherwise
|
||||
* it will return INVALID_TOKEN_CODE.
|
||||
*
|
||||
* Note: Because a stored (previously completed) invocation's PageContext will be closed,
|
||||
* this method must replace the stored PageContext with the current invocation's one (or a null).
|
||||
* See {@link org.apache.struts2.util.InvocationSessionStore#loadInvocation(String key, String token)} for details.
|
||||
*
|
||||
* @param invocation
|
||||
*
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected String handleInvalidToken(ActionInvocation invocation) throws Exception {
|
||||
ActionContext ac = invocation.getInvocationContext();
|
||||
@@ -126,11 +140,11 @@ public class TokenSessionStoreInterceptor extends TokenInterceptor {
|
||||
params.remove(tokenName);
|
||||
params.remove(TokenHelper.TOKEN_NAME_FIELD);
|
||||
|
||||
String sessionTokenName = TokenHelper.buildTokenSessionAttributeName(tokenName);
|
||||
String sessionTokenName = TokenHelper.buildTokenSessionAttributeName(tokenName);
|
||||
ActionInvocation savedInvocation = InvocationSessionStore.loadInvocation(sessionTokenName, token);
|
||||
|
||||
if (savedInvocation != null) {
|
||||
// set the valuestack to the request scope
|
||||
// set the savedInvocation's valuestack to the request scope
|
||||
ValueStack stack = savedInvocation.getStack();
|
||||
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
|
||||
|
||||
@@ -153,13 +167,23 @@ public class TokenSessionStoreInterceptor extends TokenInterceptor {
|
||||
return INVALID_TOKEN_CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles processing of valid tokens. Stores the current invocation for
|
||||
* later use by {@link handleInvalidToken}.
|
||||
* See {@link org.apache.struts2.util.InvocationSessionStore#storeInvocation(String key, String token, ActionInvocation invocation)} for details.
|
||||
*
|
||||
* @param invocation
|
||||
*
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected String handleValidToken(ActionInvocation invocation) throws Exception {
|
||||
// we know the token name and token must be there
|
||||
String key = TokenHelper.getTokenName();
|
||||
String token = TokenHelper.getToken(key);
|
||||
String sessionTokenName = TokenHelper.buildTokenSessionAttributeName(key);
|
||||
InvocationSessionStore.storeInvocation(sessionTokenName, token, invocation);
|
||||
String sessionTokenName = TokenHelper.buildTokenSessionAttributeName(key);
|
||||
InvocationSessionStore.storeInvocation(sessionTokenName, token, invocation);
|
||||
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ public class PostbackResult extends StrutsResultSupport {
|
||||
} else {
|
||||
String location = getLocation();
|
||||
// Do not prepend if the URL is a FQN
|
||||
if (!location.matches("^([a-zA-z]+:)?//.*")) {
|
||||
if (!location.matches("^([a-zA-Z]+:)?//.*")) {
|
||||
// If the URL is relative to the servlet context, prepend the servlet context path
|
||||
if (prependServletContext && (request.getContextPath() != null) && (request.getContextPath().length() > 0)) {
|
||||
location = request.getContextPath() + location;
|
||||
|
||||
@@ -250,13 +250,24 @@ public class ServletRedirectResult extends StrutsResultSupport implements Reflec
|
||||
* @throws IOException in case of IO errors
|
||||
*/
|
||||
protected void sendRedirect(HttpServletResponse response, String finalLocation) throws IOException {
|
||||
if (SC_FOUND == statusCode) {
|
||||
response.sendRedirect(finalLocation);
|
||||
} else {
|
||||
response.setStatus(statusCode);
|
||||
response.setHeader("Location", finalLocation);
|
||||
response.getWriter().write(finalLocation);
|
||||
response.getWriter().close();
|
||||
try {
|
||||
if (SC_FOUND == statusCode) {
|
||||
response.sendRedirect(finalLocation);
|
||||
} else {
|
||||
response.setStatus(statusCode);
|
||||
response.setHeader("Location", finalLocation);
|
||||
try {
|
||||
response.getWriter().write(finalLocation);
|
||||
} finally {
|
||||
response.getWriter().close();
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
LOG.warn("Unable to redirect to: {}, code: {}; {}", finalLocation, statusCode, ioe);
|
||||
throw ioe; // Re-throw required to preserve existing default behaviour (no stacktrace in above warn for this reason)
|
||||
} catch (IllegalStateException ise) {
|
||||
LOG.warn("Unable to redirect to: {}, code: {}; isCommited: {}; {}", finalLocation, statusCode, response.isCommitted(), ise);
|
||||
throw ise; // Re-throw required to preserve existing default behaviour (no stacktrace in above warn for this reason)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
package org.apache.struts2.result;
|
||||
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -100,6 +102,8 @@ public class StreamResult extends StrutsResultSupport {
|
||||
protected int bufferSize = 1024;
|
||||
protected boolean allowCaching = true;
|
||||
|
||||
private NotExcludedAcceptedPatternsChecker notExcludedAcceptedPatterns;
|
||||
|
||||
public StreamResult() {
|
||||
super();
|
||||
}
|
||||
@@ -115,6 +119,11 @@ public class StreamResult extends StrutsResultSupport {
|
||||
return allowCaching;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setNotExcludedAcceptedPatterns(NotExcludedAcceptedPatternsChecker notExcludedAcceptedPatterns) {
|
||||
this.notExcludedAcceptedPatterns = notExcludedAcceptedPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set allowCaching to <tt>false</tt> to indicate that the client should be requested not to cache the data stream.
|
||||
* This is set to <tt>false</tt> by default
|
||||
@@ -219,14 +228,17 @@ public class StreamResult extends StrutsResultSupport {
|
||||
OutputStream oOutput = null;
|
||||
|
||||
try {
|
||||
if (inputStream == null) {
|
||||
String parsedInputName = conditionalParse(inputName, invocation);
|
||||
boolean evaluated = parsedInputName != null && !parsedInputName.equals(inputName);
|
||||
boolean reevaluate = !evaluated || isAcceptableExpression(parsedInputName);
|
||||
if (inputStream == null && reevaluate) {
|
||||
LOG.debug("Find the inputstream from the invocation variable stack");
|
||||
inputStream = (InputStream) invocation.getStack().findValue(conditionalParse(inputName, invocation));
|
||||
inputStream = (InputStream) invocation.getStack().findValue(parsedInputName);
|
||||
}
|
||||
|
||||
if (inputStream == null) {
|
||||
String msg = ("Can not find a java.io.InputStream with the name [" + inputName + "] in the invocation stack. " +
|
||||
"Check the <param name=\"inputName\"> tag specified for this action.");
|
||||
String msg = ("Can not find a java.io.InputStream with the name [" + parsedInputName + "] in the invocation stack. " +
|
||||
"Check the <param name=\"inputName\"> tag specified for this action is correct, not excluded and accepted.");
|
||||
LOG.error(msg);
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
@@ -243,15 +255,16 @@ public class StreamResult extends StrutsResultSupport {
|
||||
|
||||
LOG.debug("Set the content length: {}", contentLength);
|
||||
if (contentLength != null) {
|
||||
String _contentLength = conditionalParse(contentLength, invocation);
|
||||
int _contentLengthAsInt;
|
||||
String translatedContentLength = conditionalParse(contentLength, invocation);
|
||||
int contentLengthAsInt;
|
||||
try {
|
||||
_contentLengthAsInt = Integer.parseInt(_contentLength);
|
||||
if (_contentLengthAsInt >= 0) {
|
||||
oResponse.setContentLength(_contentLengthAsInt);
|
||||
contentLengthAsInt = Integer.parseInt(translatedContentLength);
|
||||
if (contentLengthAsInt >= 0) {
|
||||
oResponse.setContentLength(contentLengthAsInt);
|
||||
}
|
||||
} catch(NumberFormatException e) {
|
||||
LOG.warn("failed to recognize {} as a number, contentLength header will not be set", _contentLength, e);
|
||||
LOG.warn("failed to recognize {} as a number, contentLength header will not be set",
|
||||
translatedContentLength, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,4 +305,22 @@ public class StreamResult extends StrutsResultSupport {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if expression doesn't contain vulnerable code
|
||||
*
|
||||
* @param expression of result
|
||||
* @return true|false
|
||||
* @since 2.5.27
|
||||
*/
|
||||
protected boolean isAcceptableExpression(String expression) {
|
||||
NotExcludedAcceptedPatternsChecker.IsAllowed isAllowed = notExcludedAcceptedPatterns.isAllowed(expression);
|
||||
if (isAllowed.isAllowed()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG.warn("Expression [{}] isn't allowed by pattern [{}]! See Accepted / Excluded patterns at\n" +
|
||||
"https://struts.apache.org/security/", expression, isAllowed.getAllowedPattern());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.apache.struts2.util;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
@@ -55,11 +56,20 @@ public class InvocationSessionStore {
|
||||
return null;
|
||||
}
|
||||
|
||||
ActionInvocation savedInvocation = null;
|
||||
if (invocationContext.invocation != null) {
|
||||
savedInvocation = invocationContext.invocation;
|
||||
ActionContext.setContext(savedInvocation.getInvocationContext());
|
||||
ActionContext.getContext().setValueStack(savedInvocation.getStack());
|
||||
final ActionInvocation savedInvocation = invocationContext.invocation;
|
||||
if (savedInvocation != null) {
|
||||
// WW-5026 - Preserve the previous PageContext (even if null) and restore it to the
|
||||
// ActionContext after loading the savedInvocation context. The saved context's PageContext
|
||||
// would already be closed at this point (causing failures if used for output).
|
||||
final ActionContext savedActionContext = savedInvocation.getInvocationContext();
|
||||
final ActionContext previousActionContext = ActionContext.getContext();
|
||||
ActionContext.setContext(savedActionContext);
|
||||
savedActionContext.setValueStack(savedInvocation.getStack());
|
||||
if (previousActionContext != null) {
|
||||
savedActionContext.put(ServletActionContext.PAGE_CONTEXT, previousActionContext.get(ServletActionContext.PAGE_CONTEXT));
|
||||
} else {
|
||||
savedActionContext.put(ServletActionContext.PAGE_CONTEXT, null);
|
||||
}
|
||||
}
|
||||
|
||||
return savedInvocation;
|
||||
|
||||
@@ -102,7 +102,7 @@ public class StrutsUtil {
|
||||
return responseWrapper.getData();
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.debug("Cannot include {}", aName.toString(), e);
|
||||
LOG.debug("Cannot include {}", aName, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@@ -125,7 +125,7 @@ public class StrutsUtil {
|
||||
}
|
||||
|
||||
public String getText(String text) {
|
||||
return (String) stack.findValue("getText('" + text + "')");
|
||||
return (String) stack.findValue("getText('" + text.replace('\'', '"') + "')");
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -68,9 +68,13 @@ public class SetTag extends ContextBeanTag {
|
||||
@Override
|
||||
protected String getBody() {
|
||||
if (trimBody) {
|
||||
return super.getBody();
|
||||
if (bodyContent == null) {
|
||||
return null;
|
||||
} else {
|
||||
return bodyContent.getString().trim();
|
||||
}
|
||||
} else {
|
||||
return (bodyContent == null ? "" : bodyContent.getString());
|
||||
return (bodyContent == null ? null : bodyContent.getString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,7 @@
|
||||
*/
|
||||
package org.apache.struts2.views.jsp.ui;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.struts2.components.UIBean;
|
||||
import org.apache.struts2.util.ComponentUtils;
|
||||
import org.apache.struts2.views.jsp.ComponentTagSupport;
|
||||
|
||||
import javax.servlet.jsp.JspException;
|
||||
@@ -32,6 +30,7 @@ import java.util.Map;
|
||||
* Abstract base class for all UI tags.
|
||||
*/
|
||||
public abstract class AbstractUITag extends ComponentTagSupport implements DynamicAttributes {
|
||||
|
||||
protected String cssClass;
|
||||
protected String cssErrorClass;
|
||||
protected String cssStyle;
|
||||
@@ -40,7 +39,9 @@ public abstract class AbstractUITag extends ComponentTagSupport implements Dynam
|
||||
protected String disabled;
|
||||
protected String label;
|
||||
protected String labelSeparator;
|
||||
@Deprecated
|
||||
protected String labelposition;
|
||||
protected String labelPosition;
|
||||
protected String requiredPosition;
|
||||
protected String errorPosition;
|
||||
protected String name;
|
||||
@@ -78,7 +79,7 @@ public abstract class AbstractUITag extends ComponentTagSupport implements Dynam
|
||||
protected String tooltipIconPath;
|
||||
|
||||
// dynamic attributes.
|
||||
protected Map<String, Object> dynamicAttributes = new HashMap<>();
|
||||
protected Map<String, String> dynamicAttributes = new HashMap<>();
|
||||
|
||||
protected void populateParams() {
|
||||
super.populateParams();
|
||||
@@ -92,7 +93,11 @@ public abstract class AbstractUITag extends ComponentTagSupport implements Dynam
|
||||
uiBean.setDisabled(disabled);
|
||||
uiBean.setLabel(label);
|
||||
uiBean.setLabelSeparator(labelSeparator);
|
||||
uiBean.setLabelposition(labelposition);
|
||||
if (labelposition != null && labelPosition == null) {
|
||||
uiBean.setLabelposition(labelposition);
|
||||
} else {
|
||||
uiBean.setLabelPosition(labelPosition);
|
||||
}
|
||||
uiBean.setRequiredPosition(requiredPosition);
|
||||
uiBean.setErrorPosition(errorPosition);
|
||||
uiBean.setName(name);
|
||||
@@ -137,6 +142,10 @@ public abstract class AbstractUITag extends ComponentTagSupport implements Dynam
|
||||
this.cssClass = cssClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #setCssClass(String)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void setClass(String cssClass) {
|
||||
this.cssClass = cssClass;
|
||||
}
|
||||
@@ -169,8 +178,17 @@ public abstract class AbstractUITag extends ComponentTagSupport implements Dynam
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public void setLabelposition(String labelPosition) {
|
||||
this.labelposition = labelPosition;
|
||||
/**
|
||||
* Deprecated since 2.5.27
|
||||
* @deprecated use {@link #setLabelPosition(String)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void setLabelposition(String labelposition) {
|
||||
this.labelposition = labelposition;
|
||||
}
|
||||
|
||||
public void setLabelPosition(String labelPosition) {
|
||||
this.labelPosition = labelPosition;
|
||||
}
|
||||
|
||||
public void setRequiredPosition(String requiredPosition) {
|
||||
@@ -302,11 +320,7 @@ public abstract class AbstractUITag extends ComponentTagSupport implements Dynam
|
||||
}
|
||||
|
||||
public void setDynamicAttribute(String uri, String localName, Object value) throws JspException {
|
||||
if (ComponentUtils.altSyntax(getStack()) && ComponentUtils.isExpression(value.toString())) {
|
||||
dynamicAttributes.put(localName, String.valueOf(ObjectUtils.defaultIfNull(findValue(value.toString()), value)));
|
||||
} else {
|
||||
dynamicAttributes.put(localName, value);
|
||||
}
|
||||
dynamicAttributes.put(localName, String.valueOf(value));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,13 +18,12 @@
|
||||
*/
|
||||
package org.apache.struts2.views.jsp.ui;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import org.apache.struts2.components.Checkbox;
|
||||
import org.apache.struts2.components.Component;
|
||||
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @see Checkbox
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user