Skip to content

Commit

Permalink
[JENKINS-69869] Categorize the user properties (#7268)
Browse files Browse the repository at this point in the history
Co-authored-by: Tim Jacomb <[email protected]>
  • Loading branch information
Wadeck and timja authored Jul 11, 2024
1 parent ea07383 commit 6fa26d2
Show file tree
Hide file tree
Showing 71 changed files with 1,873 additions and 152 deletions.
6 changes: 6 additions & 0 deletions core/src/main/java/hudson/model/MyViewsProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import hudson.Extension;
import hudson.Util;
import hudson.model.Descriptor.FormException;
import hudson.model.userproperty.UserPropertyCategory;
import hudson.security.ACL;
import hudson.util.FormValidation;
import hudson.views.MyViewsTabBar;
Expand Down Expand Up @@ -246,6 +247,11 @@ public String getDisplayName() {
public UserProperty newInstance(User user) {
return new MyViewsProperty();
}

@Override
public @NonNull UserPropertyCategory getUserPropertyCategory() {
return UserPropertyCategory.get(UserPropertyCategory.Preferences.class);
}
}

@Override
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/java/hudson/model/PaneStatusProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import static java.lang.String.format;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.model.userproperty.UserPropertyCategory;
import hudson.util.PersistedList;
import java.io.IOException;
import javax.servlet.http.HttpSession;
Expand Down Expand Up @@ -56,6 +58,10 @@ public boolean isEnabled() {
return false;
}

@Override
public @NonNull UserPropertyCategory getUserPropertyCategory() {
return UserPropertyCategory.get(UserPropertyCategory.Invisible.class);
}
}

private static class PaneStatusPropertiesSessionFallback extends PaneStatusProperties {
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/java/hudson/model/TimeZoneProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.Util;
import hudson.model.userproperty.UserPropertyCategory;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
import hudson.util.ListBoxModel.Option;
Expand Down Expand Up @@ -106,6 +107,10 @@ public FormValidation doCheckTimeZoneName(@QueryParameter String timeZoneName) {
}
}

@Override
public @NonNull UserPropertyCategory getUserPropertyCategory() {
return UserPropertyCategory.get(UserPropertyCategory.Account.class);
}
}

@CheckForNull
Expand Down
69 changes: 23 additions & 46 deletions core/src/main/java/hudson/model/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,11 @@
import hudson.XmlFile;
import hudson.init.InitMilestone;
import hudson.init.Initializer;
import hudson.model.Descriptor.FormException;
import hudson.model.listeners.SaveableListener;
import hudson.security.ACL;
import hudson.security.AccessControlled;
import hudson.security.SecurityRealm;
import hudson.security.UserMayOrMayNotExistException2;
import hudson.util.FormApply;
import hudson.util.FormValidation;
import hudson.util.RunList;
import hudson.util.XStream2;
Expand Down Expand Up @@ -77,7 +75,6 @@
import jenkins.security.LastGrantedAuthoritiesProperty;
import jenkins.security.UserDetailsCache;
import jenkins.util.SystemProperties;
import net.sf.json.JSONObject;
import org.jenkinsci.Symbol;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
Expand All @@ -87,7 +84,6 @@
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.interceptor.RequirePOST;
import org.kohsuke.stapler.verb.POST;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
Expand Down Expand Up @@ -342,6 +338,29 @@ public synchronized void addProperty(@NonNull UserProperty p) throws IOException
save();
}

/**
* Expand {@link #addProperty(UserProperty)} for multiple properties to be done at once.
* Expected to be used by the categorized configuration pages to update part of the properties.
* The properties not included in the list will be let untouched.
* It will call the {@link UserProperty#setUser(User)} method and at the end, {@link #save()} once.
*
* @since TODO
*/
public synchronized void addProperties(@NonNull List<UserProperty> multipleProperties) throws IOException {
List<UserProperty> newProperties = new ArrayList<>(this.properties);
for (UserProperty property : multipleProperties) {
UserProperty oldProp = getProperty(property.getClass());
if (oldProp != null) {
newProperties.remove(oldProp);
}
newProperties.add(property);
property.setUser(this);
}

this.properties = newProperties;
this.save();
}

/**
* List of all {@link UserProperty}s exposed primarily for the remoting API.
*/
Expand Down Expand Up @@ -859,48 +878,6 @@ public Api getApi() {
return new Api(this);
}

