Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
jenkins
提交
eb49ae16
J
jenkins
项目概览
xxadev
/
jenkins
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
J
jenkins
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
eb49ae16
编写于
4月 12, 2013
作者:
J
Jesse Glick
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #757 from Vlatombe/views_include_jobs_folder
ListView can match now items belonging to nested ItemGroups
上级
2d3fa398
bcd94569
变更
13
隐藏空白更改
内联
并排
Showing
13 changed file
with
374 addition
and
98 deletion
+374
-98
changelog.html
changelog.html
+2
-0
core/src/main/java/hudson/Functions.java
core/src/main/java/hudson/Functions.java
+100
-36
core/src/main/java/hudson/model/AbstractItem.java
core/src/main/java/hudson/model/AbstractItem.java
+17
-0
core/src/main/java/hudson/model/Items.java
core/src/main/java/hudson/model/Items.java
+43
-0
core/src/main/java/hudson/model/ListView.java
core/src/main/java/hudson/model/ListView.java
+73
-30
core/src/main/java/jenkins/model/Jenkins.java
core/src/main/java/jenkins/model/Jenkins.java
+1
-18
core/src/main/resources/hudson/model/Computer/index.jelly
core/src/main/resources/hudson/model/Computer/index.jelly
+1
-1
core/src/main/resources/hudson/model/Label/index.jelly
core/src/main/resources/hudson/model/Label/index.jelly
+1
-1
core/src/main/resources/hudson/model/ListView/configure-entries.jelly
...n/resources/hudson/model/ListView/configure-entries.jelly
+28
-3
core/src/main/resources/hudson/model/View/main.groovy
core/src/main/resources/hudson/model/View/main.groovy
+1
-1
core/src/main/resources/hudson/views/JobColumn/column.jelly
core/src/main/resources/hudson/views/JobColumn/column.jelly
+1
-1
core/src/main/resources/lib/hudson/projectView.jelly
core/src/main/resources/lib/hudson/projectView.jelly
+2
-3
core/src/test/java/hudson/FunctionsTest.java
core/src/test/java/hudson/FunctionsTest.java
+104
-4
未找到文件。
changelog.html
浏览文件 @
eb49ae16
...
...
@@ -55,6 +55,8 @@ Upcoming changes</a>
<!-- Record your changes in the trunk here. -->
<div
id=
"trunk"
style=
"display:none"
>
<!--=TRUNK-BEGIN=-->
<ul
class=
image
>
<li
class=
rfe
>
Views can now include jobs located within folders
<li
class=
rfe
>
Added confirmation dialog before reloading configuration from disk.
(
<a
href=
"https://issues.jenkins-ci.org/browse/JENKINS-15340"
>
issue 15340
</a>
)
...
...
core/src/main/java/hudson/Functions.java
浏览文件 @
eb49ae16
...
...
@@ -25,13 +25,33 @@
*/
package
hudson
;
import
com.google.common.base.Predicate
;
import
com.google.common.base.Predicates
;
import
hudson.cli.CLICommand
;
import
hudson.console.ConsoleAnnotationDescriptor
;
import
hudson.console.ConsoleAnnotatorFactory
;
import
hudson.model.*
;
import
hudson.model.AbstractProject
;
import
hudson.model.Action
;
import
hudson.model.Describable
;
import
hudson.model.Descriptor
;
import
hudson.model.DescriptorVisibilityFilter
;
import
hudson.model.Hudson
;
import
hudson.model.Item
;
import
hudson.model.ItemGroup
;
import
hudson.model.Items
;
import
hudson.model.JDK
;
import
hudson.model.Job
;
import
hudson.model.JobPropertyDescriptor
;
import
hudson.model.ModelObject
;
import
hudson.model.Node
;
import
hudson.model.PageDecorator
;
import
hudson.model.ParameterDefinition
;
import
hudson.model.ParameterDefinition.ParameterDescriptor
;
import
hudson.model.Project
;
import
hudson.model.Run
;
import
hudson.model.TopLevelItem
;
import
hudson.model.User
;
import
hudson.model.View
;
import
hudson.scm.SCM
;
import
hudson.scm.SCMDescriptor
;
import
hudson.search.SearchableModelObject
;
import
hudson.security.AccessControlled
;
import
hudson.security.AuthorizationStrategy
;
...
...
@@ -53,36 +73,11 @@ import hudson.tasks.Publisher;
import
hudson.tasks.UserAvatarResolver
;
import
hudson.util.Area
;
import
hudson.util.Iterators
;
import
hudson.scm.SCM
;
import
hudson.scm.SCMDescriptor
;
import
hudson.util.Secret
;
import
hudson.views.MyViewsTabBar
;
import
hudson.views.ViewsTabBar
;
import
hudson.widgets.RenderOnDemandClosure
;
import
jenkins.model.GlobalConfiguration
;
import
jenkins.model.GlobalConfigurationCategory
;
import
jenkins.model.GlobalConfigurationCategory.Unclassified
;
import
jenkins.model.Jenkins
;
import
jenkins.model.ModelObjectWithContextMenu
;
import
org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken
;
import
org.apache.commons.jelly.JellyContext
;
import
org.apache.commons.jelly.JellyTagException
;
import
org.apache.commons.jelly.Script
;
import
org.apache.commons.jelly.XMLOutput
;
import
org.apache.commons.jexl.parser.ASTSizeFunction
;
import
org.apache.commons.jexl.util.Introspector
;
import
org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
;
import
org.jvnet.tiger_types.Types
;
import
org.kohsuke.stapler.Ancestor
;
import
org.kohsuke.stapler.Stapler
;
import
org.kohsuke.stapler.StaplerRequest
;
import
org.kohsuke.stapler.StaplerResponse
;
import
org.kohsuke.stapler.jelly.InternationalizedStringExpression.RawHtmlArgument
;
import
javax.servlet.ServletException
;
import
javax.servlet.http.Cookie
;
import
javax.servlet.http.HttpServletRequest
;
import
javax.servlet.http.HttpServletResponse
;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.PrintWriter
;
...
...
@@ -93,8 +88,8 @@ import java.lang.management.ManagementFactory;
import
java.lang.management.MonitorInfo
;
import
java.lang.management.ThreadInfo
;
import
java.lang.management.ThreadMXBean
;
import
java.lang.reflect.Type
;
import
java.lang.reflect.ParameterizedType
;
import
java.lang.reflect.Type
;
import
java.net.MalformedURLException
;
import
java.net.URI
;
import
java.net.URISyntaxException
;
...
...
@@ -108,6 +103,7 @@ import java.util.Collection;
import
java.util.Collections
;
import
java.util.Comparator
;
import
java.util.ConcurrentModificationException
;
import
java.util.Date
;
import
java.util.Enumeration
;
import
java.util.HashMap
;
import
java.util.List
;
...
...
@@ -115,14 +111,42 @@ import java.util.Locale;
import
java.util.Map
;
import
java.util.SortedMap
;
import
java.util.TreeMap
;
import
java.util.Date
;
import
java.util.logging.Level
;
import
java.util.logging.LogManager
;
import
java.util.logging.LogRecord
;
import
java.util.logging.Logger
;
import
java.util.logging.SimpleFormatter
;
import
java.util.regex.Pattern
;
import
javax.servlet.ServletException
;
import
javax.servlet.http.Cookie
;
import
javax.servlet.http.HttpServletRequest
;
import
javax.servlet.http.HttpServletResponse
;
import
jenkins.model.GlobalConfiguration
;
import
jenkins.model.GlobalConfigurationCategory
;
import
jenkins.model.GlobalConfigurationCategory.Unclassified
;
import
jenkins.model.Jenkins
;
import
jenkins.model.ModelObjectWithContextMenu
;
import
org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken
;
import
org.apache.commons.jelly.JellyContext
;
import
org.apache.commons.jelly.JellyTagException
;
import
org.apache.commons.jelly.Script
;
import
org.apache.commons.jelly.XMLOutput
;
import
org.apache.commons.jexl.parser.ASTSizeFunction
;
import
org.apache.commons.jexl.util.Introspector
;
import
org.apache.commons.lang.StringUtils
;
import
org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
;
import
org.jvnet.tiger_types.Types
;
import
org.kohsuke.stapler.Ancestor
;
import
org.kohsuke.stapler.Stapler
;
import
org.kohsuke.stapler.StaplerRequest
;
import
org.kohsuke.stapler.StaplerResponse
;
import
org.kohsuke.stapler.jelly.InternationalizedStringExpression.RawHtmlArgument
;
import
com.google.common.base.Predicate
;
import
com.google.common.base.Predicates
;
/**
* Utility functions used in views.
...
...
@@ -895,32 +919,72 @@ public class Functions {
}
String
path
=
ancestors
.
get
(
p
);
if
(
path
!=
null
)
return
path
;
if
(
path
!=
null
)
{
return
normalizeURI
(
path
+
'/'
);
}
Item
i
=
p
;
String
url
=
""
;
Collection
<
TopLevelItem
>
viewItems
;
if
(
view
!=
null
)
{
viewItems
=
view
.
getItems
();
}
else
{
viewItems
=
Collections
.
emptyList
();
}
while
(
true
)
{
ItemGroup
ig
=
i
.
getParent
();
url
=
i
.
getShortUrl
()+
url
;
if
(
ig
==
Jenkins
.
getInstance
())
{
if
(
ig
==
Jenkins
.
getInstance
()
||
(
view
!=
null
&&
ig
==
view
.
getOwnerItemGroup
())
)
{
assert
i
instanceof
TopLevelItem
;
if
(
view
!=
null
&&
view
.
contains
((
TopLevelItem
)
i
))
{
if
(
view
Items
.
contains
((
TopLevelItem
)
i
))
{
// if p and the current page belongs to the same view, then return a relative path
return
ancestors
.
get
(
view
)+
'/'
+
url
;
return
normalizeURI
(
ancestors
.
get
(
view
)+
'/'
+
url
)
;
}
else
{
// otherwise return a path from the root Hudson
return
request
.
getContextPath
()+
'/'
+
p
.
getUrl
(
);
return
normalizeURI
(
request
.
getContextPath
()+
'/'
+
p
.
getUrl
()
);
}
}
path
=
ancestors
.
get
(
ig
);
if
(
path
!=
null
)
return
path
+
'/'
+
url
;
if
(
path
!=
null
)
{
return
normalizeURI
(
path
+
'/'
+
url
);
}
assert
ig
instanceof
Item
;
// if not, ig must have been the Hudson instance
i
=
(
Item
)
ig
;
}
}
private
static
String
normalizeURI
(
String
uri
)
{
return
URI
.
create
(
uri
).
normalize
().
toString
();
}
/**
* Gets all the {@link TopLevelItem}s recursively in the {@link ItemGroup} tree.
*
* @since XXX
*/
public
static
List
<
TopLevelItem
>
getAllTopLevelItems
(
ItemGroup
root
)
{
return
Items
.
getAllItems
(
root
,
TopLevelItem
.
class
);
}
/**
* Gets the relative display name to the given item from the specified group.
*
* @since XXX
* @param p the Item we want the relative display name
* @param g the ItemGroup used as point of reference for the item
* @return
* String like "foo » bar"
*/
public
static
String
getRelativeDisplayNameFrom
(
Item
p
,
ItemGroup
g
)
{
if
(
p
==
null
)
return
null
;
String
relativeName
=
p
.
getRelativeNameFrom
(
g
);
if
(
relativeName
==
null
)
return
null
;
return
relativeName
.
replace
(
"/"
,
" » "
);
}
public
static
Map
<
Thread
,
StackTraceElement
[]>
dumpAllThreads
()
{
Map
<
Thread
,
StackTraceElement
[]>
sorted
=
new
TreeMap
<
Thread
,
StackTraceElement
[]>(
new
ThreadSorter
());
...
...
core/src/main/java/hudson/model/AbstractItem.java
浏览文件 @
eb49ae16
...
...
@@ -326,8 +326,25 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
if
(
n
.
length
()==
0
)
return
getDisplayName
();
else
return
n
+
" \u00BB "
+
getDisplayName
();
}
/**
* This method only exists to disambiguate {@link #getRelativeNameFrom(ItemGroup)} and {@link #getRelativeNameFrom(Item)}
* @since XXX
* @see #getRelativeNameFrom(ItemGroup)
*/
public
String
getRelativeNameFromGroup
(
ItemGroup
p
)
{
return
getRelativeNameFrom
(
p
);
}
/**
* @param p
* The ItemGroup instance used as context to evaluate the relative name of this AbstractItem
* @return
* The name of the current item, relative to p.
* Nested ItemGroups are separated by / character.
*/
public
String
getRelativeNameFrom
(
ItemGroup
p
)
{
if
(
p
==
null
)
return
getFullName
();
// first list up all the parents
Map
<
ItemGroup
,
Integer
>
parents
=
new
HashMap
<
ItemGroup
,
Integer
>();
int
depth
=
0
;
...
...
core/src/main/java/hudson/model/Items.java
浏览文件 @
eb49ae16
...
...
@@ -235,6 +235,49 @@ public class Items {
public
static
XmlFile
getConfigFile
(
Item
item
)
{
return
getConfigFile
(
item
.
getRootDir
());
}
/**
* Gets all the {@link Item}s recursively in the {@link ItemGroup} tree
* and filter them by the given type.
*
* @since XXX
*/
public
static
<
T
extends
Item
>
List
<
T
>
getAllItems
(
final
ItemGroup
root
,
Class
<
T
>
type
)
{
List
<
T
>
r
=
new
ArrayList
<
T
>();
Stack
<
ItemGroup
>
q
=
new
Stack
<
ItemGroup
>();
q
.
push
(
root
);
while
(!
q
.
isEmpty
())
{
ItemGroup
<?>
parent
=
q
.
pop
();
for
(
Item
i
:
parent
.
getItems
())
{
if
(
type
.
isInstance
(
i
))
{
if
(
i
.
hasPermission
(
Item
.
READ
))
r
.
add
(
type
.
cast
(
i
));
}
if
(
i
instanceof
ItemGroup
)
q
.
push
((
ItemGroup
)
i
);
}
}
// sort by relative name, ignoring case
Collections
.
sort
(
r
,
new
Comparator
<
T
>()
{
@Override
public
int
compare
(
T
o1
,
T
o2
)
{
if
(
o1
==
null
)
{
if
(
o2
==
null
)
{
return
0
;
}
return
1
;
}
if
(
o2
==
null
)
{
return
-
1
;
}
return
o1
.
getRelativeNameFrom
(
root
).
compareToIgnoreCase
(
o2
.
getRelativeNameFrom
(
root
));
}
});
return
r
;
}
/**
* Used to load/save job configuration.
...
...
core/src/main/java/hudson/model/ListView.java
浏览文件 @
eb49ae16
...
...
@@ -33,15 +33,7 @@ import hudson.util.FormValidation;
import
hudson.util.HttpResponses
;
import
hudson.views.ListViewColumn
;
import
hudson.views.ViewJobFilter
;
import
org.kohsuke.stapler.DataBoundConstructor
;
import
org.kohsuke.stapler.HttpResponse
;
import
org.kohsuke.stapler.QueryParameter
;
import
org.kohsuke.stapler.StaplerRequest
;
import
org.kohsuke.stapler.StaplerResponse
;
import
org.kohsuke.stapler.interceptor.RequirePOST
;
import
javax.annotation.concurrent.GuardedBy
;
import
javax.servlet.ServletException
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.LinkedHashSet
;
...
...
@@ -51,6 +43,18 @@ import java.util.TreeSet;
import
java.util.regex.Pattern
;
import
java.util.regex.PatternSyntaxException
;
import
javax.annotation.concurrent.GuardedBy
;
import
javax.servlet.ServletException
;
import
net.sf.json.JSONObject
;
import
org.kohsuke.stapler.DataBoundConstructor
;
import
org.kohsuke.stapler.HttpResponse
;
import
org.kohsuke.stapler.QueryParameter
;
import
org.kohsuke.stapler.StaplerRequest
;
import
org.kohsuke.stapler.StaplerResponse
;
import
org.kohsuke.stapler.interceptor.RequirePOST
;
/**
* Displays {@link Job}s in a flat list view.
*
...
...
@@ -73,6 +77,11 @@ public class ListView extends View implements Saveable {
*/
private
String
includeRegex
;
/**
* Whether to recurse in ItemGroups
*/
private
boolean
recurse
;
/**
* Compiled include pattern from the includeRegex string.
*/
...
...
@@ -138,24 +147,18 @@ public class ListView extends View implements Saveable {
*/
public
List
<
TopLevelItem
>
getItems
()
{
SortedSet
<
String
>
names
;
List
<
TopLevelItem
>
items
=
new
ArrayList
<
TopLevelItem
>();
synchronized
(
this
)
{
names
=
new
TreeSet
<
String
>(
jobNames
);
}
if
(
includePattern
!=
null
)
{
for
(
Item
item
:
getOwnerItemGroup
().
getItems
())
{
String
itemName
=
item
.
getName
();
if
(
includePattern
.
matcher
(
itemName
).
matches
())
{
names
.
add
(
itemName
);
}
}
}
ItemGroup
<?
extends
TopLevelItem
>
parent
=
getOwnerItemGroup
();
includeItems
(
parent
,
names
);
Boolean
statusFilter
=
this
.
statusFilter
;
// capture the value to isolate us from concurrent update
List
<
TopLevelItem
>
items
=
new
ArrayList
<
TopLevelItem
>(
names
.
size
());
for
(
TopLevelItem
item
:
getOwnerItemGroup
().
getItems
())
{
if
(!
names
.
contains
(
item
.
getName
()))
continue
;
for
(
TopLevelItem
item
:
Items
.
getAllItems
(
getOwnerItemGroup
(),
TopLevelItem
.
class
))
{
if
(!
names
.
contains
(
item
.
getRelativeNameFrom
(
getOwnerItemGroup
())))
continue
;
// Add if no status filter or filter matches enabled/disabled status:
if
(
statusFilter
==
null
||
!(
item
instanceof
AbstractProject
)
||
((
AbstractProject
)
item
).
isDisabled
()
^
statusFilter
)
...
...
@@ -164,7 +167,7 @@ public class ListView extends View implements Saveable {
// check the filters
Iterable
<
ViewJobFilter
>
jobFilters
=
getJobFilters
();
List
<
TopLevelItem
>
allItems
=
new
ArrayList
<
TopLevelItem
>(
getOwnerItemGroup
()
.
getItems
());
List
<
TopLevelItem
>
allItems
=
new
ArrayList
<
TopLevelItem
>(
parent
.
getItems
());
for
(
ViewJobFilter
jobFilter:
jobFilters
)
{
items
=
jobFilter
.
filter
(
items
,
allItems
,
this
);
}
...
...
@@ -173,11 +176,37 @@ public class ListView extends View implements Saveable {
return
items
;
}
public
synchronized
boolean
contains
(
TopLevelItem
item
)
{
return
jobNames
.
contains
(
item
.
getName
());
@Override
public
boolean
contains
(
TopLevelItem
item
)
{
return
getItems
().
contains
(
item
);
}
private
void
includeItems
(
ItemGroup
<?
extends
TopLevelItem
>
parent
,
SortedSet
<
String
>
names
)
{
includeItems
(
parent
,
parent
,
names
);
}
private
void
includeItems
(
ItemGroup
<?
extends
TopLevelItem
>
root
,
ItemGroup
<?
extends
TopLevelItem
>
parent
,
SortedSet
<
String
>
names
)
{
if
(
includePattern
!=
null
)
{
for
(
Item
item
:
parent
.
getItems
())
{
if
(
recurse
&&
item
instanceof
ItemGroup
)
{
includeItems
(
root
,
(
ItemGroup
<?
extends
TopLevelItem
>)
item
,
names
);
}
String
itemName
=
item
.
getRelativeNameFrom
(
root
);
if
(
includePattern
.
matcher
(
itemName
).
matches
())
{
names
.
add
(
itemName
);
}
}
}
}
public
synchronized
boolean
jobNamesContains
(
TopLevelItem
item
)
{
if
(
item
==
null
)
return
false
;
return
jobNames
.
contains
(
item
.
getRelativeNameFrom
(
getOwnerItemGroup
()));
}
/**
* Adds the given item to this view.
*
...
...
@@ -185,7 +214,7 @@ public class ListView extends View implements Saveable {
*/
public
void
add
(
TopLevelItem
item
)
throws
IOException
{
synchronized
(
this
)
{
jobNames
.
add
(
item
.
get
Name
(
));
jobNames
.
add
(
item
.
get
RelativeNameFrom
(
getOwnerItemGroup
()
));
}
save
();
}
...
...
@@ -193,6 +222,10 @@ public class ListView extends View implements Saveable {
public
String
getIncludeRegex
()
{
return
includeRegex
;
}
public
boolean
isRecurse
()
{
return
recurse
;
}
/**
* Filter by enabled/disabled status of jobs.
...
...
@@ -208,7 +241,7 @@ public class ListView extends View implements Saveable {
TopLevelItem
item
=
((
ModifiableItemGroup
<?
extends
TopLevelItem
>)
ig
).
doCreateItem
(
req
,
rsp
);
if
(
item
!=
null
)
{
synchronized
(
this
)
{
jobNames
.
add
(
item
.
get
Name
(
));
jobNames
.
add
(
item
.
get
RelativeNameFrom
(
getOwnerItemGroup
()
));
}
owner
.
save
();
}
...
...
@@ -257,11 +290,21 @@ public class ListView extends View implements Saveable {
*/
@Override
protected
void
submit
(
StaplerRequest
req
)
throws
ServletException
,
FormException
,
IOException
{
JSONObject
json
=
req
.
getSubmittedForm
();
synchronized
(
this
)
{
recurse
=
json
.
optBoolean
(
"recurse"
,
true
);
jobNames
.
clear
();
for
(
TopLevelItem
item
:
getOwnerItemGroup
().
getItems
())
{
if
(
req
.
getParameter
(
item
.
getName
())!=
null
)
jobNames
.
add
(
item
.
getName
());
Iterable
<?
extends
TopLevelItem
>
items
;
if
(
recurse
)
{
items
=
Items
.
getAllItems
(
getOwnerItemGroup
(),
TopLevelItem
.
class
);
}
else
{
items
=
getOwnerItemGroup
().
getItems
();
}
for
(
TopLevelItem
item
:
items
)
{
String
relativeNameFrom
=
item
.
getRelativeNameFrom
(
getOwnerItemGroup
());
if
(
req
.
getParameter
(
relativeNameFrom
)!=
null
)
{
jobNames
.
add
(
relativeNameFrom
);
}
}
}
...
...
@@ -279,12 +322,12 @@ public class ListView extends View implements Saveable {
if
(
columns
==
null
)
{
columns
=
new
DescribableList
<
ListViewColumn
,
Descriptor
<
ListViewColumn
>>(
this
);
}
columns
.
rebuildHetero
(
req
,
req
.
getSubmittedForm
()
,
ListViewColumn
.
all
(),
"columns"
);
columns
.
rebuildHetero
(
req
,
json
,
ListViewColumn
.
all
(),
"columns"
);
if
(
jobFilters
==
null
)
{
jobFilters
=
new
DescribableList
<
ViewJobFilter
,
Descriptor
<
ViewJobFilter
>>(
this
);
}
jobFilters
.
rebuildHetero
(
req
,
req
.
getSubmittedForm
()
,
ViewJobFilter
.
all
(),
"jobFilters"
);
jobFilters
.
rebuildHetero
(
req
,
json
,
ViewJobFilter
.
all
(),
"jobFilters"
);
String
filter
=
Util
.
fixEmpty
(
req
.
getParameter
(
"statusFilter"
));
statusFilter
=
filter
!=
null
?
"1"
.
equals
(
filter
)
:
null
;
...
...
core/src/main/java/jenkins/model/Jenkins.java
浏览文件 @
eb49ae16
...
...
@@ -1385,24 +1385,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGro
* and filter them by the given type.
*/
public
<
T
extends
Item
>
List
<
T
>
getAllItems
(
Class
<
T
>
type
)
{
List
<
T
>
r
=
new
ArrayList
<
T
>();
Stack
<
ItemGroup
>
q
=
new
Stack
<
ItemGroup
>();
q
.
push
(
this
);
while
(!
q
.
isEmpty
())
{
ItemGroup
<?>
parent
=
q
.
pop
();
for
(
Item
i
:
parent
.
getItems
())
{
if
(
type
.
isInstance
(
i
))
{
if
(
i
.
hasPermission
(
Item
.
READ
))
r
.
add
(
type
.
cast
(
i
));
}
if
(
i
instanceof
ItemGroup
)
q
.
push
((
ItemGroup
)
i
);
}
}
return
r
;
return
Items
.
getAllItems
(
this
,
type
);
}
/**
...
...
core/src/main/resources/hudson/model/Computer/index.jelly
浏览文件 @
eb49ae16
...
...
@@ -111,7 +111,7 @@ THE SOFTWARE.
</p>
</j:when>
<j:otherwise>
<t:projectView jobs="${jobs}"
jobBaseUrl="${rootURL}/" useFullName="true"
/>
<t:projectView jobs="${jobs}"/>
</j:otherwise>
</j:choose>
...
...
core/src/main/resources/hudson/model/Label/index.jelly
浏览文件 @
eb49ae16
...
...
@@ -53,7 +53,7 @@ THE SOFTWARE.
</p>
</j:when>
<j:otherwise>
<t:projectView jobs="${jobs}"
jobBaseUrl="${rootURL}/" useFullName="true"
/>
<t:projectView jobs="${jobs}"/>
</j:otherwise>
</j:choose>
...
...
core/src/main/resources/hudson/model/ListView/configure-entries.jelly
浏览文件 @
eb49ae16
...
...
@@ -36,12 +36,24 @@ THE SOFTWARE.
<f:option value="2" selected="${it.statusFilter==false}">${%Disabled jobs only}</f:option>
</select>
</f:entry>
<f:entry title="${%Recurse in subfolders}" field="recurse">
<f:checkbox id="recurse"/>
</f:entry>
<f:entry title="${%Jobs}">
<div class="listview-jobs">
<j:forEach var="job" items="${it.ownerItemGroup.items}">
<f:checkbox name="${job.name}" checked="${it.contains(job)}" title="${job.name}" />
<br/>
<j:forEach var="job" items="${h.getAllTopLevelItems(it.ownerItemGroup)}">
<j:set var="spanClass" value=""/>
<j:set var="spanStyle" value=""/>
<j:if test="${job.parent!=it.ownerItemGroup}">
<j:set var="spanClass" value="nested"/>
<j:set var="spanStyle" value="${it.recurse?'':'display:none'}"/>
</j:if>
<span class="${spanClass}" style="${spanStyle}">
<f:checkbox name="${job.getRelativeNameFromGroup(it.ownerItemGroup)}" checked="${it.jobNamesContains(job)}" title="${h.getRelativeDisplayNameFrom(job,it.ownerItemGroup)}" />
<br/>
</span>
</j:forEach>
</div>
</f:entry>
...
...
@@ -66,5 +78,18 @@ THE SOFTWARE.
<f:repeatableHeteroProperty field="columns" hasHeader="true" addCaption="${%Add column}"/>
</f:block>
</f:section>
<script>
(function() {
Behaviour.specify("#recurse", 'ListView', 0, function(e) {
var nestedElements = $$('SPAN.nested')
e.onclick = function() {
nestedElements.each(function(el) {
e.checked ? el.show() : el.hide();
});
}
});
}());
</script>
</j:jelly>
core/src/main/resources/hudson/model/View/main.groovy
浏览文件 @
eb49ae16
...
...
@@ -11,7 +11,7 @@ if (items.isEmpty()) {
}
include
(
my
,
"noJob.jelly"
);
}
else
{
t
.
projectView
(
jobs:
items
,
jobBaseUrl:
""
,
showViewTabs:
true
,
columnExtensions:
my
.
columns
,
indenter:
my
.
indenter
)
{
t
.
projectView
(
jobs:
items
,
showViewTabs:
true
,
columnExtensions:
my
.
columns
,
indenter:
my
.
indenter
)
{
set
(
"views"
,
my
.
owner
.
views
);
set
(
"currentView"
,
my
);
if
(
my
.
owner
.
class
==
hudson
.
model
.
MyViewsProperty
.
class
)
{
...
...
core/src/main/resources/hudson/views/JobColumn/column.jelly
浏览文件 @
eb49ae16
...
...
@@ -25,6 +25,6 @@ THE SOFTWARE.
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt">
<td style="${indenter.getCss(job)}">
<a href="${jobBaseUrl}${job.shortUrl}" class='model-link'> ${
useFullName ? job.fullDisplayName : job.displayName
}</a>
<a href="${jobBaseUrl}${job.shortUrl}" class='model-link'> ${
h.getRelativeDisplayNameFrom(job,currentView.owner.itemGroup)
}</a>
</td>
</j:jelly>
\ No newline at end of file
core/src/main/resources/lib/hudson/projectView.jelly
浏览文件 @
eb49ae16
...
...
@@ -33,10 +33,9 @@ THE SOFTWARE.
</st:attribute>
<st:attribute name="useFullName" use="optional" type="boolean">
May be interpreted by columns to display the full name of a job.
Will also append a parent URL to jobBaseUrl if needed.
</st:attribute>
<st:attribute name="jobBaseUrl" type="String">
The base URL of all job links. Normally ${rootURL}/
Deprecated. Using this attribute has no effect.
</st:attribute>
<st:attribute name="showViewTabs" use="optional" type="boolean">
If the caller rendered a view tabes, set this attribute so that CSS is adjusted accordingly.
...
...
@@ -79,7 +78,7 @@ THE SOFTWARE.
</j:forEach>
<j:forEach var="job" items="${jobs}">
<t:projectViewRow jobBaseUrl="${
useFullName ? jobBaseUrl + job.parent.url : jobBaseUrl
}"/>
<t:projectViewRow jobBaseUrl="${
h.getRelativeLinkTo(job).substring(0, h.getRelativeLinkTo(job).length() - job.shortUrl.length())
}"/>
</j:forEach>
</table>
<t:iconSize><t:rssBar/></t:iconSize>
...
...
core/src/test/java/hudson/FunctionsTest.java
浏览文件 @
eb49ae16
...
...
@@ -23,18 +23,30 @@
*/
package
hudson
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
powermock
.
api
.
mockito
.
PowerMockito
.
mock
;
import
static
org
.
powermock
.
api
.
mockito
.
PowerMockito
.
mockStatic
;
import
static
org
.
powermock
.
api
.
mockito
.
PowerMockito
.
when
;
import
hudson.model.Action
;
import
hudson.model.ItemGroup
;
import
hudson.model.TopLevelItem
;
import
hudson.model.View
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Locale
;
import
hudson.model.Action
;
import
static
org
.
junit
.
Assert
.*;
import
jenkins.model.Jenkins
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.jvnet.hudson.test.Bug
;
import
org.kohsuke.stapler.Ancestor
;
import
org.kohsuke.stapler.Stapler
;
import
org.kohsuke.stapler.StaplerRequest
;
import
org.powermock.modules.junit4.PowerMockRunner
;
import
org.powermock.core.classloader.annotations.PrepareForTest
;
import
static
org
.
powermock
.
api
.
mockito
.
PowerMockito
.*
;
import
org.powermock.modules.junit4.PowerMockRunner
;
@RunWith
(
PowerMockRunner
.
class
)
public
class
FunctionsTest
{
...
...
@@ -101,6 +113,94 @@ public class FunctionsTest {
assertEquals
(
contextPath
+
"/"
+
itUrl
+
path
,
result
);
}
}
@Test
@PrepareForTest
({
Stapler
.
class
,
Jenkins
.
class
})
public
void
testGetRelativeLinkTo_JobContainedInView
()
throws
Exception
{
Jenkins
j
=
createMockJenkins
();
ItemGroup
parent
=
j
;
String
contextPath
=
"/jenkins"
;
StaplerRequest
req
=
createMockRequest
(
contextPath
);
mockStatic
(
Stapler
.
class
);
when
(
Stapler
.
getCurrentRequest
()).
thenReturn
(
req
);
View
view
=
mock
(
View
.
class
);
when
(
view
.
getOwnerItemGroup
()).
thenReturn
(
parent
);
createMockAncestors
(
req
,
createAncestor
(
view
,
"."
),
createAncestor
(
j
,
"../.."
));
TopLevelItem
i
=
createMockItem
(
parent
,
"job/i/"
);
when
(
view
.
getItems
()).
thenReturn
(
Arrays
.
asList
(
i
));
String
result
=
Functions
.
getRelativeLinkTo
(
i
);
assertEquals
(
"job/i/"
,
result
);
}
@Test
@PrepareForTest
({
Stapler
.
class
,
Jenkins
.
class
})
public
void
testGetRelativeLinkTo_JobNotContainedInView
()
throws
Exception
{
Jenkins
j
=
createMockJenkins
();
ItemGroup
parent
=
j
;
String
contextPath
=
"/jenkins"
;
StaplerRequest
req
=
createMockRequest
(
contextPath
);
mockStatic
(
Stapler
.
class
);
when
(
Stapler
.
getCurrentRequest
()).
thenReturn
(
req
);
View
view
=
mock
(
View
.
class
);
when
(
view
.
getOwnerItemGroup
()).
thenReturn
(
parent
);
createMockAncestors
(
req
,
createAncestor
(
j
,
"../.."
),
createAncestor
(
view
,
"."
));
TopLevelItem
i
=
createMockItem
(
parent
,
"job/i/"
);
when
(
view
.
getItems
()).
thenReturn
(
Collections
.<
TopLevelItem
>
emptyList
());
String
result
=
Functions
.
getRelativeLinkTo
(
i
);
assertEquals
(
"/jenkins/job/i/"
,
result
);
}
private
interface
TopLevelItemAndItemGroup
<
T
extends
TopLevelItem
>
extends
TopLevelItem
,
ItemGroup
<
T
>
{}
@Test
@PrepareForTest
({
Stapler
.
class
,
Jenkins
.
class
})
public
void
testGetRelativeLinkTo_JobContainedInViewWithinItemGroup
()
throws
Exception
{
Jenkins
j
=
createMockJenkins
();
TopLevelItemAndItemGroup
parent
=
mock
(
TopLevelItemAndItemGroup
.
class
);
when
(
parent
.
getShortUrl
()).
thenReturn
(
"parent/"
);
String
contextPath
=
"/jenkins"
;
StaplerRequest
req
=
createMockRequest
(
contextPath
);
mockStatic
(
Stapler
.
class
);
when
(
Stapler
.
getCurrentRequest
()).
thenReturn
(
req
);
View
view
=
mock
(
View
.
class
);
when
(
view
.
getOwnerItemGroup
()).
thenReturn
(
parent
);
createMockAncestors
(
req
,
createAncestor
(
j
,
"../../.."
),
createAncestor
(
parent
,
"../.."
),
createAncestor
(
view
,
"."
));
TopLevelItem
i
=
createMockItem
(
parent
,
"job/i/"
,
"parent/job/i/"
);
when
(
view
.
getItems
()).
thenReturn
(
Arrays
.
asList
(
i
));
String
result
=
Functions
.
getRelativeLinkTo
(
i
);
assertEquals
(
"job/i/"
,
result
);
}
private
void
createMockAncestors
(
StaplerRequest
req
,
Ancestor
...
ancestors
)
{
List
<
Ancestor
>
ancestorsList
=
Arrays
.
asList
(
ancestors
);
when
(
req
.
getAncestors
()).
thenReturn
(
ancestorsList
);
}
private
TopLevelItem
createMockItem
(
ItemGroup
p
,
String
shortUrl
)
{
return
createMockItem
(
p
,
shortUrl
,
shortUrl
);
}
private
TopLevelItem
createMockItem
(
ItemGroup
p
,
String
shortUrl
,
String
url
)
{
TopLevelItem
i
=
mock
(
TopLevelItem
.
class
);
when
(
i
.
getShortUrl
()).
thenReturn
(
shortUrl
);
when
(
i
.
getUrl
()).
thenReturn
(
url
);
when
(
i
.
getParent
()).
thenReturn
(
p
);
return
i
;
}
private
Jenkins
createMockJenkins
()
{
mockStatic
(
Jenkins
.
class
);
Jenkins
j
=
mock
(
Jenkins
.
class
);
when
(
Jenkins
.
getInstance
()).
thenReturn
(
j
);
return
j
;
}
private
static
Ancestor
createAncestor
(
Object
o
,
String
relativePath
)
{
Ancestor
a
=
mock
(
Ancestor
.
class
);
when
(
a
.
getObject
()).
thenReturn
(
o
);
when
(
a
.
getRelativePath
()).
thenReturn
(
relativePath
);
return
a
;
}
@Test
@PrepareForTest
(
Stapler
.
class
)
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录