You can subscribe to this list here.
2009 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(20) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2010 |
Jan
|
Feb
|
Mar
|
Apr
(1) |
May
(3) |
Jun
(1) |
Jul
(1) |
Aug
(3) |
Sep
(6) |
Oct
(1) |
Nov
|
Dec
(4) |
2011 |
Jan
|
Feb
|
Mar
|
Apr
(1) |
May
(1) |
Jun
|
Jul
|
Aug
|
Sep
(2) |
Oct
(4) |
Nov
|
Dec
|
2021 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
(14) |
Sep
(9) |
Oct
(9) |
Nov
(15) |
Dec
(5) |
2022 |
Jan
(3) |
Feb
(13) |
Mar
(2) |
Apr
|
May
(3) |
Jun
(4) |
Jul
(4) |
Aug
(1) |
Sep
|
Oct
|
Nov
(7) |
Dec
(3) |
2023 |
Jan
(1) |
Feb
(3) |
Mar
(1) |
Apr
(4) |
May
(1) |
Jun
(5) |
Jul
(3) |
Aug
|
Sep
|
Oct
(1) |
Nov
|
Dec
|
2024 |
Jan
(1) |
Feb
|
Mar
(1) |
Apr
|
May
|
Jun
(3) |
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <udi...@us...> - 2021-11-10 14:41:33
|
Revision: 1414 http://sourceforge.net/p/j-trac/code/1414 Author: udittmer Date: 2021-11-10 14:41:31 +0000 (Wed, 10 Nov 2021) Log Message: ----------- update dependencies Modified Paths: -------------- trunk/jtrac/pom.xml Modified: trunk/jtrac/pom.xml =================================================================== --- trunk/jtrac/pom.xml 2021-11-10 12:09:56 UTC (rev 1413) +++ trunk/jtrac/pom.xml 2021-11-10 14:41:31 UTC (rev 1414) @@ -347,9 +347,8 @@ <dependency> <groupId>org.apache.wicket</groupId> <artifactId>wicket-extensions</artifactId> - <version>1.3.4</version> + <version>1.3.7</version> <!-- - 1.3.7 has bug with the YUI Calendar <version>1.4.23</version> <version>1.5.16</version> --> @@ -369,7 +368,7 @@ <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> - <version>4.1.2</version> + <version>5.1.0</version> </dependency> <dependency> <groupId>org.tmate</groupId> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-11-10 12:09:59
|
Revision: 1413 http://sourceforge.net/p/j-trac/code/1413 Author: udittmer Date: 2021-11-10 12:09:56 +0000 (Wed, 10 Nov 2021) Log Message: ----------- finetuning; resurrect doc build system Modified Paths: -------------- trunk/jtrac/doc/build.xml trunk/jtrac/doc/src/index.xml trunk/jtrac/doc/styles/fopdf.xsl trunk/jtrac/doc/styles/html.xsl trunk/jtrac/doc/styles/html_chunk.xsl trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.html trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java Modified: trunk/jtrac/doc/build.xml =================================================================== --- trunk/jtrac/doc/build.xml 2021-11-10 09:37:33 UTC (rev 1412) +++ trunk/jtrac/doc/build.xml 2021-11-10 12:09:56 UTC (rev 1413) @@ -1,83 +1,83 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<project name="jtrac-doc" basedir=".." default="doc"> - - <property name="doc.src.dir" value="doc"/> - <property name="dist.doc.dir" value="target/site/doc"/> - - <target name="doc" depends="clean, doc-html, doc-html-single, doc-pdf"/> - - <target name="clean"> - <delete includeemptydirs="true" failonerror="false"> - <fileset dir="${dist.doc.dir}"/> - </delete> - </target> - - <target name="copy-images"> - <mkdir dir="${dist.doc.dir}/images"/> - <copy todir="${dist.doc.dir}/images"> - <fileset dir="${doc.src.dir}/src/images"/> - </copy> - <mkdir dir="${dist.doc.dir}/styles"/> - <copy todir="${dist.doc.dir}/styles"> - <fileset dir="${doc.src.dir}/styles" includes="*.css"/> - </copy> - </target> - - <target name="doc-pdf" depends="copy-images"> - <mkdir dir="${dist.doc.dir}/pdf"/> - <java classname="com.icl.saxon.StyleSheet" fork="true"> - <classpath> - <fileset dir="${doc.src.dir}/lib"> - <include name="**/*.jar"/> - </fileset> - </classpath> - <jvmarg value="-Xmx256M"/> - <arg value="-o"/> - <arg value="${dist.doc.dir}/pdf/docbook_fop.tmp"/> - <arg value="${doc.src.dir}/src/index.xml"/> - <arg value="${doc.src.dir}/styles/fopdf.xsl"/> - </java> - <java classname="org.apache.fop.apps.Fop" fork="true" maxmemory="256m"> - <classpath> - <fileset dir="${doc.src.dir}/lib"> - <include name="**/*.jar"/> - </fileset> - </classpath> - <arg value="${dist.doc.dir}/pdf/docbook_fop.tmp"/> - <arg value="${dist.doc.dir}/pdf/jtrac-doc.pdf"/> - </java> - <delete file="${dist.doc.dir}/pdf/docbook_fop.tmp"/> - </target> - - <target name="doc-html" depends="copy-images"> - <mkdir dir="${dist.doc.dir}/html"/> - <java classname="com.icl.saxon.StyleSheet" fork="true" dir="${dist.doc.dir}/html"> - <classpath> - <fileset dir="${doc.src.dir}/lib"> - <include name="**/*.jar"/> - </fileset> - </classpath> - <jvmarg value="-Xmx256M"/> - <arg value="${basedir}/${doc.src.dir}/src/index.xml"/> - <arg value="${basedir}/${doc.src.dir}/styles/html_chunk.xsl"/> - </java> - </target> - - <target name="doc-html-single" depends="copy-images"> - <mkdir dir="${dist.doc.dir}/html-single"/> - <java classname="com.icl.saxon.StyleSheet" fork="true"> - <classpath> - <fileset dir="${doc.src.dir}/lib"> - <include name="**/*.jar"/> - </fileset> - </classpath> - <jvmarg value="-Xmx256M"/> - <arg value="-o"/> - <arg value="${dist.doc.dir}/html-single/index.html"/> - <arg value="${doc.src.dir}/src/index.xml"/> - <arg value="${doc.src.dir}/styles/html.xsl"/> - </java> - </target> - -</project> \ No newline at end of file +<?xml version="1.0" encoding="UTF-8"?> + +<project name="jtrac-doc" basedir=".." default="doc"> + + <property name="doc.src.dir" value="doc"/> + <property name="dist.doc.dir" value="target/site/doc"/> + + <target name="doc" depends="clean, doc-html, doc-html-single, doc-pdf"/> + + <target name="clean"> + <delete includeemptydirs="true" failonerror="false"> + <fileset dir="${dist.doc.dir}"/> + </delete> + </target> + + <target name="copy-images"> + <mkdir dir="${dist.doc.dir}/images"/> + <copy todir="${dist.doc.dir}/images"> + <fileset dir="${doc.src.dir}/src/images"/> + </copy> + <mkdir dir="${dist.doc.dir}/styles"/> + <copy todir="${dist.doc.dir}/styles"> + <fileset dir="${doc.src.dir}/styles" includes="*.css"/> + </copy> + </target> + + <target name="doc-pdf" depends="copy-images"> + <mkdir dir="${dist.doc.dir}/pdf"/> + <java classname="com.icl.saxon.StyleSheet" fork="true"> + <classpath> + <fileset dir="${doc.src.dir}/lib"> + <include name="**/*.jar"/> + </fileset> + </classpath> + <jvmarg value="-Xmx256M"/> + <arg value="-o"/> + <arg value="${dist.doc.dir}/pdf/docbook_fop.tmp"/> + <arg value="${doc.src.dir}/src/index.xml"/> + <arg value="${doc.src.dir}/styles/fopdf.xsl"/> + </java> + <java classname="org.apache.fop.cli.Main" fork="true" maxmemory="256m"> + <classpath> + <fileset dir="${doc.src.dir}/lib"> + <include name="**/*.jar"/> + </fileset> + </classpath> + <arg value="${dist.doc.dir}/pdf/docbook_fop.tmp"/> + <arg value="${dist.doc.dir}/pdf/jtrac-doc.pdf"/> + </java> + <delete file="${dist.doc.dir}/pdf/docbook_fop.tmp"/> + </target> + + <target name="doc-html" depends="copy-images"> + <mkdir dir="${dist.doc.dir}/html"/> + <java classname="com.icl.saxon.StyleSheet" fork="true" dir="${dist.doc.dir}/html"> + <classpath> + <fileset dir="${doc.src.dir}/lib"> + <include name="**/*.jar"/> + </fileset> + </classpath> + <jvmarg value="-Xmx256M"/> + <arg value="${basedir}/${doc.src.dir}/src/index.xml"/> + <arg value="${basedir}/${doc.src.dir}/styles/html_chunk.xsl"/> + </java> + </target> + + <target name="doc-html-single" depends="copy-images"> + <mkdir dir="${dist.doc.dir}/html-single"/> + <java classname="com.icl.saxon.StyleSheet" fork="true"> + <classpath> + <fileset dir="${doc.src.dir}/lib"> + <include name="**/*.jar"/> + </fileset> + </classpath> + <jvmarg value="-Xmx256M"/> + <arg value="-o"/> + <arg value="${dist.doc.dir}/html-single/index.html"/> + <arg value="${doc.src.dir}/src/index.xml"/> + <arg value="${doc.src.dir}/styles/html.xsl"/> + </java> + </target> + +</project> Modified: trunk/jtrac/doc/src/index.xml =================================================================== --- trunk/jtrac/doc/src/index.xml 2021-11-10 09:37:33 UTC (rev 1412) +++ trunk/jtrac/doc/src/index.xml 2021-11-10 12:09:56 UTC (rev 1413) @@ -18,7 +18,7 @@ <surname>Thomas</surname> </author> <author> - <firstname>Ul</firstname> + <firstname>Ulf</firstname> <surname>Dittmer</surname> </author> </authorgroup> @@ -68,9 +68,11 @@ but as of early 2007 (version 2.1.0 onwards) JTrac <ulink url="https://ptrthomas.wordpress.com/2007/03/02/wicket-impressions-moving-from-spring-mvc-webflow/">switched to </ulink> using the <ulink url="https://wicket.apache.org/">Apache Wicket</ulink> framework. + After being dormant for a long time, JTrac development picked up again in 2021, and a new version 2.2 was + released that moved the Java baseline to Java 8. </para> <para> - JTrac is extremely easy to install and the only pre-requisite is a Java 6 (or higher) Runtime Environment. + JTrac is extremely easy to install and the only pre-requisite is a Java 8 (or higher) Runtime Environment. You can be up and running in seconds because JTrac comes bundled with a small-footprint web-application server called <ulink url="https://www.eclipse.org/jetty/">Jetty</ulink> and an embedded database called <ulink url="https://www.hsqldb.org/">HSQLDB</ulink>. Please refer to the @@ -125,7 +127,7 @@ <para> One of the useful features of JTrac is that it is distributed with an embedded web-application server (<ulink url="https://www.eclipse.org/jetty/">Jetty</ulink>) that has a very small footprint. - If you have Java 5 installed you can be up and running after downloading and extracting JTrac - by + If you have Java 8 installed you can be up and running after downloading and extracting JTrac - by simply using the provided start and stop scripts. JTrac scans its environment on startup and if a configured database is not detected, <ulink url="https://www.hsqldb.org/">HSQLDB</ulink> is used by default. </para> Modified: trunk/jtrac/doc/styles/fopdf.xsl =================================================================== --- trunk/jtrac/doc/styles/fopdf.xsl 2021-11-10 09:37:33 UTC (rev 1412) +++ trunk/jtrac/doc/styles/fopdf.xsl 2021-11-10 12:09:56 UTC (rev 1413) @@ -15,7 +15,7 @@ --> <!DOCTYPE xsl:stylesheet [ - <!ENTITY db_xsl_path "../lib/docbook-xsl/"> + <!ENTITY db_xsl_path "http://docbook.sourceforge.net/release/xsl/current/"> <!ENTITY admon_gfx_path "../images/admons/"> <!ENTITY copyright "©"> ]> Modified: trunk/jtrac/doc/styles/html.xsl =================================================================== --- trunk/jtrac/doc/styles/html.xsl 2021-11-10 09:37:33 UTC (rev 1412) +++ trunk/jtrac/doc/styles/html.xsl 2021-11-10 12:09:56 UTC (rev 1413) @@ -14,7 +14,7 @@ --> <!DOCTYPE xsl:stylesheet [ - <!ENTITY db_xsl_path "../lib/docbook-xsl/"> + <!ENTITY db_xsl_path "http://docbook.sourceforge.net/release/xsl/current/"> <!ENTITY callout_gfx_path "../images/callouts/"> <!ENTITY admon_gfx_path "../images/admons/"> ]> Modified: trunk/jtrac/doc/styles/html_chunk.xsl =================================================================== --- trunk/jtrac/doc/styles/html_chunk.xsl 2021-11-10 09:37:33 UTC (rev 1412) +++ trunk/jtrac/doc/styles/html_chunk.xsl 2021-11-10 12:09:56 UTC (rev 1413) @@ -15,7 +15,7 @@ --> <!DOCTYPE xsl:stylesheet [ - <!ENTITY db_xsl_path "../lib/docbook-xsl/"> + <!ENTITY db_xsl_path "http://docbook.sourceforge.net/release/xsl/current/"> <!ENTITY callout_gfx_path "../images/callouts/"> <!ENTITY admon_gfx_path "../images/admons/"> ]> Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.html =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.html 2021-11-10 09:37:33 UTC (rev 1412) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.html 2021-11-10 12:09:56 UTC (rev 1413) @@ -16,7 +16,7 @@ <a href="#" wicket:id="cancel"><img src="resources/cancel.gif" class="nav-link"/><wicket:message key="cancel"/></a> </form> <wicket:fragment wicket:id="textField"><input wicket:id="value"/></wicket:fragment> - <wicket:fragment wicket:id="numberField"><input type="number" min="0" wicket:id="value"/></wicket:fragment> + <wicket:fragment wicket:id="numberField"><input type="number" min="0" size="8" wicket:id="value"/></wicket:fragment> <wicket:fragment wicket:id="booleanField"><input type="hidden" wicket:id="value" id="valueField"/><div class="switch"></div></wicket:fragment> </wicket:extend> </body> Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java 2021-11-10 09:37:33 UTC (rev 1412) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java 2021-11-10 12:09:56 UTC (rev 1413) @@ -230,7 +230,7 @@ } }; if (ch.getNameText().equals("lastChanged")) { - // TODO: we can't sort by that column + // TODO: we can't sort by that column, so don't make it look like a link headingLink.add(new SimpleAttributeModifier("onclick", "")); } listItem.add(headingLink); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-11-10 09:37:37
|
Revision: 1412 http://sourceforge.net/p/j-trac/code/1412 Author: udittmer Date: 2021-11-10 09:37:33 +0000 (Wed, 10 Nov 2021) Log Message: ----------- removed unneeded resources, code improvements Modified Paths: -------------- trunk/jtrac/src/main/java/info/jtrac/domain/AbstractItem.java trunk/jtrac/src/main/java/info/jtrac/util/DateUtils.java trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.html trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.java trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java Added Paths: ----------- trunk/jtrac/src/main/webapp/resources/jquery-3.6.0.min.js trunk/jtrac/src/main/webapp/resources/jquery.enhanced-switch-pingpong.css trunk/jtrac/src/main/webapp/resources/jquery.enhanced-switch.js Removed Paths: ------------- trunk/jtrac/src/main/webapp/resources/yui/calendar/ Modified: trunk/jtrac/src/main/java/info/jtrac/domain/AbstractItem.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/domain/AbstractItem.java 2021-11-09 15:31:00 UTC (rev 1411) +++ trunk/jtrac/src/main/java/info/jtrac/domain/AbstractItem.java 2021-11-10 09:37:33 UTC (rev 1412) @@ -148,7 +148,7 @@ return ""; } if (o instanceof Date) { - return DateUtils.format((Date) o); + return DateUtils.formatTimeStamp((Date) o); } return o.toString(); } Modified: trunk/jtrac/src/main/java/info/jtrac/util/DateUtils.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/util/DateUtils.java 2021-11-09 15:31:00 UTC (rev 1411) +++ trunk/jtrac/src/main/java/info/jtrac/util/DateUtils.java 2021-11-10 09:37:33 UTC (rev 1412) @@ -60,9 +60,6 @@ return date == null ? "" : dateFormat.format(date); -// : (showPretty() -// ? getPrettyTime().format(date) -// : dateFormat.format(date)); } public static String formatTimeStamp (Date date) { Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.html =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.html 2021-11-09 15:31:00 UTC (rev 1411) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.html 2021-11-10 09:37:33 UTC (rev 1412) @@ -6,12 +6,8 @@ <table class="jtrac"> <tr> <td wicket:id="param" class="label"></td> + <td wicket:id="field"></td> <td> - <input wicket:id="value" id="valueField"/> - <input type="number" wicket:id="number" min="0" id="numberField" style="display: none;"/> - <div class="switch" style="margin-left: auto; margin-right: auto;"></div> - </td> - <td> <input type="submit" wicket:message="value:submit"/> </td> </tr> @@ -19,6 +15,9 @@ <p/> <a href="#" wicket:id="cancel"><img src="resources/cancel.gif" class="nav-link"/><wicket:message key="cancel"/></a> </form> + <wicket:fragment wicket:id="textField"><input wicket:id="value"/></wicket:fragment> + <wicket:fragment wicket:id="numberField"><input type="number" min="0" wicket:id="value"/></wicket:fragment> + <wicket:fragment wicket:id="booleanField"><input type="hidden" wicket:id="value" id="valueField"/><div class="switch"></div></wicket:fragment> </wicket:extend> </body> </html> Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.java 2021-11-09 15:31:00 UTC (rev 1411) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.java 2021-11-10 09:37:33 UTC (rev 1412) @@ -27,6 +27,7 @@ import org.apache.wicket.markup.html.form.NumberField; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.link.Link; +import org.apache.wicket.markup.html.panel.Fragment; import org.apache.wicket.model.BoundCompoundPropertyModel; /** @@ -50,7 +51,6 @@ private String param; private String value; - private String number; private boolean isBoolean; private boolean isNumber; @@ -57,18 +57,20 @@ public String getValue() { return value; } public void setValue (String value) { this.value = value; } - public String getNumber() { return number; } - public void setNumber (String number) { this.number = number; } - public ConfigForm (String id, final String param, final String value) { super(id); this.param = param; this.value = value; - this.number = value; this.isBoolean = Config.isBoolean(param); this.isNumber = Config.isNumber(param); + final BoundCompoundPropertyModel model = new BoundCompoundPropertyModel(this); + setModel(model); + + add(new Label("heading", localize("config." + param))); + add(new Label("param", param)); + // boolean settings use a JavaScript switch instead of a text field if (isBoolean) { // use the "switch" div instead of the value field @@ -79,7 +81,6 @@ public void renderHead(IHeaderResponse response) { String js = "$('.switch').enhancedSwitch();\n" + ((value!=null && value.equals("true")) ? "$('.switch').enhancedSwitch('setTrue');\n" : "") + - "$('#valueField').hide();\n" + "$('.switch').click(function() {\n" + " var selectedSwitch = $(this);\n" + " selectedSwitch.enhancedSwitch('toggle');\n" + @@ -89,27 +90,26 @@ response.renderOnDomReadyJavascript(js); } })); + + Fragment f = new Fragment("field", "booleanField", ConfigFormPage.this); + HiddenField hiddenField = new HiddenField("value"); + f.add(model.bind(hiddenField)); + add(f); } else if (isNumber) { - // use the number field instead of the value field // only difference is type="number" instead of type="text" - add(HeaderContributor.forJavaScript("resources/jquery-3.6.0.min.js")); - add(new HeaderContributor(new IHeaderContributor() { - public void renderHead(IHeaderResponse response) { - String js = "$('#valueField').hide();" - + "$('#numberField').show();"; - response.renderOnDomReadyJavascript(js); - } - })); + Fragment f = new Fragment("field", "numberField", ConfigFormPage.this); + NumberField numberField = new NumberField("value"); + f.add(model.bind(numberField)); + add(f); + + } else { + // regular text input field + Fragment f = new Fragment("field", "textField", ConfigFormPage.this); + TextField textField = new TextField("value"); + f.add(model.bind(textField)); + add(f); } - final BoundCompoundPropertyModel model = new BoundCompoundPropertyModel(this); - setModel(model); - - add(new Label("heading", localize("config." + param))); - add(new Label("param", param)); - add(new TextField("value")); - add(new NumberField("number")); - // cancel ========================================================== add(new Link("cancel") { public void onClick() { @@ -120,10 +120,7 @@ @Override protected void onSubmit() { - if (isNumber) - getJtrac().storeConfig(new Config(param, number)); - else - getJtrac().storeConfig(new Config(param, value)); + getJtrac().storeConfig(new Config(param, value)); setResponsePage(new ConfigListPage(param)); } } Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java 2021-11-09 15:31:00 UTC (rev 1411) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java 2021-11-10 09:37:33 UTC (rev 1412) @@ -226,12 +226,13 @@ final ColumnHeading ch = (ColumnHeading) listItem.getModelObject(); Link headingLink = new Link("heading") { public void onClick() { - // hack: it looks like a link, but clicking it does nothing - if (! ch.getNameText().equals("lastChanged")) { - doSort(ch.getNameText()); - } + doSort(ch.getNameText()); } }; + if (ch.getNameText().equals("lastChanged")) { + // TODO: we can't sort by that column + headingLink.add(new SimpleAttributeModifier("onclick", "")); + } listItem.add(headingLink); String label = ch.isField() ? ch.getLabel() : localize("item_list." + ch.getName()); headingLink.add(new Label("heading", label)); Added: trunk/jtrac/src/main/webapp/resources/jquery-3.6.0.min.js =================================================================== --- trunk/jtrac/src/main/webapp/resources/jquery-3.6.0.min.js (rev 0) +++ trunk/jtrac/src/main/webapp/resources/jquery-3.6.0.min.js 2021-11-10 09:37:33 UTC (rev 1412) @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),j=function(e,t){return e===t&&(l=!0),0},D={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&D.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(j),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(B," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split("").sort(j).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var D,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",y.option=!!ce.lastChild;var ge={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^([^.]*)(?:\.(.+)|)/;function we(){return!0}function Te(){return!1}function Ce(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ee(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ee(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Te;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Se(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n&&n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,we)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=be.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=be.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Se(t,"click",we),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Se(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?we:Te,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Te,isPropagationStopped:Te,isImmediatePropagationStopped:Te,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=we,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=we,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=we,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:!0},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(e,t){S.event.special[e]={setup:function(){return Se(this,e,Ce),!1},trigger:function(){return Se(this,e),!0},_default:function(){return!0},delegateType:t}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return Ee(this,e,t,n,r)},one:function(e,t,n,r){return Ee(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Te),this.each(function(){S.event.remove(this,e,n,t)})}});var ke=/<script|<style|<link/i,Ae=/checked\s*(?:[^=]|=\s*.checked.)/i,Ne=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function He(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&Ae.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),He(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,"script"),De)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,qe),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(Ne,""),u,l))}return n}function Oe(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Le(o[r],a[r]);else Le(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Oe(this,e,!0)},remove:function(e){return Oe(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return He(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||je(this,e).appendChild(e)})},prepend:function(){return He(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=je(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return He(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return He(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!ke.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return He(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o... [truncated message content] |
From: <udi...@us...> - 2021-11-09 15:31:02
|
Revision: 1411 http://sourceforge.net/p/j-trac/code/1411 Author: udittmer Date: 2021-11-09 15:31:00 +0000 (Tue, 09 Nov 2021) Log Message: ----------- auto-link ticket numbers like ROUTE-66 Modified Paths: -------------- trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewPanel.java Modified: trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java 2021-11-09 14:17:14 UTC (rev 1410) +++ trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java 2021-11-09 15:31:00 UTC (rev 1411) @@ -153,6 +153,19 @@ } } + // don't link if there's a slash immediately before the item; ignore case, so item-44 is the same as ITEM-44 + public static String autolinkTickets (String baseUrl, String text, Set<Space> spaces) { + if (text == null) { + return null; + } else { + for (Space space : spaces) { + text = text.replaceAll("(?i)(?<!/)"+space.getPrefixCode()+"-[0-9]+", + "<a href='"+(baseUrl==null ? "/" : baseUrl)+"app/item/$0' target='_blank'>$0</a>"); + } + return text; + } + } + private static String fmt(String key, MessageSource messageSource, Locale locale) { try { return messageSource.getMessage("item_view." + key, null, locale); Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewPanel.java 2021-11-09 14:17:14 UTC (rev 1410) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewPanel.java 2021-11-09 15:31:00 UTC (rev 1411) @@ -79,6 +79,8 @@ * @param item The {@link Item} to add. */ private void addComponents(final Item item) { + Map<String, String> configMap = getJtrac().loadAllConfig(); + String baseUrl = configMap.get("jtrac.url.base"); add(new Label("refId", new PropertyModel(item, "refId"))); @@ -191,6 +193,7 @@ add(new Label("summary", new PropertyModel(item, "summary"))); String text = item.getDetail(); text = ItemUtils.renderMarkdown(text); + text = ItemUtils.autolinkTickets(baseUrl, text, getPrincipal().getSpaces()); add(new Label("detail", text).setEscapeModelStrings(false)); final SimpleAttributeModifier sam = new SimpleAttributeModifier("class", "alt"); @@ -243,6 +246,7 @@ comment.add(new AttachmentLinkPanel("attachment", h.getAttachment())); String text = h.getComment(); text = ItemUtils.renderMarkdown(text); + text = ItemUtils.autolinkTickets(baseUrl, text, getPrincipal().getSpaces()); comment.add(new Label("comment", text).setEscapeModelStrings(false)); listItem.add(comment); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-11-09 14:17:17
|
Revision: 1410 http://sourceforge.net/p/j-trac/code/1410 Author: udittmer Date: 2021-11-09 14:17:14 +0000 (Tue, 09 Nov 2021) Log Message: ----------- replaced YUI calendar with input type={U+201C}date{U+201D} Modified Paths: -------------- trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java trunk/jtrac/src/main/java/info/jtrac/wicket/CustomFieldsFormPanel.html trunk/jtrac/src/main/java/info/jtrac/wicket/CustomFieldsFormPanel.java trunk/jtrac/src/main/java/info/jtrac/wicket/ItemSearchFormPanel.html trunk/jtrac/src/main/java/org/apache/wicket/markup/html/form/DateField.java Removed Paths: ------------- trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiCalendar.html trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiCalendar.java Modified: trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java 2021-11-09 09:54:13 UTC (rev 1409) +++ trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java 2021-11-09 14:17:14 UTC (rev 1410) @@ -20,7 +20,6 @@ import info.jtrac.domain.FilterCriteria.Expression; import info.jtrac.util.DateUtils; import info.jtrac.wicket.JtracCheckBoxMultipleChoice; -import info.jtrac.wicket.yui.YuiCalendar; import static info.jtrac.domain.ColumnHeading.Name.*; import static info.jtrac.domain.FilterCriteria.Expression.*; @@ -34,10 +33,12 @@ import org.apache.wicket.MarkupContainer; import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.form.DateField; import org.apache.wicket.markup.html.form.IChoiceRenderer; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.panel.Fragment; import org.apache.wicket.model.PropertyModel; + import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.MatchMode; import org.hibernate.criterion.Restrictions; @@ -319,11 +320,12 @@ } Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { Fragment fragment = new Fragment("fragParent", "dateField", container); - YuiCalendar calendar = new YuiCalendar("value", new PropertyModel(filterCriteria, "value"), false); - fragment.add(calendar); - if(filterCriteria.getExpression() == BETWEEN) { - YuiCalendar calendar2 = new YuiCalendar("value2", new PropertyModel(filterCriteria, "value2"), false); - fragment.add(calendar2); + DateField dateField = new DateField("value", new PropertyModel(filterCriteria, "value")); + fragment.add(dateField); + + if (filterCriteria.getExpression() == BETWEEN) { + DateField dateField2 = new DateField("value2", new PropertyModel(filterCriteria, "value2")); + fragment.add(dateField2); } else { fragment.add(new WebMarkupContainer("value2").setVisible(false)); } @@ -525,15 +527,15 @@ return getAsList(BETWEEN, GT, LT); } Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { - Fragment fragment = new Fragment("fragParent", "dateField", container); - YuiCalendar calendar = new YuiCalendar("value", new PropertyModel(filterCriteria, "value"), false); - fragment.add(calendar); - if(filterCriteria.getExpression() == BETWEEN) { - YuiCalendar calendar2 = new YuiCalendar("value2", new PropertyModel(filterCriteria, "value2"), false); - fragment.add(calendar2); - } else { + Fragment fragment = new Fragment("fragParent", "dateField", container); + DateField dateField = new DateField("value", new PropertyModel(filterCriteria, "value")); + fragment.add(dateField); + if (filterCriteria.getExpression() == BETWEEN) { + DateField dateField2 = new DateField("value2", new PropertyModel(filterCriteria, "value2")); + fragment.add(dateField2); + } else { fragment.add(new WebMarkupContainer("value2").setVisible(false)); - } + } return fragment; } void addRestrictions(DetachedCriteria criteria) { Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/CustomFieldsFormPanel.html =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/CustomFieldsFormPanel.html 2021-11-09 09:54:13 UTC (rev 1409) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/CustomFieldsFormPanel.html 2021-11-09 14:17:14 UTC (rev 1410) @@ -10,4 +10,5 @@ </wicket:panel> <wicket:fragment wicket:id="dropDown"><div wicket:id="border"><select wicket:id="field"/></div></wicket:fragment> <wicket:fragment wicket:id="textField"><input wicket:id="field"/></wicket:fragment> -</body> \ No newline at end of file + <wicket:fragment wicket:id="dateField"><input type="date" wicket:id="field"/></wicket:fragment> +</body> Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/CustomFieldsFormPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/CustomFieldsFormPanel.java 2021-11-09 09:54:13 UTC (rev 1409) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/CustomFieldsFormPanel.java 2021-11-09 14:17:14 UTC (rev 1410) @@ -20,7 +20,6 @@ import info.jtrac.domain.Item; import info.jtrac.domain.Space; import info.jtrac.domain.User; -import info.jtrac.wicket.yui.YuiCalendar; import java.util.ArrayList; import java.util.Date; @@ -39,6 +38,9 @@ import org.apache.wicket.model.BoundCompoundPropertyModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; +import org.apache.wicket.util.convert.ConversionException; +import org.apache.wicket.util.convert.IConverter; +import org.apache.wicket.util.convert.converters.AbstractConverter; import org.apache.wicket.util.convert.converters.DoubleConverter; /** @@ -119,11 +121,13 @@ * Date picker * ====================================== */ - YuiCalendar calendar = new YuiCalendar("field", new PropertyModel(model, field.getName().getText()), isRequired); - calendar.setLabel(new Model(field.getLabel())); - listItem.add(calendar); - // TODO: replace YuiCalendar with DateField - //listItem.add(new DateField("field", new PropertyModel(model, field.getName().getText()), Date.class, isRequired)); + Fragment f = new Fragment("field", "dateField", CustomFieldsFormPanel.this); + DateField dateField = new DateField("field"); + dateField.setRequired(isRequired); + dateField.setLabel(new Model(field.getLabel())); + dateField.add(new ErrorHighlighter()); + f.add(model.bind(dateField, field.getName().getText())); + listItem.add(f); } else { /* * ====================================== @@ -134,8 +138,7 @@ TextField textField = new TextField("field"); /* - * Check if the field is used to display/edit - * Double values. + * Check if the field is used to display/edit Double values. */ if (field.isDecimalNumberType()) { /* Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ItemSearchFormPanel.html =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ItemSearchFormPanel.html 2021-11-09 09:54:13 UTC (rev 1409) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ItemSearchFormPanel.html 2021-11-09 14:17:14 UTC (rev 1410) @@ -37,7 +37,7 @@ </form> <wicket:fragment wicket:id="multiSelect"><span wicket:id="values"></span></wicket:fragment> <wicket:fragment wicket:id="textField"><input wicket:id="value"/> <input wicket:id="value2"/></wicket:fragment> - <wicket:fragment wicket:id="dateField"><span wicket:id="value"></span> <span wicket:id="value2"></span></wicket:fragment> + <wicket:fragment wicket:id="dateField"><input type="date" wicket:id="value"/> <input type="date" wicket:id="value2"/></wicket:fragment> </wicket:panel> </body> </html> Deleted: trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiCalendar.html =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiCalendar.html 2021-11-09 09:54:13 UTC (rev 1409) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiCalendar.html 2021-11-09 14:17:14 UTC (rev 1410) @@ -1,5 +0,0 @@ -<wicket:panel> - <input wicket:id="field" size="8"/> - <a href="javascript:void(0)" wicket:id="button"><img src="resources/calendar.gif"/></a> - <div wicket:id="container" style="display:none;position:absolute"></div> -</wicket:panel> Deleted: trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiCalendar.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiCalendar.java 2021-11-09 09:54:13 UTC (rev 1409) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiCalendar.java 2021-11-09 14:17:14 UTC (rev 1410) @@ -1,130 +0,0 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.wicket.yui; - -import info.jtrac.wicket.ErrorHighlighter; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import org.apache.wicket.AttributeModifier; -import org.apache.wicket.behavior.HeaderContributor; -import org.apache.wicket.markup.html.IHeaderContributor; -import org.apache.wicket.markup.html.IHeaderResponse; -import org.apache.wicket.markup.html.WebMarkupContainer; -import org.apache.wicket.markup.html.form.FormComponentPanel; -import org.apache.wicket.markup.html.form.TextField; -import org.apache.wicket.model.AbstractReadOnlyModel; -import org.apache.wicket.model.IModel; -import org.apache.wicket.util.convert.ConversionException; -import org.apache.wicket.util.convert.IConverter; -import org.apache.wicket.util.convert.converters.AbstractConverter; - -/** - * yui date picker panel - */ -public class YuiCalendar extends FormComponentPanel implements IHeaderContributor { - - private TextField dateField; - private WebMarkupContainer container; - - public YuiCalendar(String id, IModel model, boolean required) { - - super(id, null); - - add(HeaderContributor.forJavaScript("resources/yui/yahoo/yahoo-min.js")); - add(HeaderContributor.forJavaScript("resources/yui/event/event-min.js")); - add(HeaderContributor.forJavaScript("resources/yui/dom/dom-min.js")); - add(HeaderContributor.forJavaScript("resources/yui/calendar/calendar-min.js")); - add(HeaderContributor.forJavaScript("resources/yui/calendar/calendar-utils.js")); - add(HeaderContributor.forCss("resources/yui/calendar/assets/calendar.css")); - - dateField = new TextField("field", model, Date.class) { - @Override - public IConverter getConverter(Class clazz) { - return new AbstractConverter() { - private DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); - public Object convertToObject(String s, Locale locale) { - if(s == null || s.trim().length() == 0) { - return null; - } - try { - return df.parse(s); - } catch (Exception e) { - throw new ConversionException(e); - } - } - protected Class getTargetType() { - return Date.class; - } - @Override - public String convertToString(Object o, Locale locale) { - Date d = (Date) o; - return df.format(d); - } - }; - } - @Override - public IModel getLabel() { - return YuiCalendar.this.getLabel(); - } - }; - dateField.setOutputMarkupId(true); - dateField.setRequired(required); - dateField.add(new ErrorHighlighter()); - add(dateField); - - final WebMarkupContainer button = new WebMarkupContainer("button"); - button.setOutputMarkupId(true); - button.add(new AttributeModifier("onclick", true, new AbstractReadOnlyModel() { - public Object getObject() { - return "showCalendar(" + getCalendarId() + ", '" + getInputId() + "');"; - } - })); - add(button); - - container = new WebMarkupContainer("container"); - container.setOutputMarkupId(true); - add(container); - } - - @Override - public void updateModel() { - dateField.updateModel(); - } - - private String getCalendarId() { - return getMarkupId(); - } - - private String getInputId() { - return dateField.getMarkupId(); - } - - private String getContainerId() { - return container.getMarkupId(); - } - - public void renderHead(IHeaderResponse response) { - String calendarId = getCalendarId(); - response.renderOnDomReadyJavascript("init" + calendarId + "()"); - response.renderJavascript( - "function init" + calendarId + "() { " - + calendarId + " = new YAHOO.widget.Calendar('" + calendarId + "', '" + getContainerId() + "'); " - + calendarId + ".selectEvent.subscribe(handleSelect, [ " + calendarId + ", '" + getInputId() + "' ], true); }", null); - } -} Modified: trunk/jtrac/src/main/java/org/apache/wicket/markup/html/form/DateField.java =================================================================== --- trunk/jtrac/src/main/java/org/apache/wicket/markup/html/form/DateField.java 2021-11-09 09:54:13 UTC (rev 1409) +++ trunk/jtrac/src/main/java/org/apache/wicket/markup/html/form/DateField.java 2021-11-09 14:17:14 UTC (rev 1410) @@ -17,12 +17,11 @@ package org.apache.wicket.markup.html.form; import org.apache.wicket.model.IModel; + import org.apache.wicket.util.convert.ConversionException; import org.apache.wicket.util.convert.IConverter; import org.apache.wicket.util.convert.converters.AbstractConverter; -import info.jtrac.wicket.ErrorHighlighter; - import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; @@ -40,42 +39,34 @@ /** * Construct. * - * @param id - * component id + * @param id component id */ public DateField(String id) { - super(id); + super(id, Date.class); } /** * Construct. * - * @param id - * component id - * @param type - * the type to use when updating the model for this text field + * @param id see Component + * @param model the model */ - public DateField(String id, Class type) + public DateField(String id, IModel model) { - super(id, type); + super(id, model, Date.class); } /** - * Construct. - * - * @param id - * see Component - * @param model - * the model + * @see org.apache.wicket.markup.html.form.TextField#getInputType() */ - public DateField(String id, IModel model) + protected String getInputType() { - super(id, model); + return "date"; } @Override - public IConverter getConverter(Class clazz) { + public IConverter getConverter (Class clazz) { return new AbstractConverter() { private DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); public Object convertToObject(String s, Locale locale) { @@ -98,28 +89,4 @@ } }; } - - /** - * @param id - * component id - * @param model - * the model - * @param type - * the type to use when updating the model for this text field - * @see org.apache.wicket.Component#Component(String, Class) - */ - public DateField(String id, Class type, boolean required) - { - super(id, type); - setRequired(required); - add(new ErrorHighlighter()); - } - - /** - * @see org.apache.wicket.markup.html.form.TextField#getInputType() - */ - protected String getInputType() - { - return "date"; - } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-11-09 09:54:14
|
Revision: 1409 http://sourceforge.net/p/j-trac/code/1409 Author: udittmer Date: 2021-11-09 09:54:13 +0000 (Tue, 09 Nov 2021) Log Message: ----------- widen input field Modified Paths: -------------- trunk/jtrac/src/main/java/info/jtrac/wicket/StoredSearchFormPage.html Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/StoredSearchFormPage.html =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/StoredSearchFormPage.html 2021-11-09 09:20:17 UTC (rev 1408) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/StoredSearchFormPage.html 2021-11-09 09:54:13 UTC (rev 1409) @@ -14,7 +14,7 @@ <tr> <td class="label"> <input type="hidden" wicket:id="idSearchLink" /> - <input wicket:id="name" size="15"/> + <input wicket:id="name" size="30"/> </td> <td> <input wicket:id="query" size="60"/> @@ -32,4 +32,4 @@ </form> </wicket:extend> </body> -</html> \ No newline at end of file +</html> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-11-09 09:20:21
|
Revision: 1408 http://sourceforge.net/p/j-trac/code/1408 Author: udittmer Date: 2021-11-09 09:20:17 +0000 (Tue, 09 Nov 2021) Log Message: ----------- user HTML number inputs in settings; start updating documentation Modified Paths: -------------- trunk/jtrac/doc/src/index.xml trunk/jtrac/src/main/java/info/jtrac/domain/Config.java trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.html trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.java trunk/jtrac/src/main/java/info/jtrac/wicket/CustomFieldsFormPanel.java Added Paths: ----------- trunk/jtrac/src/main/java/org/ trunk/jtrac/src/main/java/org/apache/ trunk/jtrac/src/main/java/org/apache/wicket/ trunk/jtrac/src/main/java/org/apache/wicket/markup/ trunk/jtrac/src/main/java/org/apache/wicket/markup/html/ trunk/jtrac/src/main/java/org/apache/wicket/markup/html/form/ trunk/jtrac/src/main/java/org/apache/wicket/markup/html/form/DateField.java trunk/jtrac/src/main/java/org/apache/wicket/markup/html/form/NumberField.java Modified: trunk/jtrac/doc/src/index.xml =================================================================== --- trunk/jtrac/doc/src/index.xml 2021-11-08 13:03:51 UTC (rev 1407) +++ trunk/jtrac/doc/src/index.xml 2021-11-09 09:20:17 UTC (rev 1408) @@ -1,1910 +1,1905 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- - -<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" -"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> - ---> - -<book> - <bookinfo> - <title>JTrac</title> - <subtitle>User / Developer Guide</subtitle> - <releaseinfo>2.2.0</releaseinfo> - <authorgroup> - <author> - <firstname>Peter</firstname> - <surname>Thomas</surname> - </author> - <author> - <firstname>Manfred</firstname> - <surname>Wolff</surname> - </author> - </authorgroup> - </bookinfo> - - <toc/> - - <preface id="preface"> - <title>Preface</title> - <para> - This document is a reference guide for <ulink url="http://jtrac.info">JTrac</ulink> - - the generic issue tracking web-application. This document is not only a user-guide - but can also serve as a reference for developers interested in contributing to JTrac. - </para> - <para> - This documentation has been generated using the DocBook configuration - used by the <ulink url="http://www.springframework.org">Spring</ulink> development team. - This particular simplified DocBook helper package was originally developed by Chris Bauer of the - <ulink url="http://www.hibernate.org">Hibernate</ulink> project. Thanks go out to all those who - perfected this very handy approach. - </para> - </preface> - - <chapter id="introduction"> - <title>Introduction</title> - <sect1 id="introduction-about"> - <title>About</title> - <para> - JTrac is a generic issue-tracking web-application that can be easily customized by adding - custom fields and drop-downs. Features include customizable workflow, field level permissions, - e-mail integration, file attachments and a detailed history view. - </para> - <para> - JTrac was created after the author felt that he could write a much better alternative to a - commercial defect tracking tool that he was having to use. Development started in 2004. - JTrac is ideal for issue tracking or bug-tracking, but it has been designed to be generic - and you can define custom fields to track almost anything you need. - </para> - <para> - JTrac development used to be hosted at <ulink url="https://jtrac.dev.java.net"/> but moved to SourceForge - in early 2006. The older working version was based on Spring MVC, Spring JDBC and MS Access and is not being - maintained any more. JTrac 2.0 uses Java 5.0 features and was completely re-written to use - <ulink url="http://www.hibernate.org/">Hibernate</ulink> for persistence and the - <ulink url="http://www.acegisecurity.org/">Acegi Security</ulink> framework for Spring. The presentation - layer for version 2.0 was mainly using - <ulink url="http://opensource.atlassian.com/confluence/spring/display/WEBFLOW/Home">Spring WebFlow</ulink> - but as of early 2007 (version 2.1.0 onwards) JTrac - <ulink url="http://ptrthomas.wordpress.com/2007/03/02/wicket-impressions-moving-from-spring-mvc-webflow/">switched to </ulink> - using the <ulink url="http://wicket.apache.org/">Apache Wicket</ulink> framework. - </para> - <para> - JTrac is extremely easy to install and the only pre-requisite is a Java 5 (or higher) Runtime Environment. - You can be up and running in seconds because JTrac comes bundled with a small-footprint web-application - server called <ulink url="http://jetty.mortbay.org/">Jetty</ulink> and an embedded database called - <ulink url="http://www.hsqldb.org/">HSQLDB</ulink>. Please refer to the - <link linkend="installation">installation</link> section of this documentation for details. - You can also choose to drop the WAR file into an application server of your choice and start using JTrac right away. - </para> - </sect1> - - <sect1 id="introduction-releasenotes"> - <title>Release Notes: Version 2.1.0</title> - <para> - There are no database changes between version 2.0 and 2.1.0. Instructions on how to upgrade are - available in the "<link linkend="upgrading">upgrading</link>" section of this document. The bundled - Jetty web-app server has been upgraded from version 6.0.2 to 6.1.1 but it is not mandatory that you - upgrade. - </para> - </sect1> - - <sect1 id="introduction-releasenotes-2_2"> - <title>Release Notes: Version 2.2.0</title> - <para> - There are no database changes between version 2.1.0 and 2.2.0. Instructions on how to upgrade are - available in the "<link linkend="upgrading">upgrading</link>" section of this document. - </para> - <para> - There where some minor changes and bugfixes: - <itemizedlist> - <listitem> - <para>It is possible to configure an individual header and an individual logo for your own JTrac.</para> - </listitem> - <listitem> - <para>Errors that were occured because of missing language ressources are fixed.</para> - </listitem> - <listitem> - <para>Some email-issues e.g. sending duplicate mails are fixed.</para> - </listitem> - </itemizedlist> - For details see the <ulink url="https://sourceforge.net/projects/j-trac/">different tracker at - the sourceforge website.</ulink> - </para> - </sect1> - </chapter> - - <chapter id="features"> - <title>Features</title> - <para> - JTrac has all the features that you would expect from a standard issue-tracking application such as - support for file-attachments and e-mail integration. JTrac offers powerful customization options, - especially in the areas of workflow and field-level permissions and compares well to even - commercial tools. - </para> - <sect1 id="features-easytoinstall"> - <title>Easy to Install</title> - <para> - One of the useful features of JTrac is that it is distributed with an embedded web-application server - (<ulink url="http://jetty.mortbay.org/">Jetty</ulink>) that has a very small footprint. - If you have Java 5 installed you can be up and running after downloading and extracting JTrac - by - simply using the provided start and stop scripts. JTrac scans its environment on startup and if a - configured database is not detected, <ulink url="http://www.hsqldb.org/">HSQLDB</ulink> is used by - default. - </para> - <para> - If you already have a web-application server that you wish to use (such as - <ulink url="http://tomcat.apache.org/">Tomcat</ulink>), you can just drop in the WAR file - provided and sign on into the application straight-away. - </para> - <para> - You can refer the <link linkend="installation">installation</link> section of this document for - more details. JTrac uses <ulink url="http://www.hibernate.org">Hibernate</ulink> and so it can - use any database <ulink url="http://www.hibernate.org/80.html">supported by Hibernate.</ulink> - </para> - </sect1> - <sect1 id="features-customfields"> - <title>Custom Fields</title> - <para> - JTrac can be used to effectively track various kinds of things such as Bugs, Action Items and Tasks. - You can easily add the following types of custom fields to a tracker Project (Space): - <itemizedlist> - <listitem> - <para>Drop Down List</para> - </listitem> - <listitem> - <para>Free Text Field</para> - </listitem> - <listitem> - <para>Date Picker</para> - </listitem> - <listitem> - <para>(Decimal) Number</para> - </listitem> - </itemizedlist> - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/custom-fields.png"/> - </imageobject> - <caption> - <para>Setting up custom fields for a project</para> - </caption> - </mediaobject> - </para> - <para> - There is a limit to how many custom fields of each type that you can configure for a Space. - These limits have been set after careful consideration and would very rarely fail to suffice. - They are as follows: Drop Down - 10, Free Text - 5, Numeric - 3, Date/Time - 3. - </para> - <para> - The screenshot below shows how creating a new item would look like for the typical - project (space) where 'Severity', 'Module', 'Type' and 'Priority' have been defined - as custom fields. - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/item-create.png"/> - </imageobject> - <caption> - <para>Creating a new item</para> - </caption> - </mediaobject> - </para> - </sect1> - <sect1 id="features-customworkflow"> - <title>Custom Workflow</title> - <para> - Each tracker project that you create can have a different workflow. JTrac allows for - complete customization of the tracker-item lifecycle, right down to the names of - each intermediate state. It is possible to create very sophisticated workflows and a visual - "map" of the workflow being edited is displayed below the state-transition toggle-buttons - to make it easier to manage. - </para> - <para> - The default workflow shown below is ideal if you simply want to track issues with just - two states - Open and Closed. However, with just a few mouse clicks, you can turn this into - a complex workflow involving multiple states. - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/custom-workflow-before.png"/> - </imageobject> - <caption> - <para>Default workflow - before customization</para> - </caption> - </mediaobject> - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/custom-workflow-after.png"/> - </imageobject> - <caption> - <para>Example of a workflow - after customization</para> - </caption> - </mediaobject> - </para> - <para> - You can even configure whether or not changing of values is permitted given the current state - within the workflow - and that too at a field-level. This flexibility allows for some - interesting possibilities. For example, assume that you want to maintain a "percentage complete" - field that can be updated at any time during the life-cycle of an item. The screenshot below - demonstrates how you can set up a drop down field called "% Complete": - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/percent-complete-field.png"/> - </imageobject> - <caption> - <para>Setting up of a drop down custom field called "% Complete"</para> - </caption> - </mediaobject> - </para> - <para> - Then when customizing the roles and field-level permissions, all you need to do is specify that - the "% Complete" field is editable even when the status is "Open". - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/percent-complete-roles.png"/> - </imageobject> - <caption> - <para>Making a custom field editable when updating an item (after new item has been created)</para> - </caption> - </mediaobject> - </para> - <para> - This results in the "% Complete" field being available for updating whenever anyone views an item - with a status of "Open". Notice also how the "% Complete" field is added as a column to the "History" - section so that the complete history of changes to this custom field can be viewed. - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/percent-complete-history.png"/> - </imageobject> - <caption> - <para>Example of a custom field that can be updated even when status is "Open"</para> - </caption> - </mediaobject> - </para> - </sect1> - <sect1 id="features-history"> - <title>Detailed History View</title> - <para> - JTrac provides a very detailed history view that completely captures all comments or status - changes for an item. The history can be seen when viewing an item as shown above. - </para> - </sect1> - <sect1 id="features-attachments"> - <title>Attachments Support</title> - <para> - You can upload file attachments at the time of creating of an item or as many times - as required after an item has been created. - </para> - </sect1> - <sect1 id="features-customroles"> - <title>Custom Roles</title> - <para> - JTrac customization does not stop at workflow - you can also define different roles for each tracker - project that you set up. This allows for power and flexibility. For example you could enforce that - a "DEVELOPER" role can only mark "Assigned" items as "Fixed" and that only a "TESTER" role has the - power to mark a "Fixed" item as "Closed". You can even disallow selected roles from being able to - create new items. JTrac gives you complete control over the workflow and you can easily tweak it - to fit your existing process rather than the other way around. - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/custom-roles.png"/> - </imageobject> - <caption> - <para>Example of workflow where each role has clearly defined responsibilities.</para> - </caption> - </mediaobject> - </para> - <para> - In the example above, the roles highlighted in red on the visual "map" of the workflow being edited - mean that at these points in the workflow - a user with the given role can assign only to other roles. - So in this case, the TESTER can assign a New item only to a DEVELOPER role and not another TESTER. - And when an item is in the "Assigned" state, a DEVELOPER can only assign to a TESTER when changing the - status to "Fixed" or "Rejected". - </para> - <para> - Field-level permissions can be mapped to roles which allows for even more flexibility. - For example, you can set a project up so that only a "MANAGER" can change the "Severity" level - after a bug is submitted. JTrac makes it possible for you to define very complex workflows, complete - with both Status and Role dependent field-level permissions, and you can do all this within a - single admin screen. - </para> - </sect1> - <sect1 id="features-search"> - <title>Search Custom Fields</title> - <para> - The intuitive search screen (shown below) allows for easy execution of common queries such as - find all items "logged by" or "assigned to" a user (or users). JTrac allows for filtering even - on custom fields. - </para> - <para> - You can navigate back to the search setup screen from the search results screen and have the - search parameters still active. This is useful when trying to tweak complex queries by - trial and error. - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/search.png"/> - </imageobject> - <caption> - <para>The search screen - which allows filtering on custom fields</para> - </caption> - </mediaobject> - </para> - </sect1> - <sect1 id="features-dashboard"> - <title>Dashboard</title> - <para> - The default view when you log into JTrac is a very handy dashboard view that provides the most - frequently used statistics at a glance. You can click on any of the numbers to immediately bring up - the search results view listing the items matched. For example, clicking on the count of items - "Logged By Me" would immediately display the list of items initiated by the currently logged in user. - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/dashboard.png"/> - </imageobject> - <caption> - <para>Statistics at a glance and search results available with just one click</para> - </caption> - </mediaobject> - </para> - <para> - For each Space, you can also drill-down to a finer level of detail by clicking on the right-arrow - 'expand' icon. This expands the dashboard rows so that you can see a break-up of item counts, not only by - whether 'logged by me' / 'assigned to me' - but against the custom workflow states defined for - the Space as well. Again, it is possible to directly navigate to the items by clicking on the dashboard numbers. - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/dashboard-expanded.png"/> - </imageobject> - <caption> - <para>Expanded statistics for a Space showing counts by workflow status</para> - </caption> - </mediaobject> - </para> - </sect1> - <sect1 id="features-searchall"> - <title>Search Across All Projects</title> - <para> - In addition to be able to search within a single space (project) JTrac allows for searching across - all (or a sub set of) projects that a user is mapped to. For example, in the screenshot of the - dashboard above, there are two projects. The "SEARCH" link at the top of the page (as well as the "search" - link on the "totals" row) would bring up the search wizard that can search across all (or a subset of) - the spaces assigned to the currently logged in user. - </para> - </sect1> - <sect1 id="features-textsearch"> - <title>Full Text Search</title> - <para> - The summary and even the detailed descriptions of items are indexed and can be searched. - Note that if you are upgrading or migrating databases, you may need to "re-index" which - is an option available to admin users. - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/full-text-search.png"/> - </imageobject> - <caption> - <para>Lucene powered full text search with wildcard support</para> - </caption> - </mediaobject> - </para> - </sect1> - <sect1 id="features-excel"> - <title>Export to Excel</title> - <para> - JTrac is able to export the results of any search (or all items in a Space) as an Excel Sheet. - The link for this appears at the top right of the search results screen. - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/excel-link.png"/> - </imageobject> - <caption> - <para>Download search results as an excel sheet</para> - </caption> - </mediaobject> - </para> - <para> - One the data is in a spreadsheet, it is very easy to analyze data and do things - like creating pivot-tables and charts. Many users use the 'export to Excel' feature - to fulfil custom reporting requirements. - </para> - </sect1> - <sect1 id="features-navigation"> - <title>Simple Navigation</title> - <para> - Most users will use only five screens in JTrac: Dashboard, Create, View, Search and Results. - Navigation has been designed with usability and simplicity in mind. A screenshot of the - search results screen is shown below. - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/search-results.png"/> - </imageobject> - <caption> - <para>The search results screen</para> - </caption> - </mediaobject> - </para> - </sect1> - <sect1 id="features-related"> - <title>Cross Referencing of Items</title> - <para> - You can establish bi-directional links between items. For example you can mark an item as - being a "duplicate of" another item. You can easily navigate to related items (and back) when - viewing any particular item. - </para> - <para> - The option to add related items is available when viewing an item after it is created. - The navigation is designed so that you have the option to execute a search before selecting an - item to cross-reference. - </para> - </sect1> - <sect1 id="features-guest"> - <title>Read-Only Access</title> - <para> - When setting up a Space you can define whether this space can be viewed by users without - having to log-in. - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/guest-allowed.png"/> - </imageobject> - <caption> - <para>Specify if read-only anonymous access is allowed per-space</para> - </caption> - </mediaobject> - </para> - </sect1> - <sect1 id="features-email"> - <title>E-mail Integration</title> - <para> - All status changes for an item will trigger e-mail notifications by default. - You can choose to add multiple users into a "notify list" at the time of raising a new item - or updating the history for an item. - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/notify-list.png"/> - </imageobject> - <caption> - <para>Add multiple users to a "notify list" while creating or updating an item</para> - </caption> - </mediaobject> - </para> - <para> - The e-mail content is very nicely formatted HTML and is laid out in the same way as how the screen looks - within JTrac when viewing an item. Users do not need to get used to a different or possibly - less pleasing format such as plain-text. - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/email-notification.png"/> - </imageobject> - <caption> - <para>Example of an e-mail sent by JTrac</para> - </caption> - </mediaobject> - </para> - <para> - Admin users can configure the SMTP / e-mail server that JTrac should use in the - "<link linkend="installation-settings">Manage Settings</link>" page. - This page can be accessed from the "OPTIONS" menu. JTrac also can handle an e-mail server that requires - authentication or even a secure connection over HTTPS. - </para> - </sect1> - <sect1 id="features-i18n"> - <title>Multi-Language Support</title> - <para> - JTrac has complete internationalization support and you can change the user interface language instantly - by editing your user-profile. Adding a translation is very easy and does not require any - Java development expertise. See <link linkend="dev-translating">this section</link> of the developer - guide for more details and please consider contributing translations to the JTrac project. - </para> - <para> - JTrac has already been translated into the following languages: - </para> - <para> - <itemizedlist> - <listitem> - <para> - Arabic - ar - </para> - </listitem> - <listitem> - <para> - Czech - cs - </para> - </listitem> - <listitem> - <para> - German - de - </para> - </listitem> - <listitem> - <para> - Greek - el - </para> - </listitem> - <listitem> - <para> - Spanish - es - </para> - </listitem> - <listitem> - <para> - Spanish (Argentina) - es_AR - </para> - </listitem> - <listitem> - <para> - Spanish (Mexico) - es_MX - </para> - </listitem> - <listitem> - <para> - French - fr - </para> - </listitem> - <listitem> - <para> - Hungarian - hu - </para> - </listitem> - <listitem> - <para> - Italian - it - </para> - </listitem> - <listitem> - <para> - Japanese - ja - </para> - </listitem> - <listitem> - <para> - Korean - ko - </para> - </listitem> - <listitem> - <para> - Dutch - nl - </para> - </listitem> - <listitem> - <para> - Polish - pl - </para> - </listitem> - <listitem> - <para> - Portugese (Brazil) - pt_BR - </para> - </listitem> - <listitem> - <para> - Russian - ru - </para> - </listitem> - <listitem> - <para> - Russian - ru - </para> - </listitem> - <listitem> - <para> - Slovenian - sl - </para> - </listitem> - <listitem> - <para> - Swedish - sv - </para> - </listitem> - <listitem> - <para> - Chinese (China) - zh_CN - </para> - </listitem> - <listitem> - <para> - Chinese (Taiwan) - zh_TW - </para> - </listitem> - </itemizedlist> - </para> - </sect1> - <sect1 id="features-ldap"> - <title>LDAP / CAS authentication support</title> - <para> - JTrac has built-in support for authenticating user credentials against an LDAP or Active Directory server. - Please refer the <link linkend="installation-ldap">LDAP section</link> of the installation guide for more details. - </para> - <para> - JTrac also has support for integrating with a - <ulink url="http://www.ja-sig.org/products/cas/">Central Authentication Service</ulink> (CAS) installation. - The install guide has more details on - <link linkend="installation-cas">how to configure single sign-on using CAS</link>. - </para> - </sect1> - </chapter> - - <chapter id="installation"> - <title>Installation</title> - <sect1 id="installation-prerequisites"> - <title>Prerequisites</title> - <para> - All you require in order to start using JTrac is Java 5 (or higher). Only a JRE (Java Runtime Environment) - is required and not the full-blown JDK (Java Development Kit). - </para> - <para> - There is a good chance that Java is already installed on your system and you can verify whether it is ready - to run JTrac by opening a command prompt and typing the following command: - </para> - <para> - <programlisting> - -java -version - </programlisting> - </para> - <para> - If the system responds with a java version that is 1.5 or greater, you are ready to download and run JTrac. - </para> - <para> - You can download Java from here: <ulink url="http://www.java.com/download"/>. - </para> - </sect1> - <sect1 id="installation-quick"> - <title>Quick Installation</title> - <para> - Once you have Java you can start using JTrac right away because you do not need to configure a web-application - server or a database. JTrac embeds both of these (<ulink url="http://jetty.mortbay.org/">Jetty</ulink> and - <ulink url="http://www.hsqldb.org/">HSQLDB</ulink>) to make it easy for you to evaluate JTrac. - </para> - <para> - <itemizedlist> - <listitem> - <para> - First, download the latest release from - <ulink url="http://sourceforge.net/project/showfiles.php?group_id=162983&package_id=184104"> - the JTrac downloads area</ulink> - </para> - </listitem> - <listitem> - <para>Extract the zip file into a directory of your choice</para> - </listitem> - <listitem> - <para> - Double-click or run "start.bat" to start JTrac - </para> - </listitem> - <listitem> - <para>Point your web-browser to <ulink url="http://localhost/jtrac"/></para> - </listitem> - <listitem> - <para>Sign on with the user name "admin" and password "admin"</para> - </listitem> - <listitem> - <para>Start using JTrac</para> - </listitem> - </itemizedlist> - </para> - <para> - You can use the "stop.bat" script to stop the application. The "start.bat" file configures the - Jetty server to use port 80 which you can easily change by editing "start.bat". You may need to do this - if for example JTrac does not start because a web-server (like Apache or IIS) is already running on port 80. - </para> - <para> - Remote users should be able to access JTrac over the network by using the machine name instead of "localhost" - in the URL shown above. If there is a problem, it could be due to a firewall running on the machine where JTrac - is installed - and you may have to reconfigure things so that for example "java.exe" is not blocked. - </para> - <sect2 id="installation-quick-linux"> - <title>Notes for Linux / Unix users</title> - <para> - If you are not on Windows, you may want to rename the start and stop *.bat files to e.g. *.sh for Linux - (*.bat is reported to work for Ubuntu). The commands within the batch files will work unchanged as long as - Java has been installed correctly i.e. "java" is in the PATH and it is the expected 1.5 or greater version. - You may need to do things like apply executable permissions to the batch files on Linux, e.g. "chmod +x *.bat". - And also - unless you have "root" permissions, you may face problems starting services on port 80 etc. - </para> - <para> - You can also read this forum thread for more details: <ulink url="http://sourceforge.net/forum/forum.php?thread_id=1971135&forum_id=552477">link</ulink> - </para> - </sect2> - <sect2 id="installation-quick-othernotes"> - <title>Security and Backup</title> - <para> - For security reasons, it is best that you change the default "admin" password after installing - JTrac, especially if you are going to make the application accessible on a public-facing web-site - over the internet. - </para> - <para> - JTrac by default will use a "data" directory that will be automatically created - within the root folder "jtrac". The data directory will contain the files needed by HSQLDB - (the embedded database) as well as hold uploaded attachments. When using JTrac in production, - this is the location that you should consider <link linkend="installation-backup">backing-up</link> - on a regular basis. - </para> - </sect2> - </sect1> - <sect1 id="installation-settings"> - <title>Settings</title> - <para> - For e-mail notifications to work, an SMTP server needs to be configured in JTrac. - Users who have the ADMIN role will be able to access the JTrac configuration settings - screen from a link on the "OPTIONS" screen. Note that you will need to provide values - for the "mail.from" and the "jtrac.url.base" properties also, for things like the hyperlinks - within e-mail to work properly. JTrac also supports mail servers that require authentication - or secure connections using SSL. You can refer the descriptions of the available configuration - property settings available from the settings screen as shown below. - </para> - <para> - If you have trouble getting e-mail to work, one of the things to watch out for is whether - there is any firewall blocking the communication between JTrac and the mail server - for example - you may have a firewall runnning on the machine where JTrac is installed. It is very common - for mail servers to be configured to prevent mail-relay requests from unknown applications or - IP addresses so you may need to check with your mail server administrator. - </para> - <para> - <mediaobject> - <imageobject role="html"> - <imagedata align="center" fileref="../images/settings.png"/> - </imageobject> - <caption> - <para>The JTrac settings screen where you can configure an e-mail / SMTP server</para> - </caption> - </mediaobject> - </para> - <para> - JTrac also can lookup and use a JNDI mail session (javax.mail.Session) if required. If you - provide a JNDI name, JTrac will ignore the SMTP server details provided. - </para> - - <para> - JTrac provides also other settings for an individual Headline (jtrac.header.text) and an - individual logo on the right side (jtrac.header.text). If no logo is set the original - JTrac logo is shown. If the jtrac.edit.item configuration item is set to true people may - edit their own items while no change is happens (editable item up to the first history entry). - Admins are always able to change the original item text. - </para> - - </sect1> - <sect1 id="installation-war"> - <title>Using only the WAR file</title> - <para> - JTrac is designed so that you can be up and running by just deploying the WAR file into your - existing web-application server. Note that you need a servlet 2.4 compliant container. - For example, if you have <ulink url="http://tomcat.apache.org/">Apache Tomcat</ulink> 5.5.X, - you can either copy the WAR file into the [tomcat.home]/webapps folder or choose to upload - the WAR file through the Tomcat console. - </para> - <para> - JTrac saves database information and uploaded attachments into a directory on the server. - This directory is logically called the "jtrac.home". When you do a "quick-install" of JTrac - by just deploying the WAR file, JTrac creates a ".jtrac" folder in the user home folder and - uses that location as the home directory. This is great for quickly evaluating JTrac but - when you actually use JTrac in production you should configure the JTrac home directory as - a location that can easily be backed-up for example. - </para> - </sect1> - <sect1 id="installation-custom"> - <title>Custom Installation</title> - <para> - To customize the location of "jtrac.home" you have the following three options. On startup, JTrac tries to - detect the value of "jtrac.home", and checks the following configuration options in the order listed below. - </para> - <sect2 id="installation-custom-initprops"> - <title>Edit the "jtrac-init.properties" file</title> - <para> - If you explode (unzip) the WAR file, you can edit the /WEB-INF/classes/jtrac-init.properties file - and customize the location of "jtrac.home". Note that you can choose to deploy JTrac as an exploded-war - and it is not mandatory that you re-package (zip) the WAR file after editing "jtrac-init.properties". - </para> - </sect2> - <sect2 id="installation-custom-servletparams"> - <title>Set a Servlet context parameter</title> - <para> - For example you can specify the jtrac.home using the Tomcat administration console or as - follows using a <Context> parameter. - </para> - <para> - <programlisting> - <![CDATA[ -<Context docBase="${catalina.home}/jtrac/jtrac.war"> - <Parameter name="jtrac.home" value="C:/data/jtrac_home"/> -</Context> ]]> - </programlisting> - </para> - </sect2> - <sect2 id="installation-custom-systemparams"> - <title>Set a System / JVM parameter</title> - <para> - If you can easily set a system property called "jtrac.home" within the environment of your - application server then you don't need to edit the WAR file. Please look at the contents of "start.bat" - to see an example of how the appropriate JVM (Java Virtual Machine) parameters can be set when deploying JTrac. - For example, if you are using <ulink url="http://tomcat.apache.org/">Apache Tomcat</ulink> 5.5.X, you can - set an environment variable called JAVA_OPTS before invoking the startup script (startup.bat / startup.sh). - Here is an example for Tomcat 5.5.X on Windows: - </para> - <para> - <programlisting> - -set JAVA_OPTS=-Djtrac.home=C:/data/jtrac_home -Dfile.encoding=UTF-8 - </programlisting> - </para> - <para> - If "jtrac.home" has not been configured in any of the above ways, JTrac will create a folder called - ".jtrac" in the user home directory and proceed to use that as "jtrac.home". - </para> - </sect2> - </sect1> - <sect1 id="installation-database"> - <title>Using a different database such as MySQL</title> - <para> - You can configure the database that JTrac uses by editing the "jtrac.properties" file that JTrac expects - within the "jtrac.home" folder. Note that if JTrac does not find a "jtrac.properties" file in the - expected location, JTrac creates a fresh one which is pre-configured for HSQLDB. The default contents of - this file are as follows: - </para> - <para> - <programlisting> - -database.driver=org.hsqldb.jdbcDriver -database.url=jdbc:hsqldb:file:${jtrac.home}/db/jtrac -database.username=sa -database.password= -hibernate.dialect=org.hibernate.dialect.HSQLDialect -hibernate.show_sql=false - </programlisting> - </para> - <para> - You can edit the values above to match the settings of a database that you wish to use. - Please refer to your database JDBC driver documentation for the values needed for - the driver and URL. You can look up the Hibernate dialect corresponding to your database type - from <ulink url="http://www.hibernate.org/hib_docs/reference/en/html/session-configuration.html#configuration-optional-dialects">this list</ulink>. - When using a custom database such as MySQL, you have to create an empty database before starting JTrac. - When JTrac starts, it will connect to the configured database and create the schema (database tables) - if not already present. - </para> - <para> - If you edit the "jtrac.properties" file, ensure that there are no trailing spaces in the entries shown above - (especially when cutting and pasting from somewhere). This can save you a lot of frustration! - </para> - <para> - Don't forget to make the database driver available for the web-application. If you are using the bundled - Jetty web-app server, this is as simple as placing the jar file in the "lib" folder along with the - jetty jar files. If you are using Tomcat, you can either place the jar file under "shared/lib" or within - the "/WEB-INF/lib" folder of the "jtrac" (exploded) WAR itself. - </para> - <para> - Given below is a sample jtrac.properties file for MySQL. It assumes that you have created a new database - called "jtrac". Using the default "root" user is obviously not advisable from a security point of view - and you should try and have a separate user for the database used by JTrac. - <programlisting> - -database.driver=com.mysql.jdbc.Driver -database.url=jdbc:mysql://localhost/jtrac -database.username=root -database.password= -hibernate.dialect=org.hibernate.dialect.MySQLDialect -hibernate.show_sql=false - </programlisting> - </para> - <para> - For MySQL, do not use the "autoReconnect=true" parameter in the MySQL URL, this is - <ulink url="http://dev.mysql.com/doc/connector/j/en/connector-j-usagenotes-j2ee.html#connector-j-usagenotes-tomcat">not recommended</ulink> - by the MySQL team. The <ulink url="http://jakarta.apache.org/commons/dbcp/">Apache DBCP</ulink> - connection pool used internally by JTrac is configured to refresh stale database connections. - </para> - <para> - You should also ensure that the database default character-set encoding is set correctly for - your requirements and UTF-8 is normally recommended. When setting up a fresh JTrac installation - on a non-default database such as MySQL, it is best that you do a small test by creating an item with - say Japanese or Chinese text in the summary or description. Verify that special characters - display correctly when viewing the item. Also check if e-mail notifications look okay as well. - </para> - <para> - If your database default character set is not what is expected you can consider setting it - explicitly for the database to be used by JTrac. For example if using MySQL, you can create the - database specifying the character set to be used like this: - <programlisting> - -create database jtrac charset utf8; - </programlisting> - And then you can force the encoding used by database connections in the JDBC URL. This is - how the corresponding line in "jtrac.properties" will look like: - <programlisting> - -database.url=jdbc:mysql://localhost/jtrac?useEncoding=true&characterEncoding=utf8 - </programlisting> - </para> - <para> - The default query to test idle database connections is "SELECT 1". This has been reported - to not work for databases such as Derby and Progress because they consider this invalid SQL. You can - customize the validation query by adding a "database.validationQuery" property in "jtrac.properties. - If you set the validation query to an empty string, this effectively disables the periodic testing of - idle database connections that Apache DBCP performs. - </para> - </sect1> - <sect1 id="installation-jndi-datasource"> - <title>Using a JNDI Datasource</title> - <para> - You can provide a JNDI datasource name as an additional property called "database.datasource.jndiname" - in the "jtrac.properties" file. JTrac will ignore the other database properties in this case. - </para> - </sect1> - <sect1 id="installation-ldap"> - <title>Configuring LDAP Authentication</title> - <para> - A couple of extra entries in "jtrac.properties" are required in order to configure LDAP authentication. - JTrac will attempt LDAP authentication if an "ldap.url" config entry is present. - </para> - <para> - <programlisting> - -ldap.url=ldap://myldaphost -ldap.searchBase=DC=foo,DC=bar,DC=org - </programlisting> - </para> - <para> - The following additional entry is recommended if you are using Active Directory. This switches on an - LDAP optimization that is specific to Active Directory. - </para> - <para> - <programlisting> - -ldap.activeDirectoryDomain=MYDOMAIN - </programlisting> - </para> - <para> - The benefit of LDAP authentication is that users can sign-on to JTrac by using their existing LDAP credentials - (username and password). At the moment, LDAP support is limited to authentication and not authorization. - Users still have to be created by an administrator and mapped to the relevant Spaces using the JTrac - administration screens before they can start using JTrac. - </para> - </sect1> - <sect1 id="installation-cas"> - <title>Integrating with CAS for Single Sign On</title> - <para> - In order to integrate JTrac with <ulink url="http://www.ja-sig.org/products/cas/">CAS</ulink> - you have to edit a couple of XML files within the "WEB-INF" folder ('web.xml' and 'applicationContext-acegi-cas.xml'). - The changes that you need to make are described below. - </para> - <sect2 id="installation-cas-webxml"> - <title>Changes to web.xml</title> - <para> - <itemizedlist> - <listitem> - <para> - Change "applicationContext-acegi.xml" to "applicationContext-acegi-cas.xml". - This appears towards the start of the file in the 'contextConfigLocation' context-param. - </para> - </listitem> - <listitem> - <para> - Uncomment the filter and filter-mapping XML for the 'acegi' FilterToBeanProxy - </para> - </listitem> - <listitem> - <para> - Uncomment the servlet and servlet-mapping XML for the 'casProxy' ProxyTicketReceptor. - You should also uncomment the associated context-param called 'edu.yale.its.tp.cas.proxyUrl' - and customize the value for your environment. - </para> - </listitem> - </itemizedlist> - </para> - </sect2> - <sect2 id="installation-cas-appcontext"> - <title>Changes to applicationContext-acegi-cas.xml</title> - <para> - In the XML corresponding to bean id "casProxyTicketValidator" - edit the following - property values to suit your environment: - </para> - <para> - <itemizedlist> - <listitem> - <para> - casValidate - </para> - </listitem> - <listitem> - <para> - proxyCallbackUrl - </para> - </listitem> - <listitem> - <para> - trustStore - </para> - </listitem> - <listitem> - <para> - loginUrl - </para> - </listitem> - <listitem> - <para> - logoutUrl - </para> - </listitem> - </itemizedlist> - </para> - <para> - For more details and troubleshooting, you can refer to the - <ulink url="http://www.acegisecurity.org/guide/springsecurity.html#cas-client">Acegi documentation on CAS integration</ulink>. - </para> - </sect2> - </sect1> - <sect1 id="installation-windowsservice"> - <title>Installing as a Windows Service</title> - <para> - Note that this approach can reportedly apply to other operating systems. If you have been able to do - this successfully on Linux or Mac environments, please consider contributing some documentation on this. - </para> - <para> - If you choose to use the bundled <ulink url="http://jetty.mortbay.org/">Jetty</ulink> web-application - server, you can easily install JTrac as a Windows service so that it can start automatically when - the host machine is switched on. One of the big advantages of this is that the console window will be invisible - and will run in the background. You can therefore avoid the possibility of the Jetty server getting - terminated because somebody decided to close "that pesky DOS Window that gets in the way". Having JTrac - start automatically... [truncated message content] |
From: <udi...@us...> - 2021-11-08 13:03:54
|
Revision: 1407 http://sourceforge.net/p/j-trac/code/1407 Author: udittmer Date: 2021-11-08 13:03:51 +0000 (Mon, 08 Nov 2021) Log Message: ----------- improve German translation Modified Paths: -------------- trunk/jtrac/src/main/resources/messages_de.properties Modified: trunk/jtrac/src/main/resources/messages_de.properties =================================================================== --- trunk/jtrac/src/main/resources/messages_de.properties 2021-11-08 10:36:34 UTC (rev 1406) +++ trunk/jtrac/src/main/resources/messages_de.properties 2021-11-08 13:03:51 UTC (rev 1407) @@ -53,8 +53,8 @@ item_form.detail = Details item_form.assignTo = Zuweisen an item_form.editReason = Grund bearbeiten -item_form.sendNotifications = eMail-Benachrichtigungen senden -item_form.notifyByEmail = per eMail benachrichtigen +item_form.sendNotifications = Email-Benachrichtigungen senden +item_form.notifyByEmail = per Email benachrichtigen item_form.attachment = Anhang item_form.error.version = Der Eintrag wurde inzwischen von einem anderen User bearbeitet oder Sie haben den Zur\u00FCck-Button benutzt. @@ -86,10 +86,10 @@ item_view_form.newStatus = Neuer Status item_view_form.assignTo = Zuweisen an item_view_form.comment = Kommentar -item_view_form.sendNotifications = eMail-Benachrichtigungen senden +item_view_form.sendNotifications = Email-Benachrichtigungen senden item_view_form.addRelatedItem = zugeh\u00F6rigen Eintrag zuweisen item_view_form.change = \u00E4ndern -item_view_form.notifyByEmail = per eMail benachrichtigen +item_view_form.notifyByEmail = per Email benachrichtigen item_view_form.attachment = Anhang item_view_form.assignedTo.error = Neue Zuweisung wird ben\u00F6tigt, wenn der Status nicht Closed ist item_view_form.status.error = Wird beim Zuweisen ben\u00F6tigt @@ -152,7 +152,7 @@ user_form.userDetails = Benutzerdetails user_form.loginId = Benutzer-ID user_form.fullName = Name des Benutzers -user_form.emailId = eMail-Adresse +user_form.emailId = Email-Adresse user_form.language = Sprache user_form.passwordMessage = <br/>Bleiben diese Felder leer wird ein neues Passwort generiert. user_form.password = Passwort @@ -161,7 +161,7 @@ user_form.loginId.error.exists = Die Benutzer-ID ist bereits vergeben user_form.passwordConfirm.error = Das wiederholte Passwort ist nicht korrekt user_form.passwordTooShort.error = Das Passwort muss mindestens {0} Zeichen lang sein. -user_form.mailPassword = Passwort per eMail versenden +user_form.mailPassword = Passwort per Email versenden user_form.locked = Gesperrt # logout @@ -173,7 +173,7 @@ # login login.title = JTrac Login login.home = Startseite -login.loginName = Benutzer-ID / eMail-Adresse +login.loginName = Benutzer-ID / Email-Adresse login.password = Passwort login.rememberMe = Auf diesem Rechner angemeldet bleiben login.submit = Start @@ -182,26 +182,26 @@ # config explanations (used in both config_list and config_form screens) config.mail.server.host = Hostname oder IP des SMTP Server f\u00FCr den Mailversand config.mail.server.port = Portnummer des SMTP Server (Default 25) -config.mail.server.username = Username am SMTP Server, wenn ben\u00F6tigt +config.mail.server.username = Benutzername am SMTP Server, wenn ben\u00F6tigt config.mail.server.password = Passwort am SMTP Server, wenn ben\u00F6tigt config.mail.server.starttls.enable = "true" wenn eine SSL Verbindung zum SMTP Server verwendet werden soll config.mail.server.ssl.enable = "true" wenn eine SSL Verbindung zum SMTP Server verwendet werden soll -config.mail.subject.prefix = Text der dem eMail Betreff vorangestellt wird (Default [jtrac]) -config.mail.session.jndiname = javax.mail.Session JNDI Name - Wenn vorhanden wird diese Verbindung statt dem SMTP Server benutzt -config.mail.from = Wir in eMails als Absenderadresse gesetzt -config.mail.smtp.localhost = \xDCberladen der HELO Settings f\xFCr sehr strikte SMTP Server +config.mail.subject.prefix = Text der dem Email Betreff vorangestellt wird (Default [jtrac]) +config.mail.session.jndiname = javax.mail.Session JNDI Name - fallse vorhanden wird diese Verbindung statt des SMTP Server benutzt +config.mail.from = Wird in Emails als Absenderadresse gesetzt +config.mail.smtp.localhost = \u00dcberladen der HELO Settings f\u00fcr sehr strikte SMTP Server # DKIM properties for additional mail authenticity -config.mail.dkim.signingDomain = Dom\u00e4ne f\u00fc die signiert wird +config.mail.dkim.signingDomain = Dom\u00e4ne f\u00fcr die signiert wird config.mail.dkim.selector = Selektor (default ist "default") config.mail.dkim.derFile = Pfad zum DER-kodierten privaten RSA Schl\u00fcssel config.mail.dkim.identity = Identit\u00e4t (default ist "mail.from") -config.jtrac.url.base = URL dieser Installation ( zB. http://myserver/jtrac ) wird ben\u00F6tigt f\u00FCr Links in den eMails -config.jtrac.header.picture = Inidividuelles Bild -config.jtrac.header.text = Individueller Text -config.jtrac.edit.item = Soll der Ersteller eines Eintrages diesen editieren k\xF6nnen? (auf true setzen, Default ist false) -config.locale.default = Default Sprache zB. "de" f\u00FCr Deutsch +config.jtrac.url.base = URL dieser Installation (z.B. http://myserver/jtrac) wird ben\u00F6tigt f\u00FCr Links in den Emails +config.jtrac.header.picture = Inidividuelles Bild oben auf Seite +config.jtrac.header.text = Individueller Text oben auf Seite +config.jtrac.edit.item = Soll der Autor eines Eintrages diesen editieren k\u00f6nnen? (auf true setzen, Default ist false) +config.locale.default = Default Sprache, z.B. "de" f\u00FCr Deutsch config.session.timeout = Session Timeout in Minuten (Default 30 Minuten) config.attachment.maxsize = Maximalgr\u00F6\u00DFe f\u00FCr Anlagen in MB, "-1" f\u00FCr Kein-Limit (Default 5) config.pwd.minLength = Minimale L\u00e4nge von Passw\u00f6rtern (Default 8) @@ -208,7 +208,7 @@ config.time.pretty = Relative Zeiten anzeigen wie auf Github, Twitter und Facebook (Default ist true) config.markdown.enabled = Fliesstext als Markdown anzeigen (Default ist true) config.attachments.openNewWindow = Text- und Bild-Anh\u00e4nge in neuem Fenster \u00f6ffnen statt sie herunterzuladen (Default ist true) -config.items.search.num = Anzahl der Eintr\u00f6ge pro Seite (Default 25) +config.items.search.num = Anzahl der Eintr\u00e4ge pro Seite (Default 25) # config_list (config_form does not have any extra messages) config_list.configurationSettings = Einstellungen @@ -417,7 +417,7 @@ user_list.spaceRole = Projekt (Rolle) user_list.allocateSpaceRole = Zuweisung<br/>Projekt / Rolle user_list.locale = Sprache -user_list.email = eMail +user_list.email = Email user_list.filterBy = Filtern nach # mail_sender (this is in the mail sending code, not a JSP) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-11-08 10:36:37
|
Revision: 1406 http://sourceforge.net/p/j-trac/code/1406 Author: udittmer Date: 2021-11-08 10:36:34 +0000 (Mon, 08 Nov 2021) Log Message: ----------- use HikariCP instead of Apache DBCP; boolean settings have a switch UI instead of a text field Modified Paths: -------------- trunk/jtrac/pom.xml trunk/jtrac/src/main/java/info/jtrac/config/DataSourceFactoryBean.java trunk/jtrac/src/main/java/info/jtrac/config/JtracConfigurer.java trunk/jtrac/src/main/java/info/jtrac/domain/Config.java trunk/jtrac/src/main/java/info/jtrac/util/DateUtils.java trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.java trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.html trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.java trunk/jtrac/src/main/java/info/jtrac/wicket/StoredSearchFormPage.java trunk/jtrac/src/main/resources/messages.properties trunk/jtrac/src/main/resources/messages_de.properties trunk/jtrac/src/main/resources/messages_en.properties trunk/jtrac/src/main/webapp/WEB-INF/applicationContext.xml Modified: trunk/jtrac/pom.xml =================================================================== --- trunk/jtrac/pom.xml 2021-10-27 15:57:10 UTC (rev 1405) +++ trunk/jtrac/pom.xml 2021-11-08 10:36:34 UTC (rev 1406) @@ -406,18 +406,12 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>com.zaxxer</groupId> + <artifactId>HikariCP</artifactId> + <version>4.0.3</version> + </dependency> <dependency> - <groupId>commons-dbcp</groupId> - <artifactId>commons-dbcp</artifactId> - <version>1.4</version> - <exclusions> - <exclusion> - <groupId>xml-apis</groupId> - <artifactId>xml-apis</artifactId> - </exclusion> - </exclusions> - </dependency> - <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>1.7.2</version> Modified: trunk/jtrac/src/main/java/info/jtrac/config/DataSourceFactoryBean.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/config/DataSourceFactoryBean.java 2021-10-27 15:57:10 UTC (rev 1405) +++ trunk/jtrac/src/main/java/info/jtrac/config/DataSourceFactoryBean.java 2021-11-08 10:36:34 UTC (rev 1406) @@ -1,241 +1,225 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.config; - -import java.sql.Connection; -import java.sql.Statement; -import javax.sql.DataSource; -import org.apache.commons.dbcp.BasicDataSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.jdbc.datasource.SingleConnectionDataSource; -import org.springframework.jndi.JndiObjectFactoryBean; -import org.springframework.util.StringUtils; - -/** - * <p> - * This class implements the Spring frameworks - * {@link org.springframework.beans.factory.FactoryBean} as well as the - * {@link org.springframework.beans.factory.DisposableBean} to conditionally - * create the correct <code>DataSource</code> object to access the database. - * </p> - * - * Supported data sources are: - * <ul> - * <li><a href="http://hsqldb.org/">HSQLDB (embedded)</a></li> - * <li><a href="http://commons.apache.org/dbcp/">Apache DBCP</a></li> - * <li>Java Naming and Directory Interface (JNDI); supported by most JDBC - * database drivers</li> - * </ul> - */ -public class DataSourceFactoryBean implements FactoryBean, DisposableBean { - /** - * Logger object - */ - private final Logger logger = LoggerFactory.getLogger(getClass()); - - /** - * DB driver class name - */ - private String driverClassName; - - /** - * DB url - */ - private String url; - - /** - * DB user - */ - private String username; - - /** - * DB password - */ - private String password; - - /** - * DB queries used to check if the database can be accessed - */ - private String validationQuery; - - /** - * DB JNDI data source name - */ - private String dataSourceJndiName; - - /** - * DB JNDI data source object - */ - private DataSource dataSource; - - /** - * This method allows to store the name of the DB driver class. - * - * @param driverClassName The name of the DB driver class. - */ - public void setDriverClassName(String driverClassName) { - this.driverClassName = driverClassName; - } - - /** - * This method allows to store the DB url. - * - * @param url The DB url. - */ - public void setUrl(String url) { - this.url = url; - } - - /** - * This method allows to store the DB user. - * - * @param username The DB user. - */ - public void setUsername(String username) { - this.username = username; - } - - /** - * This method allows to store the DB password. - * - * @param password The DB password. - */ - public void setPassword(String password) { - this.password = password; - } - - /** - * This method allows to store the SQL query string used to check if - * the DB connection is working. - * - * @param validationQuery The SQL query used to validate if the DB - * connection is working. - */ - public void setValidationQuery(String validationQuery) { - this.validationQuery = validationQuery; - } - - /** - * This method allows to store the JNDI data source name. - * - * @param dataSourceJndiName The JNDI data source name. - */ - public void setDataSourceJndiName(String dataSourceJndiName) { - this.dataSourceJndiName = dataSourceJndiName; - } - - /** - * This method returns the dataSource object used for the DB access. - * - * @throws Exception - * @see org.springframework.beans.factory.FactoryBean#getObject() - */ - public Object getObject() throws Exception { - if(StringUtils.hasText(dataSourceJndiName)) { - logger.info("JNDI datasource requested, looking up datasource from JNDI name: '" + dataSourceJndiName + "'"); - JndiObjectFactoryBean factoryBean = new JndiObjectFactoryBean(); - factoryBean.setJndiName(dataSourceJndiName); - - // "java:comp/env/" will be prefixed if the JNDI name doesn't already have it - factoryBean.setResourceRef(true); - - // This step actually does the JNDI lookup - try { - factoryBean.afterPropertiesSet(); - } catch(Exception e) { - logger.error("datasource init from JNDI failed : " + e); - logger.error("aborting application startup"); - throw new RuntimeException(e); - } // end try..catch - - dataSource = (DataSource) factoryBean.getObject(); - } else if(url.startsWith("jdbc:hsqldb:file")) { - logger.info("embedded HSQLDB mode detected, switching on spring single connection data source"); - SingleConnectionDataSource ds = new SingleConnectionDataSource(); - ds.setUrl(url); - ds.setDriverClassName(driverClassName); - ds.setUsername(username); - ds.setPassword(password); - ds.setSuppressClose(true); - dataSource = ds; - } else { - logger.info("Not using embedded HSQLDB or JNDI datasource, switching on Apache DBCP data source connection pooling"); - BasicDataSource ds = new BasicDataSource(); - ds.setUrl(url); - ds.setDriverClassName(driverClassName); - ds.setUsername(username); - ds.setPassword(password); - ds.setValidationQuery(validationQuery); - ds.setTestOnBorrow(false); - ds.setTestWhileIdle(true); - ds.setTimeBetweenEvictionRunsMillis(600000); - dataSource = ds; - } // end if..else - - return dataSource; - } - - /** - * This method returns the class name of the DataSource object which can be - * used to determine which data source implementation is currently used. - * - * @return Returns the class name <code>Object.class</code> of the - * DataSource object. - * @see org.springframework.beans.factory.FactoryBean#getObjectType() - */ - public Class getObjectType() { - return DataSource.class; - } - - /** - * This method returns if the factory is implemented as Singleton. - * - * @return Returns if the factory is implemented as Singleton or not. - * @see org.springframework.beans.factory.FactoryBean#isSingleton() - */ - public boolean isSingleton() { - return true; - } - - /** - * This method is called to clean up the references when the object is - * destroyed. - * - * @throws Exception - * @see org.springframework.beans.factory.DisposableBean#destroy() - */ - public void destroy() throws Exception { - if(dataSource instanceof SingleConnectionDataSource) { - logger.info("attempting to shut down embedded HSQLDB database"); - Connection con = dataSource.getConnection(); - Statement stmt = con.createStatement(); - stmt.executeUpdate("SHUTDOWN"); - stmt.close(); - con.close(); - logger.info("embedded HSQLDB database shut down successfully"); - } else if (dataSource instanceof BasicDataSource){ - logger.info("attempting to close Apache DBCP data source"); - ((BasicDataSource) dataSource).close(); - logger.info("Apache DBCP data source closed successfully"); - } else { - logger.info("context shutting down for JNDI datasource"); - } // end if..else - } -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac.config; + +import java.sql.Connection; +import java.sql.Statement; +import javax.sql.DataSource; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.jdbc.datasource.SingleConnectionDataSource; +import org.springframework.jndi.JndiObjectFactoryBean; +import org.springframework.util.StringUtils; + +/** + * <p> + * This class implements the Spring frameworks + * {@link org.springframework.beans.factory.FactoryBean} as well as the + * {@link org.springframework.beans.factory.DisposableBean} to conditionally + * create the correct <code>DataSource</code> object to access the database. + * </p> + * + * Supported data sources are: + * <ul> + * <li><a href="http://hsqldb.org/">HSQLDB (embedded)</a></li> + * <li><a href="https://github.com/brettwooldridge/HikariCP">HikariCP</a></li> + * <li>Java Naming and Directory Interface (JNDI); supported by most JDBC database drivers</li> + * </ul> + */ +public class DataSourceFactoryBean implements FactoryBean, DisposableBean { + /** + * Logger object + */ + private final Logger logger = LoggerFactory.getLogger(getClass()); + + /** + * DB driver class name + */ + private String driverClassName; + + /** + * DB url + */ + private String url; + + /** + * DB user + */ + private String username; + + /** + * DB password + */ + private String password; + + /** + * DB JNDI data source name + */ + private String dataSourceJndiName; + + /** + * DB JNDI data source object + */ + private DataSource dataSource; + + /** + * This method allows to store the name of the DB driver class. + * + * @param driverClassName The name of the DB driver class. + */ + public void setDriverClassName(String driverClassName) { + this.driverClassName = driverClassName; + } + + /** + * This method allows to store the DB url. + * + * @param url The DB url. + */ + public void setUrl(String url) { + this.url = url; + } + + /** + * This method allows to store the DB user. + * + * @param username The DB user. + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * This method allows to store the DB password. + * + * @param password The DB password. + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * This method allows to store the JNDI data source name. + * + * @param dataSourceJndiName The JNDI data source name. + */ + public void setDataSourceJndiName(String dataSourceJndiName) { + this.dataSourceJndiName = dataSourceJndiName; + } + + /** + * This method returns the dataSource object used for the DB access. + * + * @throws Exception + * @see org.springframework.beans.factory.FactoryBean#getObject() + */ + public Object getObject() throws Exception { + if(StringUtils.hasText(dataSourceJndiName)) { + logger.info("JNDI datasource requested, looking up datasource from JNDI name: '" + dataSourceJndiName + "'"); + JndiObjectFactoryBean factoryBean = new JndiObjectFactoryBean(); + factoryBean.setJndiName(dataSourceJndiName); + + // "java:comp/env/" will be prefixed if the JNDI name doesn't already have it + factoryBean.setResourceRef(true); + + // This step actually does the JNDI lookup + try { + factoryBean.afterPropertiesSet(); + } catch(Exception e) { + logger.error("datasource init from JNDI failed : " + e); + logger.error("aborting application startup"); + throw new RuntimeException(e); + } // end try..catch + + dataSource = (DataSource) factoryBean.getObject(); + } else if(url.startsWith("jdbc:hsqldb:file")) { + logger.info("embedded HSQLDB mode detected, using Spring single connection data source"); + SingleConnectionDataSource ds = new SingleConnectionDataSource(); + ds.setUrl(url); + ds.setDriverClassName(driverClassName); + ds.setUsername(username); + ds.setPassword(password); + ds.setSuppressClose(true); + dataSource = ds; + } else { + logger.info("Not using embedded HSQLDB or JNDI datasource, using HikariCP connection pool"); + + HikariConfig config = new HikariConfig(); + config.setDriverClassName(driverClassName); + config.setJdbcUrl(url); + config.setUsername(username); + config.setPassword(password); + config.setKeepaliveTime(300000l); // 5 minutes + + dataSource = new HikariDataSource(config); + } // end if..else + + return dataSource; + } + + /** + * This method returns the class name of the DataSource object which can be + * used to determine which data source implementation is currently used. + * + * @return Returns the class name <code>Object.class</code> of the DataSource object. + * @see org.springframework.beans.factory.FactoryBean#getObjectType() + */ + public Class getObjectType() { + return DataSource.class; + } + + /** + * This method returns if the factory is implemented as Singleton. + * + * @return Returns if the factory is implemented as Singleton or not. + * @see org.springframework.beans.factory.FactoryBean#isSingleton() + */ + public boolean isSingleton() { + return true; + } + + /** + * This method is called to clean up the references when the object is destroyed. + * + * @throws Exception + * @see org.springframework.beans.factory.DisposableBean#destroy() + */ + public void destroy() throws Exception { + if(dataSource instanceof SingleConnectionDataSource) { + logger.info("attempting to shut down embedded HSQLDB database"); + Connection con = dataSource.getConnection(); + Statement stmt = con.createStatement(); + stmt.executeUpdate("SHUTDOWN"); + stmt.close(); + con.close(); + logger.info("embedded HSQLDB database shut down successfully"); + } else if (dataSource instanceof HikariDataSource){ + logger.info("attempting to close HikariCP data source"); + ((HikariDataSource) dataSource).close(); + logger.info("HikariCP data source closed successfully"); + } else { + logger.info("context shutting down for JNDI datasource"); + } // end if..else + } +} Modified: trunk/jtrac/src/main/java/info/jtrac/config/JtracConfigurer.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/config/JtracConfigurer.java 2021-10-27 15:57:10 UTC (rev 1405) +++ trunk/jtrac/src/main/java/info/jtrac/config/JtracConfigurer.java 2021-11-08 10:36:34 UTC (rev 1406) @@ -65,7 +65,7 @@ * <ul> * <li>DataSourceFactoryBean:</li> * <ul> - * <li>switches between embedded HSQLDB or Apache DBCP (connection pool)</li> + * <li>switches between embedded HSQLDB or HikariCP (connection pool)</li> * <li>performs graceful shutdown of database if embedded HSQLDB</li> * </ul> * <li>ProviderManagerFactoryBean</li> @@ -210,14 +210,7 @@ logger.info("jtrac.timestamp = '" + timestamp + "'"); props.setProperty("jtrac.version", version); props.setProperty("jtrac.timestamp", timestamp); - - /* - * TODO: A better way (default value) to check the database should be used for Apache DBCP. - * The current "SELECT...FROM DUAL" only works on Oracle (and MySQL). - * Other databases also support "SELECT 1+1" as query - * (e.g. PostgreSQL, Hypersonic 2 (H2), MySQL, etc.). - */ - props.setProperty("database.validationQuery", "SELECT 1 FROM DUAL"); + props.setProperty("ldap.url", ""); props.setProperty("ldap.activeDirectoryDomain", ""); props.setProperty("ldap.searchBase", ""); Modified: trunk/jtrac/src/main/java/info/jtrac/domain/Config.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/domain/Config.java 2021-10-27 15:57:10 UTC (rev 1405) +++ trunk/jtrac/src/main/java/info/jtrac/domain/Config.java 2021-11-08 10:36:34 UTC (rev 1406) @@ -31,7 +31,8 @@ private String value; private static final Set<String> PARAMS; - + private static final Set<String> BOOLEAN_PARAMS; + // set up a static set of valid config key names static { PARAMS = new LinkedHashSet<String>(); @@ -61,12 +62,24 @@ PARAMS.add("markdown.enabled"); PARAMS.add("attachments.openNewWindow"); PARAMS.add("items.search.num"); + + BOOLEAN_PARAMS = new LinkedHashSet<String>(); + BOOLEAN_PARAMS.add("mail.server.starttls.enable"); + BOOLEAN_PARAMS.add("mail.server.ssl.enable"); + BOOLEAN_PARAMS.add("jtrac.edit.item"); + BOOLEAN_PARAMS.add("time.pretty"); + BOOLEAN_PARAMS.add("markdown.enabled"); + BOOLEAN_PARAMS.add("attachments.openNewWindow"); } public static Set<String> getParams() { return PARAMS; } - + + public static boolean isBoolean (String param) { + return BOOLEAN_PARAMS.contains(param); + } + public Config() { // zero arg constructor } @@ -75,7 +88,7 @@ this.param = param; this.value = value; } - + public boolean isMailConfig() { return param.startsWith("mail.") || param.startsWith("jtrac.url."); } @@ -91,7 +104,11 @@ public boolean isLocaleConfig() { return param.startsWith("locale."); } - + + public boolean isBoolean() { + return BOOLEAN_PARAMS.contains(param); + } + //========================================================================== public String getParam() { Modified: trunk/jtrac/src/main/java/info/jtrac/util/DateUtils.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/util/DateUtils.java 2021-10-27 15:57:10 UTC (rev 1405) +++ trunk/jtrac/src/main/java/info/jtrac/util/DateUtils.java 2021-11-08 10:36:34 UTC (rev 1406) @@ -43,7 +43,7 @@ if (StringUtils.hasText(pretty)) { return pretty.trim().equalsIgnoreCase("true"); } else { - return false; + return true; } } Modified: trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java 2021-10-27 15:57:10 UTC (rev 1405) +++ trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java 2021-11-08 10:36:34 UTC (rev 1406) @@ -141,7 +141,7 @@ return null; } else { String markdown = JtracApplication.get().getJtrac().loadConfig("markdown.enabled"); - if (markdown!=null && markdown.equalsIgnoreCase("true")) { + if (markdown==null || markdown.equalsIgnoreCase("true")) { // apply markdown Node document = parser.parse(text); return renderer.render(document); Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.java 2021-10-27 15:57:10 UTC (rev 1405) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.java 2021-11-08 10:36:34 UTC (rev 1406) @@ -116,7 +116,7 @@ if (StringUtils.hasText(openNewWindow)) { return openNewWindow.trim().equalsIgnoreCase("true"); } else { - return false; + return true; } } } Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.html =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.html 2021-10-27 15:57:10 UTC (rev 1405) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.html 2021-11-08 10:36:34 UTC (rev 1406) @@ -7,7 +7,10 @@ <tr> <td wicket:id="param" class="label"></td> <td> - <input wicket:id="value"/> + <input wicket:id="value" id="valueField"/> + <div class="switch" style="margin-left: auto; margin-right: auto;"></div> + </td> + <td> <input type="submit" wicket:message="value:submit"/> </td> </tr> @@ -17,4 +20,4 @@ </form> </wicket:extend> </body> -</html> \ No newline at end of file +</html> Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.java 2021-10-27 15:57:10 UTC (rev 1405) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigFormPage.java 2021-11-08 10:36:34 UTC (rev 1406) @@ -1,85 +1,106 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.wicket; - -import info.jtrac.domain.Config; -import org.apache.wicket.markup.html.basic.Label; -import org.apache.wicket.markup.html.form.Form; -import org.apache.wicket.markup.html.form.TextField; -import org.apache.wicket.markup.html.link.Link; -import org.apache.wicket.model.BoundCompoundPropertyModel; - -/** - * config value edit form - */ -public class ConfigFormPage extends BasePage { - /** - * Constructor - * - * @param param - * @param value - */ - public ConfigFormPage(String param, String value) { - add(new ConfigForm("form", param, value)); - } - - /** - * wicket form - */ - private class ConfigForm extends Form { - - private String param; - - private String value; - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public ConfigForm(String id, final String param, final String value) { - - super(id); - - this.param = param; - this.value = value; - - final BoundCompoundPropertyModel model = new BoundCompoundPropertyModel(this); - setModel(model); - - add(new Label("heading", localize("config." + param))); - add(new Label("param", param)); - add(new TextField("value")); - - // cancel ========================================================== - add(new Link("cancel") { - public void onClick() { - setResponsePage(new ConfigListPage(param)); - } - }); - } - - @Override - protected void onSubmit() { - getJtrac().storeConfig(new Config(param, value)); - setResponsePage(new ConfigListPage(param)); - } - } -} \ No newline at end of file +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac.wicket; + +import info.jtrac.domain.Config; + +import org.apache.wicket.behavior.HeaderContributor; +import org.apache.wicket.markup.html.IHeaderContributor; +import org.apache.wicket.markup.html.IHeaderResponse; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.form.HiddenField; +import org.apache.wicket.markup.html.form.TextField; +import org.apache.wicket.markup.html.link.Link; +import org.apache.wicket.model.BoundCompoundPropertyModel; + +/** + * config value edit form + */ +public class ConfigFormPage extends BasePage { + /** + * Constructor + * + * @param param + * @param value + */ + public ConfigFormPage(String param, String value) { + add(new ConfigForm("form", param, value)); + } + + /** + * wicket form + */ + private class ConfigForm extends Form { + + private String param; + private String value; + private boolean isBoolean; + + public String getValue() { return value; } + public void setValue (String value) { this.value = value; } + + public ConfigForm(String id, final String param, final String value) { + super(id); + + this.param = param; + this.value = value; + this.isBoolean = Config.isBoolean(param); + + // boolean settings use a JavaScript switch instead of a text field + if (isBoolean) { + add(HeaderContributor.forJavaScript("resources/jquery-3.6.0.min.js")); + add(HeaderContributor.forJavaScript("resources/jquery.enhanced-switch.js")); + add(HeaderContributor.forCss("resources/jquery.enhanced-switch-pingpong.css")); + add(new HeaderContributor(new IHeaderContributor() { + public void renderHead(IHeaderResponse response) { + String js = "$('.switch').enhancedSwitch();\n" + + ((value!=null && value.equals("true")) ? "$('.switch').enhancedSwitch('setTrue');\n" : "") + + "$('#valueField').hide();\n" + + "$('.switch').click(function() {\n" + + " var selectedSwitch = $(this);\n" + + " selectedSwitch.enhancedSwitch('toggle');\n" + + " //console.log(selectedSwitch.enhancedSwitch('state'));\n" + + " $('#valueField').val(selectedSwitch.enhancedSwitch('state'));\n" + + "});"; + response.renderOnDomReadyJavascript(js); + } + })); + } + + final BoundCompoundPropertyModel model = new BoundCompoundPropertyModel(this); + setModel(model); + + add(new Label("heading", localize("config." + param))); + add(new Label("param", param)); + add(new TextField("value")); + + // cancel ========================================================== + add(new Link("cancel") { + public void onClick() { + setResponsePage(new ConfigListPage(param)); + } + }); + } + + @Override + protected void onSubmit() { + getJtrac().storeConfig(new Config(param, value)); + setResponsePage(new ConfigListPage(param)); + } + } +} Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/StoredSearchFormPage.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/StoredSearchFormPage.java 2021-10-27 15:57:10 UTC (rev 1405) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/StoredSearchFormPage.java 2021-11-08 10:36:34 UTC (rev 1406) @@ -8,8 +8,6 @@ import org.apache.wicket.model.BoundCompoundPropertyModel; import org.apache.wicket.markup.html.form.Button; - - /** * Created by ncrappe on 8/09/2015. */ @@ -121,9 +119,6 @@ idLink = this.idSearchLink; } - System.out.println("idLink:"+String.valueOf(idLink)); - - getJtrac().storeStoredSearch(new StoredSearch(idLink, name, query)); setResponsePage(new StoredSearchListPage(name)); } @@ -131,7 +126,5 @@ public void setId(long idSearchLink) { this.idSearchLink = idSearchLink; } - - } -} \ No newline at end of file +} Modified: trunk/jtrac/src/main/resources/messages.properties =================================================================== --- trunk/jtrac/src/main/resources/messages.properties 2021-10-27 15:57:10 UTC (rev 1405) +++ trunk/jtrac/src/main/resources/messages.properties 2021-11-08 10:36:34 UTC (rev 1406) @@ -205,9 +205,9 @@ config.session.timeout = Time in minutes after which user session expires (default 30 minutes) config.attachment.maxsize = Maximum size in MB of file-attachments. (default 5 MB) Use -1 for no-limit config.pwd.minLength = Minimal length of passwords (default is 8) -config.time.pretty = Show relative timestamps as on Github, Twitter and Facebook (set to true, default is false) -config.markdown.enabled = Support Markdown Syntax for text display (set to true, default is false) -config.attachments.openNewWindow = Open text and image attachments in a new tab/window, rather than downloading them +config.time.pretty = Show relative timestamps as on Github, Twitter and Facebook (default is true) +config.markdown.enabled = Support Markdown Syntax for text display (default is true) +config.attachments.openNewWindow = Open text and image attachments in a new tab/window, rather than downloading them (default is true) config.items.search.num = Number of entries per page (default is 25) # config_list (config_form does not have any extra messages) Modified: trunk/jtrac/src/main/resources/messages_de.properties =================================================================== --- trunk/jtrac/src/main/resources/messages_de.properties 2021-10-27 15:57:10 UTC (rev 1405) +++ trunk/jtrac/src/main/resources/messages_de.properties 2021-11-08 10:36:34 UTC (rev 1406) @@ -205,9 +205,9 @@ config.session.timeout = Session Timeout in Minuten (Default 30 Minuten) config.attachment.maxsize = Maximalgr\u00F6\u00DFe f\u00FCr Anlagen in MB, "-1" f\u00FCr Kein-Limit (Default 5) config.pwd.minLength = Minimale L\u00e4nge von Passw\u00f6rtern (Default 8) -config.time.pretty = Relative Zeiten anzeigen wie auf Github, Twitter und Facebook (auf true setzen, Default ist false) -config.markdown.enabled = Fliesstext als Markdown anzeigen (auf true setzen, Default ist false) -config.attachments.openNewWindow = Text- und Bild-Anh\u00e4nge in neuem Fenster \u00f6ffnen statt sie herunterzuladen +config.time.pretty = Relative Zeiten anzeigen wie auf Github, Twitter und Facebook (Default ist true) +config.markdown.enabled = Fliesstext als Markdown anzeigen (Default ist true) +config.attachments.openNewWindow = Text- und Bild-Anh\u00e4nge in neuem Fenster \u00f6ffnen statt sie herunterzuladen (Default ist true) config.items.search.num = Anzahl der Eintr\u00f6ge pro Seite (Default 25) # config_list (config_form does not have any extra messages) Modified: trunk/jtrac/src/main/resources/messages_en.properties =================================================================== --- trunk/jtrac/src/main/resources/messages_en.properties 2021-10-27 15:57:10 UTC (rev 1405) +++ trunk/jtrac/src/main/resources/messages_en.properties 2021-11-08 10:36:34 UTC (rev 1406) @@ -205,9 +205,9 @@ config.session.timeout = Time in minutes after which user session expires (default 30 minutes) config.attachment.maxsize = Maximum size in MB of file-attachments. (default 5 MB) Use -1 for no-limit config.pwd.minLength = Minimal length of passwords (default is 8) -config.time.pretty = Show relative timestamps as on Github, Twitter and Facebook (set to true, default is false) -config.markdown.enabled = Support Markdown Syntax for text display (set to true, default is false) -config.attachments.openNewWindow = Open text and image attachments in a new tab/window, rather than downloading them +config.time.pretty = Show relative timestamps as on Github, Twitter and Facebook (default is true) +config.markdown.enabled = Support Markdown Syntax for text display (default is true) +config.attachments.openNewWindow = Open text and image attachments in a new tab/window, rather than downloading them (default is true) config.items.search.num = Number of entries per page (default is 25) # config_list (config_form does not have any extra messages) Modified: trunk/jtrac/src/main/webapp/WEB-INF/applicationContext.xml =================================================================== --- trunk/jtrac/src/main/webapp/WEB-INF/applicationContext.xml 2021-10-27 15:57:10 UTC (rev 1405) +++ trunk/jtrac/src/main/webapp/WEB-INF/applicationContext.xml 2021-11-08 10:36:34 UTC (rev 1406) @@ -49,13 +49,12 @@ </bean> <!-- custom factory bean that uses spring single connection data source if embedded - HSQLDB is being used, else Apache DBCP with connection pooling --> + HSQLDB is being used, else HikariCP connection pool --> <bean id="dataSource" class="info.jtrac.config.DataSourceFactoryBean"> <property name="driverClassName" value="${database.driver}"/> <property name="url" value="${database.url}"/> <property name="username" value="${database.username}"/> <property name="password" value="${database.password}"/> - <property name="validationQuery" value="${database.validationQuery}"/> <property name="dataSourceJndiName" value="${database.datasource.jndiname}"/> </bean> @@ -103,4 +102,4 @@ </property> </bean> -</beans> \ No newline at end of file +</beans> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-10-27 15:57:12
|
Revision: 1405 http://sourceforge.net/p/j-trac/code/1405 Author: udittmer Date: 2021-10-27 15:57:10 +0000 (Wed, 27 Oct 2021) Log Message: ----------- remove unused classes Removed Paths: ------------- trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiPanel.html trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiPanel.java trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiUtils.java Deleted: trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiPanel.html =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiPanel.html 2021-10-27 15:30:46 UTC (rev 1404) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiPanel.html 2021-10-27 15:57:10 UTC (rev 1405) @@ -1,6 +0,0 @@ -<wicket:panel> - <div wicket:id="dialog" style="visibility:hidden"> - <div class="hd" wicket:id="heading"></div> - <div class="bd" wicket:id="content"></div> - </div> -</wicket:panel> \ No newline at end of file Deleted: trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiPanel.java 2021-10-27 15:30:46 UTC (rev 1404) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiPanel.java 2021-10-27 15:57:10 UTC (rev 1405) @@ -1,81 +0,0 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.wicket.yui; - -import java.util.HashMap; -import java.util.Map; -import org.apache.wicket.Component; -import org.apache.wicket.behavior.HeaderContributor; -import org.apache.wicket.markup.html.IHeaderContributor; -import org.apache.wicket.markup.html.IHeaderResponse; -import org.apache.wicket.markup.html.WebMarkupContainer; -import org.apache.wicket.markup.html.basic.Label; -import org.apache.wicket.markup.html.panel.Panel; - -/** - * custom wicketized yahoo ui panel widget - */ -public class YuiPanel extends Panel { - - public static final String CONTENT_ID = "content"; - - private WebMarkupContainer dialog; - - private Map<String, Object> getDefaultConfig() { - Map<String, Object> map = new HashMap<String, Object>(); - map.put("constraintoviewport", true); - map.put("visible", false); - return map; - } - - public YuiPanel(String id, String heading, Component content, Map<String, Object> customConfig) { - super(id); - final Map<String, Object> config = getDefaultConfig(); - if(customConfig != null) { - config.putAll(customConfig); - } - add(HeaderContributor.forJavaScript("resources/yui/yahoo/yahoo-min.js")); - add(HeaderContributor.forJavaScript("resources/yui/event/event-min.js")); - add(HeaderContributor.forJavaScript("resources/yui/dom/dom-min.js")); - add(HeaderContributor.forJavaScript("resources/yui/dragdrop/dragdrop-min.js")); - add(HeaderContributor.forJavaScript("resources/yui/container/container-min.js")); - add(HeaderContributor.forCss("resources/yui/container/assets/container.css")); - - dialog = new WebMarkupContainer("dialog"); - dialog.setOutputMarkupId(true); - add(dialog); - dialog.add(new Label("heading", heading)); - dialog.add(content); - add(new HeaderContributor(new IHeaderContributor() { - public void renderHead(IHeaderResponse response) { - String markupId = dialog.getMarkupId(); - response.renderJavascript("var " + markupId + ";", null); - response.renderOnDomReadyJavascript(markupId + " = new YAHOO.widget.Panel('" + markupId + "'," - + " " + YuiUtils.getJson(config) + "); " + markupId + ".render()"); - } - })); - } - - public String getShowScript() { - return dialog.getMarkupId() + ".show();"; - } - - public String getHideScript() { - return dialog.getMarkupId() + ".hide();"; - } - -} Deleted: trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiUtils.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiUtils.java 2021-10-27 15:30:46 UTC (rev 1404) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/yui/YuiUtils.java 2021-10-27 15:57:10 UTC (rev 1405) @@ -1,55 +0,0 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.wicket.yui; - -import java.util.Map; - -/** - * utilities for doing some javascript stuff e.g. basic JSON serialization - */ -public class YuiUtils { - - /** - * custom Map to JSON converter - * TODO support values that should not be treated like strings (quoted) - */ - public static String getJson(Map<String, Object> map) { - StringBuilder sb = new StringBuilder(); - sb.append("{"); - boolean firstTime = true; - for(Map.Entry entry : map.entrySet()) { - if(firstTime) { - firstTime = false; - } else { - sb.append(", "); - } - sb.append(entry.getKey()); - sb.append(" : "); - Object value = entry.getValue(); - if(value instanceof Map) { - sb.append(getJson((Map) value)); - } else if(entry.getValue() instanceof String) { - sb.append("'" + value + "'"); - } else { - sb.append(value); - } - } - sb.append("}"); - return sb.toString(); - } - -} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-10-27 15:30:49
|
Revision: 1404 http://sourceforge.net/p/j-trac/code/1404 Author: udittmer Date: 2021-10-27 15:30:46 +0000 (Wed, 27 Oct 2021) Log Message: ----------- added the {U+201C}link configuration{U+201D} feature from the https://github.com/LibreNico/jtrac fork Modified Paths: -------------- trunk/jtrac/src/main/java/info/jtrac/Jtrac.java trunk/jtrac/src/main/java/info/jtrac/JtracDao.java trunk/jtrac/src/main/java/info/jtrac/JtracImpl.java trunk/jtrac/src/main/java/info/jtrac/hibernate/HibernateJtracDao.java trunk/jtrac/src/main/java/info/jtrac/wicket/DashboardPage.html trunk/jtrac/src/main/java/info/jtrac/wicket/DashboardPage.java trunk/jtrac/src/main/java/info/jtrac/wicket/IndividualHeadPanel.java trunk/jtrac/src/main/java/info/jtrac/wicket/OptionsPage.html trunk/jtrac/src/main/java/info/jtrac/wicket/OptionsPage.java trunk/jtrac/src/main/resources/jtrac.hbm.xml trunk/jtrac/src/main/resources/messages.properties trunk/jtrac/src/main/resources/messages_de.properties trunk/jtrac/src/main/resources/messages_en.properties Added Paths: ----------- trunk/jtrac/src/main/java/info/jtrac/domain/StoredSearch.java trunk/jtrac/src/main/java/info/jtrac/wicket/StoredSearchFormPage.html trunk/jtrac/src/main/java/info/jtrac/wicket/StoredSearchFormPage.java trunk/jtrac/src/main/java/info/jtrac/wicket/StoredSearchListPage.html trunk/jtrac/src/main/java/info/jtrac/wicket/StoredSearchListPage.java Modified: trunk/jtrac/src/main/java/info/jtrac/Jtrac.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/Jtrac.java 2021-10-27 08:20:04 UTC (rev 1403) +++ trunk/jtrac/src/main/java/info/jtrac/Jtrac.java 2021-10-27 15:30:46 UTC (rev 1404) @@ -1,124 +1,128 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac; - -import info.jtrac.domain.BatchInfo; -import info.jtrac.domain.Config; -import info.jtrac.domain.Counts; -import info.jtrac.domain.CountsHolder; -import info.jtrac.domain.Field; -import info.jtrac.domain.History; -import info.jtrac.domain.Item; -import info.jtrac.domain.ItemItem; -import info.jtrac.domain.ItemSearch; -import info.jtrac.domain.Metadata; -import info.jtrac.domain.Space; -import info.jtrac.domain.User; -import info.jtrac.domain.UserSpaceRole; - -import java.util.List; -import java.util.Map; - -import org.acegisecurity.userdetails.UserDetailsService; -import org.apache.wicket.markup.html.form.upload.FileUpload; - -/** - * Jtrac main business interface (Service Layer) - */ -public interface Jtrac extends UserDetailsService { - - // TODO remove Wicket dep with FileUpload - void storeItem(Item item, FileUpload fileUpload); - void storeItems(List<Item> items); - void updateItem(Item item, User user); - void storeHistoryForItem(long itemId, History history, FileUpload fileUpload); - Item loadItem(long id); - Item loadItemByRefId(String refId); - History loadHistory(long id); - List<Item> findItems(ItemSearch itemSearch); - int loadCountOfAllItems(); - List<Item> findAllItems(int firstResult, int batchSize); - void removeItem(Item item); - void removeItemItem(ItemItem itemItem); - //======================================================== - int loadCountOfRecordsHavingFieldNotNull(Space space, Field field); - int bulkUpdateFieldToNull(Space space, Field field); - int loadCountOfRecordsHavingFieldWithValue(Space space, Field field, int optionKey); - int bulkUpdateFieldToNullForValue(Space space, Field field, int optionKey); - int loadCountOfRecordsHavingStatus(Space space, int status); - int bulkUpdateStatusToOpen(Space space, int status); - int bulkUpdateRenameSpaceRole(Space space, String oldRoleKey, String newRoleKey); - int bulkUpdateDeleteSpaceRole(Space space, String roleKey); - //======================================================== - void storeUser(User user); - void storeUser(User user, String password, boolean sendNotifications); - void removeUser(User user); - User loadUser(long id); - User loadUser(String loginName); - List<User> findAllUsers(); - List<User> findUsersWhereIdIn(List<Long> ids); - List<User> findUsersMatching(String searchText, String searchOn); - List<User> findUsersForSpace(long spaceId); - List<UserSpaceRole> findUserRolesForSpace(long spaceId); - Map<Long, List<UserSpaceRole>> loadUserRolesMapForSpace(long spaceId); - Map<Long, List<UserSpaceRole>> loadSpaceRolesMapForUser(long userId); - List<User> findUsersWithRoleForSpace(long spaceId, String roleKey); - List<User> findUsersForUser(User user); - List<User> findUsersNotFullyAllocatedToSpace(long spaceId); - int loadCountOfHistoryInvolvingUser(User user); - //======================================================== - CountsHolder loadCountsForUser(User user); - Counts loadCountsForUserSpace(User user, Space space); - //======================================================== - void storeSpace(Space space); - Space loadSpace(long id); - Space loadSpace(String prefixCode); - List<Space> findAllSpaces(); - List<Space> findSpacesWhereIdIn(List<Long> ids); - List<Space> findSpacesWhereGuestAllowed(); - List<Space> findSpacesNotFullyAllocatedToUser(long userId); - void removeSpace(Space space); - //======================================================== - void storeUserSpaceRole(User user, Space space, String roleKey); - UserSpaceRole loadUserSpaceRole(long id); - void removeUserSpaceRole(UserSpaceRole userSpaceRole); - //======================================================== - void storeMetadata(Metadata metadata); - Metadata loadMetadata(long id); - //======================================================== - String generatePassword(); - String encodeClearText(String clearText); - Map<String, String> getLocales(); - String getDefaultLocale(); - String getJtracHome(); - int getAttachmentMaxSizeInMb(); - int getSessionTimeoutInMinutes(); - //======================================================== - Map<String, String> loadAllConfig(); - void storeConfig(Config config); - String loadConfig(String param); - //======================================================== - void rebuildIndexes(BatchInfo batchInfo); - boolean validateTextSearchQuery(String text); - //======================================================== - void executeHourlyTask(); - void executePollingTask(); - //======================================================== - String getReleaseVersion(); - String getReleaseTimestamp(); - -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac; + +import info.jtrac.domain.BatchInfo; +import info.jtrac.domain.Config; +import info.jtrac.domain.Counts; +import info.jtrac.domain.CountsHolder; +import info.jtrac.domain.Field; +import info.jtrac.domain.History; +import info.jtrac.domain.Item; +import info.jtrac.domain.ItemItem; +import info.jtrac.domain.ItemSearch; +import info.jtrac.domain.Metadata; +import info.jtrac.domain.Space; +import info.jtrac.domain.StoredSearch; +import info.jtrac.domain.User; +import info.jtrac.domain.UserSpaceRole; + +import java.util.List; +import java.util.Map; + +import org.acegisecurity.userdetails.UserDetailsService; +import org.apache.wicket.markup.html.form.upload.FileUpload; + +/** + * Jtrac main business interface (Service Layer) + */ +public interface Jtrac extends UserDetailsService { + + // TODO remove Wicket dep with FileUpload + void storeItem(Item item, FileUpload fileUpload); + void storeItems(List<Item> items); + void updateItem(Item item, User user); + void storeHistoryForItem(long itemId, History history, FileUpload fileUpload); + Item loadItem(long id); + Item loadItemByRefId(String refId); + History loadHistory(long id); + List<Item> findItems(ItemSearch itemSearch); + int loadCountOfAllItems(); + List<Item> findAllItems(int firstResult, int batchSize); + void removeItem(Item item); + void removeItemItem(ItemItem itemItem); + //======================================================== + int loadCountOfRecordsHavingFieldNotNull(Space space, Field field); + int bulkUpdateFieldToNull(Space space, Field field); + int loadCountOfRecordsHavingFieldWithValue(Space space, Field field, int optionKey); + int bulkUpdateFieldToNullForValue(Space space, Field field, int optionKey); + int loadCountOfRecordsHavingStatus(Space space, int status); + int bulkUpdateStatusToOpen(Space space, int status); + int bulkUpdateRenameSpaceRole(Space space, String oldRoleKey, String newRoleKey); + int bulkUpdateDeleteSpaceRole(Space space, String roleKey); + //======================================================== + void storeUser(User user); + void storeUser(User user, String password, boolean sendNotifications); + void removeUser(User user); + User loadUser(long id); + User loadUser(String loginName); + List<User> findAllUsers(); + List<User> findUsersWhereIdIn(List<Long> ids); + List<User> findUsersMatching(String searchText, String searchOn); + List<User> findUsersForSpace(long spaceId); + List<UserSpaceRole> findUserRolesForSpace(long spaceId); + Map<Long, List<UserSpaceRole>> loadUserRolesMapForSpace(long spaceId); + Map<Long, List<UserSpaceRole>> loadSpaceRolesMapForUser(long userId); + List<User> findUsersWithRoleForSpace(long spaceId, String roleKey); + List<User> findUsersForUser(User user); + List<User> findUsersNotFullyAllocatedToSpace(long spaceId); + int loadCountOfHistoryInvolvingUser(User user); + //======================================================== + CountsHolder loadCountsForUser(User user); + Counts loadCountsForUserSpace(User user, Space space); + //======================================================== + void storeSpace(Space space); + Space loadSpace(long id); + Space loadSpace(String prefixCode); + List<Space> findAllSpaces(); + List<Space> findSpacesWhereIdIn(List<Long> ids); + List<Space> findSpacesWhereGuestAllowed(); + List<Space> findSpacesNotFullyAllocatedToUser(long userId); + void removeSpace(Space space); + //======================================================== + void storeUserSpaceRole(User user, Space space, String roleKey); + UserSpaceRole loadUserSpaceRole(long id); + void removeUserSpaceRole(UserSpaceRole userSpaceRole); + //======================================================== + void storeMetadata(Metadata metadata); + Metadata loadMetadata(long id); + //======================================================== + String generatePassword(); + String encodeClearText(String clearText); + Map<String, String> getLocales(); + String getDefaultLocale(); + String getJtracHome(); + int getAttachmentMaxSizeInMb(); + int getSessionTimeoutInMinutes(); + //======================================================== + Map<String, String> loadAllConfig(); + void storeConfig(Config config); + String loadConfig(String param); + //======================================================== + void rebuildIndexes(BatchInfo batchInfo); + boolean validateTextSearchQuery(String text); + //======================================================== + void executeHourlyTask(); + void executePollingTask(); + //======================================================== + String getReleaseVersion(); + String getReleaseTimestamp(); + //======================================================== + List<StoredSearch> loadAllStoredSearch(); + void storeStoredSearch(StoredSearch storedSearch); + void removeStoredSearch(Long id); +} Modified: trunk/jtrac/src/main/java/info/jtrac/JtracDao.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/JtracDao.java 2021-10-27 08:20:04 UTC (rev 1403) +++ trunk/jtrac/src/main/java/info/jtrac/JtracDao.java 2021-10-27 15:30:46 UTC (rev 1404) @@ -30,6 +30,7 @@ import info.jtrac.domain.ItemItem; import info.jtrac.domain.ItemUser; import info.jtrac.domain.SpaceSequence; +import info.jtrac.domain.StoredSearch; import info.jtrac.domain.UserSpaceRole; import java.util.Collection; @@ -107,5 +108,9 @@ List<Config> findAllConfig(); void storeConfig(Config config); Config loadConfig(String key); - + //=========================================== + List<StoredSearch> findAllStoredSearch(); + void storeStoredSearch(StoredSearch storedSearch); + StoredSearch loadStoredSearch(Long id); + void removeStoredSearch(StoredSearch storedSearchToDel); } Modified: trunk/jtrac/src/main/java/info/jtrac/JtracImpl.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/JtracImpl.java 2021-10-27 08:20:04 UTC (rev 1403) +++ trunk/jtrac/src/main/java/info/jtrac/JtracImpl.java 2021-10-27 15:30:46 UTC (rev 1404) @@ -1,844 +1,866 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac; - -import info.jtrac.domain.Attachment; -import info.jtrac.domain.BatchInfo; -import info.jtrac.domain.Config; -import info.jtrac.domain.Counts; -import info.jtrac.domain.CountsHolder; -import info.jtrac.domain.Field; -import info.jtrac.domain.History; -import info.jtrac.domain.Item; -import info.jtrac.domain.ItemItem; -import info.jtrac.domain.ItemRefId; -import info.jtrac.domain.ItemSearch; -import info.jtrac.domain.ItemUser; -import info.jtrac.domain.Metadata; -import info.jtrac.domain.Role; -import info.jtrac.domain.Space; -import info.jtrac.domain.SpaceSequence; -import info.jtrac.domain.State; -import info.jtrac.domain.User; -import info.jtrac.domain.UserSpaceRole; -import info.jtrac.lucene.IndexSearcher; -import info.jtrac.lucene.Indexer; -import info.jtrac.mail.MailSender; -import info.jtrac.util.AttachmentUtils; -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; - -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Random; -import java.util.Set; - - -import org.acegisecurity.providers.encoding.PasswordEncoder; -import org.acegisecurity.userdetails.UserDetails; -import org.acegisecurity.userdetails.UsernameNotFoundException; -import org.springframework.context.MessageSource; -import org.springframework.util.StringUtils; -import org.apache.wicket.markup.html.form.upload.FileUpload; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * Jtrac Service Layer implementation - * This is where all the business logic is - * For data persistence this delegates to JtracDao - */ -public class JtracImpl implements Jtrac { - - private static final Logger logger = LoggerFactory.getLogger(JtracImpl.class); - - private JtracDao dao; - private PasswordEncoder passwordEncoder; - private MailSender mailSender; - private Indexer indexer; - private IndexSearcher indexSearcher; - private MessageSource messageSource; - - private Map<String, String> locales; - private String defaultLocale = "en"; - private String releaseVersion; - private String releaseTimestamp; - private String jtracHome; - private int attachmentMaxSizeInMb = 5; - private int sessionTimeoutInMinutes = 30; - - public void setLocaleList(String[] array) { - locales = new LinkedHashMap<String, String>(); - for(String localeString : array) { - Locale locale = StringUtils.parseLocaleString(localeString); - locales.put(localeString, localeString + " - " + locale.getDisplayName()); - } - logger.info("available locales configured " + locales); - } - - public void setDao(JtracDao dao) { - this.dao = dao; - } - - public void setPasswordEncoder(PasswordEncoder passwordEncoder) { - this.passwordEncoder = passwordEncoder; - } - - public void setIndexSearcher(IndexSearcher indexSearcher) { - this.indexSearcher = indexSearcher; - } - - public void setIndexer(Indexer indexer) { - this.indexer = indexer; - } - - public void setMessageSource(MessageSource messageSource) { - this.messageSource = messageSource; - } - - public void setReleaseTimestamp(String releaseTimestamp) { - this.releaseTimestamp = releaseTimestamp; - } - - public void setReleaseVersion(String releaseVersion) { - this.releaseVersion = releaseVersion; - } - - public void setJtracHome(String jtracHome) { - this.jtracHome = jtracHome; - } - - public String getJtracHome() { - return jtracHome; - } - - public int getAttachmentMaxSizeInMb() { - return attachmentMaxSizeInMb; - } - - public int getSessionTimeoutInMinutes() { - return sessionTimeoutInMinutes; - } - - /** - * this has not been factored into the util package or a helper class - * because it depends on the PasswordEncoder configured - */ - public String generatePassword() { - byte[] ab = new byte[1]; - Random r = new Random(); - r.nextBytes(ab); - return passwordEncoder.encodePassword(new String(ab), null).substring(24); - } - - /** - * this has not been factored into the util package or a helper class - * because it depends on the PasswordEncoder configured - */ - public String encodeClearText(String clearText) { - return passwordEncoder.encodePassword(clearText, null); - } - - public Map<String, String> getLocales() { - return locales; - } - - public String getDefaultLocale() { - return defaultLocale; - } - - /** - * this is automatically called by spring init-method hook on - * startup, also called whenever config is edited to refresh - * TODO move config into a settings class to reduce service clutter - */ - public void init() { - Map<String, String> config = loadAllConfig(); - initDefaultLocale(config.get("locale.default")); - initMailSender(config); - initAttachmentMaxSize(config.get("attachment.maxsize")); - initSessionTimeout(config.get("session.timeout")); - } - - private void initMailSender(Map<String, String> config) { - this.mailSender = new MailSender(config, messageSource, defaultLocale); - } - - private void initDefaultLocale(String localeString) { - if (localeString == null || !locales.containsKey(localeString)) { - logger.warn("invalid default locale configured = '" + localeString + "', using " + this.defaultLocale); - } else { - this.defaultLocale = localeString; - } - logger.info("default locale set to '" + this.defaultLocale + "'"); - } - - private void initAttachmentMaxSize(String s) { - try { - this.attachmentMaxSizeInMb = Integer.parseInt(s); - } catch(Exception e) { - logger.warn("invalid attachment max size '" + s + "', using " + attachmentMaxSizeInMb); - } - logger.info("attachment max size set to " + this.attachmentMaxSizeInMb + " MB"); - } - - private void initSessionTimeout(String s) { - try { - this.sessionTimeoutInMinutes = Integer.parseInt(s); - } catch(Exception e) { - logger.warn("invalid session timeout '" + s + "', using " + this.sessionTimeoutInMinutes); - } - logger.info("session timeout set to " + this.sessionTimeoutInMinutes + " minutes"); - } - - //========================================================================== - - private Attachment getAttachment(FileUpload fileUpload) { - if(fileUpload == null) { - return null; - } - logger.debug("fileUpload not null"); - String fileName = AttachmentUtils.cleanFileName(fileUpload.getClientFileName()); - Attachment attachment = new Attachment(); - attachment.setFileName(fileName); - dao.storeAttachment(attachment); - attachment.setFilePrefix(attachment.getId()); - return attachment; - } - - private void writeToFile(FileUpload fileUpload, Attachment attachment) { - if(fileUpload == null) { - return; - } - File file = AttachmentUtils.getFile(attachment, jtracHome); - try { - fileUpload.writeTo(file); - } catch (Exception e) { - throw new RuntimeException(e); - } - - } - - public synchronized void storeItem(Item item, FileUpload fileUpload) { - History history = new History(item); - Attachment attachment = getAttachment(fileUpload); - if(attachment != null) { - item.add(attachment); - history.setAttachment(attachment); - } - // timestamp can be set by import, then retain - Date now = item.getTimeStamp(); - if(now == null) { - now = new Date(); - } - item.setTimeStamp(now); - history.setTimeStamp(now); - item.add(history); - item.setSequenceNum(dao.loadNextSequenceNum(item.getSpace().getId())); - // this will at the moment execute unnecessary updates (bug in Hibernate handling of "version" property) - // se http://opensource.atlassian.com/projects/hibernate/browse/HHH-1401 - // TODO confirm if above does not happen anymore - dao.storeItem(item); - writeToFile(fileUpload, attachment); - if(indexer != null) { - indexer.index(item); - indexer.index(history); - } - if (item.isSendNotifications()) { - mailSender.send(item); - } - } - - public synchronized void storeItems(List<Item> items) { - for(Item item : items) { - item.setSendNotifications(false); - if(item.getStatus() == State.CLOSED) { - // we support CLOSED items for import also but for consistency - // simulate the item first created OPEN and then being CLOSED - item.setStatus(State.OPEN); - History history = new History(); - history.setTimeStamp(item.getTimeStamp()); - // need to do this as storeHistoryForItem does some role checks - // and so to avoid lazy initialization exception - history.setLoggedBy(loadUser(item.getLoggedBy().getId())); - history.setAssignedTo(item.getAssignedTo()); - history.setComment("-"); - history.setStatus(State.CLOSED); - history.setSendNotifications(false); - storeItem(item, null); - storeHistoryForItem(item.getId(), history, null); - } else { - storeItem(item, null); - } - } - } - - public synchronized void updateItem(Item item, User user) { - logger.debug("update item called"); - History history = new History(item); - history.setAssignedTo(null); - history.setStatus(null); - history.setLoggedBy(user); - history.setComment(item.getEditReason()); - history.setTimeStamp(new Date()); - item.add(history); - dao.storeItem(item); // merge edits + history - // TODO index? - if (item.isSendNotifications()) { - mailSender.send(item); - } - } - - public synchronized void storeHistoryForItem(long itemId, History history, FileUpload fileUpload) { - Item item = dao.loadItem(itemId); - // first apply edits onto item record before we change the item status - // the item.getEditableFieldList routine depends on the current State of the item - for(Field field : item.getEditableFieldList(history.getLoggedBy())) { - Object value = history.getValue(field.getName()); - if (value != null) { - item.setValue(field.getName(), value); - } - } - if (history.getStatus() != null) { - item.setStatus(history.getStatus()); - item.setAssignedTo(history.getAssignedTo()); // this may be null, when closing - } - item.setItemUsers(history.getItemUsers()); - // may have been set if this is an import - if(history.getTimeStamp() == null) { - history.setTimeStamp(new Date()); - } - Attachment attachment = getAttachment(fileUpload); - if(attachment != null) { - item.add(attachment); - history.setAttachment(attachment); - } - item.add(history); - dao.storeItem(item); - writeToFile(fileUpload, attachment); - if(indexer != null) { - indexer.index(history); - } - if (history.isSendNotifications()) { - mailSender.send(item); - } - } - - public Item loadItem(long id) { - return dao.loadItem(id); - } - - public Item loadItemByRefId(String refId) { - ItemRefId itemRefId = new ItemRefId(refId); // throws runtime exception if invalid id - List<Item> items = dao.findItems(itemRefId.getSequenceNum(), itemRefId.getPrefixCode()); - if (items.size() == 0) { - return null; - } - return items.get(0); - } - - public History loadHistory(long id) { - return dao.loadHistory(id); - } - - public List<Item> findItems(ItemSearch itemSearch) { - String searchText = itemSearch.getSearchText(); - if (searchText != null) { - List<Long> hits = indexSearcher.findItemIdsContainingText(searchText); - if (hits.size() == 0) { - itemSearch.setResultCount(0); - return Collections.<Item>emptyList(); - } - itemSearch.setItemIds(hits); - } - return dao.findItems(itemSearch); - } - - public int loadCountOfAllItems() { - return dao.loadCountOfAllItems(); - } - - public List<Item> findAllItems(int firstResult, int batchSize) { - return dao.findAllItems(firstResult, batchSize); - } - - public void removeItem(Item item) { - if(item.getRelatingItems() != null) { - for(ItemItem itemItem : item.getRelatingItems()) { - removeItemItem(itemItem); - } - } - if(item.getRelatedItems() != null) { - for(ItemItem itemItem : item.getRelatedItems()) { - removeItemItem(itemItem); - } - } - dao.removeItem(item); - } - - public void removeItemItem(ItemItem itemItem) { - dao.removeItemItem(itemItem); - } - - public int loadCountOfRecordsHavingFieldNotNull(Space space, Field field) { - return dao.loadCountOfRecordsHavingFieldNotNull(space, field); - } - - public int bulkUpdateFieldToNull(Space space, Field field) { - return dao.bulkUpdateFieldToNull(space, field); - } - - public int loadCountOfRecordsHavingFieldWithValue(Space space, Field field, int optionKey) { - return dao.loadCountOfRecordsHavingFieldWithValue(space, field, optionKey); - } - - public int bulkUpdateFieldToNullForValue(Space space, Field field, int optionKey) { - return dao.bulkUpdateFieldToNullForValue(space, field, optionKey); - } - - public int loadCountOfRecordsHavingStatus(Space space, int status) { - return dao.loadCountOfRecordsHavingStatus(space, status); - } - - public int bulkUpdateStatusToOpen(Space space, int status) { - return dao.bulkUpdateStatusToOpen(space, status); - } - - public int bulkUpdateRenameSpaceRole(Space space, String oldRoleKey, String newRoleKey) { - return dao.bulkUpdateRenameSpaceRole(space, oldRoleKey, newRoleKey); - } - - public int bulkUpdateDeleteSpaceRole(Space space, String roleKey) { - return dao.bulkUpdateDeleteSpaceRole(space, roleKey); - } - - // ========= Acegi UserDetailsService implementation ========== - public UserDetails loadUserByUsername(String loginName) { - List<User> users = null; - if (loginName.indexOf("@") != -1) { - users = dao.findUsersByEmail(loginName); - } else { - users = dao.findUsersByLoginName(loginName); - } - if (users.size() == 0) { - throw new UsernameNotFoundException("User not found for '" + loginName + "'"); - } - logger.debug("loadUserByUserName success for '" + loginName + "'"); - User user = users.get(0); - // if some spaces have guest access enabled, allocate these spaces as well - Set<Space> userSpaces = user.getSpaces(); - logger.debug("user spaces: " + userSpaces); - for(Space s : findSpacesWhereGuestAllowed()) { - if(!userSpaces.contains(s)) { - user.addSpaceWithRole(s, Role.ROLE_GUEST); - - } - } - for(UserSpaceRole usr : user.getSpaceRoles()) { - logger.debug("UserSpaceRole: " + usr); - // this is a hack, the effect of the next line would be to - // override hibernate lazy loading and get the space and associated metadata. - // since this only happens only once on authentication and simplifies a lot of - // code later because the security principal is "fully prepared", - // this is hopefully pardonable. The downside is that there may be as many extra db hits - // as there are spaces allocated for the user. Hibernate caching should alleviate this - usr.isAbleToCreateNewItem(); - } - return user; - } - - public User loadUser(long id) { - return dao.loadUser(id); - } - - public User loadUser(String loginName) { - List<User> users = dao.findUsersByLoginName(loginName); - if (users.size() == 0) { - return null; - } - return users.get(0); - } - - public void storeUser(User user) { - user.clearNonPersistentRoles(); - dao.storeUser(user); - } - - public void storeUser(User user, String password, boolean sendNotifications) { - if (password == null) { - password = generatePassword(); - } - user.setPassword(encodeClearText(password)); - storeUser(user); - if(sendNotifications) { - mailSender.sendUserPassword(user, password); - } - } - - public void removeUser(User user) { - for(ItemUser iu : dao.findItemUsersByUser(user)) { - dao.removeItemUser(iu); - } - dao.removeUser(user); - } - - public List<User> findAllUsers() { - return dao.findAllUsers(); - } - - public List<User> findUsersWhereIdIn(List<Long> ids) { - return dao.findUsersWhereIdIn(ids); - } - - public List<User> findUsersMatching(String searchText, String searchOn) { - return dao.findUsersMatching(searchText, searchOn); - } - - public List<User> findUsersForSpace(long spaceId) { - return dao.findUsersForSpace(spaceId); - } - - public List<UserSpaceRole> findUserRolesForSpace(long spaceId) { - return dao.findUserRolesForSpace(spaceId); - } - - public Map<Long, List<UserSpaceRole>> loadUserRolesMapForSpace(long spaceId) { - List<UserSpaceRole> list = dao.findUserRolesForSpace(spaceId); - Map<Long, List<UserSpaceRole>> map = new LinkedHashMap<Long, List<UserSpaceRole>>(); - for(UserSpaceRole usr : list) { - long userId = usr.getUser().getId(); - List<UserSpaceRole> value = map.get(userId); - if(value == null) { - value = new ArrayList<UserSpaceRole>(); - map.put(userId, value); - } - value.add(usr); - } - return map; - } - - public Map<Long, List<UserSpaceRole>> loadSpaceRolesMapForUser(long userId) { - List<UserSpaceRole> list = dao.findSpaceRolesForUser(userId); - Map<Long, List<UserSpaceRole>> map = new LinkedHashMap<Long, List<UserSpaceRole>>(); - for(UserSpaceRole usr : list) { - long spaceId = usr.getSpace() == null ? 0 : usr.getSpace().getId(); - List<UserSpaceRole> value = map.get(spaceId); - if(value == null) { - value = new ArrayList<UserSpaceRole>(); - map.put(spaceId, value); - } - value.add(usr); - } - return map; - } - - public List<User> findUsersWithRoleForSpace(long spaceId, String roleKey) { - return dao.findUsersWithRoleForSpace(spaceId, roleKey); - } - - public List<User> findUsersForUser(User user) { - Set<Space> spaces = user.getSpaces(); - if(spaces.size() == 0) { - // this will happen when a user has no spaces allocated - return Collections.emptyList(); - } - // must be a better way to make this unique? - List<User> users = dao.findUsersForSpaceSet(spaces); - Set<User> userSet = new LinkedHashSet<User>(users); - return new ArrayList<User>(userSet); - } - - public List<User> findUsersNotFullyAllocatedToSpace(long spaceId) { - // trying to reduce database hits and lazy loading as far as possible - List<User> notAtAllAllocated = dao.findUsersNotAllocatedToSpace(spaceId); - List<UserSpaceRole> usrs = dao.findUserRolesForSpace(spaceId); - List<User> notFullyAllocated = new ArrayList<User>(notAtAllAllocated); - if(usrs.size() == 0) { - return notFullyAllocated; - } - Space space = usrs.get(0).getSpace(); - Set<UserSpaceRole> allocated = new HashSet(usrs); - Set<String> roleKeys = new HashSet(space.getMetadata().getAllRoleKeys()); - Set<User> processed = new HashSet<User>(usrs.size()); - Set<User> superUsers = new HashSet(dao.findSuperUsers()); - for(UserSpaceRole usr : usrs) { - User user = usr.getUser(); - if(processed.contains(user)) { - continue; - } - processed.add(user); - // not using the user object as it is db intensive - boolean isSuperUser = superUsers.contains(user); - for(String roleKey : roleKeys) { - if(isSuperUser && Role.isAdmin(roleKey)) { - continue; - } - UserSpaceRole temp = new UserSpaceRole(user, space, roleKey); - if(!allocated.contains(temp)) { - notFullyAllocated.add(user); - break; - } - } - } - Collections.sort(notFullyAllocated); - return notFullyAllocated; - } - - public int loadCountOfHistoryInvolvingUser(User user) { - return dao.loadCountOfHistoryInvolvingUser(user); - } - - //========================================================================== - - public CountsHolder loadCountsForUser(User user) { - return dao.loadCountsForUser(user); - } - - public Counts loadCountsForUserSpace(User user, Space space) { - return dao.loadCountsForUserSpace(user, space); - } - - //========================================================================== - - public void storeUserSpaceRole(User user, Space space, String roleKey) { - user.addSpaceWithRole(space, roleKey); - storeUser(user); - } - - public void removeUserSpaceRole(UserSpaceRole userSpaceRole) { - User user = userSpaceRole.getUser(); - user.removeSpaceWithRole(userSpaceRole.getSpace(), userSpaceRole.getRoleKey()); - // dao.storeUser(user); - dao.removeUserSpaceRole(userSpaceRole); - } - - public UserSpaceRole loadUserSpaceRole(long id) { - return dao.loadUserSpaceRole(id); - } - - //========================================================================== - - public Space loadSpace(long id) { - return dao.loadSpace(id); - } - - public Space loadSpace(String prefixCode) { - List<Space> spaces = dao.findSpacesByPrefixCode(prefixCode); - if (spaces.size() == 0) { - return null; - } - return spaces.get(0); - } - - public void storeSpace(Space space) { - boolean newSpace = space.getId() == 0; - dao.storeSpace(space); - if(newSpace) { - SpaceSequence ss = new SpaceSequence(); - ss.setNextSeqNum(1); - ss.setId(space.getId()); - dao.storeSpaceSequence(ss); - } - } - - public List<Space> findAllSpaces() { - return dao.findAllSpaces(); - } - - public List<Space> findSpacesWhereIdIn(List<Long> ids) { - return dao.findSpacesWhereIdIn(ids); - } - - public List<Space> findSpacesWhereGuestAllowed() { - return dao.findSpacesWhereGuestAllowed(); - } - - public List<Space> findSpacesNotFullyAllocatedToUser(long userId) { - // trying to reduce database hits and lazy loading as far as possible - List<Space> notAtAllAllocated = dao.findSpacesNotAllocatedToUser(userId); - List<UserSpaceRole> usrs = dao.findSpaceRolesForUser(userId); - List<Space> notFullyAllocated = new ArrayList(notAtAllAllocated); - if(usrs.size() == 0) { - return notFullyAllocated; - } - Set<UserSpaceRole> allocated = new HashSet(usrs); - Set<Space> processed = new HashSet<Space>(usrs.size()); - User user = usrs.get(0).getUser(); - boolean isSuperUser = user.isSuperUser(); - for(UserSpaceRole usr : usrs) { - Space space = usr.getSpace(); - if(space == null || processed.contains(space)) { - continue; - } - processed.add(space); - for(String roleKey : space.getMetadata().getAllRoleKeys()) { - if(isSuperUser && Role.isAdmin(roleKey)) { - continue; - } - UserSpaceRole temp = new UserSpaceRole(user, space, roleKey); - if(!allocated.contains(temp)) { - notFullyAllocated.add(space); - break; - } - } - } - Collections.sort(notFullyAllocated); - return notFullyAllocated; - } - - public void removeSpace(Space space) { - logger.info("proceeding to delete space: " + space); - dao.bulkUpdateDeleteSpaceRole(space, null); - dao.bulkUpdateDeleteItemsForSpace(space); - dao.removeSpace(space); - logger.info("successfully deleted space"); - } - - //========================================================================== - - public void storeMetadata(Metadata metadata) { - dao.storeMetadata(metadata); - } - - public Metadata loadMetadata(long id) { - return dao.loadMetadata(id); - } - - //========================================================================== - - public Map<String, String> loadAllConfig() { - List<Config> list = dao.findAllConfig(); - Map<String, String> allConfig = new HashMap<String, String>(list.size()); - for (Config c : list) { - allConfig.put(c.getParam(), c.getValue()); - } - return allConfig; - } - - // TODO must be some nice generic way to do this - public void storeConfig(Config config) { - dao.storeConfig(config); - if(config.isMailConfig()) { - initMailSender(loadAllConfig()); - } else if(config.isLocaleConfig()) { - initDefaultLocale(config.getValue()); - } else if(config.isAttachmentConfig()) { - initAttachmentMaxSize(config.getValue()); - } else if(config.isSessionTimeoutConfig()) { - initSessionTimeout(config.getValue()); - } - } - - public String loadConfig(String param) { - Config config = dao.loadConfig(param); - if (config == null) { - return null; - } - String value = config.getValue(); - if (value == null || value.trim().equals("")) { - return null; - } - return value; - } - - //======================================================== - - public void rebuildIndexes(BatchInfo batchInfo) { - File file = new File(jtracHome + "/indexes"); - for (File f : file.listFiles()) { - logger.debug("deleting file: " + f); - f.delete(); - } - logger.info("existing index files deleted successfully"); - int totalSize = dao.loadCountOfAllItems(); - batchInfo.setTotalSize(totalSize); - logger.info("total items to index: " + totalSize); - int firstResult = 0; - long lastFetchedId = 0; - while(true) { - logger.info("processing batch starting from: " + firstResult + ", current: " + batchInfo.getCurrentPosition()); - List<Item> items = dao.findAllItems(firstResult, batchInfo.getBatchSize()); - for (Item item : items) { - - indexer.index(item); - - // currently history is indexed separately from item - // not sure if this is a good thing, maybe it gives - // more flexibility e.g. fine-grained search results - - int historyCount = 0; - for(History history : item.getHistory()) { - indexer.index(history); - historyCount++; - } - if(logger.isDebugEnabled()) { - logger.debug("indexed item: " + item.getId() - + " : " + item.getRefId() + ", history: " + historyCount); - } - batchInfo.incrementPosition(); - lastFetchedId = item.getId(); - } - if(logger.isDebugEnabled()) { - logger.debug("size of current batch: " + items.size()); - logger.debug("last fetched Id: " + lastFetchedId); - } - firstResult += batchInfo.getBatchSize(); - if(logger.isDebugEnabled()) { - logger.debug("setting firstResult to: " + firstResult); - } - if(batchInfo.isComplete()) { - logger.info("batch completed at position: " + batchInfo.getCurrentPosition()); - break; - } - } - - } - - public boolean validateTextSearchQuery(String text) { - return indexSearcher.validateQuery(text); - } - - //========================================================================== - - public void executeHourlyTask() { - logger.debug("hourly task called"); - } - - /* configured to be called every five minutes */ - public void executePollingTask() { - logger.debug("polling task called"); - } - - //========================================================================== - - public String getReleaseVersion() { - return releaseVersion; - } - - public String getReleaseTimestamp() { - return releaseTimestamp; - } - -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac; + +import info.jtrac.domain.Attachment; +import info.jtrac.domain.BatchInfo; +import info.jtrac.domain.Config; +import info.jtrac.domain.Counts; +import info.jtrac.domain.CountsHolder; +import info.jtrac.domain.Field; +import info.jtrac.domain.History; +import info.jtrac.domain.Item; +import info.jtrac.domain.ItemItem; +import info.jtrac.domain.ItemRefId; +import info.jtrac.domain.ItemSearch; +import info.jtrac.domain.ItemUser; +import info.jtrac.domain.Metadata; +import info.jtrac.domain.Role; +import info.jtrac.domain.Space; +import info.jtrac.domain.SpaceSequence; +import info.jtrac.domain.State; +import info.jtrac.domain.StoredSearch; +import info.jtrac.domain.User; +import info.jtrac.domain.UserSpaceRole; +import info.jtrac.lucene.IndexSearcher; +import info.jtrac.lucene.Indexer; +import info.jtrac.mail.MailSender; +import info.jtrac.util.AttachmentUtils; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import org.acegisecurity.providers.encoding.PasswordEncoder; +import org.acegisecurity.userdetails.UserDetails; +import org.acegisecurity.userdetails.UsernameNotFoundException; + +import org.springframework.context.MessageSource; +import org.springframework.util.StringUtils; + +import org.apache.wicket.markup.html.form.upload.FileUpload; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Jtrac Service Layer implementation + * This is where all the business logic is + * For data persistence this delegates to JtracDao + */ +public class JtracImpl implements Jtrac { + + private static final Logger logger = LoggerFactory.getLogger(JtracImpl.class); + + private JtracDao dao; + private PasswordEncoder passwordEncoder; + private MailSender mailSender; + private Indexer indexer; + private IndexSearcher indexSearcher; + private MessageSource messageSource; + + private Map<String, String> locales; + private String defaultLocale = "en"; + private String releaseVersion; + private String releaseTimestamp; + private String jtracHome; + private int attachmentMaxSizeInMb = 5; + private int sessionTimeoutInMinutes = 30; + + public void setLocaleList(String[] array) { + locales = new LinkedHashMap<String, String>(); + for(String localeString : array) { + Locale locale = StringUtils.parseLocaleString(localeString); + locales.put(localeString, localeString + " - " + locale.getDisplayName()); + } + logger.info("available locales configured " + locales); + } + + public void setDao(JtracDao dao) { + this.dao = dao; + } + + public void setPasswordEncoder(PasswordEncoder passwordEncoder) { + this.passwordEncoder = passwordEncoder; + } + + public void setIndexSearcher(IndexSearcher indexSearcher) { + this.indexSearcher = indexSearcher; + } + + public void setIndexer(Indexer indexer) { + this.indexer = indexer; + } + + public void setMessageSource(MessageSource messageSource) { + this.messageSource = messageSource; + } + + public void setReleaseTimestamp(String releaseTimestamp) { + this.releaseTimestamp = releaseTimestamp; + } + + public void setReleaseVersion(String releaseVersion) { + this.releaseVersion = releaseVersion; + } + + public void setJtracHome(String jtracHome) { + this.jtracHome = jtracHome; + } + + public String getJtracHome() { + return jtracHome; + } + + public int getAttachmentMaxSizeInMb() { + return attachmentMaxSizeInMb; + } + + public int getSessionTimeoutInMinutes() { + return sessionTimeoutInMinutes; + } + + /** + * this has not been factored into the util package or a helper class + * because it depends on the PasswordEncoder configured + */ + public String generatePassword() { + byte[] ab = new byte[1]; + Random r = new Random(); + r.nextBytes(ab); + return passwordEncoder.encodePassword(new String(ab), null).substring(24); + } + + /** + * this has not been factored into the util package or a helper class + * because it depends on the PasswordEncoder configured + */ + public String encodeClearText(String clearText) { + return passwordEncoder.encodePassword(clearText, null); + } + + public Map<String, String> getLocales() { + return locales; + } + + public String getDefaultLocale() { + return defaultLocale; + } + + /** + * this is automatically called by spring init-method hook on + * startup, also called whenever config is edited to refresh + * TODO move config into a settings class to reduce service clutter + */ + public void init() { + Map<String, String> config = loadAllConfig(); + initDefaultLocale(config.get("locale.default")); + initMailSender(config); + initAttachmentMaxSize(config.get("attachment.maxsize")); + initSessionTimeout(config.get("session.timeout")); + } + + private void initMailSender(Map<String, String> config) { + this.mailSender = new MailSender(config, messageSource, defaultLocale); + } + + private void initDefaultLocale(String localeString) { + if (localeString == null || !locales.containsKey(localeString)) { + logger.warn("invalid default locale configured = '" + localeString + "', using " + this.defaultLocale); + } else { + this.defaultLocale = localeString; + } + logger.info("default locale set to '" + this.defaultLocale + "'"); + } + + private void initAttachmentMaxSize(String s) { + try { + this.attachmentMaxSizeInMb = Integer.parseInt(s); + } catch(Exception e) { + logger.warn("invalid attachment max size '" + s + "', using " + attachmentMaxSizeInMb); + } + logger.info("attachment max size set to " + this.attachmentMaxSizeInMb + " MB"); + } + + private void initSessionTimeout(String s) { + try { + this.sessionTimeoutInMinutes = Integer.parseInt(s); + } catch(Exception e) { + logger.warn("invalid session timeout '" + s + "', using " + this.sessionTimeoutInMinutes); + } + logger.info("session timeout set to " + this.sessionTimeoutInMinutes + " minutes"); + } + + //========================================================================== + + private Attachment getAttachment(FileUpload fileUpload) { + if(fileUpload == null) { + return null; + } + logger.debug("fileUpload not null"); + String fileName = AttachmentUtils.cleanFileName(fileUpload.getClientFileName()); + Attachment attachment = new Attachment(); + attachment.setFileName(fileName); + dao.storeAttachment(attachment); + attachment.setFilePrefix(attachment.getId()); + return attachment; + } + + private void writeToFile(FileUpload fileUpload, Attachment attachment) { + if(fileUpload == null) { + return; + } + File file = AttachmentUtils.getFile(attachment, jtracHome); + try { + fileUpload.writeTo(file); + } catch (Exception e) { + throw new RuntimeException(e); + } + + } + + public synchronized void storeItem(Item item, FileUpload fileUpload) { + History history = new History(item); + Attachment attachment = getAttachment(fileUpload); + if(attachment != null) { + item.add(attachment); + history.setAttachment(attachment); + } + // timestamp can be set by import, then retain + Date now = item.getTimeStamp(); + if(now == null) { + now = new Date(); + } + item.setTimeStamp(now); + history.setTimeStamp(now); + item.add(history); + item.setSequenceNum(dao.loadNextSequenceNum(item.getSpace().getId())); + // this will at the moment execute unnecessary updates (bug in Hibernate handling of "version" property) + // se http://opensource.atlassian.com/projects/hibernate/browse/HHH-1401 + // TODO confirm if above does not happen anymore + dao.storeItem(item); + writeToFile(fileUpload, attachment); + if(indexer != null) { + indexer.index(item); + indexer.index(history); + } + if (item.isSendNotifications()) { + mailSender.send(item); + } + } + + public synchronized void storeItems(List<Item> items) { + for(Item item : items) { + item.setSendNotifications(false); + if(item.getStatus() == State.CLOSED) { + // we support CLOSED items for import also but for consistency + // simulate the item first created OPEN and then being CLOSED + item.setStatus(State.OPEN); + History history = new History(); + history.setTimeStamp(item.getTimeStamp()); + // need to do this as storeHistoryForItem does some role checks + // and so to avoid lazy initialization exception + history.setLoggedBy(loadUser(item.getLoggedBy().getId())); + history.setAssignedTo(item.getAssignedTo()); + history.setComment("-"); + history.setStatus(State.CLOSED); + history.setSendNotifications(false); + storeItem(item, null); + storeHistoryForItem(item.getId(), history, null); + } else { + storeItem(item, null); + } + } + } + + public synchronized void updateItem(Item item, User user) { + logger.debug("update item called"); + History history = new History(item); + history.setAssignedTo(null); + history.setStatus(null); + history.setLoggedBy(user); + history.setComment(item.getEditReason()); + history.setTimeStamp(new Date()); + item.add(history); + dao.storeItem(item); // merge edits + history + // TODO index? + if (item.isSendNotifications()) { + mailSender.send(item); + } + } + + public synchronized void storeHistoryForItem(long itemId, History history, FileUpload fileUpload) { + Item item = dao.loadItem(itemId); + // first apply edits onto item record before we change the item status + // the item.getEditableFieldList routine depends on the current State of the item + for(Field field : item.getEditableFieldList(history.getLoggedBy())) { + Object value = history.getValue(field.getName()); + if (value != null) { + item.setValue(field.getName(), value); + } + } + if (history.getStatus() != null) { + item.setStatus(history.getStatus()); + item.setAssignedTo(history.getAssignedTo()); // this may be null, when closing + } + item.setItemUsers(history.getItemUsers()); + // may have been set if this is an import + if(history.getTimeStamp() == null) { + history.setTimeStamp(new Date()); + } + Attachment attachment = getAttachment(fileUpload); + if(attachment != null) { + item.add(attachment); + history.setAttachment(attachment); + } + item.add(history); + dao.storeItem(item); + writeToFile(fileUpload, attachment); + if(indexer != null) { + indexer.index(history); + } + if (history.isSendNotifications()) { + mailSender.send(item); + } + } + + public Item loadItem(long id) { + return dao.loadItem(id); + } + + public Item loadItemByRefId(String refId) { + ItemRefId itemRefId = new ItemRefId(refId); // throws runtime exception if invalid id + List<Item> items = dao.findItems(itemRefId.getSequenceNum(), itemRefId.getPrefixCode()); + if (items.size() == 0) { + return null; + } + return items.get(0); + } + + public History loadHistory(long id) { + return dao.loadHistory(id); + } + + public List<Item> findItems(ItemSearch itemSearch) { + String searchText = itemSearch.getSearchText(); + if (searchText != null) { + List<Long> hits = indexSearcher.findItemIdsContainingText(searchText); + if (hits.size() == 0) { + itemSearch.setResultCount(0); + return Collections.<Item>emptyList(); + } + itemSearch.setItemIds(hits); + } + return dao.findItems(itemSearch); + } + + public int loadCountOfAllItems() { + return dao.loadCountOfAllItems(); + } + + public List<Item> findAllItems(int firstResult, int batchSize) { + return dao.findAllItems(firstResult, batchSize); + } + + public void removeItem(Item item) { + if(item.getRelatingItems() != null) { + for(ItemItem itemItem : item.getRelatingItems()) { + removeItemItem(itemItem); + } + } + if(item.getRelatedItems() != null) { + for(ItemItem itemItem : item.getRelatedItems()) { + removeItemItem(itemItem); + } + } + dao.removeItem(item); + } + + public void removeItemItem(ItemItem itemItem) { + dao.removeItemItem(itemItem); + } + + public int loadCountOfRecordsHavingFieldNotNull(Space space, Field field) { + return dao.loadCountOfRecordsHavingFieldNotNull(space, field); + } + + public int bulkUpdateFieldToNull(Space space, Field field) { + return dao.bulkUpdateFieldToNull(space, field); + } + + public int loadCountOfRecordsHavingFieldWithValue(Space space, Field field, int optionKey) { + return dao.loadCountOfRecordsHavingFieldWithValue(space, field, optionKey); + } + + public int bulkUpdateFieldToNullForValue(Space space, Field field, int optionKey) { + return dao.bulkUpdateFieldToNullForValue(space, field, optionKey); + } + + public int loadCountOfRecordsHavingStatus(Space space, int status) { + return dao.loadCountOfRecordsHavingStatus(space, status); + } + + public int bulkUpdateStatusToOpen(Space space, int status) { + return dao.bulkUp... [truncated message content] |
From: <udi...@us...> - 2021-10-27 08:20:06
|
Revision: 1403 http://sourceforge.net/p/j-trac/code/1403 Author: udittmer Date: 2021-10-27 08:20:04 +0000 (Wed, 27 Oct 2021) Log Message: ----------- finalize Markdown support Modified Paths: -------------- trunk/jtrac/pom.xml trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java trunk/jtrac/src/main/java/info/jtrac/wicket/BasePanel.java trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewPanel.java Modified: trunk/jtrac/pom.xml =================================================================== --- trunk/jtrac/pom.xml 2021-10-26 15:22:35 UTC (rev 1402) +++ trunk/jtrac/pom.xml 2021-10-27 08:20:04 UTC (rev 1403) @@ -4,7 +4,7 @@ <groupId>info.jtrac</groupId> <artifactId>jtrac</artifactId> <packaging>war</packaging> - <version>2.2.0-b2</version> + <version>2.2.0-b3</version> <name>JTrac</name> <description> JTrac is a generic issue-tracking web-application that can be easily customized by adding custom fields Modified: trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java 2021-10-26 15:22:35 UTC (rev 1402) +++ trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java 2021-10-27 08:20:04 UTC (rev 1403) @@ -26,6 +26,7 @@ import info.jtrac.domain.Space; import info.jtrac.domain.User; import info.jtrac.exception.JtracSecurityException; +import info.jtrac.wicket.JtracApplication; import java.io.BufferedReader; import java.io.StringReader; @@ -48,7 +49,7 @@ import org.commonmark.Extension; import org.commonmark.node.*; import org.commonmark.parser.Parser; -import org.commonmark.renderer.html.HtmlRenderer; +import org.commonmark.renderer.html.*; import org.commonmark.ext.autolink.AutolinkExtension; import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension; import org.commonmark.ext.ins.InsExtension; @@ -73,8 +74,27 @@ extensions.add(AutolinkExtension.create()); extensions.add(StrikethroughExtension.create()); extensions.add(InsExtension.create()); - parser = Parser.builder().extensions(extensions).build(); - renderer = HtmlRenderer.builder().extensions(extensions).sanitizeUrls(true).build(); + parser = Parser.builder() + .extensions(extensions) + .build(); + renderer = HtmlRenderer.builder() + .extensions(extensions) + .attributeProviderFactory(new AttributeProviderFactory() { + @Override + public AttributeProvider create (AttributeProviderContext context) { + return new AttributeProvider() { + @Override + public void setAttributes (Node node, String tagName, Map<String, String> attributes) { + // if (tagName.equals("a")) would probably work as well + if (node instanceof Link) { + attributes.put("target", "_blank"); + } + } + }; + } + }) + .sanitizeUrls(true) + .build(); } /** @@ -114,15 +134,22 @@ throw new RuntimeException(e); } return sb.toString().replaceAll("\t", " "); - } + } public static String renderMarkdown (String text) { if (text == null) { return null; } else { - Node document = parser.parse(text); - return renderer.render(document); -// return text.replaceAll(URL_PATTERN, "<a href='$0' target='_blank'>$0</a>"); + String markdown = JtracApplication.get().getJtrac().loadConfig("markdown.enabled"); + if (markdown!=null && markdown.equalsIgnoreCase("true")) { + // apply markdown + Node document = parser.parse(text); + return renderer.render(document); + } else { + // at least auto-link URLs + text = fixWhiteSpace(text); + return text.replaceAll(URL_PATTERN, "<a href='$0' target='_blank'>$0</a>"); + } } } @@ -144,17 +171,15 @@ return getAsHtml(item, request, response, messageSource, locale); } - private static String getAsHtml(Item item, HttpServletRequest request, HttpServletResponse response, - MessageSource ms, Locale loc) { - + private static String getAsHtml (Item item, HttpServletRequest request, HttpServletResponse response, MessageSource ms, Locale loc) { boolean isWeb = request != null && response != null; - + String tableStyle = " class='jtrac'"; String tdStyle = ""; String thStyle = ""; String altStyle = " class='alt'"; String labelStyle = " class='label'"; - + if (!isWeb) { // inline CSS so that HTML mail works across most mail-reader clients String tdCommonStyle = "border: 1px solid black"; Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/BasePanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/BasePanel.java 2021-10-26 15:22:35 UTC (rev 1402) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/BasePanel.java 2021-10-27 08:20:04 UTC (rev 1403) @@ -65,13 +65,4 @@ m.setLocalizer(getLocalizer()); return m.getString(); } - - protected boolean markdown() { - String markdown = getJtrac().loadConfig("markdown.enabled"); - if (StringUtils.hasText(markdown)) { - return markdown.trim().equalsIgnoreCase("true"); - } else { - return false; - } - } } Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewPanel.java 2021-10-26 15:22:35 UTC (rev 1402) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewPanel.java 2021-10-27 08:20:04 UTC (rev 1403) @@ -189,10 +189,8 @@ add(new Label("loggedBy", new PropertyModel(item, "loggedBy.name"))); add(new Label("assignedTo", new PropertyModel(item, "assignedTo.name"))); add(new Label("summary", new PropertyModel(item, "summary"))); - String text = ItemUtils.fixWhiteSpace(item.getDetail()); - if (markdown()) { - text = ItemUtils.renderMarkdown(text); - } + String text = item.getDetail(); + text = ItemUtils.renderMarkdown(text); add(new Label("detail", text).setEscapeModelStrings(false)); final SimpleAttributeModifier sam = new SimpleAttributeModifier("class", "alt"); @@ -243,13 +241,11 @@ WebMarkupContainer comment = new WebMarkupContainer("comment"); comment.add(new AttachmentLinkPanel("attachment", h.getAttachment())); - String text = ItemUtils.fixWhiteSpace(h.getComment()); - if (markdown()) { - text = ItemUtils.renderMarkdown(text); - } + String text = h.getComment(); + text = ItemUtils.renderMarkdown(text); comment.add(new Label("comment", text).setEscapeModelStrings(false)); listItem.add(comment); - + listItem.add(new Label("timeStamp", DateUtils.formatTimeStamp(h.getTimeStamp()))); listItem.add(new ListView("fields", editable) { /* (non-Javadoc) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-10-26 15:22:38
|
Revision: 1402 http://sourceforge.net/p/j-trac/code/1402 Author: udittmer Date: 2021-10-26 15:22:35 +0000 (Tue, 26 Oct 2021) Log Message: ----------- instead of just auto-linking URLs, enable Markdown syntax Modified Paths: -------------- trunk/jtrac/pom.xml trunk/jtrac/src/main/java/info/jtrac/config/JtracConfigurer.java trunk/jtrac/src/main/java/info/jtrac/domain/Config.java trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java trunk/jtrac/src/main/java/info/jtrac/web/RestMultiActionController.java trunk/jtrac/src/main/java/info/jtrac/wicket/BasePanel.java trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigListPage.java trunk/jtrac/src/main/java/info/jtrac/wicket/IndividualHeadPanel.java trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewPanel.java trunk/jtrac/src/main/resources/messages.properties trunk/jtrac/src/main/resources/messages_de.properties trunk/jtrac/src/main/resources/messages_en.properties Modified: trunk/jtrac/pom.xml =================================================================== --- trunk/jtrac/pom.xml 2021-10-15 13:20:11 UTC (rev 1401) +++ trunk/jtrac/pom.xml 2021-10-26 15:22:35 UTC (rev 1402) @@ -430,6 +430,26 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>org.commonmark</groupId> + <artifactId>commonmark</artifactId> + <version>0.18.0</version> + </dependency> + <dependency> + <groupId>org.commonmark</groupId> + <artifactId>commonmark-ext-gfm-strikethrough</artifactId> + <version>0.18.0</version> + </dependency> + <dependency> + <groupId>org.commonmark</groupId> + <artifactId>commonmark-ext-ins</artifactId> + <version>0.18.0</version> + </dependency> + <dependency> + <groupId>org.commonmark</groupId> + <artifactId>commonmark-ext-autolink</artifactId> + <version>0.18.0</version> + </dependency> <!-- <dependency> <groupId>org.openqa.selenium.client-drivers</groupId> Modified: trunk/jtrac/src/main/java/info/jtrac/config/JtracConfigurer.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/config/JtracConfigurer.java 2021-10-15 13:20:11 UTC (rev 1401) +++ trunk/jtrac/src/main/java/info/jtrac/config/JtracConfigurer.java 2021-10-26 15:22:35 UTC (rev 1402) @@ -1,243 +1,243 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.config; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FilenameFilter; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.io.Writer; -import java.util.Properties; -import javax.servlet.ServletContext; -import org.springframework.beans.factory.BeanCreationException; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.FileSystemResource; -import org.springframework.web.context.ServletContextAware; - -/** - * <p> - * Custom extension of the Spring PropertyPlaceholderConfigurer that - * sets up the jtrac.home System property (creates if required) and also creates - * a default jtrac.properties file for HSQLDB - useful for those who want - * to quickly evaluate JTrac. Just dropping the war into a servlet container - * would work without the need to even configure a datasource. - * </p> - * <p> - * This class would effectively do nothing if a <code>jtrac.properties</code> file exists in jtrac.home - * </p> - * <ol> - * <li>A "jtrac.home" property is looked for in <code>/WEB-INF/classes/jtrac-init.properties</code></li> - * <li>if not found, then a <code>jtrac.home</code> system property is checked for</li> - * <li>then a servlet context init-parameter called <code>jtrac.home</code> is looked for</li> - * <li>last resort, a <code>.jtrac</code> folder is created in the <code>user.home</code> and used as <code>jtrac.home</code></li> - * </ol> - * - * <p> - * Other tasks - * </p> - * <ul> - * <li>initialize the "test" query for checking idle database connections</li> - * <li>initialize list of available locales based on the properties files available</li> - * </ul> - * - * <p> - * Also playing an important role during startup are the following factory beans: - * </p> - * <ul> - * <li>DataSourceFactoryBean:</li> - * <ul> - * <li>switches between embedded HSQLDB or Apache DBCP (connection pool)</li> - * <li>performs graceful shutdown of database if embedded HSQLDB</li> - * </ul> - * <li>ProviderManagerFactoryBean</li> - * <ul> - * <li>conditionally includes LDAP authentication if requested</li> - * </ul> - * </ul> - * - * <p> - * Note that later on during startup, the HibernateJtracDao would check if - * database tables exist, and if they don't, would proceed to create them. - * </p> - */ - -public class JtracConfigurer extends PropertyPlaceholderConfigurer implements ServletContextAware { - - private ServletContext servletContext; - - public void setServletContext(ServletContext servletContext) { - this.servletContext = servletContext; - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { - // do our custom configuration before spring gets a chance to - try { - configureJtrac(); - } catch(Exception e) { - throw new BeanCreationException("JtracConfigurer failed", e); - } - super.postProcessBeanFactory(beanFactory); - } - - private void configureJtrac() throws Exception { - String jtracHome = null; - ClassPathResource jtracInitResource = new ClassPathResource("jtrac-init.properties"); - // jtrac-init.properties assumed to exist - Properties props = loadProps(jtracInitResource.getFile()); - logger.info("found 'jtrac-init.properties' on classpath, processing..."); - jtracHome = props.getProperty("jtrac.home"); - if (jtracHome != null) { - logger.info("'jtrac.home' property initialized from 'jtrac-init.properties' as '" + jtracHome + "'"); - } - //====================================================================== - FilenameFilter ff = new FilenameFilter() { - public boolean accept(File dir, String name) { - return name.startsWith("messages_") && name.endsWith(".properties"); - } - }; - File[] messagePropsFiles = jtracInitResource.getFile().getParentFile().listFiles(ff); - String locales = "en"; - for(File f : messagePropsFiles) { - int endIndex = f.getName().indexOf('.'); - String localeCode = f.getName().substring(9, endIndex); - locales += "," + localeCode; - } - logger.info("locales available configured are '" + locales + "'"); - props.setProperty("jtrac.locales", locales); - //====================================================================== - if (jtracHome == null) { - logger.info("valid 'jtrac.home' property not available in 'jtrac-init.properties', trying system properties."); - jtracHome = System.getProperty("jtrac.home"); - if (jtracHome != null) { - logger.info("'jtrac.home' property initialized from system properties as '" + jtracHome + "'"); - } - } - if (jtracHome == null) { - logger.info("valid 'jtrac.home' property not available in system properties, trying servlet init paramters."); - jtracHome = servletContext.getInitParameter("jtrac.home"); - if (jtracHome != null) { - logger.info("Servlet init parameter 'jtrac.home' exists: '" + jtracHome + "'"); - } - } - if (jtracHome == null) { - jtracHome = System.getProperty("user.home") + "/.jtrac"; - logger.warn("Servlet init paramter 'jtrac.home' does not exist. Will use 'user.home' directory '" + jtracHome + "'"); - } - //====================================================================== - File homeFile = new File(jtracHome); - if (!homeFile.exists()) { - homeFile.mkdir(); - logger.info("directory does not exist, created '" + homeFile.getPath() + "'"); - if (!homeFile.exists()) { - String message = "invalid path '" + homeFile.getAbsolutePath() + "', try creating this directory first. Aborting."; - logger.error(message); - throw new Exception(message); - } - } else { - logger.info("directory already exists: '" + homeFile.getPath() + "'"); - } - props.setProperty("jtrac.home", homeFile.getAbsolutePath()); - //====================================================================== - File attachmentsFile = new File(jtracHome + "/attachments"); - if (!attachmentsFile.exists()) { - attachmentsFile.mkdir(); - logger.info("directory does not exist, created '" + attachmentsFile.getPath() + "'"); - } else { - logger.info("directory already exists: '" + attachmentsFile.getPath() + "'"); - } - File indexesFile = new File(jtracHome + "/indexes"); - if (!indexesFile.exists()) { - indexesFile.mkdir(); - logger.info("directory does not exist, created '" + indexesFile.getPath() + "'"); - } else { - logger.info("directory already exists: '" + indexesFile.getPath() + "'"); - } - //====================================================================== - File propsFile = new File(homeFile.getPath() + "/jtrac.properties"); - if (!propsFile.exists()) { - propsFile.createNewFile(); - logger.info("properties file does not exist, created '" + propsFile.getPath() + "'"); - OutputStream os = new FileOutputStream(propsFile); - Writer out = new PrintWriter(os); - try { - out.write("database.driver=org.hsqldb.jdbcDriver\n"); - out.write("database.url=jdbc:hsqldb:file:${jtrac.home}/db/jtrac\n"); - out.write("database.username=sa\n"); - out.write("database.password=\n"); - out.write("hibernate.dialect=org.hibernate.dialect.HSQLDialect\n"); - out.write("hibernate.show_sql=false\n"); - } finally { - out.close(); - os.close(); - } - logger.info("HSQLDB will be used. Finished creating '" + propsFile.getPath() + "'"); - } else { - logger.info("'jtrac.properties' file exists: '" + propsFile.getPath() + "'"); - } - //====================================================================== - String version = "0.0.0"; - String timestamp = "0000"; - ClassPathResource versionResource = new ClassPathResource("jtrac-version.properties"); - if(versionResource.exists()) { - logger.info("found 'jtrac-version.properties' on classpath, processing..."); - Properties versionProps = loadProps(versionResource.getFile()); - version = versionProps.getProperty("version"); - timestamp = versionProps.getProperty("timestamp"); - } else { - logger.info("did not find 'jtrac-version.properties' on classpath"); - } - logger.info("jtrac.version = '" + version + "'"); - logger.info("jtrac.timestamp = '" + timestamp + "'"); - props.setProperty("jtrac.version", version); - props.setProperty("jtrac.timestamp", timestamp); - - /* - * TODO: A better way (default value) to check the database should be used for Apache DBCP. - * The current "SELECT...FROM DUAL" only works on Oracle (and MySQL). - * Other databases also support "SELECT 1+1" as query - * (e.g. PostgreSQL, Hypersonic 2 (H2), MySQL, etc.). - */ - props.setProperty("database.validationQuery", "SELECT 1 FROM DUAL"); - props.setProperty("ldap.url", ""); - props.setProperty("ldap.activeDirectoryDomain", ""); - props.setProperty("ldap.searchBase", ""); - props.setProperty("database.datasource.jndiname", ""); - // set default properties that can be overridden by user if required - setProperties(props); - // finally set the property that spring is expecting, manually - FileSystemResource fsr = new FileSystemResource(propsFile); - setLocation(fsr); - } - - private Properties loadProps(File file) throws Exception { - InputStream is = null; - Properties props = new Properties(); - try { - is = new FileInputStream(file); - props.load(is); - } finally { - is.close(); - } - return props; - } -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac.config; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.Properties; +import javax.servlet.ServletContext; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.FileSystemResource; +import org.springframework.web.context.ServletContextAware; + +/** + * <p> + * Custom extension of the Spring PropertyPlaceholderConfigurer that + * sets up the jtrac.home System property (creates if required) and also creates + * a default jtrac.properties file for HSQLDB - useful for those who want + * to quickly evaluate JTrac. Just dropping the war into a servlet container + * would work without the need to even configure a datasource. + * </p> + * <p> + * This class would effectively do nothing if a <code>jtrac.properties</code> file exists in jtrac.home + * </p> + * <ol> + * <li>A "jtrac.home" property is looked for in <code>/WEB-INF/classes/jtrac-init.properties</code></li> + * <li>if not found, then a <code>jtrac.home</code> system property is checked for</li> + * <li>then a servlet context init-parameter called <code>jtrac.home</code> is looked for</li> + * <li>last resort, a <code>.jtrac</code> folder is created in the <code>user.home</code> and used as <code>jtrac.home</code></li> + * </ol> + * + * <p> + * Other tasks + * </p> + * <ul> + * <li>initialize the "test" query for checking idle database connections</li> + * <li>initialize list of available locales based on the properties files available</li> + * </ul> + * + * <p> + * Also playing an important role during startup are the following factory beans: + * </p> + * <ul> + * <li>DataSourceFactoryBean:</li> + * <ul> + * <li>switches between embedded HSQLDB or Apache DBCP (connection pool)</li> + * <li>performs graceful shutdown of database if embedded HSQLDB</li> + * </ul> + * <li>ProviderManagerFactoryBean</li> + * <ul> + * <li>conditionally includes LDAP authentication if requested</li> + * </ul> + * </ul> + * + * <p> + * Note that later on during startup, the HibernateJtracDao would check if + * database tables exist, and if they don't, would proceed to create them. + * </p> + */ + +public class JtracConfigurer extends PropertyPlaceholderConfigurer implements ServletContextAware { + + private ServletContext servletContext; + + public void setServletContext(ServletContext servletContext) { + this.servletContext = servletContext; + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { + // do our custom configuration before spring gets a chance to + try { + configureJtrac(); + } catch(Exception e) { + throw new BeanCreationException("JtracConfigurer failed", e); + } + super.postProcessBeanFactory(beanFactory); + } + + private void configureJtrac() throws Exception { + String jtracHome = null; + ClassPathResource jtracInitResource = new ClassPathResource("jtrac-init.properties"); + // jtrac-init.properties assumed to exist + Properties props = loadProps(jtracInitResource.getFile()); + logger.info("found 'jtrac-init.properties' on classpath, processing..."); + jtracHome = props.getProperty("jtrac.home"); + if (jtracHome != null) { + logger.info("'jtrac.home' property initialized from 'jtrac-init.properties' as '" + jtracHome + "'"); + } + //====================================================================== + FilenameFilter ff = new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.startsWith("messages_") && name.endsWith(".properties"); + } + }; + File[] messagePropsFiles = jtracInitResource.getFile().getParentFile().listFiles(ff); + String locales = "en"; + for(File f : messagePropsFiles) { + int endIndex = f.getName().indexOf('.'); + String localeCode = f.getName().substring(9, endIndex); + locales += "," + localeCode; + } + logger.info("locales available configured are '" + locales + "'"); + props.setProperty("jtrac.locales", locales); + //====================================================================== + if (jtracHome == null) { + logger.info("valid 'jtrac.home' property not available in 'jtrac-init.properties', trying system properties."); + jtracHome = System.getProperty("jtrac.home"); + if (jtracHome != null) { + logger.info("'jtrac.home' property initialized from system properties as '" + jtracHome + "'"); + } + } + if (jtracHome == null) { + logger.info("valid 'jtrac.home' property not available in system properties, trying servlet init parameters."); + jtracHome = servletContext.getInitParameter("jtrac.home"); + if (jtracHome != null) { + logger.info("Servlet init parameter 'jtrac.home' exists: '" + jtracHome + "'"); + } + } + if (jtracHome == null) { + jtracHome = System.getProperty("user.home") + "/.jtrac"; + logger.warn("Servlet init parameter 'jtrac.home' does not exist. Will use 'user.home' directory '" + jtracHome + "'"); + } + //====================================================================== + File homeFile = new File(jtracHome); + if (!homeFile.exists()) { + homeFile.mkdir(); + logger.info("directory does not exist, created '" + homeFile.getPath() + "'"); + if (!homeFile.exists()) { + String message = "invalid path '" + homeFile.getAbsolutePath() + "', try creating this directory first. Aborting."; + logger.error(message); + throw new Exception(message); + } + } else { + logger.info("directory already exists: '" + homeFile.getPath() + "'"); + } + props.setProperty("jtrac.home", homeFile.getAbsolutePath()); + //====================================================================== + File attachmentsFile = new File(jtracHome + "/attachments"); + if (!attachmentsFile.exists()) { + attachmentsFile.mkdir(); + logger.info("directory does not exist, created '" + attachmentsFile.getPath() + "'"); + } else { + logger.info("directory already exists: '" + attachmentsFile.getPath() + "'"); + } + File indexesFile = new File(jtracHome + "/indexes"); + if (!indexesFile.exists()) { + indexesFile.mkdir(); + logger.info("directory does not exist, created '" + indexesFile.getPath() + "'"); + } else { + logger.info("directory already exists: '" + indexesFile.getPath() + "'"); + } + //====================================================================== + File propsFile = new File(homeFile.getPath() + "/jtrac.properties"); + if (!propsFile.exists()) { + propsFile.createNewFile(); + logger.info("properties file does not exist, created '" + propsFile.getPath() + "'"); + OutputStream os = new FileOutputStream(propsFile); + Writer out = new PrintWriter(os); + try { + out.write("database.driver=org.hsqldb.jdbcDriver\n"); + out.write("database.url=jdbc:hsqldb:file:${jtrac.home}/db/jtrac\n"); + out.write("database.username=sa\n"); + out.write("database.password=\n"); + out.write("hibernate.dialect=org.hibernate.dialect.HSQLDialect\n"); + out.write("hibernate.show_sql=false\n"); + } finally { + out.close(); + os.close(); + } + logger.info("HSQLDB will be used. Finished creating '" + propsFile.getPath() + "'"); + } else { + logger.info("'jtrac.properties' file exists: '" + propsFile.getPath() + "'"); + } + //====================================================================== + String version = "0.0.0"; + String timestamp = "0000"; + ClassPathResource versionResource = new ClassPathResource("jtrac-version.properties"); + if(versionResource.exists()) { + logger.info("found 'jtrac-version.properties' on classpath, processing..."); + Properties versionProps = loadProps(versionResource.getFile()); + version = versionProps.getProperty("version"); + timestamp = versionProps.getProperty("timestamp"); + } else { + logger.info("did not find 'jtrac-version.properties' on classpath"); + } + logger.info("jtrac.version = '" + version + "'"); + logger.info("jtrac.timestamp = '" + timestamp + "'"); + props.setProperty("jtrac.version", version); + props.setProperty("jtrac.timestamp", timestamp); + + /* + * TODO: A better way (default value) to check the database should be used for Apache DBCP. + * The current "SELECT...FROM DUAL" only works on Oracle (and MySQL). + * Other databases also support "SELECT 1+1" as query + * (e.g. PostgreSQL, Hypersonic 2 (H2), MySQL, etc.). + */ + props.setProperty("database.validationQuery", "SELECT 1 FROM DUAL"); + props.setProperty("ldap.url", ""); + props.setProperty("ldap.activeDirectoryDomain", ""); + props.setProperty("ldap.searchBase", ""); + props.setProperty("database.datasource.jndiname", ""); + // set default properties that can be overridden by user if required + setProperties(props); + // finally set the property that spring is expecting, manually + FileSystemResource fsr = new FileSystemResource(propsFile); + setLocation(fsr); + } + + private Properties loadProps(File file) throws Exception { + InputStream is = null; + Properties props = new Properties(); + try { + is = new FileInputStream(file); + props.load(is); + } finally { + is.close(); + } + return props; + } +} Modified: trunk/jtrac/src/main/java/info/jtrac/domain/Config.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/domain/Config.java 2021-10-15 13:20:11 UTC (rev 1401) +++ trunk/jtrac/src/main/java/info/jtrac/domain/Config.java 2021-10-26 15:22:35 UTC (rev 1402) @@ -58,7 +58,7 @@ PARAMS.add("attachment.maxsize"); PARAMS.add("pwd.minLength"); PARAMS.add("time.pretty"); - PARAMS.add("urls.autolink"); + PARAMS.add("markdown.enabled"); PARAMS.add("attachments.openNewWindow"); PARAMS.add("items.search.num"); } Modified: trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java 2021-10-15 13:20:11 UTC (rev 1401) +++ trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java 2021-10-26 15:22:35 UTC (rev 1402) @@ -26,23 +26,33 @@ import info.jtrac.domain.Space; import info.jtrac.domain.User; import info.jtrac.exception.JtracSecurityException; + import java.io.BufferedReader; import java.io.StringReader; import java.io.Writer; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; +import java.util.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + import org.apache.wicket.PageParameters; + import org.dom4j.Element; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.springframework.context.MessageSource; import org.springframework.web.servlet.support.RequestContextUtils; import org.springframework.web.util.HtmlUtils; +import org.commonmark.Extension; +import org.commonmark.node.*; +import org.commonmark.parser.Parser; +import org.commonmark.renderer.html.HtmlRenderer; +import org.commonmark.ext.autolink.AutolinkExtension; +import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension; +import org.commonmark.ext.ins.InsExtension; + /** * Utilities to convert an Item into HTML etc. * The getAsHtml() routine is used to diplay an item - within a tag lib for JSP @@ -54,6 +64,19 @@ public static final String URL_PATTERN = "http(s?)://[:\\p{L}\\p{Digit}\\.\\-_\\/=#\\?%]+"; + private static Parser parser; + + private static HtmlRenderer renderer; + + static { + Set<Extension> extensions = new HashSet<>(); + extensions.add(AutolinkExtension.create()); + extensions.add(StrikethroughExtension.create()); + extensions.add(InsExtension.create()); + parser = Parser.builder().extensions(extensions).build(); + renderer = HtmlRenderer.builder().extensions(extensions).sanitizeUrls(true).build(); + } + /** * does HTML escaping, converts tabs to spaces and converts leading * spaces (for each multi-line) to as many ' ' sequences as required @@ -93,11 +116,13 @@ return sb.toString().replaceAll("\t", " "); } - public static String htmlizeUrls (String text) { + public static String renderMarkdown (String text) { if (text == null) { return null; } else { - return text.replaceAll(URL_PATTERN, "<a href='$0' target='_blank'>$0</a>"); + Node document = parser.parse(text); + return renderer.render(document); +// return text.replaceAll(URL_PATTERN, "<a href='$0' target='_blank'>$0</a>"); } } @@ -108,11 +133,11 @@ return "???item_view." + key + "???"; } } - + public static String getAsHtml(Item item, MessageSource messageSource, Locale locale) { return getAsHtml(item, null, null, messageSource, locale); } - + public static String getAsHtml(Item item, HttpServletRequest request, HttpServletResponse response) { Locale locale = RequestContextUtils.getLocale(request); MessageSource messageSource = RequestContextUtils.getWebApplicationContext(request); Modified: trunk/jtrac/src/main/java/info/jtrac/web/RestMultiActionController.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/web/RestMultiActionController.java 2021-10-15 13:20:11 UTC (rev 1401) +++ trunk/jtrac/src/main/java/info/jtrac/web/RestMultiActionController.java 2021-10-26 15:22:35 UTC (rev 1402) @@ -1,221 +1,220 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.web; - -import info.jtrac.domain.Item; -import info.jtrac.domain.ItemSearch; -import info.jtrac.domain.Space; -import info.jtrac.domain.User; -import info.jtrac.exception.InvalidRefIdException; -import info.jtrac.util.ItemUtils; -import info.jtrac.util.XmlUtils; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.util.List; -import java.util.StringTokenizer; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.apache.commons.codec.binary.Base64; -import org.apache.wicket.PageParameters; -import org.apache.wicket.protocol.http.RequestUtils; -import org.apache.wicket.util.value.ValueMap; -import org.dom4j.Document; -import org.dom4j.Element; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.mvc.multiaction.MethodNameResolver; -import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException; - -/** - * Spring MultiActionController that handles REST requests - * returns XML messages - */ -public class RestMultiActionController extends AbstractMultiActionController { - - /** - * custom MethodNameResolver is configured that checks the value of an expected - * paramter called "method" in the request and formats the value that may be - * in the form of "namespace.subnamespace.action" into "namespaceSubnamespaceAction" - * or more like a java method name - */ - public RestMultiActionController() { - setMethodNameResolver(new MethodNameResolver() { - public String getHandlerMethodName(HttpServletRequest request) throws NoSuchRequestHandlingMethodException { - String temp = request.getParameter("method"); - if (temp == null) { - return null; - } - StringBuffer sb = new StringBuffer(); - for(int i = 0; i < temp.length(); i++) { - char c = temp.charAt(i); - if (c == '.') { - i++; - c = temp.charAt(i); - sb.append(Character.toUpperCase(c)); - } else { - sb.append(c); - } - } - return sb.toString(); - } - }); - } - - /** - * override Spring template method as a crude interceptor - * here we are doing HTTP basic authentication TODO better security - */ - @Override - protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { - if(!authenticate(request)) { - String title = "Basic realm=\"JTrac Remote API\""; - response.setHeader("WWW-Authenticate", title); - response.setStatus(401); - return null; - } else { - return super.handleRequestInternal(request, response); - } - } - - private boolean authenticate(HttpServletRequest request) { - String authHeader = request.getHeader("Authorization"); - logger.debug("auth header: " + authHeader); - if (authHeader == null) { - return false; - } - StringTokenizer st = new StringTokenizer(authHeader); - if (st.hasMoreTokens()) { - String basic = st.nextToken(); - if (basic.equalsIgnoreCase("Basic")) { - String credentials = st.nextToken(); - Base64 decoder = new Base64(); - String userPass = new String(decoder.decode(credentials.getBytes())); - int p = userPass.indexOf(":"); - if (p == -1) { - return false; - } - String loginName = userPass.substring(0, p); - String password = userPass.substring(p + 1); - User user = jtrac.loadUser(loginName); - if(user == null) { - return false; - } - String encoded = jtrac.encodeClearText(password); - if(user.getPassword().equals(encoded)) { - request.setAttribute("user", user); - return true; - } - } - } - return false; - } - - private void writeXml(Document document, HttpServletResponse response) throws Exception { - writeXml(document.getRootElement(), response); - } - - private void writeXml(Element element, HttpServletResponse response) throws Exception { - initXmlResponse(response); - element.write(response.getWriter()); - } - - private void initXmlResponse(HttpServletResponse response) { - applyCacheSeconds(response, 0, true); - response.setContentType("text/xml"); - } - - private String getContent(HttpServletRequest request) throws Exception { - InputStream is = request.getInputStream(); - int ch; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - while ((ch = is.read()) != -1) { - baos.write((byte) ch); - } - return new String(baos.toByteArray()); - } - - //============================ REQUEST HANDLERS ============================ - - public void versionGet(HttpServletRequest request, HttpServletResponse response) throws Exception { - Document d = XmlUtils.getNewDocument("version"); - Element root = d.getRootElement(); - root.addAttribute("number", jtrac.getReleaseVersion()); - root.addAttribute("timestamp", jtrac.getReleaseTimestamp()); - writeXml(d, response); - } - - public void itemGet(HttpServletRequest request, HttpServletResponse response) throws Exception { - String refId = request.getParameter("refId"); - Item item = null; - try { - item = jtrac.loadItemByRefId(refId); - } catch (InvalidRefIdException e) { - // TODO - } - // TODO if item == null - if (item == null) { - return; - } - Element e = ItemUtils.getAsXml(item); - writeXml(e, response); - } - - public void itemPut(HttpServletRequest request, HttpServletResponse response) throws Exception { - logger.debug(getContent(request)); - Document d = XmlUtils.getNewDocument("success"); - Element root = d.getRootElement(); - root.addElement("refId").addText("FOOBAR-123"); - writeXml(d, response); - } - - public void spaceUsersGet(HttpServletRequest request, HttpServletResponse response) throws Exception { - String prefixCode = request.getParameter("prefixCode"); - Space space = jtrac.loadSpace(prefixCode); - Document d = XmlUtils.getNewDocument("users"); - Element root = d.getRootElement(); - root.addAttribute("prefixCode", prefixCode); - List<User> users = jtrac.findUsersForSpace(space.getId()); - for(User user : users) { - root.addElement("user").addAttribute("loginName", user.getLoginName()).addText(user.getName()); - } - writeXml(d, response); - } - - public void itemSearchGet(HttpServletRequest request, HttpServletResponse response) throws Exception { - String queryString = request.getQueryString(); - logger.debug("parsing queryString: " + queryString); - ValueMap map = new ValueMap(); - RequestUtils.decodeParameters(queryString, map); - logger.debug("decoded: " + map); - User user = (User) request.getAttribute("user"); - PageParameters params = new PageParameters(map); - ItemSearch itemSearch = ItemUtils.getItemSearch(user, params, jtrac); - initXmlResponse(response); - ItemUtils.writeAsXml(itemSearch, jtrac, response.getWriter()); - } - - public void itemAllGet(HttpServletRequest request, HttpServletResponse response) throws Exception { - // GOD mode! - User user = (User) request.getAttribute("user"); - if(!user.isSuperUser()) { - // TODO error code - return; - } - initXmlResponse(response); - ItemUtils.writeAsXml(jtrac, response.getWriter()); - } - -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac.web; + +import info.jtrac.domain.Item; +import info.jtrac.domain.ItemSearch; +import info.jtrac.domain.Space; +import info.jtrac.domain.User; +import info.jtrac.exception.InvalidRefIdException; +import info.jtrac.util.ItemUtils; +import info.jtrac.util.XmlUtils; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.util.List; +import java.util.StringTokenizer; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.codec.binary.Base64; +import org.apache.wicket.PageParameters; +import org.apache.wicket.protocol.http.RequestUtils; +import org.apache.wicket.util.value.ValueMap; +import org.dom4j.Document; +import org.dom4j.Element; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.multiaction.MethodNameResolver; +import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException; + +/** + * Spring MultiActionController that handles REST requests + * returns XML messages + */ +public class RestMultiActionController extends AbstractMultiActionController { + + /** + * custom MethodNameResolver is configured that checks the value of an expected parameter + * called "method" in the request and formats the value that may be in the form of + * "namespace.subnamespace.action" into "namespaceSubnamespaceAction" or more like a Java method name + */ + public RestMultiActionController() { + setMethodNameResolver(new MethodNameResolver() { + public String getHandlerMethodName(HttpServletRequest request) throws NoSuchRequestHandlingMethodException { + String temp = request.getParameter("method"); + if (temp == null) { + return null; + } + StringBuffer sb = new StringBuffer(); + for(int i = 0; i < temp.length(); i++) { + char c = temp.charAt(i); + if (c == '.') { + i++; + c = temp.charAt(i); + sb.append(Character.toUpperCase(c)); + } else { + sb.append(c); + } + } + return sb.toString(); + } + }); + } + + /** + * override Spring template method as a crude interceptor + * here we are doing HTTP basic authentication TODO better security + */ + @Override + protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { + if(!authenticate(request)) { + String title = "Basic realm=\"JTrac Remote API\""; + response.setHeader("WWW-Authenticate", title); + response.setStatus(401); + return null; + } else { + return super.handleRequestInternal(request, response); + } + } + + private boolean authenticate(HttpServletRequest request) { + String authHeader = request.getHeader("Authorization"); + logger.debug("auth header: " + authHeader); + if (authHeader == null) { + return false; + } + StringTokenizer st = new StringTokenizer(authHeader); + if (st.hasMoreTokens()) { + String basic = st.nextToken(); + if (basic.equalsIgnoreCase("Basic")) { + String credentials = st.nextToken(); + Base64 decoder = new Base64(); + String userPass = new String(decoder.decode(credentials.getBytes())); + int p = userPass.indexOf(":"); + if (p == -1) { + return false; + } + String loginName = userPass.substring(0, p); + String password = userPass.substring(p + 1); + User user = jtrac.loadUser(loginName); + if(user == null) { + return false; + } + String encoded = jtrac.encodeClearText(password); + if(user.getPassword().equals(encoded)) { + request.setAttribute("user", user); + return true; + } + } + } + return false; + } + + private void writeXml(Document document, HttpServletResponse response) throws Exception { + writeXml(document.getRootElement(), response); + } + + private void writeXml(Element element, HttpServletResponse response) throws Exception { + initXmlResponse(response); + element.write(response.getWriter()); + } + + private void initXmlResponse(HttpServletResponse response) { + applyCacheSeconds(response, 0, true); + response.setContentType("text/xml"); + } + + private String getContent(HttpServletRequest request) throws Exception { + InputStream is = request.getInputStream(); + int ch; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + while ((ch = is.read()) != -1) { + baos.write((byte) ch); + } + return new String(baos.toByteArray()); + } + + //============================ REQUEST HANDLERS ============================ + + public void versionGet(HttpServletRequest request, HttpServletResponse response) throws Exception { + Document d = XmlUtils.getNewDocument("version"); + Element root = d.getRootElement(); + root.addAttribute("number", jtrac.getReleaseVersion()); + root.addAttribute("timestamp", jtrac.getReleaseTimestamp()); + writeXml(d, response); + } + + public void itemGet(HttpServletRequest request, HttpServletResponse response) throws Exception { + String refId = request.getParameter("refId"); + Item item = null; + try { + item = jtrac.loadItemByRefId(refId); + } catch (InvalidRefIdException e) { + // TODO + } + // TODO if item == null + if (item == null) { + return; + } + Element e = ItemUtils.getAsXml(item); + writeXml(e, response); + } + + public void itemPut(HttpServletRequest request, HttpServletResponse response) throws Exception { + logger.debug(getContent(request)); + Document d = XmlUtils.getNewDocument("success"); + Element root = d.getRootElement(); + root.addElement("refId").addText("FOOBAR-123"); + writeXml(d, response); + } + + public void spaceUsersGet(HttpServletRequest request, HttpServletResponse response) throws Exception { + String prefixCode = request.getParameter("prefixCode"); + Space space = jtrac.loadSpace(prefixCode); + Document d = XmlUtils.getNewDocument("users"); + Element root = d.getRootElement(); + root.addAttribute("prefixCode", prefixCode); + List<User> users = jtrac.findUsersForSpace(space.getId()); + for(User user : users) { + root.addElement("user").addAttribute("loginName", user.getLoginName()).addText(user.getName()); + } + writeXml(d, response); + } + + public void itemSearchGet(HttpServletRequest request, HttpServletResponse response) throws Exception { + String queryString = request.getQueryString(); + logger.debug("parsing queryString: " + queryString); + ValueMap map = new ValueMap(); + RequestUtils.decodeParameters(queryString, map); + logger.debug("decoded: " + map); + User user = (User) request.getAttribute("user"); + PageParameters params = new PageParameters(map); + ItemSearch itemSearch = ItemUtils.getItemSearch(user, params, jtrac); + initXmlResponse(response); + ItemUtils.writeAsXml(itemSearch, jtrac, response.getWriter()); + } + + public void itemAllGet(HttpServletRequest request, HttpServletResponse response) throws Exception { + // GOD mode! + User user = (User) request.getAttribute("user"); + if(!user.isSuperUser()) { + // TODO error code + return; + } + initXmlResponse(response); + ItemUtils.writeAsXml(jtrac, response.getWriter()); + } + +} Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/BasePanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/BasePanel.java 2021-10-15 13:20:11 UTC (rev 1401) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/BasePanel.java 2021-10-26 15:22:35 UTC (rev 1402) @@ -66,10 +66,10 @@ return m.getString(); } - protected boolean autoLinkUrls() { - String autoLink = getJtrac().loadConfig("urls.autolink"); - if (StringUtils.hasText(autoLink)) { - return autoLink.trim().equalsIgnoreCase("true"); + protected boolean markdown() { + String markdown = getJtrac().loadConfig("markdown.enabled"); + if (StringUtils.hasText(markdown)) { + return markdown.trim().equalsIgnoreCase("true"); } else { return false; } Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigListPage.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigListPage.java 2021-10-15 13:20:11 UTC (rev 1401) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ConfigListPage.java 2021-10-26 15:22:35 UTC (rev 1402) @@ -26,6 +26,8 @@ import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; +import org.springframework.util.StringUtils; + /** * This class is responsible to list all configuration parameters. * It also declares which edit forms will be called to modify the configuration parameters. @@ -59,7 +61,10 @@ // Password value to be edited // obscure the password, but give an indication of its length - listItem.add(new Label("value", new String(new char[value.length()]).replaceAll("\0", "*"))); + if (StringUtils.hasText(value)) + listItem.add(new Label("value", new String(new char[value.length()]).replaceAll("\0", "*"))); + else + listItem.add(new Label("value", "")); listItem.add(new Link("link") { public void onClick() { Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/IndividualHeadPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/IndividualHeadPanel.java 2021-10-15 13:20:11 UTC (rev 1401) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/IndividualHeadPanel.java 2021-10-26 15:22:35 UTC (rev 1402) @@ -1,66 +1,75 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.wicket; - -import java.util.Map; - -import org.apache.wicket.AttributeModifier; -import org.apache.wicket.markup.html.basic.Label; -import org.apache.wicket.markup.html.image.Image; -import org.apache.wicket.model.AbstractReadOnlyModel; - - -/** - * header navigation - */ -public class IndividualHeadPanel extends BasePanel { - - /** - * Default serialVersionID. - */ - private static final long serialVersionUID = 1L; - - /** - * Constructor. - */ - public IndividualHeadPanel() { - super("individuel"); - - final Map<String, String> configMap = getJtrac().loadAllConfig(); - Image img= new Image( "icon"); - img.add(new AttributeModifier("src", true, new AbstractReadOnlyModel() { - private static final long serialVersionUID = 1L; - public final Object getObject() { - // based on some condition return the image source - String urlbase = configMap.get("jtrac.url.base"); - String url = configMap.get("jtrac.header.picture"); - if ((url == null) ||("".equals(url))) - return urlbase + "/resources/jtrac-logo.gif"; - else - return url; - } - })); - add(img); - String message = configMap.get("jtrac.header.text"); - if ((message == null) ||("".equals(message))) - add(new Label("message", "JTrac - Open Source Issue Tracking System")); - else if ((message != null) && ("no".equals(message))) - add(new Label("message", "")); - else - add(new Label("message", message)); - } -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac.wicket; + +import java.util.Map; + +import org.apache.wicket.AttributeModifier; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.image.Image; +import org.apache.wicket.model.AbstractReadOnlyModel; + +import org.springframework.util.StringUtils; + +/** + * header navigation + */ + +public class IndividualHeadPanel extends BasePanel { + + /** + * Default serialVersionID. + */ + private static final long serialVersionUID = 1L; + + /** + * Constructor. + */ + public IndividualHeadPanel() { + super("individuel"); + + final Map<String, String> configMap = getJtrac().loadAllConfig(); + + Image img = new Image("icon"); + img.add(new AttributeModifier("src", true, new AbstractReadOnlyModel() { + @Override + public final Object getObject() { + // based on some condition return the image source + String url = configMap.get("jtrac.header.picture"); + if (StringUtils.hasText(url)) { + return url; + } else { + String urlbase = configMap.get("jtrac.url.base"); + if (! urlbase.endsWith("/")) { + // some servers, especially Jetty, can't handle multiple slashes + urlbase = urlbase + "/"; + } + return urlbase + "resources/jtrac-logo.gif"; + } + } + })); + add(img); + + String message = configMap.get("jtrac.header.text"); + if (! StringUtils.hasText(message)) + add(new Label("message", "JTrac - Open Source Issue Tracking System")); + else if ((message != null) && ("no".equals(message))) + add(new Label("message", "")); + else + add(new Label("message", message)); + } +} Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewPanel.java 2021-10-15 13:20:11 UTC (rev 1401) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewPanel.java 2021-10-26 15:22:35 UTC (rev 1402) @@ -25,9 +25,11 @@ import info.jtrac.domain.ItemSearch; import info.jtrac.util.DateUtils; import info.jtrac.util.ItemUtils; + import java.util.ArrayList; import java.util.List; import java.util.Map; + import org.apache.wicket.PageParameters; import org.apache.wicket.behavior.SimpleAttributeModifier; import org.apache.wicket.markup.html.WebMarkupContainer; @@ -188,8 +190,8 @@ add(new Label("assignedTo", new PropertyModel(item, "assignedTo.name"))); add(new Label("summary", new PropertyModel(item, "summary"))); String text = ItemUtils.fixWhiteSpace(item.getDetail()); - if (autoLinkUrls()) { - text = ItemUtils.htmlizeUrls(text); + if (markdown()) { + text = ItemUtils.renderMarkdown(text); } add(new Label("detail", text).setEscapeModelStrings(false)); @@ -242,8 +244,8 @@ WebMarkupContainer comment = new WebMarkupContainer("comment"); comment.add(new AttachmentLinkPanel("attachment", h.getAttachment())); String text = ItemUtils.fixWhiteSpace(h.getComment()); - if (autoLinkUrls()) { - text = ItemUtils.htmlizeUrls(text); + if (markdown()) { + text = ItemUtils.renderMarkdown(text); } comment.add(new Label("comment", text).setEscapeModelStrings(false)); listItem.add(comment); Modified: trunk/jtrac/src/main/resources/messages.properties =================================================================== --- trunk/jtrac/src/main/resources/messages.properties 2021-10-15 13:20:11 UTC (rev 1401) +++ trunk/jtrac/src/main/resources/messages.properties 2021-10-26 15:22:35 UTC (rev 1402) @@ -204,8 +204,9 @@ config.attachment.maxsize = Maximum size in MB of file-attachments. (default 5 MB) Use -1 for no-limit config.pwd.minLength = Minimal length of passwords (default is 8) config.time.pretty = Show relative timestamps as on Github, Twitter and Facebook (set to true, default is false) -config.urls.autolink = Auto-link URLs in tickets and comments (set to true, default is false) +config.markdown.enabled = Support Markdown Syntax for text display (set to true, default is false) config.attachments.openNewWindow = Open text and image attachments in a new tab/window, rather than downloading them +config.items.search.num = Number of entries per page (default is 25) # config_list (config_form does not have any extra messages) config_list.configurationSettings = Configuration Settings Modified: trunk/jtrac/src/main/resources/messages_de.properties =================================================================== --- trunk/jtrac/src/main/resources/messages_de.properties 2021-10-15 13:20:11 UTC (rev 1401) +++ trunk/jtrac/src/main/resources/messages_de.properties 2021-10-26 15:22:35 UTC (rev 1402) @@ -205,7 +205,7 @@ config.attachment.maxsize = Maximalgr\u00F6\u00DFe f\u00FCr Anlagen in MB, "-1" f\u00FCr Kein-Limit (Default 5) config.pwd.minLength = Minimale L\u00e4nge von Passw\u00f6rtern (Default 8) config.time.pretty = Relative Zeiten anzeigen wie auf Github, Twitter und Facebook (auf true setzen, Default ist false) -config.urls.autolink = URLs in Tickets und Kommentaren automatisch verlinken (auf true setzen, Default ist false) +config.markdown.enabled = Fliesstext als Markdown anzeigen (auf true setzen, Default ist false) config.attachments.openNewWindow = Text- und Bild-Anh\u00e4nge in neuem Fenster \u00f6ffnen statt sie herunterzuladen config.items.search.num = Anzahl der Eintr\u00f6ge pro Seite (Default 25) Modified: trunk/jtrac/src/main/resources/messages_en.properties =================================================================== --- trunk/jtrac/src/main/resources/messages_en.properties 2021-10-15 13:20:11 UTC (rev 1401) +++ trunk/jtrac/src/main/resources/messages_en.properties 2021-10-26 15:22:35 UTC (rev 1402) @@ -205,7 +205,7 @@ config.attachment.maxsize = Maximum size in MB of file-attachments. (default 5 MB) Use -1 for no-limit config.pwd.minLength = Minimal length of passwords (default is 8) config.time.pretty = Show relative timestamps as on Github, Twitter and Facebook (set to true, default is false) -config.urls.autolink = Auto-link URLs in tickets and comments (set to true, default is false) +config.markdown.enabled = Support Markdown Syntax for text display (set to true, default is false) config.attachments.openNewWindow = Open text and image attachments in a new tab/window, rather than downloading them config.items.search.num = Number of entries per page (default is 25) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-10-15 13:20:13
|
Revision: 1401 http://sourceforge.net/p/j-trac/code/1401 Author: udittmer Date: 2021-10-15 13:20:11 +0000 (Fri, 15 Oct 2021) Log Message: ----------- version 2.2.0-b2 Modified Paths: -------------- trunk/jtrac/pom.xml Modified: trunk/jtrac/pom.xml =================================================================== --- trunk/jtrac/pom.xml 2021-10-07 15:01:29 UTC (rev 1400) +++ trunk/jtrac/pom.xml 2021-10-15 13:20:11 UTC (rev 1401) @@ -4,7 +4,7 @@ <groupId>info.jtrac</groupId> <artifactId>jtrac</artifactId> <packaging>war</packaging> - <version>2.2.0-b1</version> + <version>2.2.0-b2</version> <name>JTrac</name> <description> JTrac is a generic issue-tracking web-application that can be easily customized by adding custom fields This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-10-07 15:01:32
|
Revision: 1400 http://sourceforge.net/p/j-trac/code/1400 Author: udittmer Date: 2021-10-07 15:01:29 +0000 (Thu, 07 Oct 2021) Log Message: ----------- fix issue with YUI calendar, fix issue with searching by timestamp when using pretty dates Modified Paths: -------------- trunk/jtrac/pom.xml trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java trunk/jtrac/src/main/java/info/jtrac/domain/ItemSearch.java trunk/jtrac/src/main/java/info/jtrac/util/DateUtils.java trunk/jtrac/src/main/java/info/jtrac/wicket/ItemSearchFormPanel.java Modified: trunk/jtrac/pom.xml =================================================================== --- trunk/jtrac/pom.xml 2021-10-06 12:47:21 UTC (rev 1399) +++ trunk/jtrac/pom.xml 2021-10-07 15:01:29 UTC (rev 1400) @@ -347,8 +347,9 @@ <dependency> <groupId>org.apache.wicket</groupId> <artifactId>wicket-extensions</artifactId> - <version>1.3.7</version> + <version>1.3.4</version> <!-- + 1.3.7 has bug with the YUI Calendar <version>1.4.23</version> <version>1.5.16</version> --> Modified: trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java 2021-10-06 12:47:21 UTC (rev 1399) +++ trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java 2021-10-07 15:01:29 UTC (rev 1400) @@ -144,7 +144,7 @@ || name == STATUS; } } - + public static List<ColumnHeading> getColumnHeadings(Space s) { List<ColumnHeading> list = new ArrayList<ColumnHeading>(); list.add(new ColumnHeading(ID)); @@ -158,9 +158,9 @@ } list.add(new ColumnHeading(TIME_STAMP)); list.add(new ColumnHeading(LAST_CHANGED)); - return list; + return list; } - + public static List<ColumnHeading> getColumnHeadings() { List<ColumnHeading> list = new ArrayList<ColumnHeading>(); list.add(new ColumnHeading(ID)); @@ -171,8 +171,8 @@ list.add(new ColumnHeading(ASSIGNED_TO)); list.add(new ColumnHeading(TIME_STAMP)); list.add(new ColumnHeading(LAST_CHANGED)); - return list; - } + return list; + } public List<Expression> getValidFilterExpressions() { return processor.getValidFilterExpressions(); Modified: trunk/jtrac/src/main/java/info/jtrac/domain/ItemSearch.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/domain/ItemSearch.java 2021-10-06 12:47:21 UTC (rev 1399) +++ trunk/jtrac/src/main/java/info/jtrac/domain/ItemSearch.java 2021-10-07 15:01:29 UTC (rev 1400) @@ -321,7 +321,7 @@ ch.getFilterCriteria().setExpression(FilterCriteria.Expression.IN); ch.getFilterCriteria().setValues(getSingletonList(i)); } - + public List<ColumnHeading> getColumnHeadingsToRender() { List<ColumnHeading> list = new ArrayList<ColumnHeading>(columnHeadings.size()); for(ColumnHeading ch : columnHeadings) { @@ -331,7 +331,17 @@ } return list; } - + + public List<ColumnHeading> getColumnHeadingsToSearch() { + List<ColumnHeading> list = new ArrayList<ColumnHeading>(columnHeadings.size()); + for (ColumnHeading ch : columnHeadings) { + if (! ch.getNameText().equals("lastChanged")) { + list.add(ch); + } + } + return list; + } + //========================================================================== public boolean isBatchMode() { Modified: trunk/jtrac/src/main/java/info/jtrac/util/DateUtils.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/util/DateUtils.java 2021-10-06 12:47:21 UTC (rev 1399) +++ trunk/jtrac/src/main/java/info/jtrac/util/DateUtils.java 2021-10-07 15:01:29 UTC (rev 1400) @@ -59,9 +59,10 @@ public static String format (Date date) { return date == null ? "" - : (showPretty() - ? getPrettyTime().format(date) - : dateFormat.format(date)); + : dateFormat.format(date); +// : (showPretty() +// ? getPrettyTime().format(date) +// : dateFormat.format(date)); } public static String formatTimeStamp (Date date) { Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ItemSearchFormPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ItemSearchFormPanel.java 2021-10-06 12:47:21 UTC (rev 1399) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ItemSearchFormPanel.java 2021-10-07 15:01:29 UTC (rev 1400) @@ -1,200 +1,200 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.wicket; - -import info.jtrac.Jtrac; -import info.jtrac.domain.ColumnHeading; -import info.jtrac.domain.FilterCriteria.Expression; -import info.jtrac.domain.Item; -import info.jtrac.domain.ItemRefId; -import info.jtrac.domain.ItemSearch; -import info.jtrac.domain.Space; -import info.jtrac.domain.User; -import info.jtrac.exception.InvalidRefIdException; -import java.util.Arrays; -import java.util.List; -import org.apache.wicket.Component; -import org.apache.wicket.PageParameters; -import org.apache.wicket.ajax.AjaxRequestTarget; -import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; -import org.apache.wicket.markup.html.WebMarkupContainer; -import org.apache.wicket.markup.html.basic.Label; -import org.apache.wicket.markup.html.form.Button; -import org.apache.wicket.markup.html.form.CheckBox; -import org.apache.wicket.markup.html.form.DropDownChoice; -import org.apache.wicket.markup.html.form.Form; -import org.apache.wicket.markup.html.form.IChoiceRenderer; -import org.apache.wicket.markup.html.link.Link; -import org.apache.wicket.markup.html.list.ListItem; -import org.apache.wicket.markup.html.list.ListView; -import org.apache.wicket.markup.html.panel.FeedbackPanel; -import org.apache.wicket.model.CompoundPropertyModel; -import org.apache.wicket.model.PropertyModel; - -/** - * item search form panel - */ -public class ItemSearchFormPanel extends BasePanel { - - private ItemSearch itemSearch; - private boolean expandAll; - - public ItemSearchFormPanel(String id, User user) { - super(id); - this.itemSearch = new ItemSearch(user); - addComponents(); - } - - public ItemSearchFormPanel(String id) { - super(id); - Space s = getCurrentSpace(); - if(s != null) { - this.itemSearch = new ItemSearch(s); - } else { - this.itemSearch = new ItemSearch(getPrincipal()); - } - addComponents(); - } - - public ItemSearchFormPanel(String id, ItemSearch itemSearch) { - super(id); - this.itemSearch = itemSearch; - addComponents(); - } - - private void addComponents() { - final Form form = new Form("form"); - add(form); - form.add(new FeedbackPanel("feedback")); - form.setModel(new CompoundPropertyModel(itemSearch)); - List<Integer> sizes = Arrays.asList(new Integer[] {5, 10, 15, 25, 50, 100, -1}); - DropDownChoice pageSizeChoice = new DropDownChoice("pageSize", sizes, new IChoiceRenderer() { - public Object getDisplayValue(Object o) { - return ((Integer) o) == -1 ? localize("item_search_form.noLimit") : o.toString(); - } - public String getIdValue(Object o, int i) { - return o.toString(); - } - }); - form.add(pageSizeChoice); - form.add(new CheckBox("showHistory")); - form.add(new Button("search") { - @Override - public void onSubmit() { - String refId = itemSearch.getRefId(); - if(refId != null) { - if(getCurrentSpace() != null) { - // user can save typing by entering the refId number without the space prefixCode - try { - long id = Long.parseLong(refId); - refId = getCurrentSpace().getPrefixCode() + "-" + id; - } catch(Exception e) { - // oops that didn't work, continue - } - } - try { - new ItemRefId(refId); - } catch(InvalidRefIdException e) { - form.error(localize("item_search_form.error.refId.invalid")); - return; - } - Item item = getJtrac().loadItemByRefId(refId); - if(item == null) { - form.error(localize("item_search_form.error.refId.notFound")); - return; - } - JtracSession.get().setItemSearch(itemSearch); - setResponsePage(ItemViewPage.class, new PageParameters("0=" + item.getRefId())); - return; - } - String searchText = itemSearch.getSearchText(); - if(searchText != null) { - if(!getJtrac().validateTextSearchQuery(searchText)) { - form.error(localize("item_search_form.error.summary.invalid")); - return; - } - } - setResponsePage(ItemListPage.class, itemSearch.getAsQueryString()); - } - }); - form.add(new Link("expandAll") { - public void onClick() { - expandAll = true; - } - @Override - public boolean isVisible() { - return !expandAll; - } - }); - form.add(new ListView("columns", itemSearch.getColumnHeadings()) { - protected void populateItem(final ListItem listItem) { - final ColumnHeading ch = (ColumnHeading) listItem.getModelObject(); - String label = ch.isField() ? ch.getLabel() : localize("item_list." + ch.getName()); - listItem.add(new Label("columnName", label)); - listItem.add(new CheckBox("visible", new PropertyModel(ch, "visible"))); - List<Expression> validExpressions = ch.getValidFilterExpressions(); - DropDownChoice expressionChoice = new IndicatingDropDownChoice("expression", validExpressions, new IChoiceRenderer() { - public Object getDisplayValue(Object o) { - String key = ((Expression) o).getKey(); - return localize("item_filter." + key); - } - public String getIdValue(Object o, int i) { - return ((Expression) o).getKey(); - } - }); - // always pre-select "equal to" for filter criteria on ID - if(ch.getName() == ColumnHeading.Name.ID) { - ch.getFilterCriteria().setExpression(Expression.EQ); - } - if(expandAll && ch.getFilterCriteria().getExpression() == null) { - ch.getFilterCriteria().setExpression(validExpressions.get(0)); - } - expressionChoice.setModel(new PropertyModel(ch.getFilterCriteria(), "expression")); - expressionChoice.setNullValid(true); - listItem.add(expressionChoice); - Component fragParent = null; - fragParent = getFilterUiFragment(ch); - fragParent.setOutputMarkupId(true); - listItem.add(fragParent); - expressionChoice.add(new AjaxFormComponentUpdatingBehavior("onChange") { - protected void onUpdate(AjaxRequestTarget target) { - if(!ch.getFilterCriteria().requiresUiFragmentUpdate()) { - return; - } - Component fragment = getFilterUiFragment(ch); - fragment.setOutputMarkupId(true); - listItem.replace(fragment); - target.addComponent(fragment); - target.appendJavascript("document.getElementById('" + fragment.getMarkupId() + "').focus()"); - } - }); - } - }); - } - - private Component getFilterUiFragment(ColumnHeading ch) { - if(ch.getFilterCriteria().getExpression() == null) { - return new WebMarkupContainer("fragParent"); - } - User user = JtracSession.get().getUser(); - // the space could be null also - Space space = JtracSession.get().getCurrentSpace(); - Jtrac jtrac = JtracApplication.get().getJtrac(); - return ch.getFilterUiFragment(this, user, space, jtrac); - } - -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac.wicket; + +import info.jtrac.Jtrac; +import info.jtrac.domain.ColumnHeading; +import info.jtrac.domain.FilterCriteria.Expression; +import info.jtrac.domain.Item; +import info.jtrac.domain.ItemRefId; +import info.jtrac.domain.ItemSearch; +import info.jtrac.domain.Space; +import info.jtrac.domain.User; +import info.jtrac.exception.InvalidRefIdException; +import java.util.Arrays; +import java.util.List; +import org.apache.wicket.Component; +import org.apache.wicket.PageParameters; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.form.Button; +import org.apache.wicket.markup.html.form.CheckBox; +import org.apache.wicket.markup.html.form.DropDownChoice; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.form.IChoiceRenderer; +import org.apache.wicket.markup.html.link.Link; +import org.apache.wicket.markup.html.list.ListItem; +import org.apache.wicket.markup.html.list.ListView; +import org.apache.wicket.markup.html.panel.FeedbackPanel; +import org.apache.wicket.model.CompoundPropertyModel; +import org.apache.wicket.model.PropertyModel; + +/** + * item search form panel + */ +public class ItemSearchFormPanel extends BasePanel { + + private ItemSearch itemSearch; + private boolean expandAll; + + public ItemSearchFormPanel(String id, User user) { + super(id); + this.itemSearch = new ItemSearch(user); + addComponents(); + } + + public ItemSearchFormPanel(String id) { + super(id); + Space s = getCurrentSpace(); + if(s != null) { + this.itemSearch = new ItemSearch(s); + } else { + this.itemSearch = new ItemSearch(getPrincipal()); + } + addComponents(); + } + + public ItemSearchFormPanel(String id, ItemSearch itemSearch) { + super(id); + this.itemSearch = itemSearch; + addComponents(); + } + + private void addComponents() { + final Form form = new Form("form"); + add(form); + form.add(new FeedbackPanel("feedback")); + form.setModel(new CompoundPropertyModel(itemSearch)); + List<Integer> sizes = Arrays.asList(new Integer[] {5, 10, 15, 25, 50, 100, -1}); + DropDownChoice pageSizeChoice = new DropDownChoice("pageSize", sizes, new IChoiceRenderer() { + public Object getDisplayValue(Object o) { + return ((Integer) o) == -1 ? localize("item_search_form.noLimit") : o.toString(); + } + public String getIdValue(Object o, int i) { + return o.toString(); + } + }); + form.add(pageSizeChoice); + form.add(new CheckBox("showHistory")); + form.add(new Button("search") { + @Override + public void onSubmit() { + String refId = itemSearch.getRefId(); + if(refId != null) { + if(getCurrentSpace() != null) { + // user can save typing by entering the refId number without the space prefixCode + try { + long id = Long.parseLong(refId); + refId = getCurrentSpace().getPrefixCode() + "-" + id; + } catch(Exception e) { + // oops that didn't work, continue + } + } + try { + new ItemRefId(refId); + } catch(InvalidRefIdException e) { + form.error(localize("item_search_form.error.refId.invalid")); + return; + } + Item item = getJtrac().loadItemByRefId(refId); + if(item == null) { + form.error(localize("item_search_form.error.refId.notFound")); + return; + } + JtracSession.get().setItemSearch(itemSearch); + setResponsePage(ItemViewPage.class, new PageParameters("0=" + item.getRefId())); + return; + } + String searchText = itemSearch.getSearchText(); + if(searchText != null) { + if(!getJtrac().validateTextSearchQuery(searchText)) { + form.error(localize("item_search_form.error.summary.invalid")); + return; + } + } + setResponsePage(ItemListPage.class, itemSearch.getAsQueryString()); + } + }); + form.add(new Link("expandAll") { + public void onClick() { + expandAll = true; + } + @Override + public boolean isVisible() { + return !expandAll; + } + }); + form.add(new ListView("columns", itemSearch.getColumnHeadingsToSearch()) { + protected void populateItem(final ListItem listItem) { + final ColumnHeading ch = (ColumnHeading) listItem.getModelObject(); + String label = ch.isField() ? ch.getLabel() : localize("item_list." + ch.getName()); + listItem.add(new Label("columnName", label)); + listItem.add(new CheckBox("visible", new PropertyModel(ch, "visible"))); + List<Expression> validExpressions = ch.getValidFilterExpressions(); + DropDownChoice expressionChoice = new IndicatingDropDownChoice("expression", validExpressions, new IChoiceRenderer() { + public Object getDisplayValue(Object o) { + String key = ((Expression) o).getKey(); + return localize("item_filter." + key); + } + public String getIdValue(Object o, int i) { + return ((Expression) o).getKey(); + } + }); + // always pre-select "equal to" for filter criteria on ID + if(ch.getName() == ColumnHeading.Name.ID) { + ch.getFilterCriteria().setExpression(Expression.EQ); + } + if(expandAll && ch.getFilterCriteria().getExpression() == null) { + ch.getFilterCriteria().setExpression(validExpressions.get(0)); + } + expressionChoice.setModel(new PropertyModel(ch.getFilterCriteria(), "expression")); + expressionChoice.setNullValid(true); + listItem.add(expressionChoice); + Component fragParent = null; + fragParent = getFilterUiFragment(ch); + fragParent.setOutputMarkupId(true); + listItem.add(fragParent); + expressionChoice.add(new AjaxFormComponentUpdatingBehavior("onChange") { + protected void onUpdate(AjaxRequestTarget target) { + if(!ch.getFilterCriteria().requiresUiFragmentUpdate()) { + return; + } + Component fragment = getFilterUiFragment(ch); + fragment.setOutputMarkupId(true); + listItem.replace(fragment); + target.addComponent(fragment); + target.appendJavascript("document.getElementById('" + fragment.getMarkupId() + "').focus()"); + } + }); + } + }); + } + + private Component getFilterUiFragment(ColumnHeading ch) { + if(ch.getFilterCriteria().getExpression() == null) { + return new WebMarkupContainer("fragParent"); + } + User user = JtracSession.get().getUser(); + // the space could be null also + Space space = JtracSession.get().getCurrentSpace(); + Jtrac jtrac = JtracApplication.get().getJtrac(); + return ch.getFilterUiFragment(this, user, space, jtrac); + } + +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-10-06 12:47:25
|
Revision: 1399 http://sourceforge.net/p/j-trac/code/1399 Author: udittmer Date: 2021-10-06 12:47:21 +0000 (Wed, 06 Oct 2021) Log Message: ----------- roll back Wicket 1.4 changes; needs more work Modified Paths: -------------- trunk/jtrac/pom.xml trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java trunk/jtrac/src/main/java/info/jtrac/wicket/HeaderPanel.java trunk/jtrac/src/main/java/info/jtrac/wicket/IndexRebuildPage.java trunk/jtrac/src/main/java/info/jtrac/wicket/ItemFormPage.java trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewFormPanel.java trunk/jtrac/src/main/java/info/jtrac/wicket/JtracCheckBoxMultipleChoice.java trunk/jtrac/src/main/java/info/jtrac/wicket/SpaceFieldFormPage.java trunk/jtrac/src/main/java/info/jtrac/wicket/SpaceFormPage.java trunk/jtrac/src/main/java/info/jtrac/wicket/UserFormPage.java Modified: trunk/jtrac/pom.xml =================================================================== --- trunk/jtrac/pom.xml 2021-10-06 08:41:39 UTC (rev 1398) +++ trunk/jtrac/pom.xml 2021-10-06 12:47:21 UTC (rev 1399) @@ -4,7 +4,7 @@ <groupId>info.jtrac</groupId> <artifactId>jtrac</artifactId> <packaging>war</packaging> - <version>2.2.0-b2</version> + <version>2.2.0-b1</version> <name>JTrac</name> <description> JTrac is a generic issue-tracking web-application that can be easily customized by adding custom fields @@ -337,9 +337,9 @@ <dependency> <groupId>org.apache.wicket</groupId> <artifactId>wicket</artifactId> + <version>1.3.7</version> + <!-- <version>1.4.23</version> - <!-- - <version>1.3.7</version> <version>1.5.16</version> --> <type>pom</type> @@ -347,9 +347,9 @@ <dependency> <groupId>org.apache.wicket</groupId> <artifactId>wicket-extensions</artifactId> + <version>1.3.7</version> + <!-- <version>1.4.23</version> - <!-- - <version>1.3.7</version> <version>1.5.16</version> --> </dependency> Modified: trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java 2021-10-06 08:41:39 UTC (rev 1398) +++ trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java 2021-10-06 12:47:21 UTC (rev 1399) @@ -89,7 +89,8 @@ STATUS("status"), ASSIGNED_TO("assignedTo"), TIME_STAMP("timeStamp"), - SPACE("space"); + SPACE("space"), + LAST_CHANGED("lastChanged"); private String text; @@ -156,6 +157,7 @@ list.add(new ColumnHeading(f)); } list.add(new ColumnHeading(TIME_STAMP)); + list.add(new ColumnHeading(LAST_CHANGED)); return list; } @@ -168,6 +170,7 @@ list.add(new ColumnHeading(LOGGED_BY)); list.add(new ColumnHeading(ASSIGNED_TO)); list.add(new ColumnHeading(TIME_STAMP)); + list.add(new ColumnHeading(LAST_CHANGED)); return list; } @@ -516,6 +519,7 @@ }; //============================================================== case TIME_STAMP: + case LAST_CHANGED: return new Processor() { List<Expression> getValidFilterExpressions() { return getAsList(BETWEEN, GT, LT); Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/HeaderPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/HeaderPanel.java 2021-10-06 08:41:39 UTC (rev 1398) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/HeaderPanel.java 2021-10-06 12:47:21 UTC (rev 1399) @@ -1,135 +1,133 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.wicket; - -import info.jtrac.domain.Space; -import info.jtrac.domain.State; -import info.jtrac.domain.User; - -import java.util.ArrayList; -import java.util.List; - -import javax.servlet.http.Cookie; - -import org.acegisecurity.context.SecurityContextHolder; -import org.apache.wicket.PageParameters; -import org.apache.wicket.markup.html.WebMarkupContainer; -import org.apache.wicket.markup.html.basic.Label; -import org.apache.wicket.markup.html.link.Link; -import org.apache.wicket.protocol.http.WebRequest; -import org.apache.wicket.protocol.http.WebResponse; - -/** - * header navigation - */ -public class HeaderPanel extends BasePanel { - - public HeaderPanel() { - super("header"); - - final User user = getPrincipal(); - final Space space = getCurrentSpace(); - final List<Space> spaces = new ArrayList(user.getSpaces()); - - add(new Link("dashboard") { - public void onClick() { - setCurrentSpace(null); - setResponsePage(DashboardPage.class); - } - }); - - if (space == null) { - add(new WebMarkupContainer("spaceName").setVisible(false)); - add(new Label("prefixCode", "")); - add(new WebMarkupContainer("new").setVisible(false)); - add(new Link("search") { - public void onClick() { - // if only one space don't use generic search screen - if(spaces.size() == 1) { - Space current = spaces.get(0); - setCurrentSpace(current); - } else { - setCurrentSpace(null); // may have come here with back button! - } - setResponsePage(ItemSearchFormPage.class); - } - @Override - public boolean isVisible() { - return spaces.size() > 0; - } - }); - } else { - add(new Label("spaceName", space.getName())); - add(new Label("prefixCode", space.getPrefixCode())); - if (user.getPermittedTransitions(space, State.NEW).size() > 0) { - add(new Link("new") { - public void onClick() { - setResponsePage(ItemFormPage.class); - } - }); - } else { - add(new WebMarkupContainer("new").setVisible(false)); - } - - add(new Link("search") { - public void onClick() { - setResponsePage(ItemSearchFormPage.class); - } - }); - } - - if(user.getId() == 0) { - add(new WebMarkupContainer("options").setVisible(false)); - add(new WebMarkupContainer("logout").setVisible(false)); - add(new Link("login") { - public void onClick() { - setResponsePage(LoginPage.class); - } - }); - add(new WebMarkupContainer("user").setVisible(false)); - } else { - add(new Link("options") { - public void onClick() { - JtracSession.get().setCurrentSpace(null); - setResponsePage(OptionsPage.class); - } - }); - add(new Link("logout") { - public void onClick() { - Cookie cookie = new Cookie("jtrac", ""); - String path = ((WebRequest) getRequest()).getHttpServletRequest().getContextPath(); - cookie.setPath(path); - ((WebResponse) getResponse()).clearCookie(cookie); - getSession().invalidate(); - logger.debug("invalidated session and cleared cookie"); - // is acegi - cas being used ? - String logoutUrl = JtracApplication.get().getCasLogoutUrl(); - if(logoutUrl != null) { - logger.debug("cas authentication being used, clearing security context and redirecting to cas logout page"); - SecurityContextHolder.clearContext(); - // have to use stateless page reference because session is killed - setResponsePage(CasLogoutPage.class); - } else { - setResponsePage(LogoutPage.class, new PageParameters("locale=" + user.getLocale())); - } - } - }); - add(new WebMarkupContainer("login").setVisible(false)); - add(new Label("user", user.getName())); - } - } -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac.wicket; + +import info.jtrac.domain.Space; +import info.jtrac.domain.State; +import info.jtrac.domain.User; +import java.util.ArrayList; +import java.util.List; +import javax.servlet.http.Cookie; +import org.acegisecurity.context.SecurityContextHolder; +import org.apache.wicket.PageParameters; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.link.Link; +import org.apache.wicket.protocol.http.WebRequest; +import org.apache.wicket.protocol.http.WebResponse; + +/** + * header navigation + */ +public class HeaderPanel extends BasePanel { + + public HeaderPanel() { + super("header"); + + final User user = getPrincipal(); + final Space space = getCurrentSpace(); + final List<Space> spaces = new ArrayList(user.getSpaces()); + + add(new Link("dashboard") { + public void onClick() { + setCurrentSpace(null); + setResponsePage(DashboardPage.class); + } + }); + + if (space == null) { + add(new WebMarkupContainer("spaceName").setVisible(false)); + add(new WebMarkupContainer("new").setVisible(false)); + add(new Link("search") { + public void onClick() { + // if only one space don't use generic search screen + if(spaces.size() == 1) { + Space current = spaces.get(0); + setCurrentSpace(current); + } else { + setCurrentSpace(null); // may have come here with back button! + } + setResponsePage(ItemSearchFormPage.class); + } + @Override + public boolean isVisible() { + return spaces.size() > 0; + } + }); + } else { + add(new Label("spaceName", space.getName())); + add(new Label("prefixCode", space.getPrefixCode())); + if (user.getPermittedTransitions(space, State.NEW).size() > 0) { + add(new Link("new") { + public void onClick() { + setResponsePage(ItemFormPage.class); + } + }); + } else { + add(new WebMarkupContainer("new").setVisible(false)); + } + + add(new Link("search") { + public void onClick() { + setResponsePage(ItemSearchFormPage.class); + } + }); + } + + if(user.getId() == 0) { + add(new WebMarkupContainer("options").setVisible(false)); + add(new WebMarkupContainer("logout").setVisible(false)); + add(new Link("login") { + public void onClick() { + setResponsePage(LoginPage.class); + } + }); + add(new WebMarkupContainer("user").setVisible(false)); + } else { + add(new Link("options") { + public void onClick() { + JtracSession.get().setCurrentSpace(null); + setResponsePage(OptionsPage.class); + } + }); + add(new Link("logout") { + public void onClick() { + Cookie cookie = new Cookie("jtrac", ""); + String path = ((WebRequest) getRequest()).getHttpServletRequest().getContextPath(); + cookie.setPath(path); + ((WebResponse) getResponse()).clearCookie(cookie); + getSession().invalidate(); + logger.debug("invalidated session and cleared cookie"); + // is acegi - cas being used ? + String logoutUrl = JtracApplication.get().getCasLogoutUrl(); + if(logoutUrl != null) { + logger.debug("cas authentication being used, clearing security context and redirecting to cas logout page"); + SecurityContextHolder.clearContext(); + // have to use stateless page reference because session is killed + setResponsePage(CasLogoutPage.class); + } else { + setResponsePage(LogoutPage.class, new PageParameters("locale=" + user.getLocale())); + } + } + }); + add(new WebMarkupContainer("login").setVisible(false)); + add(new Label("user", user.getName())); + } + + } + +} Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/IndexRebuildPage.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/IndexRebuildPage.java 2021-10-06 08:41:39 UTC (rev 1398) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/IndexRebuildPage.java 2021-10-06 12:47:21 UTC (rev 1399) @@ -1,114 +1,117 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.wicket; - -import info.jtrac.Jtrac; -import info.jtrac.domain.BatchInfo; -import org.apache.wicket.ajax.AjaxSelfUpdatingTimerBehavior; -import org.apache.wicket.markup.html.WebMarkupContainer; -import org.apache.wicket.markup.html.basic.Label; -import org.apache.wicket.markup.html.form.Button; -import org.apache.wicket.markup.html.form.Form; -import org.apache.wicket.markup.html.link.Link; -import org.apache.wicket.model.AbstractReadOnlyModel; -import org.apache.wicket.model.IModel; -import org.apache.wicket.util.time.Duration; - -/** - * rebuild indexes admin option - */ - -public class IndexRebuildPage extends BasePage { - - public IndexRebuildPage(boolean success) { - if(success) { - add(new Label("heading", localize("index_rebuild_success.message"))); - add(new WebMarkupContainer("form").setVisible(false)); - } else { - add(new Label("heading", localize("index_rebuild.heading"))); - add(new RebuildIndexesForm("form")); - } - } - - /** - * wicket form - */ - private class RebuildIndexesForm extends Form { - - private BatchInfo batchInfo = new BatchInfo(); - private String errorMessage; - private boolean complete; - - public RebuildIndexesForm(String id) { - super(id); - - final Label progress = new Label("progress"); - progress.setOutputMarkupId(true); - - Button button = new Button("start") { - @Override - public void onSubmit() { - // hide the button - this.setVisible(false); - // long running process, use thread - new Thread() { - private transient Jtrac jtrac = getJtrac(); - public void run() { - try { - jtrac.rebuildIndexes(batchInfo); - } catch (Exception e) { - logger.error("indexing error", e); - errorMessage = e.getMessage(); - } - complete = true; - } - }.start(); - - // poll and update the progress every 5 seconds - progress.add(new AjaxSelfUpdatingTimerBehavior(Duration.seconds(5))); - IModel model = new AbstractReadOnlyModel() { - public Object getObject() { - if(complete) { - if(errorMessage != null) { - setResponsePage(new ErrorPage(errorMessage)); - } else { - // reshow the page, with success message - setResponsePage(new IndexRebuildPage(true)); - } - } - int total = batchInfo.getTotalSize(); - int current = batchInfo.getCurrentPosition(); - int percent = total == 0 ? 0 : 100 * current / total; - return percent + "% [" + current + " / " + total + "]"; - }; - }; - progress.setDefaultModel(model); - } - }; - - add(button); - - add(new Link("cancel") { - public void onClick() { - setResponsePage(OptionsPage.class); - } - }); - - add(progress); - } - } -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac.wicket; + +import info.jtrac.Jtrac; +import info.jtrac.domain.BatchInfo; +import org.apache.wicket.ajax.AjaxSelfUpdatingTimerBehavior; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.form.Button; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.link.Link; +import org.apache.wicket.model.AbstractReadOnlyModel; +import org.apache.wicket.model.IModel; +import org.apache.wicket.util.time.Duration; + +/** + * rebuild indexes admin option + */ +public class IndexRebuildPage extends BasePage { + + public IndexRebuildPage(boolean success) { + if(success) { + add(new Label("heading", localize("index_rebuild_success.message"))); + add(new WebMarkupContainer("form").setVisible(false)); + } else { + add(new Label("heading", localize("index_rebuild.heading"))); + add(new RebuildIndexesForm("form")); + } + } + + /** + * wicket form + */ + private class RebuildIndexesForm extends Form { + + private BatchInfo batchInfo = new BatchInfo(); + private String errorMessage; + private boolean complete; + + public RebuildIndexesForm(String id) { + + super(id); + + final Label progress = new Label("progress"); + progress.setOutputMarkupId(true); + + Button button = new Button("start") { + @Override + public void onSubmit() { + // hide the button + this.setVisible(false); + // long running process, use thread + new Thread() { + private transient Jtrac jtrac = getJtrac(); + public void run() { + try { + jtrac.rebuildIndexes(batchInfo); + } catch (Exception e) { + logger.error("indexing error", e); + errorMessage = e.getMessage(); + } + complete = true; + } + }.start(); + + // poll and update the progress every 5 seconds + progress.add(new AjaxSelfUpdatingTimerBehavior(Duration.seconds(5))); + IModel model = new AbstractReadOnlyModel() { + public Object getObject() { + if(complete) { + if(errorMessage != null) { + setResponsePage(new ErrorPage(errorMessage)); + } else { + // reshow the page, with success message + setResponsePage(new IndexRebuildPage(true)); + } + } + int total = batchInfo.getTotalSize(); + int current = batchInfo.getCurrentPosition(); + int percent = total == 0 ? 0 : 100 * current / total; + return percent + "% [" + current + " / " + total + "]"; + }; + }; + progress.setModel(model); + } + }; + + add(button); + + add(new Link("cancel") { + public void onClick() { + setResponsePage(OptionsPage.class); + } + + }); + + add(progress); + } + + } + +} Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ItemFormPage.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ItemFormPage.java 2021-10-06 08:41:39 UTC (rev 1398) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ItemFormPage.java 2021-10-06 12:47:21 UTC (rev 1399) @@ -1,318 +1,317 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.wicket; - -import info.jtrac.domain.Item; -import info.jtrac.domain.ItemSearch; -import info.jtrac.domain.ItemUser; -import info.jtrac.domain.Space; -import info.jtrac.domain.State; -import info.jtrac.domain.User; -import info.jtrac.domain.UserSpaceRole; -import info.jtrac.util.UserUtils; - -import java.util.List; - -import org.apache.wicket.PageParameters; -import org.apache.wicket.behavior.HeaderContributor; -import org.apache.wicket.markup.html.IHeaderContributor; -import org.apache.wicket.markup.html.IHeaderResponse; -import org.apache.wicket.markup.html.WebMarkupContainer; -import org.apache.wicket.markup.html.form.Button; -import org.apache.wicket.markup.html.form.CheckBox; -import org.apache.wicket.markup.html.form.DropDownChoice; -import org.apache.wicket.markup.html.form.Form; -import org.apache.wicket.markup.html.form.IChoiceRenderer; -import org.apache.wicket.markup.html.form.ListMultipleChoice; -import org.apache.wicket.markup.html.form.TextArea; -import org.apache.wicket.markup.html.form.TextField; -import org.apache.wicket.markup.html.form.upload.FileUpload; -import org.apache.wicket.markup.html.form.upload.FileUploadField; -import org.apache.wicket.markup.html.link.Link; -import org.apache.wicket.markup.html.panel.FeedbackPanel; -import org.apache.wicket.model.BoundCompoundPropertyModel; -import org.apache.wicket.model.LoadableDetachableModel; -import org.apache.wicket.util.lang.Bytes; - -/** - * Create / Edit item form page - */ -public class ItemFormPage extends BasePage { - /** - * Default constructor - */ - public ItemFormPage() { - Item item = new Item(); - item.setSpace(getCurrentSpace()); - item.setStatus(State.NEW); - add(new ItemForm("form", item)); - } - - /** - * Constructor with provided item id. - * - * @param itemId The - */ - public ItemFormPage(long itemId) { - Item item = getJtrac().loadItem(itemId); - add(new ItemForm("form", item)); - } - - /** - * Wicket form - */ - private class ItemForm extends Form { - private JtracFeedbackMessageFilter filter; - private FileUploadField fileUploadField = new FileUploadField("file"); - private boolean editMode; - private int version; - - /** - * Constructor - * - * @param id - * @param item - */ - public ItemForm(String id, final Item item) { - super(id); - setMultiPart(true); - FeedbackPanel feedback = new FeedbackPanel("feedback"); - filter = new JtracFeedbackMessageFilter(); - feedback.setFilter(filter); - add(feedback); - version = item.getVersion(); - - if (item.getId() > 0) { - editMode = true; - } - - BoundCompoundPropertyModel model = null; - if (editMode) { - /* - * This ensures that the model object is re-loaded as part - * of the form submission workflow before form binding and - * avoids hibernate lazy loading issues during the whole - * update transaction. - */ - LoadableDetachableModel itemModel = new LoadableDetachableModel() { - protected Object load() { - logger.debug("attaching existing item " + item.getId()); - return getJtrac().loadItem(item.getId()); - } - }; - model = new BoundCompoundPropertyModel(itemModel); - } else { - model = new BoundCompoundPropertyModel(item); - } - setModel(model); - - /* - * =================================================== - * Summary - * =================================================== - */ - final TextField summaryField = new TextField("summary"); - summaryField.setRequired(true); - summaryField.add(new ErrorHighlighter()); - summaryField.setOutputMarkupId(true); - add(summaryField); - add(new HeaderContributor(new IHeaderContributor() { - public void renderHead(IHeaderResponse response) { - response.renderOnLoadJavascript("document.getElementById('" + - summaryField.getMarkupId() + "').focus()"); - } - })); - - /* - * =================================================== - * Delete button - * =================================================== - */ - Button delete = new Button("delete") { - @Override - public void onSubmit() { - String heading = localize("item_delete.confirm"); - String warning = localize("item_delete.line2"); - String line1 = localize("item_delete.line1"); - ConfirmPage confirm = new ConfirmPage(ItemFormPage.this, heading, warning, new String[] { line1 }) { - public void onConfirm() { - // avoid lazy init problem - getJtrac().removeItem(getJtrac().loadItem(item.getId())); - ItemSearch itemSearch = JtracSession.get().getItemSearch(); - if (itemSearch != null) { - setResponsePage(new ItemListPage(itemSearch)); - } else { - setResponsePage(DashboardPage.class); - } - } - }; - setResponsePage(confirm); - } - }; - - delete.setDefaultFormProcessing(false); - add(delete); - if (!editMode) { - delete.setVisible(false); - } - - /* - * =================================================== - * Detail - * =================================================== - */ - add(new TextArea("detail").setRequired(true).add(new ErrorHighlighter())); - - /* - * =================================================== - * Custom fields - * =================================================== - */ - if (editMode) { - add(new CustomFieldsFormPanel("fields", model, item.getSpace()).setRenderBodyOnly(true)); - } else { - add(new CustomFieldsFormPanel("fields", model, item, getPrincipal()).setRenderBodyOnly(true)); - } - - /* - * Hide some components if editing item - */ - WebMarkupContainer hideAssignedTo = new WebMarkupContainer("hideAssignedTo"); - WebMarkupContainer hideNotifyList = new WebMarkupContainer("hideNotifyList"); - WebMarkupContainer hideEditReason = new WebMarkupContainer("hideEditReason"); - add(hideAssignedTo); - add(hideNotifyList); - add(hideEditReason); - if (editMode) { - hideAssignedTo.setVisible(false); - hideNotifyList.setVisible(false); - hideEditReason.add(new TextArea("editReason").setRequired(true).add(new ErrorHighlighter())); - } else { - hideEditReason.setVisible(false); - - /* - * =================================================== - * Assigned to - * =================================================== - */ - Space space = item.getSpace(); - List<UserSpaceRole> userSpaceRoles = getJtrac().findUserRolesForSpace(space.getId()); - List<User> assignable = UserUtils.filterUsersAbleToTransitionFrom(userSpaceRoles, space, State.OPEN); - DropDownChoice choice = new DropDownChoice("assignedTo", assignable, new IChoiceRenderer() { - public Object getDisplayValue(Object o) { - return ((User) o).getName(); - } - - public String getIdValue(Object o, int i) { - return ((User) o).getId() + ""; - } - }); - - choice.setNullValid(true); - choice.setRequired(true); - WebMarkupContainer border = new WebMarkupContainer("border"); - border.add(choice); - border.add(new ErrorHighlighter(choice)); - hideAssignedTo.add(border); - - /* - * =================================================== - * Notify list - * =================================================== - */ - List<ItemUser> choices = UserUtils.convertToItemUserList(userSpaceRoles); - ListMultipleChoice itemUsers = new JtracCheckBoxMultipleChoice("itemUsers", choices, - new IChoiceRenderer() { - public Object getDisplayValue(Object o) { - return ((ItemUser) o).getUser().getName(); - } - - public String getIdValue(Object o, int i) { - return ((ItemUser) o).getUser().getId() + ""; - } - }, true); - hideNotifyList.add(itemUsers); - - /* - * =================================================== - * Attachment - * =================================================== - */ - hideNotifyList.add(fileUploadField); - setMaxSize(Bytes.megabytes(getJtrac().getAttachmentMaxSizeInMb())); - } - - /* - * =================================================== - * Send notifications - * =================================================== - */ - add(new CheckBox("sendNotifications")); - - /* - * =================================================== - * Cancel - * =================================================== - */ - add(new Link("cancel") { - public void onClick() { - setResponsePage(ItemViewPage.class, new PageParameters("0=" + item.getRefId())); - } - }.setVisible(editMode && JtracSession.get().getItemSearch() != null)); - } - - /* (non-Javadoc) - * @see org.apache.wicket.markup.html.form.Form#onValidate() - */ - @Override - protected void onValidate() { - filter.reset(); - Item item = (Item) getModelObject(); - if (editMode && item.getVersion() != version) { - /* - * User must have used back button after edit - */ - error(localize("item_form.error.version")); - } - } - - /* (non-Javadoc) - * @see org.apache.wicket.markup.html.form.Form#onSubmit() - */ - @Override - protected void onSubmit() { - final FileUpload fileUpload = fileUploadField.getFileUpload(); - Item item = (Item) getModelObject(); - User user = getPrincipal(); - if (editMode) { - getJtrac().updateItem(item, user); - } else { - item.setLoggedBy(user); - item.setStatus(State.OPEN); - getJtrac().storeItem(item, fileUpload); - } - - /* - * On creating an item, clear any search filter (especially - * the related item) from session. - */ - JtracSession.get().setItemSearch(null); - setResponsePage(ItemViewPage.class, new PageParameters("0=" + item.getRefId())); - } - - } // end inner class ItemForm -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac.wicket; + +import info.jtrac.domain.Item; +import info.jtrac.domain.ItemSearch; +import info.jtrac.domain.ItemUser; +import info.jtrac.domain.Space; +import info.jtrac.domain.State; +import info.jtrac.domain.User; +import info.jtrac.domain.UserSpaceRole; +import info.jtrac.util.UserUtils; +import java.util.List; +import org.apache.wicket.PageParameters; +import org.apache.wicket.behavior.HeaderContributor; +import org.apache.wicket.markup.html.IHeaderContributor; +import org.apache.wicket.markup.html.IHeaderResponse; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.form.Button; +import org.apache.wicket.markup.html.form.CheckBox; +import org.apache.wicket.markup.html.form.DropDownChoice; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.form.IChoiceRenderer; +import org.apache.wicket.markup.html.form.ListMultipleChoice; +import org.apache.wicket.markup.html.form.TextArea; +import org.apache.wicket.markup.html.form.TextField; +import org.apache.wicket.markup.html.form.upload.FileUpload; +import org.apache.wicket.markup.html.form.upload.FileUploadField; +import org.apache.wicket.markup.html.link.Link; +import org.apache.wicket.markup.html.panel.FeedbackPanel; +import org.apache.wicket.model.BoundCompoundPropertyModel; +import org.apache.wicket.model.LoadableDetachableModel; +import org.apache.wicket.util.lang.Bytes; + +/** + * Create / Edit item form page + */ +public class ItemFormPage extends BasePage { + /** + * Default constructor + */ + public ItemFormPage() { + Item item = new Item(); + item.setSpace(getCurrentSpace()); + item.setStatus(State.NEW); + add(new ItemForm("form", item)); + } + + /** + * Constructor with provided item id. + * + * @param itemId The + */ + public ItemFormPage(long itemId) { + Item item = getJtrac().loadItem(itemId); + add(new ItemForm("form", item)); + } + + /** + * Wicket form + */ + private class ItemForm extends Form { + private JtracFeedbackMessageFilter filter; + private FileUploadField fileUploadField = new FileUploadField("file"); + private boolean editMode; + private int version; + + /** + * Constructor + * + * @param id + * @param item + */ + public ItemForm(String id, final Item item) { + super(id); + setMultiPart(true); + FeedbackPanel feedback = new FeedbackPanel("feedback"); + filter = new JtracFeedbackMessageFilter(); + feedback.setFilter(filter); + add(feedback); + version = item.getVersion(); + + if (item.getId() > 0) { + editMode = true; + } + + BoundCompoundPropertyModel model = null; + if (editMode) { + /* + * This ensures that the model object is re-loaded as part + * of the form submission workflow before form binding and + * avoids hibernate lazy loading issues during the whole + * update transaction. + */ + LoadableDetachableModel itemModel = new LoadableDetachableModel() { + protected Object load() { + logger.debug("attaching existing item " + item.getId()); + return getJtrac().loadItem(item.getId()); + } + }; + model = new BoundCompoundPropertyModel(itemModel); + } else { + model = new BoundCompoundPropertyModel(item); + } + setModel(model); + + /* + * =================================================== + * Summary + * =================================================== + */ + final TextField summaryField = new TextField("summary"); + summaryField.setRequired(true); + summaryField.add(new ErrorHighlighter()); + summaryField.setOutputMarkupId(true); + add(summaryField); + add(new HeaderContributor(new IHeaderContributor() { + public void renderHead(IHeaderResponse response) { + response.renderOnLoadJavascript("document.getElementById('" + + summaryField.getMarkupId() + "').focus()"); + } + })); + + /* + * =================================================== + * Delete button + * =================================================== + */ + Button delete = new Button("delete") { + @Override + public void onSubmit() { + String heading = localize("item_delete.confirm"); + String warning = localize("item_delete.line2"); + String line1 = localize("item_delete.line1"); + ConfirmPage confirm = new ConfirmPage(ItemFormPage.this, heading, warning, new String[] { line1 }) { + public void onConfirm() { + // avoid lazy init problem + getJtrac().removeItem(getJtrac().loadItem(item.getId())); + ItemSearch itemSearch = JtracSession.get().getItemSearch(); + if (itemSearch != null) { + setResponsePage(new ItemListPage(itemSearch)); + } else { + setResponsePage(DashboardPage.class); + } + } + }; + setResponsePage(confirm); + } + }; + + delete.setDefaultFormProcessing(false); + add(delete); + if (!editMode) { + delete.setVisible(false); + } + + /* + * =================================================== + * Detail + * =================================================== + */ + add(new TextArea("detail").setRequired(true).add(new ErrorHighlighter())); + + /* + * =================================================== + * Custom fields + * =================================================== + */ + if (editMode) { + add(new CustomFieldsFormPanel("fields", model, item.getSpace()).setRenderBodyOnly(true)); + } else { + add(new CustomFieldsFormPanel("fields", model, item, getPrincipal()).setRenderBodyOnly(true)); + } + + /* + * Hide some components if editing item + */ + WebMarkupContainer hideAssignedTo = new WebMarkupContainer("hideAssignedTo"); + WebMarkupContainer hideNotifyList = new WebMarkupContainer("hideNotifyList"); + WebMarkupContainer hideEditReason = new WebMarkupContainer("hideEditReason"); + add(hideAssignedTo); + add(hideNotifyList); + add(hideEditReason); + if (editMode) { + hideAssignedTo.setVisible(false); + hideNotifyList.setVisible(false); + hideEditReason.add(new TextArea("editReason").setRequired(true).add(new ErrorHighlighter())); + } else { + hideEditReason.setVisible(false); + + /* + * =================================================== + * Assigned to + * =================================================== + */ + Space space = item.getSpace(); + List<UserSpaceRole> userSpaceRoles = getJtrac().findUserRolesForSpace(space.getId()); + List<User> assignable = UserUtils.filterUsersAbleToTransitionFrom(userSpaceRoles, space, State.OPEN); + DropDownChoice choice = new DropDownChoice("assignedTo", assignable, new IChoiceRenderer() { + public Object getDisplayValue(Object o) { + return ((User) o).getName(); + } + + public String getIdValue(Object o, int i) { + return ((User) o).getId() + ""; + } + }); + + choice.setNullValid(true); + choice.setRequired(true); + WebMarkupContainer border = new WebMarkupContainer("border"); + border.add(choice); + border.add(new ErrorHighlighter(choice)); + hideAssignedTo.add(border); + + /* + * =================================================== + * Notify list + * =================================================== + */ + List<ItemUser> choices = UserUtils.convertToItemUserList(userSpaceRoles); + ListMultipleChoice itemUsers = new JtracCheckBoxMultipleChoice("itemUsers", choices, + new IChoiceRenderer() { + public Object getDisplayValue(Object o) { + return ((ItemUser) o).getUser().getName(); + } + + public String getIdValue(Object o, int i) { + return ((ItemUser) o).getUser().getId() + ""; + } + }, true); + hideNotifyList.add(itemUsers); + + /* + * =================================================== + * Attachment + * =================================================== + */ + hideNotifyList.add(fileUploadField); + setMaxSize(Bytes.megabytes(getJtrac().getAttachmentMaxSizeInMb())); + } + + /* + * =================================================== + * Send notifications + * =================================================== + */ + add(new CheckBox("sendNotifications")); + + /* + * =================================================== + * Cancel + * =================================================== + */ + add(new Link("cancel") { + public void onClick() { + setResponsePage(ItemViewPage.class, new PageParameters("0=" + item.getRefId())); + } + }.setVisible(editMode && JtracSession.get().getItemSearch() != null)); + } + + /* (non-Javadoc) + * @see org.apache.wicket.markup.html.form.Form#validate() + */ + @Override + protected void validate() { + filter.reset(); + Item item = (Item) getModelObject(); + if (editMode && item.getVersion() != version) { + /* + * User must have used back button after edit + */ + error(localize("item_form.error.version")); + } + super.validate(); + } + + /* (non-Javadoc) + * @see org.apache.wicket.markup.html.form.Form#onSubmit() + */ + @Override + protected void onSubmit() { + final FileUpload fileUpload = fileUploadField.getFileUpload(); + Item item = (Item) getModelObject(); + User user = getPrincipal(); + if (editMode) { + getJtrac().updateItem(item, user); + } else { + item.setLoggedBy(user); + item.setStatus(State.OPEN); + getJtrac().storeItem(item, fileUpload); + } + + /* + * On creating an item, clear any search filter (especially + * the related item) from session. + */ + JtracSession.get().setItemSearch(null); + setResponsePage(ItemViewPage.class, new PageParameters("0=" + item.getRefId())); + } + + } // end inner class ItemForm +} \ No newline at end of file Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java 2021-10-06 08:41:39 UTC (rev 1398) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java 2021-10-06 12:47:21 UTC (rev 1399) @@ -226,7 +226,10 @@ final ColumnHeading ch = (ColumnHeading) listItem.getModelObject(); Link headingLink = new Link("heading") { public void onClick() { - doSort(ch.getNameText()); + // hack: it looks like a link, but clicking it does nothing + if (! ch.getNameText().equals("lastChanged")) { + doSort(ch.getNameText()); + } } }; listItem.add(headingLink); @@ -251,7 +254,7 @@ protected void populateItem(ListItem listItem) { // cast to AbstactItem - show history may be == true final AbstractItem item = (AbstractItem) listItem.getModelObject(); - + if (selectedItemId == item.getId()) { listItem.add(new SimpleAttributeModifier("class", "selected")); } else if(listItem.getIndex() % 2 == 1) { @@ -259,7 +262,7 @@ } final boolean showHistory = itemSearch.isShowHistory(); - + ListView fieldValues = new ListView("columns", columnHeadings) { protected void populateItem(ListItem listItem) { ColumnHeading ch = (ColumnHeading) listItem.getModelObject(); @@ -319,12 +322,17 @@ value = new PropertyModel(item, "assignedTo.name"); break; case TIME_STAMP: - if (item instanceof History) { + value = new Model(DateUtils.formatTimeStamp(item.getTimeStamp())); + break; + case LAST_CHANGED: + if (item instanceof Item) { + History history = ((Item) item).getLatestHistory(); + if (history == null) + value = new Model(DateUtils.formatTimeStamp(item.getTimeStamp())); + else + value = new Model(DateUtils.formatTimeStamp(((Item) item).getLatestHistory().getTimeStamp())); + } else { value = new Model(DateUtils.formatTimeStamp(item.getTimeStamp())); - } else { - if (item instanceof Item) { - value = new Model(DateUtils.formatTimeStamp( ((Item)item).getLatestHistory().getTimeStamp())); - } } break; case SPACE: Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewFormPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewFormPanel.java 2021-10-06 08:41:39 UTC (rev 1398) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewFormPanel.java 2021-10-06 12:47:21 UTC (rev 1399) @@ -1,198 +1,200 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.wicket; - -import info.jtrac.domain.History; -import info.jtrac.domain.Item; -import info.jtrac.domain.ItemSearch; -import info.jtrac.domain.ItemUser; -import info.jtrac.domain.Space; -import info.jtrac.domain.State; -import info.jtrac.domain.User; -import info.jtrac.domain.UserSpaceRole; -import info.jtrac.util.UserUtils; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.apache.wicket.PageParameters; -import org.apache.wicket.ajax.AjaxRequestTarget; -import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; -import org.apache.wicket.markup.html.WebMarkupContainer; -import org.apache.wicket.markup.html.form.CheckBox; -import org.apache.wicket.markup.html.form.DropDownChoice; -import org.apache.wicket.markup.html.form.Form; -import org.apache.wicket.markup.html.form.FormComponent; -import org.apache.wicket.markup.html.form.IChoiceRenderer; -import org.apache.wicket.markup.html.form.ListMultipleChoice; -import org.apache.wicket.markup.html.form.TextArea; -import org.apache.wicket.markup.html.form.upload.FileUpload; -import org.apache.wicket.markup.html.form.upload.FileUploadField; -import org.apache.wicket.markup.html.form.validation.AbstractFormValidator; -import org.apache.wicket.markup.html.panel.FeedbackPanel; -import org.apache.wicket.model.BoundCompoundPropertyModel; -import org.apache.wicket.util.lang.Bytes; - -/** - * Form to update history for item - */ -public class ItemViewFormPanel extends BasePanel { - - private JtracFeedbackMessageFilter filter; - private ItemSearch itemSearch; - - public ItemViewFormPanel(String id, Item item, ItemSearch itemSearch) { - super(id); - this.itemSearch = itemSearch; - FeedbackPanel feedback = new FeedbackPanel("feedback"); - filter = new JtracFeedbackMessageFilter(); - feedback.setFilter(filter); - add(feedback); - add(new ItemViewForm("form", item)); - } - - /** - * wicket form - */ - private class ItemViewForm extends Form { - - private FileUploadField fileUploadField; - private long itemId; - private DropDownChoice assignedToChoice; - private DropDownChoice statusChoice; - - public ItemViewForm(String id, final Item item) { - super(id); - setMultiPart(true); - this.itemId = item.getId(); - final History history = new History(); - history.setItemUsers(item.getItemUsers()); - final BoundCompoundPropertyModel model = new BoundCompoundPropertyModel(history); - setModel(model); - add(new TextArea("comment").setRequired(true).add(new ErrorHighlighter())); - // custom fields =================================================== - User user = getPrincipal(); - add(new CustomFieldsFormPanel("fields", model, item, user)); - // ================================================================= - final Space space = item.getSpace(); - final List<UserSpaceRole> userSpaceRoles = getJtrac().findUserRolesForSpace(space.getId()); - // assigned to ===================================================== - final WebMarkupContainer border = new WebMarkupContainer("border"); - border.setOutputMarkupId(true); - final WebMarkupContainer hide = new WebMarkupContainer("hide"); - border.add(hide); - final List<User> emptyList = new ArrayList<User>(0); // will be populated over Ajax - assignedToChoice = new DropDownChoice("assignedTo", emptyList, new IChoiceRenderer() { - public Object getDisplayValue(Object o) { - return ((User) o).getName(); - } - public String getIdValue(Object o, int i) { - return ((User) o).getId() + ""; - } - }); - assignedToChoice.setOutputMarkupId(true); - assignedToChoice.setVisible(false); - assignedToChoice.setNullValid(true); - border.add(new ErrorHighlighter(assignedToChoice)); - border.add(assignedToChoice); - add(border); - // status ========================================================== - final Map<Integer, String> statesMap = item.getPermittedTransitions(user); - List<Integer> states = new ArrayList(statesMap.keySet()); - statusChoice = new IndicatingDropDownChoice("status", states, new IChoiceRenderer() { - public Object getDisplayValue(Object o) { - return statesMap.get(o... [truncated message content] |
From: <udi...@us...> - 2021-10-06 08:41:44
|
Revision: 1398 http://sourceforge.net/p/j-trac/code/1398 Author: udittmer Date: 2021-10-06 08:41:39 +0000 (Wed, 06 Oct 2021) Log Message: ----------- update Wicket to 1.4 Modified Paths: -------------- trunk/jtrac/pom.xml trunk/jtrac/src/main/java/info/jtrac/wicket/HeaderPanel.java trunk/jtrac/src/main/java/info/jtrac/wicket/IndexRebuildPage.java trunk/jtrac/src/main/java/info/jtrac/wicket/ItemFormPage.java trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewFormPanel.java trunk/jtrac/src/main/java/info/jtrac/wicket/JtracCheckBoxMultipleChoice.java trunk/jtrac/src/main/java/info/jtrac/wicket/SpaceFieldFormPage.java trunk/jtrac/src/main/java/info/jtrac/wicket/SpaceFormPage.java trunk/jtrac/src/main/java/info/jtrac/wicket/UserFormPage.java Modified: trunk/jtrac/pom.xml =================================================================== --- trunk/jtrac/pom.xml 2021-10-05 16:45:41 UTC (rev 1397) +++ trunk/jtrac/pom.xml 2021-10-06 08:41:39 UTC (rev 1398) @@ -4,7 +4,7 @@ <groupId>info.jtrac</groupId> <artifactId>jtrac</artifactId> <packaging>war</packaging> - <version>2.2.0-b1</version> + <version>2.2.0-b2</version> <name>JTrac</name> <description> JTrac is a generic issue-tracking web-application that can be easily customized by adding custom fields @@ -337,9 +337,9 @@ <dependency> <groupId>org.apache.wicket</groupId> <artifactId>wicket</artifactId> + <version>1.4.23</version> + <!-- <version>1.3.7</version> - <!-- - <version>1.4.23</version> <version>1.5.16</version> --> <type>pom</type> @@ -347,9 +347,9 @@ <dependency> <groupId>org.apache.wicket</groupId> <artifactId>wicket-extensions</artifactId> + <version>1.4.23</version> + <!-- <version>1.3.7</version> - <!-- - <version>1.4.23</version> <version>1.5.16</version> --> </dependency> Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/HeaderPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/HeaderPanel.java 2021-10-05 16:45:41 UTC (rev 1397) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/HeaderPanel.java 2021-10-06 08:41:39 UTC (rev 1398) @@ -1,133 +1,135 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.wicket; - -import info.jtrac.domain.Space; -import info.jtrac.domain.State; -import info.jtrac.domain.User; -import java.util.ArrayList; -import java.util.List; -import javax.servlet.http.Cookie; -import org.acegisecurity.context.SecurityContextHolder; -import org.apache.wicket.PageParameters; -import org.apache.wicket.markup.html.WebMarkupContainer; -import org.apache.wicket.markup.html.basic.Label; -import org.apache.wicket.markup.html.link.Link; -import org.apache.wicket.protocol.http.WebRequest; -import org.apache.wicket.protocol.http.WebResponse; - -/** - * header navigation - */ -public class HeaderPanel extends BasePanel { - - public HeaderPanel() { - super("header"); - - final User user = getPrincipal(); - final Space space = getCurrentSpace(); - final List<Space> spaces = new ArrayList(user.getSpaces()); - - add(new Link("dashboard") { - public void onClick() { - setCurrentSpace(null); - setResponsePage(DashboardPage.class); - } - }); - - if (space == null) { - add(new WebMarkupContainer("spaceName").setVisible(false)); - add(new WebMarkupContainer("new").setVisible(false)); - add(new Link("search") { - public void onClick() { - // if only one space don't use generic search screen - if(spaces.size() == 1) { - Space current = spaces.get(0); - setCurrentSpace(current); - } else { - setCurrentSpace(null); // may have come here with back button! - } - setResponsePage(ItemSearchFormPage.class); - } - @Override - public boolean isVisible() { - return spaces.size() > 0; - } - }); - } else { - add(new Label("spaceName", space.getName())); - add(new Label("prefixCode", space.getPrefixCode())); - if (user.getPermittedTransitions(space, State.NEW).size() > 0) { - add(new Link("new") { - public void onClick() { - setResponsePage(ItemFormPage.class); - } - }); - } else { - add(new WebMarkupContainer("new").setVisible(false)); - } - - add(new Link("search") { - public void onClick() { - setResponsePage(ItemSearchFormPage.class); - } - }); - } - - if(user.getId() == 0) { - add(new WebMarkupContainer("options").setVisible(false)); - add(new WebMarkupContainer("logout").setVisible(false)); - add(new Link("login") { - public void onClick() { - setResponsePage(LoginPage.class); - } - }); - add(new WebMarkupContainer("user").setVisible(false)); - } else { - add(new Link("options") { - public void onClick() { - JtracSession.get().setCurrentSpace(null); - setResponsePage(OptionsPage.class); - } - }); - add(new Link("logout") { - public void onClick() { - Cookie cookie = new Cookie("jtrac", ""); - String path = ((WebRequest) getRequest()).getHttpServletRequest().getContextPath(); - cookie.setPath(path); - ((WebResponse) getResponse()).clearCookie(cookie); - getSession().invalidate(); - logger.debug("invalidated session and cleared cookie"); - // is acegi - cas being used ? - String logoutUrl = JtracApplication.get().getCasLogoutUrl(); - if(logoutUrl != null) { - logger.debug("cas authentication being used, clearing security context and redirecting to cas logout page"); - SecurityContextHolder.clearContext(); - // have to use stateless page reference because session is killed - setResponsePage(CasLogoutPage.class); - } else { - setResponsePage(LogoutPage.class, new PageParameters("locale=" + user.getLocale())); - } - } - }); - add(new WebMarkupContainer("login").setVisible(false)); - add(new Label("user", user.getName())); - } - - } - -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac.wicket; + +import info.jtrac.domain.Space; +import info.jtrac.domain.State; +import info.jtrac.domain.User; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.Cookie; + +import org.acegisecurity.context.SecurityContextHolder; +import org.apache.wicket.PageParameters; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.link.Link; +import org.apache.wicket.protocol.http.WebRequest; +import org.apache.wicket.protocol.http.WebResponse; + +/** + * header navigation + */ +public class HeaderPanel extends BasePanel { + + public HeaderPanel() { + super("header"); + + final User user = getPrincipal(); + final Space space = getCurrentSpace(); + final List<Space> spaces = new ArrayList(user.getSpaces()); + + add(new Link("dashboard") { + public void onClick() { + setCurrentSpace(null); + setResponsePage(DashboardPage.class); + } + }); + + if (space == null) { + add(new WebMarkupContainer("spaceName").setVisible(false)); + add(new Label("prefixCode", "")); + add(new WebMarkupContainer("new").setVisible(false)); + add(new Link("search") { + public void onClick() { + // if only one space don't use generic search screen + if(spaces.size() == 1) { + Space current = spaces.get(0); + setCurrentSpace(current); + } else { + setCurrentSpace(null); // may have come here with back button! + } + setResponsePage(ItemSearchFormPage.class); + } + @Override + public boolean isVisible() { + return spaces.size() > 0; + } + }); + } else { + add(new Label("spaceName", space.getName())); + add(new Label("prefixCode", space.getPrefixCode())); + if (user.getPermittedTransitions(space, State.NEW).size() > 0) { + add(new Link("new") { + public void onClick() { + setResponsePage(ItemFormPage.class); + } + }); + } else { + add(new WebMarkupContainer("new").setVisible(false)); + } + + add(new Link("search") { + public void onClick() { + setResponsePage(ItemSearchFormPage.class); + } + }); + } + + if(user.getId() == 0) { + add(new WebMarkupContainer("options").setVisible(false)); + add(new WebMarkupContainer("logout").setVisible(false)); + add(new Link("login") { + public void onClick() { + setResponsePage(LoginPage.class); + } + }); + add(new WebMarkupContainer("user").setVisible(false)); + } else { + add(new Link("options") { + public void onClick() { + JtracSession.get().setCurrentSpace(null); + setResponsePage(OptionsPage.class); + } + }); + add(new Link("logout") { + public void onClick() { + Cookie cookie = new Cookie("jtrac", ""); + String path = ((WebRequest) getRequest()).getHttpServletRequest().getContextPath(); + cookie.setPath(path); + ((WebResponse) getResponse()).clearCookie(cookie); + getSession().invalidate(); + logger.debug("invalidated session and cleared cookie"); + // is acegi - cas being used ? + String logoutUrl = JtracApplication.get().getCasLogoutUrl(); + if(logoutUrl != null) { + logger.debug("cas authentication being used, clearing security context and redirecting to cas logout page"); + SecurityContextHolder.clearContext(); + // have to use stateless page reference because session is killed + setResponsePage(CasLogoutPage.class); + } else { + setResponsePage(LogoutPage.class, new PageParameters("locale=" + user.getLocale())); + } + } + }); + add(new WebMarkupContainer("login").setVisible(false)); + add(new Label("user", user.getName())); + } + } +} Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/IndexRebuildPage.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/IndexRebuildPage.java 2021-10-05 16:45:41 UTC (rev 1397) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/IndexRebuildPage.java 2021-10-06 08:41:39 UTC (rev 1398) @@ -1,117 +1,114 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.wicket; - -import info.jtrac.Jtrac; -import info.jtrac.domain.BatchInfo; -import org.apache.wicket.ajax.AjaxSelfUpdatingTimerBehavior; -import org.apache.wicket.markup.html.WebMarkupContainer; -import org.apache.wicket.markup.html.basic.Label; -import org.apache.wicket.markup.html.form.Button; -import org.apache.wicket.markup.html.form.Form; -import org.apache.wicket.markup.html.link.Link; -import org.apache.wicket.model.AbstractReadOnlyModel; -import org.apache.wicket.model.IModel; -import org.apache.wicket.util.time.Duration; - -/** - * rebuild indexes admin option - */ -public class IndexRebuildPage extends BasePage { - - public IndexRebuildPage(boolean success) { - if(success) { - add(new Label("heading", localize("index_rebuild_success.message"))); - add(new WebMarkupContainer("form").setVisible(false)); - } else { - add(new Label("heading", localize("index_rebuild.heading"))); - add(new RebuildIndexesForm("form")); - } - } - - /** - * wicket form - */ - private class RebuildIndexesForm extends Form { - - private BatchInfo batchInfo = new BatchInfo(); - private String errorMessage; - private boolean complete; - - public RebuildIndexesForm(String id) { - - super(id); - - final Label progress = new Label("progress"); - progress.setOutputMarkupId(true); - - Button button = new Button("start") { - @Override - public void onSubmit() { - // hide the button - this.setVisible(false); - // long running process, use thread - new Thread() { - private transient Jtrac jtrac = getJtrac(); - public void run() { - try { - jtrac.rebuildIndexes(batchInfo); - } catch (Exception e) { - logger.error("indexing error", e); - errorMessage = e.getMessage(); - } - complete = true; - } - }.start(); - - // poll and update the progress every 5 seconds - progress.add(new AjaxSelfUpdatingTimerBehavior(Duration.seconds(5))); - IModel model = new AbstractReadOnlyModel() { - public Object getObject() { - if(complete) { - if(errorMessage != null) { - setResponsePage(new ErrorPage(errorMessage)); - } else { - // reshow the page, with success message - setResponsePage(new IndexRebuildPage(true)); - } - } - int total = batchInfo.getTotalSize(); - int current = batchInfo.getCurrentPosition(); - int percent = total == 0 ? 0 : 100 * current / total; - return percent + "% [" + current + " / " + total + "]"; - }; - }; - progress.setModel(model); - } - }; - - add(button); - - add(new Link("cancel") { - public void onClick() { - setResponsePage(OptionsPage.class); - } - - }); - - add(progress); - } - - } - -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac.wicket; + +import info.jtrac.Jtrac; +import info.jtrac.domain.BatchInfo; +import org.apache.wicket.ajax.AjaxSelfUpdatingTimerBehavior; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.form.Button; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.link.Link; +import org.apache.wicket.model.AbstractReadOnlyModel; +import org.apache.wicket.model.IModel; +import org.apache.wicket.util.time.Duration; + +/** + * rebuild indexes admin option + */ + +public class IndexRebuildPage extends BasePage { + + public IndexRebuildPage(boolean success) { + if(success) { + add(new Label("heading", localize("index_rebuild_success.message"))); + add(new WebMarkupContainer("form").setVisible(false)); + } else { + add(new Label("heading", localize("index_rebuild.heading"))); + add(new RebuildIndexesForm("form")); + } + } + + /** + * wicket form + */ + private class RebuildIndexesForm extends Form { + + private BatchInfo batchInfo = new BatchInfo(); + private String errorMessage; + private boolean complete; + + public RebuildIndexesForm(String id) { + super(id); + + final Label progress = new Label("progress"); + progress.setOutputMarkupId(true); + + Button button = new Button("start") { + @Override + public void onSubmit() { + // hide the button + this.setVisible(false); + // long running process, use thread + new Thread() { + private transient Jtrac jtrac = getJtrac(); + public void run() { + try { + jtrac.rebuildIndexes(batchInfo); + } catch (Exception e) { + logger.error("indexing error", e); + errorMessage = e.getMessage(); + } + complete = true; + } + }.start(); + + // poll and update the progress every 5 seconds + progress.add(new AjaxSelfUpdatingTimerBehavior(Duration.seconds(5))); + IModel model = new AbstractReadOnlyModel() { + public Object getObject() { + if(complete) { + if(errorMessage != null) { + setResponsePage(new ErrorPage(errorMessage)); + } else { + // reshow the page, with success message + setResponsePage(new IndexRebuildPage(true)); + } + } + int total = batchInfo.getTotalSize(); + int current = batchInfo.getCurrentPosition(); + int percent = total == 0 ? 0 : 100 * current / total; + return percent + "% [" + current + " / " + total + "]"; + }; + }; + progress.setDefaultModel(model); + } + }; + + add(button); + + add(new Link("cancel") { + public void onClick() { + setResponsePage(OptionsPage.class); + } + }); + + add(progress); + } + } +} Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ItemFormPage.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ItemFormPage.java 2021-10-05 16:45:41 UTC (rev 1397) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ItemFormPage.java 2021-10-06 08:41:39 UTC (rev 1398) @@ -1,317 +1,318 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.wicket; - -import info.jtrac.domain.Item; -import info.jtrac.domain.ItemSearch; -import info.jtrac.domain.ItemUser; -import info.jtrac.domain.Space; -import info.jtrac.domain.State; -import info.jtrac.domain.User; -import info.jtrac.domain.UserSpaceRole; -import info.jtrac.util.UserUtils; -import java.util.List; -import org.apache.wicket.PageParameters; -import org.apache.wicket.behavior.HeaderContributor; -import org.apache.wicket.markup.html.IHeaderContributor; -import org.apache.wicket.markup.html.IHeaderResponse; -import org.apache.wicket.markup.html.WebMarkupContainer; -import org.apache.wicket.markup.html.form.Button; -import org.apache.wicket.markup.html.form.CheckBox; -import org.apache.wicket.markup.html.form.DropDownChoice; -import org.apache.wicket.markup.html.form.Form; -import org.apache.wicket.markup.html.form.IChoiceRenderer; -import org.apache.wicket.markup.html.form.ListMultipleChoice; -import org.apache.wicket.markup.html.form.TextArea; -import org.apache.wicket.markup.html.form.TextField; -import org.apache.wicket.markup.html.form.upload.FileUpload; -import org.apache.wicket.markup.html.form.upload.FileUploadField; -import org.apache.wicket.markup.html.link.Link; -import org.apache.wicket.markup.html.panel.FeedbackPanel; -import org.apache.wicket.model.BoundCompoundPropertyModel; -import org.apache.wicket.model.LoadableDetachableModel; -import org.apache.wicket.util.lang.Bytes; - -/** - * Create / Edit item form page - */ -public class ItemFormPage extends BasePage { - /** - * Default constructor - */ - public ItemFormPage() { - Item item = new Item(); - item.setSpace(getCurrentSpace()); - item.setStatus(State.NEW); - add(new ItemForm("form", item)); - } - - /** - * Constructor with provided item id. - * - * @param itemId The - */ - public ItemFormPage(long itemId) { - Item item = getJtrac().loadItem(itemId); - add(new ItemForm("form", item)); - } - - /** - * Wicket form - */ - private class ItemForm extends Form { - private JtracFeedbackMessageFilter filter; - private FileUploadField fileUploadField = new FileUploadField("file"); - private boolean editMode; - private int version; - - /** - * Constructor - * - * @param id - * @param item - */ - public ItemForm(String id, final Item item) { - super(id); - setMultiPart(true); - FeedbackPanel feedback = new FeedbackPanel("feedback"); - filter = new JtracFeedbackMessageFilter(); - feedback.setFilter(filter); - add(feedback); - version = item.getVersion(); - - if (item.getId() > 0) { - editMode = true; - } - - BoundCompoundPropertyModel model = null; - if (editMode) { - /* - * This ensures that the model object is re-loaded as part - * of the form submission workflow before form binding and - * avoids hibernate lazy loading issues during the whole - * update transaction. - */ - LoadableDetachableModel itemModel = new LoadableDetachableModel() { - protected Object load() { - logger.debug("attaching existing item " + item.getId()); - return getJtrac().loadItem(item.getId()); - } - }; - model = new BoundCompoundPropertyModel(itemModel); - } else { - model = new BoundCompoundPropertyModel(item); - } - setModel(model); - - /* - * =================================================== - * Summary - * =================================================== - */ - final TextField summaryField = new TextField("summary"); - summaryField.setRequired(true); - summaryField.add(new ErrorHighlighter()); - summaryField.setOutputMarkupId(true); - add(summaryField); - add(new HeaderContributor(new IHeaderContributor() { - public void renderHead(IHeaderResponse response) { - response.renderOnLoadJavascript("document.getElementById('" + - summaryField.getMarkupId() + "').focus()"); - } - })); - - /* - * =================================================== - * Delete button - * =================================================== - */ - Button delete = new Button("delete") { - @Override - public void onSubmit() { - String heading = localize("item_delete.confirm"); - String warning = localize("item_delete.line2"); - String line1 = localize("item_delete.line1"); - ConfirmPage confirm = new ConfirmPage(ItemFormPage.this, heading, warning, new String[] { line1 }) { - public void onConfirm() { - // avoid lazy init problem - getJtrac().removeItem(getJtrac().loadItem(item.getId())); - ItemSearch itemSearch = JtracSession.get().getItemSearch(); - if (itemSearch != null) { - setResponsePage(new ItemListPage(itemSearch)); - } else { - setResponsePage(DashboardPage.class); - } - } - }; - setResponsePage(confirm); - } - }; - - delete.setDefaultFormProcessing(false); - add(delete); - if (!editMode) { - delete.setVisible(false); - } - - /* - * =================================================== - * Detail - * =================================================== - */ - add(new TextArea("detail").setRequired(true).add(new ErrorHighlighter())); - - /* - * =================================================== - * Custom fields - * =================================================== - */ - if (editMode) { - add(new CustomFieldsFormPanel("fields", model, item.getSpace()).setRenderBodyOnly(true)); - } else { - add(new CustomFieldsFormPanel("fields", model, item, getPrincipal()).setRenderBodyOnly(true)); - } - - /* - * Hide some components if editing item - */ - WebMarkupContainer hideAssignedTo = new WebMarkupContainer("hideAssignedTo"); - WebMarkupContainer hideNotifyList = new WebMarkupContainer("hideNotifyList"); - WebMarkupContainer hideEditReason = new WebMarkupContainer("hideEditReason"); - add(hideAssignedTo); - add(hideNotifyList); - add(hideEditReason); - if (editMode) { - hideAssignedTo.setVisible(false); - hideNotifyList.setVisible(false); - hideEditReason.add(new TextArea("editReason").setRequired(true).add(new ErrorHighlighter())); - } else { - hideEditReason.setVisible(false); - - /* - * =================================================== - * Assigned to - * =================================================== - */ - Space space = item.getSpace(); - List<UserSpaceRole> userSpaceRoles = getJtrac().findUserRolesForSpace(space.getId()); - List<User> assignable = UserUtils.filterUsersAbleToTransitionFrom(userSpaceRoles, space, State.OPEN); - DropDownChoice choice = new DropDownChoice("assignedTo", assignable, new IChoiceRenderer() { - public Object getDisplayValue(Object o) { - return ((User) o).getName(); - } - - public String getIdValue(Object o, int i) { - return ((User) o).getId() + ""; - } - }); - - choice.setNullValid(true); - choice.setRequired(true); - WebMarkupContainer border = new WebMarkupContainer("border"); - border.add(choice); - border.add(new ErrorHighlighter(choice)); - hideAssignedTo.add(border); - - /* - * =================================================== - * Notify list - * =================================================== - */ - List<ItemUser> choices = UserUtils.convertToItemUserList(userSpaceRoles); - ListMultipleChoice itemUsers = new JtracCheckBoxMultipleChoice("itemUsers", choices, - new IChoiceRenderer() { - public Object getDisplayValue(Object o) { - return ((ItemUser) o).getUser().getName(); - } - - public String getIdValue(Object o, int i) { - return ((ItemUser) o).getUser().getId() + ""; - } - }, true); - hideNotifyList.add(itemUsers); - - /* - * =================================================== - * Attachment - * =================================================== - */ - hideNotifyList.add(fileUploadField); - setMaxSize(Bytes.megabytes(getJtrac().getAttachmentMaxSizeInMb())); - } - - /* - * =================================================== - * Send notifications - * =================================================== - */ - add(new CheckBox("sendNotifications")); - - /* - * =================================================== - * Cancel - * =================================================== - */ - add(new Link("cancel") { - public void onClick() { - setResponsePage(ItemViewPage.class, new PageParameters("0=" + item.getRefId())); - } - }.setVisible(editMode && JtracSession.get().getItemSearch() != null)); - } - - /* (non-Javadoc) - * @see org.apache.wicket.markup.html.form.Form#validate() - */ - @Override - protected void validate() { - filter.reset(); - Item item = (Item) getModelObject(); - if (editMode && item.getVersion() != version) { - /* - * User must have used back button after edit - */ - error(localize("item_form.error.version")); - } - super.validate(); - } - - /* (non-Javadoc) - * @see org.apache.wicket.markup.html.form.Form#onSubmit() - */ - @Override - protected void onSubmit() { - final FileUpload fileUpload = fileUploadField.getFileUpload(); - Item item = (Item) getModelObject(); - User user = getPrincipal(); - if (editMode) { - getJtrac().updateItem(item, user); - } else { - item.setLoggedBy(user); - item.setStatus(State.OPEN); - getJtrac().storeItem(item, fileUpload); - } - - /* - * On creating an item, clear any search filter (especially - * the related item) from session. - */ - JtracSession.get().setItemSearch(null); - setResponsePage(ItemViewPage.class, new PageParameters("0=" + item.getRefId())); - } - - } // end inner class ItemForm -} \ No newline at end of file +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac.wicket; + +import info.jtrac.domain.Item; +import info.jtrac.domain.ItemSearch; +import info.jtrac.domain.ItemUser; +import info.jtrac.domain.Space; +import info.jtrac.domain.State; +import info.jtrac.domain.User; +import info.jtrac.domain.UserSpaceRole; +import info.jtrac.util.UserUtils; + +import java.util.List; + +import org.apache.wicket.PageParameters; +import org.apache.wicket.behavior.HeaderContributor; +import org.apache.wicket.markup.html.IHeaderContributor; +import org.apache.wicket.markup.html.IHeaderResponse; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.form.Button; +import org.apache.wicket.markup.html.form.CheckBox; +import org.apache.wicket.markup.html.form.DropDownChoice; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.form.IChoiceRenderer; +import org.apache.wicket.markup.html.form.ListMultipleChoice; +import org.apache.wicket.markup.html.form.TextArea; +import org.apache.wicket.markup.html.form.TextField; +import org.apache.wicket.markup.html.form.upload.FileUpload; +import org.apache.wicket.markup.html.form.upload.FileUploadField; +import org.apache.wicket.markup.html.link.Link; +import org.apache.wicket.markup.html.panel.FeedbackPanel; +import org.apache.wicket.model.BoundCompoundPropertyModel; +import org.apache.wicket.model.LoadableDetachableModel; +import org.apache.wicket.util.lang.Bytes; + +/** + * Create / Edit item form page + */ +public class ItemFormPage extends BasePage { + /** + * Default constructor + */ + public ItemFormPage() { + Item item = new Item(); + item.setSpace(getCurrentSpace()); + item.setStatus(State.NEW); + add(new ItemForm("form", item)); + } + + /** + * Constructor with provided item id. + * + * @param itemId The + */ + public ItemFormPage(long itemId) { + Item item = getJtrac().loadItem(itemId); + add(new ItemForm("form", item)); + } + + /** + * Wicket form + */ + private class ItemForm extends Form { + private JtracFeedbackMessageFilter filter; + private FileUploadField fileUploadField = new FileUploadField("file"); + private boolean editMode; + private int version; + + /** + * Constructor + * + * @param id + * @param item + */ + public ItemForm(String id, final Item item) { + super(id); + setMultiPart(true); + FeedbackPanel feedback = new FeedbackPanel("feedback"); + filter = new JtracFeedbackMessageFilter(); + feedback.setFilter(filter); + add(feedback); + version = item.getVersion(); + + if (item.getId() > 0) { + editMode = true; + } + + BoundCompoundPropertyModel model = null; + if (editMode) { + /* + * This ensures that the model object is re-loaded as part + * of the form submission workflow before form binding and + * avoids hibernate lazy loading issues during the whole + * update transaction. + */ + LoadableDetachableModel itemModel = new LoadableDetachableModel() { + protected Object load() { + logger.debug("attaching existing item " + item.getId()); + return getJtrac().loadItem(item.getId()); + } + }; + model = new BoundCompoundPropertyModel(itemModel); + } else { + model = new BoundCompoundPropertyModel(item); + } + setModel(model); + + /* + * =================================================== + * Summary + * =================================================== + */ + final TextField summaryField = new TextField("summary"); + summaryField.setRequired(true); + summaryField.add(new ErrorHighlighter()); + summaryField.setOutputMarkupId(true); + add(summaryField); + add(new HeaderContributor(new IHeaderContributor() { + public void renderHead(IHeaderResponse response) { + response.renderOnLoadJavascript("document.getElementById('" + + summaryField.getMarkupId() + "').focus()"); + } + })); + + /* + * =================================================== + * Delete button + * =================================================== + */ + Button delete = new Button("delete") { + @Override + public void onSubmit() { + String heading = localize("item_delete.confirm"); + String warning = localize("item_delete.line2"); + String line1 = localize("item_delete.line1"); + ConfirmPage confirm = new ConfirmPage(ItemFormPage.this, heading, warning, new String[] { line1 }) { + public void onConfirm() { + // avoid lazy init problem + getJtrac().removeItem(getJtrac().loadItem(item.getId())); + ItemSearch itemSearch = JtracSession.get().getItemSearch(); + if (itemSearch != null) { + setResponsePage(new ItemListPage(itemSearch)); + } else { + setResponsePage(DashboardPage.class); + } + } + }; + setResponsePage(confirm); + } + }; + + delete.setDefaultFormProcessing(false); + add(delete); + if (!editMode) { + delete.setVisible(false); + } + + /* + * =================================================== + * Detail + * =================================================== + */ + add(new TextArea("detail").setRequired(true).add(new ErrorHighlighter())); + + /* + * =================================================== + * Custom fields + * =================================================== + */ + if (editMode) { + add(new CustomFieldsFormPanel("fields", model, item.getSpace()).setRenderBodyOnly(true)); + } else { + add(new CustomFieldsFormPanel("fields", model, item, getPrincipal()).setRenderBodyOnly(true)); + } + + /* + * Hide some components if editing item + */ + WebMarkupContainer hideAssignedTo = new WebMarkupContainer("hideAssignedTo"); + WebMarkupContainer hideNotifyList = new WebMarkupContainer("hideNotifyList"); + WebMarkupContainer hideEditReason = new WebMarkupContainer("hideEditReason"); + add(hideAssignedTo); + add(hideNotifyList); + add(hideEditReason); + if (editMode) { + hideAssignedTo.setVisible(false); + hideNotifyList.setVisible(false); + hideEditReason.add(new TextArea("editReason").setRequired(true).add(new ErrorHighlighter())); + } else { + hideEditReason.setVisible(false); + + /* + * =================================================== + * Assigned to + * =================================================== + */ + Space space = item.getSpace(); + List<UserSpaceRole> userSpaceRoles = getJtrac().findUserRolesForSpace(space.getId()); + List<User> assignable = UserUtils.filterUsersAbleToTransitionFrom(userSpaceRoles, space, State.OPEN); + DropDownChoice choice = new DropDownChoice("assignedTo", assignable, new IChoiceRenderer() { + public Object getDisplayValue(Object o) { + return ((User) o).getName(); + } + + public String getIdValue(Object o, int i) { + return ((User) o).getId() + ""; + } + }); + + choice.setNullValid(true); + choice.setRequired(true); + WebMarkupContainer border = new WebMarkupContainer("border"); + border.add(choice); + border.add(new ErrorHighlighter(choice)); + hideAssignedTo.add(border); + + /* + * =================================================== + * Notify list + * =================================================== + */ + List<ItemUser> choices = UserUtils.convertToItemUserList(userSpaceRoles); + ListMultipleChoice itemUsers = new JtracCheckBoxMultipleChoice("itemUsers", choices, + new IChoiceRenderer() { + public Object getDisplayValue(Object o) { + return ((ItemUser) o).getUser().getName(); + } + + public String getIdValue(Object o, int i) { + return ((ItemUser) o).getUser().getId() + ""; + } + }, true); + hideNotifyList.add(itemUsers); + + /* + * =================================================== + * Attachment + * =================================================== + */ + hideNotifyList.add(fileUploadField); + setMaxSize(Bytes.megabytes(getJtrac().getAttachmentMaxSizeInMb())); + } + + /* + * =================================================== + * Send notifications + * =================================================== + */ + add(new CheckBox("sendNotifications")); + + /* + * =================================================== + * Cancel + * =================================================== + */ + add(new Link("cancel") { + public void onClick() { + setResponsePage(ItemViewPage.class, new PageParameters("0=" + item.getRefId())); + } + }.setVisible(editMode && JtracSession.get().getItemSearch() != null)); + } + + /* (non-Javadoc) + * @see org.apache.wicket.markup.html.form.Form#onValidate() + */ + @Override + protected void onValidate() { + filter.reset(); + Item item = (Item) getModelObject(); + if (editMode && item.getVersion() != version) { + /* + * User must have used back button after edit + */ + error(localize("item_form.error.version")); + } + } + + /* (non-Javadoc) + * @see org.apache.wicket.markup.html.form.Form#onSubmit() + */ + @Override + protected void onSubmit() { + final FileUpload fileUpload = fileUploadField.getFileUpload(); + Item item = (Item) getModelObject(); + User user = getPrincipal(); + if (editMode) { + getJtrac().updateItem(item, user); + } else { + item.setLoggedBy(user); + item.setStatus(State.OPEN); + getJtrac().storeItem(item, fileUpload); + } + + /* + * On creating an item, clear any search filter (especially + * the related item) from session. + */ + JtracSession.get().setItemSearch(null); + setResponsePage(ItemViewPage.class, new PageParameters("0=" + item.getRefId())); + } + + } // end inner class ItemForm +} Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewFormPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewFormPanel.java 2021-10-05 16:45:41 UTC (rev 1397) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ItemViewFormPanel.java 2021-10-06 08:41:39 UTC (rev 1398) @@ -1,200 +1,198 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.wicket; - -import info.jtrac.domain.History; -import info.jtrac.domain.Item; -import info.jtrac.domain.ItemSearch; -import info.jtrac.domain.ItemUser; -import info.jtrac.domain.Space; -import info.jtrac.domain.State; -import info.jtrac.domain.User; -import info.jtrac.domain.UserSpaceRole; -import info.jtrac.util.UserUtils; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import org.apache.wicket.PageParameters; -import org.apache.wicket.ajax.AjaxRequestTarget; -import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; -import org.apache.wicket.markup.html.WebMarkupContainer; -import org.apache.wicket.markup.html.form.CheckBox; -import org.apache.wicket.markup.html.form.DropDownChoice; -import org.apache.wicket.markup.html.form.Form; -import org.apache.wicket.markup.html.form.FormComponent; -import org.apache.wicket.markup.html.form.IChoiceRenderer; -import org.apache.wicket.markup.html.form.ListMultipleChoice; -import org.apache.wicket.markup.html.form.TextArea; -import org.apache.wicket.markup.html.form.upload.FileUpload; -import org.apache.wicket.markup.html.form.upload.FileUploadField; -import org.apache.wicket.markup.html.form.validation.AbstractFormValidator; -import org.apache.wicket.markup.html.panel.FeedbackPanel; -import org.apache.wicket.model.BoundCompoundPropertyModel; -import org.apache.wicket.util.lang.Bytes; - -/** - * Form to update history for item - */ -public class ItemViewFormPanel extends BasePanel { - - private JtracFeedbackMessageFilter filter; - private ItemSearch itemSearch; - - public ItemViewFormPanel(String id, Item item, ItemSearch itemSearch) { - super(id); - this.itemSearch = itemSearch; - FeedbackPanel feedback = new FeedbackPanel("feedback"); - filter = new JtracFeedbackMessageFilter(); - feedback.setFilter(filter); - add(feedback); - add(new ItemViewForm("form", item)); - } - - /** - * wicket form - */ - private class ItemViewForm extends Form { - - private FileUploadField fileUploadField; - private long itemId; - private DropDownChoice assignedToChoice; - private DropDownChoice statusChoice; - - public ItemViewForm(String id, final Item item) { - super(id); - setMultiPart(true); - this.itemId = item.getId(); - final History history = new History(); - history.setItemUsers(item.getItemUsers()); - final BoundCompoundPropertyModel model = new BoundCompoundPropertyModel(history); - setModel(model); - add(new TextArea("comment").setRequired(true).add(new ErrorHighlighter())); - // custom fields =================================================== - User user = getPrincipal(); - add(new CustomFieldsFormPanel("fields", model, item, user)); - // ================================================================= - final Space space = item.getSpace(); - final List<UserSpaceRole> userSpaceRoles = getJtrac().findUserRolesForSpace(space.getId()); - // assigned to ===================================================== - final WebMarkupContainer border = new WebMarkupContainer("border"); - border.setOutputMarkupId(true); - final WebMarkupContainer hide = new WebMarkupContainer("hide"); - border.add(hide); - final List<User> emptyList = new ArrayList<User>(0); // will be populated over Ajax - assignedToChoice = new DropDownChoice("assignedTo", emptyList, new IChoiceRenderer() { - public Object getDisplayValue(Object o) { - return ((User) o).getName(); - } - public String getIdValue(Object o, int i) { - return ((User) o).getId() + ""; - } - }); - assignedToChoice.setOutputMarkupId(true); - assignedToChoice.setVisible(false); - assignedToChoice.setNullValid(true); - border.add(new ErrorHighlighter(assignedToChoice)); - border.add(assignedToChoice); - add(border); - // status ========================================================== - final Map<Integer, String> statesMap = item.getPermittedTransitions(user); - List<Integer> states = new ArrayList(statesMap.keySet()); - statusChoice = new IndicatingDropDownChoice("status", states, new IChoiceRenderer() { - public Object getDisplayValue(Object o) { - return statesMap.get(o); - } - public String getIdValue(Object o, int i) { - return o.toString(); - } - }); - statusChoice.setNullValid(true); - statusChoice.add(new ErrorHighlighter()); - statusChoice.add(new AjaxFormComponentUpdatingBehavior("onChange") { - protected void onUpdate(AjaxRequestTarget target) { - Integer selectedStatus = (Integer) getFormComponent().getConvertedInput(); - if (selectedStatus == null) { - assignedToChoice.setVisible(false); - hide.setVisible(true); - } else { - List<User> assignable = UserUtils.filterUsersAbleToTransitionFrom(userSpaceRoles, space, selectedStatus); - assignedToChoice.setChoices(assignable); - assignedToChoice.setVisible(true); - hide.setVisible(false); - } - target.addComponent(border); - } - }); - add(statusChoice); - // notify list ===================================================== - List<ItemUser> choices = UserUtils.convertToItemUserList(userSpaceRoles); - ListMultipleChoice itemUsers = new JtracCheckBoxMultipleChoice("itemUsers", choices, new IChoiceRenderer() { - public Object getDisplayValue(Object o) { - return ((ItemUser) o).getUser().getName(); - } - public String getIdValue(Object o, int i) { - return ((ItemUser) o).getUser().getId() + ""; - } - }, true); - add(itemUsers); - // attachment ====================================================== - fileUploadField = new FileUploadField("file"); - add(fileUploadField); - setMaxSize(Bytes.megabytes(getJtrac().getAttachmentMaxSizeInMb())); - // send notifications=============================================== - add(new CheckBox("sendNotifications")); - // validation that assignedTo is not null if status is not null and not CLOSED - // have to use FormValidator because this is conditional validation across two FormComponents - add(new AbstractFormValidator() { - public FormComponent[] getDependentFormComponents() { - // actually we depend on assignedToChoice also, but wicket logs a warning when the - // component is not visible but we are doing ajax. anyway we use assignedToChoice.getInput() - // not assignedToChoice.convertedInput() so no danger there - return new FormComponent[] {statusChoice}; - } - public void validate(Form unused) { - if(assignedToChoice.getInput() == null || assignedToChoice.getInput().trim().length() == 0) { - Integer i = (Integer) statusChoice.getConvertedInput(); - if (i != null && i != State.CLOSED) { - // user may have customized the name of the CLOSED State e.g. for i18n - // so when reporting the error, use the display name - String closedDisplayName = space.getMetadata().getStatusValue(State.CLOSED); - assignedToChoice.error(localize("item_view_form.assignedTo.error", closedDisplayName)); - } - } - } - }); - } - - @Override - protected void validate() { - filter.reset(); - super.validate(); - } - - @Override - protected void onSubmit() { - final FileUpload fileUpload = fileUploadField.getFileUpload(); - History history = (History) getModelObject(); - User user = JtracSession.get().getUser(); - history.setLoggedBy(user); - getJtrac().storeHistoryForItem(itemId, history, fileUpload); - setResponsePage(ItemViewPage.class, new PageParameters("0=" + history.getRefId())); - } - - } - - -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file... [truncated message content] |
From: <udi...@us...> - 2021-10-05 16:45:42
|
Revision: 1397 http://sourceforge.net/p/j-trac/code/1397 Author: udittmer Date: 2021-10-05 16:45:41 +0000 (Tue, 05 Oct 2021) Log Message: ----------- better {U+201C}last changed{U+201D} implementation Modified Paths: -------------- trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java Removed Paths: ------------- trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java.patch Modified: trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java 2021-09-24 13:38:25 UTC (rev 1396) +++ trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java 2021-10-05 16:45:41 UTC (rev 1397) @@ -89,8 +89,7 @@ STATUS("status"), ASSIGNED_TO("assignedTo"), TIME_STAMP("timeStamp"), - SPACE("space"), - LAST_CHANGED("lastChanged"); + SPACE("space"); private String text; @@ -157,7 +156,6 @@ list.add(new ColumnHeading(f)); } list.add(new ColumnHeading(TIME_STAMP)); - list.add(new ColumnHeading(LAST_CHANGED)); return list; } @@ -170,7 +168,6 @@ list.add(new ColumnHeading(LOGGED_BY)); list.add(new ColumnHeading(ASSIGNED_TO)); list.add(new ColumnHeading(TIME_STAMP)); - list.add(new ColumnHeading(LAST_CHANGED)); return list; } @@ -519,7 +516,6 @@ }; //============================================================== case TIME_STAMP: - case LAST_CHANGED: return new Processor() { List<Expression> getValidFilterExpressions() { return getAsList(BETWEEN, GT, LT); Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java 2021-09-24 13:38:25 UTC (rev 1396) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java 2021-10-05 16:45:41 UTC (rev 1397) @@ -226,10 +226,7 @@ final ColumnHeading ch = (ColumnHeading) listItem.getModelObject(); Link headingLink = new Link("heading") { public void onClick() { - // hack: it looks like a link, but clicking it does nothing - if (! ch.getNameText().equals("lastChanged")) { - doSort(ch.getNameText()); - } + doSort(ch.getNameText()); } }; listItem.add(headingLink); @@ -254,7 +251,7 @@ protected void populateItem(ListItem listItem) { // cast to AbstactItem - show history may be == true final AbstractItem item = (AbstractItem) listItem.getModelObject(); - + if (selectedItemId == item.getId()) { listItem.add(new SimpleAttributeModifier("class", "selected")); } else if(listItem.getIndex() % 2 == 1) { @@ -262,7 +259,7 @@ } final boolean showHistory = itemSearch.isShowHistory(); - + ListView fieldValues = new ListView("columns", columnHeadings) { protected void populateItem(ListItem listItem) { ColumnHeading ch = (ColumnHeading) listItem.getModelObject(); @@ -322,17 +319,12 @@ value = new PropertyModel(item, "assignedTo.name"); break; case TIME_STAMP: - value = new Model(DateUtils.formatTimeStamp(item.getTimeStamp())); - break; - case LAST_CHANGED: - if (item instanceof Item) { - History history = ((Item) item).getLatestHistory(); - if (history == null) - value = new Model(DateUtils.formatTimeStamp(item.getTimeStamp())); - else - value = new Model(DateUtils.formatTimeStamp(((Item) item).getLatestHistory().getTimeStamp())); + if (item instanceof History) { + value = new Model(DateUtils.formatTimeStamp(item.getTimeStamp())); } else { - value = new Model(DateUtils.formatTimeStamp(item.getTimeStamp())); + if (item instanceof Item) { + value = new Model(DateUtils.formatTimeStamp( ((Item)item).getLatestHistory().getTimeStamp())); + } } break; case SPACE: Deleted: trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java.patch =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java.patch 2021-09-24 13:38:25 UTC (rev 1396) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java.patch 2021-10-05 16:45:41 UTC (rev 1397) @@ -1,50 +0,0 @@ -Index: ItemListPanel.java -=================================================================== ---- ItemListPanel.java (revision 1351) -+++ ItemListPanel.java (working copy) -@@ -18,20 +18,20 @@ - - import info.jtrac.domain.AbstractItem; - import info.jtrac.domain.ColumnHeading; --import info.jtrac.domain.ColumnHeading.Name; - import info.jtrac.domain.History; -+import info.jtrac.domain.Item; - import info.jtrac.domain.ItemSearch; -+import info.jtrac.domain.ColumnHeading.Name; - import info.jtrac.util.DateUtils; - import info.jtrac.util.ExcelUtils; -- - import info.jtrac.util.ItemUtils; --import static info.jtrac.domain.ColumnHeading.Name.*; - - import java.io.IOException; - import java.io.OutputStreamWriter; - import java.util.ArrayList; - import java.util.List; - import java.util.Map; -+ - import org.apache.wicket.IRequestTarget; - import org.apache.wicket.PageParameters; - import org.apache.wicket.RequestCycle; -@@ -52,6 +52,7 @@ - /** - * item list panel - */ -+@SuppressWarnings("serial") - public class ItemListPanel extends BasePanel { - - private ItemSearch itemSearch; -@@ -318,7 +319,13 @@ - value = new PropertyModel(item, "assignedTo.name"); - break; - case TIME_STAMP: -+ if (item instanceof History) { - value = new Model(DateUtils.formatTimeStamp(item.getTimeStamp())); -+ } else { -+ if (item instanceof Item) { -+ value = new Model(DateUtils.formatTimeStamp( ((Item)item).getLatestHistory().getTimeStamp())); -+ } -+ } - break; - case SPACE: - if(showHistory) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-09-24 13:38:28
|
Revision: 1396 http://sourceforge.net/p/j-trac/code/1396 Author: udittmer Date: 2021-09-24 13:38:25 +0000 (Fri, 24 Sep 2021) Log Message: ----------- show additional field {U+201C}Last Changed{U+201D}, which displays the date of the last comment Modified Paths: -------------- trunk/jtrac/pom.xml trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java trunk/jtrac/src/main/java/info/jtrac/domain/Item.java trunk/jtrac/src/main/java/info/jtrac/domain/ItemSearch.java trunk/jtrac/src/main/java/info/jtrac/wicket/ItemListPanel.java trunk/jtrac/src/main/resources/messages_de.properties trunk/jtrac/src/main/resources/messages_en.properties Modified: trunk/jtrac/pom.xml =================================================================== --- trunk/jtrac/pom.xml 2021-09-23 08:50:14 UTC (rev 1395) +++ trunk/jtrac/pom.xml 2021-09-24 13:38:25 UTC (rev 1396) @@ -339,6 +339,7 @@ <artifactId>wicket</artifactId> <version>1.3.7</version> <!-- + <version>1.4.23</version> <version>1.5.16</version> --> <type>pom</type> @@ -348,6 +349,7 @@ <artifactId>wicket-extensions</artifactId> <version>1.3.7</version> <!-- + <version>1.4.23</version> <version>1.5.16</version> --> </dependency> Modified: trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java 2021-09-23 08:50:14 UTC (rev 1395) +++ trunk/jtrac/src/main/java/info/jtrac/domain/ColumnHeading.java 2021-09-24 13:38:25 UTC (rev 1396) @@ -1,843 +1,848 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.domain; - -import info.jtrac.Jtrac; -import info.jtrac.domain.FilterCriteria.Expression; -import info.jtrac.util.DateUtils; -import info.jtrac.wicket.JtracCheckBoxMultipleChoice; -import info.jtrac.wicket.yui.YuiCalendar; - -import static info.jtrac.domain.ColumnHeading.Name.*; -import static info.jtrac.domain.FilterCriteria.Expression.*; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.apache.wicket.MarkupContainer; -import org.apache.wicket.markup.html.WebMarkupContainer; -import org.apache.wicket.markup.html.form.IChoiceRenderer; -import org.apache.wicket.markup.html.form.TextField; -import org.apache.wicket.markup.html.panel.Fragment; -import org.apache.wicket.model.PropertyModel; -import org.hibernate.criterion.DetachedCriteria; -import org.hibernate.criterion.MatchMode; -import org.hibernate.criterion.Restrictions; - -/** - * used to render columns in the search results table - * and also in the search filter screen - */ -public class ColumnHeading implements Serializable { - - private static final Map<String, Name> NAMES_MAP; - - // set up a static Map to resolve a String to our ColumnHeading.Name enum value - static { - NAMES_MAP = new HashMap<String, Name>(); - for (Name n : Name.values()) { - NAMES_MAP.put(n.text, n); - } - } - - /** - * Resolve a String to a valid enum value for ColumnHeading.Name - */ - private static Name convertToName(String text) { - Name n = NAMES_MAP.get(text); - if (n == null) { - throw new RuntimeException("Bad name " + text); - } - return n; - } - - /** - * test if a given string is a valid column heading name - */ - public static boolean isValidName(String text) { - return NAMES_MAP.containsKey(text); - } - - public static boolean isValidFieldOrColumnName(String text) { - return isValidName(text) || Field.isValidName(text); - } - - public enum Name { - - ID("id"), - SUMMARY("summary"), - DETAIL("detail"), - LOGGED_BY("loggedBy"), - STATUS("status"), - ASSIGNED_TO("assignedTo"), - TIME_STAMP("timeStamp"), - SPACE("space"); - - private String text; - - Name(String text) { - this.text = text; - } - - public String getText() { - return text; - } - - @Override - public String toString() { - return text; - } - - } - - private Field field; - private Name name; - private String label; - private boolean visible = true; - private Processor processor; - - private FilterCriteria filterCriteria = new FilterCriteria(); - - public ColumnHeading(Name name) { - this.name = name; - if(name == DETAIL || name == SPACE) { - visible = false; - } - processor = getProcessor(); - } - - public ColumnHeading(Field field) { - this.field = field; - this.label = field.getLabel(); - processor = getProcessor(); - } - - public boolean isField() { - return field != null; - } - - public boolean isDropDownType() { - if(isField()) { - return field.isDropDownType(); - } else { - return name == LOGGED_BY - || name == ASSIGNED_TO - || name == STATUS; - } - } - - public static List<ColumnHeading> getColumnHeadings(Space s) { - List<ColumnHeading> list = new ArrayList<ColumnHeading>(); - list.add(new ColumnHeading(ID)); - list.add(new ColumnHeading(SUMMARY)); - list.add(new ColumnHeading(DETAIL)); - list.add(new ColumnHeading(STATUS)); - list.add(new ColumnHeading(LOGGED_BY)); - list.add(new ColumnHeading(ASSIGNED_TO)); - for(Field f : s.getMetadata().getFieldList()) { - list.add(new ColumnHeading(f)); - } - list.add(new ColumnHeading(TIME_STAMP)); - return list; - } - - public static List<ColumnHeading> getColumnHeadings() { - List<ColumnHeading> list = new ArrayList<ColumnHeading>(); - list.add(new ColumnHeading(ID)); - list.add(new ColumnHeading(SPACE)); - list.add(new ColumnHeading(SUMMARY)); - list.add(new ColumnHeading(DETAIL)); - list.add(new ColumnHeading(LOGGED_BY)); - list.add(new ColumnHeading(ASSIGNED_TO)); - list.add(new ColumnHeading(TIME_STAMP)); - return list; - } - - public List<Expression> getValidFilterExpressions() { - return processor.getValidFilterExpressions(); - } - - public Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { - return processor.getFilterUiFragment(container, user, space, jtrac); - } - - public void addRestrictions(DetachedCriteria criteria) { - processor.addRestrictions(criteria); - } - - public String getAsQueryString() { - return processor.getAsQueryString(); - } - - public void loadFromQueryString(String s, User user, Jtrac jtrac) { - processor.loadFromQueryString(s, user, jtrac); - } - - /** - * also see description below for the private getProcessor() method - */ - private abstract class Processor implements Serializable { - - /* return the possible expressions (equals, greater-than etc) to show on filter UI for selection */ - abstract List<Expression> getValidFilterExpressions(); - - /* return the wicket ui fragment that will be shown over ajax (based on selected expression) */ - abstract Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac); - - /* get as hibernate restriction and append to passed in criteria that will be used to query the database */ - abstract void addRestrictions(DetachedCriteria criteria); - - /* return a querystring representation of the filter criteria to create a bookmarkable url */ - abstract String getAsQueryString(); - - /* load a querystring representation and initialize filter critera when acting on a bookmarkable url */ - abstract void loadFromQueryString(String s, User user, Jtrac jtrac); - - } - - /** - * this routine is a massive if-then construct that acts as a factory for the - * right implementation of the responsibilities defined in the "Processor" class (above) - * based on the type of ColumnHeading - the right implementation will be returned. - * having everything in one place below, makes it easy to maintain, as the - * logic of each of the methods are closely interdependent for a given column type - * for e.g. the kind of hibernate criteria needed depends on what is made available on the UI - */ - private Processor getProcessor() { - if(isField()) { - switch(field.getName().getType()) { - //============================================================== - case 1: - case 2: - case 3: - return new Processor() { - List<Expression> getValidFilterExpressions() { - return getAsList(IN); - } - Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { - Fragment fragment = new Fragment("fragParent", "multiSelect", container); - final Map<String, String> options = field.getOptions(); - JtracCheckBoxMultipleChoice choice = new JtracCheckBoxMultipleChoice("values", new ArrayList(options.keySet()), new IChoiceRenderer() { - public Object getDisplayValue(Object o) { - return options.get(o); - } - public String getIdValue(Object o, int i) { - return o.toString(); - } - }); - fragment.add(choice); - choice.setModel(new PropertyModel(filterCriteria, "values")); - return fragment; - } - void addRestrictions(DetachedCriteria criteria) { - if(filterHasValueList()) { - List values = filterCriteria.getValues(); - List<Integer> keys = new ArrayList<Integer>(values.size()); - for(Object o : values) { - keys.add(new Integer(o.toString())); - } - criteria.add(Restrictions.in(getNameText(), keys)); - } - } - String getAsQueryString() { - return getQueryStringFromValueList(); - } - void loadFromQueryString(String s, User user, Jtrac jtrac) { - setValueListFromQueryString(s); - } - }; - //============================================================== - case 4: // decimal number - return new Processor() { - List<Expression> getValidFilterExpressions() { - return getAsList(EQ, NOT_EQ, GT, LT, BETWEEN); - } - Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { - Fragment fragment = new Fragment("fragParent", "textField", container); - TextField textField = new TextField("value", Double.class); - textField.setModel(new PropertyModel(filterCriteria, "value")); - fragment.add(textField); - if(filterCriteria.getExpression() == BETWEEN) { - TextField textField2 = new TextField("value2", Double.class); - textField.setModel(new PropertyModel(filterCriteria, "value2")); - fragment.add(textField2); - } else { - fragment.add(new WebMarkupContainer("value2").setVisible(false)); - } - return fragment; - } - void addRestrictions(DetachedCriteria criteria) { - if(filterHasValue()) { - Object value = filterCriteria.getValue(); - switch(filterCriteria.getExpression()) { - case EQ: criteria.add(Restrictions.eq(getNameText(), value)); break; - case NOT_EQ: criteria.add(Restrictions.not(Restrictions.eq(name.text, value))); break; - case GT: criteria.add(Restrictions.gt(getNameText(), value)); break; - case LT: criteria.add(Restrictions.lt(getNameText(), value)); break; - case BETWEEN: - criteria.add(Restrictions.gt(getNameText(), value)); - criteria.add(Restrictions.lt(getNameText(), filterCriteria.getValue2())); - break; - default: - } - } - } - String getAsQueryString() { - return getQueryStringFromValue(Double.class); - } - void loadFromQueryString(String s, User user, Jtrac jtrac) { - setValueFromQueryString(s, Double.class); - } - - }; - //============================================================== - case 6: // date - return new Processor() { - List<Expression> getValidFilterExpressions() { - return getAsList(EQ, NOT_EQ, GT, LT, BETWEEN); - } - Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { - Fragment fragment = new Fragment("fragParent", "dateField", container); - YuiCalendar calendar = new YuiCalendar("value", new PropertyModel(filterCriteria, "value"), false); - fragment.add(calendar); - if(filterCriteria.getExpression() == BETWEEN) { - YuiCalendar calendar2 = new YuiCalendar("value2", new PropertyModel(filterCriteria, "value2"), false); - fragment.add(calendar2); - } else { - fragment.add(new WebMarkupContainer("value2").setVisible(false)); - } - return fragment; - } - void addRestrictions(DetachedCriteria criteria) { - if(filterHasValue()) { - Object value = filterCriteria.getValue(); - switch(filterCriteria.getExpression()) { - case EQ: criteria.add(Restrictions.eq(getNameText(), value)); break; - case NOT_EQ: criteria.add(Restrictions.not(Restrictions.eq(getNameText(), value))); break; - case GT: criteria.add(Restrictions.gt(getNameText(), value)); break; - case LT: criteria.add(Restrictions.lt(getNameText(), value)); break; - case BETWEEN: - criteria.add(Restrictions.gt(getNameText(), value)); - criteria.add(Restrictions.lt(getNameText(), filterCriteria.getValue2())); - break; - default: - } - } - } - String getAsQueryString() { - return getQueryStringFromValue(Date.class); - } - void loadFromQueryString(String s, User user, Jtrac jtrac) { - setValueFromQueryString(s, Date.class); - } - }; - //============================================================== - case 5: // free text - return new Processor() { - List<Expression> getValidFilterExpressions() { - return getAsList(CONTAINS); - } - Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { - return getTextFieldFragment(container); - } - void addRestrictions(DetachedCriteria criteria) { - if(filterHasValue()) { - criteria.add(Restrictions.ilike(getNameText(), - (String) filterCriteria.getValue(), MatchMode.ANYWHERE)); - } - } - String getAsQueryString() { - return getQueryStringFromValue(String.class); - } - void loadFromQueryString(String s, User user, Jtrac jtrac) { - setValueFromQueryString(s, String.class); - } - }; - //============================================================== - default: - throw new RuntimeException("Unknown Column Heading " + name); - } - } else { // this is not a custom field but one of the "built-in" columns - switch(name) { - //============================================================== - case ID: - return new Processor() { - List<Expression> getValidFilterExpressions() { - return getAsList(EQ); - } - Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { - return getTextFieldFragment(container); - } - void addRestrictions(DetachedCriteria criteria) { - if(filterHasValue()) { - // should never come here for criteria: see ItemSearch#getRefId() - throw new RuntimeException("should not come here for 'id'"); - } - } - String getAsQueryString() { - return getQueryStringFromValue(String.class); - } - void loadFromQueryString(String s, User user, Jtrac jtrac) { - setValueFromQueryString(s, String.class); - } - }; - //============================================================== - case SUMMARY: - return new Processor() { - List<Expression> getValidFilterExpressions() { - return getAsList(CONTAINS); - } - Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { - return getTextFieldFragment(container); - } - void addRestrictions(DetachedCriteria criteria) { - if(filterHasValue()) { - criteria.add(Restrictions.ilike(getNameText(), (String) filterCriteria.getValue(), MatchMode.ANYWHERE)); - } - } - String getAsQueryString() { - return getQueryStringFromValue(String.class); - } - void loadFromQueryString(String s, User user, Jtrac jtrac) { - setValueFromQueryString(s, String.class); - } - }; - //============================================================== - case DETAIL: - return new Processor() { - List<Expression> getValidFilterExpressions() { - return getAsList(CONTAINS); - } - Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { - return getTextFieldFragment(container); - } - void addRestrictions(DetachedCriteria criteria) { - // do nothing, 'detail' already processed, see: ItemSearch#getSearchText() - } - String getAsQueryString() { - return getQueryStringFromValue(String.class); - } - void loadFromQueryString(String s, User user, Jtrac jtrac) { - setValueFromQueryString(s, String.class); - } - }; - - //============================================================== - case STATUS: - return new Processor() { - List<Expression> getValidFilterExpressions() { - return getAsList(IN); - } - Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { - Fragment fragment = new Fragment("fragParent", "multiSelect", container); - // status selectable only when context space is not null - final Map<Integer, String> options = space.getMetadata().getStatesMap(); - options.remove(State.NEW); - JtracCheckBoxMultipleChoice choice = new JtracCheckBoxMultipleChoice("values", new ArrayList(options.keySet()), new IChoiceRenderer() { - public Object getDisplayValue(Object o) { - return options.get(o); - } - public String getIdValue(Object o, int i) { - return o.toString(); - } - }); - fragment.add(choice); - choice.setModel(new PropertyModel(filterCriteria, "values")); - return fragment; - } - void addRestrictions(DetachedCriteria criteria) { - if(filterHasValueList()) { - criteria.add(Restrictions.in(getNameText(), filterCriteria.getValues())); - } - } - String getAsQueryString() { - return getQueryStringFromValueList(); - } - void loadFromQueryString(String s, User user, Jtrac jtrac) { - setStatusListFromQueryString(s); - } - }; - //============================================================== - case ASSIGNED_TO: - case LOGGED_BY: - return new Processor() { - List<Expression> getValidFilterExpressions() { - return getAsList(IN); - } - Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { - Fragment fragment = new Fragment("fragParent", "multiSelect", container); - List<User> users = null; - if(space == null) { - users = jtrac.findUsersForUser(user); - } else { - users = jtrac.findUsersForSpace(space.getId()); - } - JtracCheckBoxMultipleChoice choice = new JtracCheckBoxMultipleChoice("values", users, new IChoiceRenderer() { - public Object getDisplayValue(Object o) { - return ((User) o).getName(); - } - public String getIdValue(Object o, int i) { - return ((User) o).getId() + ""; - } - }); - fragment.add(choice); - choice.setModel(new PropertyModel(filterCriteria, "values")); - return fragment; - } - void addRestrictions(DetachedCriteria criteria) { - if(filterHasValueList()) { - criteria.add(Restrictions.in(getNameText(), filterCriteria.getValues())); - } - } - String getAsQueryString() { - return getQueryStringFromUserList(); - } - void loadFromQueryString(String s, User user, Jtrac jtrac) { - setUserListFromQueryString(s, jtrac); - } - }; - //============================================================== - case TIME_STAMP: - return new Processor() { - List<Expression> getValidFilterExpressions() { - return getAsList(BETWEEN, GT, LT); - } - Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { - Fragment fragment = new Fragment("fragParent", "dateField", container); - YuiCalendar calendar = new YuiCalendar("value", new PropertyModel(filterCriteria, "value"), false); - fragment.add(calendar); - if(filterCriteria.getExpression() == BETWEEN) { - YuiCalendar calendar2 = new YuiCalendar("value2", new PropertyModel(filterCriteria, "value2"), false); - fragment.add(calendar2); - } else { - fragment.add(new WebMarkupContainer("value2").setVisible(false)); - } - return fragment; - } - void addRestrictions(DetachedCriteria criteria) { - if(filterHasValue()) { - Object value = filterCriteria.getValue(); - switch(filterCriteria.getExpression()) { - case GT: criteria.add(Restrictions.gt(getNameText(), value)); break; - case LT: criteria.add(Restrictions.lt(getNameText(), value)); break; - case BETWEEN: - criteria.add(Restrictions.gt(getNameText(), value)); - criteria.add(Restrictions.lt(getNameText(), filterCriteria.getValue2())); - break; - default: - } - } - } - String getAsQueryString() { - return getQueryStringFromValue(Date.class); - } - void loadFromQueryString(String s, User user, Jtrac jtrac) { - setValueFromQueryString(s, Date.class); - } - }; - //============================================================== - case SPACE: - return new Processor() { - List<Expression> getValidFilterExpressions() { - return getAsList(IN); - } - Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { - Fragment fragment = new Fragment("fragParent", "multiSelect", container); - List<Space> spaces = new ArrayList(user.getSpaces()); - JtracCheckBoxMultipleChoice choice = new JtracCheckBoxMultipleChoice("values", spaces, new IChoiceRenderer() { - public Object getDisplayValue(Object o) { - return ((Space) o).getName(); - } - public String getIdValue(Object o, int i) { - return ((Space) o).getId() + ""; - } - }); - fragment.add(choice); - choice.setModel(new PropertyModel(filterCriteria, "values")); - return fragment; - } - void addRestrictions(DetachedCriteria criteria) { - // already handled space as special case, see ItemSearch#getSelectedSpaces() - } - String getAsQueryString() { - return getQueryStringFromSpaceList(); - } - void loadFromQueryString(String s, User user, Jtrac jtrac) { - setSpaceListFromQueryString(s, user, jtrac); - } - }; - //============================================================== - default: - throw new RuntimeException("Unknown Column Heading " + name); - } - } - } - - private List<Expression> getAsList(Expression... expressions) { - List<Expression> list = new ArrayList<Expression>(); - for(Expression e : expressions) { - list.add(e); - } - return list; - } - - private Fragment getTextFieldFragment(MarkupContainer container) { - Fragment fragment = new Fragment("fragParent", "textField", container); - TextField textField = new TextField("value", String.class); - textField.setModel(new PropertyModel(filterCriteria, "value")); - fragment.add(textField); - fragment.add(new WebMarkupContainer("value2").setVisible(false)); - return fragment; - } - - private boolean filterHasValueList() { - if(filterCriteria.getExpression() != null - && filterCriteria.getValues() != null - && filterCriteria.getValues().size() > 0) { - return true; - } - return false; - } - - private boolean filterHasValue() { - Object value = filterCriteria.getValue(); - if(filterCriteria.getExpression() != null && value != null && value.toString().trim().length() > 0) { - return true; - } - return false; - } - - private String prependExpression(String s) { - return filterCriteria.getExpression().getKey() + "_" + s; - } - - private String getQueryStringFromValueList() { - if(!filterHasValueList()) { - return null; - } - String temp = ""; - for(Object o : filterCriteria.getValues()) { - if(temp.length() > 0) { - temp = temp + "_"; - } - temp = temp + o; - } - return prependExpression(temp); - } - - private String getQueryStringFromValue(Class clazz) { - if(!filterHasValue()) { - return null; - } - String temp = ""; - if(clazz.equals(Date.class)) { - temp = DateUtils.format((Date) filterCriteria.getValue()); - if(filterCriteria.getValue2() != null) { - temp = temp + "_" + DateUtils.format((Date) filterCriteria.getValue2()); - } - } else { - temp = filterCriteria.getValue() + ""; - if(filterCriteria.getValue2() != null) { - temp = temp + "_" + filterCriteria.getValue2(); - } - } - return prependExpression(temp); - } - - // TODO refactor code duplication - private String getQueryStringFromUserList() { - if(!filterHasValueList()) { - return null; - } - String temp = ""; - for(User u : (List<User>) filterCriteria.getValues()) { - if(temp.length() > 0) { - temp = temp + "_"; - } - temp = temp + u.getId(); - } - return prependExpression(temp); - } - - // TODO refactor code duplication - private String getQueryStringFromSpaceList() { - if(!filterHasValueList()) { - return null; - } - String temp = ""; - for(Space s : (List<Space>) filterCriteria.getValues()) { - if(temp.length() > 0) { - temp = temp + "_"; - } - temp = temp + s.getId(); - } - return prependExpression(temp); - } - - private List<String> setExpressionAndGetRemainingTokens(String s) { - String [] tokens = s.split("_"); - filterCriteria.setExpression(FilterCriteria.convertToExpression(tokens[0])); - List<String> remainingTokens = new ArrayList<String>(); - // ignore first token, this has been parsed as Expression above - for(int i = 1; i < tokens.length; i++ ) { - remainingTokens.add(tokens[i]); - } - return remainingTokens; - } - - private void setValueListFromQueryString(String raw) { - filterCriteria.setValues(setExpressionAndGetRemainingTokens(raw)); - } - - // TODO refactor with more methods in filtercriteria - private void setValueFromQueryString(String raw, Class clazz) { - List<String> tokens = setExpressionAndGetRemainingTokens(raw); - String v1 = tokens.get(0); - String v2 = tokens.size() > 1 ? tokens.get(1) : null; - if(clazz.equals(Double.class)) { - filterCriteria.setValue(new Double(v1)); - if(v2 != null) { - filterCriteria.setValue2(new Double(v2)); - } - } else if(clazz.equals(Date.class)) { - filterCriteria.setValue(DateUtils.convert(v1)); - if(v2 != null) { - filterCriteria.setValue2(DateUtils.convert(v2)); - } - } else { // String - filterCriteria.setValue(v1); - if(v2 != null) { - filterCriteria.setValue2(v2); - } - } - - } - - private void setUserListFromQueryString(String raw, Jtrac jtrac) { - List<String> tokens = setExpressionAndGetRemainingTokens(raw); - List<User> users = jtrac.findUsersWhereIdIn(getAsListOfLong(tokens)); - filterCriteria.setValues(users); - } - - private void setSpaceListFromQueryString(String raw, User user, Jtrac jtrac) { - List<String> tokens = setExpressionAndGetRemainingTokens(raw); - List<Space> temp = jtrac.findSpacesWhereIdIn(getAsListOfLong(tokens)); - // for security, prevent URL spoofing to show spaces not allocated to user - List<Space> spaces = new ArrayList<Space>(); - for(Space s : temp) { - if(user.isAllocatedToSpace(s.getId())) { - spaces.add(s); - } - } - filterCriteria.setValues(spaces); - } - - private void setStatusListFromQueryString(String raw) { - List<String> tokens = setExpressionAndGetRemainingTokens(raw); - List<Integer> statuses = new ArrayList<Integer>(); - for(String s : tokens) { - statuses.add(new Integer(s)); - } - filterCriteria.setValues(statuses); - } - - private List<Long> getAsListOfLong(List<String> tokens) { - List<Long> ids = new ArrayList<Long>(); - for(String s : tokens) { - ids.add(new Long(s)); - } - return ids; - } - - /* custom accessor */ - public void setName(String nameAsString) { - name = convertToName(nameAsString); - } - - public String getNameText() { - if(isField()) { - return field.getName().getText(); - } - return name.text; - } - - //========================================================================== - - public Name getName() { - return name; - } - - public Field getField() { - return field; - } - - public String getLabel() { - return label; - } - - public FilterCriteria getFilterCriteria() { - return filterCriteria; - } - - public void setFilterCriteria(FilterCriteria filterCriteria) { - this.filterCriteria = filterCriteria; - } - - public boolean isVisible() { - return visible; - } - - public void setVisible(boolean visible) { - this.visible = visible; - } - - @Override - public int hashCode() { - if(isField()) { - return field.hashCode(); - } - return name.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof ColumnHeading)) { - return false; - } - final ColumnHeading ch = (ColumnHeading) o; - if(ch.isField()) { - return ch.field.equals(field); - } - return ch.name.equals(name); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("name [").append(name); - sb.append("]; filterCriteria [").append(filterCriteria); - sb.append("]"); - return sb.toString(); - } - -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac.domain; + +import info.jtrac.Jtrac; +import info.jtrac.domain.FilterCriteria.Expression; +import info.jtrac.util.DateUtils; +import info.jtrac.wicket.JtracCheckBoxMultipleChoice; +import info.jtrac.wicket.yui.YuiCalendar; + +import static info.jtrac.domain.ColumnHeading.Name.*; +import static info.jtrac.domain.FilterCriteria.Expression.*; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.wicket.MarkupContainer; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.form.IChoiceRenderer; +import org.apache.wicket.markup.html.form.TextField; +import org.apache.wicket.markup.html.panel.Fragment; +import org.apache.wicket.model.PropertyModel; +import org.hibernate.criterion.DetachedCriteria; +import org.hibernate.criterion.MatchMode; +import org.hibernate.criterion.Restrictions; + +/** + * used to render columns in the search results table + * and also in the search filter screen + */ +public class ColumnHeading implements Serializable { + + private static final Map<String, Name> NAMES_MAP; + + // set up a static Map to resolve a String to our ColumnHeading.Name enum value + static { + NAMES_MAP = new HashMap<String, Name>(); + for (Name n : Name.values()) { + NAMES_MAP.put(n.text, n); + } + } + + /** + * Resolve a String to a valid enum value for ColumnHeading.Name + */ + private static Name convertToName(String text) { + Name n = NAMES_MAP.get(text); + if (n == null) { + throw new RuntimeException("Bad name " + text); + } + return n; + } + + /** + * test if a given string is a valid column heading name + */ + public static boolean isValidName(String text) { + return NAMES_MAP.containsKey(text); + } + + public static boolean isValidFieldOrColumnName(String text) { + return isValidName(text) || Field.isValidName(text); + } + + public enum Name { + + ID("id"), + SUMMARY("summary"), + DETAIL("detail"), + LOGGED_BY("loggedBy"), + STATUS("status"), + ASSIGNED_TO("assignedTo"), + TIME_STAMP("timeStamp"), + SPACE("space"), + LAST_CHANGED("lastChanged"); + + private String text; + + Name(String text) { + this.text = text; + } + + public String getText() { + return text; + } + + @Override + public String toString() { + return text; + } + + } + + private Field field; + private Name name; + private String label; + private boolean visible = true; + private Processor processor; + + private FilterCriteria filterCriteria = new FilterCriteria(); + + public ColumnHeading(Name name) { + this.name = name; + if(name == DETAIL || name == SPACE) { + visible = false; + } + processor = getProcessor(); + } + + public ColumnHeading(Field field) { + this.field = field; + this.label = field.getLabel(); + processor = getProcessor(); + } + + public boolean isField() { + return field != null; + } + + public boolean isDropDownType() { + if(isField()) { + return field.isDropDownType(); + } else { + return name == LOGGED_BY + || name == ASSIGNED_TO + || name == STATUS; + } + } + + public static List<ColumnHeading> getColumnHeadings(Space s) { + List<ColumnHeading> list = new ArrayList<ColumnHeading>(); + list.add(new ColumnHeading(ID)); + list.add(new ColumnHeading(SUMMARY)); + list.add(new ColumnHeading(DETAIL)); + list.add(new ColumnHeading(STATUS)); + list.add(new ColumnHeading(LOGGED_BY)); + list.add(new ColumnHeading(ASSIGNED_TO)); + for(Field f : s.getMetadata().getFieldList()) { + list.add(new ColumnHeading(f)); + } + list.add(new ColumnHeading(TIME_STAMP)); + list.add(new ColumnHeading(LAST_CHANGED)); + return list; + } + + public static List<ColumnHeading> getColumnHeadings() { + List<ColumnHeading> list = new ArrayList<ColumnHeading>(); + list.add(new ColumnHeading(ID)); + list.add(new ColumnHeading(SPACE)); + list.add(new ColumnHeading(SUMMARY)); + list.add(new ColumnHeading(DETAIL)); + list.add(new ColumnHeading(LOGGED_BY)); + list.add(new ColumnHeading(ASSIGNED_TO)); + list.add(new ColumnHeading(TIME_STAMP)); + list.add(new ColumnHeading(LAST_CHANGED)); + return list; + } + + public List<Expression> getValidFilterExpressions() { + return processor.getValidFilterExpressions(); + } + + public Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { + return processor.getFilterUiFragment(container, user, space, jtrac); + } + + public void addRestrictions(DetachedCriteria criteria) { + processor.addRestrictions(criteria); + } + + public String getAsQueryString() { + return processor.getAsQueryString(); + } + + public void loadFromQueryString(String s, User user, Jtrac jtrac) { + processor.loadFromQueryString(s, user, jtrac); + } + + /** + * also see description below for the private getProcessor() method + */ + private abstract class Processor implements Serializable { + + /* return the possible expressions (equals, greater-than etc) to show on filter UI for selection */ + abstract List<Expression> getValidFilterExpressions(); + + /* return the wicket ui fragment that will be shown over ajax (based on selected expression) */ + abstract Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac); + + /* get as hibernate restriction and append to passed in criteria that will be used to query the database */ + abstract void addRestrictions(DetachedCriteria criteria); + + /* return a querystring representation of the filter criteria to create a bookmarkable url */ + abstract String getAsQueryString(); + + /* load a querystring representation and initialize filter critera when acting on a bookmarkable url */ + abstract void loadFromQueryString(String s, User user, Jtrac jtrac); + + } + + /** + * this routine is a massive if-then construct that acts as a factory for the + * right implementation of the responsibilities defined in the "Processor" class (above) + * based on the type of ColumnHeading - the right implementation will be returned. + * having everything in one place below, makes it easy to maintain, as the + * logic of each of the methods are closely interdependent for a given column type + * for e.g. the kind of hibernate criteria needed depends on what is made available on the UI + */ + private Processor getProcessor() { + if(isField()) { + switch(field.getName().getType()) { + //============================================================== + case 1: + case 2: + case 3: + return new Processor() { + List<Expression> getValidFilterExpressions() { + return getAsList(IN); + } + Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { + Fragment fragment = new Fragment("fragParent", "multiSelect", container); + final Map<String, String> options = field.getOptions(); + JtracCheckBoxMultipleChoice choice = new JtracCheckBoxMultipleChoice("values", new ArrayList(options.keySet()), new IChoiceRenderer() { + public Object getDisplayValue(Object o) { + return options.get(o); + } + public String getIdValue(Object o, int i) { + return o.toString(); + } + }); + fragment.add(choice); + choice.setModel(new PropertyModel(filterCriteria, "values")); + return fragment; + } + void addRestrictions(DetachedCriteria criteria) { + if(filterHasValueList()) { + List values = filterCriteria.getValues(); + List<Integer> keys = new ArrayList<Integer>(values.size()); + for(Object o : values) { + keys.add(new Integer(o.toString())); + } + criteria.add(Restrictions.in(getNameText(), keys)); + } + } + String getAsQueryString() { + return getQueryStringFromValueList(); + } + void loadFromQueryString(String s, User user, Jtrac jtrac) { + setValueListFromQueryString(s); + } + }; + //============================================================== + case 4: // decimal number + return new Processor() { + List<Expression> getValidFilterExpressions() { + return getAsList(EQ, NOT_EQ, GT, LT, BETWEEN); + } + Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { + Fragment fragment = new Fragment("fragParent", "textField", container); + TextField textField = new TextField("value", Double.class); + textField.setModel(new PropertyModel(filterCriteria, "value")); + fragment.add(textField); + if(filterCriteria.getExpression() == BETWEEN) { + TextField textField2 = new TextField("value2", Double.class); + textField.setModel(new PropertyModel(filterCriteria, "value2")); + fragment.add(textField2); + } else { + fragment.add(new WebMarkupContainer("value2").setVisible(false)); + } + return fragment; + } + void addRestrictions(DetachedCriteria criteria) { + if(filterHasValue()) { + Object value = filterCriteria.getValue(); + switch(filterCriteria.getExpression()) { + case EQ: criteria.add(Restrictions.eq(getNameText(), value)); break; + case NOT_EQ: criteria.add(Restrictions.not(Restrictions.eq(name.text, value))); break; + case GT: criteria.add(Restrictions.gt(getNameText(), value)); break; + case LT: criteria.add(Restrictions.lt(getNameText(), value)); break; + case BETWEEN: + criteria.add(Restrictions.gt(getNameText(), value)); + criteria.add(Restrictions.lt(getNameText(), filterCriteria.getValue2())); + break; + default: + } + } + } + String getAsQueryString() { + return getQueryStringFromValue(Double.class); + } + void loadFromQueryString(String s, User user, Jtrac jtrac) { + setValueFromQueryString(s, Double.class); + } + + }; + //============================================================== + case 6: // date + return new Processor() { + List<Expression> getValidFilterExpressions() { + return getAsList(EQ, NOT_EQ, GT, LT, BETWEEN); + } + Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { + Fragment fragment = new Fragment("fragParent", "dateField", container); + YuiCalendar calendar = new YuiCalendar("value", new PropertyModel(filterCriteria, "value"), false); + fragment.add(calendar); + if(filterCriteria.getExpression() == BETWEEN) { + YuiCalendar calendar2 = new YuiCalendar("value2", new PropertyModel(filterCriteria, "value2"), false); + fragment.add(calendar2); + } else { + fragment.add(new WebMarkupContainer("value2").setVisible(false)); + } + return fragment; + } + void addRestrictions(DetachedCriteria criteria) { + if(filterHasValue()) { + Object value = filterCriteria.getValue(); + switch(filterCriteria.getExpression()) { + case EQ: criteria.add(Restrictions.eq(getNameText(), value)); break; + case NOT_EQ: criteria.add(Restrictions.not(Restrictions.eq(getNameText(), value))); break; + case GT: criteria.add(Restrictions.gt(getNameText(), value)); break; + case LT: criteria.add(Restrictions.lt(getNameText(), value)); break; + case BETWEEN: + criteria.add(Restrictions.gt(getNameText(), value)); + criteria.add(Restrictions.lt(getNameText(), filterCriteria.getValue2())); + break; + default: + } + } + } + String getAsQueryString() { + return getQueryStringFromValue(Date.class); + } + void loadFromQueryString(String s, User user, Jtrac jtrac) { + setValueFromQueryString(s, Date.class); + } + }; + //============================================================== + case 5: // free text + return new Processor() { + List<Expression> getValidFilterExpressions() { + return getAsList(CONTAINS); + } + Fragment getFilterUiFragment(MarkupContainer container, User user, Space space, Jtrac jtrac) { + return getTextFieldFragment(container); + } + void addRestrictions(DetachedCriteria criteria) { + if(filterHasValue()) { + criteria.add(Restrictions.ilike(getNameText(), + (String) filterCriteria.getValue(), MatchMode.ANYWHERE)); + } + } + String getAsQueryString() { + return getQueryStringFromValue(String.class); + } + void loadFromQueryString(String s, User user, Jtrac jtrac) { + setValueFromQueryString(s, String.class); + } + }; + //============================================================== + default: + throw new RuntimeException("Unknown Column Heading " + name); + } + } else { // this is not a... [truncated message content] |
From: <udi...@us...> - 2021-09-23 08:50:16
|
Revision: 1395 http://sourceforge.net/p/j-trac/code/1395 Author: udittmer Date: 2021-09-23 08:50:14 +0000 (Thu, 23 Sep 2021) Log Message: ----------- update PrettyTime dependency Modified Paths: -------------- trunk/jtrac/pom.xml Modified: trunk/jtrac/pom.xml =================================================================== --- trunk/jtrac/pom.xml 2021-09-23 08:44:28 UTC (rev 1394) +++ trunk/jtrac/pom.xml 2021-09-23 08:50:14 UTC (rev 1395) @@ -195,7 +195,7 @@ <dependency> <groupId>org.ocpsoft.prettytime</groupId> <artifactId>prettytime</artifactId> - <version>5.0.1.Final</version> + <version>5.0.2.Final</version> </dependency> <dependency> <groupId>org.springframework</groupId> @@ -338,6 +338,9 @@ <groupId>org.apache.wicket</groupId> <artifactId>wicket</artifactId> <version>1.3.7</version> + <!-- + <version>1.5.16</version> + --> <type>pom</type> </dependency> <dependency> @@ -344,6 +347,9 @@ <groupId>org.apache.wicket</groupId> <artifactId>wicket-extensions</artifactId> <version>1.3.7</version> + <!-- + <version>1.5.16</version> + --> </dependency> <dependency> <groupId>org.slf4j</groupId> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-09-23 08:44:30
|
Revision: 1394 http://sourceforge.net/p/j-trac/code/1394 Author: udittmer Date: 2021-09-23 08:44:28 +0000 (Thu, 23 Sep 2021) Log Message: ----------- make number of items in item list configurable Modified Paths: -------------- trunk/jtrac/src/main/java/info/jtrac/domain/Config.java trunk/jtrac/src/main/java/info/jtrac/domain/ItemSearch.java trunk/jtrac/src/main/resources/messages_de.properties trunk/jtrac/src/main/resources/messages_en.properties Modified: trunk/jtrac/src/main/java/info/jtrac/domain/Config.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/domain/Config.java 2021-09-16 15:44:49 UTC (rev 1393) +++ trunk/jtrac/src/main/java/info/jtrac/domain/Config.java 2021-09-23 08:44:28 UTC (rev 1394) @@ -60,6 +60,7 @@ PARAMS.add("time.pretty"); PARAMS.add("urls.autolink"); PARAMS.add("attachments.openNewWindow"); + PARAMS.add("items.search.num"); } public static Set<String> getParams() { Modified: trunk/jtrac/src/main/java/info/jtrac/domain/ItemSearch.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/domain/ItemSearch.java 2021-09-16 15:44:49 UTC (rev 1393) +++ trunk/jtrac/src/main/java/info/jtrac/domain/ItemSearch.java 2021-09-23 08:44:28 UTC (rev 1394) @@ -1,447 +1,449 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.domain; - - -import info.jtrac.Jtrac; -import static info.jtrac.domain.ColumnHeading.Name.*; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import org.apache.wicket.PageParameters; -import org.hibernate.criterion.DetachedCriteria; -import org.hibernate.criterion.Order; -import org.hibernate.criterion.Restrictions; - -/** - * Object that holds filter criteria when searching for Items - * and also creates a Hibernate Criteria query to pass to the DAO - */ -public class ItemSearch implements Serializable { - - private Space space; // if null, means aggregate across all spaces - private User user; // this will be set in the case space is null - - private int pageSize = 25; - private int currentPage; - private long resultCount; - private String sortFieldName = "id"; - private boolean sortDescending = true; - private boolean showHistory; - private boolean batchMode; - - private long selectedItemId; - private String relatingItemRefId; - private Collection<Long> itemIds; - - private List<ColumnHeading> columnHeadings; - private Map<String, FilterCriteria> filterCriteriaMap = new LinkedHashMap<String, FilterCriteria>(); - - private String defaultVisibleFlags; - - - public ItemSearch(User user) { - this.user = user; - this.columnHeadings = ColumnHeading.getColumnHeadings(); - this.defaultVisibleFlags = getVisibleFlags(); - } - - public ItemSearch(Space space) { - this.space = space; - this.columnHeadings = ColumnHeading.getColumnHeadings(space); - this.defaultVisibleFlags = getVisibleFlags(); - } - - public void initFromPageParameters(PageParameters params, User user, Jtrac jtrac) { - showHistory = params.getBoolean("showHistory"); - pageSize = params.getInt("pageSize", 25); - sortDescending = !params.getBoolean("sortAscending"); - sortFieldName = params.getString("sortFieldName", "id"); - for(Object o : params.keySet()) { - String name = o.toString(); - if(ColumnHeading.isValidFieldOrColumnName(name)) { - ColumnHeading ch = getColumnHeading(name); - ch.loadFromQueryString(params.getString(name), user, jtrac); - } - } - relatingItemRefId = params.getString("relatingItemRefId", null); - String visibleFlags = params.getString("cols", null); - if(visibleFlags != null) { - int i = 0; - for(ColumnHeading ch : columnHeadings) { - if(i >= visibleFlags.length()) { - break; - } - char flag = visibleFlags.charAt(i); - if(flag == '1') { - ch.setVisible(true); - } else { - ch.setVisible(false); - } - i++; - } - } - } - - private String getVisibleFlags() { - StringBuilder visibleFlags = new StringBuilder(); - for(ColumnHeading ch : columnHeadings) { - if(ch.isVisible()) { - visibleFlags.append("1"); - } else { - visibleFlags.append("0"); - } - } - return visibleFlags.toString(); - } - - public PageParameters getAsQueryString() { - Map<String, String> map = new HashMap<String, String>(); - if(space != null) { - map.put("s", space.getId() + ""); - } - for(ColumnHeading ch : columnHeadings) { - String s = ch.getAsQueryString(); - if(s != null) { - map.put(ch.getNameText(), s); - } - } - String visibleFlags = getVisibleFlags(); - if(!visibleFlags.equals(defaultVisibleFlags)) { - map.put("cols", visibleFlags.toString()); - } - if(showHistory) { - map.put("showHistory", "true"); - } - if(pageSize != 25) { - map.put("pageSize", pageSize + ""); - } - if(!sortDescending) { - map.put("sortAscending", "true"); - } - if(!sortFieldName.equals("id")) { - map.put("sortFieldName", sortFieldName); - } - if(relatingItemRefId != null) { - map.put("relatingItemRefId", relatingItemRefId); - } - return new PageParameters(map); - } - - private DetachedCriteria parent; // temp working variable hack - - // have to do this two step process as "order by" clause conflicts with "count (*)" clause - // so the DAO has to use getCriteriaForCount() separately - public DetachedCriteria getCriteria() { - DetachedCriteria criteria = getCriteriaForCount(); - if (sortFieldName == null) { // can happen only for multi-space search - sortFieldName = "id"; // effectively is a sort on created date - } - if(sortFieldName.equals("id") || sortFieldName.equals("space")) { - if(showHistory) { - // if showHistory: sort by item.id and then history.id - if(sortDescending) { - if(space == null) { - DetachedCriteria parentSpace = parent.createCriteria("space"); - parentSpace.addOrder(Order.desc("name")); - } - criteria.addOrder(Order.desc("parent.id")); - criteria.addOrder(Order.desc("id")); - } else { - if(space == null) { - DetachedCriteria parentSpace = parent.createCriteria("space"); - parentSpace.addOrder(Order.asc("name")); - } - criteria.addOrder(Order.asc("parent.id")); - criteria.addOrder(Order.asc("id")); - } - } else { - if (sortDescending) { - if(space == null) { - DetachedCriteria parentSpace = criteria.createCriteria("space"); - parentSpace.addOrder(Order.desc("name")); - } - criteria.addOrder(Order.desc("id")); - } else { - if(space == null) { - DetachedCriteria parentSpace = criteria.createCriteria("space"); - parentSpace.addOrder(Order.asc("name")); - } - criteria.addOrder(Order.asc("id")); - } - } - } else { - if (sortDescending) { - criteria.addOrder(Order.desc(sortFieldName)); - } else { - criteria.addOrder(Order.asc(sortFieldName)); - } - } - return criteria; - } - - public DetachedCriteria getCriteriaForCount() { - DetachedCriteria criteria = null; - if (showHistory) { - criteria = DetachedCriteria.forClass(History.class); - // apply restrictions to parent, this is an inner join ============= - parent = criteria.createCriteria("parent"); - if(space == null) { - parent.add(Restrictions.in("space", getSelectedSpaces())); - } else { - parent.add(Restrictions.eq("space", space)); - } - if (itemIds != null) { - parent.add(Restrictions.in("id", itemIds)); - } - } else { - criteria = DetachedCriteria.forClass(Item.class); - if(space == null) { - criteria.add(Restrictions.in("space", getSelectedSpaces())); - } else { - criteria.add(Restrictions.eq("space", space)); - } - if (itemIds != null) { - criteria.add(Restrictions.in("id", itemIds)); - } - } - for(ColumnHeading ch : columnHeadings) { - ch.addRestrictions(criteria); - } - return criteria; - } - - public List<Field> getFields() { - if(space == null) { - List<Field> list = new ArrayList<Field>(2); - Field severity = new Field(Field.Name.SEVERITY); - severity.initOptions(); - list.add(severity); - Field priority = new Field(Field.Name.PRIORITY); - priority.initOptions(); - list.add(priority); - return list; - } else { - return space.getMetadata().getFieldList(); - } - } - - private ColumnHeading getColumnHeading(ColumnHeading.Name name) { - for(ColumnHeading ch : columnHeadings) { - if(ch.getName() == name) { - return ch; - } - } - return null; - } - - private ColumnHeading getColumnHeading(String name) { - for(ColumnHeading ch : columnHeadings) { - if(ch.getNameText().equals(name)) { - return ch; - } - } - return null; - } - - private String getStringValue(ColumnHeading ch) { - String s = (String) ch.getFilterCriteria().getValue(); - if(s == null || s.trim().length() == 0) { - ch.getFilterCriteria().setExpression(null); - return null; - } - return s; - } - - public String getRefId() { - ColumnHeading ch = getColumnHeading(ID); - return getStringValue(ch); - } - - public String getSearchText() { - ColumnHeading ch = getColumnHeading(DETAIL); - return getStringValue(ch); - } - - public Collection<Space> getSelectedSpaces() { - ColumnHeading ch = getColumnHeading(SPACE); - List values = ch.getFilterCriteria().getValues(); - if(values == null || values.size() == 0) { - ch.getFilterCriteria().setExpression(null); - return user.getSpaces(); - } - return values; - } - - public void toggleSortDirection() { - sortDescending = !sortDescending; - } - - private List getSingletonList(Object o) { - List list = new ArrayList(1); - list.add(o); - return list; - } - - public void setLoggedBy(User loggedBy) { - ColumnHeading ch = getColumnHeading(LOGGED_BY); - ch.getFilterCriteria().setExpression(FilterCriteria.Expression.IN); - ch.getFilterCriteria().setValues(getSingletonList(loggedBy)); - } - - public void setAssignedTo(User assignedTo) { - ColumnHeading ch = getColumnHeading(ASSIGNED_TO); - ch.getFilterCriteria().setExpression(FilterCriteria.Expression.IN); - ch.getFilterCriteria().setValues(getSingletonList(assignedTo)); - } - - public void setStatus(int i) { - ColumnHeading ch = getColumnHeading(STATUS); - ch.getFilterCriteria().setExpression(FilterCriteria.Expression.IN); - ch.getFilterCriteria().setValues(getSingletonList(i)); - } - - public List<ColumnHeading> getColumnHeadingsToRender() { - List<ColumnHeading> list = new ArrayList<ColumnHeading>(columnHeadings.size()); - for(ColumnHeading ch : columnHeadings) { - if(ch.isVisible()) { - list.add(ch); - } - } - return list; - } - - //========================================================================== - - public boolean isBatchMode() { - return batchMode; - } - - public void setBatchMode(boolean batchMode) { - this.batchMode = batchMode; - } - - public Space getSpace() { - return space; - } - - public void setSpace(Space space) { - this.space = space; - } - - public User getUser() { - return user; - } - - public void setUser(User user) { - this.user = user; - } - - public int getPageSize() { - return pageSize; - } - - public void setPageSize(int pageSize) { - this.pageSize = pageSize; - } - - public int getCurrentPage() { - return currentPage; - } - - public void setCurrentPage(int currentPage) { - this.currentPage = currentPage; - } - - public long getResultCount() { - return resultCount; - } - - public void setResultCount(long resultCount) { - this.resultCount = resultCount; - } - - public String getSortFieldName() { - return sortFieldName; - } - - public void setSortFieldName(String sortFieldName) { - this.sortFieldName = sortFieldName; - } - - public boolean isSortDescending() { - return sortDescending; - } - - public void setSortDescending(boolean sortDescending) { - this.sortDescending = sortDescending; - } - - public boolean isShowHistory() { - return showHistory; - } - - public void setShowHistory(boolean showHistory) { - this.showHistory = showHistory; - } - - public long getSelectedItemId() { - return selectedItemId; - } - - public void setSelectedItemId(long selectedItemId) { - this.selectedItemId = selectedItemId; - } - - public String getRelatingItemRefId() { - return relatingItemRefId; - } - - public void setRelatingItemRefId(String relatingItemRefId) { - this.relatingItemRefId = relatingItemRefId; - } - - public Collection<Long> getItemIds() { - return itemIds; - } - - public void setItemIds(Collection<Long> itemIds) { - this.itemIds = itemIds; - } - - public List<ColumnHeading> getColumnHeadings() { - return columnHeadings; - } - - public void setColumnHeadings(List<ColumnHeading> columnHeadings) { - this.columnHeadings = columnHeadings; - } - - public Map<String, FilterCriteria> getFilterCriteriaMap() { - return filterCriteriaMap; - } - - public void setFilterCriteriaMap(Map<String, FilterCriteria> filterCriteriaMap) { - this.filterCriteriaMap = filterCriteriaMap; - } - -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac.domain; + + +import info.jtrac.Jtrac; +import static info.jtrac.domain.ColumnHeading.Name.*; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.wicket.PageParameters; +import org.hibernate.criterion.DetachedCriteria; +import org.hibernate.criterion.Order; +import org.hibernate.criterion.Restrictions; + +/** + * Object that holds filter criteria when searching for Items + * and also creates a Hibernate Criteria query to pass to the DAO + */ +public class ItemSearch implements Serializable { + + private Space space; // if null, means aggregate across all spaces + private User user; // this will be set in the case space is null + + private int pageSize = 25; + private int currentPage; + private long resultCount; + private String sortFieldName = "id"; + private boolean sortDescending = true; + private boolean showHistory; + private boolean batchMode; + + private long selectedItemId; + private String relatingItemRefId; + private Collection<Long> itemIds; + + private List<ColumnHeading> columnHeadings; + private Map<String, FilterCriteria> filterCriteriaMap = new LinkedHashMap<String, FilterCriteria>(); + + private String defaultVisibleFlags; + + public ItemSearch(User user) { + this.user = user; + this.columnHeadings = ColumnHeading.getColumnHeadings(); + this.defaultVisibleFlags = getVisibleFlags(); + } + + public ItemSearch(Space space) { + this.space = space; + this.columnHeadings = ColumnHeading.getColumnHeadings(space); + this.defaultVisibleFlags = getVisibleFlags(); + } + + public void initFromPageParameters(PageParameters params, User user, Jtrac jtrac) { + showHistory = params.getBoolean("showHistory"); + try { + pageSize = params.getInt("pageSize", Integer.parseInt(jtrac.loadConfig("items.search.num"))); + } catch (RuntimeException rtex) { /* ignore, the default is fine */ } + sortDescending = !params.getBoolean("sortAscending"); + sortFieldName = params.getString("sortFieldName", "id"); + for(Object o : params.keySet()) { + String name = o.toString(); + if(ColumnHeading.isValidFieldOrColumnName(name)) { + ColumnHeading ch = getColumnHeading(name); + ch.loadFromQueryString(params.getString(name), user, jtrac); + } + } + relatingItemRefId = params.getString("relatingItemRefId", null); + String visibleFlags = params.getString("cols", null); + if(visibleFlags != null) { + int i = 0; + for(ColumnHeading ch : columnHeadings) { + if(i >= visibleFlags.length()) { + break; + } + char flag = visibleFlags.charAt(i); + if(flag == '1') { + ch.setVisible(true); + } else { + ch.setVisible(false); + } + i++; + } + } + } + + private String getVisibleFlags() { + StringBuilder visibleFlags = new StringBuilder(); + for(ColumnHeading ch : columnHeadings) { + if(ch.isVisible()) { + visibleFlags.append("1"); + } else { + visibleFlags.append("0"); + } + } + return visibleFlags.toString(); + } + + public PageParameters getAsQueryString() { + Map<String, String> map = new HashMap<String, String>(); + if(space != null) { + map.put("s", space.getId() + ""); + } + for(ColumnHeading ch : columnHeadings) { + String s = ch.getAsQueryString(); + if(s != null) { + map.put(ch.getNameText(), s); + } + } + String visibleFlags = getVisibleFlags(); + if(!visibleFlags.equals(defaultVisibleFlags)) { + map.put("cols", visibleFlags.toString()); + } + if(showHistory) { + map.put("showHistory", "true"); + } + if(pageSize != 25) { + map.put("pageSize", pageSize + ""); + } + if(!sortDescending) { + map.put("sortAscending", "true"); + } + if(!sortFieldName.equals("id")) { + map.put("sortFieldName", sortFieldName); + } + if(relatingItemRefId != null) { + map.put("relatingItemRefId", relatingItemRefId); + } + return new PageParameters(map); + } + + private DetachedCriteria parent; // temp working variable hack + + // have to do this two step process as "order by" clause conflicts with "count (*)" clause + // so the DAO has to use getCriteriaForCount() separately + public DetachedCriteria getCriteria() { + DetachedCriteria criteria = getCriteriaForCount(); + if (sortFieldName == null) { // can happen only for multi-space search + sortFieldName = "id"; // effectively is a sort on created date + } + if(sortFieldName.equals("id") || sortFieldName.equals("space")) { + if(showHistory) { + // if showHistory: sort by item.id and then history.id + if(sortDescending) { + if(space == null) { + DetachedCriteria parentSpace = parent.createCriteria("space"); + parentSpace.addOrder(Order.desc("name")); + } + criteria.addOrder(Order.desc("parent.id")); + criteria.addOrder(Order.desc("id")); + } else { + if(space == null) { + DetachedCriteria parentSpace = parent.createCriteria("space"); + parentSpace.addOrder(Order.asc("name")); + } + criteria.addOrder(Order.asc("parent.id")); + criteria.addOrder(Order.asc("id")); + } + } else { + if (sortDescending) { + if(space == null) { + DetachedCriteria parentSpace = criteria.createCriteria("space"); + parentSpace.addOrder(Order.desc("name")); + } + criteria.addOrder(Order.desc("id")); + } else { + if(space == null) { + DetachedCriteria parentSpace = criteria.createCriteria("space"); + parentSpace.addOrder(Order.asc("name")); + } + criteria.addOrder(Order.asc("id")); + } + } + } else { + if (sortDescending) { + criteria.addOrder(Order.desc(sortFieldName)); + } else { + criteria.addOrder(Order.asc(sortFieldName)); + } + } + return criteria; + } + + public DetachedCriteria getCriteriaForCount() { + DetachedCriteria criteria = null; + if (showHistory) { + criteria = DetachedCriteria.forClass(History.class); + // apply restrictions to parent, this is an inner join ============= + parent = criteria.createCriteria("parent"); + if(space == null) { + parent.add(Restrictions.in("space", getSelectedSpaces())); + } else { + parent.add(Restrictions.eq("space", space)); + } + if (itemIds != null) { + parent.add(Restrictions.in("id", itemIds)); + } + } else { + criteria = DetachedCriteria.forClass(Item.class); + if(space == null) { + criteria.add(Restrictions.in("space", getSelectedSpaces())); + } else { + criteria.add(Restrictions.eq("space", space)); + } + if (itemIds != null) { + criteria.add(Restrictions.in("id", itemIds)); + } + } + for(ColumnHeading ch : columnHeadings) { + ch.addRestrictions(criteria); + } + return criteria; + } + + public List<Field> getFields() { + if(space == null) { + List<Field> list = new ArrayList<Field>(2); + Field severity = new Field(Field.Name.SEVERITY); + severity.initOptions(); + list.add(severity); + Field priority = new Field(Field.Name.PRIORITY); + priority.initOptions(); + list.add(priority); + return list; + } else { + return space.getMetadata().getFieldList(); + } + } + + private ColumnHeading getColumnHeading(ColumnHeading.Name name) { + for(ColumnHeading ch : columnHeadings) { + if(ch.getName() == name) { + return ch; + } + } + return null; + } + + private ColumnHeading getColumnHeading(String name) { + for(ColumnHeading ch : columnHeadings) { + if(ch.getNameText().equals(name)) { + return ch; + } + } + return null; + } + + private String getStringValue(ColumnHeading ch) { + String s = (String) ch.getFilterCriteria().getValue(); + if(s == null || s.trim().length() == 0) { + ch.getFilterCriteria().setExpression(null); + return null; + } + return s; + } + + public String getRefId() { + ColumnHeading ch = getColumnHeading(ID); + return getStringValue(ch); + } + + public String getSearchText() { + ColumnHeading ch = getColumnHeading(DETAIL); + return getStringValue(ch); + } + + public Collection<Space> getSelectedSpaces() { + ColumnHeading ch = getColumnHeading(SPACE); + List values = ch.getFilterCriteria().getValues(); + if(values == null || values.size() == 0) { + ch.getFilterCriteria().setExpression(null); + return user.getSpaces(); + } + return values; + } + + public void toggleSortDirection() { + sortDescending = !sortDescending; + } + + private List getSingletonList(Object o) { + List list = new ArrayList(1); + list.add(o); + return list; + } + + public void setLoggedBy(User loggedBy) { + ColumnHeading ch = getColumnHeading(LOGGED_BY); + ch.getFilterCriteria().setExpression(FilterCriteria.Expression.IN); + ch.getFilterCriteria().setValues(getSingletonList(loggedBy)); + } + + public void setAssignedTo(User assignedTo) { + ColumnHeading ch = getColumnHeading(ASSIGNED_TO); + ch.getFilterCriteria().setExpression(FilterCriteria.Expression.IN); + ch.getFilterCriteria().setValues(getSingletonList(assignedTo)); + } + + public void setStatus(int i) { + ColumnHeading ch = getColumnHeading(STATUS); + ch.getFilterCriteria().setExpression(FilterCriteria.Expression.IN); + ch.getFilterCriteria().setValues(getSingletonList(i)); + } + + public List<ColumnHeading> getColumnHeadingsToRender() { + List<ColumnHeading> list = new ArrayList<ColumnHeading>(columnHeadings.size()); + for(ColumnHeading ch : columnHeadings) { + if(ch.isVisible()) { + list.add(ch); + } + } + return list; + } + + //========================================================================== + + public boolean isBatchMode() { + return batchMode; + } + + public void setBatchMode(boolean batchMode) { + this.batchMode = batchMode; + } + + public Space getSpace() { + return space; + } + + public void setSpace(Space space) { + this.space = space; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public int getPageSize() { + return pageSize; + } + + public void setPageSize(int pageSize) { + this.pageSize = pageSize; + } + + public int getCurrentPage() { + return currentPage; + } + + public void setCurrentPage(int currentPage) { + this.currentPage = currentPage; + } + + public long getResultCount() { + return resultCount; + } + + public void setResultCount(long resultCount) { + this.resultCount = resultCount; + } + + public String getSortFieldName() { + return sortFieldName; + } + + public void setSortFieldName(String sortFieldName) { + this.sortFieldName = sortFieldName; + } + + public boolean isSortDescending() { + return sortDescending; + } + + public void setSortDescending(boolean sortDescending) { + this.sortDescending = sortDescending; + } + + public boolean isShowHistory() { + return showHistory; + } + + public void setShowHistory(boolean showHistory) { + this.showHistory = showHistory; + } + + public long getSelectedItemId() { + return selectedItemId; + } + + public void setSelectedItemId(long selectedItemId) { + this.selectedItemId = selectedItemId; + } + + public String getRelatingItemRefId() { + return relatingItemRefId; + } + + public void setRelatingItemRefId(String relatingItemRefId) { + this.relatingItemRefId = relatingItemRefId; + } + + public Collection<Long> getItemIds() { + return itemIds; + } + + public void setItemIds(Collection<Long> itemIds) { + this.itemIds = itemIds; + } + + public List<ColumnHeading> getColumnHeadings() { + return columnHeadings; + } + + public void setColumnHeadings(List<ColumnHeading> columnHeadings) { + this.columnHeadings = columnHeadings; + } + + public Map<String, FilterCriteria> getFilterCriteriaMap() { + return filterCriteriaMap; + } + + public void setFilterCriteriaMap(Map<String, FilterCriteria> filterCriteriaMap) { + this.filterCriteriaMap = filterCriteriaMap; + } + +} Modified: trunk/jtrac/src/main/resources/messages_de.properties =================================================================== --- trunk/jtrac/src/main/resources/messages_de.properties 2021-09-16 15:44:49 UTC (rev 1393) +++ trunk/jtrac/src/main/resources/messages_de.properties 2021-09-23 08:44:28 UTC (rev 1394) @@ -206,6 +206,7 @@ config.time.pretty = Relative Zeiten anzeigen wie auf Github, Twitter und Facebook (auf true setzen, Default ist false) config.urls.autolink = URLs in Tickets und Kommentaren automatisch verlinken (auf true setzen, Default ist false) config.attachments.openNewWindow = Text- und Bild-Anh\u00e4nge in neuem Fenster \u00f6ffnen statt sie herunterzuladen +config.items.search.num = Anzahl der Eintr\u00f6ge pro Seite (Default 25) # config_list (config_form does not have any extra messages) config_list.configurationSettings = Einstellungen Modified: trunk/jtrac/src/main/resources/messages_en.properties =================================================================== --- trunk/jtrac/src/main/resources/messages_en.properties 2021-09-16 15:44:49 UTC (rev 1393) +++ trunk/jtrac/src/main/resources/messages_en.properties 2021-09-23 08:44:28 UTC (rev 1394) @@ -206,6 +206,7 @@ config.time.pretty = Show relative timestamps as on Github, Twitter and Facebook (set to true, default is false) config.urls.autolink = Auto-link URLs in tickets and comments (set to true, default is false) config.attachments.openNewWindow = Open text and image attachments in a new tab/window, rather than downloading them +config.items.search.num = Number of entries per page (default is 25) # config_list (config_form does not have any extra messages) config_list.configurationSettings = Configuration Settings This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-09-16 15:44:50
|
Revision: 1393 http://sourceforge.net/p/j-trac/code/1393 Author: udittmer Date: 2021-09-16 15:44:49 +0000 (Thu, 16 Sep 2021) Log Message: ----------- also match URLs that contain % Modified Paths: -------------- trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java Modified: trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java 2021-09-16 09:16:08 UTC (rev 1392) +++ trunk/jtrac/src/main/java/info/jtrac/util/ItemUtils.java 2021-09-16 15:44:49 UTC (rev 1393) @@ -52,7 +52,7 @@ private static final Logger logger = LoggerFactory.getLogger(ItemUtils.class); - public static final String URL_PATTERN = "http(s?)://[:\\p{L}\\p{Digit}\\.\\-_\\/=#\\?]+"; + public static final String URL_PATTERN = "http(s?)://[:\\p{L}\\p{Digit}\\.\\-_\\/=#\\?%]+"; /** * does HTML escaping, converts tabs to spaces and converts leading This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-09-16 09:16:11
|
Revision: 1392 http://sourceforge.net/p/j-trac/code/1392 Author: udittmer Date: 2021-09-16 09:16:08 +0000 (Thu, 16 Sep 2021) Log Message: ----------- missed a couple of files Modified Paths: -------------- trunk/jtrac/src/main/java/info/jtrac/domain/Config.java trunk/jtrac/src/main/resources/messages_en.properties Modified: trunk/jtrac/src/main/java/info/jtrac/domain/Config.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/domain/Config.java 2021-09-16 09:11:45 UTC (rev 1391) +++ trunk/jtrac/src/main/java/info/jtrac/domain/Config.java 2021-09-16 09:16:08 UTC (rev 1392) @@ -59,6 +59,7 @@ PARAMS.add("pwd.minLength"); PARAMS.add("time.pretty"); PARAMS.add("urls.autolink"); + PARAMS.add("attachments.openNewWindow"); } public static Set<String> getParams() { Modified: trunk/jtrac/src/main/resources/messages_en.properties =================================================================== --- trunk/jtrac/src/main/resources/messages_en.properties 2021-09-16 09:11:45 UTC (rev 1391) +++ trunk/jtrac/src/main/resources/messages_en.properties 2021-09-16 09:16:08 UTC (rev 1392) @@ -204,7 +204,8 @@ config.attachment.maxsize = Maximum size in MB of file-attachments. (default 5 MB) Use -1 for no-limit config.pwd.minLength = Minimal length of passwords (default is 8) config.time.pretty = Show relative timestamps as on Github, Twitter and Facebook (set to true, default is false) -config.urls.autolink = Auto-link URLs in tickets and comments +config.urls.autolink = Auto-link URLs in tickets and comments (set to true, default is false) +config.attachments.openNewWindow = Open text and image attachments in a new tab/window, rather than downloading them # config_list (config_form does not have any extra messages) config_list.configurationSettings = Configuration Settings This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-09-16 09:11:48
|
Revision: 1391 http://sourceforge.net/p/j-trac/code/1391 Author: udittmer Date: 2021-09-16 09:11:45 +0000 (Thu, 16 Sep 2021) Log Message: ----------- make new attachment behavior configurable Modified Paths: -------------- trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.html trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.java trunk/jtrac/src/main/resources/messages.properties trunk/jtrac/src/main/resources/messages_de.properties Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.html =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.html 2021-09-16 07:18:08 UTC (rev 1390) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.html 2021-09-16 09:11:45 UTC (rev 1391) @@ -1,6 +1,4 @@ <wicket:panel> - <!-- - <span wicket:id="attachment"><a href="#" target="_blank"><img src="resources/attachment.gif" class="nav-link"/><span wicket:id="fileName"></span></a></span> - --> - <a wicket:id="attachment" href="#" target="_blank"><img src="resources/attachment.gif" class="nav-link"/><span wicket:id="fileName"></span></a> + <span id="oldStyle"><span wicket:id="attachment"><a href="#" target="_blank"><img src="resources/attachment.gif" class="nav-link"/><span wicket:id="fileName"></span></a></span></span> + <span id="newStyle"><a wicket:id="attachment" href="#" target="_blank"><img src="resources/attachment.gif" class="nav-link"/><span wicket:id="fileName"></span></a></span> </wicket:panel> Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.java 2021-09-16 07:18:08 UTC (rev 1390) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.java 2021-09-16 09:11:45 UTC (rev 1391) @@ -25,9 +25,12 @@ import java.io.IOException; import java.io.InputStream; +import org.springframework.util.StringUtils; + import org.apache.wicket.IRequestTarget; import org.apache.wicket.RequestCycle; import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.internal.HtmlHeaderContainer; import org.apache.wicket.markup.html.link.Link; import org.apache.wicket.protocol.http.WebResponse; import org.apache.wicket.util.io.Streams; @@ -51,6 +54,7 @@ Link link = new Link("attachment") { // adapted from wicket.markup.html.link.DownloadLink // with the difference that the File is instantiated only after onClick + @Override public void onClick() { getRequestCycle().setRequestTarget(new IRequestTarget() { @@ -61,14 +65,17 @@ public void respond (RequestCycle requestCycle) { WebResponse r = (WebResponse) requestCycle.getResponse(); String fileType = AttachmentUtils.guessFileType(attachment, getJtrac().getJtracHome()); - System.out.println(fileType); - if (fileType != null && (fileType.startsWith("image") || fileType.startsWith("text"))) { - r.setHeader("Content-Disposition", "inline; filename="+fileName); + if (openNewWindow()) { + if (fileType != null && (fileType.startsWith("image") || fileType.startsWith("text"))) { + r.setHeader("Content-Disposition", "inline; filename="+fileName); + //r.setInlineHeader(fileName); + // not available in Wicket 1.3.7 + } else { + r.setAttachmentHeader(fileName); + } } else { r.setAttachmentHeader(fileName); } - //r.setInlineHeader(fileName); - // not available in Wicket 1.3.7 try { File file = AttachmentUtils.getFile(attachment, getJtrac().getJtracHome()); InputStream is = new FileInputStream(file); @@ -89,10 +96,27 @@ } }); } + + @Override + public void renderHead (HtmlHeaderContainer container) { + if (openNewWindow()) { + container.getHeaderResponse().renderString("<style>#oldStyle { display: none; } #newStyle { display: inline}</style>"); + } else { + container.getHeaderResponse().renderString("<style>#oldStyle { display: inline; } #newStyle { display: none}</style>"); + } + } }; link.add(new Label("fileName", fileName)); add(link); } - + + protected boolean openNewWindow() { + String openNewWindow = getJtrac().loadConfig("attachments.openNewWindow"); + if (StringUtils.hasText(openNewWindow)) { + return openNewWindow.trim().equalsIgnoreCase("true"); + } else { + return false; + } + } } Modified: trunk/jtrac/src/main/resources/messages.properties =================================================================== --- trunk/jtrac/src/main/resources/messages.properties 2021-09-16 07:18:08 UTC (rev 1390) +++ trunk/jtrac/src/main/resources/messages.properties 2021-09-16 09:11:45 UTC (rev 1391) @@ -205,6 +205,7 @@ config.pwd.minLength = Minimal length of passwords (default is 8) config.time.pretty = Show relative timestamps as on Github, Twitter and Facebook (set to true, default is false) config.urls.autolink = Auto-link URLs in tickets and comments (set to true, default is false) +config.attachments.openNewWindow = Open text and image attachments in a new tab/window, rather than downloading them # config_list (config_form does not have any extra messages) config_list.configurationSettings = Configuration Settings Modified: trunk/jtrac/src/main/resources/messages_de.properties =================================================================== --- trunk/jtrac/src/main/resources/messages_de.properties 2021-09-16 07:18:08 UTC (rev 1390) +++ trunk/jtrac/src/main/resources/messages_de.properties 2021-09-16 09:11:45 UTC (rev 1391) @@ -205,6 +205,7 @@ config.pwd.minLength = Minimale L\u00e4nge von Passw\u00f6rtern (Default 8) config.time.pretty = Relative Zeiten anzeigen wie auf Github, Twitter und Facebook (auf true setzen, Default ist false) config.urls.autolink = URLs in Tickets und Kommentaren automatisch verlinken (auf true setzen, Default ist false) +config.attachments.openNewWindow = Text- und Bild-Anh\u00e4nge in neuem Fenster \u00f6ffnen statt sie herunterzuladen # config_list (config_form does not have any extra messages) config_list.configurationSettings = Einstellungen This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <udi...@us...> - 2021-09-16 07:18:11
|
Revision: 1390 http://sourceforge.net/p/j-trac/code/1390 Author: udittmer Date: 2021-09-16 07:18:08 +0000 (Thu, 16 Sep 2021) Log Message: ----------- open text and image attachments in new tab instead of downloading them Modified Paths: -------------- trunk/jtrac/src/main/java/info/jtrac/util/AttachmentUtils.java trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.html trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.java trunk/jtrac/src/main/resources/messages.properties trunk/jtrac/src/main/resources/messages_de.properties Modified: trunk/jtrac/src/main/java/info/jtrac/util/AttachmentUtils.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/util/AttachmentUtils.java 2021-09-15 07:13:53 UTC (rev 1389) +++ trunk/jtrac/src/main/java/info/jtrac/util/AttachmentUtils.java 2021-09-16 07:18:08 UTC (rev 1390) @@ -1,41 +1,53 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.util; - -import info.jtrac.domain.Attachment; -import java.io.File; - -/** - * Utils that deal with Attachments, upload / download and - * file name String manipulation - */ -public class AttachmentUtils { - - public static String cleanFileName(String path) { - // the client browser could be on Unix or Windows, we don't know - int index = path.lastIndexOf('/'); - if (index == -1) { - index = path.lastIndexOf('\\'); - } - return (index != -1 ? path.substring(index + 1) : path); - } - - public static File getFile(Attachment attachment, String jtracHome) { - return new File(jtracHome + "/attachments/" + attachment.getFilePrefix() + "_" + attachment.getFileName()); - } - -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac.util; + +import info.jtrac.domain.Attachment; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +/** + * Utils that deal with Attachments, upload / download and file name String manipulation + */ + +public class AttachmentUtils { + + public static String cleanFileName(String path) { + // the client browser could be on Unix or Windows, we don't know + int index = path.lastIndexOf('/'); + if (index == -1) { + index = path.lastIndexOf('\\'); + } + return (index != -1 ? path.substring(index + 1) : path); + } + + public static File getFile (Attachment attachment, String jtracHome) { + return new File(jtracHome + "/attachments/" + attachment.getFilePrefix() + "_" + attachment.getFileName()); + } + + public static String guessFileType (Attachment attachment, String jtracHome) { + String fileType ="application/octet-stream"; + try { + File file = getFile(attachment, jtracHome); + fileType = Files.probeContentType(file.toPath()); + } catch (IOException ioex) { + //System.out.println("can't determine file type of "+attachment.getFileName()+": "+ioex.getMessage()); + } + return fileType; + } +} Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.html =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.html 2021-09-15 07:13:53 UTC (rev 1389) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.html 2021-09-16 07:18:08 UTC (rev 1390) @@ -1,3 +1,6 @@ <wicket:panel> + <!-- <span wicket:id="attachment"><a href="#" target="_blank"><img src="resources/attachment.gif" class="nav-link"/><span wicket:id="fileName"></span></a></span> + --> + <a wicket:id="attachment" href="#" target="_blank"><img src="resources/attachment.gif" class="nav-link"/><span wicket:id="fileName"></span></a> </wicket:panel> Modified: trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.java =================================================================== --- trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.java 2021-09-15 07:13:53 UTC (rev 1389) +++ trunk/jtrac/src/main/java/info/jtrac/wicket/AttachmentLinkPanel.java 2021-09-16 07:18:08 UTC (rev 1390) @@ -1,89 +1,98 @@ -/* - * Copyright 2002-2005 the original author or authors. - * - * Licensed 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 info.jtrac.wicket; - -import info.jtrac.domain.Attachment; -import info.jtrac.util.AttachmentUtils; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import org.apache.wicket.IRequestTarget; -import org.apache.wicket.RequestCycle; -import org.apache.wicket.markup.html.basic.Label; -import org.apache.wicket.markup.html.link.Link; -import org.apache.wicket.protocol.http.WebResponse; -import org.apache.wicket.util.io.Streams; - -/** - * link for downloading an attachment - */ -public class AttachmentLinkPanel extends BasePanel { - - public AttachmentLinkPanel(String id, final Attachment attachment) { - - super(id); - - if(attachment == null) { - add(new Label("attachment", "")); - setVisible(false); - return; - } - - final String fileName = getResponse().encodeURL(attachment.getFileName()).toString(); - - Link link = new Link("attachment") { - // adapted from wicket.markup.html.link.DownloadLink - // with the difference that the File is instantiated only after onClick - public void onClick() { - getRequestCycle().setRequestTarget(new IRequestTarget() { - - public void detach(RequestCycle requestCycle) { - } - - public void respond(RequestCycle requestCycle) { - WebResponse r = (WebResponse) requestCycle.getResponse(); - r.setAttachmentHeader(fileName); - try { - File file = AttachmentUtils.getFile(attachment, getJtrac().getJtracHome()); - InputStream is = new FileInputStream(file); - try { - Streams.copy(is, r.getOutputStream()); - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - try { - is.close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } catch (FileNotFoundException e) { - throw new RuntimeException(e); - } - } - }); - } - }; - - link.add(new Label("fileName", fileName)); - add(link); - - } - -} +/* + * Copyright 2002-2005 the original author or authors. + * + * Licensed 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 info.jtrac.wicket; + +import info.jtrac.domain.Attachment; +import info.jtrac.util.AttachmentUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.wicket.IRequestTarget; +import org.apache.wicket.RequestCycle; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.link.Link; +import org.apache.wicket.protocol.http.WebResponse; +import org.apache.wicket.util.io.Streams; + +/** + * link for downloading an attachment + */ +public class AttachmentLinkPanel extends BasePanel { + + public AttachmentLinkPanel (String id, final Attachment attachment) { + super(id); + + if (attachment == null) { + add(new Label("attachment", "")); + setVisible(false); + return; + } + + final String fileName = getResponse().encodeURL(attachment.getFileName()).toString(); + + Link link = new Link("attachment") { + // adapted from wicket.markup.html.link.DownloadLink + // with the difference that the File is instantiated only after onClick + public void onClick() { + getRequestCycle().setRequestTarget(new IRequestTarget() { + + @Override + public void detach (RequestCycle requestCycle) { } + + @Override + public void respond (RequestCycle requestCycle) { + WebResponse r = (WebResponse) requestCycle.getResponse(); + String fileType = AttachmentUtils.guessFileType(attachment, getJtrac().getJtracHome()); + System.out.println(fileType); + if (fileType != null && (fileType.startsWith("image") || fileType.startsWith("text"))) { + r.setHeader("Content-Disposition", "inline; filename="+fileName); + } else { + r.setAttachmentHeader(fileName); + } + //r.setInlineHeader(fileName); + // not available in Wicket 1.3.7 + try { + File file = AttachmentUtils.getFile(attachment, getJtrac().getJtracHome()); + InputStream is = new FileInputStream(file); + try { + Streams.copy(is, r.getOutputStream()); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + try { + is.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + } + }); + } + }; + + link.add(new Label("fileName", fileName)); + add(link); + } + +} Modified: trunk/jtrac/src/main/resources/messages.properties =================================================================== --- trunk/jtrac/src/main/resources/messages.properties 2021-09-15 07:13:53 UTC (rev 1389) +++ trunk/jtrac/src/main/resources/messages.properties 2021-09-16 07:18:08 UTC (rev 1390) @@ -204,6 +204,7 @@ config.attachment.maxsize = Maximum size in MB of file-attachments. (default 5 MB) Use -1 for no-limit config.pwd.minLength = Minimal length of passwords (default is 8) config.time.pretty = Show relative timestamps as on Github, Twitter and Facebook (set to true, default is false) +config.urls.autolink = Auto-link URLs in tickets and comments (set to true, default is false) # config_list (config_form does not have any extra messages) config_list.configurationSettings = Configuration Settings Modified: trunk/jtrac/src/main/resources/messages_de.properties =================================================================== --- trunk/jtrac/src/main/resources/messages_de.properties 2021-09-15 07:13:53 UTC (rev 1389) +++ trunk/jtrac/src/main/resources/messages_de.properties 2021-09-16 07:18:08 UTC (rev 1390) @@ -204,7 +204,7 @@ config.attachment.maxsize = Maximalgr\u00F6\u00DFe f\u00FCr Anlagen in MB, "-1" f\u00FCr Kein-Limit (Default 5) config.pwd.minLength = Minimale L\u00e4nge von Passw\u00f6rtern (Default 8) config.time.pretty = Relative Zeiten anzeigen wie auf Github, Twitter und Facebook (auf true setzen, Default ist false) -config.urls.autolink = URLs in Tickets und Kommentaren automatisch verlinken +config.urls.autolink = URLs in Tickets und Kommentaren automatisch verlinken (auf true setzen, Default ist false) # config_list (config_form does not have any extra messages) config_list.configurationSettings = Einstellungen This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |