Skip to content
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-18884] Remove 'People' view #9060

Merged
merged 5 commits into from Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
337 changes: 0 additions & 337 deletions core/src/main/java/hudson/model/View.java
Expand Up @@ -41,15 +41,13 @@
import hudson.init.Initializer;
import hudson.model.Descriptor.FormException;
import hudson.model.listeners.ItemListener;
import hudson.scm.ChangeLogSet;
import hudson.search.CollectionSearchIndex;
import hudson.search.SearchIndexBuilder;
import hudson.security.ACL;
import hudson.security.AccessControlled;
import hudson.security.Permission;
import hudson.security.PermissionGroup;
import hudson.security.PermissionScope;
import hudson.tasks.UserAvatarResolver;
import hudson.util.AlternativeUiTextProvider;
import hudson.util.AlternativeUiTextProvider.Message;
import hudson.util.DescribableList;
Expand All @@ -70,18 +68,15 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
Expand All @@ -98,19 +93,13 @@
import jenkins.model.item_category.Categories;
import jenkins.model.item_category.Category;
import jenkins.model.item_category.ItemCategory;
import jenkins.scm.RunWithSCM;
import jenkins.security.stapler.StaplerAccessibleType;
import jenkins.util.ProgressiveRendering;
import jenkins.util.xml.XMLUtils;
import jenkins.widgets.HasWidgets;
import net.sf.json.JSON;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.jenkins.ui.icon.Icon;
import org.jenkins.ui.icon.IconSet;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
Expand Down Expand Up @@ -635,332 +624,6 @@ public ACL getACL() {
@Deprecated
public void onJobRenamed(Item item, String oldName, String newName) {}

@ExportedBean(defaultVisibility = 2)
public static final class UserInfo implements Comparable<UserInfo> {
private final User user;
/**
* When did this user made a last commit on any of our projects? Can be null.
*/
private Calendar lastChange;
/**
* Which project did this user commit? Can be null.
*/
private Job<?, ?> project;

/** @see UserAvatarResolver */
String avatar;

UserInfo(User user, Job<?, ?> p, Calendar lastChange) {
this.user = user;
this.project = p;
this.lastChange = lastChange;
}

@Exported
public User getUser() {
return user;
}

@Exported
public Calendar getLastChange() {
return lastChange;
}

@Deprecated
public AbstractProject getProject() {
return project instanceof AbstractProject ? (AbstractProject) project : null;
}

@Exported(name = "project")
public Job<?, ?> getJob() {
return project;
}

/**
* Returns a human-readable string representation of when this user was last active.
*/
public String getLastChangeTimeString() {
if (lastChange == null) return "N/A";
long duration = new GregorianCalendar().getTimeInMillis() - ordinal();
return Util.getTimeSpanString(duration);
}

public String getTimeSortKey() {
if (lastChange == null) return "-";
return Util.XS_DATETIME_FORMATTER.format(lastChange.getTime());
}

@Override
public int compareTo(UserInfo that) {
long rhs = that.ordinal();
long lhs = this.ordinal();
return Long.compare(rhs, lhs);
}

private long ordinal() {
if (lastChange == null) return 0;
return lastChange.getTimeInMillis();
}
}

/**
* Does this {@link View} has any associated user information recorded?
* @deprecated Potentially very expensive call; do not use from Jelly views.
*/
@Deprecated
public boolean hasPeople() {
return People.isApplicable(getItems());
}

/**
* Gets the users that show up in the changelog of this job collection.
*/
public People getPeople() {
return new People(this);
}

/**
* @since 1.484
*/
public AsynchPeople getAsynchPeople() {
return new AsynchPeople(this);
}

@ExportedBean
@StaplerAccessibleType
public static final class People {
@Exported
public final List<UserInfo> users;

public final ModelObject parent;

public People(Jenkins parent) {
this.parent = parent;
// for Hudson, really load all users
Map<User, UserInfo> users = getUserInfo(parent.getItems());
User unknown = User.getUnknown();
for (User u : User.getAll()) {
if (u == unknown) continue; // skip the special 'unknown' user
if (!users.containsKey(u))
users.put(u, new UserInfo(u, null, null));
}
this.users = toList(users);
}

public People(View parent) {
this.parent = parent;
this.users = toList(getUserInfo(parent.getItems()));
}

private Map<User, UserInfo> getUserInfo(Collection<? extends Item> items) {
Map<User, UserInfo> users = new HashMap<>();
for (Item item : items) {
for (Job<?, ?> job : item.getAllJobs()) {
RunList<? extends Run<?, ?>> runs = job.getBuilds();
for (Run<?, ?> r : runs) {
if (r instanceof RunWithSCM) {
RunWithSCM<?, ?> runWithSCM = (RunWithSCM<?, ?>) r;

for (ChangeLogSet<? extends ChangeLogSet.Entry> c : runWithSCM.getChangeSets()) {
for (ChangeLogSet.Entry entry : c) {
User user = entry.getAuthor();

UserInfo info = users.get(user);
if (info == null)
users.put(user, new UserInfo(user, job, r.getTimestamp()));
else if (info.getLastChange().before(r.getTimestamp())) {
info.project = job;
info.lastChange = r.getTimestamp();
}
}
}
}
}
}
}
return users;
}

private List<UserInfo> toList(Map<User, UserInfo> users) {
ArrayList<UserInfo> list = new ArrayList<>(users.values());
Collections.sort(list);
return Collections.unmodifiableList(list);
}

public Api getApi() {
return new Api(this);
}

/**
* @deprecated Potentially very expensive call; do not use from Jelly views.
*/
@Deprecated
public static boolean isApplicable(Collection<? extends Item> items) {
for (Item item : items) {
for (Job job : item.getAllJobs()) {
RunList<? extends Run<?, ?>> runs = job.getBuilds();

for (Run<?, ?> r : runs) {
if (r instanceof RunWithSCM) {
RunWithSCM<?, ?> runWithSCM = (RunWithSCM<?, ?>) r;
for (ChangeLogSet<? extends ChangeLogSet.Entry> c : runWithSCM.getChangeSets()) {
for (ChangeLogSet.Entry entry : c) {
User user = entry.getAuthor();
if (user != null)
return true;
}
}
}
}
}
}
return false;
}
}

/**
* Variant of {@link People} which can be displayed progressively, since it may be slow.
* @since 1.484
*/
public static final class AsynchPeople extends ProgressiveRendering { // JENKINS-15206

private final Collection<TopLevelItem> items;
private final User unknown;
private final Map<User, UserInfo> users = new HashMap<>();
private final Set<User> modified = new HashSet<>();
private final String iconSize;
public final ModelObject parent;

/** @see Jenkins#getAsynchPeople */
public AsynchPeople(Jenkins parent) {
this.parent = parent;
items = parent.getItems();
unknown = User.getUnknown();
}

/** @see View#getAsynchPeople */
public AsynchPeople(View parent) {
this.parent = parent;
items = parent.getItems();
unknown = null;
}

{
StaplerRequest req = Stapler.getCurrentRequest();
iconSize = req != null ? Functions.validateIconSize(Functions.getCookie(req, "iconSize", "32x32")) : "32x32";
}

@Override protected void compute() throws Exception {
int itemCount = 0;
for (Item item : items) {
for (Job<?, ?> job : item.getAllJobs()) {
RunList<? extends Run<?, ?>> builds = job.getBuilds();
int buildCount = 0;
for (Run<?, ?> r : builds) {
if (canceled()) {
return;
}
if (!(r instanceof RunWithSCM)) {
continue;
}

RunWithSCM<?, ?> runWithSCM = (RunWithSCM<?, ?>) r;
for (ChangeLogSet<? extends ChangeLogSet.Entry> c : runWithSCM.getChangeSets()) {
for (ChangeLogSet.Entry entry : c) {
User user = entry.getAuthor();
UserInfo info = users.get(user);
if (info == null) {
UserInfo userInfo = new UserInfo(user, job, r.getTimestamp());
userInfo.avatar = UserAvatarResolver.resolveOrNull(user, iconSize);
synchronized (this) {
users.put(user, userInfo);
modified.add(user);
}
} else if (info.getLastChange().before(r.getTimestamp())) {
synchronized (this) {
info.project = job;
info.lastChange = r.getTimestamp();
modified.add(user);
}
}
}
}
// TODO consider also adding the user of the UserCause when applicable
buildCount++;
// TODO this defeats lazy-loading. Should rather do a breadth-first search, as in hudson.plugins.view.dashboard.builds.LatestBuilds
// (though currently there is no quick implementation of RunMap.size() ~ idOnDisk.size(), which would be needed for proper progress)
progress((itemCount + 1.0 * buildCount / builds.size()) / (items.size() + 1));
}
}
itemCount++;
progress(1.0 * itemCount / (items.size() + /* handling User.getAll */1));
}
if (unknown != null) {
if (canceled()) {
return;
}
for (User u : User.getAll()) { // TODO nice to have a method to iterate these lazily
if (canceled()) {
return;
}
if (u == unknown) {
continue;
}
if (!users.containsKey(u)) {
UserInfo userInfo = new UserInfo(u, null, null);
userInfo.avatar = UserAvatarResolver.resolveOrNull(u, iconSize);
synchronized (this) {
users.put(u, userInfo);
modified.add(u);
}
}
}
}
}

@NonNull
@Override protected synchronized JSON data() {
JSONArray r = new JSONArray();
for (User u : modified) {
UserInfo i = users.get(u);
JSONObject entry = new JSONObject().
accumulate("id", u.getId()).
accumulate("fullName", u.getFullName()).
accumulate("url", u.getUrl() + "/").
accumulate("avatar", i.avatar != null ? i.avatar : Stapler.getCurrentRequest().getContextPath() + Functions.getResourcePath() + "/images/svgs/person.svg").
accumulate("timeSortKey", i.getTimeSortKey()).
accumulate("lastChangeTimeString", i.getLastChangeTimeString());
Job<?, ?> p = i.getJob();
if (p != null) {
entry.accumulate("projectUrl", p.getUrl()).accumulate("projectFullDisplayName", p.getFullDisplayName());
}
r.add(entry);
}
modified.clear();
return r;
}

public Api getApi() {
return new Api(new People());
}

/** JENKINS-16397 workaround */
@Restricted(NoExternalUse.class)
@ExportedBean
public final class People {

private View.People people;

@Exported public synchronized List<UserInfo> getUsers() {
if (people == null) {
people = parent instanceof Jenkins ? new View.People((Jenkins) parent) : new View.People((View) parent);
}
return people.users;
}
}

}

void addDisplayNamesToSearchIndex(SearchIndexBuilder sib, Collection<TopLevelItem> items) {
for (TopLevelItem item : items) {

Expand Down