-
-
Notifications
You must be signed in to change notification settings - Fork 9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[JENKINS-39300] View.getDisplayName() is ignored when rendering tabs #2603
Changes from 7 commits
0763ffd
5b1ace0
7c43986
056b3b0
e0a80dc
417e0d8
f3d13af
8cb1070
19e8be4
559a5af
beaabb0
5106675
fd2009a
d1c40f4
3538931
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -415,7 +415,7 @@ public List<Widget> getWidgets() { | |
* If this view uses <t:projectView> for rendering, this method returns columns to be displayed. | ||
*/ | ||
public Iterable<? extends ListViewColumn> getColumns() { | ||
return ListViewColumn.createDefaultInitialColumnList(); | ||
return ListViewColumn.createDefaultInitialColumnList(this); | ||
} | ||
|
||
/** | ||
|
@@ -1219,11 +1219,20 @@ public static DescriptorExtensionList<View,ViewDescriptor> all() { | |
return Jenkins.getInstance().<View,ViewDescriptor>getDescriptorList(View.class); | ||
} | ||
|
||
/** | ||
* Returns the {@link ViewDescriptor} instances that can be instantiated for the {@link ViewGroup} in the current | ||
* {@link StaplerRequest}. <strong>NOTE: This method must be called from a {@link StaplerRequest}</strong> | ||
* @return the list of instantiable {@link ViewDescriptor} instances for the current {@link StaplerRequest} | ||
*/ | ||
public static List<ViewDescriptor> allInstantiable() { | ||
List<ViewDescriptor> r = new ArrayList<ViewDescriptor>(); | ||
for (ViewDescriptor d : all()) | ||
if(d.isInstantiable()) | ||
ViewGroup owner = Stapler.getCurrentRequest().findAncestorObject(ViewGroup.class); | ||
for (ViewDescriptor d : DescriptorVisibilityFilter.apply(owner, all())) { | ||
if (d.isApplicableIn(owner) && d.isInstantiable() | ||
&& owner.getACL().hasCreatePermission(Jenkins.getAuthentication(), owner, d)) { | ||
r.add(d); | ||
} | ||
} | ||
return r; | ||
} | ||
|
||
|
@@ -1268,6 +1277,9 @@ public static View create(StaplerRequest req, StaplerResponse rsp, ViewGroup own | |
if (mode==null || mode.length()==0) { | ||
if(isXmlSubmission) { | ||
View v = createViewFromXML(name, req.getInputStream()); | ||
if (!owner.getACL().hasCreatePermission(Jenkins.getAuthentication(), owner, v.getDescriptor())) { | ||
throw new Failure(Messages.View_CreatePermissionMissing(v.getDescriptor().getDisplayName())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
v.owner = owner; | ||
rsp.setStatus(HttpServletResponse.SC_OK); | ||
return v; | ||
|
@@ -1276,7 +1288,7 @@ public static View create(StaplerRequest req, StaplerResponse rsp, ViewGroup own | |
} | ||
|
||
View v; | ||
if (mode!=null && mode.equals("copy")) { | ||
if ("copy".equals(mode)) { | ||
v = copy(req, owner, name); | ||
} else { | ||
ViewDescriptor descriptor = all().findByName(mode); | ||
|
@@ -1287,6 +1299,9 @@ public static View create(StaplerRequest req, StaplerResponse rsp, ViewGroup own | |
// create a view | ||
v = descriptor.newInstance(req,req.getSubmittedForm()); | ||
} | ||
if (!owner.getACL().hasCreatePermission(Jenkins.getAuthentication(), owner, v.getDescriptor())) { | ||
throw new Failure(Messages.View_CreatePermissionMissing(v.getDescriptor().getDisplayName())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||
} | ||
v.owner = owner; | ||
|
||
// redirect to the config screen | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,17 +23,26 @@ | |
*/ | ||
package hudson.model; | ||
|
||
import hudson.util.FormValidation; | ||
import hudson.views.ListViewColumn; | ||
import hudson.views.ListViewColumnDescriptor; | ||
import hudson.views.ViewJobFilter; | ||
import java.lang.reflect.ParameterizedType; | ||
import java.lang.reflect.Type; | ||
import java.util.Iterator; | ||
import java.util.List; | ||
import javax.annotation.CheckForNull; | ||
import javax.annotation.Nonnull; | ||
import jenkins.model.DirectlyModifiableTopLevelItemGroup; | ||
import jenkins.model.Jenkins; | ||
import org.apache.commons.lang.StringUtils; | ||
import org.jvnet.tiger_types.Types; | ||
import org.kohsuke.accmod.Restricted; | ||
import org.kohsuke.accmod.restrictions.DoNotUse; | ||
import org.kohsuke.stapler.AncestorInPath; | ||
import org.kohsuke.stapler.QueryParameter; | ||
import org.kohsuke.stapler.Stapler; | ||
import org.kohsuke.stapler.StaplerRequest; | ||
|
||
/** | ||
* {@link Descriptor} for {@link View}. | ||
|
@@ -101,13 +110,65 @@ public AutoCompletionCandidates doAutoCompleteCopyNewItemFrom(@QueryParameter fi | |
* Possible {@link ListViewColumnDescriptor}s that can be used with this view. | ||
*/ | ||
public List<Descriptor<ListViewColumn>> getColumnsDescriptors() { | ||
return ListViewColumn.all(); | ||
StaplerRequest request = Stapler.getCurrentRequest(); | ||
View view = request.findAncestorObject(clazz); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return view == null ? DescriptorVisibilityFilter.applyType(clazz, ListViewColumn.all()) | ||
: DescriptorVisibilityFilter.apply(view, ListViewColumn.all()); | ||
} | ||
|
||
/** | ||
* Possible {@link ViewJobFilter} types that can be used with this view. | ||
*/ | ||
public List<Descriptor<ViewJobFilter>> getJobFiltersDescriptors() { | ||
return ViewJobFilter.all(); | ||
StaplerRequest request = Stapler.getCurrentRequest(); | ||
View view = request.findAncestorObject(clazz); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||
return view == null ? DescriptorVisibilityFilter.applyType(clazz, ViewJobFilter.all()) | ||
: DescriptorVisibilityFilter.apply(view, ViewJobFilter.all()); | ||
} | ||
|
||
/** | ||
* Validation of the display name field. | ||
* | ||
* @param view the view to check the new display name of. | ||
* @param value the proposed new display name. | ||
* @return the validation result. | ||
* @since FIXME | ||
*/ | ||
@SuppressWarnings("unused") // expose utility check method to subclasses | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a particular subclass you had in mind? Not in this PR; some other linked one? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes but it needs to use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So where is that subclass? |
||
protected FormValidation checkDisplayName(@Nonnull View view, @CheckForNull String value) { | ||
if (StringUtils.isBlank(value)) { | ||
// no custom name, no need to check | ||
return FormValidation.ok(); | ||
} | ||
for (View v: view.owner.getViews()) { | ||
if (v.getViewName().equals(view.getViewName())) { | ||
continue; | ||
} | ||
if (StringUtils.equals(v.getDisplayName(), value)) { | ||
return FormValidation.warning(Messages.View_DisplayNameNotUniqueWarning(value)); | ||
} | ||
} | ||
return FormValidation.ok(); | ||
} | ||
|
||
/** | ||
* Returns true if this {@link View} type is applicable to the given {@link ViewGroup} type. | ||
* | ||
* @return true to indicate applicable, in which case the view will be instantiable within the type of owner. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Specify default implementation. |
||
* @since FIXME | ||
*/ | ||
public boolean isApplicable(Class<? extends ViewGroup> ownerType) { | ||
return true; | ||
} | ||
|
||
/** | ||
* Returns true if this {@link View} type is applicable in the specific {@link ViewGroup}. | ||
* | ||
* @return true to indicate applicable, in which case the view will be instantiable within the given owner. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Specify default implementation. |
||
* @since FIXME | ||
*/ | ||
public boolean isApplicableIn(ViewGroup owner) { | ||
return isApplicable(owner.getClass()); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,7 @@ | |
import hudson.model.Describable; | ||
import hudson.model.Descriptor; | ||
import hudson.model.Descriptor.FormException; | ||
import hudson.model.DescriptorVisibilityFilter; | ||
import jenkins.model.Jenkins; | ||
import hudson.model.Item; | ||
import hudson.model.ItemGroup; | ||
|
@@ -121,20 +122,50 @@ public Descriptor<ListViewColumn> getDescriptor() { | |
/** | ||
* Creates the list of {@link ListViewColumn}s to be used for newly created {@link ListView}s and their likes. | ||
* @since 1.391 | ||
* @deprecated use {@link #createDefaultInitialColumnList(Class)} | ||
*/ | ||
@Deprecated | ||
public static List<ListViewColumn> createDefaultInitialColumnList() { | ||
return createDefaultInitialColumnList(ListViewColumn.all()); | ||
} | ||
|
||
/** | ||
* Creates the list of {@link ListViewColumn}s to be used for newly created {@link ListView}s and their likes. | ||
* | ||
* @see ListView#initColumns() | ||
* @since FIXME | ||
*/ | ||
public static List<ListViewColumn> createDefaultInitialColumnList(Class<? extends View> context) { | ||
return createDefaultInitialColumnList(DescriptorVisibilityFilter.applyType(context, ListViewColumn.all())); | ||
} | ||
|
||
/** | ||
* Creates the list of {@link ListViewColumn}s to be used for a {@link ListView}s and their likes. | ||
* | ||
* @see View#getColumns() | ||
* @since FIXME | ||
*/ | ||
public static List<ListViewColumn> createDefaultInitialColumnList(View view) { | ||
return createDefaultInitialColumnList(DescriptorVisibilityFilter.apply(view, ListViewColumn.all())); | ||
} | ||
|
||
private static List<ListViewColumn> createDefaultInitialColumnList(List<Descriptor<ListViewColumn>> descriptors) { | ||
// OK, set up default list of columns: | ||
// create all instances | ||
ArrayList<ListViewColumn> r = new ArrayList<ListViewColumn>(); | ||
final JSONObject emptyJSON = new JSONObject(); | ||
for (Descriptor<ListViewColumn> d : ListViewColumn.all()) | ||
for (Descriptor<ListViewColumn> d : descriptors) | ||
try { | ||
if (d instanceof ListViewColumnDescriptor) { | ||
ListViewColumnDescriptor ld = (ListViewColumnDescriptor) d; | ||
if (!ld.shownByDefault()) continue; // skip this | ||
if (!ld.shownByDefault()) { | ||
continue; // skip this | ||
} | ||
} | ||
ListViewColumn lvc = d.newInstance(null, emptyJSON); | ||
if (!lvc.shownByDefault()) continue; // skip this | ||
if (!lvc.shownByDefault()) { | ||
continue; // skip this | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. gratuitous reformatting |
||
|
||
r.add(lvc); | ||
} catch (FormException e) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3052,7 +3052,7 @@ public void run(Reactor session) throws Exception { | |
// initialize views by inserting the default view if necessary | ||
// this is both for clean Jenkins and for backward compatibility. | ||
if(views.size()==0 || primaryView==null) { | ||
View v = new AllView(Messages.Hudson_ViewName()); | ||
View v = new AllView("all"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks like it fixes JENKINS-38606. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is. But will it cause a fallout in upgrading Jenkins instances? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will only affect new instances. Old instances can get the fix by recreating the All view (add new view, change default, delete all view, add back all view, change default, tidy up) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could be more aggressive and rename the AllView to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks like a reasonable default for new All views. No need to edit existing ones. Would just cause confusion and broken links. |
||
setViewOwner(v); | ||
views.add(0,v); | ||
primaryView = v.getViewName(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,7 +32,7 @@ THE SOFTWARE. | |
<st:include page="sidepanel.jelly" it="${it.user}"/> | ||
|
||
<l:main-panel> | ||
<j:invokeStatic var="views" className="hudson.model.View" method="allInstantiable" /> | ||
<j:invokeStatic var="views" className="hudson.model.View" method="allInstantiable"/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gratuitous diff hunk. |
||
<n:form nameTitle="${%View name}" action="createView" | ||
descriptors="${views}" checkUrl="${rootURL}/${it.url}viewExistsCheck" xmlns:n="/lib/hudson/newFromList" /> | ||
</l:main-panel> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,7 +31,7 @@ THE SOFTWARE. | |
<st:include page="sidepanel.jelly" /> | ||
|
||
<l:main-panel> | ||
<j:invokeStatic var="views" className="hudson.model.View" method="allInstantiable" /> | ||
<j:invokeStatic var="views" className="hudson.model.View" method="allInstantiable"/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||
<n:form nameTitle="${%View name}" action="createView" copyTitle="${%Copy Existing View}" | ||
descriptors="${views}" checkUrl="checkViewName" xmlns:n="/lib/hudson/newFromList" /> | ||
</l:main-panel> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,24 +22,32 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
THE SOFTWARE. | ||
--> | ||
|
||
<!-- | ||
<%@ attribute name="name" required="true" type="java.lang.String" %> | ||
<%@ attribute name="href" required="true" type="java.lang.String" %> | ||
<%@ attribute name="active" required="true" type="java.lang.Boolean" %> | ||
<%@ attribute name="title" required="false" type="java.lang.String" %> | ||
--> | ||
<?jelly escape-by-default='true'?> | ||
<j:jelly xmlns:j="jelly:core"> | ||
<div class="tab${active?' active':''}"> | ||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler"> | ||
<st:documentation> | ||
<st:attribute name="name" use="required"> | ||
The name of the tab | ||
</st:attribute> | ||
<st:attribute name="href" use="required"> | ||
The url of the tab | ||
</st:attribute> | ||
<st:attribute name="active" type="java.lang.Boolean"> | ||
Whether the tab is active or not | ||
</st:attribute> | ||
<st:attribute name="title"> | ||
The title of the tab | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am restoring behaviour that was broken in this earlier commit (appears to be removed by omission as the old style documentation of parameters was retained) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. jenkinsci/stapler#84 created for you ;-) |
||
</st:attribute> | ||
</st:documentation> | ||
<div class="tab${attrs.active?' active':''}"> | ||
<j:choose> | ||
<j:when test="${active}"> | ||
<j:when test="${attrs.active}"> | ||
<input type="radio" id="tab-${tabBarId}-${tabIndex}" name="tab-group-${tabBarId}" checked="checked" /> | ||
</j:when> | ||
<j:otherwise> | ||
<input type="radio" id="tab-${tabBarId}-${tabIndex}" name="tab-group-${tabBarId}" /> | ||
</j:otherwise> | ||
</j:choose> | ||
<a href="${href}" class="${name.equals('+')?'addTab':''}">${name}</a> | ||
<a href="${href}" class="${name.equals('+')?'addTab':''}" title="${attrs.title}">${attrs.name}</a> | ||
</div> | ||
<j:set scope="parent" var="tabIndex" value="${tabIndex+1}" /> | ||
</j:jelly> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: this is the javadoc that applied to the method before my changes as the method would have blown up with a NPE if invoked outside of the
newView.jelly
screen of aViewGroup
as soon asAllView.isInstantiable()
was calledThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps just check
owner == null
and returnall()
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to also check whether the
StaplerRequest
is null.