/**
* Accepts submission from the configuration page.
*/
@POST
public void doConfigSubmit(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, FormException {
checkPermission(Jenkins.ADMINISTER);

JSONObject json = req.getSubmittedForm();
String oldFullName = this.fullName;
fullName = json.getString("fullName");
description = json.getString("description");

List<UserProperty> props = new ArrayList<>();
int i = 0;
for (UserPropertyDescriptor d : UserProperty.all()) {
UserProperty p = getProperty(d.clazz);

JSONObject o = json.optJSONObject("userProperty" + i++);
if (o != null) {
if (p != null) {
p = p.reconfigure(req, o);
} else {
p = d.newInstance(req, o);
}
}

if (p != null) {
p.setUser(this);
props.add(p);
}
}
this.properties = props;

save();

if (oldFullName != null && !oldFullName.equals(this.fullName)) {
UserDetailsCache.get().invalidate(oldFullName);
}

FormApply.success(".").generateResponse(req, rsp, this);
}

/**
* Deletes this user from Hudson.
*/
Expand Down
26 changes: 26 additions & 0 deletions core/src/main/java/hudson/model/UserProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@

package hudson.model;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.DescriptorExtensionList;
import hudson.ExtensionPoint;
import hudson.model.Descriptor.FormException;
import hudson.model.userproperty.UserPropertyCategory;
import java.util.ArrayList;
import java.util.List;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
Expand Down Expand Up @@ -58,6 +62,10 @@ public abstract class UserProperty implements ReconfigurableDescribable<UserProp
*/
protected transient User user;

/**
* This method is used to inform the property about its owner.
* It could be called multiple times, even without change, thus it should be idempotent.
*/
protected void setUser(User u) {
this.user = u;
}
Expand All @@ -75,6 +83,24 @@ public static DescriptorExtensionList<UserProperty, UserPropertyDescriptor> all(
return Jenkins.get().getDescriptorList(UserProperty.class);
}

/**
* Returns all the registered {@link UserPropertyCategory} descriptors for a given category.
*
* @since TODO
*/
public static List<UserPropertyDescriptor> allByCategoryClass(@NonNull Class<? extends UserPropertyCategory> categoryClass) {
DescriptorExtensionList<UserProperty, UserPropertyDescriptor> all = all();

List<UserPropertyDescriptor> onlyForTheCategory = new ArrayList<>(all.size());
for (UserPropertyDescriptor descriptor : all) {
if (descriptor.getUserPropertyCategory().getClass().equals(categoryClass)) {
onlyForTheCategory.add(descriptor);
}
}

return onlyForTheCategory;
}

@Override
public UserProperty reconfigure(StaplerRequest req, JSONObject form) throws FormException {
return form == null ? null : getDescriptor().newInstance(req, form);
Expand Down
53 changes: 53 additions & 0 deletions core/src/main/java/hudson/model/UserPropertyDescriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@

package hudson.model;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.model.userproperty.UserPropertyCategory;
import java.util.Optional;
import org.jenkinsci.Symbol;

/**
* {@link Descriptor} for {@link UserProperty}.
*
Expand Down Expand Up @@ -73,4 +79,51 @@ protected UserPropertyDescriptor() {
public boolean isEnabled() {
return true;
}

/**
* Define the category for this user property descriptor.
*
* @return never null, always the same value for a given instance of {@link Descriptor}.
*
* @since TODO
*/
public @NonNull UserPropertyCategory getUserPropertyCategory() {
// As this method is expected to be overloaded by subclasses
// the logic here is just done to support plugins with older core version
String categoryAsString = this.getUserPropertyCategoryAsString();
if (categoryAsString != null) {
Optional<UserPropertyCategory> firstIfFound = UserPropertyCategory.all().stream()
.filter(cat -> {
Symbol symbolAnnotation = cat.getClass().getAnnotation(Symbol.class);
if (symbolAnnotation != null) {

Check warning on line 98 in core/src/main/java/hudson/model/UserPropertyDescriptor.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 98 is only partially covered, one branch is missing
for (String symbolValue : symbolAnnotation.value()) {
if (symbolValue.equalsIgnoreCase(categoryAsString)) {
return true;
}
}
}
return false;
})
.findFirst();
if (firstIfFound.isPresent()) {

Check warning on line 108 in core/src/main/java/hudson/model/UserPropertyDescriptor.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 108 is only partially covered, one branch is missing
return firstIfFound.get();
}
}
return UserPropertyCategory.get(UserPropertyCategory.Unclassified.class);
}

/**
* Method proposed to prevent plugins to rely on too recent core version
* while keeping the possibility to use the categories.
*
* @deprecated This should only be used when the core requirement is below the version this method was added
*
* @return String name corresponding to the symbol of {@link #getUserPropertyCategory()}
*
* @since TODO
*/
@Deprecated
protected @CheckForNull String getUserPropertyCategoryAsString() {
return null;
}
}
Loading

0 comments on commit 6fa26d2

Please sign in to comment.