You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

EditRepositoryPage.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. /*
  2. * Copyright 2011 gitblit.com.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.gitblit.wicket.pages;
  17. import java.text.MessageFormat;
  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collections;
  21. import java.util.HashSet;
  22. import java.util.Iterator;
  23. import java.util.LinkedHashMap;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.Set;
  27. import org.apache.wicket.PageParameters;
  28. import org.apache.wicket.behavior.SimpleAttributeModifier;
  29. import org.apache.wicket.extensions.markup.html.form.palette.Palette;
  30. import org.apache.wicket.markup.html.WebMarkupContainer;
  31. import org.apache.wicket.markup.html.basic.Label;
  32. import org.apache.wicket.markup.html.form.Button;
  33. import org.apache.wicket.markup.html.form.CheckBox;
  34. import org.apache.wicket.markup.html.form.DropDownChoice;
  35. import org.apache.wicket.markup.html.form.Form;
  36. import org.apache.wicket.markup.html.form.IChoiceRenderer;
  37. import org.apache.wicket.markup.html.form.Radio;
  38. import org.apache.wicket.markup.html.form.RadioGroup;
  39. import org.apache.wicket.markup.html.form.TextField;
  40. import org.apache.wicket.markup.html.list.ListItem;
  41. import org.apache.wicket.markup.html.list.ListView;
  42. import org.apache.wicket.model.CompoundPropertyModel;
  43. import org.apache.wicket.model.IModel;
  44. import org.apache.wicket.model.Model;
  45. import org.apache.wicket.model.util.CollectionModel;
  46. import org.apache.wicket.model.util.ListModel;
  47. import com.gitblit.Constants;
  48. import com.gitblit.Constants.AccessRestrictionType;
  49. import com.gitblit.Constants.AuthorizationControl;
  50. import com.gitblit.Constants.FederationStrategy;
  51. import com.gitblit.GitBlit;
  52. import com.gitblit.GitBlitException;
  53. import com.gitblit.Keys;
  54. import com.gitblit.models.RepositoryModel;
  55. import com.gitblit.models.UserModel;
  56. import com.gitblit.utils.ArrayUtils;
  57. import com.gitblit.utils.StringUtils;
  58. import com.gitblit.wicket.GitBlitWebSession;
  59. import com.gitblit.wicket.StringChoiceRenderer;
  60. import com.gitblit.wicket.WicketUtils;
  61. import com.gitblit.wicket.panels.BulletListPanel;
  62. public class EditRepositoryPage extends RootSubPage {
  63. private final boolean isCreate;
  64. private boolean isAdmin;
  65. private IModel<String> mailingLists;
  66. public EditRepositoryPage() {
  67. // create constructor
  68. super();
  69. isCreate = true;
  70. RepositoryModel model = new RepositoryModel();
  71. String restriction = GitBlit.getString(Keys.git.defaultAccessRestriction, null);
  72. model.accessRestriction = AccessRestrictionType.fromName(restriction);
  73. String authorization = GitBlit.getString(Keys.git.defaultAuthorizationControl, null);
  74. model.authorizationControl = AuthorizationControl.fromName(authorization);
  75. setupPage(model);
  76. }
  77. public EditRepositoryPage(PageParameters params) {
  78. // edit constructor
  79. super(params);
  80. isCreate = false;
  81. String name = WicketUtils.getRepositoryName(params);
  82. RepositoryModel model = GitBlit.self().getRepositoryModel(name);
  83. setupPage(model);
  84. }
  85. protected void setupPage(final RepositoryModel repositoryModel) {
  86. // ensure this user can create or edit this repository
  87. checkPermissions(repositoryModel);
  88. List<String> indexedBranches = new ArrayList<String>();
  89. List<String> federationSets = new ArrayList<String>();
  90. List<String> repositoryUsers = new ArrayList<String>();
  91. List<String> repositoryTeams = new ArrayList<String>();
  92. List<String> preReceiveScripts = new ArrayList<String>();
  93. List<String> postReceiveScripts = new ArrayList<String>();
  94. if (isCreate) {
  95. super.setupPage(getString("gb.newRepository"), "");
  96. } else {
  97. super.setupPage(getString("gb.edit"), repositoryModel.name);
  98. if (repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE)) {
  99. repositoryUsers.addAll(GitBlit.self().getRepositoryUsers(repositoryModel));
  100. repositoryTeams.addAll(GitBlit.self().getRepositoryTeams(repositoryModel));
  101. Collections.sort(repositoryUsers);
  102. }
  103. federationSets.addAll(repositoryModel.federationSets);
  104. if (!ArrayUtils.isEmpty(repositoryModel.indexedBranches)) {
  105. indexedBranches.addAll(repositoryModel.indexedBranches);
  106. }
  107. }
  108. final String oldName = repositoryModel.name;
  109. // users palette
  110. final Palette<String> usersPalette = new Palette<String>("users", new ListModel<String>(
  111. repositoryUsers), new CollectionModel<String>(GitBlit.self().getAllUsernames()),
  112. new StringChoiceRenderer(), 10, false);
  113. // teams palette
  114. final Palette<String> teamsPalette = new Palette<String>("teams", new ListModel<String>(
  115. repositoryTeams), new CollectionModel<String>(GitBlit.self().getAllTeamnames()),
  116. new StringChoiceRenderer(), 8, false);
  117. // indexed local branches palette
  118. List<String> allLocalBranches = new ArrayList<String>();
  119. allLocalBranches.add(Constants.DEFAULT_BRANCH);
  120. allLocalBranches.addAll(repositoryModel.getLocalBranches());
  121. boolean luceneEnabled = GitBlit.getBoolean(Keys.web.allowLuceneIndexing, true);
  122. final Palette<String> indexedBranchesPalette = new Palette<String>("indexedBranches", new ListModel<String>(
  123. indexedBranches), new CollectionModel<String>(allLocalBranches),
  124. new StringChoiceRenderer(), 8, false);
  125. indexedBranchesPalette.setEnabled(luceneEnabled);
  126. // federation sets palette
  127. List<String> sets = GitBlit.getStrings(Keys.federation.sets);
  128. final Palette<String> federationSetsPalette = new Palette<String>("federationSets",
  129. new ListModel<String>(federationSets), new CollectionModel<String>(sets),
  130. new StringChoiceRenderer(), 8, false);
  131. // pre-receive palette
  132. if (!ArrayUtils.isEmpty(repositoryModel.preReceiveScripts)) {
  133. preReceiveScripts.addAll(repositoryModel.preReceiveScripts);
  134. }
  135. final Palette<String> preReceivePalette = new Palette<String>("preReceiveScripts",
  136. new ListModel<String>(preReceiveScripts), new CollectionModel<String>(GitBlit
  137. .self().getPreReceiveScriptsUnused(repositoryModel)),
  138. new StringChoiceRenderer(), 12, true);
  139. // post-receive palette
  140. if (!ArrayUtils.isEmpty(repositoryModel.postReceiveScripts)) {
  141. postReceiveScripts.addAll(repositoryModel.postReceiveScripts);
  142. }
  143. final Palette<String> postReceivePalette = new Palette<String>("postReceiveScripts",
  144. new ListModel<String>(postReceiveScripts), new CollectionModel<String>(GitBlit
  145. .self().getPostReceiveScriptsUnused(repositoryModel)),
  146. new StringChoiceRenderer(), 12, true);
  147. // custom fields
  148. final Map<String, String> customFieldsMap = GitBlit.getMap(Keys.groovy.customFields);
  149. List<String> customKeys = new ArrayList<String>(customFieldsMap.keySet());
  150. final ListView<String> customFieldsListView = new ListView<String>("customFieldsListView", customKeys) {
  151. private static final long serialVersionUID = 1L;
  152. @Override
  153. protected void populateItem(ListItem<String> item) {
  154. String key = item.getModelObject();
  155. item.add(new Label("customFieldLabel", customFieldsMap.get(key)));
  156. String value = "";
  157. if (repositoryModel.customFields != null && repositoryModel.customFields.containsKey(key)) {
  158. value = repositoryModel.customFields.get(key);
  159. }
  160. TextField<String> field = new TextField<String>("customFieldValue", new Model<String>(value));
  161. item.add(field);
  162. }
  163. };
  164. customFieldsListView.setReuseItems(true);
  165. CompoundPropertyModel<RepositoryModel> model = new CompoundPropertyModel<RepositoryModel>(
  166. repositoryModel);
  167. Form<RepositoryModel> form = new Form<RepositoryModel>("editForm", model) {
  168. private static final long serialVersionUID = 1L;
  169. @Override
  170. protected void onSubmit() {
  171. try {
  172. // confirm a repository name was entered
  173. if (StringUtils.isEmpty(repositoryModel.name)) {
  174. error(getString("gb.pleaseSetRepositoryName"));
  175. return;
  176. }
  177. // automatically convert backslashes to forward slashes
  178. repositoryModel.name = repositoryModel.name.replace('\\', '/');
  179. // Automatically replace // with /
  180. repositoryModel.name = repositoryModel.name.replace("//", "/");
  181. // prohibit folder paths
  182. if (repositoryModel.name.startsWith("/")) {
  183. error(getString("gb.illegalLeadingSlash"));
  184. return;
  185. }
  186. if (repositoryModel.name.startsWith("../")) {
  187. error(getString("gb.illegalRelativeSlash"));
  188. return;
  189. }
  190. if (repositoryModel.name.contains("/../")) {
  191. error(getString("gb.illegalRelativeSlash"));
  192. return;
  193. }
  194. if (repositoryModel.name.endsWith("/")) {
  195. repositoryModel.name = repositoryModel.name.substring(0, repositoryModel.name.length() - 1);
  196. }
  197. // confirm valid characters in repository name
  198. Character c = StringUtils.findInvalidCharacter(repositoryModel.name);
  199. if (c != null) {
  200. error(MessageFormat.format(getString("gb.illegalCharacterRepositoryName"),
  201. c));
  202. return;
  203. }
  204. // confirm access restriction selection
  205. if (repositoryModel.accessRestriction == null) {
  206. error(getString("gb.selectAccessRestriction"));
  207. return;
  208. }
  209. // confirm federation strategy selection
  210. if (repositoryModel.federationStrategy == null) {
  211. error(getString("gb.selectFederationStrategy"));
  212. return;
  213. }
  214. // save federation set preferences
  215. if (repositoryModel.federationStrategy.exceeds(FederationStrategy.EXCLUDE)) {
  216. repositoryModel.federationSets.clear();
  217. Iterator<String> sets = federationSetsPalette.getSelectedChoices();
  218. while (sets.hasNext()) {
  219. repositoryModel.federationSets.add(sets.next());
  220. }
  221. }
  222. // set mailing lists
  223. String ml = mailingLists.getObject();
  224. if (!StringUtils.isEmpty(ml)) {
  225. Set<String> list = new HashSet<String>();
  226. for (String address : ml.split("(,|\\s)")) {
  227. if (StringUtils.isEmpty(address)) {
  228. continue;
  229. }
  230. list.add(address.toLowerCase());
  231. }
  232. repositoryModel.mailingLists = new ArrayList<String>(list);
  233. }
  234. // indexed branches
  235. List<String> indexedBranches = new ArrayList<String>();
  236. Iterator<String> branches = indexedBranchesPalette.getSelectedChoices();
  237. while (branches.hasNext()) {
  238. indexedBranches.add(branches.next());
  239. }
  240. repositoryModel.indexedBranches = indexedBranches;
  241. // pre-receive scripts
  242. List<String> preReceiveScripts = new ArrayList<String>();
  243. Iterator<String> pres = preReceivePalette.getSelectedChoices();
  244. while (pres.hasNext()) {
  245. preReceiveScripts.add(pres.next());
  246. }
  247. repositoryModel.preReceiveScripts = preReceiveScripts;
  248. // post-receive scripts
  249. List<String> postReceiveScripts = new ArrayList<String>();
  250. Iterator<String> post = postReceivePalette.getSelectedChoices();
  251. while (post.hasNext()) {
  252. postReceiveScripts.add(post.next());
  253. }
  254. repositoryModel.postReceiveScripts = postReceiveScripts;
  255. // custom fields
  256. repositoryModel.customFields = new LinkedHashMap<String, String>();
  257. for (int i = 0; i < customFieldsListView.size(); i++) {
  258. ListItem<String> child = (ListItem<String>) customFieldsListView.get(i);
  259. String key = child.getModelObject();
  260. TextField<String> field = (TextField<String>) child.get("customFieldValue");
  261. String value = field.getValue();
  262. repositoryModel.customFields.put(key, value);
  263. }
  264. // save the repository
  265. GitBlit.self().updateRepositoryModel(oldName, repositoryModel, isCreate);
  266. // repository access
  267. if (repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE)) {
  268. // save the user access list
  269. Iterator<String> users = usersPalette.getSelectedChoices();
  270. List<String> repositoryUsers = new ArrayList<String>();
  271. while (users.hasNext()) {
  272. repositoryUsers.add(users.next());
  273. }
  274. // ensure the owner is added to the user list
  275. if (repositoryModel.owner != null
  276. && !repositoryUsers.contains(repositoryModel.owner)) {
  277. repositoryUsers.add(repositoryModel.owner);
  278. }
  279. GitBlit.self().setRepositoryUsers(repositoryModel, repositoryUsers);
  280. // save the team access list
  281. Iterator<String> teams = teamsPalette.getSelectedChoices();
  282. List<String> repositoryTeams = new ArrayList<String>();
  283. while (teams.hasNext()) {
  284. repositoryTeams.add(teams.next());
  285. }
  286. GitBlit.self().setRepositoryTeams(repositoryModel, repositoryTeams);
  287. }
  288. } catch (GitBlitException e) {
  289. error(e.getMessage());
  290. return;
  291. }
  292. setRedirect(false);
  293. setResponsePage(RepositoriesPage.class);
  294. }
  295. };
  296. // do not let the browser pre-populate these fields
  297. form.add(new SimpleAttributeModifier("autocomplete", "off"));
  298. // field names reflective match RepositoryModel fields
  299. form.add(new TextField<String>("name").setEnabled(isCreate || isAdmin));
  300. form.add(new TextField<String>("description"));
  301. form.add(new DropDownChoice<String>("owner", GitBlit.self().getAllUsernames())
  302. .setEnabled(GitBlitWebSession.get().canAdmin()));
  303. form.add(new CheckBox("allowForks"));
  304. form.add(new DropDownChoice<AccessRestrictionType>("accessRestriction", Arrays
  305. .asList(AccessRestrictionType.values()), new AccessRestrictionRenderer()));
  306. form.add(new CheckBox("isFrozen"));
  307. // TODO enable origin definition
  308. form.add(new TextField<String>("origin").setEnabled(false/* isCreate */));
  309. // allow relinking HEAD to a branch or tag other than master on edit repository
  310. List<String> availableRefs = new ArrayList<String>();
  311. if (!ArrayUtils.isEmpty(repositoryModel.availableRefs)) {
  312. availableRefs.addAll(repositoryModel.availableRefs);
  313. }
  314. form.add(new DropDownChoice<String>("HEAD", availableRefs).setEnabled(availableRefs.size() > 0));
  315. // federation strategies - remove ORIGIN choice if this repository has
  316. // no origin.
  317. List<FederationStrategy> federationStrategies = new ArrayList<FederationStrategy>(
  318. Arrays.asList(FederationStrategy.values()));
  319. if (StringUtils.isEmpty(repositoryModel.origin)) {
  320. federationStrategies.remove(FederationStrategy.FEDERATE_ORIGIN);
  321. }
  322. form.add(new DropDownChoice<FederationStrategy>("federationStrategy", federationStrategies,
  323. new FederationTypeRenderer()));
  324. form.add(new CheckBox("useTickets"));
  325. form.add(new CheckBox("useDocs"));
  326. form.add(new CheckBox("showRemoteBranches"));
  327. form.add(new CheckBox("showReadme"));
  328. form.add(new CheckBox("skipSizeCalculation"));
  329. form.add(new CheckBox("skipSummaryMetrics"));
  330. mailingLists = new Model<String>(ArrayUtils.isEmpty(repositoryModel.mailingLists) ? ""
  331. : StringUtils.flattenStrings(repositoryModel.mailingLists, " "));
  332. form.add(new TextField<String>("mailingLists", mailingLists));
  333. form.add(indexedBranchesPalette);
  334. RadioGroup<AuthorizationControl> group = new RadioGroup<AuthorizationControl>("authorizationControl");
  335. Radio<AuthorizationControl> allowAuthenticated = new Radio<AuthorizationControl>("allowAuthenticated", new Model<AuthorizationControl>(AuthorizationControl.AUTHENTICATED));
  336. Radio<AuthorizationControl> allowNamed = new Radio<AuthorizationControl>("allowNamed", new Model<AuthorizationControl>(AuthorizationControl.NAMED));
  337. group.add(allowAuthenticated);
  338. group.add(allowNamed);
  339. form.add(group);
  340. form.add(usersPalette);
  341. form.add(teamsPalette);
  342. form.add(federationSetsPalette);
  343. form.add(preReceivePalette);
  344. form.add(new BulletListPanel("inheritedPreReceive", getString("gb.inherited"), GitBlit.self()
  345. .getPreReceiveScriptsInherited(repositoryModel)));
  346. form.add(postReceivePalette);
  347. form.add(new BulletListPanel("inheritedPostReceive", getString("gb.inherited"), GitBlit.self()
  348. .getPostReceiveScriptsInherited(repositoryModel)));
  349. WebMarkupContainer customFieldsSection = new WebMarkupContainer("customFieldsSection");
  350. customFieldsSection.add(customFieldsListView);
  351. form.add(customFieldsSection.setVisible(!GitBlit.getString(Keys.groovy.customFields, "").isEmpty()));
  352. form.add(new Button("save"));
  353. Button cancel = new Button("cancel") {
  354. private static final long serialVersionUID = 1L;
  355. @Override
  356. public void onSubmit() {
  357. setResponsePage(RepositoriesPage.class);
  358. }
  359. };
  360. cancel.setDefaultFormProcessing(false);
  361. form.add(cancel);
  362. add(form);
  363. }
  364. /**
  365. * Unfortunately must repeat part of AuthorizaitonStrategy here because that
  366. * mechanism does not take PageParameters into consideration, only page
  367. * instantiation.
  368. *
  369. * Repository Owners should be able to edit their repository.
  370. */
  371. private void checkPermissions(RepositoryModel model) {
  372. boolean authenticateAdmin = GitBlit.getBoolean(Keys.web.authenticateAdminPages, true);
  373. boolean allowAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, true);
  374. GitBlitWebSession session = GitBlitWebSession.get();
  375. UserModel user = session.getUser();
  376. if (allowAdmin) {
  377. if (authenticateAdmin) {
  378. if (user == null) {
  379. // No Login Available
  380. error(getString("gb.errorAdminLoginRequired"), true);
  381. }
  382. if (isCreate) {
  383. // Create Repository
  384. if (!user.canAdmin) {
  385. // Only Administrators May Create
  386. error(getString("gb.errorOnlyAdminMayCreateRepository"), true);
  387. }
  388. } else {
  389. // Edit Repository
  390. if (user.canAdmin) {
  391. // Admins can edit everything
  392. isAdmin = true;
  393. return;
  394. } else {
  395. if (!model.owner.equalsIgnoreCase(user.username)) {
  396. // User is not an Admin nor Owner
  397. error(getString("gb.errorOnlyAdminOrOwnerMayEditRepository"), true);
  398. }
  399. }
  400. }
  401. }
  402. } else {
  403. // No Administration Permitted
  404. error(getString("gb.errorAdministrationDisabled"), true);
  405. }
  406. }
  407. private class AccessRestrictionRenderer implements IChoiceRenderer<AccessRestrictionType> {
  408. private static final long serialVersionUID = 1L;
  409. private final Map<AccessRestrictionType, String> map;
  410. public AccessRestrictionRenderer() {
  411. map = getAccessRestrictions();
  412. }
  413. @Override
  414. public String getDisplayValue(AccessRestrictionType type) {
  415. return map.get(type);
  416. }
  417. @Override
  418. public String getIdValue(AccessRestrictionType type, int index) {
  419. return Integer.toString(index);
  420. }
  421. }
  422. private class FederationTypeRenderer implements IChoiceRenderer<FederationStrategy> {
  423. private static final long serialVersionUID = 1L;
  424. private final Map<FederationStrategy, String> map;
  425. public FederationTypeRenderer() {
  426. map = getFederationTypes();
  427. }
  428. @Override
  429. public String getDisplayValue(FederationStrategy type) {
  430. return map.get(type);
  431. }
  432. @Override
  433. public String getIdValue(FederationStrategy type, int index) {
  434. return Integer.toString(index);
  435. }
  436. }
  437. }