25
25
package hudson.model;
27
import com.thoughtworks.xstream.converters.ConversionException;
28
import com.thoughtworks.xstream.io.StreamException;
29
import com.thoughtworks.xstream.io.xml.XppDriver;
27
30
import hudson.DescriptorExtensionList;
28
31
import hudson.Extension;
29
32
import hudson.ExtensionPoint;
33
import hudson.Functions;
30
34
import hudson.Indenter;
31
35
import hudson.Util;
36
import hudson.XmlFile;
32
37
import hudson.model.Descriptor.FormException;
33
38
import hudson.model.Node.Mode;
39
import hudson.model.labels.LabelAtom;
34
40
import hudson.model.labels.LabelAtomPropertyDescriptor;
41
import hudson.scm.ChangeLogSet;
35
42
import hudson.scm.ChangeLogSet.Entry;
36
43
import hudson.search.CollectionSearchIndex;
37
44
import hudson.search.SearchIndexBuilder;
40
47
import hudson.security.Permission;
41
48
import hudson.security.PermissionGroup;
42
49
import hudson.security.PermissionScope;
50
import hudson.tasks.UserAvatarResolver;
43
51
import hudson.util.AlternativeUiTextProvider;
44
52
import hudson.util.AlternativeUiTextProvider.Message;
53
import hudson.util.AtomicFileWriter;
45
54
import hudson.util.DescribableList;
46
55
import hudson.util.DescriptorList;
56
import hudson.util.IOException2;
57
import hudson.util.IOUtils;
47
58
import hudson.util.RunList;
59
import hudson.util.XStream2;
48
60
import hudson.views.ListViewColumn;
49
61
import hudson.widgets.Widget;
50
62
import jenkins.model.Jenkins;
63
import jenkins.util.ProgressiveRendering;
64
import net.sf.json.JSON;
65
import net.sf.json.JSONArray;
66
import net.sf.json.JSONObject;
67
import org.kohsuke.stapler.HttpResponse;
68
import org.kohsuke.stapler.HttpResponses;
69
import org.kohsuke.stapler.Stapler;
51
70
import org.kohsuke.stapler.StaplerRequest;
52
71
import org.kohsuke.stapler.StaplerResponse;
72
import org.kohsuke.stapler.WebMethod;
53
73
import org.kohsuke.stapler.export.Exported;
54
74
import org.kohsuke.stapler.export.ExportedBean;
55
75
import org.kohsuke.stapler.interceptor.RequirePOST;
57
77
import javax.servlet.ServletException;
78
import javax.servlet.http.HttpServletResponse;
79
import javax.xml.transform.Source;
80
import javax.xml.transform.Transformer;
81
import javax.xml.transform.TransformerException;
82
import javax.xml.transform.TransformerFactory;
83
import javax.xml.transform.stream.StreamResult;
84
import javax.xml.transform.stream.StreamSource;
85
import java.io.BufferedInputStream;
86
import java.io.ByteArrayInputStream;
87
import java.io.FileInputStream;
58
88
import java.io.IOException;
89
import java.io.InputStream;
90
import java.io.StringWriter;
59
91
import java.util.ArrayList;
60
92
import java.util.Arrays;
61
93
import java.util.Calendar;
449
490
result.addAll(getOwnerViewActions());
450
491
synchronized (this) {
451
492
if (transientActions == null) {
452
transientActions = TransientViewActionFactory.createAllFor(this);
493
updateTransientActions();
454
495
result.addAll(transientActions);
500
public synchronized void updateTransientActions() {
501
transientActions = TransientViewActionFactory.createAllFor(this);
459
504
public Object getDynamic(String token) {
460
505
for (Action a : getActions()) {
461
506
String url = a.getUrlName();
731
* Variant of {@link People} which can be displayed progressively, since it may be slow.
734
public static final class AsynchPeople extends ProgressiveRendering { // JENKINS-15206
736
private final Collection<TopLevelItem> items;
737
private final User unknown;
738
private final Map<User,UserInfo> users = new HashMap<User,UserInfo>();
739
private final Set<User> modified = new HashSet<User>();
740
private final String iconSize;
741
public final ModelObject parent;
743
/** @see Jenkins#getAsynchPeople} */
744
public AsynchPeople(Jenkins parent) {
745
this.parent = parent;
746
items = parent.getItems();
747
unknown = User.getUnknown();
750
/** @see View#getAsynchPeople */
751
public AsynchPeople(View parent) {
752
this.parent = parent;
753
items = parent.getItems();
758
StaplerRequest req = Stapler.getCurrentRequest();
759
iconSize = req != null ? Functions.getCookie(req, "iconSize", "32x32") : "32x32";
762
@Override protected void compute() throws Exception {
764
for (Item item : items) {
765
for (Job<?,?> job : item.getAllJobs()) {
766
if (job instanceof AbstractProject) {
767
AbstractProject<?,?> p = (AbstractProject) job;
768
RunList<? extends AbstractBuild<?,?>> builds = p.getBuilds();
770
for (AbstractBuild<?,?> build : builds) {
774
for (ChangeLogSet.Entry entry : build.getChangeSet()) {
775
User user = entry.getAuthor();
776
synchronized (this) {
777
UserInfo info = users.get(user);
779
users.put(user, new UserInfo(user, p, build.getTimestamp()));
781
} else if (info.getLastChange().before(build.getTimestamp())) {
783
info.lastChange = build.getTimestamp();
789
progress((itemCount + 1.0 * buildCount / builds.size()) / (items.size() + 1));
794
progress(1.0 * itemCount / (items.size() + /* handling User.getAll */1));
796
if (unknown != null) {
800
for (User u : User.getAll()) { // XXX nice to have a method to iterate these lazily
804
if (!users.containsKey(u)) {
805
synchronized (this) {
806
users.put(u, new UserInfo(u, null, null));
814
@Override protected synchronized JSON data() {
815
JSONArray r = new JSONArray();
816
for (User u : modified) {
817
UserInfo i = users.get(u);
818
JSONObject entry = new JSONObject().
819
accumulate("id", u.getId()).
820
accumulate("fullName", u.getFullName()).
821
accumulate("url", u.getUrl()).
822
accumulate("avatar", UserAvatarResolver.resolve(u, iconSize)).
823
accumulate("timeSortKey", i.getTimeSortKey()).
824
accumulate("lastChangeTimeString", i.getLastChangeTimeString());
825
AbstractProject<?,?> p = i.getProject();
827
entry.accumulate("projectUrl", p.getUrl()).accumulate("projectFullDisplayName", p.getFullDisplayName());
835
public Api getApi() {
836
return new Api(parent instanceof Jenkins ? new People((Jenkins) parent) : new People((View) parent));
678
841
void addDisplayNamesToSearchIndex(SearchIndexBuilder sib, Collection<TopLevelItem> items) {
679
842
for(TopLevelItem item : items) {
974
* Accepts <tt>config.xml</tt> submission, as well as serve it.
976
@WebMethod(name = "config.xml")
977
public HttpResponse doConfigDotXml(StaplerRequest req) throws IOException {
978
if (req.getMethod().equals("GET")) {
980
checkPermission(READ);
981
return new HttpResponse() {
982
public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
983
rsp.setContentType("application/xml");
984
// pity we don't have a handy way to clone Jenkins.XSTREAM to temp add the omit Field
985
XStream2 xStream2 = new XStream2();
986
xStream2.omitField(View.class, "owner");
987
rsp.getOutputStream().write("<?xml version='1.0' encoding='UTF-8'?>\n".getBytes("UTF-8"));
988
xStream2.toXML(this, rsp.getOutputStream());
992
if (req.getMethod().equals("POST")) {
994
updateByXml((Source)new StreamSource(req.getReader()));
995
return HttpResponses.ok();
999
return HttpResponses.error(SC_BAD_REQUEST, "Unexpected request method " + req.getMethod());
1003
* Updates Job by its XML definition.
1005
public void updateByXml(Source source) throws IOException {
1006
checkPermission(CONFIGURE);
1007
StringWriter out = new StringWriter();
1009
// this allows us to use UTF-8 for storing data,
1010
// plus it checks any well-formedness issue in the submitted
1012
Transformer t = TransformerFactory.newInstance()
1015
new StreamResult(out));
1017
} catch (TransformerException e) {
1018
throw new IOException2("Failed to persist configuration.xml", e);
1021
// try to reflect the changes by reloading
1022
InputStream in = new BufferedInputStream(new ByteArrayInputStream(out.toString().getBytes("UTF-8")));
1024
Jenkins.XSTREAM.unmarshal(new XppDriver().createReader(in), this);
1025
} catch (StreamException e) {
1026
throw new IOException2("Unable to read",e);
1027
} catch(ConversionException e) {
1028
throw new IOException2("Unable to read",e);
1029
} catch(Error e) {// mostly reflection errors
1030
throw new IOException2("Unable to read",e);
810
1038
* A list of available view types.
811
1039
* @deprecated as of 1.286
812
1040
* Use {@link #all()} for read access, and use {@link Extension} for registration.
851
1079
public static View create(StaplerRequest req, StaplerResponse rsp, ViewGroup owner)
852
1080
throws FormException, IOException, ServletException {
1081
String requestContentType = req.getContentType();
1082
if(requestContentType==null)
1083
throw new Failure("No Content-Type header set");
1085
boolean isXmlSubmission = requestContentType.startsWith("application/xml") || requestContentType.startsWith("text/xml");
853
1087
String name = req.getParameter("name");
854
1088
checkGoodName(name);
855
1089
if(owner.getView(name)!=null)
856
1090
throw new FormException(Messages.Hudson_ViewAlreadyExists(name),"name");
858
1092
String mode = req.getParameter("mode");
859
if (mode==null || mode.length()==0)
860
throw new FormException(Messages.View_MissingMode(),"mode");
1093
if (mode==null || mode.length()==0) {
1094
if(isXmlSubmission) {
1096
v = createViewFromXML(name, req.getInputStream());
1098
rsp.setStatus(HttpServletResponse.SC_OK);
1101
throw new FormException(Messages.View_MissingMode(),"mode");
862
1104
// create a view
863
1105
View v = all().findByName(mode).newInstance(req,req.getSubmittedForm());
1114
public static View createViewFromXML(String name, InputStream xml) throws IOException {
1115
InputStream in = new BufferedInputStream(xml);
1117
View v = (View) Jenkins.XSTREAM.fromXML(in);
1120
} catch(StreamException e) {
1121
throw new IOException2("Unable to read",e);
1122
} catch(ConversionException e) {
1123
throw new IOException2("Unable to read",e);
1124
} catch(Error e) {// mostly reflection errors
1125
throw new IOException2("Unable to read",e);
872
1131
public static class PropertyList extends DescribableList<ViewProperty,ViewPropertyDescriptor> {
873
1132
private PropertyList(View owner) {