summaryrefslogtreecommitdiffstats
path: root/src/main/java/com/gitblit/wicket/pages
diff options
context:
space:
mode:
authorJames Moger <james.moger@gitblit.com>2013-03-27 12:46:05 -0400
committerJames Moger <james.moger@gitblit.com>2013-03-27 17:22:08 -0400
commitf6b200be4c8b90c26886c6cdd5809abac8c4ac15 (patch)
treea948dbcf6f24bf884ad95a8d6830b4ec4e1706cf /src/main/java/com/gitblit/wicket/pages
parentb79ade104858ce6714a7329b7629b331564a2ea5 (diff)
downloadgitblit-f6b200be4c8b90c26886c6cdd5809abac8c4ac15.tar.gz
gitblit-f6b200be4c8b90c26886c6cdd5809abac8c4ac15.zip
Reorganized to Apache Standard Directory Layout & integrated Moxie
This is a massive commit which reorganizes the entire project structure (although it is still monolithic), removes the Build classes, and switches to Moxie, a smarter Ant build tookit based on the original Gitblit Build classes. The Ant build script will likely require additional fine-tuning, but this is big step forward.
Diffstat (limited to 'src/main/java/com/gitblit/wicket/pages')
-rw-r--r--src/main/java/com/gitblit/wicket/pages/ActivityPage.html23
-rw-r--r--src/main/java/com/gitblit/wicket/pages/ActivityPage.java207
-rw-r--r--src/main/java/com/gitblit/wicket/pages/BasePage.html62
-rw-r--r--src/main/java/com/gitblit/wicket/pages/BasePage.java460
-rw-r--r--src/main/java/com/gitblit/wicket/pages/BlamePage.html39
-rw-r--r--src/main/java/com/gitblit/wicket/pages/BlamePage.java129
-rw-r--r--src/main/java/com/gitblit/wicket/pages/BlobDiffPage.html26
-rw-r--r--src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java85
-rw-r--r--src/main/java/com/gitblit/wicket/pages/BlobPage.html38
-rw-r--r--src/main/java/com/gitblit/wicket/pages/BlobPage.java194
-rw-r--r--src/main/java/com/gitblit/wicket/pages/BranchesPage.html15
-rw-r--r--src/main/java/com/gitblit/wicket/pages/BranchesPage.java34
-rw-r--r--src/main/java/com/gitblit/wicket/pages/ChangePasswordPage.html31
-rw-r--r--src/main/java/com/gitblit/wicket/pages/ChangePasswordPage.java139
-rw-r--r--src/main/java/com/gitblit/wicket/pages/CommitDiffPage.html43
-rw-r--r--src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java192
-rw-r--r--src/main/java/com/gitblit/wicket/pages/CommitPage.html99
-rw-r--r--src/main/java/com/gitblit/wicket/pages/CommitPage.java223
-rw-r--r--src/main/java/com/gitblit/wicket/pages/DocsPage.html28
-rw-r--r--src/main/java/com/gitblit/wicket/pages/DocsPage.java82
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html111
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java696
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EditTeamPage.html66
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EditTeamPage.java250
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EditUserPage.html78
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EditUserPage.java261
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.html53
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java67
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_es.html56
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_ko.html57
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_nl.html53
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pl.html56
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_BR.html53
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_zh_CN.html55
-rw-r--r--src/main/java/com/gitblit/wicket/pages/FederationPage.html17
-rw-r--r--src/main/java/com/gitblit/wicket/pages/FederationPage.java52
-rw-r--r--src/main/java/com/gitblit/wicket/pages/FederationRegistrationPage.html39
-rw-r--r--src/main/java/com/gitblit/wicket/pages/FederationRegistrationPage.java95
-rw-r--r--src/main/java/com/gitblit/wicket/pages/ForkPage.html38
-rw-r--r--src/main/java/com/gitblit/wicket/pages/ForkPage.java107
-rw-r--r--src/main/java/com/gitblit/wicket/pages/ForksPage.html20
-rw-r--r--src/main/java/com/gitblit/wicket/pages/ForksPage.java156
-rw-r--r--src/main/java/com/gitblit/wicket/pages/GitSearchPage.html25
-rw-r--r--src/main/java/com/gitblit/wicket/pages/GitSearchPage.java69
-rw-r--r--src/main/java/com/gitblit/wicket/pages/GravatarProfilePage.html20
-rw-r--r--src/main/java/com/gitblit/wicket/pages/GravatarProfilePage.java64
-rw-r--r--src/main/java/com/gitblit/wicket/pages/HistoryPage.html25
-rw-r--r--src/main/java/com/gitblit/wicket/pages/HistoryPage.java65
-rw-r--r--src/main/java/com/gitblit/wicket/pages/LogPage.html25
-rw-r--r--src/main/java/com/gitblit/wicket/pages/LogPage.java69
-rw-r--r--src/main/java/com/gitblit/wicket/pages/LogoutPage.html33
-rw-r--r--src/main/java/com/gitblit/wicket/pages/LogoutPage.java51
-rw-r--r--src/main/java/com/gitblit/wicket/pages/LuceneSearchPage.html92
-rw-r--r--src/main/java/com/gitblit/wicket/pages/LuceneSearchPage.java257
-rw-r--r--src/main/java/com/gitblit/wicket/pages/MarkdownPage.html18
-rw-r--r--src/main/java/com/gitblit/wicket/pages/MarkdownPage.java73
-rw-r--r--src/main/java/com/gitblit/wicket/pages/MetricsPage.html44
-rw-r--r--src/main/java/com/gitblit/wicket/pages/MetricsPage.java184
-rw-r--r--src/main/java/com/gitblit/wicket/pages/PatchPage.html13
-rw-r--r--src/main/java/com/gitblit/wicket/pages/PatchPage.java69
-rw-r--r--src/main/java/com/gitblit/wicket/pages/ProjectPage.html70
-rw-r--r--src/main/java/com/gitblit/wicket/pages/ProjectPage.java355
-rw-r--r--src/main/java/com/gitblit/wicket/pages/ProjectsPage.html37
-rw-r--r--src/main/java/com/gitblit/wicket/pages/ProjectsPage.java235
-rw-r--r--src/main/java/com/gitblit/wicket/pages/RawPage.java161
-rw-r--r--src/main/java/com/gitblit/wicket/pages/RepositoriesPage.html14
-rw-r--r--src/main/java/com/gitblit/wicket/pages/RepositoriesPage.java186
-rw-r--r--src/main/java/com/gitblit/wicket/pages/RepositoryPage.html82
-rw-r--r--src/main/java/com/gitblit/wicket/pages/RepositoryPage.java608
-rw-r--r--src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.html23
-rw-r--r--src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.java102
-rw-r--r--src/main/java/com/gitblit/wicket/pages/RootPage.html41
-rw-r--r--src/main/java/com/gitblit/wicket/pages/RootPage.java454
-rw-r--r--src/main/java/com/gitblit/wicket/pages/RootSubPage.html18
-rw-r--r--src/main/java/com/gitblit/wicket/pages/RootSubPage.java109
-rw-r--r--src/main/java/com/gitblit/wicket/pages/SendProposalPage.html24
-rw-r--r--src/main/java/com/gitblit/wicket/pages/SendProposalPage.java152
-rw-r--r--src/main/java/com/gitblit/wicket/pages/SummaryPage.html54
-rw-r--r--src/main/java/com/gitblit/wicket/pages/SummaryPage.java238
-rw-r--r--src/main/java/com/gitblit/wicket/pages/TagPage.html38
-rw-r--r--src/main/java/com/gitblit/wicket/pages/TagPage.java99
-rw-r--r--src/main/java/com/gitblit/wicket/pages/TagsPage.html15
-rw-r--r--src/main/java/com/gitblit/wicket/pages/TagsPage.java35
-rw-r--r--src/main/java/com/gitblit/wicket/pages/TicketPage.html39
-rw-r--r--src/main/java/com/gitblit/wicket/pages/TicketPage.java81
-rw-r--r--src/main/java/com/gitblit/wicket/pages/TicketsPage.html27
-rw-r--r--src/main/java/com/gitblit/wicket/pages/TicketsPage.java76
-rw-r--r--src/main/java/com/gitblit/wicket/pages/TreePage.html55
-rw-r--r--src/main/java/com/gitblit/wicket/pages/TreePage.java186
-rw-r--r--src/main/java/com/gitblit/wicket/pages/UserPage.html50
-rw-r--r--src/main/java/com/gitblit/wicket/pages/UserPage.java155
-rw-r--r--src/main/java/com/gitblit/wicket/pages/UsersPage.html13
-rw-r--r--src/main/java/com/gitblit/wicket/pages/UsersPage.java33
-rw-r--r--src/main/java/com/gitblit/wicket/pages/prettify/lang-apollo.js2
-rw-r--r--src/main/java/com/gitblit/wicket/pages/prettify/lang-css.js2
-rw-r--r--src/main/java/com/gitblit/wicket/pages/prettify/lang-hs.js2
-rw-r--r--src/main/java/com/gitblit/wicket/pages/prettify/lang-lisp.js2
-rw-r--r--src/main/java/com/gitblit/wicket/pages/prettify/lang-lua.js2
-rw-r--r--src/main/java/com/gitblit/wicket/pages/prettify/lang-ml.js2
-rw-r--r--src/main/java/com/gitblit/wicket/pages/prettify/lang-proto.js1
-rw-r--r--src/main/java/com/gitblit/wicket/pages/prettify/lang-scala.js2
-rw-r--r--src/main/java/com/gitblit/wicket/pages/prettify/lang-sql.js2
-rw-r--r--src/main/java/com/gitblit/wicket/pages/prettify/lang-vb.js2
-rw-r--r--src/main/java/com/gitblit/wicket/pages/prettify/lang-vhdl.js3
-rw-r--r--src/main/java/com/gitblit/wicket/pages/prettify/lang-wiki.js2
-rw-r--r--src/main/java/com/gitblit/wicket/pages/prettify/lang-yaml.js2
-rw-r--r--src/main/java/com/gitblit/wicket/pages/prettify/prettify.css1
-rw-r--r--src/main/java/com/gitblit/wicket/pages/prettify/prettify.js33
108 files changed, 9706 insertions, 0 deletions
diff --git a/src/main/java/com/gitblit/wicket/pages/ActivityPage.html b/src/main/java/com/gitblit/wicket/pages/ActivityPage.html
new file mode 100644
index 00000000..4b10c2cf
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/ActivityPage.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+<body>
+<wicket:extend>
+ <div class="pageTitle">
+ <h2><wicket:message key="gb.recentActivity"></wicket:message><small> <span class="hidden-phone">/ <span wicket:id="subheader">[days back]</span></span></small></h2>
+ </div>
+ <div class="hidden-phone" style="height: 155px;text-align: center;">
+ <table>
+ <tr>
+ <td><span class="hidden-tablet" id="chartDaily"></span></td>
+ <td><span id="chartRepositories"></span></td>
+ <td><span id="chartAuthors"></span></td>
+ </tr>
+ </table>
+ </div>
+ <div wicket:id="activityPanel" style="padding-top:5px;" >[activity panel]</div>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/ActivityPage.java b/src/main/java/com/gitblit/wicket/pages/ActivityPage.java
new file mode 100644
index 00000000..bceac8f4
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/ActivityPage.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.behavior.HeaderContributor;
+import org.apache.wicket.markup.html.basic.Label;
+
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.Activity;
+import com.gitblit.models.Metric;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.utils.ActivityUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.PageRegistration;
+import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
+import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.charting.GoogleChart;
+import com.gitblit.wicket.charting.GoogleCharts;
+import com.gitblit.wicket.charting.GoogleLineChart;
+import com.gitblit.wicket.charting.GooglePieChart;
+import com.gitblit.wicket.panels.ActivityPanel;
+
+/**
+ * Activity Page shows a list of recent commits across all visible Gitblit
+ * repositories.
+ *
+ * @author James Moger
+ *
+ */
+public class ActivityPage extends RootPage {
+
+ public ActivityPage(PageParameters params) {
+ super(params);
+ setupPage("", "");
+
+ // parameters
+ int daysBack = WicketUtils.getDaysBack(params);
+ if (daysBack < 1) {
+ daysBack = 14;
+ }
+ String objectId = WicketUtils.getObject(params);
+
+ // determine repositories to view and retrieve the activity
+ List<RepositoryModel> models = getRepositories(params);
+ List<Activity> recentActivity = ActivityUtils.getRecentActivity(models,
+ daysBack, objectId, getTimeZone());
+
+ if (recentActivity.size() == 0) {
+ // no activity, skip graphs and activity panel
+ add(new Label("subheader", MessageFormat.format(getString("gb.recentActivityNone"),
+ daysBack)));
+ add(new Label("activityPanel"));
+ } else {
+ // calculate total commits and total authors
+ int totalCommits = 0;
+ Set<String> uniqueAuthors = new HashSet<String>();
+ for (Activity activity : recentActivity) {
+ totalCommits += activity.getCommitCount();
+ uniqueAuthors.addAll(activity.getAuthorMetrics().keySet());
+ }
+ int totalAuthors = uniqueAuthors.size();
+
+ // add the subheader with stat numbers
+ add(new Label("subheader", MessageFormat.format(getString("gb.recentActivityStats"),
+ daysBack, totalCommits, totalAuthors)));
+
+ // create the activity charts
+ GoogleCharts charts = createCharts(recentActivity);
+ add(new HeaderContributor(charts));
+
+ // add activity panel
+ add(new ActivityPanel("activityPanel", recentActivity));
+ }
+ }
+
+ @Override
+ protected boolean reusePageParameters() {
+ return true;
+ }
+
+ @Override
+ protected void addDropDownMenus(List<PageRegistration> pages) {
+ DropDownMenuRegistration filters = new DropDownMenuRegistration("gb.filters",
+ ActivityPage.class);
+
+ PageParameters currentParameters = getPageParameters();
+ int daysBack = GitBlit.getInteger(Keys.web.activityDuration, 14);
+ if (currentParameters != null && !currentParameters.containsKey("db")) {
+ currentParameters.put("db", daysBack);
+ }
+
+ // preserve time filter options on repository choices
+ filters.menuItems.addAll(getRepositoryFilterItems(currentParameters));
+
+ // preserve repository filter options on time choices
+ filters.menuItems.addAll(getTimeFilterItems(currentParameters));
+
+ if (filters.menuItems.size() > 0) {
+ // Reset Filter
+ filters.menuItems.add(new DropDownMenuItem(getString("gb.reset"), null, null));
+ }
+ pages.add(filters);
+ }
+
+ /**
+ * Creates the daily activity line chart, the active repositories pie chart,
+ * and the active authors pie chart
+ *
+ * @param recentActivity
+ * @return
+ */
+ private GoogleCharts createCharts(List<Activity> recentActivity) {
+ // activity metrics
+ Map<String, Metric> repositoryMetrics = new HashMap<String, Metric>();
+ Map<String, Metric> authorMetrics = new HashMap<String, Metric>();
+
+ // aggregate repository and author metrics
+ for (Activity activity : recentActivity) {
+
+ // aggregate author metrics
+ for (Map.Entry<String, Metric> entry : activity.getAuthorMetrics().entrySet()) {
+ String author = entry.getKey();
+ if (!authorMetrics.containsKey(author)) {
+ authorMetrics.put(author, new Metric(author));
+ }
+ authorMetrics.get(author).count += entry.getValue().count;
+ }
+
+ // aggregate repository metrics
+ for (Map.Entry<String, Metric> entry : activity.getRepositoryMetrics().entrySet()) {
+ String repository = StringUtils.stripDotGit(entry.getKey());
+ if (!repositoryMetrics.containsKey(repository)) {
+ repositoryMetrics.put(repository, new Metric(repository));
+ }
+ repositoryMetrics.get(repository).count += entry.getValue().count;
+ }
+ }
+
+ // build google charts
+ int w = 310;
+ int h = 150;
+ GoogleCharts charts = new GoogleCharts();
+
+ // sort in reverse-chronological order and then reverse that
+ Collections.sort(recentActivity);
+ Collections.reverse(recentActivity);
+
+ // daily line chart
+ GoogleChart chart = new GoogleLineChart("chartDaily", getString("gb.dailyActivity"), "day",
+ getString("gb.commits"));
+ SimpleDateFormat df = new SimpleDateFormat("MMM dd");
+ df.setTimeZone(getTimeZone());
+ for (Activity metric : recentActivity) {
+ chart.addValue(df.format(metric.startDate), metric.getCommitCount());
+ }
+ chart.setWidth(w);
+ chart.setHeight(h);
+ charts.addChart(chart);
+
+ // active repositories pie chart
+ chart = new GooglePieChart("chartRepositories", getString("gb.activeRepositories"),
+ getString("gb.repository"), getString("gb.commits"));
+ for (Metric metric : repositoryMetrics.values()) {
+ chart.addValue(metric.name, metric.count);
+ }
+ chart.setWidth(w);
+ chart.setHeight(h);
+ charts.addChart(chart);
+
+ // active authors pie chart
+ chart = new GooglePieChart("chartAuthors", getString("gb.activeAuthors"),
+ getString("gb.author"), getString("gb.commits"));
+ for (Metric metric : authorMetrics.values()) {
+ chart.addValue(metric.name, metric.count);
+ }
+ chart.setWidth(w);
+ chart.setHeight(h);
+ charts.addChart(chart);
+
+ return charts;
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/BasePage.html b/src/main/java/com/gitblit/wicket/pages/BasePage.html
new file mode 100644
index 00000000..4a642e73
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/BasePage.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+ <!-- Head -->
+ <wicket:head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title wicket:id="title">[page title]</title>
+ <link rel="icon" href="gitblt-favicon.png" type="image/png" />
+
+ <link rel="stylesheet" href="bootstrap/css/bootstrap.css"/>
+ <link rel="stylesheet" type="text/css" href="gitblit.css"/>
+ </wicket:head>
+
+ <body>
+
+ <!-- page content -->
+ <wicket:child />
+
+ <!-- page footer -->
+ <div class="container">
+ <footer class="footer">
+ <p class="pull-right">
+ <a title="gitblit homepage" href="http://gitblit.com/">
+ <span wicket:id="gbVersion"></span>
+ </a>
+ </p>
+ <div wicket:id="userPanel">[user panel]</div>
+ </footer>
+ </div>
+
+ <!-- Override Bootstrap's responsive menu background highlighting -->
+ <style>
+ @media (max-width: 979px) {
+ .nav-collapse .nav > li > a:hover, .nav-collapse .dropdown-menu a:hover {
+ background-color: #000070;
+ }
+
+ .navbar div > ul .dropdown-menu li a {
+ color: #ccc;
+ }
+ }
+ </style>
+
+ <!-- Include scripts at end for faster page loading -->
+ <script type="text/javascript" src="bootstrap/js/jquery.js"></script>
+ <script type="text/javascript" src="bootstrap/js/bootstrap.js"></script>
+ </body>
+
+ <!-- user fragment -->
+ <wicket:fragment wicket:id="userFragment">
+ <span class="userPanel" wicket:id="username"></span>
+ <span class="userPanel" wicket:id="loginLink"></span>
+ <span class="hidden-phone">
+ <span class="userPanel" wicket:id="separator"></span>
+ <span class="userPanel"><a wicket:id="changePasswordLink"><wicket:message key="gb.changePassword"></wicket:message></a></span>
+ </span>
+ </wicket:fragment>
+
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/BasePage.java b/src/main/java/com/gitblit/wicket/pages/BasePage.java
new file mode 100644
index 00000000..5c73df33
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/BasePage.java
@@ -0,0 +1,460 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.RedirectToUrlException;
+import org.apache.wicket.RequestCycle;
+import org.apache.wicket.RestartResponseException;
+import org.apache.wicket.markup.html.CSSPackageResource;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.link.ExternalLink;
+import org.apache.wicket.markup.html.panel.FeedbackPanel;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.protocol.http.RequestUtils;
+import org.apache.wicket.protocol.http.WebRequest;
+import org.apache.wicket.protocol.http.WebResponse;
+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.Constants;
+import com.gitblit.Constants.AccessPermission;
+import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.Constants.AuthorizationControl;
+import com.gitblit.Constants.FederationStrategy;
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.ProjectModel;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.TeamModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.utils.TimeUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.LinkPanel;
+
+public abstract class BasePage extends WebPage {
+
+ private final Logger logger;
+
+ private transient TimeUtils timeUtils;
+
+ public BasePage() {
+ super();
+ logger = LoggerFactory.getLogger(getClass());
+ customizeHeader();
+ login();
+ }
+
+ public BasePage(PageParameters params) {
+ super(params);
+ logger = LoggerFactory.getLogger(getClass());
+ customizeHeader();
+ login();
+ }
+
+ private void customizeHeader() {
+ if (GitBlit.getBoolean(Keys.web.useResponsiveLayout, true)) {
+ add(CSSPackageResource.getHeaderContribution("bootstrap/css/bootstrap-responsive.css"));
+ }
+ }
+
+ protected String getLanguageCode() {
+ return GitBlitWebSession.get().getLocale().getLanguage();
+ }
+
+ protected String getCountryCode() {
+ return GitBlitWebSession.get().getLocale().getCountry().toLowerCase();
+ }
+
+ protected TimeUtils getTimeUtils() {
+ if (timeUtils == null) {
+ ResourceBundle bundle;
+ try {
+ bundle = ResourceBundle.getBundle("com.gitblit.wicket.GitBlitWebApp", GitBlitWebSession.get().getLocale());
+ } catch (Throwable t) {
+ bundle = ResourceBundle.getBundle("com.gitblit.wicket.GitBlitWebApp");
+ }
+ timeUtils = new TimeUtils(bundle);
+ }
+ return timeUtils;
+ }
+
+ @Override
+ protected void onBeforeRender() {
+ if (GitBlit.isDebugMode()) {
+ // strip Wicket tags in debug mode for jQuery DOM traversal
+ Application.get().getMarkupSettings().setStripWicketTags(true);
+ }
+ super.onBeforeRender();
+ }
+
+ @Override
+ protected void onAfterRender() {
+ if (GitBlit.isDebugMode()) {
+ // restore Wicket debug tags
+ Application.get().getMarkupSettings().setStripWicketTags(false);
+ }
+ super.onAfterRender();
+ }
+
+ private void login() {
+ GitBlitWebSession session = GitBlitWebSession.get();
+ if (session.isLoggedIn() && !session.isSessionInvalidated()) {
+ // already have a session, refresh usermodel to pick up
+ // any changes to permissions or roles (issue-186)
+ UserModel user = GitBlit.self().getUserModel(session.getUser().username);
+ session.setUser(user);
+ return;
+ }
+
+ // try to authenticate by servlet request
+ HttpServletRequest httpRequest = ((WebRequest) getRequestCycle().getRequest()).getHttpServletRequest();
+ UserModel user = GitBlit.self().authenticate(httpRequest);
+
+ // Login the user
+ if (user != null) {
+ // issue 62: fix session fixation vulnerability
+ session.replaceSession();
+ session.setUser(user);
+
+ // Set Cookie
+ WebResponse response = (WebResponse) getRequestCycle().getResponse();
+ GitBlit.self().setCookie(response, user);
+
+ session.continueRequest();
+ }
+ }
+
+ protected void setupPage(String repositoryName, String pageName) {
+ if (repositoryName != null && repositoryName.trim().length() > 0) {
+ add(new Label("title", getServerName() + " - " + repositoryName));
+ } else {
+ add(new Label("title", getServerName()));
+ }
+
+ ExternalLink rootLink = new ExternalLink("rootLink", urlFor(RepositoriesPage.class, null).toString());
+ WicketUtils.setHtmlTooltip(rootLink, GitBlit.getString(Keys.web.siteName, Constants.NAME));
+ add(rootLink);
+
+ // Feedback panel for info, warning, and non-fatal error messages
+ add(new FeedbackPanel("feedback"));
+
+ // footer
+ if (GitBlit.getBoolean(Keys.web.authenticateViewPages, true)
+ || GitBlit.getBoolean(Keys.web.authenticateAdminPages, true)) {
+ UserFragment userFragment = new UserFragment("userPanel", "userFragment", BasePage.this);
+ add(userFragment);
+ } else {
+ add(new Label("userPanel", ""));
+ }
+
+ add(new Label("gbVersion", "v" + Constants.getVersion()));
+ if (GitBlit.getBoolean(Keys.web.aggressiveHeapManagement, false)) {
+ System.gc();
+ }
+ }
+
+ protected Map<AccessRestrictionType, String> getAccessRestrictions() {
+ Map<AccessRestrictionType, String> map = new LinkedHashMap<AccessRestrictionType, String>();
+ for (AccessRestrictionType type : AccessRestrictionType.values()) {
+ switch (type) {
+ case NONE:
+ map.put(type, getString("gb.notRestricted"));
+ break;
+ case PUSH:
+ map.put(type, getString("gb.pushRestricted"));
+ break;
+ case CLONE:
+ map.put(type, getString("gb.cloneRestricted"));
+ break;
+ case VIEW:
+ map.put(type, getString("gb.viewRestricted"));
+ break;
+ }
+ }
+ return map;
+ }
+
+ protected Map<AccessPermission, String> getAccessPermissions() {
+ Map<AccessPermission, String> map = new LinkedHashMap<AccessPermission, String>();
+ for (AccessPermission type : AccessPermission.values()) {
+ switch (type) {
+ case NONE:
+ map.put(type, MessageFormat.format(getString("gb.noPermission"), type.code));
+ break;
+ case EXCLUDE:
+ map.put(type, MessageFormat.format(getString("gb.excludePermission"), type.code));
+ break;
+ case VIEW:
+ map.put(type, MessageFormat.format(getString("gb.viewPermission"), type.code));
+ break;
+ case CLONE:
+ map.put(type, MessageFormat.format(getString("gb.clonePermission"), type.code));
+ break;
+ case PUSH:
+ map.put(type, MessageFormat.format(getString("gb.pushPermission"), type.code));
+ break;
+ case CREATE:
+ map.put(type, MessageFormat.format(getString("gb.createPermission"), type.code));
+ break;
+ case DELETE:
+ map.put(type, MessageFormat.format(getString("gb.deletePermission"), type.code));
+ break;
+ case REWIND:
+ map.put(type, MessageFormat.format(getString("gb.rewindPermission"), type.code));
+ break;
+ }
+ }
+ return map;
+ }
+
+ protected Map<FederationStrategy, String> getFederationTypes() {
+ Map<FederationStrategy, String> map = new LinkedHashMap<FederationStrategy, String>();
+ for (FederationStrategy type : FederationStrategy.values()) {
+ switch (type) {
+ case EXCLUDE:
+ map.put(type, getString("gb.excludeFromFederation"));
+ break;
+ case FEDERATE_THIS:
+ map.put(type, getString("gb.federateThis"));
+ break;
+ case FEDERATE_ORIGIN:
+ map.put(type, getString("gb.federateOrigin"));
+ break;
+ }
+ }
+ return map;
+ }
+
+ protected Map<AuthorizationControl, String> getAuthorizationControls() {
+ Map<AuthorizationControl, String> map = new LinkedHashMap<AuthorizationControl, String>();
+ for (AuthorizationControl type : AuthorizationControl.values()) {
+ switch (type) {
+ case AUTHENTICATED:
+ map.put(type, getString("gb.allowAuthenticatedDescription"));
+ break;
+ case NAMED:
+ map.put(type, getString("gb.allowNamedDescription"));
+ break;
+ }
+ }
+ return map;
+ }
+
+ protected TimeZone getTimeZone() {
+ return GitBlit.getBoolean(Keys.web.useClientTimezone, false) ? GitBlitWebSession.get()
+ .getTimezone() : GitBlit.getTimezone();
+ }
+
+ protected String getServerName() {
+ ServletWebRequest servletWebRequest = (ServletWebRequest) getRequest();
+ HttpServletRequest req = servletWebRequest.getHttpServletRequest();
+ return req.getServerName();
+ }
+
+ public static String getRepositoryUrl(RepositoryModel repository) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(WicketUtils.getGitblitURL(RequestCycle.get().getRequest()));
+ sb.append(Constants.GIT_PATH);
+ sb.append(repository.name);
+
+ // inject username into repository url if authentication is required
+ if (repository.accessRestriction.exceeds(AccessRestrictionType.NONE)
+ && GitBlitWebSession.get().isLoggedIn()) {
+ String username = GitBlitWebSession.get().getUsername();
+ sb.insert(sb.indexOf("://") + 3, username + "@");
+ }
+ return sb.toString();
+ }
+
+ protected List<ProjectModel> getProjectModels() {
+ final UserModel user = GitBlitWebSession.get().getUser();
+ List<ProjectModel> projects = GitBlit.self().getProjectModels(user, true);
+ return projects;
+ }
+
+ protected List<ProjectModel> getProjects(PageParameters params) {
+ if (params == null) {
+ return getProjectModels();
+ }
+
+ boolean hasParameter = false;
+ String regex = WicketUtils.getRegEx(params);
+ String team = WicketUtils.getTeam(params);
+ int daysBack = params.getInt("db", 0);
+
+ List<ProjectModel> availableModels = getProjectModels();
+ Set<ProjectModel> models = new HashSet<ProjectModel>();
+
+ if (!StringUtils.isEmpty(regex)) {
+ // filter the projects by the regex
+ hasParameter = true;
+ Pattern pattern = Pattern.compile(regex);
+ for (ProjectModel model : availableModels) {
+ if (pattern.matcher(model.name).find()) {
+ models.add(model);
+ }
+ }
+ }
+
+ if (!StringUtils.isEmpty(team)) {
+ // filter the projects by the specified teams
+ hasParameter = true;
+ List<String> teams = StringUtils.getStringsFromValue(team, ",");
+
+ // need TeamModels first
+ List<TeamModel> teamModels = new ArrayList<TeamModel>();
+ for (String name : teams) {
+ TeamModel teamModel = GitBlit.self().getTeamModel(name);
+ if (teamModel != null) {
+ teamModels.add(teamModel);
+ }
+ }
+
+ // brute-force our way through finding the matching models
+ for (ProjectModel projectModel : availableModels) {
+ for (String repositoryName : projectModel.repositories) {
+ for (TeamModel teamModel : teamModels) {
+ if (teamModel.hasRepositoryPermission(repositoryName)) {
+ models.add(projectModel);
+ }
+ }
+ }
+ }
+ }
+
+ if (!hasParameter) {
+ models.addAll(availableModels);
+ }
+
+ // time-filter the list
+ if (daysBack > 0) {
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+ cal.add(Calendar.DATE, -1 * daysBack);
+ Date threshold = cal.getTime();
+ Set<ProjectModel> timeFiltered = new HashSet<ProjectModel>();
+ for (ProjectModel model : models) {
+ if (model.lastChange.after(threshold)) {
+ timeFiltered.add(model);
+ }
+ }
+ models = timeFiltered;
+ }
+
+ List<ProjectModel> list = new ArrayList<ProjectModel>(models);
+ Collections.sort(list);
+ return list;
+ }
+
+ public void warn(String message, Throwable t) {
+ logger.warn(message, t);
+ }
+
+ public void error(String message, boolean redirect) {
+ logger.error(message + " for " + GitBlitWebSession.get().getUsername());
+ if (redirect) {
+ GitBlitWebSession.get().cacheErrorMessage(message);
+ String relativeUrl = urlFor(RepositoriesPage.class, null).toString();
+ String absoluteUrl = RequestUtils.toAbsolutePath(relativeUrl);
+ throw new RedirectToUrlException(absoluteUrl);
+ } else {
+ super.error(message);
+ }
+ }
+
+ public void error(String message, Throwable t, boolean redirect) {
+ logger.error(message, t);
+ if (redirect) {
+ GitBlitWebSession.get().cacheErrorMessage(message);
+ throw new RestartResponseException(getApplication().getHomePage());
+ } else {
+ super.error(message);
+ }
+ }
+
+ public void authenticationError(String message) {
+ logger.error(getRequest().getURL() + " for " + GitBlitWebSession.get().getUsername());
+ if (!GitBlitWebSession.get().isLoggedIn()) {
+ // cache the request if we have not authenticated.
+ // the request will continue after authentication.
+ GitBlitWebSession.get().cacheRequest(getClass());
+ }
+ error(message, true);
+ }
+
+ /**
+ * Panel fragment for displaying login or logout/change_password links.
+ *
+ */
+ static class UserFragment extends Fragment {
+
+ private static final long serialVersionUID = 1L;
+
+ public UserFragment(String id, String markupId, MarkupContainer markupProvider) {
+ super(id, markupId, markupProvider);
+
+ GitBlitWebSession session = GitBlitWebSession.get();
+ if (session.isLoggedIn()) {
+ UserModel user = session.getUser();
+ boolean editCredentials = GitBlit.self().supportsCredentialChanges(user);
+ boolean standardLogin = session.authenticationType.isStandard();
+
+ // username, logout, and change password
+ add(new Label("username", user.getDisplayName() + ":"));
+ add(new LinkPanel("loginLink", null, markupProvider.getString("gb.logout"),
+ LogoutPage.class).setVisible(standardLogin));
+
+ // quick and dirty hack for showing a separator
+ add(new Label("separator", "|").setVisible(standardLogin && editCredentials));
+ add(new BookmarkablePageLink<Void>("changePasswordLink",
+ ChangePasswordPage.class).setVisible(editCredentials));
+ } else {
+ // login
+ add(new Label("username").setVisible(false));
+ add(new Label("loginLink").setVisible(false));
+ add(new Label("separator").setVisible(false));
+ add(new Label("changePasswordLink").setVisible(false));
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/BlamePage.html b/src/main/java/com/gitblit/wicket/pages/BlamePage.html
new file mode 100644
index 00000000..9391eaf0
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/BlamePage.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <!-- blame nav links -->
+ <div class="page_nav2">
+ <a wicket:id="blobLink"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="headLink"><wicket:message key="gb.head"></wicket:message></a> | <a wicket:id="commitLink"><wicket:message key="gb.commit"></wicket:message></a> | <a wicket:id="commitDiffLink"><wicket:message key="gb.commitdiff"></wicket:message></a>
+ </div>
+
+ <!-- commit header -->
+ <div wicket:id="commitHeader">[commit header]</div>
+
+ <!-- breadcrumbs -->
+ <div wicket:id="breadcrumbs">[breadcrumbs]</div>
+
+ <!-- blame content -->
+ <table class="annotated" style="margin-bottom:5px;">
+ <tbody>
+ <tr>
+ <th><wicket:message key="gb.commit">[commit]</wicket:message></th>
+ <th><wicket:message key="gb.line">[line]</wicket:message></th>
+ <th><wicket:message key="gb.content">[content]</wicket:message></th>
+ </tr>
+ <tr wicket:id="annotation">
+ <td><span class="sha1" wicket:id="commit"></span></td>
+ <td><span class="sha1" wicket:id="line"></span></td>
+ <td><span class="sha1" wicket:id="data"></span></td>
+ </tr>
+ </tbody>
+ </table>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/BlamePage.java b/src/main/java/com/gitblit/wicket/pages/BlamePage.java
new file mode 100644
index 00000000..d76181d2
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/BlamePage.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.AnnotatedLine;
+import com.gitblit.utils.DiffUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.CommitHeaderPanel;
+import com.gitblit.wicket.panels.LinkPanel;
+import com.gitblit.wicket.panels.PathBreadcrumbsPanel;
+
+public class BlamePage extends RepositoryPage {
+
+ public BlamePage(PageParameters params) {
+ super(params);
+
+ final String blobPath = WicketUtils.getPath(params);
+
+ RevCommit commit = getCommit();
+
+ add(new BookmarkablePageLink<Void>("blobLink", BlobPage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
+ add(new BookmarkablePageLink<Void>("commitLink", CommitPage.class,
+ WicketUtils.newObjectParameter(repositoryName, objectId)));
+ add(new BookmarkablePageLink<Void>("commitDiffLink", CommitDiffPage.class,
+ WicketUtils.newObjectParameter(repositoryName, objectId)));
+
+ // blame page links
+ add(new BookmarkablePageLink<Void>("headLink", BlamePage.class,
+ WicketUtils.newPathParameter(repositoryName, Constants.HEAD, blobPath)));
+ add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
+
+ add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
+
+ add(new PathBreadcrumbsPanel("breadcrumbs", repositoryName, blobPath, objectId));
+
+ String format = GitBlit.getString(Keys.web.datetimestampLongFormat,
+ "EEEE, MMMM d, yyyy HH:mm Z");
+ final DateFormat df = new SimpleDateFormat(format);
+ df.setTimeZone(getTimeZone());
+ List<AnnotatedLine> lines = DiffUtils.blame(getRepository(), blobPath, objectId);
+ ListDataProvider<AnnotatedLine> blameDp = new ListDataProvider<AnnotatedLine>(lines);
+ DataView<AnnotatedLine> blameView = new DataView<AnnotatedLine>("annotation", blameDp) {
+ private static final long serialVersionUID = 1L;
+ private int count;
+ private String lastCommitId = "";
+ private boolean showInitials = true;
+
+ public void populateItem(final Item<AnnotatedLine> item) {
+ AnnotatedLine entry = item.getModelObject();
+ item.add(new Label("line", "" + entry.lineNumber));
+ item.add(new Label("data", StringUtils.escapeForHtml(entry.data, true))
+ .setEscapeModelStrings(false));
+ if (!lastCommitId.equals(entry.commitId)) {
+ lastCommitId = entry.commitId;
+ count++;
+ // show the link for first line
+ LinkPanel commitLink = new LinkPanel("commit", null,
+ getShortObjectId(entry.commitId), CommitPage.class,
+ newCommitParameter(entry.commitId));
+ WicketUtils.setHtmlTooltip(commitLink,
+ MessageFormat.format("{0}, {1}", entry.author, df.format(entry.when)));
+ item.add(commitLink);
+ showInitials = true;
+ } else {
+ if (showInitials) {
+ showInitials = false;
+ // show author initials
+ item.add(new Label("commit", getInitials(entry.author)));
+ } else {
+ // hide the commit link until the next block
+ item.add(new Label("commit").setVisible(false));
+ }
+ }
+ if (count % 2 == 0) {
+ WicketUtils.setCssClass(item, "even");
+ } else {
+ WicketUtils.setCssClass(item, "odd");
+ }
+ }
+ };
+ add(blameView);
+ }
+
+ private String getInitials(String author) {
+ StringBuilder sb = new StringBuilder();
+ String[] chunks = author.split(" ");
+ for (String chunk : chunks) {
+ sb.append(chunk.charAt(0));
+ }
+ return sb.toString().toUpperCase();
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.blame");
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.html b/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.html
new file mode 100644
index 00000000..c6336429
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <!-- blob nav links -->
+ <div class="page_nav2">
+ <a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="patchLink"><wicket:message key="gb.patch"></wicket:message></a> | <a wicket:id="commitLink"><wicket:message key="gb.commit"></wicket:message></a> | <a wicket:id="commitDiffLink"><wicket:message key="gb.commitdiff"></wicket:message></a>
+ </div>
+
+ <!-- commit header -->
+ <div wicket:id="commitHeader">[commit header]</div>
+
+ <!-- breadcrumbs -->
+ <div wicket:id="breadcrumbs">[breadcrumbs]</div>
+
+ <!-- diff content -->
+ <pre wicket:id="diffText">[diff text]</pre>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java b/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java
new file mode 100644
index 00000000..d86d2e63
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.utils.DiffUtils;
+import com.gitblit.utils.DiffUtils.DiffOutputType;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.CommitHeaderPanel;
+import com.gitblit.wicket.panels.PathBreadcrumbsPanel;
+
+public class BlobDiffPage extends RepositoryPage {
+
+ public BlobDiffPage(PageParameters params) {
+ super(params);
+
+ final String blobPath = WicketUtils.getPath(params);
+ final String baseObjectId = WicketUtils.getBaseObjectId(params);
+
+ Repository r = getRepository();
+ RevCommit commit = getCommit();
+
+ DiffOutputType diffType = DiffOutputType.forName(GitBlit.getString(Keys.web.diffStyle,
+ DiffOutputType.GITBLIT.name()));
+
+ String diff;
+ if (StringUtils.isEmpty(baseObjectId)) {
+ // use first parent
+ diff = DiffUtils.getDiff(r, commit, blobPath, diffType);
+ add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
+ } else {
+ // base commit specified
+ RevCommit baseCommit = JGitUtils.getCommit(r, baseObjectId);
+ diff = DiffUtils.getDiff(r, baseCommit, commit, blobPath, diffType);
+ add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class,
+ WicketUtils.newBlobDiffParameter(repositoryName, baseObjectId, objectId,
+ blobPath)));
+ }
+
+ add(new BookmarkablePageLink<Void>("commitLink", CommitPage.class,
+ WicketUtils.newObjectParameter(repositoryName, objectId)));
+ add(new BookmarkablePageLink<Void>("commitDiffLink", CommitDiffPage.class,
+ WicketUtils.newObjectParameter(repositoryName, objectId)));
+
+ // diff page links
+ add(new BookmarkablePageLink<Void>("blameLink", BlamePage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
+ add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
+
+ add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
+
+ add(new PathBreadcrumbsPanel("breadcrumbs", repositoryName, blobPath, objectId));
+
+ add(new Label("diffText", diff).setEscapeModelStrings(false));
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.diff");
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/BlobPage.html b/src/main/java/com/gitblit/wicket/pages/BlobPage.html
new file mode 100644
index 00000000..80f061fd
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/BlobPage.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<!-- contribute google-code-prettify resources to the page header -->
+<wicket:head>
+ <wicket:link>
+ <link href="prettify/prettify.css" type="text/css" rel="stylesheet" />
+ <script type="text/javascript" src="prettify/prettify.js"></script>
+ </wicket:link>
+</wicket:head>
+
+<wicket:extend>
+<!-- need to specify body.onload -->
+<body onload="prettyPrint()">
+
+ <!-- blob nav links -->
+ <div class="page_nav2">
+ <a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a> | <a wicket:id="headLink"><wicket:message key="gb.head"></wicket:message></a>
+ </div>
+
+ <!-- commit header -->
+ <div wicket:id="commitHeader">[commit header]</div>
+
+ <!-- breadcrumbs -->
+ <div wicket:id="breadcrumbs">[breadcrumbs]</div>
+
+ <!-- blob content -->
+ <pre style="border:0px;" wicket:id="blobText">[blob content]</pre>
+
+ <!-- blob image -->
+ <img wicket:id="blobImage" style="padding-top:5px;"></img>
+
+</body>
+</wicket:extend>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/BlobPage.java b/src/main/java/com/gitblit/wicket/pages/BlobPage.java
new file mode 100644
index 00000000..e2b8546b
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/BlobPage.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.image.Image;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.ExternalImage;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.CommitHeaderPanel;
+import com.gitblit.wicket.panels.PathBreadcrumbsPanel;
+
+public class BlobPage extends RepositoryPage {
+
+ public BlobPage(PageParameters params) {
+ super(params);
+
+ Repository r = getRepository();
+ final String blobPath = WicketUtils.getPath(params);
+ String [] encodings = GitBlit.getEncodings();
+
+ if (StringUtils.isEmpty(blobPath)) {
+ // blob by objectid
+
+ add(new BookmarkablePageLink<Void>("blameLink", BlamePage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, blobPath))
+ .setEnabled(false));
+ add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class).setEnabled(false));
+ add(new BookmarkablePageLink<Void>("rawLink", RawPage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
+ add(new BookmarkablePageLink<Void>("headLink", BlobPage.class).setEnabled(false));
+ add(new CommitHeaderPanel("commitHeader", objectId));
+ add(new PathBreadcrumbsPanel("breadcrumbs", repositoryName, blobPath, objectId));
+ Component c = new Label("blobText", JGitUtils.getStringContent(r, objectId, encodings));
+ WicketUtils.setCssClass(c, "plainprint");
+ add(c);
+ } else {
+ // standard blob view
+ String extension = null;
+ if (blobPath.lastIndexOf('.') > -1) {
+ extension = blobPath.substring(blobPath.lastIndexOf('.') + 1).toLowerCase();
+ }
+
+ // see if we should redirect to the markdown page
+ for (String ext : GitBlit.getStrings(Keys.web.markdownExtensions)) {
+ if (ext.equals(extension)) {
+ setResponsePage(MarkdownPage.class, params);
+ return;
+ }
+ }
+
+ // manually get commit because it can be null
+ RevCommit commit = JGitUtils.getCommit(r, objectId);
+
+ // blob page links
+ add(new BookmarkablePageLink<Void>("blameLink", BlamePage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
+ add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
+ add(new BookmarkablePageLink<Void>("rawLink", RawPage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
+ add(new BookmarkablePageLink<Void>("headLink", BlobPage.class,
+ WicketUtils.newPathParameter(repositoryName, Constants.HEAD, blobPath)));
+
+ add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
+
+ add(new PathBreadcrumbsPanel("breadcrumbs", repositoryName, blobPath, objectId));
+
+ // Map the extensions to types
+ Map<String, Integer> map = new HashMap<String, Integer>();
+ for (String ext : GitBlit.getStrings(Keys.web.prettyPrintExtensions)) {
+ map.put(ext.toLowerCase(), 1);
+ }
+ for (String ext : GitBlit.getStrings(Keys.web.imageExtensions)) {
+ map.put(ext.toLowerCase(), 2);
+ }
+ for (String ext : GitBlit.getStrings(Keys.web.binaryExtensions)) {
+ map.put(ext.toLowerCase(), 3);
+ }
+
+ if (extension != null) {
+ int type = 0;
+ if (map.containsKey(extension)) {
+ type = map.get(extension);
+ }
+ switch (type) {
+ case 2:
+ // image blobs
+ add(new Label("blobText").setVisible(false));
+ add(new ExternalImage("blobImage", urlFor(RawPage.class, WicketUtils.newPathParameter(repositoryName, objectId, blobPath)).toString()));
+ break;
+ case 3:
+ // binary blobs
+ add(new Label("blobText", "Binary File"));
+ add(new Image("blobImage").setVisible(false));
+ break;
+ default:
+ // plain text
+ String source = JGitUtils.getStringContent(r, commit.getTree(), blobPath, encodings);
+ String table = generateSourceView(source, type == 1);
+ add(new Label("blobText", table).setEscapeModelStrings(false));
+ add(new Image("blobImage").setVisible(false));
+ }
+ } else {
+ // plain text
+ String source = JGitUtils.getStringContent(r, commit.getTree(), blobPath, encodings);
+ String table = generateSourceView(source, false);
+ add(new Label("blobText", table).setEscapeModelStrings(false));
+ add(new Image("blobImage").setVisible(false));
+ }
+ }
+ }
+
+ protected String generateSourceView(String source, boolean prettyPrint) {
+ String [] lines = source.split("\n");
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("<!-- start blob table -->");
+ sb.append("<table width=\"100%\"><tbody><tr>");
+
+ // nums column
+ sb.append("<!-- start nums column -->");
+ sb.append("<td id=\"nums\">");
+ sb.append("<pre>");
+ String numPattern = "<span id=\"L{0}\" class=\"num\">{0}</span>\n";
+ for (int i = 0; i < lines.length; i++) {
+ sb.append(MessageFormat.format(numPattern, "" + (i + 1)));
+ }
+ sb.append("</pre>");
+ sb.append("<!-- end nums column -->");
+ sb.append("</td>");
+
+ sb.append("<!-- start lines column -->");
+ sb.append("<td id=\"lines\">");
+ sb.append("<div class=\"sourceview\">");
+ if (prettyPrint) {
+ sb.append("<pre class=\"prettyprint\">");
+ } else {
+ sb.append("<pre class=\"plainprint\">");
+ }
+ lines = StringUtils.escapeForHtml(source, true).split("\n");
+
+ sb.append("<table width=\"100%\"><tbody>");
+
+ String linePattern = "<tr class=\"{0}\"><td><a href=\"#L{2}\">{1}</a>\r</tr>";
+ for (int i = 0; i < lines.length; i++) {
+ String line = lines[i].replace('\r', ' ');
+ String cssClass = (i % 2 == 0) ? "even" : "odd";
+ sb.append(MessageFormat.format(linePattern, cssClass, line, "" + (i + 1)));
+ }
+ sb.append("</tbody></table></pre>");
+ sb.append("</pre>");
+ sb.append("</div>");
+ sb.append("</td>");
+ sb.append("<!-- end lines column -->");
+
+ sb.append("</tr></tbody></table>");
+ sb.append("<!-- end blob table -->");
+
+ return sb.toString();
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.view");
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/BranchesPage.html b/src/main/java/com/gitblit/wicket/pages/BranchesPage.html
new file mode 100644
index 00000000..65fd9b9d
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/BranchesPage.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <!-- branches -->
+ <div style="margin-top:5px;" wicket:id="branchesPanel">[branches panel]</div>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/BranchesPage.java b/src/main/java/com/gitblit/wicket/pages/BranchesPage.java
new file mode 100644
index 00000000..8684fb3d
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/BranchesPage.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import org.apache.wicket.PageParameters;
+
+import com.gitblit.wicket.panels.BranchesPanel;
+
+public class BranchesPage extends RepositoryPage {
+
+ public BranchesPage(PageParameters params) {
+ super(params);
+
+ add(new BranchesPanel("branchesPanel", getRepositoryModel(), getRepository(), -1, isShowAdmin() || isOwner()));
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.branches");
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/ChangePasswordPage.html b/src/main/java/com/gitblit/wicket/pages/ChangePasswordPage.html
new file mode 100644
index 00000000..36439a91
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/ChangePasswordPage.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+ <wicket:extend>
+ <body onload="document.getElementById('password').focus();">
+ <div>
+ <form style="text-align:center;" wicket:id="passwordForm">
+ <center>
+ <table class="plain">
+ <tr>
+ <th><wicket:message key="gb.password"></wicket:message> &nbsp;</th>
+ <td class="edit"><input type="password" wicket:id="password" id="password" size="30" tabindex="1" /></td>
+ </tr>
+ <tr>
+ <th><wicket:message key="gb.confirmPassword"></wicket:message> &nbsp;</th>
+ <td class="edit"><input type="password" wicket:id="confirmPassword" size="30" tabindex="2" /></td>
+ </tr>
+ </table>
+ <div class="form-actions">
+ <input class="btn btn-primary" type="submit" wicket:message="value:gb.save" wicket:id="save" tabindex="3" />
+ <input class="btn" type="submit" wicket:message="value:gb.cancel" wicket:id="cancel" tabindex="4" />
+ </div>
+ </center>
+ </form>
+ </div>
+ </body>
+ </wicket:extend>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/ChangePasswordPage.java b/src/main/java/com/gitblit/wicket/pages/ChangePasswordPage.java
new file mode 100644
index 00000000..c4014208
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/ChangePasswordPage.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+
+import org.apache.wicket.RestartResponseException;
+import org.apache.wicket.markup.html.form.Button;
+import org.apache.wicket.markup.html.form.PasswordTextField;
+import org.apache.wicket.markup.html.form.StatelessForm;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.protocol.http.WebResponse;
+
+import com.gitblit.GitBlit;
+import com.gitblit.GitBlitException;
+import com.gitblit.Keys;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+
+public class ChangePasswordPage extends RootSubPage {
+
+ IModel<String> password = new Model<String>("");
+ IModel<String> confirmPassword = new Model<String>("");
+
+ public ChangePasswordPage() {
+ super();
+
+ if (!GitBlitWebSession.get().isLoggedIn()) {
+ // Change password requires a login
+ throw new RestartResponseException(getApplication().getHomePage());
+ }
+
+ if (!GitBlit.getBoolean(Keys.web.authenticateAdminPages, true)
+ && !GitBlit.getBoolean(Keys.web.authenticateViewPages, false)) {
+ // no authentication enabled
+ throw new RestartResponseException(getApplication().getHomePage());
+ }
+
+ UserModel user = GitBlitWebSession.get().getUser();
+ if (!GitBlit.self().supportsCredentialChanges(user)) {
+ error(MessageFormat.format(getString("gb.userServiceDoesNotPermitPasswordChanges"),
+ GitBlit.getString(Keys.realm.userService, "${baseFolder}/users.conf")), true);
+ }
+
+ setupPage(getString("gb.changePassword"), user.username);
+
+ StatelessForm<Void> form = new StatelessForm<Void>("passwordForm") {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void onSubmit() {
+ String password = ChangePasswordPage.this.password.getObject();
+ String confirmPassword = ChangePasswordPage.this.confirmPassword.getObject();
+ // ensure passwords match
+ if (!password.equals(confirmPassword)) {
+ error(getString("gb.passwordsDoNotMatch"));
+ return;
+ }
+
+ // ensure password satisfies minimum length requirement
+ int minLength = GitBlit.getInteger(Keys.realm.minPasswordLength, 5);
+ if (minLength < 4) {
+ minLength = 4;
+ }
+ if (password.length() < minLength) {
+ error(MessageFormat.format(getString("gb.passwordTooShort"), minLength));
+ return;
+ }
+
+ UserModel user = GitBlitWebSession.get().getUser();
+
+ // convert to MD5 digest, if appropriate
+ String type = GitBlit.getString(Keys.realm.passwordStorage, "md5");
+ if (type.equalsIgnoreCase("md5")) {
+ // store MD5 digest of password
+ password = StringUtils.MD5_TYPE + StringUtils.getMD5(password);
+ } else if (type.equalsIgnoreCase("combined-md5")) {
+ // store MD5 digest of username+password
+ password = StringUtils.COMBINED_MD5_TYPE
+ + StringUtils.getMD5(user.username.toLowerCase() + password);
+ }
+
+ user.password = password;
+ try {
+ GitBlit.self().updateUserModel(user.username, user, false);
+ if (GitBlit.getBoolean(Keys.web.allowCookieAuthentication, false)) {
+ WebResponse response = (WebResponse) getRequestCycle().getResponse();
+ GitBlit.self().setCookie(response, user);
+ }
+ } catch (GitBlitException e) {
+ error(e.getMessage());
+ return;
+ }
+ setRedirect(false);
+ info(getString("gb.passwordChanged"));
+ setResponsePage(RepositoriesPage.class);
+ }
+ };
+ PasswordTextField passwordField = new PasswordTextField("password", password);
+ passwordField.setResetPassword(false);
+ form.add(passwordField);
+ PasswordTextField confirmPasswordField = new PasswordTextField("confirmPassword",
+ confirmPassword);
+ confirmPasswordField.setResetPassword(false);
+ form.add(confirmPasswordField);
+
+ form.add(new Button("save"));
+ Button cancel = new Button("cancel") {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void onSubmit() {
+ setRedirect(false);
+ error(getString("gb.passwordChangeAborted"));
+ setResponsePage(RepositoriesPage.class);
+ }
+ };
+ cancel.setDefaultFormProcessing(false);
+ form.add(cancel);
+
+ add(form);
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.html b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.html
new file mode 100644
index 00000000..de39f4ca
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <!-- commitdiff nav links -->
+ <div class="page_nav2">
+ <wicket:message key="gb.parent"></wicket:message>: <span wicket:id="parentLink">[parent link]</span> | <a wicket:id="patchLink"><wicket:message key="gb.patch"></wicket:message></a> | <a wicket:id="commitLink"><wicket:message key="gb.commit"></wicket:message></a>
+ </div>
+
+ <!-- commit header -->
+ <div wicket:id="commitHeader">[commit header]</div>
+
+ <!-- changed paths -->
+ <div style="padding-top:15px;">
+ <!-- commit legend -->
+ <div class="hidden-phone" style="text-align:right;" wicket:id="commitLegend"></div>
+
+ <div class="header"><i class="icon-file"></i> <wicket:message key="gb.changedFiles">[changed files]</wicket:message></div>
+ </div>
+
+ <table class="pretty">
+ <tr wicket:id="changedPath">
+ <td class="changeType"><span wicket:id="changeType">[change type]</span></td>
+ <td class="path"><span wicket:id="pathName">[commit path]</span></td>
+ <td class="hidden-phone rightAlign">
+ <span class="link">
+ <a wicket:id="patch"><wicket:message key="gb.patch"></wicket:message></a> | <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="blame"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a>
+ </span>
+ </td>
+ </tr>
+ </table>
+
+ <!-- diff content -->
+ <pre style="padding-top:10px;" wicket:id="diffText">[diff text]</pre>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
new file mode 100644
index 00000000..3ad70742
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.link.ExternalLink;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+import org.eclipse.jgit.diff.DiffEntry.ChangeType;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.PathModel.PathChangeModel;
+import com.gitblit.models.SubmoduleModel;
+import com.gitblit.utils.DiffUtils;
+import com.gitblit.utils.DiffUtils.DiffOutputType;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.CommitHeaderPanel;
+import com.gitblit.wicket.panels.CommitLegendPanel;
+import com.gitblit.wicket.panels.LinkPanel;
+
+public class CommitDiffPage extends RepositoryPage {
+
+ public CommitDiffPage(PageParameters params) {
+ super(params);
+
+ Repository r = getRepository();
+
+ DiffOutputType diffType = DiffOutputType.forName(GitBlit.getString(Keys.web.diffStyle,
+ DiffOutputType.GITBLIT.name()));
+
+ RevCommit commit = null, otherCommit = null;
+
+ if( objectId.contains("..") )
+ {
+ String[] parts = objectId.split("\\.\\.");
+ commit = getCommit(r, parts[0]);
+ otherCommit = getCommit(r, parts[1]);
+ }
+ else
+ {
+ commit = getCommit();
+ }
+
+ String diff;
+
+ if(otherCommit == null)
+ {
+ diff = DiffUtils.getCommitDiff(r, commit, diffType);
+ }
+ else
+ {
+ diff = DiffUtils.getDiff(r, commit, otherCommit, diffType);
+ }
+
+ List<String> parents = new ArrayList<String>();
+ if (commit.getParentCount() > 0) {
+ for (RevCommit parent : commit.getParents()) {
+ parents.add(parent.name());
+ }
+ }
+
+ // commit page links
+ if (parents.size() == 0) {
+ add(new Label("parentLink", getString("gb.none")));
+ } else {
+ add(new LinkPanel("parentLink", null, parents.get(0).substring(0, 8),
+ CommitDiffPage.class, newCommitParameter(parents.get(0))));
+ }
+ add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class,
+ WicketUtils.newObjectParameter(repositoryName, objectId)));
+ add(new BookmarkablePageLink<Void>("commitLink", CommitPage.class,
+ WicketUtils.newObjectParameter(repositoryName, objectId)));
+
+ add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
+
+ // changed paths list
+ List<PathChangeModel> paths;
+
+ if( otherCommit == null )
+ {
+ paths = JGitUtils.getFilesInCommit(r, commit);
+ }
+ else
+ {
+ paths = JGitUtils.getFilesInCommit(r, otherCommit);
+ }
+
+ add(new CommitLegendPanel("commitLegend", paths));
+ ListDataProvider<PathChangeModel> pathsDp = new ListDataProvider<PathChangeModel>(paths);
+ DataView<PathChangeModel> pathsView = new DataView<PathChangeModel>("changedPath", pathsDp) {
+ private static final long serialVersionUID = 1L;
+ int counter;
+
+ public void populateItem(final Item<PathChangeModel> item) {
+ final PathChangeModel entry = item.getModelObject();
+ Label changeType = new Label("changeType", "");
+ WicketUtils.setChangeTypeCssClass(changeType, entry.changeType);
+ setChangeTypeTooltip(changeType, entry.changeType);
+ item.add(changeType);
+
+ boolean hasSubmodule = false;
+ String submodulePath = null;
+ if (entry.isTree()) {
+ // tree
+ item.add(new LinkPanel("pathName", null, entry.path, TreePage.class,
+ WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ } else if (entry.isSubmodule()) {
+ // submodule
+ String submoduleId = entry.objectId;
+ SubmoduleModel submodule = getSubmodule(entry.path);
+ submodulePath = submodule.gitblitPath;
+ hasSubmodule = submodule.hasSubmodule;
+
+ item.add(new LinkPanel("pathName", "list", entry.path + " @ " +
+ getShortObjectId(submoduleId), TreePage.class,
+ WicketUtils
+ .newPathParameter(submodulePath, submoduleId, "")).setEnabled(hasSubmodule));
+ } else {
+ // blob
+ item.add(new LinkPanel("pathName", "list", entry.path, BlobPage.class,
+ WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ }
+
+ // quick links
+ if (entry.isSubmodule()) {
+ // submodule
+ item.add(new ExternalLink("patch", "").setEnabled(false));
+ item.add(new BookmarkablePageLink<Void>("view", CommitPage.class, WicketUtils
+ .newObjectParameter(submodulePath, entry.objectId)).setEnabled(hasSubmodule));
+ item.add(new ExternalLink("blame", "").setEnabled(false));
+ item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ } else {
+ // tree or blob
+ item.add(new BookmarkablePageLink<Void>("patch", PatchPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path))
+ .setEnabled(!entry.changeType.equals(ChangeType.ADD)));
+ }
+ WicketUtils.setAlternatingBackground(item, counter);
+ counter++;
+ }
+ };
+ add(pathsView);
+ add(new Label("diffText", diff).setEscapeModelStrings(false));
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.commitdiff");
+ }
+
+ private RevCommit getCommit(Repository r, String rev)
+ {
+ RevCommit otherCommit = JGitUtils.getCommit(r, rev);
+ if (otherCommit == null) {
+ error(MessageFormat.format(getString("gb.failedToFindCommit"), rev, repositoryName, getPageName()), true);
+ }
+ return otherCommit;
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/CommitPage.html b/src/main/java/com/gitblit/wicket/pages/CommitPage.html
new file mode 100644
index 00000000..79a038c9
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/CommitPage.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <!-- commit nav links -->
+ <div class="page_nav2">
+ <wicket:message key="gb.parent"></wicket:message>: <span wicket:id="parentLink">[parent link]</span> | <a wicket:id="patchLink"><wicket:message key="gb.patch"></wicket:message></a> | <span wicket:id="commitdiffLink">[commitdiff link]</span>
+ </div>
+
+ <!-- commit header -->
+ <div wicket:id="commitHeader">[commit header]</div>
+
+ <div class="row">
+
+
+ <div class="span10">
+ <!-- commit info -->
+ <table class="plain">
+ <tr><th><wicket:message key="gb.refs">refs</wicket:message></th><td><div wicket:id="refsPanel">[references]</div></td></tr>
+ <tr><th><wicket:message key="gb.author">author</wicket:message></th><td><span class="sha1" wicket:id="commitAuthor">[author</span></td></tr>
+ <tr><th></th><td><span class="sha1" wicket:id="commitAuthorDate">[author date]</span></td></tr>
+ <tr><th><wicket:message key="gb.committer">committer</wicket:message></th><td><span class="sha1" wicket:id="commitCommitter">[committer]</span></td></tr>
+ <tr><th></th><td><span class="sha1" wicket:id="commitCommitterDate">[commit date]</span></td></tr>
+ <tr class="hidden-phone"><th><wicket:message key="gb.commit">commit</wicket:message></th><td><span class="sha1" wicket:id="commitId">[commit id]</span></td></tr>
+ <tr class="hidden-phone"><th><wicket:message key="gb.tree">tree</wicket:message></th>
+ <td><span class="sha1" wicket:id="commitTree">[commit tree]</span>
+ <span class="link">
+ <a wicket:id="treeLink"><wicket:message key="gb.tree"></wicket:message></a> | <span wicket:id="compressedLinks"></span>
+ </span>
+ </td></tr>
+ <tr class="hidden-phone"><th valign="top"><wicket:message key="gb.parent">parent</wicket:message></th>
+ <td>
+ <span wicket:id="commitParents">
+ <span class="sha1" wicket:id="commitParent">[commit parents]</span>
+ <span class="link">
+ <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="diff"><wicket:message key="gb.diff"></wicket:message></a>
+ </span><br/>
+ </span>
+ </td>
+ </tr>
+ </table>
+ </div>
+
+ </div>
+
+ <!-- full message -->
+ <div class="commit_message" wicket:id="fullMessage">[commit message]</div>
+
+ <!-- git notes -->
+ <table class="gitnotes">
+ <tr wicket:id="notes">
+ <td class="info">
+ <table>
+ <tr><td><span wicket:id="refName"></span></td></tr>
+ <tr><td><span class="sha1" wicket:id="authorName"></span></td></tr>
+ <tr><td><span class="sha1" wicket:id="authorDate"></span></td></tr>
+ </table>
+ <!-- Note Author Gravatar -->
+ <span style="vertical-align: top;" wicket:id="noteAuthorAvatar" />
+ </td>
+ <td class="message"><span class="sha1" wicket:id="noteContent"></span></td>
+ </tr>
+ </table>
+
+ <!-- commit legend -->
+ <div class="hidden-phone" style="text-align:right;" wicket:id="commitLegend"></div>
+
+ <!-- header -->
+ <div class="header"><i class="icon-file"></i> <wicket:message key="gb.changedFiles">[changed files]</wicket:message></div>
+
+ <!-- changed paths -->
+ <table class="pretty">
+ <tr wicket:id="changedPath">
+ <td class="changeType"><span wicket:id="changeType">[change type]</span></td>
+ <td class="path"><span wicket:id="pathName">[commit path]</span></td>
+ <td class="hidden-phone rightAlign">
+ <span class="link">
+ <a wicket:id="diff"><wicket:message key="gb.diff"></wicket:message></a> | <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="blame"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a>
+ </span>
+ </td>
+ </tr>
+ </table>
+
+ <wicket:fragment wicket:id="fullPersonIdent">
+ <span wicket:id="personName"></span><span wicket:id="personAddress"></span>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="partialPersonIdent">
+ <span wicket:id="personName"></span>
+ </wicket:fragment>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/CommitPage.java b/src/main/java/com/gitblit/wicket/pages/CommitPage.java
new file mode 100644
index 00000000..c5a24c8d
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/CommitPage.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.link.ExternalLink;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+import org.apache.wicket.model.StringResourceModel;
+import org.eclipse.jgit.diff.DiffEntry.ChangeType;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import com.gitblit.Constants;
+import com.gitblit.GitBlit;
+import com.gitblit.models.GitNote;
+import com.gitblit.models.PathModel.PathChangeModel;
+import com.gitblit.models.SubmoduleModel;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.CommitHeaderPanel;
+import com.gitblit.wicket.panels.CommitLegendPanel;
+import com.gitblit.wicket.panels.CompressedDownloadsPanel;
+import com.gitblit.wicket.panels.GravatarImage;
+import com.gitblit.wicket.panels.LinkPanel;
+import com.gitblit.wicket.panels.RefsPanel;
+
+public class CommitPage extends RepositoryPage {
+
+ public CommitPage(PageParameters params) {
+ super(params);
+
+ Repository r = getRepository();
+ RevCommit c = getCommit();
+
+ List<String> parents = new ArrayList<String>();
+ if (c.getParentCount() > 0) {
+ for (RevCommit parent : c.getParents()) {
+ parents.add(parent.name());
+ }
+ }
+
+ // commit page links
+ if (parents.size() == 0) {
+ add(new Label("parentLink", "none"));
+ add(new Label("commitdiffLink", getString("gb.commitdiff")));
+ } else {
+ add(new LinkPanel("parentLink", null, getShortObjectId(parents.get(0)),
+ CommitPage.class, newCommitParameter(parents.get(0))));
+ add(new LinkPanel("commitdiffLink", null, new StringResourceModel("gb.commitdiff",
+ this, null), CommitDiffPage.class, WicketUtils.newObjectParameter(
+ repositoryName, objectId)));
+ }
+ add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class,
+ WicketUtils.newObjectParameter(repositoryName, objectId)));
+
+ add(new CommitHeaderPanel("commitHeader", repositoryName, c));
+
+ addRefs(r, c);
+
+ // author
+ add(createPersonPanel("commitAuthor", c.getAuthorIdent(), Constants.SearchType.AUTHOR));
+ add(WicketUtils.createTimestampLabel("commitAuthorDate", c.getAuthorIdent().getWhen(),
+ getTimeZone(), getTimeUtils()));
+
+ // committer
+ add(createPersonPanel("commitCommitter", c.getCommitterIdent(), Constants.SearchType.COMMITTER));
+ add(WicketUtils.createTimestampLabel("commitCommitterDate",
+ c.getCommitterIdent().getWhen(), getTimeZone(), getTimeUtils()));
+
+ add(new Label("commitId", c.getName()));
+
+ add(new LinkPanel("commitTree", "list", c.getTree().getName(), TreePage.class,
+ newCommitParameter()));
+ add(new BookmarkablePageLink<Void>("treeLink", TreePage.class, newCommitParameter()));
+ final String baseUrl = WicketUtils.getGitblitURL(getRequest());
+
+ add(new CompressedDownloadsPanel("compressedLinks", baseUrl, repositoryName, objectId, null));
+
+ // Parent Commits
+ ListDataProvider<String> parentsDp = new ListDataProvider<String>(parents);
+ DataView<String> parentsView = new DataView<String>("commitParents", parentsDp) {
+ private static final long serialVersionUID = 1L;
+
+ public void populateItem(final Item<String> item) {
+ String entry = item.getModelObject();
+ item.add(new LinkPanel("commitParent", "list", entry, CommitPage.class,
+ newCommitParameter(entry)));
+ item.add(new BookmarkablePageLink<Void>("view", CommitPage.class,
+ newCommitParameter(entry)));
+ item.add(new BookmarkablePageLink<Void>("diff", CommitDiffPage.class,
+ newCommitParameter(entry)));
+ }
+ };
+ add(parentsView);
+
+ addFullText("fullMessage", c.getFullMessage(), true);
+
+ // git notes
+ List<GitNote> notes = JGitUtils.getNotesOnCommit(r, c);
+ ListDataProvider<GitNote> notesDp = new ListDataProvider<GitNote>(notes);
+ DataView<GitNote> notesView = new DataView<GitNote>("notes", notesDp) {
+ private static final long serialVersionUID = 1L;
+
+ public void populateItem(final Item<GitNote> item) {
+ GitNote entry = item.getModelObject();
+ item.add(new RefsPanel("refName", repositoryName, Arrays.asList(entry.notesRef)));
+ item.add(createPersonPanel("authorName", entry.notesRef.getAuthorIdent(),
+ Constants.SearchType.AUTHOR));
+ item.add(new GravatarImage("noteAuthorAvatar", entry.notesRef.getAuthorIdent()));
+ item.add(WicketUtils.createTimestampLabel("authorDate", entry.notesRef
+ .getAuthorIdent().getWhen(), getTimeZone(), getTimeUtils()));
+ item.add(new Label("noteContent", GitBlit.self().processCommitMessage(
+ repositoryName, entry.content)).setEscapeModelStrings(false));
+ }
+ };
+ add(notesView.setVisible(notes.size() > 0));
+
+ // changed paths list
+ List<PathChangeModel> paths = JGitUtils.getFilesInCommit(r, c);
+ add(new CommitLegendPanel("commitLegend", paths));
+ ListDataProvider<PathChangeModel> pathsDp = new ListDataProvider<PathChangeModel>(paths);
+ DataView<PathChangeModel> pathsView = new DataView<PathChangeModel>("changedPath", pathsDp) {
+ private static final long serialVersionUID = 1L;
+ int counter;
+
+ public void populateItem(final Item<PathChangeModel> item) {
+ final PathChangeModel entry = item.getModelObject();
+ Label changeType = new Label("changeType", "");
+ WicketUtils.setChangeTypeCssClass(changeType, entry.changeType);
+ setChangeTypeTooltip(changeType, entry.changeType);
+ item.add(changeType);
+
+ boolean hasSubmodule = false;
+ String submodulePath = null;
+ if (entry.isTree()) {
+ // tree
+ item.add(new LinkPanel("pathName", null, entry.path, TreePage.class,
+ WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ } else if (entry.isSubmodule()) {
+ // submodule
+ String submoduleId = entry.objectId;
+ SubmoduleModel submodule = getSubmodule(entry.path);
+ submodulePath = submodule.gitblitPath;
+ hasSubmodule = submodule.hasSubmodule;
+
+ item.add(new LinkPanel("pathName", "list", entry.path + " @ " +
+ getShortObjectId(submoduleId), TreePage.class,
+ WicketUtils.newPathParameter(submodulePath, submoduleId, "")).setEnabled(hasSubmodule));
+ } else {
+ // blob
+ String displayPath = entry.path;
+ String path = entry.path;
+ if (entry.isSymlink()) {
+ path = JGitUtils.getStringContent(getRepository(), getCommit().getTree(), path);
+ displayPath = entry.path + " -> " + path;
+ }
+ item.add(new LinkPanel("pathName", "list", displayPath, BlobPage.class,
+ WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, path)));
+ }
+
+ // quick links
+ if (entry.isSubmodule()) {
+ // submodule
+ item.add(new BookmarkablePageLink<Void>("diff", BlobDiffPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path))
+ .setEnabled(!entry.changeType.equals(ChangeType.ADD)));
+ item.add(new BookmarkablePageLink<Void>("view", CommitPage.class, WicketUtils
+ .newObjectParameter(submodulePath, entry.objectId)).setEnabled(hasSubmodule));
+ item.add(new ExternalLink("blame", "").setEnabled(false));
+ item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path))
+ .setEnabled(!entry.changeType.equals(ChangeType.ADD)));
+ } else {
+ // tree or blob
+ item.add(new BookmarkablePageLink<Void>("diff", BlobDiffPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path))
+ .setEnabled(!entry.changeType.equals(ChangeType.ADD)
+ && !entry.changeType.equals(ChangeType.DELETE)));
+ item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path))
+ .setEnabled(!entry.changeType.equals(ChangeType.ADD)));
+ item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path))
+ .setEnabled(!entry.changeType.equals(ChangeType.ADD)));
+ }
+
+ WicketUtils.setAlternatingBackground(item, counter);
+ counter++;
+ }
+ };
+ add(pathsView);
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.commit");
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/DocsPage.html b/src/main/java/com/gitblit/wicket/pages/DocsPage.html
new file mode 100644
index 00000000..ad93000c
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/DocsPage.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <!-- header -->
+ <div style="margin-top:5px;" class="header"><i class="icon-book" style="vertical-align: middle;"></i> <b><span wicket:id="header">[header]</span></b></div>
+
+ <!-- documents -->
+ <table style="width:100%" class="pretty">
+ <tr wicket:id="document">
+ <td class="icon"><img wicket:id="docIcon" /></td>
+ <td><span wicket:id="docName"></span></td>
+ <td class="size"><span wicket:id="docSize">[doc size]</span></td>
+ <td class="treeLinks">
+ <span class="hidden-phone link">
+ <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="raw"><wicket:message key="gb.raw"></wicket:message></a> | <a wicket:id="blame"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a>
+ </span>
+ </td>
+ </tr>
+ </table>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/DocsPage.java b/src/main/java/com/gitblit/wicket/pages/DocsPage.java
new file mode 100644
index 00000000..9ddc98d3
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/DocsPage.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+import org.eclipse.jgit.lib.Repository;
+
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.PathModel;
+import com.gitblit.utils.ByteFormat;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.LinkPanel;
+
+public class DocsPage extends RepositoryPage {
+
+ public DocsPage(PageParameters params) {
+ super(params);
+
+ Repository r = getRepository();
+ List<String> extensions = GitBlit.getStrings(Keys.web.markdownExtensions);
+ List<PathModel> paths = JGitUtils.getDocuments(r, extensions);
+
+ final ByteFormat byteFormat = new ByteFormat();
+
+ add(new Label("header", getString("gb.docs")));
+
+ // documents list
+ ListDataProvider<PathModel> pathsDp = new ListDataProvider<PathModel>(paths);
+ DataView<PathModel> pathsView = new DataView<PathModel>("document", pathsDp) {
+ private static final long serialVersionUID = 1L;
+ int counter;
+
+ public void populateItem(final Item<PathModel> item) {
+ PathModel entry = item.getModelObject();
+ item.add(WicketUtils.newImage("docIcon", "file_world_16x16.png"));
+ item.add(new Label("docSize", byteFormat.format(entry.size)));
+ item.add(new LinkPanel("docName", "list", entry.name, BlobPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+
+ // links
+ item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ item.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ WicketUtils.setAlternatingBackground(item, counter);
+ counter++;
+ }
+ };
+ add(pathsView);
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.docs");
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html b/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html
new file mode 100644
index 00000000..7fc0de23
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html
@@ -0,0 +1,111 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<wicket:extend>
+<body onload="document.getElementById('name').focus();">
+ <form style="padding-top:5px;" wicket:id="editForm">
+
+<div class="tabbable">
+ <!-- tab titles -->
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="#general" data-toggle="tab"><wicket:message key="gb.general"></wicket:message></a></li>
+ <li><a href="#permissions" data-toggle="tab"><wicket:message key="gb.accessPermissions"></wicket:message></a></li>
+ <li><a href="#federation" data-toggle="tab"><wicket:message key="gb.federation"></wicket:message></a></li>
+ <li><a href="#search" data-toggle="tab"><wicket:message key="gb.search"></wicket:message></a></li>
+ <li><a href="#hooks" data-toggle="tab"><wicket:message key="gb.hookScripts"></wicket:message></a></li>
+ </ul>
+
+ <!-- tab content -->
+ <div class="tab-content">
+
+ <!-- general tab -->
+ <div class="tab-pane active" id="general">
+ <table class="plain">
+ <tbody class="settings">
+ <tr><th><wicket:message key="gb.name"></wicket:message></th><td class="edit"><input class="span4" type="text" wicket:id="name" id="name" size="40" tabindex="1" /> &nbsp;<span class="help-inline"><wicket:message key="gb.nameDescription"></wicket:message></span></td></tr>
+ <tr><th><wicket:message key="gb.description"></wicket:message></th><td class="edit"><input class="span4" type="text" wicket:id="description" size="40" tabindex="2" /></td></tr>
+ <tr><th colspan="2"><hr/></th></tr>
+ <tr><th><wicket:message key="gb.origin"></wicket:message></th><td class="edit"><input class="span5" type="text" wicket:id="origin" size="80" tabindex="3" /></td></tr>
+ <tr><th><wicket:message key="gb.headRef"></wicket:message></th><td class="edit"><select class="span3" wicket:id="HEAD" tabindex="4" /> &nbsp;<span class="help-inline"><wicket:message key="gb.headRefDescription"></wicket:message></span></td></tr>
+ <tr><th><wicket:message key="gb.gcPeriod"></wicket:message></th><td class="edit"><select class="span2" wicket:id="gcPeriod" tabindex="5" /> &nbsp;<span class="help-inline"><wicket:message key="gb.gcPeriodDescription"></wicket:message></span></td></tr>
+ <tr><th><wicket:message key="gb.gcThreshold"></wicket:message></th><td class="edit"><input class="span1" type="text" wicket:id="gcThreshold" tabindex="6" /> &nbsp;<span class="help-inline"><wicket:message key="gb.gcThresholdDescription"></wicket:message></span></td></tr>
+ <tr><th colspan="2"><hr/></th></tr>
+ <tr><th><wicket:message key="gb.enableTickets"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="useTickets" tabindex="7" /> &nbsp;<span class="help-inline"><wicket:message key="gb.useTicketsDescription"></wicket:message></span></label></td></tr>
+ <tr><th><wicket:message key="gb.enableDocs"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="useDocs" tabindex="8" /> &nbsp;<span class="help-inline"><wicket:message key="gb.useDocsDescription"></wicket:message></span></label></td></tr>
+ <tr><th><wicket:message key="gb.showRemoteBranches"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="showRemoteBranches" tabindex="9" /> &nbsp;<span class="help-inline"><wicket:message key="gb.showRemoteBranchesDescription"></wicket:message></span></label></td></tr>
+ <tr><th><wicket:message key="gb.showReadme"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="showReadme" tabindex="10" /> &nbsp;<span class="help-inline"><wicket:message key="gb.showReadmeDescription"></wicket:message></span></label></td></tr>
+ <tr><th><wicket:message key="gb.skipSizeCalculation"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="skipSizeCalculation" tabindex="11" /> &nbsp;<span class="help-inline"><wicket:message key="gb.skipSizeCalculationDescription"></wicket:message></span></label></td></tr>
+ <tr><th><wicket:message key="gb.skipSummaryMetrics"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="skipSummaryMetrics" tabindex="12" /> &nbsp;<span class="help-inline"><wicket:message key="gb.skipSummaryMetricsDescription"></wicket:message></span></label></td></tr>
+ <tr><th><wicket:message key="gb.maxActivityCommits"></wicket:message></th><td class="edit"><select class="span2" wicket:id="maxActivityCommits" tabindex="13" /> &nbsp;<span class="help-inline"><wicket:message key="gb.maxActivityCommitsDescription"></wicket:message></span></td></tr>
+ <tr><th colspan="2"><hr/></th></tr>
+ <tr><th><wicket:message key="gb.mailingLists"></wicket:message></th><td class="edit"><input class="span8" type="text" wicket:id="mailingLists" size="40" tabindex="14" /></td></tr>
+ </tbody>
+ </table>
+ </div>
+
+ <!-- access permissions -->
+ <div class="tab-pane" id="permissions">
+ <table class="plain">
+ <tbody class="settings">
+ <tr><th><wicket:message key="gb.owners"></wicket:message></th><td class="edit"><span wicket:id="owners" tabindex="15" /> </td></tr>
+ <tr><th colspan="2"><hr/></th></tr>
+ <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select class="span4" wicket:id="accessRestriction" tabindex="16" /></td></tr>
+ <tr><th colspan="2"><hr/></th></tr>
+ <tr><th><wicket:message key="gb.authorizationControl"></wicket:message></th><td style="padding:2px;"><span class="authorizationControl" wicket:id="authorizationControl"></span></td></tr>
+ <tr><th colspan="2"><hr/></th></tr>
+ <tr><th><wicket:message key="gb.isFrozen"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="isFrozen" tabindex="17" /> &nbsp;<span class="help-inline"><wicket:message key="gb.isFrozenDescription"></wicket:message></span></label></td></tr>
+ <tr><th><wicket:message key="gb.allowForks"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="allowForks" tabindex="18" /> &nbsp;<span class="help-inline"><wicket:message key="gb.allowForksDescription"></wicket:message></span></label></td></tr>
+ <tr><th><wicket:message key="gb.verifyCommitter"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="verifyCommitter" tabindex="19" /> &nbsp;<span class="help-inline"><wicket:message key="gb.verifyCommitterDescription"></wicket:message></span><br/><span class="help-inline" style="padding-left:10px;"><wicket:message key="gb.verifyCommitterNote"></wicket:message></span></label></td></tr>
+ <tr><th colspan="2"><hr/></th></tr>
+ <tr><th><wicket:message key="gb.userPermissions"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>
+ <tr><th colspan="2"><hr/></th></tr>
+ <tr><th><wicket:message key="gb.teamPermissions"></wicket:message></th><td style="padding:2px;"><span wicket:id="teams"></span></td></tr>
+ </tbody>
+ </table>
+ </div>
+
+ <!-- federation -->
+ <div class="tab-pane" id="federation">
+ <table class="plain">
+ <tbody class="settings">
+ <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span4" wicket:id="federationStrategy" tabindex="20" /></td></tr>
+ <tr><th><wicket:message key="gb.federationSets"></wicket:message></th><td style="padding:2px;"><span wicket:id="federationSets"></span></td></tr>
+ </tbody>
+ </table>
+ </div>
+
+ <!-- search -->
+ <div class="tab-pane" id="search">
+ <table class="plain">
+ <tbody class="settings">
+ <tr><th><wicket:message key="gb.indexedBranches"></wicket:message></th><td style="padding:2px;"><span wicket:id="indexedBranches"></span></td></tr>
+ </tbody>
+ </table>
+ </div>
+
+ <!-- hooks -->
+ <div class="tab-pane" id="hooks">
+ <table class="plain">
+ <tbody class="settings">
+ <tr><th><wicket:message key="gb.preReceiveScripts"></wicket:message><p></p><span wicket:id="inheritedPreReceive"></span></th><td style="padding:2px;"><span wicket:id="preReceiveScripts"></span></td></tr>
+ <tr><th><wicket:message key="gb.postReceiveScripts"></wicket:message><p></p><span wicket:id="inheritedPostReceive"></span></th><td style="padding:2px;"><span wicket:id="postReceiveScripts"></span></td></tr>
+ <div wicket:id="customFieldsSection">
+ <tr><td colspan="2"><h3><wicket:message key="gb.customFields"></wicket:message> &nbsp;<small><wicket:message key="gb.customFieldsDescription"></wicket:message></small></h3></td></tr>
+ <tr wicket:id="customFieldsListView"><th><span wicket:id="customFieldLabel"></span></th><td class="edit"><input class="span8" type="text" wicket:id="customFieldValue" /></td></tr>
+ </div>
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="form-actions"><input class="btn btn-primary" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" /> &nbsp; <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /></div>
+ </div>
+</div>
+</form>
+</body>
+</wicket:extend>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java
new file mode 100644
index 00000000..d68d6550
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java
@@ -0,0 +1,696 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.behavior.SimpleAttributeModifier;
+import org.apache.wicket.extensions.markup.html.form.palette.Palette;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Button;
+import org.apache.wicket.markup.html.form.CheckBox;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.IChoiceRenderer;
+import org.apache.wicket.markup.html.form.RadioChoice;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.util.CollectionModel;
+import org.apache.wicket.model.util.ListModel;
+
+import com.gitblit.Constants;
+import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.Constants.AuthorizationControl;
+import com.gitblit.Constants.FederationStrategy;
+import com.gitblit.Constants.RegistrantType;
+import com.gitblit.GitBlit;
+import com.gitblit.GitBlitException;
+import com.gitblit.Keys;
+import com.gitblit.models.RegistrantAccessPermission;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.StringChoiceRenderer;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.BulletListPanel;
+import com.gitblit.wicket.panels.RegistrantPermissionsPanel;
+
+public class EditRepositoryPage extends RootSubPage {
+
+ private final boolean isCreate;
+
+ private boolean isAdmin;
+
+ RepositoryModel repositoryModel;
+
+ private IModel<String> mailingLists;
+
+ public EditRepositoryPage() {
+ // create constructor
+ super();
+ isCreate = true;
+ RepositoryModel model = new RepositoryModel();
+ String restriction = GitBlit.getString(Keys.git.defaultAccessRestriction, null);
+ model.accessRestriction = AccessRestrictionType.fromName(restriction);
+ String authorization = GitBlit.getString(Keys.git.defaultAuthorizationControl, null);
+ model.authorizationControl = AuthorizationControl.fromName(authorization);
+
+ GitBlitWebSession session = GitBlitWebSession.get();
+ UserModel user = session.getUser();
+ if (user != null && user.canCreate() && !user.canAdmin()) {
+ // personal create permissions, inject personal repository path
+ model.name = user.getPersonalPath() + "/";
+ model.projectPath = user.getPersonalPath();
+ model.addOwner(user.username);
+ // personal repositories are private by default
+ model.accessRestriction = AccessRestrictionType.VIEW;
+ model.authorizationControl = AuthorizationControl.NAMED;
+ }
+
+ setupPage(model);
+ setStatelessHint(false);
+ setOutputMarkupId(true);
+ }
+
+ public EditRepositoryPage(PageParameters params) {
+ // edit constructor
+ super(params);
+ isCreate = false;
+ String name = WicketUtils.getRepositoryName(params);
+ RepositoryModel model = GitBlit.self().getRepositoryModel(name);
+ setupPage(model);
+ setStatelessHint(false);
+ setOutputMarkupId(true);
+ }
+
+ @Override
+ protected boolean requiresPageMap() {
+ return true;
+ }
+
+ protected void setupPage(RepositoryModel model) {
+ this.repositoryModel = model;
+
+ // ensure this user can create or edit this repository
+ checkPermissions(repositoryModel);
+
+ List<String> indexedBranches = new ArrayList<String>();
+ List<String> federationSets = new ArrayList<String>();
+ final List<RegistrantAccessPermission> repositoryUsers = new ArrayList<RegistrantAccessPermission>();
+ final List<RegistrantAccessPermission> repositoryTeams = new ArrayList<RegistrantAccessPermission>();
+ List<String> preReceiveScripts = new ArrayList<String>();
+ List<String> postReceiveScripts = new ArrayList<String>();
+
+ GitBlitWebSession session = GitBlitWebSession.get();
+ final UserModel user = session.getUser() == null ? UserModel.ANONYMOUS : session.getUser();
+ final boolean allowEditName = isCreate || isAdmin || repositoryModel.isUsersPersonalRepository(user.username);
+
+ if (isCreate) {
+ if (user.canAdmin()) {
+ super.setupPage(getString("gb.newRepository"), "");
+ } else {
+ super.setupPage(getString("gb.newRepository"), user.getDisplayName());
+ }
+ } else {
+ super.setupPage(getString("gb.edit"), repositoryModel.name);
+ repositoryUsers.addAll(GitBlit.self().getUserAccessPermissions(repositoryModel));
+ repositoryTeams.addAll(GitBlit.self().getTeamAccessPermissions(repositoryModel));
+ Collections.sort(repositoryUsers);
+ Collections.sort(repositoryTeams);
+
+ federationSets.addAll(repositoryModel.federationSets);
+ if (!ArrayUtils.isEmpty(repositoryModel.indexedBranches)) {
+ indexedBranches.addAll(repositoryModel.indexedBranches);
+ }
+ }
+
+ final String oldName = repositoryModel.name;
+
+ final RegistrantPermissionsPanel usersPalette = new RegistrantPermissionsPanel("users",
+ RegistrantType.USER, GitBlit.self().getAllUsernames(), repositoryUsers, getAccessPermissions());
+ final RegistrantPermissionsPanel teamsPalette = new RegistrantPermissionsPanel("teams",
+ RegistrantType.TEAM, GitBlit.self().getAllTeamnames(), repositoryTeams, getAccessPermissions());
+
+ // owners palette
+ List<String> owners = new ArrayList<String>(repositoryModel.owners);
+ List<String> persons = GitBlit.self().getAllUsernames();
+ final Palette<String> ownersPalette = new Palette<String>("owners", new ListModel<String>(owners), new CollectionModel<String>(
+ persons), new StringChoiceRenderer(), 12, true);
+
+ // indexed local branches palette
+ List<String> allLocalBranches = new ArrayList<String>();
+ allLocalBranches.add(Constants.DEFAULT_BRANCH);
+ allLocalBranches.addAll(repositoryModel.getLocalBranches());
+ boolean luceneEnabled = GitBlit.getBoolean(Keys.web.allowLuceneIndexing, true);
+ final Palette<String> indexedBranchesPalette = new Palette<String>("indexedBranches", new ListModel<String>(
+ indexedBranches), new CollectionModel<String>(allLocalBranches),
+ new StringChoiceRenderer(), 8, false);
+ indexedBranchesPalette.setEnabled(luceneEnabled);
+
+ // federation sets palette
+ List<String> sets = GitBlit.getStrings(Keys.federation.sets);
+ final Palette<String> federationSetsPalette = new Palette<String>("federationSets",
+ new ListModel<String>(federationSets), new CollectionModel<String>(sets),
+ new StringChoiceRenderer(), 8, false);
+
+ // pre-receive palette
+ if (!ArrayUtils.isEmpty(repositoryModel.preReceiveScripts)) {
+ preReceiveScripts.addAll(repositoryModel.preReceiveScripts);
+ }
+ final Palette<String> preReceivePalette = new Palette<String>("preReceiveScripts",
+ new ListModel<String>(preReceiveScripts), new CollectionModel<String>(GitBlit
+ .self().getPreReceiveScriptsUnused(repositoryModel)),
+ new StringChoiceRenderer(), 12, true);
+
+ // post-receive palette
+ if (!ArrayUtils.isEmpty(repositoryModel.postReceiveScripts)) {
+ postReceiveScripts.addAll(repositoryModel.postReceiveScripts);
+ }
+ final Palette<String> postReceivePalette = new Palette<String>("postReceiveScripts",
+ new ListModel<String>(postReceiveScripts), new CollectionModel<String>(GitBlit
+ .self().getPostReceiveScriptsUnused(repositoryModel)),
+ new StringChoiceRenderer(), 12, true);
+
+ // custom fields
+ final Map<String, String> customFieldsMap = GitBlit.getMap(Keys.groovy.customFields);
+ List<String> customKeys = new ArrayList<String>(customFieldsMap.keySet());
+ final ListView<String> customFieldsListView = new ListView<String>("customFieldsListView", customKeys) {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void populateItem(ListItem<String> item) {
+ String key = item.getModelObject();
+ item.add(new Label("customFieldLabel", customFieldsMap.get(key)));
+
+ String value = "";
+ if (repositoryModel.customFields != null && repositoryModel.customFields.containsKey(key)) {
+ value = repositoryModel.customFields.get(key);
+ }
+ TextField<String> field = new TextField<String>("customFieldValue", new Model<String>(value));
+ item.add(field);
+ }
+ };
+ customFieldsListView.setReuseItems(true);
+
+ CompoundPropertyModel<RepositoryModel> rModel = new CompoundPropertyModel<RepositoryModel>(
+ repositoryModel);
+ Form<RepositoryModel> form = new Form<RepositoryModel>("editForm", rModel) {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void onSubmit() {
+ try {
+ // confirm a repository name was entered
+ if (repositoryModel.name == null && StringUtils.isEmpty(repositoryModel.name)) {
+ error(getString("gb.pleaseSetRepositoryName"));
+ return;
+ }
+
+ // ensure name is trimmed
+ repositoryModel.name = repositoryModel.name.trim();
+
+ // automatically convert backslashes to forward slashes
+ repositoryModel.name = repositoryModel.name.replace('\\', '/');
+ // Automatically replace // with /
+ repositoryModel.name = repositoryModel.name.replace("//", "/");
+
+ // prohibit folder paths
+ if (repositoryModel.name.startsWith("/")) {
+ error(getString("gb.illegalLeadingSlash"));
+ return;
+ }
+ if (repositoryModel.name.startsWith("../")) {
+ error(getString("gb.illegalRelativeSlash"));
+ return;
+ }
+ if (repositoryModel.name.contains("/../")) {
+ error(getString("gb.illegalRelativeSlash"));
+ return;
+ }
+ if (repositoryModel.name.endsWith("/")) {
+ repositoryModel.name = repositoryModel.name.substring(0, repositoryModel.name.length() - 1);
+ }
+
+ // confirm valid characters in repository name
+ Character c = StringUtils.findInvalidCharacter(repositoryModel.name);
+ if (c != null) {
+ error(MessageFormat.format(getString("gb.illegalCharacterRepositoryName"),
+ c));
+ return;
+ }
+
+ if (user.canCreate() && !user.canAdmin() && allowEditName) {
+ // ensure repository name begins with the user's path
+ if (!repositoryModel.name.startsWith(user.getPersonalPath())) {
+ error(MessageFormat.format(getString("gb.illegalPersonalRepositoryLocation"),
+ user.getPersonalPath()));
+ return;
+ }
+
+ if (repositoryModel.name.equals(user.getPersonalPath())) {
+ // reset path prefix and show error
+ repositoryModel.name = user.getPersonalPath() + "/";
+ error(getString("gb.pleaseSetRepositoryName"));
+ return;
+ }
+ }
+
+ // confirm access restriction selection
+ if (repositoryModel.accessRestriction == null) {
+ error(getString("gb.selectAccessRestriction"));
+ return;
+ }
+
+ // confirm federation strategy selection
+ if (repositoryModel.federationStrategy == null) {
+ error(getString("gb.selectFederationStrategy"));
+ return;
+ }
+
+ // save federation set preferences
+ if (repositoryModel.federationStrategy.exceeds(FederationStrategy.EXCLUDE)) {
+ repositoryModel.federationSets.clear();
+ Iterator<String> sets = federationSetsPalette.getSelectedChoices();
+ while (sets.hasNext()) {
+ repositoryModel.federationSets.add(sets.next());
+ }
+ }
+
+ // set mailing lists
+ String ml = mailingLists.getObject();
+ if (!StringUtils.isEmpty(ml)) {
+ Set<String> list = new HashSet<String>();
+ for (String address : ml.split("(,|\\s)")) {
+ if (StringUtils.isEmpty(address)) {
+ continue;
+ }
+ list.add(address.toLowerCase());
+ }
+ repositoryModel.mailingLists = new ArrayList<String>(list);
+ }
+
+ // indexed branches
+ List<String> indexedBranches = new ArrayList<String>();
+ Iterator<String> branches = indexedBranchesPalette.getSelectedChoices();
+ while (branches.hasNext()) {
+ indexedBranches.add(branches.next());
+ }
+ repositoryModel.indexedBranches = indexedBranches;
+
+ // owners
+ repositoryModel.owners.clear();
+ Iterator<String> owners = ownersPalette.getSelectedChoices();
+ while (owners.hasNext()) {
+ repositoryModel.addOwner(owners.next());
+ }
+
+ // pre-receive scripts
+ List<String> preReceiveScripts = new ArrayList<String>();
+ Iterator<String> pres = preReceivePalette.getSelectedChoices();
+ while (pres.hasNext()) {
+ preReceiveScripts.add(pres.next());
+ }
+ repositoryModel.preReceiveScripts = preReceiveScripts;
+
+ // post-receive scripts
+ List<String> postReceiveScripts = new ArrayList<String>();
+ Iterator<String> post = postReceivePalette.getSelectedChoices();
+ while (post.hasNext()) {
+ postReceiveScripts.add(post.next());
+ }
+ repositoryModel.postReceiveScripts = postReceiveScripts;
+
+ // custom fields
+ repositoryModel.customFields = new LinkedHashMap<String, String>();
+ for (int i = 0; i < customFieldsListView.size(); i++) {
+ ListItem<String> child = (ListItem<String>) customFieldsListView.get(i);
+ String key = child.getModelObject();
+
+ TextField<String> field = (TextField<String>) child.get("customFieldValue");
+ String value = field.getValue();
+
+ repositoryModel.customFields.put(key, value);
+ }
+
+ // save the repository
+ GitBlit.self().updateRepositoryModel(oldName, repositoryModel, isCreate);
+
+ // repository access permissions
+ if (repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE)) {
+ GitBlit.self().setUserAccessPermissions(repositoryModel, repositoryUsers);
+ GitBlit.self().setTeamAccessPermissions(repositoryModel, repositoryTeams);
+ }
+ } catch (GitBlitException e) {
+ error(e.getMessage());
+ return;
+ }
+ setRedirect(false);
+ setResponsePage(RepositoriesPage.class);
+ }
+ };
+
+ // do not let the browser pre-populate these fields
+ form.add(new SimpleAttributeModifier("autocomplete", "off"));
+
+ // field names reflective match RepositoryModel fields
+ form.add(new TextField<String>("name").setEnabled(allowEditName));
+ form.add(new TextField<String>("description"));
+ form.add(ownersPalette);
+ form.add(new CheckBox("allowForks").setEnabled(GitBlit.getBoolean(Keys.web.allowForking, true)));
+ DropDownChoice<AccessRestrictionType> accessRestriction = new DropDownChoice<AccessRestrictionType>("accessRestriction", Arrays
+ .asList(AccessRestrictionType.values()), new AccessRestrictionRenderer());
+ form.add(accessRestriction);
+ form.add(new CheckBox("isFrozen"));
+ // TODO enable origin definition
+ form.add(new TextField<String>("origin").setEnabled(false/* isCreate */));
+
+ // allow relinking HEAD to a branch or tag other than master on edit repository
+ List<String> availableRefs = new ArrayList<String>();
+ if (!ArrayUtils.isEmpty(repositoryModel.availableRefs)) {
+ availableRefs.addAll(repositoryModel.availableRefs);
+ }
+ form.add(new DropDownChoice<String>("HEAD", availableRefs).setEnabled(availableRefs.size() > 0));
+
+ boolean gcEnabled = GitBlit.getBoolean(Keys.git.enableGarbageCollection, false);
+ List<Integer> gcPeriods = Arrays.asList(1, 2, 3, 4, 5, 7, 10, 14 );
+ form.add(new DropDownChoice<Integer>("gcPeriod", gcPeriods, new GCPeriodRenderer()).setEnabled(gcEnabled));
+ form.add(new TextField<String>("gcThreshold").setEnabled(gcEnabled));
+
+ // federation strategies - remove ORIGIN choice if this repository has
+ // no origin.
+ List<FederationStrategy> federationStrategies = new ArrayList<FederationStrategy>(
+ Arrays.asList(FederationStrategy.values()));
+ if (StringUtils.isEmpty(repositoryModel.origin)) {
+ federationStrategies.remove(FederationStrategy.FEDERATE_ORIGIN);
+ }
+ form.add(new DropDownChoice<FederationStrategy>("federationStrategy", federationStrategies,
+ new FederationTypeRenderer()));
+ form.add(new CheckBox("useTickets"));
+ form.add(new CheckBox("useDocs"));
+ form.add(new CheckBox("showRemoteBranches"));
+ form.add(new CheckBox("showReadme"));
+ form.add(new CheckBox("skipSizeCalculation"));
+ form.add(new CheckBox("skipSummaryMetrics"));
+ List<Integer> maxActivityCommits = Arrays.asList(-1, 0, 25, 50, 75, 100, 150, 200, 250, 500 );
+ form.add(new DropDownChoice<Integer>("maxActivityCommits", maxActivityCommits, new MaxActivityCommitsRenderer()));
+
+ mailingLists = new Model<String>(ArrayUtils.isEmpty(repositoryModel.mailingLists) ? ""
+ : StringUtils.flattenStrings(repositoryModel.mailingLists, " "));
+ form.add(new TextField<String>("mailingLists", mailingLists));
+ form.add(indexedBranchesPalette);
+
+ List<AuthorizationControl> acList = Arrays.asList(AuthorizationControl.values());
+ final RadioChoice<AuthorizationControl> authorizationControl = new RadioChoice<Constants.AuthorizationControl>(
+ "authorizationControl", acList, new AuthorizationControlRenderer());
+ form.add(authorizationControl);
+
+ final CheckBox verifyCommitter = new CheckBox("verifyCommitter");
+ verifyCommitter.setOutputMarkupId(true);
+ form.add(verifyCommitter);
+
+ form.add(usersPalette);
+ form.add(teamsPalette);
+ form.add(federationSetsPalette);
+ form.add(preReceivePalette);
+ form.add(new BulletListPanel("inheritedPreReceive", getString("gb.inherited"), GitBlit.self()
+ .getPreReceiveScriptsInherited(repositoryModel)));
+ form.add(postReceivePalette);
+ form.add(new BulletListPanel("inheritedPostReceive", getString("gb.inherited"), GitBlit.self()
+ .getPostReceiveScriptsInherited(repositoryModel)));
+
+ WebMarkupContainer customFieldsSection = new WebMarkupContainer("customFieldsSection");
+ customFieldsSection.add(customFieldsListView);
+ form.add(customFieldsSection.setVisible(!GitBlit.getString(Keys.groovy.customFields, "").isEmpty()));
+
+ // initial enable/disable of permission controls
+ if (repositoryModel.accessRestriction.equals(AccessRestrictionType.NONE)) {
+ // anonymous everything, disable all controls
+ usersPalette.setEnabled(false);
+ teamsPalette.setEnabled(false);
+ authorizationControl.setEnabled(false);
+ verifyCommitter.setEnabled(false);
+ } else {
+ // authenticated something
+ // enable authorization controls
+ authorizationControl.setEnabled(true);
+ verifyCommitter.setEnabled(true);
+
+ boolean allowFineGrainedControls = repositoryModel.authorizationControl.equals(AuthorizationControl.NAMED);
+ usersPalette.setEnabled(allowFineGrainedControls);
+ teamsPalette.setEnabled(allowFineGrainedControls);
+ }
+
+ accessRestriction.add(new AjaxFormComponentUpdatingBehavior("onchange") {
+
+ private static final long serialVersionUID = 1L;
+
+ protected void onUpdate(AjaxRequestTarget target) {
+ // enable/disable permissions panel based on access restriction
+ boolean allowAuthorizationControl = repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE);
+ authorizationControl.setEnabled(allowAuthorizationControl);
+ verifyCommitter.setEnabled(allowAuthorizationControl);
+
+ boolean allowFineGrainedControls = allowAuthorizationControl && repositoryModel.authorizationControl.equals(AuthorizationControl.NAMED);
+ usersPalette.setEnabled(allowFineGrainedControls);
+ teamsPalette.setEnabled(allowFineGrainedControls);
+
+ if (allowFineGrainedControls) {
+ repositoryModel.authorizationControl = AuthorizationControl.NAMED;
+ }
+
+ target.addComponent(authorizationControl);
+ target.addComponent(verifyCommitter);
+ target.addComponent(usersPalette);
+ target.addComponent(teamsPalette);
+ }
+ });
+
+ authorizationControl.add(new AjaxFormChoiceComponentUpdatingBehavior() {
+
+ private static final long serialVersionUID = 1L;
+
+ protected void onUpdate(AjaxRequestTarget target) {
+ // enable/disable permissions panel based on access restriction
+ boolean allowAuthorizationControl = repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE);
+ authorizationControl.setEnabled(allowAuthorizationControl);
+
+ boolean allowFineGrainedControls = allowAuthorizationControl && repositoryModel.authorizationControl.equals(AuthorizationControl.NAMED);
+ usersPalette.setEnabled(allowFineGrainedControls);
+ teamsPalette.setEnabled(allowFineGrainedControls);
+
+ if (allowFineGrainedControls) {
+ repositoryModel.authorizationControl = AuthorizationControl.NAMED;
+ }
+
+ target.addComponent(authorizationControl);
+ target.addComponent(usersPalette);
+ target.addComponent(teamsPalette);
+ }
+ });
+
+ form.add(new Button("save"));
+ Button cancel = new Button("cancel") {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void onSubmit() {
+ setResponsePage(RepositoriesPage.class);
+ }
+ };
+ cancel.setDefaultFormProcessing(false);
+ form.add(cancel);
+
+ add(form);
+ }
+
+ /**
+ * Unfortunately must repeat part of AuthorizaitonStrategy here because that
+ * mechanism does not take PageParameters into consideration, only page
+ * instantiation.
+ *
+ * Repository Owners should be able to edit their repository.
+ */
+ private void checkPermissions(RepositoryModel model) {
+ boolean authenticateAdmin = GitBlit.getBoolean(Keys.web.authenticateAdminPages, true);
+ boolean allowAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, true);
+
+ GitBlitWebSession session = GitBlitWebSession.get();
+ UserModel user = session.getUser();
+
+ if (allowAdmin) {
+ if (authenticateAdmin) {
+ if (user == null) {
+ // No Login Available
+ error(getString("gb.errorAdminLoginRequired"), true);
+ }
+ if (isCreate) {
+ // Create Repository
+ if (!user.canCreate() && !user.canAdmin()) {
+ // Only administrators or permitted users may create
+ error(getString("gb.errorOnlyAdminMayCreateRepository"), true);
+ }
+ } else {
+ // Edit Repository
+ if (user.canAdmin()) {
+ // Admins can edit everything
+ isAdmin = true;
+ return;
+ } else {
+ if (!model.isOwner(user.username)) {
+ // User is not an Admin nor Owner
+ error(getString("gb.errorOnlyAdminOrOwnerMayEditRepository"), true);
+ }
+ }
+ }
+ }
+ } else {
+ // No Administration Permitted
+ error(getString("gb.errorAdministrationDisabled"), true);
+ }
+ }
+
+ private class AccessRestrictionRenderer implements IChoiceRenderer<AccessRestrictionType> {
+
+ private static final long serialVersionUID = 1L;
+
+ private final Map<AccessRestrictionType, String> map;
+
+ public AccessRestrictionRenderer() {
+ map = getAccessRestrictions();
+ }
+
+ @Override
+ public String getDisplayValue(AccessRestrictionType type) {
+ return map.get(type);
+ }
+
+ @Override
+ public String getIdValue(AccessRestrictionType type, int index) {
+ return Integer.toString(index);
+ }
+ }
+
+ private class FederationTypeRenderer implements IChoiceRenderer<FederationStrategy> {
+
+ private static final long serialVersionUID = 1L;
+
+ private final Map<FederationStrategy, String> map;
+
+ public FederationTypeRenderer() {
+ map = getFederationTypes();
+ }
+
+ @Override
+ public String getDisplayValue(FederationStrategy type) {
+ return map.get(type);
+ }
+
+ @Override
+ public String getIdValue(FederationStrategy type, int index) {
+ return Integer.toString(index);
+ }
+ }
+
+ private class AuthorizationControlRenderer implements IChoiceRenderer<AuthorizationControl> {
+
+ private static final long serialVersionUID = 1L;
+
+ private final Map<AuthorizationControl, String> map;
+
+ public AuthorizationControlRenderer() {
+ map = getAuthorizationControls();
+ }
+
+ @Override
+ public String getDisplayValue(AuthorizationControl type) {
+ return map.get(type);
+ }
+
+ @Override
+ public String getIdValue(AuthorizationControl type, int index) {
+ return Integer.toString(index);
+ }
+ }
+
+ private class GCPeriodRenderer implements IChoiceRenderer<Integer> {
+
+ private static final long serialVersionUID = 1L;
+
+ public GCPeriodRenderer() {
+ }
+
+ @Override
+ public String getDisplayValue(Integer value) {
+ if (value == 1) {
+ return getString("gb.duration.oneDay");
+ } else {
+ return MessageFormat.format(getString("gb.duration.days"), value);
+ }
+ }
+
+ @Override
+ public String getIdValue(Integer value, int index) {
+ return Integer.toString(index);
+ }
+ }
+
+ private class MaxActivityCommitsRenderer implements IChoiceRenderer<Integer> {
+
+ private static final long serialVersionUID = 1L;
+
+ public MaxActivityCommitsRenderer() {
+ }
+
+ @Override
+ public String getDisplayValue(Integer value) {
+ if (value == -1) {
+ return getString("gb.excludeFromActivity");
+ } else if (value == 0) {
+ return getString("gb.noMaximum");
+ } else {
+ return value + " " + getString("gb.commits");
+ }
+ }
+
+ @Override
+ public String getIdValue(Integer value, int index) {
+ return Integer.toString(index);
+ }
+ }
+
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/EditTeamPage.html b/src/main/java/com/gitblit/wicket/pages/EditTeamPage.html
new file mode 100644
index 00000000..a60d1715
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/EditTeamPage.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<wicket:extend>
+<body onload="document.getElementById('name').focus();">
+<!-- Team Table -->
+<form style="padding-top:5px;" wicket:id="editForm">
+
+<div class="tabbable">
+ <!-- tab titles -->
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="#general" data-toggle="tab"><wicket:message key="gb.general"></wicket:message></a></li>
+ <li><a href="#permissions" data-toggle="tab"><wicket:message key="gb.accessPermissions"></wicket:message></a></li>
+ </ul>
+
+ <!-- tab content -->
+ <div class="tab-content">
+
+ <!-- general tab -->
+ <div class="tab-pane active" id="general">
+ <table class="plain">
+ <tbody class="settings">
+ <tr><th><wicket:message key="gb.teamName"></wicket:message></th><td class="edit"><input type="text" wicket:id="name" id="name" size="30" tabindex="1" /></td></tr>
+ <tr><th><wicket:message key="gb.canAdmin"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canAdmin" tabindex="2" /> &nbsp;<span class="help-inline"><wicket:message key="gb.canAdminDescription"></wicket:message></span></label></td></tr>
+ <tr><th><wicket:message key="gb.canCreate"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canCreate" tabindex="3" /> &nbsp;<span class="help-inline"><wicket:message key="gb.canCreateDescription"></wicket:message></span></label></td></tr>
+ <tr><th><wicket:message key="gb.canFork"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canFork" tabindex="4" /> &nbsp;<span class="help-inline"><wicket:message key="gb.canForkDescription"></wicket:message></span></label></td></tr>
+ <tr><th><wicket:message key="gb.mailingLists"></wicket:message></th><td class="edit"><input class="span8" type="text" wicket:id="mailingLists" size="40" tabindex="5" /></td></tr>
+ </tbody>
+ </table>
+ </div>
+
+ <!-- access permissions tab -->
+ <div class="tab-pane" id="permissions">
+ <table class="plain">
+ <tbody class="settings">
+ <tr><th><wicket:message key="gb.teamMembers"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>
+ <tr><td colspan="2"><hr></hr></td></tr>
+ <tr><th><wicket:message key="gb.repositoryPermissions"></wicket:message></th><td style="padding:2px;"><span wicket:id="repositories"></span></td></tr>
+ </tbody>
+ </table>
+ </div>
+
+ <!-- hooks -->
+ <div class="tab-pane" id="hooks">
+ <table class="plain">
+ <tbody class="settings">
+ <tr><td colspan="2" style="padding-top:10px;"><h3><wicket:message key="gb.hookScripts"></wicket:message> &nbsp;<small><wicket:message key="gb.hookScriptsDescription"></wicket:message></small></h3></td></tr>
+ <tr><th><wicket:message key="gb.preReceiveScripts"></wicket:message><p></p><span wicket:id="inheritedPreReceive"></span></th><td style="padding:2px;"><span wicket:id="preReceiveScripts"></span></td></tr>
+ <tr><th><wicket:message key="gb.postReceiveScripts"></wicket:message><p></p><span wicket:id="inheritedPostReceive"></span></th><td style="padding:2px;"><span wicket:id="postReceiveScripts"></span></td></tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="row">
+ <div class="form-actions"><input class="btn btn-primary" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" tabindex="6" /> &nbsp; <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" tabindex="7" /></div>
+</div>
+
+</form>
+</body>
+</wicket:extend>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java b/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java
new file mode 100644
index 00000000..8344d387
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.behavior.SimpleAttributeModifier;
+import org.apache.wicket.extensions.markup.html.form.palette.Palette;
+import org.apache.wicket.markup.html.form.Button;
+import org.apache.wicket.markup.html.form.CheckBox;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.util.CollectionModel;
+import org.apache.wicket.model.util.ListModel;
+
+import com.gitblit.GitBlit;
+import com.gitblit.GitBlitException;
+import com.gitblit.Keys;
+import com.gitblit.Constants.RegistrantType;
+import com.gitblit.models.RegistrantAccessPermission;
+import com.gitblit.models.TeamModel;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.RequiresAdminRole;
+import com.gitblit.wicket.StringChoiceRenderer;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.BulletListPanel;
+import com.gitblit.wicket.panels.RegistrantPermissionsPanel;
+
+@RequiresAdminRole
+public class EditTeamPage extends RootSubPage {
+
+ private final boolean isCreate;
+
+ private IModel<String> mailingLists;
+
+ public EditTeamPage() {
+ // create constructor
+ super();
+ isCreate = true;
+ setupPage(new TeamModel(""));
+ setStatelessHint(false);
+ setOutputMarkupId(true);
+ }
+
+ public EditTeamPage(PageParameters params) {
+ // edit constructor
+ super(params);
+ isCreate = false;
+ String name = WicketUtils.getTeamname(params);
+ TeamModel model = GitBlit.self().getTeamModel(name);
+ setupPage(model);
+ setStatelessHint(false);
+ setOutputMarkupId(true);
+ }
+
+ @Override
+ protected boolean requiresPageMap() {
+ return true;
+ }
+
+ protected void setupPage(final TeamModel teamModel) {
+ if (isCreate) {
+ super.setupPage(getString("gb.newTeam"), "");
+ } else {
+ super.setupPage(getString("gb.edit"), teamModel.name);
+ }
+
+ CompoundPropertyModel<TeamModel> model = new CompoundPropertyModel<TeamModel>(teamModel);
+
+ List<String> repos = getAccessRestrictedRepositoryList(true, null);
+
+ List<String> teamUsers = new ArrayList<String>(teamModel.users);
+ Collections.sort(teamUsers);
+ List<String> preReceiveScripts = new ArrayList<String>();
+ List<String> postReceiveScripts = new ArrayList<String>();
+
+ final String oldName = teamModel.name;
+ final List<RegistrantAccessPermission> permissions = teamModel.getRepositoryPermissions();
+
+ // users palette
+ final Palette<String> users = new Palette<String>("users", new ListModel<String>(
+ new ArrayList<String>(teamUsers)), new CollectionModel<String>(GitBlit.self()
+ .getAllUsernames()), new StringChoiceRenderer(), 10, false);
+
+ // pre-receive palette
+ if (teamModel.preReceiveScripts != null) {
+ preReceiveScripts.addAll(teamModel.preReceiveScripts);
+ }
+ final Palette<String> preReceivePalette = new Palette<String>("preReceiveScripts",
+ new ListModel<String>(preReceiveScripts), new CollectionModel<String>(GitBlit
+ .self().getPreReceiveScriptsUnused(null)), new StringChoiceRenderer(),
+ 12, true);
+
+ // post-receive palette
+ if (teamModel.postReceiveScripts != null) {
+ postReceiveScripts.addAll(teamModel.postReceiveScripts);
+ }
+ final Palette<String> postReceivePalette = new Palette<String>("postReceiveScripts",
+ new ListModel<String>(postReceiveScripts), new CollectionModel<String>(GitBlit
+ .self().getPostReceiveScriptsUnused(null)), new StringChoiceRenderer(),
+ 12, true);
+
+ Form<TeamModel> form = new Form<TeamModel>("editForm", model) {
+
+ private static final long serialVersionUID = 1L;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.wicket.markup.html.form.Form#onSubmit()
+ */
+ @Override
+ protected void onSubmit() {
+ String teamname = teamModel.name;
+ if (StringUtils.isEmpty(teamname)) {
+ error(getString("gb.pleaseSetTeamName"));
+ return;
+ }
+ if (isCreate) {
+ TeamModel model = GitBlit.self().getTeamModel(teamname);
+ if (model != null) {
+ error(MessageFormat.format(getString("gb.teamNameUnavailable"), teamname));
+ return;
+ }
+ }
+ // update team permissions
+ for (RegistrantAccessPermission repositoryPermission : permissions) {
+ teamModel.setRepositoryPermission(repositoryPermission.registrant, repositoryPermission.permission);
+ }
+
+ Iterator<String> selectedUsers = users.getSelectedChoices();
+ List<String> members = new ArrayList<String>();
+ while (selectedUsers.hasNext()) {
+ members.add(selectedUsers.next().toLowerCase());
+ }
+ teamModel.users.clear();
+ teamModel.users.addAll(members);
+
+ // set mailing lists
+ String ml = mailingLists.getObject();
+ if (!StringUtils.isEmpty(ml)) {
+ Set<String> list = new HashSet<String>();
+ for (String address : ml.split("(,|\\s)")) {
+ if (StringUtils.isEmpty(address)) {
+ continue;
+ }
+ list.add(address.toLowerCase());
+ }
+ teamModel.mailingLists.clear();
+ teamModel.mailingLists.addAll(list);
+ }
+
+ // pre-receive scripts
+ List<String> preReceiveScripts = new ArrayList<String>();
+ Iterator<String> pres = preReceivePalette.getSelectedChoices();
+ while (pres.hasNext()) {
+ preReceiveScripts.add(pres.next());
+ }
+ teamModel.preReceiveScripts.clear();
+ teamModel.preReceiveScripts.addAll(preReceiveScripts);
+
+ // post-receive scripts
+ List<String> postReceiveScripts = new ArrayList<String>();
+ Iterator<String> post = postReceivePalette.getSelectedChoices();
+ while (post.hasNext()) {
+ postReceiveScripts.add(post.next());
+ }
+ teamModel.postReceiveScripts.clear();
+ teamModel.postReceiveScripts.addAll(postReceiveScripts);
+
+ try {
+ GitBlit.self().updateTeamModel(oldName, teamModel, isCreate);
+ } catch (GitBlitException e) {
+ error(e.getMessage());
+ return;
+ }
+ setRedirect(false);
+ if (isCreate) {
+ // create another team
+ info(MessageFormat.format(getString("gb.teamCreated"),
+ teamModel.name));
+ }
+ // back to users page
+ setResponsePage(UsersPage.class);
+ }
+ };
+
+ // do not let the browser pre-populate these fields
+ form.add(new SimpleAttributeModifier("autocomplete", "off"));
+
+ // not all user services support manipulating team memberships
+ boolean editMemberships = GitBlit.self().supportsTeamMembershipChanges(null);
+
+ // field names reflective match TeamModel fields
+ form.add(new TextField<String>("name"));
+ form.add(new CheckBox("canAdmin"));
+ form.add(new CheckBox("canFork").setEnabled(GitBlit.getBoolean(Keys.web.allowForking, true)));
+ form.add(new CheckBox("canCreate"));
+ form.add(users.setEnabled(editMemberships));
+ mailingLists = new Model<String>(teamModel.mailingLists == null ? ""
+ : StringUtils.flattenStrings(teamModel.mailingLists, " "));
+ form.add(new TextField<String>("mailingLists", mailingLists));
+
+ form.add(new RegistrantPermissionsPanel("repositories", RegistrantType.REPOSITORY,
+ repos, permissions, getAccessPermissions()));
+ form.add(preReceivePalette);
+ form.add(new BulletListPanel("inheritedPreReceive", "inherited", GitBlit.self()
+ .getPreReceiveScriptsInherited(null)));
+ form.add(postReceivePalette);
+ form.add(new BulletListPanel("inheritedPostReceive", "inherited", GitBlit.self()
+ .getPostReceiveScriptsInherited(null)));
+
+ form.add(new Button("save"));
+ Button cancel = new Button("cancel") {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void onSubmit() {
+ setResponsePage(UsersPage.class);
+ }
+ };
+ cancel.setDefaultFormProcessing(false);
+ form.add(cancel);
+
+ add(form);
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/EditUserPage.html b/src/main/java/com/gitblit/wicket/pages/EditUserPage.html
new file mode 100644
index 00000000..e79011c8
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/EditUserPage.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<wicket:extend>
+<body onload="document.getElementById('username').focus();">
+<!-- User Table -->
+<form style="padding-top:5px;" wicket:id="editForm">
+
+<div class="tabbable">
+ <!-- tab titles -->
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="#general" data-toggle="tab"><wicket:message key="gb.general"></wicket:message></a></li>
+ <li><a href="#attributes" data-toggle="tab"><wicket:message key="gb.attributes"></wicket:message></a></li>
+ <li><a href="#permissions" data-toggle="tab"><wicket:message key="gb.accessPermissions"></wicket:message></a></li>
+ </ul>
+
+ <!-- tab content -->
+ <div class="tab-content">
+
+ <!-- general tab -->
+ <div class="tab-pane active" id="general">
+ <table class="plain">
+ <tbody class="settings">
+ <tr><th><wicket:message key="gb.username"></wicket:message></th><td class="edit"><input type="text" wicket:id="username" id="username" size="30" tabindex="1" /></td></tr>
+ <tr><th><wicket:message key="gb.password"></wicket:message></th><td class="edit"><input type="password" wicket:id="password" size="30" tabindex="2" /></td></tr>
+ <tr><th><wicket:message key="gb.confirmPassword"></wicket:message></th><td class="edit"><input type="password" wicket:id="confirmPassword" size="30" tabindex="3" /></td></tr>
+ <tr><th><wicket:message key="gb.displayName"></wicket:message></th><td class="edit"><input type="text" wicket:id="displayName" size="30" tabindex="4" /></td></tr>
+ <tr><th><wicket:message key="gb.emailAddress"></wicket:message></th><td class="edit"><input type="text" wicket:id="emailAddress" size="30" tabindex="5" /></td></tr>
+ <tr><th><wicket:message key="gb.canAdmin"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canAdmin" tabindex="6" /> &nbsp;<span class="help-inline"><wicket:message key="gb.canAdminDescription"></wicket:message></span></label></td></tr>
+ <tr><th><wicket:message key="gb.canCreate"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canCreate" tabindex="7" /> &nbsp;<span class="help-inline"><wicket:message key="gb.canCreateDescription"></wicket:message></span></label></td></tr>
+ <tr><th><wicket:message key="gb.canFork"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canFork" tabindex="8" /> &nbsp;<span class="help-inline"><wicket:message key="gb.canForkDescription"></wicket:message></span></label></td></tr>
+ <tr><th><wicket:message key="gb.excludeFromFederation"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="excludeFromFederation" tabindex="9" /> &nbsp;<span class="help-inline"><wicket:message key="gb.excludeFromFederationDescription"></wicket:message></span></label></td></tr>
+ </tbody>
+ </table>
+ </div>
+
+ <!-- attributes tab -->
+ <div class="tab-pane" id="attributes">
+ <table class="plain">
+ <tbody class="settings">
+ <tr><th><wicket:message key="gb.organizationalUnit"></wicket:message> (OU)</th><td class="edit"><input type="text" wicket:id="organizationalUnit" size="30" tabindex="1" /></td></tr>
+ <tr><th><wicket:message key="gb.organization"></wicket:message> (O)</th><td class="edit"><input type="text" wicket:id="organization" size="30" tabindex="2" /></td></tr>
+ <tr><th><wicket:message key="gb.locality"></wicket:message> (L)</th><td class="edit"><input type="text" wicket:id="locality" size="30" tabindex="3" /></td></tr>
+ <tr><th><wicket:message key="gb.stateProvince"></wicket:message> (ST)</th><td class="edit"><input type="text" wicket:id="stateProvince" size="30" tabindex="4" /></td></tr>
+ <tr><th><wicket:message key="gb.countryCode"></wicket:message> (C)</th><td class="edit"><input type="text" wicket:id="countryCode" size="15 " tabindex="5" /></td></tr>
+ </tbody>
+ </table>
+ </div>
+
+ <!-- access permissions tab -->
+ <div class="tab-pane" id="permissions">
+ <table class="plain">
+ <tbody class="settings">
+ <tr><th><wicket:message key="gb.teamMemberships"></wicket:message></th><td style="padding:2px;"><span wicket:id="teams"></span></td></tr>
+ <tr><td colspan="2"><hr></hr></td></tr>
+ <tr><th><wicket:message key="gb.repositoryPermissions"></wicket:message></th>
+ <td style="padding:2px;">
+ <div wicket:id="repositories"></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="row">
+ <div class="form-actions"><input class="btn btn-primary" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" tabindex="9" /> &nbsp; <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" tabindex="10" /></div>
+</div>
+
+</form>
+</body>
+
+</wicket:extend>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/EditUserPage.java b/src/main/java/com/gitblit/wicket/pages/EditUserPage.java
new file mode 100644
index 00000000..c060f237
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/EditUserPage.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.behavior.SimpleAttributeModifier;
+import org.apache.wicket.extensions.markup.html.form.palette.Palette;
+import org.apache.wicket.markup.html.form.Button;
+import org.apache.wicket.markup.html.form.CheckBox;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.PasswordTextField;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.util.CollectionModel;
+import org.apache.wicket.model.util.ListModel;
+
+import com.gitblit.Constants.RegistrantType;
+import com.gitblit.GitBlit;
+import com.gitblit.GitBlitException;
+import com.gitblit.Keys;
+import com.gitblit.models.RegistrantAccessPermission;
+import com.gitblit.models.TeamModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.RequiresAdminRole;
+import com.gitblit.wicket.StringChoiceRenderer;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.RegistrantPermissionsPanel;
+
+@RequiresAdminRole
+public class EditUserPage extends RootSubPage {
+
+ private final boolean isCreate;
+
+ public EditUserPage() {
+ // create constructor
+ super();
+ if (!GitBlit.self().supportsAddUser()) {
+ error(MessageFormat.format(getString("gb.userServiceDoesNotPermitAddUser"),
+ GitBlit.getString(Keys.realm.userService, "${baseFolder}/users.conf")), true);
+ }
+ isCreate = true;
+ setupPage(new UserModel(""));
+ setStatelessHint(false);
+ setOutputMarkupId(true);
+ }
+
+ public EditUserPage(PageParameters params) {
+ // edit constructor
+ super(params);
+ isCreate = false;
+ String name = WicketUtils.getUsername(params);
+ UserModel model = GitBlit.self().getUserModel(name);
+ setupPage(model);
+ setStatelessHint(false);
+ setOutputMarkupId(true);
+ }
+
+ @Override
+ protected boolean requiresPageMap() {
+ return true;
+ }
+
+ protected void setupPage(final UserModel userModel) {
+ if (isCreate) {
+ super.setupPage(getString("gb.newUser"), "");
+ } else {
+ super.setupPage(getString("gb.edit"), userModel.username);
+ }
+
+ final Model<String> confirmPassword = new Model<String>(
+ StringUtils.isEmpty(userModel.password) ? "" : userModel.password);
+ CompoundPropertyModel<UserModel> model = new CompoundPropertyModel<UserModel>(userModel);
+
+ // build list of projects including all repositories wildcards
+ List<String> repos = getAccessRestrictedRepositoryList(true, userModel);
+
+ List<String> userTeams = new ArrayList<String>();
+ for (TeamModel team : userModel.teams) {
+ userTeams.add(team.name);
+ }
+ Collections.sort(userTeams);
+
+ final String oldName = userModel.username;
+ final List<RegistrantAccessPermission> permissions = GitBlit.self().getUserAccessPermissions(userModel);
+
+ final Palette<String> teams = new Palette<String>("teams", new ListModel<String>(
+ new ArrayList<String>(userTeams)), new CollectionModel<String>(GitBlit.self()
+ .getAllTeamnames()), new StringChoiceRenderer(), 10, false);
+ Form<UserModel> form = new Form<UserModel>("editForm", model) {
+
+ private static final long serialVersionUID = 1L;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.wicket.markup.html.form.Form#onSubmit()
+ */
+ @Override
+ protected void onSubmit() {
+ if (StringUtils.isEmpty(userModel.username)) {
+ error(getString("gb.pleaseSetUsername"));
+ return;
+ }
+ // force username to lower-case
+ userModel.username = userModel.username.toLowerCase();
+ String username = userModel.username;
+ if (isCreate) {
+ UserModel model = GitBlit.self().getUserModel(username);
+ if (model != null) {
+ error(MessageFormat.format(getString("gb.usernameUnavailable"), username));
+ return;
+ }
+ }
+ boolean rename = !StringUtils.isEmpty(oldName)
+ && !oldName.equalsIgnoreCase(username);
+ if (GitBlit.self().supportsCredentialChanges(userModel)) {
+ if (!userModel.password.equals(confirmPassword.getObject())) {
+ error(getString("gb.passwordsDoNotMatch"));
+ return;
+ }
+ String password = userModel.password;
+ if (!password.toUpperCase().startsWith(StringUtils.MD5_TYPE)
+ && !password.toUpperCase().startsWith(StringUtils.COMBINED_MD5_TYPE)) {
+ // This is a plain text password.
+ // Check length.
+ int minLength = GitBlit.getInteger(Keys.realm.minPasswordLength, 5);
+ if (minLength < 4) {
+ minLength = 4;
+ }
+ if (password.trim().length() < minLength) {
+ error(MessageFormat.format(getString("gb.passwordTooShort"),
+ minLength));
+ return;
+ }
+
+ // Optionally store the password MD5 digest.
+ String type = GitBlit.getString(Keys.realm.passwordStorage, "md5");
+ if (type.equalsIgnoreCase("md5")) {
+ // store MD5 digest of password
+ userModel.password = StringUtils.MD5_TYPE
+ + StringUtils.getMD5(userModel.password);
+ } else if (type.equalsIgnoreCase("combined-md5")) {
+ // store MD5 digest of username+password
+ userModel.password = StringUtils.COMBINED_MD5_TYPE
+ + StringUtils.getMD5(username + userModel.password);
+ }
+ } else if (rename
+ && password.toUpperCase().startsWith(StringUtils.COMBINED_MD5_TYPE)) {
+ error(getString("gb.combinedMd5Rename"));
+ return;
+ }
+ }
+
+ // update user permissions
+ for (RegistrantAccessPermission repositoryPermission : permissions) {
+ userModel.setRepositoryPermission(repositoryPermission.registrant, repositoryPermission.permission);
+ }
+
+ Iterator<String> selectedTeams = teams.getSelectedChoices();
+ userModel.teams.clear();
+ while (selectedTeams.hasNext()) {
+ TeamModel team = GitBlit.self().getTeamModel(selectedTeams.next());
+ if (team == null) {
+ continue;
+ }
+ userModel.teams.add(team);
+ }
+
+ try {
+ GitBlit.self().updateUserModel(oldName, userModel, isCreate);
+ } catch (GitBlitException e) {
+ error(e.getMessage());
+ return;
+ }
+ setRedirect(false);
+ if (isCreate) {
+ // create another user
+ info(MessageFormat.format(getString("gb.userCreated"),
+ userModel.username));
+ setResponsePage(EditUserPage.class);
+ } else {
+ // back to users page
+ setResponsePage(UsersPage.class);
+ }
+ }
+ };
+
+ // do not let the browser pre-populate these fields
+ form.add(new SimpleAttributeModifier("autocomplete", "off"));
+
+ // not all user services support manipulating username and password
+ boolean editCredentials = GitBlit.self().supportsCredentialChanges(userModel);
+
+ // not all user services support manipulating display name
+ boolean editDisplayName = GitBlit.self().supportsDisplayNameChanges(userModel);
+
+ // not all user services support manipulating email address
+ boolean editEmailAddress = GitBlit.self().supportsEmailAddressChanges(userModel);
+
+ // not all user services support manipulating team memberships
+ boolean editTeams = GitBlit.self().supportsTeamMembershipChanges(userModel);
+
+ // field names reflective match UserModel fields
+ form.add(new TextField<String>("username").setEnabled(editCredentials));
+ PasswordTextField passwordField = new PasswordTextField("password");
+ passwordField.setResetPassword(false);
+ form.add(passwordField.setEnabled(editCredentials));
+ PasswordTextField confirmPasswordField = new PasswordTextField("confirmPassword",
+ confirmPassword);
+ confirmPasswordField.setResetPassword(false);
+ form.add(confirmPasswordField.setEnabled(editCredentials));
+ form.add(new TextField<String>("displayName").setEnabled(editDisplayName));
+ form.add(new TextField<String>("emailAddress").setEnabled(editEmailAddress));
+ form.add(new CheckBox("canAdmin"));
+ form.add(new CheckBox("canFork").setEnabled(GitBlit.getBoolean(Keys.web.allowForking, true)));
+ form.add(new CheckBox("canCreate"));
+ form.add(new CheckBox("excludeFromFederation"));
+ form.add(new RegistrantPermissionsPanel("repositories", RegistrantType.REPOSITORY, repos, permissions, getAccessPermissions()));
+ form.add(teams.setEnabled(editTeams));
+
+ form.add(new TextField<String>("organizationalUnit").setEnabled(editDisplayName));
+ form.add(new TextField<String>("organization").setEnabled(editDisplayName));
+ form.add(new TextField<String>("locality").setEnabled(editDisplayName));
+ form.add(new TextField<String>("stateProvince").setEnabled(editDisplayName));
+ form.add(new TextField<String>("countryCode").setEnabled(editDisplayName));
+ form.add(new Button("save"));
+ Button cancel = new Button("cancel") {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void onSubmit() {
+ setResponsePage(UsersPage.class);
+ }
+ };
+ cancel.setDefaultFormProcessing(false);
+ form.add(cancel);
+
+ add(form);
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.html b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.html
new file mode 100644
index 00000000..d46a5ded
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <h2>Empty Repository</h2>
+ <p></p>
+ <div class="row">
+ <div class="span10">
+ <div class="alert alert-success">
+ <span wicket:id="repository" style="font-weight: bold;">[repository]</span> is an empty repository and can not be viewed by Gitblit.
+ <p></p>
+ Please push some commits to <span wicket:id="pushurl"></span>
+ <p></p>
+ <hr/>
+ After you have pushed commits you may <b>refresh</b> this page to view your repository.
+ </div>
+ </div>
+ </div>
+
+ <h3>Git Command-Line Syntax</h3>
+ <span style="padding-bottom:5px;">If you do not have a local Git repository, then you should clone this repository, commit some files, and then push your commits back to Gitblit.</span>
+ <p></p>
+ <pre style="padding: 5px 30px;" wicket:id="cloneSyntax"></pre>
+ <p></p>
+ <span style="padding-bottom:5px;">If you already have a local Git repository with commits, then you may add this repository as a remote and push to it.</span>
+ <p></p>
+ <pre wicket:id="remoteSyntax" style="padding: 5px 30px;"></pre>
+ <p></p>
+ <h3>Learn Git</h3>
+ If you are unsure how to use this information, consider reviewing the <a href="http://book.git-scm.com">Git Community Book</a> or <a href="http://progit.org/book" target="_blank">Pro Git</a> for a better understanding on how to use Git.
+ <p></p>
+ <h4>Open Source Git Clients</h4>
+ <ul>
+ <li><a href="http://git-scm.com">Git</a> - the official, command-line Git</li>
+ <li><a href="http://tortoisegit.googlecode.com">TortoiseGit</a> - Windows file explorer integration (requires official, command-line Git)</li>
+ <li><a href="http://eclipse.org/egit">Eclipse/EGit</a> - Git for the Eclipse IDE (based on JGit, like Gitblit)</li>
+ <li><a href="https://code.google.com/p/gitextensions/">Git Extensions</a> - C# frontend for Git that features Windows Explorer and Visual Studio integration</li>
+ <li><a href="http://gitx.laullon.com/">GitX (L)</a> - a Mac OS X Git client</li>
+ </ul>
+ <p></p>
+ <h4>Commercial/Closed-Source Git Clients</h4>
+ <ul>
+ <li><a href="http://www.syntevo.com/smartgit">SmartGit</a> - A Java Git, Mercurial, and SVN client application (requires official, command-line Git)</li>
+ <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - A free Mac Client for Git, Mercurial, and SVN</li>
+ </ul>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java
new file mode 100644
index 00000000..be0dad9e
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.wicket.GitblitRedirectException;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.RepositoryUrlPanel;
+
+public class EmptyRepositoryPage extends RootPage {
+
+ public EmptyRepositoryPage(PageParameters params) {
+ super(params);
+
+ setVersioned(false);
+
+ String repositoryName = WicketUtils.getRepositoryName(params);
+ RepositoryModel repository = GitBlit.self().getRepositoryModel(repositoryName);
+ if (repository == null) {
+ error(getString("gb.canNotLoadRepository") + " " + repositoryName, true);
+ }
+
+ if (repository.hasCommits) {
+ // redirect to the summary page if this repository is not empty
+ throw new GitblitRedirectException(SummaryPage.class, params);
+ }
+
+ setupPage(repositoryName, getString("gb.emptyRepository"));
+
+ List<String> repositoryUrls = new ArrayList<String>();
+
+ if (GitBlit.getBoolean(Keys.git.enableGitServlet, true)) {
+ // add the Gitblit repository url
+ repositoryUrls.add(getRepositoryUrl(repository));
+ }
+ repositoryUrls.addAll(GitBlit.self().getOtherCloneUrls(repositoryName));
+
+ String primaryUrl = ArrayUtils.isEmpty(repositoryUrls) ? "" : repositoryUrls.get(0);
+ add(new Label("repository", repositoryName));
+ add(new RepositoryUrlPanel("pushurl", primaryUrl));
+ add(new Label("cloneSyntax", MessageFormat.format("git clone {0}", repositoryUrls.get(0))));
+ add(new Label("remoteSyntax", MessageFormat.format("git remote add gitblit {0}\ngit push gitblit master", primaryUrl)));
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_es.html b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_es.html
new file mode 100644
index 00000000..2849fc70
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_es.html
@@ -0,0 +1,56 @@
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="es">
+
+<body>
+<wicket:extend>
+
+ <h2>Repositorio Vac&iacute;o</h2>
+ <p></p>
+ <div class="row">
+ <div class="span7">
+ <div class="alert alert-success">
+ <span wicket:id="repository" style="font-weight: bold;">[repository]</span> es un repositorio vac&iacute;o y no puede ser visto en Gitblit.
+ <p></p>
+ Por favor, empuja algunas consignas a <span wicket:id="pushurl"></span>
+ <p></p>
+ <hr/>
+ Despu&eacute;s de empujar tus consignas puedes <b>refrescar</b> &eacute;sta p&aacute;gina para ver tu Repositorio.
+ </div>
+ </div>
+ </div>
+
+ <h3>Sintaxis de la L&iacute;nea de Comandos de Git</h3>
+ <span style="padding-bottom:5px;">Si no tienes un Repositiorio local Git, puedes clonar &eacute;ste, consignar algunos archivos, y despu&eacute;s empujar las consignas de vuelta a Gitblit.</span>
+ <p></p>
+ <pre style="padding: 5px 30px;" wicket:id="cloneSyntax"></pre>
+ <p></p>
+ <span style="padding-bottom:5px;">Si ya tienes un repositorio local Git con algunas consignas, puedes a&ntilde;adir &eacute;ste como remoto y empujar desde all&iacute;.</span>
+ <p></p>
+ <pre style="padding: 5px 30px;" wicket:id="remoteSyntax"></pre>
+ <p></p>
+ <h3>Aprender Git</h3>
+ Si no est&aacute;s seguro de como usar esta informaci&oacute;n, &eacute;chale un vistazo al <a href="http://book.git-scm.com">Libro de la cominidad Git</a> o <a href="http://progit.org/book" target="_blank">Pro Git</a> para una mejor compresi&oacute;n de como usar Git.
+ <p></p>
+ <h4>Clientes Git de C&oacute;digo abierto.</h4>
+ <ul>
+ <li><a href="http://git-scm.com">Git</a> - El Git oficial en l&iacute;nea de comandos</li>
+ <li><a href="http://tortoisegit.googlecode.com">TortoiseGit</a> - Explorador de archivos integrado en Windows (necesita Git oficial en l&iacute;nea de comandos)</li>
+ <li><a href="http://eclipse.org/egit">Eclipse/EGit</a> - Git para el IDE de Eclipse (basado en JGit, como Gitblit)</li>
+ <li><a href="https://code.google.com/p/gitextensions/">Git Extensions</a> - Interfaz de usuario gr&aacute;fico Git en C# con integraci&oacute;n en IE y en Visual Studio</li>
+ <li><a href="http://gitx.laullon.com/">GitX (L)</a> - Cliente Git para Mac OS X</li>
+ </ul>
+ <p></p>
+ <h4>Clientes Git comerciales</h4>
+ <ul>
+ <li><a href="http://www.syntevo.com/smartgit">SmartGit</a> - aplicaci&oacute;n Java (necesita Git oficial en l&iacute;nea de comandos)</li>
+ <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - Un cliente Git gratuito para Mac, Mercurial, y SVN</li>
+ </ul>
+</wicket:extend>
+</body>
+</html>
+
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_ko.html b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_ko.html
new file mode 100644
index 00000000..591335e4
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_ko.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <h2>비어있는 저장소</h2>
+ <p></p>
+ <div class="row">
+ <div class="span10">
+ <div class="alert alert-success">
+ <span wicket:id="repository" style="font-weight: bold;">[repository]</span> 저장소는 비어 있어서 Gitblit 에서 볼 수 없습니다.
+ <p></p>
+ 이 Git url 에 커밋해 주세요. <span wicket:id="pushurl"></span>
+ <p></p>
+ <hr/>
+ After you have pushed commits you may <b>refresh</b> this page to view your repository.
+ </div>
+ </div>
+ </div>
+
+ <p></p>
+ <h3>Git 명령어</h3>
+ <span style="padding-bottom:5px;">로컬 Git 저장소가 없다면, 이 저장소를 클론(clone) 한 후, 몇 파일을 커밋하고, 그 커밋을 Gitblit 에 푸시(push) 하세요.</span>
+ <p></p>
+ <pre style="padding: 5px 30px;" wicket:id="cloneSyntax"></pre>
+ <p></p>
+ <span style="padding-bottom:5px;">만약 커밋된 로컬 Git 저장소가 있다면, 다음과 같이 저장소에 리모트를 추가하고 푸시(push)할 수 있습니다.</span>
+ <p></p>
+ <pre style="padding: 5px 30px;" wicket:id="remoteSyntax"></pre>
+ <p></p>
+ <h3>Git 배우기</h3>
+ 만약 사용법에 자신이 없다면, Git 사용법을 더 잘 이해하기 위해
+ <a href="http://book.git-scm.com">Git Community Book</a> 또는
+ <a href="http://progit.org/book" target="_blank">Pro Git</a>,
+ <a href="http://dogfeet.github.com/articles/2012/progit.html" target="_blank">Pro Git 한글</a> 을 볼 것을 고려해 보세요.
+ <p></p>
+ <h4>오픈소스 Git 클라이언트</h4>
+ <ul>
+ <li><a href="http://git-scm.com">Git</a> - 명령어 기반 공식 Git</li>
+ <li><a href="http://tortoisegit.googlecode.com">TortoiseGit</a> - 윈도의 파일 탐색기에 통합된 UI 클라이언트 (명령어 기반 공식 Git 필요)</li>
+ <li><a href="http://eclipse.org/egit">Eclipse/EGit</a> - 이클립스 IDE 플러그인 (Gitblit 과 같은 JGit 기반)</li>
+ <li><a href="https://code.google.com/p/gitextensions/">Git Extensions</a> - C# frontend for Git that features Windows Explorer and Visual Studio integration</li>
+ <li><a href="http://gitx.laullon.com/">GitX (L)</a> - a Mac OS X Git client</li>
+ </ul>
+ <p></p>
+ <h4>유료 Git 클라이언트</h4>
+ <ul>
+ <li><a href="http://www.syntevo.com/smartgit">SmartGit</a> - 자바 어플리케이션 (명령어 기반 공식 Git 필요)</li>
+ <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - A free Mac Client for Git, Mercurial, and SVN</li>
+ </ul>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_nl.html b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_nl.html
new file mode 100644
index 00000000..a8ee2e25
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_nl.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="nl"
+ lang="nl">
+
+<body>
+<wicket:extend>
+
+ <h2>Empty Repository</h2>
+ <p></p>
+ <div class="row">
+ <div class="span10">
+ <div class="alert alert-success">
+ <span wicket:id="repository" style="font-weight: bold;">[repository]</span> is een lege repositorie en kan niet bekeken worden door Gitblit.
+ <p></p>
+ Push aub een paar commitsome commits naar <span wicket:id="pushurl"></span>
+ <p></p>
+ <hr/>
+ Nadat u een paar commits gepushed hebt kunt u deze pagina <b>verversen</b> om de repository te bekijken.
+ </div>
+ </div>
+ </div>
+
+ <h3>Git Command-Line Syntax</h3>
+ <span style="padding-bottom:5px;">Als u geen lokale Git repositorie heeft, kunt u deze repository clonen, er een paar bestanden naar committen en deze commits teug pushen naar Gitblit.</span>
+ <p></p>
+ <pre style="padding: 5px 30px;" wicket:id="cloneSyntax"></pre>
+ <p></p>
+ <span style="padding-bottom:5px;">Als u al een lokale Git repositorie heeft met commits kunt u deze repository als een remote toevoegen en er naar toe pushen.</span>
+ <p></p>
+ <pre wicket:id="remoteSyntax" style="padding: 5px 30px;"></pre>
+ <p></p>
+ <h3>Learn Git</h3>
+ Als u niet goed weet wat u met deze informatie aan moet raden we aan om het <a href="http://book.git-scm.com">Git Community Book</a> of <a href="http://progit.org/book" target="_blank">Pro Git</a> te bestuderen voor een betere begrip van hoe u Git kunt gebruiken.
+ <p></p>
+ <h4>Open Source Git Clients</h4>
+ <ul>
+ <li><a href="http://git-scm.com">Git</a> - de officiele, command-line Git</li>
+ <li><a href="http://tortoisegit.googlecode.com">TortoiseGit</a> - Windows bestandsverkenner ingetratie (officiele command-line Git is wel nodig)</li>
+ <li><a href="http://eclipse.org/egit">Eclipse/EGit</a> - Git voor de Eclipse IDE (gebaseerd op JGit, zoals Gitblit)</li>
+ <li><a href="https://code.google.com/p/gitextensions/">Git Extensions</a> - C# frontend voor Git met Windows Explorer en Visual Studio integratie</li>
+ <li><a href="http://gitx.laullon.com/">GitX (L)</a> - een Mac OS X Git client</li>
+ </ul>
+ <p></p>
+ <h4>Commercial/Closed-Source Git Clients</h4>
+ <ul>
+ <li><a href="http://www.syntevo.com/smartgit">SmartGit</a> - Een Java Git, Mercurial, en SVN client applicatie (officiele command-line Git is wel nodig)</li>
+ <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - Een gratis Mac Client voor Git, Mercurial, en SVN</li>
+ </ul>
+</wicket:extend>
+</body>
+</html>
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pl.html b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pl.html
new file mode 100644
index 00000000..109899aa
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pl.html
@@ -0,0 +1,56 @@
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="es">
+
+<body>
+<wicket:extend>
+
+ <h2>Puste repozytorium</h2>
+ <p></p>
+ <div class="row">
+ <div class="span10">
+ <div class="alert alert-success">
+ <span wicket:id="repository" style="font-weight: bold;">[repository]</span> jest pustym repozytorium i nie mo&#380;e by&#263; zaprezentowane przez Gitblit.
+ <p></p>
+ Wgraj, poprzez push, dowolne zmiany do lokalizacji <span wicket:id="pushurl"></span>
+ <p></p>
+ <hr/>
+ Po wgraniu zmian <b>od&#347;wie&#380;</b> stron&#281;, aby podejrze&#263; repozytorium.
+ </div>
+ </div>
+ </div>
+
+ <h3>Sk&#322;adnia linii polece&#324; GITa</h3>
+ <span style="padding-bottom:5px;">Je&#347;li nie posiadasz lokalnego repozytorium GITa to sklonuj to repozytorium, wgraj dowolne pliki, a nast&#281;pnie wy&#347;lij poprzez push zmiany na Gitblit.</span>
+ <p></p>
+ <pre style="padding: 5px 30px;" wicket:id="cloneSyntax"></pre>
+ <p></p>
+ <span style="padding-bottom:5px;">Gdy posiadasz lokalne repozytorium GITa z dowolnymi zmianami, to mo&#380;esz doda&#263; to repozytorium jako remote i wys&#322;a&#263; do niego zmiany poprzez push.</span>
+ <p></p>
+ <pre style="padding: 5px 30px;" wicket:id="remoteSyntax"></pre>
+ <p></p>
+ <h3>Nauka GITa</h3>
+ Je&#380;eli powy&#380;sze informacje s&#261; dla Ciebie niezrozumia&#322;e, zapoznaj si&#281; z ksi&#261;&#380;k&#261; <a href="http://git-scm.com/book/pl" target="_blank">Pro Git - Wersja PL</a> dla lepszego zrozumienia, jak poprawnie u&#380;ywa&#263; GITa.
+ <p></p>
+ <h4>Darmowi klienci GITa</h4>
+ <ul>
+ <li><a href="http://git-scm.com">Git</a> - Oficjalny klient, dost&#281;pny przez lini&#281; polece&#324;</li>
+ <li><a href="http://tortoisegit.googlecode.com">TortoiseGit</a> - Rozszerzenie eksploratora Windows (wymaga oficjalnego, dost&#281;pnego przez lini&#281; polece&#324; klienta)</li>
+ <li><a href="http://eclipse.org/egit">Eclipse/EGit</a> - GIT dla edytora Eclipse (oparty o JGit, podobnie jak Gitblit)</li>
+ <li><a href="https://code.google.com/p/gitextensions/">Git Extensions</a> - napisana w C# fasada na GIT, udost&#281;pniaj&#261;ca integracj&#281; dla Windows Explorer oraz Visual Studio</li>
+ <li><a href="http://gitx.laullon.com/">GitX (L)</a> - klient GIT na Mac OS X</li>
+ </ul>
+ <p></p>
+ <h4>Komercyjni klienci GITa</h4>
+ <ul>
+ <li><a href="http://www.syntevo.com/smartgit">SmartGit</a> - aplikacja napisana w Javie (wymaga oficjalnego, dost&#281;pnego przez lini&#281; polece&#324; klienta)</li>
+ <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - darmowy klient GIT, Mercurial i SVN na Mac OS X</li>
+ </ul>
+</wicket:extend>
+</body>
+</html>
+
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_BR.html b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_BR.html
new file mode 100644
index 00000000..351ef879
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_BR.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="pt-br"
+ lang="pt-br">
+
+<body>
+<wicket:extend>
+
+ <h2>Repositório Vazio</h2>
+ <p></p>
+ <div class="row">
+ <div class="span10">
+ <div class="alert alert-success">
+ <span wicket:id="repository" style="font-weight: bold;">[repository]</span> é um repositório vazio e não pode ser visualizado pelo Gitblit.
+ <p></p>
+ Por favor faça o push de alguns commits para <span wicket:id="pushurl"></span>
+ <p></p>
+ <hr/>
+ Depois de ter feito push você poderá <b>atualizar</b> esta página para visualizar seu repositório.
+ </div>
+ </div>
+ </div>
+
+ <h3>Sintaxe dos comandos do Git</h3>
+ <span style="padding-bottom:5px;">Se você ainda não tem um repositório local do Git, então você deve primeiro clonar este repositório, fazer commit de alguns arquivos e então fazer push desses commits para o Gitblit.</span>
+ <p></p>
+ <pre style="padding: 5px 30px;" wicket:id="cloneSyntax"></pre>
+ <p></p>
+ <span style="padding-bottom:5px;">Se você já tem um repositório Git local com alguns commits, então você deve adicionar este repositório como uma referência remota e então fazer push.</span>
+ <p></p>
+ <pre wicket:id="remoteSyntax" style="padding: 5px 30px;"></pre>
+ <p></p>
+ <h3>Aprenda Git</h3>
+ Se você estiver com dúvidas sobre como ultilizar essas informações, uma sugestão seria dar uma olhada no livro <a href="http://book.git-scm.com">Git Community Book</a> ou <a href="http://progit.org/book" target="_blank">Pro Git</a> para entender melhor como usar o Git.
+ <p></p>
+ <h4>Alguns clients do Git que são Open Source</h4>
+ <ul>
+ <li><a href="http://git-scm.com">Git</a> - o Git oficial através de linhas de comando</li>
+ <li><a href="http://tortoisegit.googlecode.com">TortoiseGit</a> - Faz integração do Explorer do Windows com o Git (por isso requer o Git Oficial)</li>
+ <li><a href="http://eclipse.org/egit">Eclipse/EGit</a> - Git para a IDE Eclipse (baseada no JGit, como o Gitblit)</li>
+ <li><a href="https://code.google.com/p/gitextensions/">Git Extensions</a> - Interface (em C#) para o Git cuja a característica é a integração com o Windows Explorer e o Visual Studio</li>
+ <li><a href="http://gitx.laullon.com/">GitX (L)</a> - um Cliente do Git para Mac OS X</li>
+ </ul>
+ <p></p>
+ <h4>Clients do Git proprietários ou com Código Fechado</h4>
+ <ul>
+ <li><a href="http://www.syntevo.com/smartgit">SmartGit</a> - Aplicação Client (em Java) para Git, Mercurial, e SVN (por isso requer o Git Oficial)</li>
+ <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - Client gratuito para o Mac que suporta Git, Mercurial e SVN</li>
+ </ul>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_zh_CN.html b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_zh_CN.html
new file mode 100644
index 00000000..4b21800e
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_zh_CN.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="zh-CN"
+ lang="zh-CN">
+
+<body>
+<wicket:extend>
+
+ <h2>空版本库</h2>
+ <p></p>
+ <div class="row">
+ <div class="span10">
+ <div class="alert alert-success">
+ <span wicket:id="repository" style="font-weight: bold;">[repository]</span> 版本库目前为空。
+ Gitblit 无法查看。
+ <p></p>
+ 请往此网址进行推送 <span wicket:id="pushurl"></span>
+ <p></p>
+ <hr/>
+ 当你推送完毕后你可以 <b>刷新</b> 此页面重新查看您的版本库。
+ </div>
+ </div>
+ </div>
+
+ <h3>Git 命令行格式</h3>
+ <span style="padding-bottom:5px;">如果您没有本地 Git 版本库, 您可以克隆此版本库, 提交一些文件, 然后将您的提交推送回Gitblit。</span>
+ <p></p>
+ <pre style="padding: 5px 30px;" wicket:id="cloneSyntax"></pre>
+ <p></p>
+ <span style="padding-bottom:5px;">如果您已经有一个本地的提交过的版本库, 那么您可以将此版本库加为远程
+ 版本库,并进行推送。</span>
+ <p></p>
+ <pre wicket:id="remoteSyntax" style="padding: 5px 30px;"></pre>
+ <p></p>
+ <h3>学习 Git</h3>
+ 如果您不明白这些信息什么意思, 您可以参考 <a href="http://book.git-scm.com">Git Community Book</a> 或者 <a href="http://progit.org/book" target="_blank">Pro Git</a> 去更加深入的学习 Git 的用法。
+ <p></p>
+ <h4>开源 Git 客户端</h4>
+ <ul>
+ <li><a href="http://git-scm.com">Git</a> - 官方, 命令行版本 Git</li>
+ <li><a href="http://tortoisegit.googlecode.com">TortoiseGit</a> - 与 Windows 资源管理器集成 (需要官方, 命令行 Git 的支持)</li>
+ <li><a href="http://eclipse.org/egit">Eclipse/EGit</a> - Git for the Eclipse IDE (基于 JGit, 类似 Gitblit)</li>
+ <li><a href="https://code.google.com/p/gitextensions/">Git Extensions</a> - C# 版本的 Git 前端,与 Windows 资源管理器和 Visual Studio 集成</li>
+ <li><a href="http://gitx.laullon.com/">GitX (L)</a> - Mac OS X Git 客户端</li>
+ </ul>
+ <p></p>
+ <h4>商业/闭源 Git 客户端</h4>
+ <ul>
+ <li><a href="http://www.syntevo.com/smartgit">SmartGit</a> - Java 版本的支持 Git, Mercurial 和 SVN 客户端应用 (需要官方, 命令行 Git 的支持)</li>
+ <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - 免费的 Mac Git Mercurial 以及 SVN 客户端, Mercurial, and SVN</li>
+ </ul>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/FederationPage.html b/src/main/java/com/gitblit/wicket/pages/FederationPage.html
new file mode 100644
index 00000000..bb39d345
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/FederationPage.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+<body>
+<wicket:extend>
+
+ <div wicket:id="federationTokensPanel">[federation tokens panel]</div>
+
+ <div style="padding-top: 10px;" wicket:id="federationProposalsPanel">[federation proposals panel]</div>
+
+ <div style="padding-top: 10px;" wicket:id="federationRegistrationsPanel">[federation registrations panel]</div>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/FederationPage.java b/src/main/java/com/gitblit/wicket/pages/FederationPage.java
new file mode 100644
index 00000000..1f98c172
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/FederationPage.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.wicket.panels.FederationProposalsPanel;
+import com.gitblit.wicket.panels.FederationRegistrationsPanel;
+import com.gitblit.wicket.panels.FederationTokensPanel;
+
+public class FederationPage extends RootPage {
+
+ public FederationPage() {
+ super();
+ setupPage("", "");
+
+ boolean showFederation = showAdmin && GitBlit.canFederate();
+ add(new FederationTokensPanel("federationTokensPanel", showFederation)
+ .setVisible(showFederation));
+ FederationProposalsPanel proposalsPanel = new FederationProposalsPanel(
+ "federationProposalsPanel");
+ if (showFederation) {
+ proposalsPanel.hideIfEmpty();
+ } else {
+ proposalsPanel.setVisible(false);
+ }
+
+ boolean showRegistrations = GitBlit.getBoolean(Keys.web.showFederationRegistrations, false);
+ FederationRegistrationsPanel registrationsPanel = new FederationRegistrationsPanel(
+ "federationRegistrationsPanel");
+ if (showAdmin || showRegistrations) {
+ registrationsPanel.hideIfEmpty();
+ } else {
+ registrationsPanel.setVisible(false);
+ }
+ add(proposalsPanel);
+ add(registrationsPanel);
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/FederationRegistrationPage.html b/src/main/java/com/gitblit/wicket/pages/FederationRegistrationPage.html
new file mode 100644
index 00000000..d7b9bdde
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/FederationRegistrationPage.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+ <!-- registration info -->
+ <table class="plain">
+ <tr><th><wicket:message key="gb.url">url</wicket:message></th><td><img style="border:0px;vertical-align:middle;" wicket:id="typeIcon" /> <span wicket:id="url">[url]</span></td></tr>
+ <tr><th><wicket:message key="gb.token">token</wicket:message></th><td><span class="sha1" wicket:id="token">[token]</span></td></tr>
+ <tr><th><wicket:message key="gb.folder">folder</wicket:message></th><td><span wicket:id="folder">[folder]</span></td></tr>
+ <tr><th><wicket:message key="gb.frequency">frequency</wicket:message></th><td><span wicket:id="frequency">[frequency]</span></td></tr>
+ <tr><th><wicket:message key="gb.lastPull">lastPull</wicket:message></th><td><span wicket:id="lastPull">[lastPull]</span></td></tr>
+ <tr><th><wicket:message key="gb.nextPull">nextPull</wicket:message></th><td><span wicket:id="nextPull">[nextPull]</span></td></tr>
+ <tr><th valign="top"><wicket:message key="gb.exclusions">exclusions</wicket:message></th><td><span class="sha1" wicket:id="exclusions">[exclusions]</span></td></tr>
+ <tr><th valign="top"><wicket:message key="gb.inclusions">inclusions</wicket:message></th><td><span class="sha1" wicket:id="inclusions">[inclusions]</span></td></tr>
+ </table>
+
+ <table class="repositories">
+ <tr>
+ <th class="left">
+ <img style="vertical-align: top; border: 1px solid #888; background-color: white;" src="git-black-16x16.png"/>
+ <wicket:message key="gb.repositories">[repositories]</wicket:message>
+ </th>
+ <th class="right"><wicket:message key="gb.status">[status]</wicket:message></th>
+ </tr>
+ <tbody>
+ <tr wicket:id="row">
+ <td class="left"><img style="border:0px;vertical-align:middle;" wicket:id="statusIcon" /><span wicket:id="name">[name]</span></td>
+ <td class="right"><span wicket:id="status">[status]</span></td>
+ </tr>
+ </tbody>
+ </table>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/FederationRegistrationPage.java b/src/main/java/com/gitblit/wicket/pages/FederationRegistrationPage.java
new file mode 100644
index 00000000..19c30a5e
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/FederationRegistrationPage.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+
+import com.gitblit.GitBlit;
+import com.gitblit.models.FederationModel;
+import com.gitblit.models.FederationModel.RepositoryStatus;
+import com.gitblit.wicket.WicketUtils;
+
+public class FederationRegistrationPage extends RootSubPage {
+
+ public FederationRegistrationPage(PageParameters params) {
+ super(params);
+
+ setStatelessHint(true);
+
+ String url = WicketUtils.getUrlParameter(params);
+ String name = WicketUtils.getNameParameter(params);
+
+ FederationModel registration = GitBlit.self().getFederationRegistration(url, name);
+ if (registration == null) {
+ error(getString("gb.couldNotFindFederationRegistration"), true);
+ }
+
+ setupPage(registration.isResultData() ? getString("gb.federationResults")
+ : getString("gb.federationRegistration"), registration.url);
+
+ add(new Label("url", registration.url));
+ add(WicketUtils.getRegistrationImage("typeIcon", registration, this));
+ add(new Label("frequency", registration.frequency));
+ add(new Label("folder", registration.folder));
+ add(new Label("token", showAdmin ? registration.token : "--"));
+ add(WicketUtils.createTimestampLabel("lastPull", registration.lastPull, getTimeZone(), getTimeUtils()));
+ add(WicketUtils.createTimestampLabel("nextPull", registration.nextPull, getTimeZone(), getTimeUtils()));
+
+ StringBuilder inclusions = new StringBuilder();
+ for (String inc : registration.inclusions) {
+ inclusions.append(inc).append("<br/>");
+ }
+ StringBuilder exclusions = new StringBuilder();
+ for (String ex : registration.exclusions) {
+ exclusions.append(ex).append("<br/>");
+ }
+
+ add(new Label("inclusions", inclusions.toString()).setEscapeModelStrings(false));
+
+ add(new Label("exclusions", exclusions.toString()).setEscapeModelStrings(false));
+
+ List<RepositoryStatus> list = registration.getStatusList();
+ Collections.sort(list);
+ DataView<RepositoryStatus> dataView = new DataView<RepositoryStatus>("row",
+ new ListDataProvider<RepositoryStatus>(list)) {
+ private static final long serialVersionUID = 1L;
+ private int counter;
+
+ @Override
+ protected void onBeforeRender() {
+ super.onBeforeRender();
+ counter = 0;
+ }
+
+ public void populateItem(final Item<RepositoryStatus> item) {
+ final RepositoryStatus entry = item.getModelObject();
+ item.add(WicketUtils.getPullStatusImage("statusIcon", entry.status));
+ item.add(new Label("name", entry.name));
+ item.add(new Label("status", entry.status.name()));
+ WicketUtils.setAlternatingBackground(item, counter);
+ counter++;
+ }
+ };
+ add(dataView);
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/ForkPage.html b/src/main/java/com/gitblit/wicket/pages/ForkPage.html
new file mode 100644
index 00000000..72093696
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/ForkPage.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:head>
+ <noscript>
+ <meta http-equiv="refresh" content="5"></meta>
+ </noscript>
+ <script type="text/javascript"">
+ function doLoad() { setTimeout( "refresh()", 5*1000 ); }
+ function refresh() { window.location.reload(); }
+ </script>
+</wicket:head>
+<wicket:extend>
+<!-- need to specify body.onload -->
+<body onload="doLoad()">
+
+ <div class="row">
+ <div class="span6 offset3">
+ <div style="opacity:0.2;">
+ <center><img style="padding:10px" src="git-black_210x210.png"></img></center>
+ </div>
+ <div wicket:id="forkText" class="pageTitle project" style="border:0;font-weight:bold; text-align:center;">[fork text]</div>
+ </div>
+ <div class="span4 offset4">
+ <div class="progress progress-striped active">
+ <div class="bar" style="width: 100%;"></div>
+ </div>
+ </div>
+ </div>
+</body>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/ForkPage.java b/src/main/java/com/gitblit/wicket/pages/ForkPage.java
new file mode 100644
index 00000000..340bd823
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/ForkPage.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2012 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.GitBlit;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.GitblitRedirectException;
+import com.gitblit.wicket.WicketUtils;
+
+public class ForkPage extends RepositoryPage {
+
+
+ public ForkPage(PageParameters params) {
+ super(params);
+
+ setVersioned(false);
+
+ GitBlitWebSession session = GitBlitWebSession.get();
+
+ RepositoryModel repository = getRepositoryModel();
+ UserModel user = session.getUser();
+ boolean canFork = user.canFork(repository);
+
+ if (!canFork) {
+ // redirect to the summary page if this repository is not empty
+ GitBlitWebSession.get().cacheErrorMessage(
+ MessageFormat.format(getString("gb.forkNotAuthorized"), repository.name));
+ throw new GitblitRedirectException(SummaryPage.class, WicketUtils.newRepositoryParameter(repository.name));
+ }
+
+ String fork = GitBlit.self().getFork(user.username, repository.name);
+ if (fork != null) {
+ // redirect to user's fork
+ throw new GitblitRedirectException(SummaryPage.class, WicketUtils.newRepositoryParameter(fork));
+ }
+
+ add(new Label("forkText", getString("gb.preparingFork")));
+
+ if (!session.isForking()) {
+ // prepare session
+ session.isForking(true);
+
+ // fork it
+ ForkThread forker = new ForkThread(repository, session);
+ forker.start();
+ }
+ }
+
+ @Override
+ protected boolean allowForkControls() {
+ return false;
+ }
+
+ @Override
+ protected String getPageName() {
+ return "fork";
+ }
+
+ /**
+ * ForkThread does the work of working the repository in a background
+ * thread. The completion status is tracked through a session variable and
+ * monitored by this page.
+ */
+ private static class ForkThread extends Thread {
+
+ private final RepositoryModel repository;
+ private final GitBlitWebSession session;
+
+ public ForkThread(RepositoryModel repository, GitBlitWebSession session) {
+ this.repository = repository;
+ this.session = session;
+ }
+
+ @Override
+ public void run() {
+ UserModel user = session.getUser();
+ try {
+ GitBlit.self().fork(repository, user);
+ } catch (Exception e) {
+ LoggerFactory.getLogger(ForkPage.class).error(MessageFormat.format("Failed to fork {0} for {1}", repository.name, user.username), e);
+ } finally {
+ session.isForking(false);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/ForksPage.html b/src/main/java/com/gitblit/wicket/pages/ForksPage.html
new file mode 100644
index 00000000..c18d2a49
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/ForksPage.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <div wicket:id="fork">
+ <div>
+ <span wicket:id="anAvatar" style="vertical-align: baseline;font-weight:bold;"></span>
+ <span wicket:id="aProject">[a project]</span> / <span wicket:id="aFork">[a fork]</span>
+ <span style="padding-left:10px;" wicket:id="lastChange"></span>
+ </div>
+ </div>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/ForksPage.java b/src/main/java/com/gitblit/wicket/pages/ForksPage.java
new file mode 100644
index 00000000..cc483878
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/ForksPage.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2012 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+import org.eclipse.jgit.lib.PersonIdent;
+
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.ForkModel;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.GravatarImage;
+import com.gitblit.wicket.panels.LinkPanel;
+
+public class ForksPage extends RepositoryPage {
+
+ public ForksPage(PageParameters params) {
+ super(params);
+
+ final RepositoryModel pageRepository = getRepositoryModel();
+
+ ForkModel root = GitBlit.self().getForkNetwork(pageRepository.name);
+ List<FlatFork> network = flatten(root);
+
+ ListDataProvider<FlatFork> forksDp = new ListDataProvider<FlatFork>(network);
+ DataView<FlatFork> forksList = new DataView<FlatFork>("fork", forksDp) {
+ private static final long serialVersionUID = 1L;
+
+ public void populateItem(final Item<FlatFork> item) {
+ FlatFork fork = item.getModelObject();
+ RepositoryModel repository = fork.repository;
+
+ if (repository.isPersonalRepository()) {
+ UserModel user = GitBlit.self().getUserModel(repository.projectPath.substring(1));
+ PersonIdent ident = new PersonIdent(user.getDisplayName(), user.emailAddress == null ? user.getDisplayName() : user.emailAddress);
+ item.add(new GravatarImage("anAvatar", ident, 20));
+ if (pageRepository.equals(repository)) {
+ // do not link to self
+ item.add(new Label("aProject", user.getDisplayName()));
+ } else {
+ item.add(new LinkPanel("aProject", null, user.getDisplayName(), UserPage.class, WicketUtils.newUsernameParameter(user.username)));
+ }
+ } else {
+ Component swatch;
+ if (repository.isBare){
+ swatch = new Label("anAvatar", "&nbsp;").setEscapeModelStrings(false);
+ } else {
+ swatch = new Label("anAvatar", "!");
+ WicketUtils.setHtmlTooltip(swatch, getString("gb.workingCopyWarning"));
+ }
+ WicketUtils.setCssClass(swatch, "repositorySwatch");
+ WicketUtils.setCssBackground(swatch, repository.toString());
+ item.add(swatch);
+ String projectName = repository.projectPath;
+ if (StringUtils.isEmpty(projectName)) {
+ projectName = GitBlit.getString(Keys.web.repositoryRootGroupName, "main");
+ }
+ if (pageRepository.equals(repository)) {
+ // do not link to self
+ item.add(new Label("aProject", projectName));
+ } else {
+ item.add(new LinkPanel("aProject", null, projectName, ProjectPage.class, WicketUtils.newProjectParameter(projectName)));
+ }
+ }
+
+ String repo = StringUtils.getLastPathElement(repository.name);
+ UserModel user = GitBlitWebSession.get().getUser();
+ if (user == null) {
+ user = UserModel.ANONYMOUS;
+ }
+ if (user.canView(repository)) {
+ if (pageRepository.equals(repository)) {
+ // do not link to self
+ item.add(new Label("aFork", StringUtils.stripDotGit(repo)));
+ } else {
+ item.add(new LinkPanel("aFork", null, StringUtils.stripDotGit(repo), SummaryPage.class, WicketUtils.newRepositoryParameter(repository.name)));
+ }
+ item.add(WicketUtils.createDateLabel("lastChange", repository.lastChange, getTimeZone(), getTimeUtils()));
+ } else {
+ item.add(new Label("aFork", repo));
+ item.add(new Label("lastChange").setVisible(false));
+ }
+
+ WicketUtils.setCssStyle(item, "margin-left:" + (32*fork.level) + "px;");
+ if (fork.level == 0) {
+ WicketUtils.setCssClass(item, "forkSource");
+ } else {
+ WicketUtils.setCssClass(item, "forkEntry");
+ }
+ }
+ };
+
+ add(forksList);
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.forks");
+ }
+
+ protected List<FlatFork> flatten(ForkModel root) {
+ List<FlatFork> list = new ArrayList<FlatFork>();
+ list.addAll(flatten(root, 0));
+ return list;
+ }
+
+ protected List<FlatFork> flatten(ForkModel node, int level) {
+ List<FlatFork> list = new ArrayList<FlatFork>();
+ list.add(new FlatFork(node.repository, level));
+ if (!node.isLeaf()) {
+ for (ForkModel fork : node.forks) {
+ list.addAll(flatten(fork, level + 1));
+ }
+ }
+ return list;
+ }
+
+ private class FlatFork implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ public final RepositoryModel repository;
+ public final int level;
+
+ public FlatFork(RepositoryModel repository, int level) {
+ this.repository = repository;
+ this.level = level;
+ }
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/GitSearchPage.html b/src/main/java/com/gitblit/wicket/pages/GitSearchPage.html
new file mode 100644
index 00000000..9bb1f418
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/GitSearchPage.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <!-- pager links -->
+ <div style="padding-top:5px;">
+ <a wicket:id="firstPageTop"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageTop"><wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageTop"><wicket:message key="gb.pageNext"></wicket:message></a>
+ </div>
+
+ <!-- history -->
+ <div style="margin-top:5px;" wicket:id="searchPanel">[search panel]</div>
+
+ <!-- pager links -->
+ <div style="padding-bottom:5px;">
+ <a wicket:id="firstPageBottom"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageBottom"><wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageBottom"><wicket:message key="gb.pageNext"></wicket:message></a>
+ </div>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/GitSearchPage.java b/src/main/java/com/gitblit/wicket/pages/GitSearchPage.java
new file mode 100644
index 00000000..6b0714f0
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/GitSearchPage.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+
+import com.gitblit.Constants;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.SearchPanel;
+
+public class GitSearchPage extends RepositoryPage {
+
+ public GitSearchPage(PageParameters params) {
+ super(params);
+
+ String value = WicketUtils.getSearchString(params);
+ String type = WicketUtils.getSearchType(params);
+ Constants.SearchType searchType = Constants.SearchType.forName(type);
+
+ int pageNumber = WicketUtils.getPage(params);
+ int prevPage = Math.max(0, pageNumber - 1);
+ int nextPage = pageNumber + 1;
+
+ SearchPanel search = new SearchPanel("searchPanel", repositoryName, objectId, value,
+ searchType, getRepository(), -1, pageNumber - 1, getRepositoryModel().showRemoteBranches);
+ boolean hasMore = search.hasMore();
+ add(search);
+
+ add(new BookmarkablePageLink<Void>("firstPageTop", GitSearchPage.class,
+ WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType))
+ .setEnabled(pageNumber > 1));
+ add(new BookmarkablePageLink<Void>("prevPageTop", GitSearchPage.class,
+ WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType,
+ prevPage)).setEnabled(pageNumber > 1));
+ add(new BookmarkablePageLink<Void>("nextPageTop", GitSearchPage.class,
+ WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType,
+ nextPage)).setEnabled(hasMore));
+
+ add(new BookmarkablePageLink<Void>("firstPageBottom", GitSearchPage.class,
+ WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType))
+ .setEnabled(pageNumber > 1));
+ add(new BookmarkablePageLink<Void>("prevPageBottom", GitSearchPage.class,
+ WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType,
+ prevPage)).setEnabled(pageNumber > 1));
+ add(new BookmarkablePageLink<Void>("nextPageBottom", GitSearchPage.class,
+ WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType,
+ nextPage)).setEnabled(hasMore));
+
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.search");
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/GravatarProfilePage.html b/src/main/java/com/gitblit/wicket/pages/GravatarProfilePage.html
new file mode 100644
index 00000000..0cc0f1fc
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/GravatarProfilePage.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+<body>
+<wicket:extend>
+ <div class="pageTitle">
+ <h2>Gravatar<small> / <span wicket:id="username">[username]</span></small></h2>
+ </div>
+ <img class="gravatar" wicket:id="profileImage"></img>
+ <h2 wicket:id="displayName"></h2>
+ <div style="color:#888;"wicket:id="location"></div>
+ <div style="padding-top:5px;" wicket:id="aboutMe"></div>
+ <p></p>
+ <a wicket:id="profileLink"><wicket:message key="gb.completeGravatarProfile">[Complete profile on Gravatar.com]</wicket:message></a>
+ <p></p>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/GravatarProfilePage.java b/src/main/java/com/gitblit/wicket/pages/GravatarProfilePage.java
new file mode 100644
index 00000000..ee567d75
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/GravatarProfilePage.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.ExternalLink;
+
+import com.gitblit.models.GravatarProfile;
+import com.gitblit.utils.ActivityUtils;
+import com.gitblit.wicket.ExternalImage;
+import com.gitblit.wicket.WicketUtils;
+
+/**
+ * Gravatar Profile Page shows the Gravatar information, if available.
+ *
+ * @author James Moger
+ *
+ */
+public class GravatarProfilePage extends RootPage {
+
+ public GravatarProfilePage(PageParameters params) {
+ super();
+ setupPage("", "");
+ String object = WicketUtils.getObject(params);
+ GravatarProfile profile = null;
+ try {
+ if (object.indexOf('@') > -1) {
+ profile = ActivityUtils.getGravatarProfileFromAddress(object);
+ } else {
+ profile = ActivityUtils.getGravatarProfile(object);
+ }
+ } catch (IOException e) {
+ error(MessageFormat.format(getString("gb.failedToFindGravatarProfile"), object), e, true);
+ }
+
+ if (profile == null) {
+ error(MessageFormat.format(getString("gb.failedToFindGravatarProfile"), object), true);
+ }
+ add(new Label("displayName", profile.displayName));
+ add(new Label("username", profile.preferredUsername));
+ add(new Label("location", profile.currentLocation));
+ add(new Label("aboutMe", profile.aboutMe));
+ ExternalImage image = new ExternalImage("profileImage", profile.thumbnailUrl + "?s=256&d=identicon");
+ add(image);
+ add(new ExternalLink("profileLink", profile.profileUrl));
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/HistoryPage.html b/src/main/java/com/gitblit/wicket/pages/HistoryPage.html
new file mode 100644
index 00000000..f9bd2f69
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/HistoryPage.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <!-- pager links -->
+ <div style="padding-top:5px;">
+ <a wicket:id="firstPageTop"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageTop"><wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageTop"><wicket:message key="gb.pageNext"></wicket:message></a>
+ </div>
+
+ <!-- history -->
+ <div style="margin-top:5px;" wicket:id="historyPanel">[history panel]</div>
+
+ <!-- pager links -->
+ <div style="padding-bottom:5px;">
+ <a wicket:id="firstPageBottom"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageBottom"><wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageBottom"><wicket:message key="gb.pageNext"></wicket:message></a>
+ </div>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/HistoryPage.java b/src/main/java/com/gitblit/wicket/pages/HistoryPage.java
new file mode 100644
index 00000000..563202e6
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/HistoryPage.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.HistoryPanel;
+
+public class HistoryPage extends RepositoryPage {
+
+ public HistoryPage(PageParameters params) {
+ super(params);
+
+ String path = WicketUtils.getPath(params);
+ int pageNumber = WicketUtils.getPage(params);
+ int prevPage = Math.max(0, pageNumber - 1);
+ int nextPage = pageNumber + 1;
+
+ HistoryPanel history = new HistoryPanel("historyPanel", repositoryName, objectId, path,
+ getRepository(), -1, pageNumber - 1, getRepositoryModel().showRemoteBranches);
+ boolean hasMore = history.hasMore();
+ add(history);
+
+ add(new BookmarkablePageLink<Void>("firstPageTop", HistoryPage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, path))
+ .setEnabled(pageNumber > 1));
+ add(new BookmarkablePageLink<Void>("prevPageTop", HistoryPage.class,
+ WicketUtils.newHistoryPageParameter(repositoryName, objectId, path, prevPage))
+ .setEnabled(pageNumber > 1));
+ add(new BookmarkablePageLink<Void>("nextPageTop", HistoryPage.class,
+ WicketUtils.newHistoryPageParameter(repositoryName, objectId, path, nextPage))
+ .setEnabled(hasMore));
+
+ add(new BookmarkablePageLink<Void>("firstPageBottom", HistoryPage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, path))
+ .setEnabled(pageNumber > 1));
+ add(new BookmarkablePageLink<Void>("prevPageBottom", HistoryPage.class,
+ WicketUtils.newHistoryPageParameter(repositoryName, objectId, path, prevPage))
+ .setEnabled(pageNumber > 1));
+ add(new BookmarkablePageLink<Void>("nextPageBottom", HistoryPage.class,
+ WicketUtils.newHistoryPageParameter(repositoryName, objectId, path, nextPage))
+ .setEnabled(hasMore));
+
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.history");
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/LogPage.html b/src/main/java/com/gitblit/wicket/pages/LogPage.html
new file mode 100644
index 00000000..8e5cb96a
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/LogPage.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <!-- pager links -->
+ <div style="padding-top:5px;">
+ <a wicket:id="firstPageTop"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageTop"><wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageTop"><wicket:message key="gb.pageNext"></wicket:message></a>
+ </div>
+
+ <!-- log -->
+ <div style="margin-top:5px;" wicket:id="logPanel">[log panel]</div>
+
+ <!-- pager links -->
+ <div style="padding-bottom:5px;">
+ <a wicket:id="firstPageBottom"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageBottom"><wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageBottom"><wicket:message key="gb.pageNext"></wicket:message></a>
+ </div>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/LogPage.java b/src/main/java/com/gitblit/wicket/pages/LogPage.java
new file mode 100644
index 00000000..ee8ddfef
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/LogPage.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.LogPanel;
+
+public class LogPage extends RepositoryPage {
+
+ public LogPage(PageParameters params) {
+ super(params);
+
+ addSyndicationDiscoveryLink();
+
+ int pageNumber = WicketUtils.getPage(params);
+ int prevPage = Math.max(0, pageNumber - 1);
+ int nextPage = pageNumber + 1;
+ String refid = objectId;
+ if (StringUtils.isEmpty(refid)) {
+ refid = getRepositoryModel().HEAD;
+ }
+ LogPanel logPanel = new LogPanel("logPanel", repositoryName, refid, getRepository(), -1,
+ pageNumber - 1, getRepositoryModel().showRemoteBranches);
+ boolean hasMore = logPanel.hasMore();
+ add(logPanel);
+
+ add(new BookmarkablePageLink<Void>("firstPageTop", LogPage.class,
+ WicketUtils.newObjectParameter(repositoryName, objectId))
+ .setEnabled(pageNumber > 1));
+ add(new BookmarkablePageLink<Void>("prevPageTop", LogPage.class,
+ WicketUtils.newLogPageParameter(repositoryName, objectId, prevPage))
+ .setEnabled(pageNumber > 1));
+ add(new BookmarkablePageLink<Void>("nextPageTop", LogPage.class,
+ WicketUtils.newLogPageParameter(repositoryName, objectId, nextPage))
+ .setEnabled(hasMore));
+
+ add(new BookmarkablePageLink<Void>("firstPageBottom", LogPage.class,
+ WicketUtils.newObjectParameter(repositoryName, objectId))
+ .setEnabled(pageNumber > 1));
+ add(new BookmarkablePageLink<Void>("prevPageBottom", LogPage.class,
+ WicketUtils.newLogPageParameter(repositoryName, objectId, prevPage))
+ .setEnabled(pageNumber > 1));
+ add(new BookmarkablePageLink<Void>("nextPageBottom", LogPage.class,
+ WicketUtils.newLogPageParameter(repositoryName, objectId, nextPage))
+ .setEnabled(hasMore));
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.log");
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/LogoutPage.html b/src/main/java/com/gitblit/wicket/pages/LogoutPage.html
new file mode 100644
index 00000000..d4077830
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/LogoutPage.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+<body>
+<wicket:extend>
+ <div class="navbar navbar-fixed-top">
+ <div class="navbar-inner">
+ <div class="container">
+ <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </a>
+ <a class="brand" wicket:id="rootLink">
+ <img src="gitblt_25_white.png" width="79" height="25" alt="gitblit" class="logo"/>
+ </a>
+
+ </div>
+ </div>
+ </div>
+
+ <!-- subclass content -->
+ <div class="container">
+ <div style="text-align:center" wicket:id="feedback">[Feedback Panel]</div>
+
+ <h1><wicket:message key="gb.sessionEnded">[Session has ended]</wicket:message></h1>
+ <p><wicket:message key="gb.closeBrowser">[Please close the browser]</wicket:message></p>
+ </div>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/LogoutPage.java b/src/main/java/com/gitblit/wicket/pages/LogoutPage.java
new file mode 100644
index 00000000..982de0ec
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/LogoutPage.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.protocol.http.WebRequest;
+import org.apache.wicket.protocol.http.WebResponse;
+
+import com.gitblit.GitBlit;
+import com.gitblit.models.UserModel;
+import com.gitblit.wicket.GitBlitWebSession;
+
+public class LogoutPage extends BasePage {
+
+ public LogoutPage() {
+ super();
+ GitBlitWebSession session = GitBlitWebSession.get();
+ UserModel user = session.getUser();
+ GitBlit.self().setCookie((WebResponse) getResponse(), null);
+ GitBlit.self().logout(user);
+ session.invalidate();
+
+ /*
+ * Now check whether the authentication was realized via the Authorization in the header.
+ * If so, it is likely to be cached by the browser, and cannot be undone. Effectively, this means
+ * that you cannot log out...
+ */
+ if ( ((WebRequest)getRequest()).getHttpServletRequest().getHeader("Authorization") != null ) {
+ // authentication will be done via this route anyway, show a page to close the browser:
+ // this will be done by Wicket.
+ setupPage(null, getString("gb.logout"));
+
+ } else {
+ setRedirect(true);
+ setResponsePage(getApplication().getHomePage());
+ } // not via WWW-Auth
+ } // LogoutPage
+} \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/LuceneSearchPage.html b/src/main/java/com/gitblit/wicket/pages/LuceneSearchPage.html
new file mode 100644
index 00000000..aba43de8
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/LuceneSearchPage.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<!-- contribute google-code-prettify resources to the page header -->
+<wicket:head>
+ <wicket:link>
+ <link href="prettify/prettify.css" type="text/css" rel="stylesheet" />
+ <script type="text/javascript" src="prettify/prettify.js"></script>
+ </wicket:link>
+</wicket:head>
+
+<wicket:extend>
+<body onload="document.getElementById('query').focus(); prettyPrint();">
+ <div class="pageTitle">
+ <h2><wicket:message key="gb.search"></wicket:message></h2>
+ </div>
+ <form class="form-inline" wicket:id="searchForm">
+ <div class="row">
+ <div class="span3">
+ <h3><wicket:message key="gb.repositories"></wicket:message></h3>
+ <select wicket:id="repositories" ></select>
+ </div>
+ <div class="span9" style="margin-left:10px">
+ <div>
+ <h3><wicket:message key="gb.query"></wicket:message></h3>
+ <input class="span8" id="query" type="text" wicket:id="query" placeholder="enter search text"></input>
+ <button class="btn btn-primary" type="submit" value="Search"><wicket:message key="gb.search"></wicket:message></button>
+ </div>
+ <div style="margin-top:10px;">
+ <div style="margin-left:0px;" class="span3">
+ <div class="alert alert">
+ <b>type:</b> commit or blob<br/>
+ <b>commit:</b> commit id<br/>
+ <b>path:</b> "path/to/blob"<br/>
+ <b>branch:</b> "refs/heads/master"<br/>
+ <b>author:</b> or <b>committer:</b>
+ </div>
+ </div>
+ <div style="margin-left:10px;" class="span4">
+ <div class="alert alert-info">
+ type:commit AND "bug fix"<br/>
+ type:commit AND author:james*<br/>
+ type:blob AND "int errorCode"<br/>
+ type:blob AND test AND path:*.java<br/>
+ commit:d91e5*
+ </div>
+ </div>
+ <div style="margin-left:10px;" class="span2">
+ <wicket:message key="gb.queryHelp"></wicket:message>
+ </div>
+ </div>
+ </div>
+ </div>
+ </form>
+
+ <div class="row-fluid">
+ <!-- results header -->
+ <div class="span8">
+ <h3><span wicket:id="resultsHeader"></span> <small><br/><span wicket:id="resultsCount"></span></small></h3>
+ </div>
+ <!-- pager links -->
+ <div class="span4" wicket:id="topPager"></div>
+ </div>
+
+ <div class="row-fluid">
+ <!-- search result repeater -->
+ <div class="searchResult" wicket:id="searchResults">
+ <div><i wicket:id="type"></i><span class="summary" wicket:id="summary"></span> <span wicket:id="tags" style="padding-left:10px;"></span></div>
+ <div class="body">
+ <div class="fragment" wicket:id="fragment"></div>
+ <div><span class="author" wicket:id="author"></span> <span class="date" ><wicket:message key="gb.authored"></wicket:message> <span class="date" wicket:id="date"></span></span></div>
+ <span class="repository" wicket:id="repository"></span>:<span class="branch" wicket:id="branch"></span>
+ </div>
+ </div>
+
+ <!-- pager links -->
+ <div wicket:id="bottomPager"></div>
+
+ </div>
+</body>
+
+ <wicket:fragment wicket:id="tagsPanel">
+ <span wicket:id="tag">
+ <span wicket:id="tagLink">[tag]</span>
+ </span>
+ </wicket:fragment>
+
+</wicket:extend>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/LuceneSearchPage.java b/src/main/java/com/gitblit/wicket/pages/LuceneSearchPage.java
new file mode 100644
index 00000000..79795ff2
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/LuceneSearchPage.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2012 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.ListMultipleChoice;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+import org.apache.wicket.model.Model;
+import org.eclipse.jgit.lib.Constants;
+
+import com.gitblit.Constants.SearchType;
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.SearchResult;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.SessionlessForm;
+import com.gitblit.wicket.StringChoiceRenderer;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.LinkPanel;
+import com.gitblit.wicket.panels.PagerPanel;
+
+public class LuceneSearchPage extends RootPage {
+
+ public LuceneSearchPage() {
+ super();
+ setup(null);
+ }
+
+ public LuceneSearchPage(PageParameters params) {
+ super(params);
+ setup(params);
+ }
+
+ private void setup(PageParameters params) {
+ setupPage("", "");
+
+ // default values
+ ArrayList<String> repositories = new ArrayList<String>();
+ String query = "";
+ int page = 1;
+ int pageSize = GitBlit.getInteger(Keys.web.itemsPerPage, 50);
+
+ if (params != null) {
+ String repository = WicketUtils.getRepositoryName(params);
+ if (!StringUtils.isEmpty(repository)) {
+ repositories.add(repository);
+ }
+
+ page = WicketUtils.getPage(params);
+
+ if (params.containsKey("repositories")) {
+ String value = params.getString("repositories", "");
+ List<String> list = StringUtils.getStringsFromValue(value);
+ repositories.addAll(list);
+ }
+
+ if (params.containsKey("query")) {
+ query = params.getString("query", "");
+ } else {
+ String value = WicketUtils.getSearchString(params);
+ String type = WicketUtils.getSearchType(params);
+ com.gitblit.Constants.SearchType searchType = com.gitblit.Constants.SearchType.forName(type);
+ if (!StringUtils.isEmpty(value)) {
+ if (searchType == SearchType.COMMIT) {
+ query = "type:" + searchType.name().toLowerCase() + " AND \"" + value + "\"";
+ } else {
+ query = searchType.name().toLowerCase() + ":\"" + value + "\"";
+ }
+ }
+ }
+ }
+
+ // display user-accessible selections
+ UserModel user = GitBlitWebSession.get().getUser();
+ List<String> availableRepositories = new ArrayList<String>();
+ for (RepositoryModel model : GitBlit.self().getRepositoryModels(user)) {
+ if (model.hasCommits && !ArrayUtils.isEmpty(model.indexedBranches)) {
+ availableRepositories.add(model.name);
+ }
+ }
+ boolean luceneEnabled = GitBlit.getBoolean(Keys.web.allowLuceneIndexing, true);
+ if (luceneEnabled) {
+ if (availableRepositories.size() == 0) {
+ info(getString("gb.noIndexedRepositoriesWarning"));
+ }
+ } else {
+ error(getString("gb.luceneDisabled"));
+ }
+
+ // enforce user-accessible repository selections
+ ArrayList<String> searchRepositories = new ArrayList<String>();
+ for (String selectedRepository : repositories) {
+ if (availableRepositories.contains(selectedRepository)) {
+ searchRepositories.add(selectedRepository);
+ }
+ }
+
+ // search form
+ final Model<String> queryModel = new Model<String>(query);
+ final Model<ArrayList<String>> repositoriesModel = new Model<ArrayList<String>>(searchRepositories);
+ SessionlessForm<Void> form = new SessionlessForm<Void>("searchForm", getClass()) {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void onSubmit() {
+ String q = queryModel.getObject();
+ if (StringUtils.isEmpty(q)) {
+ error(getString("gb.undefinedQueryWarning"));
+ return;
+ }
+ if (repositoriesModel.getObject().size() == 0) {
+ error(getString("gb.noSelectedRepositoriesWarning"));
+ return;
+ }
+ PageParameters params = new PageParameters();
+ params.put("repositories", StringUtils.flattenStrings(repositoriesModel.getObject()));
+ params.put("query", queryModel.getObject());
+ LuceneSearchPage page = new LuceneSearchPage(params);
+ setResponsePage(page);
+ }
+ };
+
+ ListMultipleChoice<String> selections = new ListMultipleChoice<String>("repositories",
+ repositoriesModel, availableRepositories, new StringChoiceRenderer());
+ selections.setMaxRows(8);
+ form.add(selections.setEnabled(luceneEnabled));
+ form.add(new TextField<String>("query", queryModel).setEnabled(luceneEnabled));
+ add(form.setEnabled(luceneEnabled));
+
+ // execute search
+ final List<SearchResult> results = new ArrayList<SearchResult>();
+ if (!ArrayUtils.isEmpty(searchRepositories) && !StringUtils.isEmpty(query)) {
+ results.addAll(GitBlit.self().search(query, page, pageSize, searchRepositories));
+ }
+
+ // results header
+ if (results.size() == 0) {
+ if (!ArrayUtils.isEmpty(searchRepositories) && !StringUtils.isEmpty(query)) {
+ add(new Label("resultsHeader", query).setRenderBodyOnly(true));
+ add(new Label("resultsCount", getString("gb.noHits")).setRenderBodyOnly(true));
+ } else {
+ add(new Label("resultsHeader").setVisible(false));
+ add(new Label("resultsCount").setVisible(false));
+ }
+ } else {
+ add(new Label("resultsHeader", query).setRenderBodyOnly(true));
+ add(new Label("resultsCount", MessageFormat.format(getString("gb.queryResults"),
+ results.get(0).hitId, results.get(results.size() - 1).hitId, results.get(0).totalHits)).
+ setRenderBodyOnly(true));
+ }
+
+ // search results view
+ ListDataProvider<SearchResult> resultsDp = new ListDataProvider<SearchResult>(results);
+ final DataView<SearchResult> resultsView = new DataView<SearchResult>("searchResults", resultsDp) {
+ private static final long serialVersionUID = 1L;
+ public void populateItem(final Item<SearchResult> item) {
+ final SearchResult sr = item.getModelObject();
+ switch(sr.type) {
+ case commit: {
+ Label icon = WicketUtils.newIcon("type", "icon-refresh");
+ WicketUtils.setHtmlTooltip(icon, "commit");
+ item.add(icon);
+ item.add(new LinkPanel("summary", null, sr.summary, CommitPage.class, WicketUtils.newObjectParameter(sr.repository, sr.commitId)));
+ // show tags
+ Fragment fragment = new Fragment("tags", "tagsPanel", LuceneSearchPage.this);
+ List<String> tags = sr.tags;
+ if (tags == null) {
+ tags = new ArrayList<String>();
+ }
+ ListDataProvider<String> tagsDp = new ListDataProvider<String>(tags);
+ final DataView<String> tagsView = new DataView<String>("tag", tagsDp) {
+ private static final long serialVersionUID = 1L;
+ public void populateItem(final Item<String> item) {
+ String tag = item.getModelObject();
+ Component c = new LinkPanel("tagLink", null, tag, TagPage.class,
+ WicketUtils.newObjectParameter(sr.repository, Constants.R_TAGS + tag));
+ WicketUtils.setCssClass(c, "tagRef");
+ item.add(c);
+ }
+ };
+ fragment.add(tagsView);
+ item.add(fragment);
+ break;
+ }
+ case blob: {
+ Label icon = WicketUtils.newIcon("type", "icon-file");
+ WicketUtils.setHtmlTooltip(icon, "blob");
+ item.add(icon);
+ item.add(new LinkPanel("summary", null, sr.path, BlobPage.class, WicketUtils.newPathParameter(sr.repository, sr.branch, sr.path)));
+ item.add(new Label("tags").setVisible(false));
+ break;
+ }
+ case issue: {
+ Label icon = WicketUtils.newIcon("type", "icon-file");
+ WicketUtils.setHtmlTooltip(icon, "issue");
+ item.add(icon);
+ item.add(new Label("summary", "issue: " + sr.issueId));
+ item.add(new Label("tags").setVisible(false));
+ break;
+ }
+ }
+ item.add(new Label("fragment", sr.fragment).setEscapeModelStrings(false).setVisible(!StringUtils.isEmpty(sr.fragment)));
+ item.add(new LinkPanel("repository", null, sr.repository, SummaryPage.class, WicketUtils.newRepositoryParameter(sr.repository)));
+ if (StringUtils.isEmpty(sr.branch)) {
+ item.add(new Label("branch", "null"));
+ } else {
+ item.add(new LinkPanel("branch", "branch", StringUtils.getRelativePath(Constants.R_HEADS, sr.branch), LogPage.class, WicketUtils.newObjectParameter(sr.repository, sr.branch)));
+ }
+ item.add(new Label("author", sr.author));
+ item.add(WicketUtils.createDatestampLabel("date", sr.date, getTimeZone(), getTimeUtils()));
+ }
+ };
+ add(resultsView.setVisible(results.size() > 0));
+
+ PageParameters pagerParams = new PageParameters();
+ pagerParams.put("repositories", StringUtils.flattenStrings(repositoriesModel.getObject()));
+ pagerParams.put("query", queryModel.getObject());
+
+ boolean showPager = false;
+ int totalPages = 0;
+ if (results.size() > 0) {
+ totalPages = (results.get(0).totalHits / pageSize) + (results.get(0).totalHits % pageSize > 0 ? 1 : 0);
+ showPager = results.get(0).totalHits > pageSize;
+ }
+
+ add(new PagerPanel("topPager", page, totalPages, LuceneSearchPage.class, pagerParams).setVisible(showPager));
+ add(new PagerPanel("bottomPager", page, totalPages, LuceneSearchPage.class, pagerParams).setVisible(showPager));
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/MarkdownPage.html b/src/main/java/com/gitblit/wicket/pages/MarkdownPage.html
new file mode 100644
index 00000000..7900625b
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/MarkdownPage.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+ <!-- markdown nav links -->
+ <div class="page_nav2">
+ <a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a> | <a wicket:id="headLink"><wicket:message key="gb.head"></wicket:message></a>
+ </div>
+
+ <!-- markdown content -->
+ <div class="markdown" style="padding-bottom:5px;" wicket:id="markdownText">[markdown content]</div>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/MarkdownPage.java b/src/main/java/com/gitblit/wicket/pages/MarkdownPage.java
new file mode 100644
index 00000000..e032cbf9
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/MarkdownPage.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+import java.text.ParseException;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import com.gitblit.GitBlit;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.MarkdownUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.WicketUtils;
+
+public class MarkdownPage extends RepositoryPage {
+
+ public MarkdownPage(PageParameters params) {
+ super(params);
+
+ final String markdownPath = WicketUtils.getPath(params);
+
+ Repository r = getRepository();
+ RevCommit commit = JGitUtils.getCommit(r, objectId);
+ String [] encodings = GitBlit.getEncodings();
+
+ // markdown page links
+ add(new BookmarkablePageLink<Void>("blameLink", BlamePage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, markdownPath)));
+ add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, markdownPath)));
+ add(new BookmarkablePageLink<Void>("rawLink", RawPage.class, WicketUtils.newPathParameter(
+ repositoryName, objectId, markdownPath)));
+ add(new BookmarkablePageLink<Void>("headLink", MarkdownPage.class,
+ WicketUtils.newPathParameter(repositoryName, Constants.HEAD, markdownPath)));
+
+ // Read raw markdown content and transform it to html
+ String markdownText = JGitUtils.getStringContent(r, commit.getTree(), markdownPath, encodings);
+ String htmlText;
+ try {
+ htmlText = MarkdownUtils.transformMarkdown(markdownText);
+ } catch (ParseException p) {
+ markdownText = MessageFormat.format("<div class=\"alert alert-error\"><strong>{0}:</strong> {1}</div>{2}", getString("gb.error"), getString("gb.markdownFailure"), markdownText);
+ htmlText = StringUtils.breakLinesForHtml(markdownText);
+ }
+
+ // Add the html to the page
+ add(new Label("markdownText", htmlText).setEscapeModelStrings(false));
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.markdown");
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/MetricsPage.html b/src/main/java/com/gitblit/wicket/pages/MetricsPage.html
new file mode 100644
index 00000000..4aefc798
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/MetricsPage.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+<div style="padding-top:10px;">
+ <!-- branch name -->
+ <div><span class="metricsTitle" wicket:id="branchTitle"></span></div>
+
+ <table style="width:100%;">
+ <tr>
+ <!-- branch stats -->
+ <td colspan=2>
+ <h2><wicket:message key="gb.stats"></wicket:message></h2>
+ <span wicket:id="branchStats"></span>
+ </td>
+ </tr>
+ <tr>
+ <!-- commit activity trend -->
+ <td>
+ <h2><wicket:message key="gb.commitActivityTrend"></wicket:message></h2>
+ <div><img wicket:id="commitsChart" /></div>
+ </td>
+ <!-- commit activity by day of week -->
+ <td>
+ <h2><wicket:message key="gb.commitActivityDOW"></wicket:message></h2>
+ <div><img wicket:id="dayOfWeekChart" /></div>
+ </td>
+ </tr>
+ <tr>
+ <!-- commit activity by primary authors -->
+ <td colspan=2>
+ <h2><wicket:message key="gb.commitActivityAuthors"></wicket:message></h2>
+ <div style="text-align: center;"><img wicket:id="authorsChart" /></div>
+ </td>
+ </tr>
+ </table>
+</div>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/MetricsPage.java b/src/main/java/com/gitblit/wicket/pages/MetricsPage.java
new file mode 100644
index 00000000..5904a64a
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/MetricsPage.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.eclipse.jgit.lib.Repository;
+import org.wicketstuff.googlecharts.Chart;
+import org.wicketstuff.googlecharts.ChartAxis;
+import org.wicketstuff.googlecharts.ChartAxisType;
+import org.wicketstuff.googlecharts.ChartProvider;
+import org.wicketstuff.googlecharts.ChartType;
+import org.wicketstuff.googlecharts.IChartData;
+import org.wicketstuff.googlecharts.LineStyle;
+import org.wicketstuff.googlecharts.MarkerType;
+import org.wicketstuff.googlecharts.ShapeMarker;
+
+import com.gitblit.models.Metric;
+import com.gitblit.utils.MetricUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.WicketUtils;
+
+public class MetricsPage extends RepositoryPage {
+
+ public MetricsPage(PageParameters params) {
+ super(params);
+ Repository r = getRepository();
+ if (StringUtils.isEmpty(objectId)) {
+ add(new Label("branchTitle", getRepositoryModel().HEAD));
+ } else {
+ add(new Label("branchTitle", objectId));
+ }
+ Metric metricsTotal = null;
+ List<Metric> metrics = MetricUtils.getDateMetrics(r, objectId, true, null, getTimeZone());
+ metricsTotal = metrics.remove(0);
+ if (metricsTotal == null) {
+ add(new Label("branchStats", ""));
+ } else {
+ add(new Label("branchStats",
+ MessageFormat.format(getString("gb.branchStats"), metricsTotal.count,
+ metricsTotal.tag, getTimeUtils().duration(metricsTotal.duration))));
+ }
+ insertLinePlot("commitsChart", metrics);
+ insertBarPlot("dayOfWeekChart", getDayOfWeekMetrics(r, objectId));
+ insertPieChart("authorsChart", getAuthorMetrics(r, objectId));
+ }
+
+ private void insertLinePlot(String wicketId, List<Metric> metrics) {
+ if ((metrics != null) && (metrics.size() > 0)) {
+ IChartData data = WicketUtils.getChartData(metrics);
+
+ ChartProvider provider = new ChartProvider(new Dimension(400, 100), ChartType.LINE,
+ data);
+ ChartAxis dateAxis = new ChartAxis(ChartAxisType.BOTTOM);
+ dateAxis.setLabels(new String[] { metrics.get(0).name,
+ metrics.get(metrics.size() / 2).name, metrics.get(metrics.size() - 1).name });
+ provider.addAxis(dateAxis);
+
+ ChartAxis commitAxis = new ChartAxis(ChartAxisType.LEFT);
+ commitAxis.setLabels(new String[] { "",
+ String.valueOf((int) WicketUtils.maxValue(metrics)) });
+ provider.addAxis(commitAxis);
+
+ provider.setLineStyles(new LineStyle[] { new LineStyle(2, 4, 0), new LineStyle(0, 4, 1) });
+ provider.addShapeMarker(new ShapeMarker(MarkerType.CIRCLE, Color.BLUE, 1, -1, 5));
+
+ add(new Chart(wicketId, provider));
+ } else {
+ add(WicketUtils.newBlankImage(wicketId));
+ }
+ }
+
+ private void insertBarPlot(String wicketId, List<Metric> metrics) {
+ if ((metrics != null) && (metrics.size() > 0)) {
+ IChartData data = WicketUtils.getChartData(metrics);
+
+ ChartProvider provider = new ChartProvider(new Dimension(400, 100),
+ ChartType.BAR_VERTICAL_SET, data);
+ ChartAxis dateAxis = new ChartAxis(ChartAxisType.BOTTOM);
+ List<String> labels = new ArrayList<String>();
+ for (Metric metric : metrics) {
+ labels.add(metric.name);
+ }
+ dateAxis.setLabels(labels.toArray(new String[labels.size()]));
+ provider.addAxis(dateAxis);
+
+ ChartAxis commitAxis = new ChartAxis(ChartAxisType.LEFT);
+ commitAxis.setLabels(new String[] { "",
+ String.valueOf((int) WicketUtils.maxValue(metrics)) });
+ provider.addAxis(commitAxis);
+
+ add(new Chart(wicketId, provider));
+ } else {
+ add(WicketUtils.newBlankImage(wicketId));
+ }
+ }
+
+ private void insertPieChart(String wicketId, List<Metric> metrics) {
+ if ((metrics != null) && (metrics.size() > 0)) {
+ IChartData data = WicketUtils.getChartData(metrics);
+ List<String> labels = new ArrayList<String>();
+ for (Metric metric : metrics) {
+ labels.add(metric.name);
+ }
+ ChartProvider provider = new ChartProvider(new Dimension(800, 200), ChartType.PIE, data);
+ provider.setPieLabels(labels.toArray(new String[labels.size()]));
+ add(new Chart(wicketId, provider));
+ } else {
+ add(WicketUtils.newBlankImage(wicketId));
+ }
+ }
+
+ private List<Metric> getDayOfWeekMetrics(Repository repository, String objectId) {
+ List<Metric> list = MetricUtils.getDateMetrics(repository, objectId, false, "E", getTimeZone());
+ SimpleDateFormat sdf = new SimpleDateFormat("E");
+ Calendar cal = Calendar.getInstance();
+
+ List<Metric> sorted = new ArrayList<Metric>();
+ int firstDayOfWeek = cal.getFirstDayOfWeek();
+ int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
+
+ // rewind date to first day of week
+ cal.add(Calendar.DATE, firstDayOfWeek - dayOfWeek);
+ for (int i = 0; i < 7; i++) {
+ String day = sdf.format(cal.getTime());
+ for (Metric metric : list) {
+ if (metric.name.equals(day)) {
+ sorted.add(metric);
+ list.remove(metric);
+ break;
+ }
+ }
+ cal.add(Calendar.DATE, 1);
+ }
+ return sorted;
+ }
+
+ private List<Metric> getAuthorMetrics(Repository repository, String objectId) {
+ List<Metric> authors = MetricUtils.getAuthorMetrics(repository, objectId, true);
+ Collections.sort(authors, new Comparator<Metric>() {
+ @Override
+ public int compare(Metric o1, Metric o2) {
+ if (o1.count > o2.count) {
+ return -1;
+ } else if (o1.count < o2.count) {
+ return 1;
+ }
+ return 0;
+ }
+ });
+ if (authors.size() > 10) {
+ return authors.subList(0, 9);
+ }
+ return authors;
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.metrics");
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/PatchPage.html b/src/main/java/com/gitblit/wicket/pages/PatchPage.html
new file mode 100644
index 00000000..719a46d1
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/PatchPage.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+
+ <!-- patch content -->
+ <pre style="border:0px;" wicket:id="patchText">[patch content]</pre>
+
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/PatchPage.java b/src/main/java/com/gitblit/wicket/pages/PatchPage.java
new file mode 100644
index 00000000..878cfb45
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/PatchPage.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.basic.Label;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import com.gitblit.GitBlit;
+import com.gitblit.utils.DiffUtils;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.WicketUtils;
+
+public class PatchPage extends WebPage {
+
+ public PatchPage(PageParameters params) {
+ super(params);
+
+ if (!params.containsKey("r")) {
+ GitBlitWebSession.get().cacheErrorMessage(getString("gb.repositoryNotSpecified"));
+ redirectToInterceptPage(new RepositoriesPage());
+ return;
+ }
+
+ final String repositoryName = WicketUtils.getRepositoryName(params);
+ final String baseObjectId = WicketUtils.getBaseObjectId(params);
+ final String objectId = WicketUtils.getObject(params);
+ final String blobPath = WicketUtils.getPath(params);
+
+ Repository r = GitBlit.self().getRepository(repositoryName);
+ if (r == null) {
+ GitBlitWebSession.get().cacheErrorMessage(getString("gb.canNotLoadRepository") + " " + repositoryName);
+ redirectToInterceptPage(new RepositoriesPage());
+ return;
+ }
+
+ RevCommit commit = JGitUtils.getCommit(r, objectId);
+ if (commit == null) {
+ GitBlitWebSession.get().cacheErrorMessage(getString("gb.commitIsNull"));
+ redirectToInterceptPage(new RepositoriesPage());
+ return;
+ }
+
+ RevCommit baseCommit = null;
+ if (!StringUtils.isEmpty(baseObjectId)) {
+ baseCommit = JGitUtils.getCommit(r, baseObjectId);
+ }
+ String patch = DiffUtils.getCommitPatch(r, baseCommit, commit, blobPath);
+ add(new Label("patchText", patch));
+ r.close();
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/ProjectPage.html b/src/main/java/com/gitblit/wicket/pages/ProjectPage.html
new file mode 100644
index 00000000..3e73ba52
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/ProjectPage.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+
+ <div class="row">
+ <div class="span12">
+ <h2><span wicket:id="projectTitle"></span> <small><span wicket:id="projectDescription"></span></small>
+ <a class="hidden-phone hidden-tablet brand" style="text-decoration: none;" wicket:id="syndication" wicket:message="title:gb.feed">
+ <img style="border:0px;vertical-align:middle;" src="feed_16x16.png"></img>
+ </a>
+ </h2>
+ <div class="markdown" wicket:id="projectMessage">[project message]</div>
+ </div>
+ </div>
+
+ <div class="tabbable">
+ <!-- tab titles -->
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="#repositories" data-toggle="tab"><wicket:message key="gb.repositories"></wicket:message></a></li>
+ <li ><a href="#activity" data-toggle="tab"><wicket:message key="gb.activity"></wicket:message></a></li>
+ </ul>
+
+ <!-- tab content -->
+ <div class="tab-content">
+
+ <!-- repositories tab -->
+ <div class="tab-pane active" id="repositories">
+ <!-- markdown -->
+ <div class="row">
+ <div class="span12">
+ <div class="markdown" wicket:id="repositoriesMessage">[repositories message]</div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="span6" style="border-bottom:1px solid #eee;" wicket:id="repositoryList">
+ <span wicket:id="repository"></span>
+ </div>
+ </div>
+ </div>
+
+ <!-- activity tab -->
+ <div class="tab-pane" id="activity">
+ <div class="pageTitle">
+ <h2><wicket:message key="gb.recentActivity"></wicket:message><small> <span class="hidden-phone">/ <span wicket:id="subheader">[days back]</span></span></small></h2>
+ </div>
+
+ <div class="hidden-phone" style="height: 155px;text-align: center;">
+ <table>
+ <tr>
+ <td><span class="hidden-tablet" id="chartDaily"></span></td>
+ <td><span id="chartRepositories"></span></td>
+ <td><span id="chartAuthors"></span></td>
+ </tr>
+ </table>
+ </div>
+
+ <div wicket:id="activityPanel">[activity panel]</div>
+ </div>
+
+ </div>
+ </div>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/ProjectPage.java b/src/main/java/com/gitblit/wicket/pages/ProjectPage.java
new file mode 100644
index 00000000..7eba0331
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/ProjectPage.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2012 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.behavior.HeaderContributor;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.ExternalLink;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.SyndicationServlet;
+import com.gitblit.models.Activity;
+import com.gitblit.models.Metric;
+import com.gitblit.models.ProjectModel;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.utils.ActivityUtils;
+import com.gitblit.utils.MarkdownUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebApp;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.GitblitRedirectException;
+import com.gitblit.wicket.PageRegistration;
+import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
+import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.charting.GoogleChart;
+import com.gitblit.wicket.charting.GoogleCharts;
+import com.gitblit.wicket.charting.GoogleLineChart;
+import com.gitblit.wicket.charting.GooglePieChart;
+import com.gitblit.wicket.panels.ActivityPanel;
+import com.gitblit.wicket.panels.ProjectRepositoryPanel;
+
+public class ProjectPage extends RootPage {
+
+ List<ProjectModel> projectModels = new ArrayList<ProjectModel>();
+
+ public ProjectPage() {
+ super();
+ throw new GitblitRedirectException(GitBlitWebApp.get().getHomePage());
+ }
+
+ public ProjectPage(PageParameters params) {
+ super(params);
+ setup(params);
+ }
+
+ @Override
+ protected boolean reusePageParameters() {
+ return true;
+ }
+
+ private void setup(PageParameters params) {
+ setupPage("", "");
+ // check to see if we should display a login message
+ boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, true);
+ if (authenticateView && !GitBlitWebSession.get().isLoggedIn()) {
+ authenticationError("Please login");
+ return;
+ }
+
+ String projectName = WicketUtils.getProjectName(params);
+ if (StringUtils.isEmpty(projectName)) {
+ throw new GitblitRedirectException(GitBlitWebApp.get().getHomePage());
+ }
+
+ ProjectModel project = getProjectModel(projectName);
+ if (project == null) {
+ throw new GitblitRedirectException(GitBlitWebApp.get().getHomePage());
+ }
+
+ add(new Label("projectTitle", project.getDisplayName()));
+ add(new Label("projectDescription", project.description));
+
+ String feedLink = SyndicationServlet.asLink(getRequest().getRelativePathPrefixToContextRoot(), projectName, null, 0);
+ add(new ExternalLink("syndication", feedLink));
+
+ add(WicketUtils.syndicationDiscoveryLink(SyndicationServlet.getTitle(project.getDisplayName(),
+ null), feedLink));
+
+ // project markdown message
+ String pmessage = transformMarkdown(project.projectMarkdown);
+ Component projectMessage = new Label("projectMessage", pmessage)
+ .setEscapeModelStrings(false).setVisible(pmessage.length() > 0);
+ add(projectMessage);
+
+ // markdown message above repositories list
+ String rmessage = transformMarkdown(project.repositoriesMarkdown);
+ Component repositoriesMessage = new Label("repositoriesMessage", rmessage)
+ .setEscapeModelStrings(false).setVisible(rmessage.length() > 0);
+ add(repositoriesMessage);
+
+ List<RepositoryModel> repositories = getRepositories(params);
+
+ Collections.sort(repositories, new Comparator<RepositoryModel>() {
+ @Override
+ public int compare(RepositoryModel o1, RepositoryModel o2) {
+ // reverse-chronological sort
+ return o2.lastChange.compareTo(o1.lastChange);
+ }
+ });
+
+ final ListDataProvider<RepositoryModel> dp = new ListDataProvider<RepositoryModel>(repositories);
+ DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("repositoryList", dp) {
+ private static final long serialVersionUID = 1L;
+
+ public void populateItem(final Item<RepositoryModel> item) {
+ final RepositoryModel entry = item.getModelObject();
+
+ ProjectRepositoryPanel row = new ProjectRepositoryPanel("repository",
+ getLocalizer(), this, showAdmin, entry, getAccessRestrictions());
+ item.add(row);
+ }
+ };
+ add(dataView);
+
+ // project activity
+ // parameters
+ int daysBack = WicketUtils.getDaysBack(params);
+ if (daysBack < 1) {
+ daysBack = 14;
+ }
+ String objectId = WicketUtils.getObject(params);
+
+ List<Activity> recentActivity = ActivityUtils.getRecentActivity(repositories,
+ daysBack, objectId, getTimeZone());
+ if (recentActivity.size() == 0) {
+ // no activity, skip graphs and activity panel
+ add(new Label("subheader", MessageFormat.format(getString("gb.recentActivityNone"),
+ daysBack)));
+ add(new Label("activityPanel"));
+ } else {
+ // calculate total commits and total authors
+ int totalCommits = 0;
+ Set<String> uniqueAuthors = new HashSet<String>();
+ for (Activity activity : recentActivity) {
+ totalCommits += activity.getCommitCount();
+ uniqueAuthors.addAll(activity.getAuthorMetrics().keySet());
+ }
+ int totalAuthors = uniqueAuthors.size();
+
+ // add the subheader with stat numbers
+ add(new Label("subheader", MessageFormat.format(getString("gb.recentActivityStats"),
+ daysBack, totalCommits, totalAuthors)));
+
+ // create the activity charts
+ GoogleCharts charts = createCharts(recentActivity);
+ add(new HeaderContributor(charts));
+
+ // add activity panel
+ add(new ActivityPanel("activityPanel", recentActivity));
+ }
+ }
+
+ /**
+ * Creates the daily activity line chart, the active repositories pie chart,
+ * and the active authors pie chart
+ *
+ * @param recentActivity
+ * @return
+ */
+ private GoogleCharts createCharts(List<Activity> recentActivity) {
+ // activity metrics
+ Map<String, Metric> repositoryMetrics = new HashMap<String, Metric>();
+ Map<String, Metric> authorMetrics = new HashMap<String, Metric>();
+
+ // aggregate repository and author metrics
+ for (Activity activity : recentActivity) {
+
+ // aggregate author metrics
+ for (Map.Entry<String, Metric> entry : activity.getAuthorMetrics().entrySet()) {
+ String author = entry.getKey();
+ if (!authorMetrics.containsKey(author)) {
+ authorMetrics.put(author, new Metric(author));
+ }
+ authorMetrics.get(author).count += entry.getValue().count;
+ }
+
+ // aggregate repository metrics
+ for (Map.Entry<String, Metric> entry : activity.getRepositoryMetrics().entrySet()) {
+ String repository = StringUtils.stripDotGit(entry.getKey());
+ if (!repositoryMetrics.containsKey(repository)) {
+ repositoryMetrics.put(repository, new Metric(repository));
+ }
+ repositoryMetrics.get(repository).count += entry.getValue().count;
+ }
+ }
+
+ // build google charts
+ int w = 310;
+ int h = 150;
+ GoogleCharts charts = new GoogleCharts();
+
+ // sort in reverse-chronological order and then reverse that
+ Collections.sort(recentActivity);
+ Collections.reverse(recentActivity);
+
+ // daily line chart
+ GoogleChart chart = new GoogleLineChart("chartDaily", getString("gb.dailyActivity"), "day",
+ getString("gb.commits"));
+ SimpleDateFormat df = new SimpleDateFormat("MMM dd");
+ df.setTimeZone(getTimeZone());
+ for (Activity metric : recentActivity) {
+ chart.addValue(df.format(metric.startDate), metric.getCommitCount());
+ }
+ chart.setWidth(w);
+ chart.setHeight(h);
+ charts.addChart(chart);
+
+ // active repositories pie chart
+ chart = new GooglePieChart("chartRepositories", getString("gb.activeRepositories"),
+ getString("gb.repository"), getString("gb.commits"));
+ for (Metric metric : repositoryMetrics.values()) {
+ chart.addValue(metric.name, metric.count);
+ }
+ chart.setWidth(w);
+ chart.setHeight(h);
+ charts.addChart(chart);
+
+ // active authors pie chart
+ chart = new GooglePieChart("chartAuthors", getString("gb.activeAuthors"),
+ getString("gb.author"), getString("gb.commits"));
+ for (Metric metric : authorMetrics.values()) {
+ chart.addValue(metric.name, metric.count);
+ }
+ chart.setWidth(w);
+ chart.setHeight(h);
+ charts.addChart(chart);
+
+ return charts;
+ }
+
+ @Override
+ protected void addDropDownMenus(List<PageRegistration> pages) {
+ PageParameters params = getPageParameters();
+
+ DropDownMenuRegistration projects = new DropDownMenuRegistration("gb.projects",
+ ProjectPage.class);
+ projects.menuItems.addAll(getProjectsMenu());
+ pages.add(0, projects);
+
+ DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",
+ ProjectPage.class);
+ // preserve time filter option on repository choices
+ menu.menuItems.addAll(getRepositoryFilterItems(params));
+
+ // preserve repository filter option on time choices
+ menu.menuItems.addAll(getTimeFilterItems(params));
+
+ if (menu.menuItems.size() > 0) {
+ // Reset Filter
+ menu.menuItems.add(new DropDownMenuItem(getString("gb.reset"), null, null));
+ }
+
+ pages.add(menu);
+ }
+
+ @Override
+ protected List<ProjectModel> getProjectModels() {
+ if (projectModels.isEmpty()) {
+ List<RepositoryModel> repositories = getRepositoryModels();
+ List<ProjectModel> projects = GitBlit.self().getProjectModels(repositories, false);
+ projectModels.addAll(projects);
+ }
+ return projectModels;
+ }
+
+ private ProjectModel getProjectModel(String name) {
+ for (ProjectModel project : getProjectModels()) {
+ if (name.equalsIgnoreCase(project.name)) {
+ return project;
+ }
+ }
+ return null;
+ }
+
+ protected List<DropDownMenuItem> getProjectsMenu() {
+ List<DropDownMenuItem> menu = new ArrayList<DropDownMenuItem>();
+ List<ProjectModel> projects = new ArrayList<ProjectModel>();
+ for (ProjectModel model : getProjectModels()) {
+ if (!model.isUserProject()) {
+ projects.add(model);
+ }
+ }
+ int maxProjects = 15;
+ boolean showAllProjects = projects.size() > maxProjects;
+ if (showAllProjects) {
+
+ // sort by last changed
+ Collections.sort(projects, new Comparator<ProjectModel>() {
+ @Override
+ public int compare(ProjectModel o1, ProjectModel o2) {
+ return o2.lastChange.compareTo(o1.lastChange);
+ }
+ });
+
+ // take most recent subset
+ projects = projects.subList(0, maxProjects);
+
+ // sort those by name
+ Collections.sort(projects);
+ }
+
+ for (ProjectModel project : projects) {
+ menu.add(new DropDownMenuItem(project.getDisplayName(), "p", project.name));
+ }
+ if (showAllProjects) {
+ menu.add(new DropDownMenuItem());
+ menu.add(new DropDownMenuItem("all projects", null, null));
+ }
+ return menu;
+ }
+
+ private String transformMarkdown(String markdown) {
+ String message = "";
+ if (!StringUtils.isEmpty(markdown)) {
+ // Read user-supplied message
+ try {
+ message = MarkdownUtils.transformMarkdown(markdown);
+ } catch (Throwable t) {
+ message = getString("gb.failedToRead") + " " + markdown;
+ warn(message, t);
+ }
+ }
+ return message;
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/ProjectsPage.html b/src/main/java/com/gitblit/wicket/pages/ProjectsPage.html
new file mode 100644
index 00000000..528ed48f
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/ProjectsPage.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+ <div class="markdown" style="padding-bottom:5px;" wicket:id="projectsMessage">[projects message]</div>
+
+ <table class="repositories">
+ <thead>
+ <tr>
+ <th class="left">
+ <i class="icon-folder-close" ></i>
+ <wicket:message key="gb.project">Project</wicket:message>
+ </th>
+ <th class="hidden-phone" ><span><wicket:message key="gb.description">Description</wicket:message></span></th>
+ <th class="hidden-phone"><wicket:message key="gb.repositories">Repositories</wicket:message></th>
+ <th><wicket:message key="gb.lastChange">Last Change</wicket:message></th>
+ <th class="right"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr wicket:id="project">
+ <td class="left" style="padding-left:3px;" ><span style="padding-left:3px;" wicket:id="projectTitle">[project title]</span></td>
+ <td class="hidden-phone"><span class="list" wicket:id="projectDescription">[project description]</span></td>
+ <td class="hidden-phone" style="padding-right:15px;"><span style="font-size:0.8em;" wicket:id="repositoryCount">[repository count]</span></td>
+ <td><span wicket:id="projectLastChange">[last change]</span></td>
+ <td class="rightAlign"></td>
+ </tr>
+ </tbody>
+ </table>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/ProjectsPage.java b/src/main/java/com/gitblit/wicket/pages/ProjectsPage.java
new file mode 100644
index 00000000..7f0b002e
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/ProjectsPage.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2012 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+import org.apache.wicket.resource.ContextRelativeResource;
+import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
+import org.eclipse.jgit.lib.Constants;
+
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.ProjectModel;
+import com.gitblit.utils.MarkdownUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.PageRegistration;
+import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
+import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.LinkPanel;
+
+public class ProjectsPage extends RootPage {
+
+ public ProjectsPage() {
+ super();
+ setup(null);
+ }
+
+ public ProjectsPage(PageParameters params) {
+ super(params);
+ setup(params);
+ }
+
+ @Override
+ protected boolean reusePageParameters() {
+ return true;
+ }
+
+ @Override
+ protected List<ProjectModel> getProjectModels() {
+ return GitBlit.self().getProjectModels(getRepositoryModels(), false);
+ }
+
+ private void setup(PageParameters params) {
+ setupPage("", "");
+ // check to see if we should display a login message
+ boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, true);
+ if (authenticateView && !GitBlitWebSession.get().isLoggedIn()) {
+ String messageSource = GitBlit.getString(Keys.web.loginMessage, "gitblit");
+ String message = readMarkdown(messageSource, "login.mkd");
+ Component repositoriesMessage = new Label("projectsMessage", message);
+ add(repositoriesMessage.setEscapeModelStrings(false));
+ add(new Label("projectsPanel"));
+ return;
+ }
+
+ // Load the markdown welcome message
+ String messageSource = GitBlit.getString(Keys.web.repositoriesMessage, "gitblit");
+ String message = readMarkdown(messageSource, "welcome.mkd");
+ Component projectsMessage = new Label("projectsMessage", message).setEscapeModelStrings(
+ false).setVisible(message.length() > 0);
+ add(projectsMessage);
+
+ List<ProjectModel> projects = getProjects(params);
+
+ ListDataProvider<ProjectModel> dp = new ListDataProvider<ProjectModel>(projects);
+
+ DataView<ProjectModel> dataView = new DataView<ProjectModel>("project", dp) {
+ private static final long serialVersionUID = 1L;
+ int counter;
+
+ @Override
+ protected void onBeforeRender() {
+ super.onBeforeRender();
+ counter = 0;
+ }
+
+ public void populateItem(final Item<ProjectModel> item) {
+ final ProjectModel entry = item.getModelObject();
+
+ PageParameters pp = WicketUtils.newProjectParameter(entry.name);
+ item.add(new LinkPanel("projectTitle", "list", entry.getDisplayName(),
+ ProjectPage.class, pp));
+ item.add(new LinkPanel("projectDescription", "list", entry.description,
+ ProjectPage.class, pp));
+
+ item.add(new Label("repositoryCount", entry.repositories.size()
+ + " "
+ + (entry.repositories.size() == 1 ? getString("gb.repository")
+ : getString("gb.repositories"))));
+
+ String lastChange;
+ if (entry.lastChange.getTime() == 0) {
+ lastChange = "--";
+ } else {
+ lastChange = getTimeUtils().timeAgo(entry.lastChange);
+ }
+ Label lastChangeLabel = new Label("projectLastChange", lastChange);
+ item.add(lastChangeLabel);
+ WicketUtils.setCssClass(lastChangeLabel, getTimeUtils()
+ .timeAgoCss(entry.lastChange));
+ WicketUtils.setAlternatingBackground(item, counter);
+ counter++;
+ }
+ };
+ add(dataView);
+
+ // push the panel down if we are hiding the admin controls and the
+ // welcome message
+ if (!showAdmin && !projectsMessage.isVisible()) {
+ WicketUtils.setCssStyle(dataView, "padding-top:5px;");
+ }
+ }
+
+ @Override
+ protected void addDropDownMenus(List<PageRegistration> pages) {
+ PageParameters params = getPageParameters();
+
+ pages.add(0, new PageRegistration("gb.projects", ProjectsPage.class, params));
+
+ DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",
+ ProjectsPage.class);
+ // preserve time filter option on repository choices
+ menu.menuItems.addAll(getRepositoryFilterItems(params));
+
+ // preserve repository filter option on time choices
+ menu.menuItems.addAll(getTimeFilterItems(params));
+
+ if (menu.menuItems.size() > 0) {
+ // Reset Filter
+ menu.menuItems.add(new DropDownMenuItem(getString("gb.reset"), null, null));
+ }
+
+ pages.add(menu);
+ }
+
+ private String readMarkdown(String messageSource, String resource) {
+ String message = "";
+ if (messageSource.equalsIgnoreCase("gitblit")) {
+ // Read default message
+ message = readDefaultMarkdown(resource);
+ } else {
+ // Read user-supplied message
+ if (!StringUtils.isEmpty(messageSource)) {
+ File file = new File(messageSource);
+ if (file.exists()) {
+ try {
+ FileInputStream fis = new FileInputStream(file);
+ InputStreamReader reader = new InputStreamReader(fis,
+ Constants.CHARACTER_ENCODING);
+ message = MarkdownUtils.transformMarkdown(reader);
+ reader.close();
+ } catch (Throwable t) {
+ message = getString("gb.failedToRead") + " " + file;
+ warn(message, t);
+ }
+ } else {
+ message = messageSource + " " + getString("gb.isNotValidFile");
+ }
+ }
+ }
+ return message;
+ }
+
+ private String readDefaultMarkdown(String file) {
+ String base = file.substring(0, file.lastIndexOf('.'));
+ String ext = file.substring(file.lastIndexOf('.'));
+ String lc = getLanguageCode();
+ String cc = getCountryCode();
+
+ // try to read file_en-us.ext, file_en.ext, file.ext
+ List<String> files = new ArrayList<String>();
+ if (!StringUtils.isEmpty(lc)) {
+ if (!StringUtils.isEmpty(cc)) {
+ files.add(base + "_" + lc + "-" + cc + ext);
+ files.add(base + "_" + lc + "_" + cc + ext);
+ }
+ files.add(base + "_" + lc + ext);
+ }
+ files.add(file);
+
+ for (String name : files) {
+ String message;
+ InputStreamReader reader = null;
+ try {
+ ContextRelativeResource res = WicketUtils.getResource(name);
+ InputStream is = res.getResourceStream().getInputStream();
+ reader = new InputStreamReader(is, Constants.CHARACTER_ENCODING);
+ message = MarkdownUtils.transformMarkdown(reader);
+ reader.close();
+ return message;
+ } catch (ResourceStreamNotFoundException t) {
+ continue;
+ } catch (Throwable t) {
+ message = MessageFormat.format(getString("gb.failedToReadMessage"), file);
+ error(message, t, false);
+ return message;
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (Exception e) {
+ }
+ }
+ }
+ }
+ return MessageFormat.format(getString("gb.failedToReadMessage"), file);
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/RawPage.java b/src/main/java/com/gitblit/wicket/pages/RawPage.java
new file mode 100644
index 00000000..28e8bae2
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/RawPage.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.wicket.IRequestTarget;
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.RequestCycle;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.protocol.http.WebResponse;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.WicketUtils;
+
+public class RawPage extends WebPage {
+
+ private final Logger logger = LoggerFactory.getLogger(getClass().getSimpleName());
+
+ public RawPage(final PageParameters params) {
+ super(params);
+
+ if (!params.containsKey("r")) {
+ error(getString("gb.repositoryNotSpecified"));
+ redirectToInterceptPage(new RepositoriesPage());
+ }
+
+ getRequestCycle().setRequestTarget(new IRequestTarget() {
+ @Override
+ public void detach(RequestCycle requestCycle) {
+ }
+
+ @Override
+ public void respond(RequestCycle requestCycle) {
+ WebResponse response = (WebResponse) requestCycle.getResponse();
+
+ final String repositoryName = WicketUtils.getRepositoryName(params);
+ final String objectId = WicketUtils.getObject(params);
+ final String blobPath = WicketUtils.getPath(params);
+ String[] encodings = GitBlit.getEncodings();
+
+ Repository r = GitBlit.self().getRepository(repositoryName);
+ if (r == null) {
+ error(getString("gb.canNotLoadRepository") + " " + repositoryName);
+ redirectToInterceptPage(new RepositoriesPage());
+ return;
+ }
+
+ if (StringUtils.isEmpty(blobPath)) {
+ // objectid referenced raw view
+ byte [] binary = JGitUtils.getByteContent(r, objectId);
+ response.setContentType("application/octet-stream");
+ response.setContentLength(binary.length);
+ try {
+ response.getOutputStream().write(binary);
+ } catch (Exception e) {
+ logger.error("Failed to write binary response", e);
+ }
+ } else {
+ // standard raw blob view
+ RevCommit commit = JGitUtils.getCommit(r, objectId);
+
+ String filename = blobPath;
+ if (blobPath.indexOf('/') > -1) {
+ filename = blobPath.substring(blobPath.lastIndexOf('/') + 1);
+ }
+
+ String extension = null;
+ if (blobPath.lastIndexOf('.') > -1) {
+ extension = blobPath.substring(blobPath.lastIndexOf('.') + 1);
+ }
+
+ // Map the extensions to types
+ Map<String, Integer> map = new HashMap<String, Integer>();
+ for (String ext : GitBlit.getStrings(Keys.web.imageExtensions)) {
+ map.put(ext.toLowerCase(), 2);
+ }
+ for (String ext : GitBlit.getStrings(Keys.web.binaryExtensions)) {
+ map.put(ext.toLowerCase(), 3);
+ }
+
+ if (extension != null) {
+ int type = 0;
+ if (map.containsKey(extension)) {
+ type = map.get(extension);
+ }
+ switch (type) {
+ case 2:
+ // image blobs
+ byte[] image = JGitUtils.getByteContent(r, commit.getTree(), blobPath, true);
+ response.setContentType("image/" + extension.toLowerCase());
+ response.setContentLength(image.length);
+ try {
+ response.getOutputStream().write(image);
+ } catch (IOException e) {
+ logger.error("Failed to write image response", e);
+ }
+ break;
+ case 3:
+ // binary blobs (download)
+ byte[] binary = JGitUtils.getByteContent(r, commit.getTree(), blobPath, true);
+ response.setContentLength(binary.length);
+ response.setContentType("application/octet-stream");
+ response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
+ try {
+ response.getOutputStream().write(binary);
+ } catch (IOException e) {
+ logger.error("Failed to write binary response", e);
+ }
+ break;
+ default:
+ // plain text
+ String content = JGitUtils.getStringContent(r, commit.getTree(),
+ blobPath, encodings);
+ response.setContentType("text/plain; charset=UTF-8");
+ try {
+ response.getOutputStream().write(content.getBytes("UTF-8"));
+ } catch (Exception e) {
+ logger.error("Failed to write text response", e);
+ }
+ }
+
+ } else {
+ // plain text
+ String content = JGitUtils.getStringContent(r, commit.getTree(), blobPath,
+ encodings);
+ response.setContentType("text/plain; charset=UTF-8");
+ try {
+ response.getOutputStream().write(content.getBytes("UTF-8"));
+ } catch (Exception e) {
+ logger.error("Failed to write text response", e);
+ }
+ }
+ }
+ r.close();
+ }
+ });
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoriesPage.html b/src/main/java/com/gitblit/wicket/pages/RepositoriesPage.html
new file mode 100644
index 00000000..d2d27157
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/RepositoriesPage.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+ <div class="markdown" style="padding-bottom:5px;" wicket:id="repositoriesMessage">[repositories message]</div>
+
+ <div wicket:id="repositoriesPanel">[repositories panel]</div>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoriesPage.java b/src/main/java/com/gitblit/wicket/pages/RepositoriesPage.java
new file mode 100644
index 00000000..4bce77f5
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/RepositoriesPage.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.resource.ContextRelativeResource;
+import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
+import org.eclipse.jgit.lib.Constants;
+
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.utils.MarkdownUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.PageRegistration;
+import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
+import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.RepositoriesPanel;
+
+public class RepositoriesPage extends RootPage {
+
+ public RepositoriesPage() {
+ super();
+ setup(null);
+ }
+
+ public RepositoriesPage(PageParameters params) {
+ super(params);
+ setup(params);
+ }
+
+ @Override
+ protected boolean reusePageParameters() {
+ return true;
+ }
+
+ private void setup(PageParameters params) {
+ setupPage("", "");
+ // check to see if we should display a login message
+ boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, true);
+ if (authenticateView && !GitBlitWebSession.get().isLoggedIn()) {
+ String messageSource = GitBlit.getString(Keys.web.loginMessage, "gitblit");
+ String message = readMarkdown(messageSource, "login.mkd");
+ Component repositoriesMessage = new Label("repositoriesMessage", message);
+ add(repositoriesMessage.setEscapeModelStrings(false));
+ add(new Label("repositoriesPanel"));
+ return;
+ }
+
+ // Load the markdown welcome message
+ String messageSource = GitBlit.getString(Keys.web.repositoriesMessage, "gitblit");
+ String message = readMarkdown(messageSource, "welcome.mkd");
+ Component repositoriesMessage = new Label("repositoriesMessage", message)
+ .setEscapeModelStrings(false).setVisible(message.length() > 0);
+ add(repositoriesMessage);
+
+ List<RepositoryModel> repositories = getRepositories(params);
+
+ RepositoriesPanel repositoriesPanel = new RepositoriesPanel("repositoriesPanel", showAdmin,
+ true, repositories, true, getAccessRestrictions());
+ // push the panel down if we are hiding the admin controls and the
+ // welcome message
+ if (!showAdmin && !repositoriesMessage.isVisible()) {
+ WicketUtils.setCssStyle(repositoriesPanel, "padding-top:5px;");
+ }
+ add(repositoriesPanel);
+ }
+
+ @Override
+ protected void addDropDownMenus(List<PageRegistration> pages) {
+ PageParameters params = getPageParameters();
+
+ DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",
+ RepositoriesPage.class);
+ // preserve time filter option on repository choices
+ menu.menuItems.addAll(getRepositoryFilterItems(params));
+
+ // preserve repository filter option on time choices
+ menu.menuItems.addAll(getTimeFilterItems(params));
+
+ if (menu.menuItems.size() > 0) {
+ // Reset Filter
+ menu.menuItems.add(new DropDownMenuItem(getString("gb.reset"), null, null));
+ }
+
+ pages.add(menu);
+ }
+
+ private String readMarkdown(String messageSource, String resource) {
+ String message = "";
+ if (messageSource.equalsIgnoreCase("gitblit")) {
+ // Read default message
+ message = readDefaultMarkdown(resource);
+ } else {
+ // Read user-supplied message
+ if (!StringUtils.isEmpty(messageSource)) {
+ File file = GitBlit.getFileOrFolder(messageSource);
+ if (file.exists()) {
+ try {
+ FileInputStream fis = new FileInputStream(file);
+ InputStreamReader reader = new InputStreamReader(fis,
+ Constants.CHARACTER_ENCODING);
+ message = MarkdownUtils.transformMarkdown(reader);
+ reader.close();
+ } catch (Throwable t) {
+ message = getString("gb.failedToRead") + " " + file;
+ warn(message, t);
+ }
+ } else {
+ message = messageSource + " " + getString("gb.isNotValidFile");
+ }
+ }
+ }
+ return message;
+ }
+
+ private String readDefaultMarkdown(String file) {
+ String base = file.substring(0, file.lastIndexOf('.'));
+ String ext = file.substring(file.lastIndexOf('.'));
+ String lc = getLanguageCode();
+ String cc = getCountryCode();
+
+ // try to read file_en-us.ext, file_en.ext, file.ext
+ List<String> files = new ArrayList<String>();
+ if (!StringUtils.isEmpty(lc)) {
+ if (!StringUtils.isEmpty(cc)) {
+ files.add(base + "_" + lc + "-" + cc + ext);
+ files.add(base + "_" + lc + "_" + cc + ext);
+ }
+ files.add(base + "_" + lc + ext);
+ }
+ files.add(file);
+
+ for (String name : files) {
+ String message;
+ InputStreamReader reader = null;
+ try {
+ ContextRelativeResource res = WicketUtils.getResource(name);
+ InputStream is = res.getResourceStream().getInputStream();
+ reader = new InputStreamReader(is, Constants.CHARACTER_ENCODING);
+ message = MarkdownUtils.transformMarkdown(reader);
+ reader.close();
+ return message;
+ } catch (ResourceStreamNotFoundException t) {
+ continue;
+ } catch (Throwable t) {
+ message = MessageFormat.format(getString("gb.failedToReadMessage"), file);
+ error(message, t, false);
+ return message;
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (Exception e) {
+ }
+ }
+ }
+ }
+ return MessageFormat.format(getString("gb.failedToReadMessage"), file);
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.html b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.html
new file mode 100644
index 00000000..d49f0188
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+ <wicket:extend>
+ <!-- page nav links -->
+ <div class="navbar navbar-fixed-top">
+ <div class="navbar-inner">
+ <div class="container">
+ <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </a>
+ <a class="brand" wicket:id="rootLink">
+ <img src="gitblt_25_white.png" width="79" height="25" alt="gitblit" class="logo"/>
+ </a>
+
+ <div class="nav-collapse" wicket:id="navPanel"></div>
+
+ <a class="hidden-phone hidden-tablet brand" style="text-decoration: none;" wicket:id="syndication" wicket:message="title:gb.feed">
+ <img style="border:0px;vertical-align:middle;" src="feed_16x16.png"></img>
+ </a>
+
+ <form class="hidden-phone hidden-tablet pull-right" style="margin-top:10px;" wicket:id="searchForm">
+ <span class="search">
+ <select class="small" wicket:id="searchType"/>
+ <input type="text" id="searchBox" wicket:id="searchBox" value=""/>
+ </span>
+ </form>
+ </div>
+ </div>
+ </div>
+
+ <!-- page content -->
+ <div class="container">
+ <div style="text-align:center;" wicket:id="feedback">[Feedback Panel]</div>
+
+ <!-- page header -->
+ <div class="pageTitle">
+ <div class="row">
+ <div class="controls">
+ <span wicket:id="workingCopyIndicator"></span>
+ <span class="hidden-phone hidden-tablet" wicket:id="forksProhibitedIndicator"></span>
+ <div class="hidden-phone btn-group pull-right">
+ <!-- future spot for other repo buttons -->
+ <a class="btn btn-info" wicket:id="myForkLink"><img style="border:0px;vertical-align:middle;" src="fork_16x16.png"></img> <wicket:message key="gb.myFork"></wicket:message></a>
+ <a class="btn btn-info" wicket:id="forkLink"><img style="border:0px;vertical-align:middle;" src="fork_16x16.png"></img> <wicket:message key="gb.fork"></wicket:message></a>
+ </div>
+ </div>
+ <div class="span7">
+ <div><span class="project" wicket:id="projectTitle">[project title]</span>/<img wicket:id="repositoryIcon" style="padding-left: 10px;"></img><span class="repository" wicket:id="repositoryName">[repository name]</span> <span class="hidden-phone"><span wicket:id="pageName">[page name]</span></span></div>
+ <span wicket:id="originRepository">[origin repository]</span>
+ </div>
+ </div>
+ </div>
+
+ <wicket:child />
+ </div>
+
+ <wicket:fragment wicket:id="originFragment">
+ <p class="originRepository"><wicket:message key="gb.forkedFrom">[forked from]</wicket:message> <span wicket:id="originRepository">[origin repository]</span></p>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="workingCopyFragment">
+ <div class="pull-right" style="padding-top:0px;margin-bottom:0px;padding-left:5px">
+ <span class="alert alert-info" style="padding: 6px 14px 6px 14px;vertical-align: middle;"><i class="icon-exclamation-sign"></i>&nbsp;<span class="hidden-phone" wicket:id="workingCopy" style="font-weight:bold;">[working copy]</span></span>
+ </div>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="forksProhibitedFragment">
+ <div class="pull-right" style="padding-top:0px;margin-bottom:0px;padding-left:5px">
+ <span class="alert alert-error" style="padding: 6px 14px 6px 14px;vertical-align: middle;"><i class="icon-ban-circle"></i>&nbsp;<span class="hidden-phone" wicket:id="forksProhibited" style="font-weight:bold;">[forks prohibited]</span></span>
+ </div>
+ </wicket:fragment>
+
+ </wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
new file mode 100644
index 00000000..a477b741
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
@@ -0,0 +1,608 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.io.Serializable;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.markup.html.link.ExternalLink;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.protocol.http.RequestUtils;
+import org.apache.wicket.request.target.basic.RedirectRequestTarget;
+import org.eclipse.jgit.diff.DiffEntry.ChangeType;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import com.gitblit.Constants;
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.PagesServlet;
+import com.gitblit.SyndicationServlet;
+import com.gitblit.models.ProjectModel;
+import com.gitblit.models.RefModel;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.SubmoduleModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.utils.TicgitUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.PageRegistration;
+import com.gitblit.wicket.PageRegistration.OtherPageLink;
+import com.gitblit.wicket.SessionlessForm;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.LinkPanel;
+import com.gitblit.wicket.panels.NavigationPanel;
+import com.gitblit.wicket.panels.RefsPanel;
+
+public abstract class RepositoryPage extends BasePage {
+
+ protected final String projectName;
+ protected final String repositoryName;
+ protected final String objectId;
+
+ private transient Repository r;
+
+ private RepositoryModel m;
+
+ private Map<String, SubmoduleModel> submodules;
+
+ private final Map<String, PageRegistration> registeredPages;
+ private boolean showAdmin;
+ private boolean isOwner;
+
+ public RepositoryPage(PageParameters params) {
+ super(params);
+ repositoryName = WicketUtils.getRepositoryName(params);
+ String root =StringUtils.getFirstPathElement(repositoryName);
+ if (StringUtils.isEmpty(root)) {
+ projectName = GitBlit.getString(Keys.web.repositoryRootGroupName, "main");
+ } else {
+ projectName = root;
+ }
+ objectId = WicketUtils.getObject(params);
+
+ if (StringUtils.isEmpty(repositoryName)) {
+ error(MessageFormat.format(getString("gb.repositoryNotSpecifiedFor"), getPageName()), true);
+ }
+
+ if (!getRepositoryModel().hasCommits) {
+ setResponsePage(EmptyRepositoryPage.class, params);
+ }
+
+ if (getRepositoryModel().isCollectingGarbage) {
+ error(MessageFormat.format(getString("gb.busyCollectingGarbage"), getRepositoryModel().name), true);
+ }
+
+ if (objectId != null) {
+ RefModel branch = null;
+ if ((branch = JGitUtils.getBranch(getRepository(), objectId)) != null) {
+ UserModel user = GitBlitWebSession.get().getUser();
+ if (user == null) {
+ // workaround until get().getUser() is reviewed throughout the app
+ user = UserModel.ANONYMOUS;
+ }
+ boolean canAccess = user.canView(getRepositoryModel(),
+ branch.reference.getName());
+ if (!canAccess) {
+ error(getString("gb.accessDenied"), true);
+ }
+ }
+ }
+
+ // register the available page links for this page and user
+ registeredPages = registerPages();
+
+ // standard page links
+ List<PageRegistration> pages = new ArrayList<PageRegistration>(registeredPages.values());
+ NavigationPanel navigationPanel = new NavigationPanel("navPanel", getClass(), pages);
+ add(navigationPanel);
+
+ add(new ExternalLink("syndication", SyndicationServlet.asLink(getRequest()
+ .getRelativePathPrefixToContextRoot(), repositoryName, null, 0)));
+
+ // add floating search form
+ SearchForm searchForm = new SearchForm("searchForm", repositoryName);
+ add(searchForm);
+ searchForm.setTranslatedAttributes();
+
+ // set stateless page preference
+ setStatelessHint(true);
+ }
+
+ private Map<String, PageRegistration> registerPages() {
+ PageParameters params = null;
+ if (!StringUtils.isEmpty(repositoryName)) {
+ params = WicketUtils.newRepositoryParameter(repositoryName);
+ }
+ Map<String, PageRegistration> pages = new LinkedHashMap<String, PageRegistration>();
+
+ // standard links
+ pages.put("repositories", new PageRegistration("gb.repositories", RepositoriesPage.class));
+ pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));
+ pages.put("log", new PageRegistration("gb.log", LogPage.class, params));
+ pages.put("branches", new PageRegistration("gb.branches", BranchesPage.class, params));
+ pages.put("tags", new PageRegistration("gb.tags", TagsPage.class, params));
+ pages.put("tree", new PageRegistration("gb.tree", TreePage.class, params));
+ if (GitBlit.getBoolean(Keys.web.allowForking, true)) {
+ pages.put("forks", new PageRegistration("gb.forks", ForksPage.class, params));
+ }
+
+ // conditional links
+ Repository r = getRepository();
+ RepositoryModel model = getRepositoryModel();
+
+ // per-repository extra page links
+ if (model.useTickets && TicgitUtils.getTicketsBranch(r) != null) {
+ pages.put("tickets", new PageRegistration("gb.tickets", TicketsPage.class, params));
+ }
+ if (model.useDocs) {
+ pages.put("docs", new PageRegistration("gb.docs", DocsPage.class, params));
+ }
+ if (JGitUtils.getPagesBranch(r) != null) {
+ OtherPageLink pagesLink = new OtherPageLink("gb.pages", PagesServlet.asLink(
+ getRequest().getRelativePathPrefixToContextRoot(), repositoryName, null));
+ pages.put("pages", pagesLink);
+ }
+
+ // Conditionally add edit link
+ showAdmin = false;
+ if (GitBlit.getBoolean(Keys.web.authenticateAdminPages, true)) {
+ boolean allowAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, false);
+ showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin();
+ } else {
+ showAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, false);
+ }
+ isOwner = GitBlitWebSession.get().isLoggedIn()
+ && (model.isOwner(GitBlitWebSession.get()
+ .getUsername()));
+ if (showAdmin || isOwner) {
+ pages.put("edit", new PageRegistration("gb.edit", EditRepositoryPage.class, params));
+ }
+ return pages;
+ }
+
+ protected boolean allowForkControls() {
+ return GitBlit.getBoolean(Keys.web.allowForking, true);
+ }
+
+ @Override
+ protected void setupPage(String repositoryName, String pageName) {
+ String projectName = StringUtils.getFirstPathElement(repositoryName);
+ ProjectModel project = GitBlit.self().getProjectModel(projectName);
+ if (project.isUserProject()) {
+ // user-as-project
+ add(new LinkPanel("projectTitle", null, project.getDisplayName(),
+ UserPage.class, WicketUtils.newUsernameParameter(project.name.substring(1))));
+ } else {
+ // project
+ add(new LinkPanel("projectTitle", null, project.name,
+ ProjectPage.class, WicketUtils.newProjectParameter(project.name)));
+ }
+
+ String name = StringUtils.stripDotGit(repositoryName);
+ if (!StringUtils.isEmpty(projectName) && name.startsWith(projectName)) {
+ name = name.substring(projectName.length() + 1);
+ }
+ add(new LinkPanel("repositoryName", null, name, SummaryPage.class,
+ WicketUtils.newRepositoryParameter(repositoryName)));
+ add(new Label("pageName", pageName).setRenderBodyOnly(true));
+
+ UserModel user = GitBlitWebSession.get().getUser();
+ if (user == null) {
+ user = UserModel.ANONYMOUS;
+ }
+
+ // indicate origin repository
+ RepositoryModel model = getRepositoryModel();
+ if (StringUtils.isEmpty(model.originRepository)) {
+ add(new Label("originRepository").setVisible(false));
+ } else {
+ RepositoryModel origin = GitBlit.self().getRepositoryModel(model.originRepository);
+ if (origin == null) {
+ // no origin repository
+ add(new Label("originRepository").setVisible(false));
+ } else if (!user.canView(origin)) {
+ // show origin repository without link
+ Fragment forkFrag = new Fragment("originRepository", "originFragment", this);
+ forkFrag.add(new Label("originRepository", StringUtils.stripDotGit(model.originRepository)));
+ add(forkFrag);
+ } else {
+ // link to origin repository
+ Fragment forkFrag = new Fragment("originRepository", "originFragment", this);
+ forkFrag.add(new LinkPanel("originRepository", null, StringUtils.stripDotGit(model.originRepository),
+ SummaryPage.class, WicketUtils.newRepositoryParameter(model.originRepository)));
+ add(forkFrag);
+ }
+ }
+
+ // show sparkleshare folder icon
+ if (model.isSparkleshared()) {
+ add(WicketUtils.newImage("repositoryIcon", "folder_star_32x32.png",
+ getString("gb.isSparkleshared")));
+ } else {
+ add(WicketUtils.newClearPixel("repositoryIcon").setVisible(false));
+ }
+
+ if (getRepositoryModel().isBare) {
+ add(new Label("workingCopyIndicator").setVisible(false));
+ } else {
+ Fragment wc = new Fragment("workingCopyIndicator", "workingCopyFragment", this);
+ Label lbl = new Label("workingCopy", getString("gb.workingCopy"));
+ WicketUtils.setHtmlTooltip(lbl, getString("gb.workingCopyWarning"));
+ wc.add(lbl);
+ add(wc);
+ }
+
+ // fork controls
+ if (!allowForkControls() || user == null || !user.isAuthenticated) {
+ // must be logged-in to fork, hide all fork controls
+ add(new ExternalLink("forkLink", "").setVisible(false));
+ add(new ExternalLink("myForkLink", "").setVisible(false));
+ add(new Label("forksProhibitedIndicator").setVisible(false));
+ } else {
+ String fork = GitBlit.self().getFork(user.username, model.name);
+ boolean hasFork = fork != null;
+ boolean canFork = user.canFork(model);
+
+ if (hasFork || !canFork) {
+ // user not allowed to fork or fork already exists or repo forbids forking
+ add(new ExternalLink("forkLink", "").setVisible(false));
+
+ if (user.canFork() && !model.allowForks) {
+ // show forks prohibited indicator
+ Fragment wc = new Fragment("forksProhibitedIndicator", "forksProhibitedFragment", this);
+ Label lbl = new Label("forksProhibited", getString("gb.forksProhibited"));
+ WicketUtils.setHtmlTooltip(lbl, getString("gb.forksProhibitedWarning"));
+ wc.add(lbl);
+ add(wc);
+ } else {
+ // can not fork, no need for forks prohibited indicator
+ add(new Label("forksProhibitedIndicator").setVisible(false));
+ }
+
+ if (hasFork && !fork.equals(model.name)) {
+ // user has fork, view my fork link
+ String url = getRequestCycle().urlFor(SummaryPage.class, WicketUtils.newRepositoryParameter(fork)).toString();
+ add(new ExternalLink("myForkLink", url));
+ } else {
+ // no fork, hide view my fork link
+ add(new ExternalLink("myForkLink", "").setVisible(false));
+ }
+ } else if (canFork) {
+ // can fork and we do not have one
+ add(new Label("forksProhibitedIndicator").setVisible(false));
+ add(new ExternalLink("myForkLink", "").setVisible(false));
+ String url = getRequestCycle().urlFor(ForkPage.class, WicketUtils.newRepositoryParameter(model.name)).toString();
+ add(new ExternalLink("forkLink", url));
+ }
+ }
+
+ super.setupPage(repositoryName, pageName);
+ }
+
+ protected void addSyndicationDiscoveryLink() {
+ add(WicketUtils.syndicationDiscoveryLink(SyndicationServlet.getTitle(repositoryName,
+ objectId), SyndicationServlet.asLink(getRequest()
+ .getRelativePathPrefixToContextRoot(), repositoryName, objectId, 0)));
+ }
+
+ protected Repository getRepository() {
+ if (r == null) {
+ Repository r = GitBlit.self().getRepository(repositoryName);
+ if (r == null) {
+ error(getString("gb.canNotLoadRepository") + " " + repositoryName, true);
+ return null;
+ }
+ this.r = r;
+ }
+ return r;
+ }
+
+ protected RepositoryModel getRepositoryModel() {
+ if (m == null) {
+ RepositoryModel model = GitBlit.self().getRepositoryModel(
+ GitBlitWebSession.get().getUser(), repositoryName);
+ if (model == null) {
+ if (GitBlit.self().hasRepository(repositoryName, true)) {
+ // has repository, but unauthorized
+ authenticationError(getString("gb.unauthorizedAccessForRepository") + " " + repositoryName);
+ } else {
+ // does not have repository
+ error(getString("gb.canNotLoadRepository") + " " + repositoryName, true);
+ }
+ return null;
+ }
+ m = model;
+ }
+ return m;
+ }
+
+ protected RevCommit getCommit() {
+ RevCommit commit = JGitUtils.getCommit(r, objectId);
+ if (commit == null) {
+ error(MessageFormat.format(getString("gb.failedToFindCommit"),
+ objectId, repositoryName, getPageName()), true);
+ }
+ getSubmodules(commit);
+ return commit;
+ }
+
+ private Map<String, SubmoduleModel> getSubmodules(RevCommit commit) {
+ if (submodules == null) {
+ submodules = new HashMap<String, SubmoduleModel>();
+ for (SubmoduleModel model : JGitUtils.getSubmodules(r, commit.getTree())) {
+ submodules.put(model.path, model);
+ }
+ }
+ return submodules;
+ }
+
+ protected SubmoduleModel getSubmodule(String path) {
+ SubmoduleModel model = submodules.get(path);
+ if (model == null) {
+ // undefined submodule?!
+ model = new SubmoduleModel(path.substring(path.lastIndexOf('/') + 1), path, path);
+ model.hasSubmodule = false;
+ model.gitblitPath = model.name;
+ return model;
+ } else {
+ // extract the repository name from the clone url
+ List<String> patterns = GitBlit.getStrings(Keys.git.submoduleUrlPatterns);
+ String submoduleName = StringUtils.extractRepositoryPath(model.url, patterns.toArray(new String[0]));
+
+ // determine the current path for constructing paths relative
+ // to the current repository
+ String currentPath = "";
+ if (repositoryName.indexOf('/') > -1) {
+ currentPath = repositoryName.substring(0, repositoryName.lastIndexOf('/') + 1);
+ }
+
+ // try to locate the submodule repository
+ // prefer bare to non-bare names
+ List<String> candidates = new ArrayList<String>();
+
+ // relative
+ candidates.add(currentPath + StringUtils.stripDotGit(submoduleName));
+ candidates.add(candidates.get(candidates.size() - 1) + ".git");
+
+ // relative, no subfolder
+ if (submoduleName.lastIndexOf('/') > -1) {
+ String name = submoduleName.substring(submoduleName.lastIndexOf('/') + 1);
+ candidates.add(currentPath + StringUtils.stripDotGit(name));
+ candidates.add(currentPath + candidates.get(candidates.size() - 1) + ".git");
+ }
+
+ // absolute
+ candidates.add(StringUtils.stripDotGit(submoduleName));
+ candidates.add(candidates.get(candidates.size() - 1) + ".git");
+
+ // absolute, no subfolder
+ if (submoduleName.lastIndexOf('/') > -1) {
+ String name = submoduleName.substring(submoduleName.lastIndexOf('/') + 1);
+ candidates.add(StringUtils.stripDotGit(name));
+ candidates.add(candidates.get(candidates.size() - 1) + ".git");
+ }
+
+ // create a unique, ordered set of candidate paths
+ Set<String> paths = new LinkedHashSet<String>(candidates);
+ for (String candidate : paths) {
+ if (GitBlit.self().hasRepository(candidate)) {
+ model.hasSubmodule = true;
+ model.gitblitPath = candidate;
+ return model;
+ }
+ }
+
+ // we do not have a copy of the submodule, but we need a path
+ model.gitblitPath = candidates.get(0);
+ return model;
+ }
+ }
+
+ protected String getShortObjectId(String objectId) {
+ return objectId.substring(0, GitBlit.getInteger(Keys.web.shortCommitIdLength, 6));
+ }
+
+ protected void addRefs(Repository r, RevCommit c) {
+ add(new RefsPanel("refsPanel", repositoryName, c, JGitUtils.getAllRefs(r, getRepositoryModel().showRemoteBranches)));
+ }
+
+ protected void addFullText(String wicketId, String text, boolean substituteRegex) {
+ String html = StringUtils.escapeForHtml(text, true);
+ if (substituteRegex) {
+ html = GitBlit.self().processCommitMessage(repositoryName, text);
+ } else {
+ html = StringUtils.breakLinesForHtml(html);
+ }
+ add(new Label(wicketId, html).setEscapeModelStrings(false));
+ }
+
+ protected abstract String getPageName();
+
+ protected Component createPersonPanel(String wicketId, PersonIdent identity,
+ Constants.SearchType searchType) {
+ String name = identity == null ? "" : identity.getName();
+ String address = identity == null ? "" : identity.getEmailAddress();
+ name = StringUtils.removeNewlines(name);
+ address = StringUtils.removeNewlines(address);
+ boolean showEmail = GitBlit.getBoolean(Keys.web.showEmailAddresses, false);
+ if (!showEmail || StringUtils.isEmpty(name) || StringUtils.isEmpty(address)) {
+ String value = name;
+ if (StringUtils.isEmpty(value)) {
+ if (showEmail) {
+ value = address;
+ } else {
+ value = getString("gb.missingUsername");
+ }
+ }
+ Fragment partial = new Fragment(wicketId, "partialPersonIdent", this);
+ LinkPanel link = new LinkPanel("personName", "list", value, GitSearchPage.class,
+ WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType));
+ setPersonSearchTooltip(link, value, searchType);
+ partial.add(link);
+ return partial;
+ } else {
+ Fragment fullPerson = new Fragment(wicketId, "fullPersonIdent", this);
+ LinkPanel nameLink = new LinkPanel("personName", "list", name, GitSearchPage.class,
+ WicketUtils.newSearchParameter(repositoryName, objectId, name, searchType));
+ setPersonSearchTooltip(nameLink, name, searchType);
+ fullPerson.add(nameLink);
+
+ LinkPanel addressLink = new LinkPanel("personAddress", "hidden-phone list", "<" + address + ">",
+ GitSearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId,
+ address, searchType));
+ setPersonSearchTooltip(addressLink, address, searchType);
+ fullPerson.add(addressLink);
+ return fullPerson;
+ }
+ }
+
+ protected void setPersonSearchTooltip(Component component, String value,
+ Constants.SearchType searchType) {
+ if (searchType.equals(Constants.SearchType.AUTHOR)) {
+ WicketUtils.setHtmlTooltip(component, getString("gb.searchForAuthor") + " " + value);
+ } else if (searchType.equals(Constants.SearchType.COMMITTER)) {
+ WicketUtils.setHtmlTooltip(component, getString("gb.searchForCommitter") + " " + value);
+ }
+ }
+
+ protected void setChangeTypeTooltip(Component container, ChangeType type) {
+ switch (type) {
+ case ADD:
+ WicketUtils.setHtmlTooltip(container, getString("gb.addition"));
+ break;
+ case COPY:
+ case RENAME:
+ WicketUtils.setHtmlTooltip(container, getString("gb.rename"));
+ break;
+ case DELETE:
+ WicketUtils.setHtmlTooltip(container, getString("gb.deletion"));
+ break;
+ case MODIFY:
+ WicketUtils.setHtmlTooltip(container, getString("gb.modification"));
+ break;
+ }
+ }
+
+ @Override
+ protected void onBeforeRender() {
+ // dispose of repository object
+ if (r != null) {
+ r.close();
+ r = null;
+ }
+ // setup page header and footer
+ setupPage(repositoryName, "/ " + getPageName());
+ super.onBeforeRender();
+ }
+
+ protected PageParameters newRepositoryParameter() {
+ return WicketUtils.newRepositoryParameter(repositoryName);
+ }
+
+ protected PageParameters newCommitParameter() {
+ return WicketUtils.newObjectParameter(repositoryName, objectId);
+ }
+
+ protected PageParameters newCommitParameter(String commitId) {
+ return WicketUtils.newObjectParameter(repositoryName, commitId);
+ }
+
+ public boolean isShowAdmin() {
+ return showAdmin;
+ }
+
+ public boolean isOwner() {
+ return isOwner;
+ }
+
+ private class SearchForm extends SessionlessForm<Void> implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final String repositoryName;
+
+ private final IModel<String> searchBoxModel = new Model<String>("");
+
+ private final IModel<Constants.SearchType> searchTypeModel = new Model<Constants.SearchType>(
+ Constants.SearchType.COMMIT);
+
+ public SearchForm(String id, String repositoryName) {
+ super(id, RepositoryPage.this.getClass(), RepositoryPage.this.getPageParameters());
+ this.repositoryName = repositoryName;
+ DropDownChoice<Constants.SearchType> searchType = new DropDownChoice<Constants.SearchType>(
+ "searchType", Arrays.asList(Constants.SearchType.values()));
+ searchType.setModel(searchTypeModel);
+ add(searchType.setVisible(GitBlit.getBoolean(Keys.web.showSearchTypeSelection, false)));
+ TextField<String> searchBox = new TextField<String>("searchBox", searchBoxModel);
+ add(searchBox);
+ }
+
+ void setTranslatedAttributes() {
+ WicketUtils.setHtmlTooltip(get("searchType"), getString("gb.searchTypeTooltip"));
+ WicketUtils.setHtmlTooltip(get("searchBox"),
+ MessageFormat.format(getString("gb.searchTooltip"), repositoryName));
+ WicketUtils.setInputPlaceholder(get("searchBox"), getString("gb.search"));
+ }
+
+ @Override
+ public void onSubmit() {
+ Constants.SearchType searchType = searchTypeModel.getObject();
+ String searchString = searchBoxModel.getObject();
+ if (searchString == null) {
+ return;
+ }
+ for (Constants.SearchType type : Constants.SearchType.values()) {
+ if (searchString.toLowerCase().startsWith(type.name().toLowerCase() + ":")) {
+ searchType = type;
+ searchString = searchString.substring(type.name().toLowerCase().length() + 1)
+ .trim();
+ break;
+ }
+ }
+ Class<? extends BasePage> searchPageClass = GitSearchPage.class;
+ RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName);
+ if (GitBlit.getBoolean(Keys.web.allowLuceneIndexing, true)
+ && !ArrayUtils.isEmpty(model.indexedBranches)) {
+ // this repository is Lucene-indexed
+ searchPageClass = LuceneSearchPage.class;
+ }
+ // use an absolute url to workaround Wicket-Tomcat problems with
+ // mounted url parameters (issue-111)
+ PageParameters params = WicketUtils.newSearchParameter(repositoryName, null, searchString, searchType);
+ String relativeUrl = urlFor(searchPageClass, params).toString();
+ String absoluteUrl = RequestUtils.toAbsolutePath(relativeUrl);
+ getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl));
+ }
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.html b/src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.html
new file mode 100644
index 00000000..6487a0ac
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+ <!-- proposal info -->
+ <table class="plain">
+ <tr><th><wicket:message key="gb.received">received</wicket:message></th><td><span wicket:id="received">[received]</span></td></tr>
+ <tr><th><wicket:message key="gb.url">url</wicket:message></th><td><span wicket:id="url">[url]</span></td></tr>
+ <tr><th><wicket:message key="gb.message">message</wicket:message></th><td><span wicket:id="message">[message]</span></td></tr>
+ <tr><th><wicket:message key="gb.type">type</wicket:message></th><td><span wicket:id="tokenType">[token type]</span></td></tr>
+ <tr><th><wicket:message key="gb.token">token</wicket:message></th><td><span class="sha1" wicket:id="token">[token]</span></td></tr>
+ <tr><th valign="top"><wicket:message key="gb.proposal">proposal</wicket:message></th><td><span class="sha1" wicket:id="definition">[definition]</span></td></tr>
+ </table>
+
+ <div wicket:id="repositoriesPanel"></div>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.java b/src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.java
new file mode 100644
index 00000000..e1813861
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+
+import com.gitblit.Constants.FederationToken;
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.FederationProposal;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.RequiresAdminRole;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.RepositoriesPanel;
+
+@RequiresAdminRole
+public class ReviewProposalPage extends RootSubPage {
+
+ private final String PROPS_PATTERN = "{0} = {1}\n";
+
+ private final String WEBXML_PATTERN = "\n<context-param>\n\t<param-name>{0}</param-name>\n\t<param-value>{1}</param-value>\n</context-param>\n";
+
+ public ReviewProposalPage(PageParameters params) {
+ super(params);
+
+ final String token = WicketUtils.getToken(params);
+
+ FederationProposal proposal = GitBlit.self().getPendingFederationProposal(token);
+ if (proposal == null) {
+ error(getString("gb.couldNotFindFederationProposal"), true);
+ }
+
+ setupPage(getString("gb.proposals"), proposal.url);
+
+
+ add(new Label("url", proposal.url));
+ add(new Label("message", proposal.message));
+ add(WicketUtils.createTimestampLabel("received", proposal.received, getTimeZone(), getTimeUtils()));
+ add(new Label("token", proposal.token));
+ add(new Label("tokenType", proposal.tokenType.name()));
+
+ String p;
+ if (GitBlit.isGO()) {
+ // gitblit.properties definition
+ p = PROPS_PATTERN;
+ } else {
+ // web.xml definition
+ p = WEBXML_PATTERN;
+ }
+
+ // build proposed definition
+ StringBuilder sb = new StringBuilder();
+ sb.append(asParam(p, proposal.name, "url", proposal.url));
+ sb.append(asParam(p, proposal.name, "token", proposal.token));
+
+ if (FederationToken.USERS_AND_REPOSITORIES.equals(proposal.tokenType)
+ || FederationToken.ALL.equals(proposal.tokenType)) {
+ sb.append(asParam(p, proposal.name, "mergeAccounts", "false"));
+ }
+ sb.append(asParam(p, proposal.name, "frequency",
+ GitBlit.getString(Keys.federation.defaultFrequency, "60 mins")));
+ sb.append(asParam(p, proposal.name, "folder", proposal.name));
+ sb.append(asParam(p, proposal.name, "bare", "true"));
+ sb.append(asParam(p, proposal.name, "mirror", "true"));
+ sb.append(asParam(p, proposal.name, "sendStatus", "true"));
+ sb.append(asParam(p, proposal.name, "notifyOnError", "true"));
+ sb.append(asParam(p, proposal.name, "exclude", ""));
+ sb.append(asParam(p, proposal.name, "include", ""));
+
+ add(new Label("definition", StringUtils.breakLinesForHtml(StringUtils.escapeForHtml(sb
+ .toString().trim(), true))).setEscapeModelStrings(false));
+
+ List<RepositoryModel> repositories = new ArrayList<RepositoryModel>(
+ proposal.repositories.values());
+ RepositoriesPanel repositoriesPanel = new RepositoriesPanel("repositoriesPanel", false,
+ false, repositories, false, getAccessRestrictions());
+ add(repositoriesPanel);
+ }
+
+ private String asParam(String pattern, String name, String key, String value) {
+ return MessageFormat.format(pattern, Keys.federation._ROOT + "." + name + "." + key, value);
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.html b/src/main/java/com/gitblit/wicket/pages/RootPage.html
new file mode 100644
index 00000000..b35b1b3a
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/RootPage.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+<body>
+<wicket:extend>
+ <div class="navbar navbar-fixed-top">
+ <div class="navbar-inner">
+ <div class="container">
+ <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </a>
+ <a class="brand" wicket:id="rootLink">
+ <img src="gitblt_25_white.png" width="79" height="25" alt="gitblit" class="logo"/>
+ </a>
+
+ <div class="nav-collapse" wicket:id="navPanel"></div>
+
+ <form class="pull-right" wicket:id="loginForm" style="padding-top:3px;">
+ <span class="form-search">
+ <input wicket:id="username" class="input-small" type="text" />
+ <input wicket:id="password" class="input-small" type="password" />
+ <button class="btn btn-primary" type="submit"><wicket:message key="gb.login"></wicket:message></button>
+ </span>
+ </form>
+ </div>
+ </div>
+ </div>
+
+ <!-- subclass content -->
+ <div class="container">
+ <div style="text-align:center" wicket:id="feedback">[Feedback Panel]</div>
+
+ <wicket:child/>
+ </div>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.java b/src/main/java/com/gitblit/wicket/pages/RootPage.java
new file mode 100644
index 00000000..adcd7b16
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/RootPage.java
@@ -0,0 +1,454 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Pattern;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.form.PasswordTextField;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.protocol.http.WebResponse;
+
+import com.gitblit.Constants;
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.TeamModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.PageRegistration;
+import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
+import com.gitblit.wicket.SessionlessForm;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.NavigationPanel;
+
+/**
+ * Root page is a topbar, navigable page like Repositories, Users, or
+ * Federation.
+ *
+ * @author James Moger
+ *
+ */
+public abstract class RootPage extends BasePage {
+
+ boolean showAdmin;
+
+ IModel<String> username = new Model<String>("");
+ IModel<String> password = new Model<String>("");
+ List<RepositoryModel> repositoryModels = new ArrayList<RepositoryModel>();
+
+ public RootPage() {
+ super();
+ }
+
+ public RootPage(PageParameters params) {
+ super(params);
+ }
+
+ @Override
+ protected void setupPage(String repositoryName, String pageName) {
+ boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, false);
+ boolean authenticateAdmin = GitBlit.getBoolean(Keys.web.authenticateAdminPages, true);
+ boolean allowAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, true);
+
+ if (authenticateAdmin) {
+ showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin();
+ // authentication requires state and session
+ setStatelessHint(false);
+ } else {
+ showAdmin = allowAdmin;
+ if (authenticateView) {
+ // authentication requires state and session
+ setStatelessHint(false);
+ } else {
+ // no authentication required, no state and no session required
+ setStatelessHint(true);
+ }
+ }
+ boolean showRegistrations = GitBlit.canFederate()
+ && GitBlit.getBoolean(Keys.web.showFederationRegistrations, false);
+
+ // navigation links
+ List<PageRegistration> pages = new ArrayList<PageRegistration>();
+ pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class,
+ getRootPageParameters()));
+ pages.add(new PageRegistration("gb.activity", ActivityPage.class, getRootPageParameters()));
+ if (GitBlit.getBoolean(Keys.web.allowLuceneIndexing, true)) {
+ pages.add(new PageRegistration("gb.search", LuceneSearchPage.class));
+ }
+ if (showAdmin) {
+ pages.add(new PageRegistration("gb.users", UsersPage.class));
+ }
+ if (showAdmin || showRegistrations) {
+ pages.add(new PageRegistration("gb.federation", FederationPage.class));
+ }
+
+ if (!authenticateView || (authenticateView && GitBlitWebSession.get().isLoggedIn())) {
+ addDropDownMenus(pages);
+ }
+
+ NavigationPanel navPanel = new NavigationPanel("navPanel", getClass(), pages);
+ add(navPanel);
+
+ // login form
+ SessionlessForm<Void> loginForm = new SessionlessForm<Void>("loginForm", getClass(), getPageParameters()) {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void onSubmit() {
+ String username = RootPage.this.username.getObject();
+ char[] password = RootPage.this.password.getObject().toCharArray();
+
+ UserModel user = GitBlit.self().authenticate(username, password);
+ if (user == null) {
+ error(getString("gb.invalidUsernameOrPassword"));
+ } else if (user.username.equals(Constants.FEDERATION_USER)) {
+ // disallow the federation user from logging in via the
+ // web ui
+ error(getString("gb.invalidUsernameOrPassword"));
+ user = null;
+ } else {
+ loginUser(user);
+ }
+ }
+ };
+ TextField<String> unameField = new TextField<String>("username", username);
+ WicketUtils.setInputPlaceholder(unameField, getString("gb.username"));
+ loginForm.add(unameField);
+ PasswordTextField pwField = new PasswordTextField("password", password);
+ WicketUtils.setInputPlaceholder(pwField, getString("gb.password"));
+ loginForm.add(pwField);
+ add(loginForm);
+
+ if (authenticateView || authenticateAdmin) {
+ loginForm.setVisible(!GitBlitWebSession.get().isLoggedIn());
+ } else {
+ loginForm.setVisible(false);
+ }
+
+ // display an error message cached from a redirect
+ String cachedMessage = GitBlitWebSession.get().clearErrorMessage();
+ if (!StringUtils.isEmpty(cachedMessage)) {
+ error(cachedMessage);
+ } else if (showAdmin) {
+ int pendingProposals = GitBlit.self().getPendingFederationProposals().size();
+ if (pendingProposals == 1) {
+ info(getString("gb.OneProposalToReview"));
+ } else if (pendingProposals > 1) {
+ info(MessageFormat.format(getString("gb.nFederationProposalsToReview"),
+ pendingProposals));
+ }
+ }
+
+ super.setupPage(repositoryName, pageName);
+ }
+
+ private PageParameters getRootPageParameters() {
+ if (reusePageParameters()) {
+ PageParameters pp = getPageParameters();
+ if (pp != null) {
+ PageParameters params = new PageParameters(pp);
+ // remove named project parameter
+ params.remove("p");
+
+ // remove named repository parameter
+ params.remove("r");
+
+ // remove named user parameter
+ params.remove("user");
+
+ // remove days back parameter if it is the default value
+ if (params.containsKey("db")
+ && params.getInt("db") == GitBlit.getInteger(Keys.web.activityDuration, 14)) {
+ params.remove("db");
+ }
+ return params;
+ }
+ }
+ return null;
+ }
+
+ protected boolean reusePageParameters() {
+ return false;
+ }
+
+ private void loginUser(UserModel user) {
+ if (user != null) {
+ // Set the user into the session
+ GitBlitWebSession session = GitBlitWebSession.get();
+ // issue 62: fix session fixation vulnerability
+ session.replaceSession();
+ session.setUser(user);
+
+ // Set Cookie
+ if (GitBlit.getBoolean(Keys.web.allowCookieAuthentication, false)) {
+ WebResponse response = (WebResponse) getRequestCycle().getResponse();
+ GitBlit.self().setCookie(response, user);
+ }
+
+ if (!session.continueRequest()) {
+ PageParameters params = getPageParameters();
+ if (params == null) {
+ // redirect to this page
+ setResponsePage(getClass());
+ } else {
+ // Strip username and password and redirect to this page
+ params.remove("username");
+ params.remove("password");
+ setResponsePage(getClass(), params);
+ }
+ }
+ }
+ }
+
+ protected List<RepositoryModel> getRepositoryModels() {
+ if (repositoryModels.isEmpty()) {
+ final UserModel user = GitBlitWebSession.get().getUser();
+ List<RepositoryModel> repositories = GitBlit.self().getRepositoryModels(user);
+ repositoryModels.addAll(repositories);
+ Collections.sort(repositoryModels);
+ }
+ return repositoryModels;
+ }
+
+ protected void addDropDownMenus(List<PageRegistration> pages) {
+
+ }
+
+ protected List<DropDownMenuItem> getRepositoryFilterItems(PageParameters params) {
+ final UserModel user = GitBlitWebSession.get().getUser();
+ Set<DropDownMenuItem> filters = new LinkedHashSet<DropDownMenuItem>();
+ List<RepositoryModel> repositories = getRepositoryModels();
+
+ // accessible repositories by federation set
+ Map<String, AtomicInteger> setMap = new HashMap<String, AtomicInteger>();
+ for (RepositoryModel repository : repositories) {
+ for (String set : repository.federationSets) {
+ String key = set.toLowerCase();
+ if (setMap.containsKey(key)) {
+ setMap.get(key).incrementAndGet();
+ } else {
+ setMap.put(key, new AtomicInteger(1));
+ }
+ }
+ }
+ if (setMap.size() > 0) {
+ List<String> sets = new ArrayList<String>(setMap.keySet());
+ Collections.sort(sets);
+ for (String set : sets) {
+ filters.add(new DropDownMenuItem(MessageFormat.format("{0} ({1})", set,
+ setMap.get(set).get()), "set", set, params));
+ }
+ // divider
+ filters.add(new DropDownMenuItem());
+ }
+
+ // user's team memberships
+ if (user != null && user.teams.size() > 0) {
+ List<TeamModel> teams = new ArrayList<TeamModel>(user.teams);
+ Collections.sort(teams);
+ for (TeamModel team : teams) {
+ filters.add(new DropDownMenuItem(MessageFormat.format("{0} ({1})", team.name,
+ team.repositories.size()), "team", team.name, params));
+ }
+ // divider
+ filters.add(new DropDownMenuItem());
+ }
+
+ // custom filters
+ String customFilters = GitBlit.getString(Keys.web.customFilters, null);
+ if (!StringUtils.isEmpty(customFilters)) {
+ boolean addedExpression = false;
+ List<String> expressions = StringUtils.getStringsFromValue(customFilters, "!!!");
+ for (String expression : expressions) {
+ if (!StringUtils.isEmpty(expression)) {
+ addedExpression = true;
+ filters.add(new DropDownMenuItem(null, "x", expression, params));
+ }
+ }
+ // if we added any custom expressions, add a divider
+ if (addedExpression) {
+ filters.add(new DropDownMenuItem());
+ }
+ }
+ return new ArrayList<DropDownMenuItem>(filters);
+ }
+
+ protected List<DropDownMenuItem> getTimeFilterItems(PageParameters params) {
+ // days back choices - additive parameters
+ int daysBack = GitBlit.getInteger(Keys.web.activityDuration, 14);
+ if (daysBack < 1) {
+ daysBack = 14;
+ }
+ List<DropDownMenuItem> items = new ArrayList<DropDownMenuItem>();
+ Set<Integer> choicesSet = new HashSet<Integer>(Arrays.asList(daysBack, 14, 28, 60, 90, 180));
+ List<Integer> choices = new ArrayList<Integer>(choicesSet);
+ Collections.sort(choices);
+ String lastDaysPattern = getString("gb.lastNDays");
+ for (Integer db : choices) {
+ String txt = MessageFormat.format(lastDaysPattern, db);
+ items.add(new DropDownMenuItem(txt, "db", db.toString(), params));
+ }
+ items.add(new DropDownMenuItem());
+ return items;
+ }
+
+ protected List<RepositoryModel> getRepositories(PageParameters params) {
+ if (params == null) {
+ return getRepositoryModels();
+ }
+
+ boolean hasParameter = false;
+ String projectName = WicketUtils.getProjectName(params);
+ String userName = WicketUtils.getUsername(params);
+ if (StringUtils.isEmpty(projectName)) {
+ if (!StringUtils.isEmpty(userName)) {
+ projectName = "~" + userName;
+ }
+ }
+ String repositoryName = WicketUtils.getRepositoryName(params);
+ String set = WicketUtils.getSet(params);
+ String regex = WicketUtils.getRegEx(params);
+ String team = WicketUtils.getTeam(params);
+ int daysBack = params.getInt("db", 0);
+
+ List<RepositoryModel> availableModels = getRepositoryModels();
+ Set<RepositoryModel> models = new HashSet<RepositoryModel>();
+
+ if (!StringUtils.isEmpty(repositoryName)) {
+ // try named repository
+ hasParameter = true;
+ for (RepositoryModel model : availableModels) {
+ if (model.name.equalsIgnoreCase(repositoryName)) {
+ models.add(model);
+ break;
+ }
+ }
+ }
+
+ if (!StringUtils.isEmpty(projectName)) {
+ // try named project
+ hasParameter = true;
+ if (projectName.equalsIgnoreCase(GitBlit.getString(Keys.web.repositoryRootGroupName, "main"))) {
+ // root project/group
+ for (RepositoryModel model : availableModels) {
+ if (model.name.indexOf('/') == -1) {
+ models.add(model);
+ }
+ }
+ } else {
+ // named project/group
+ String group = projectName.toLowerCase() + "/";
+ for (RepositoryModel model : availableModels) {
+ if (model.name.toLowerCase().startsWith(group)) {
+ models.add(model);
+ }
+ }
+ }
+ }
+
+ if (!StringUtils.isEmpty(regex)) {
+ // filter the repositories by the regex
+ hasParameter = true;
+ Pattern pattern = Pattern.compile(regex);
+ for (RepositoryModel model : availableModels) {
+ if (pattern.matcher(model.name).find()) {
+ models.add(model);
+ }
+ }
+ }
+
+ if (!StringUtils.isEmpty(set)) {
+ // filter the repositories by the specified sets
+ hasParameter = true;
+ List<String> sets = StringUtils.getStringsFromValue(set, ",");
+ for (RepositoryModel model : availableModels) {
+ for (String curr : sets) {
+ if (model.federationSets.contains(curr)) {
+ models.add(model);
+ }
+ }
+ }
+ }
+
+ if (!StringUtils.isEmpty(team)) {
+ // filter the repositories by the specified teams
+ hasParameter = true;
+ List<String> teams = StringUtils.getStringsFromValue(team, ",");
+
+ // need TeamModels first
+ List<TeamModel> teamModels = new ArrayList<TeamModel>();
+ for (String name : teams) {
+ TeamModel teamModel = GitBlit.self().getTeamModel(name);
+ if (teamModel != null) {
+ teamModels.add(teamModel);
+ }
+ }
+
+ // brute-force our way through finding the matching models
+ for (RepositoryModel repositoryModel : availableModels) {
+ for (TeamModel teamModel : teamModels) {
+ if (teamModel.hasRepositoryPermission(repositoryModel.name)) {
+ models.add(repositoryModel);
+ }
+ }
+ }
+ }
+
+ if (!hasParameter) {
+ models.addAll(availableModels);
+ }
+
+ // time-filter the list
+ if (daysBack > 0) {
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+ cal.add(Calendar.DATE, -1 * daysBack);
+ Date threshold = cal.getTime();
+ Set<RepositoryModel> timeFiltered = new HashSet<RepositoryModel>();
+ for (RepositoryModel model : models) {
+ if (model.lastChange.after(threshold)) {
+ timeFiltered.add(model);
+ }
+ }
+ models = timeFiltered;
+ }
+
+ List<RepositoryModel> list = new ArrayList<RepositoryModel>(models);
+ Collections.sort(list);
+ return list;
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/RootSubPage.html b/src/main/java/com/gitblit/wicket/pages/RootSubPage.html
new file mode 100644
index 00000000..2b109f9b
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/RootSubPage.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+ <!-- page header -->
+ <div class="pageTitle">
+ <h2><span wicket:id="pageName">[page name]</span> <small><span wicket:id="pageSubName">[sub name]</span></small></h2>
+ </div>
+
+ <!-- Subclass Content -->
+ <wicket:child/>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/RootSubPage.java b/src/main/java/com/gitblit/wicket/pages/RootSubPage.java
new file mode 100644
index 00000000..e7e12ccc
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/RootSubPage.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.Session;
+import org.apache.wicket.markup.html.basic.Label;
+
+import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.Constants.AuthorizationControl;
+import com.gitblit.GitBlit;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.StringUtils;
+
+/**
+ * RootSubPage is a non-topbar navigable RootPage. It also has a page header.
+ *
+ * @author James Moger
+ *
+ */
+public abstract class RootSubPage extends RootPage {
+
+ public RootSubPage() {
+ super();
+ createPageMapIfNeeded();
+ }
+
+ public RootSubPage(PageParameters params) {
+ super(params);
+ createPageMapIfNeeded();
+ }
+
+ protected boolean requiresPageMap() {
+ return false;
+ }
+
+ protected void createPageMapIfNeeded() {
+ if (requiresPageMap()) {
+ // because Gitblit strives for page-statelessness
+ // Wicket seems to get confused as to when it really should
+ // generate a page map for complex pages. Conditionally ensure we
+ // have a page map for complex AJAX pages like the EditNNN pages.
+ Session.get().pageMapForName(null, true);
+ setVersioned(true);
+ }
+ }
+
+ @Override
+ protected void setupPage(String pageName, String subName) {
+ add(new Label("pageName", pageName));
+ if (!StringUtils.isEmpty(subName)) {
+ subName = "/ " + subName;
+ }
+ add(new Label("pageSubName", subName));
+ super.setupPage("", pageName);
+ }
+
+ protected List<String> getAccessRestrictedRepositoryList(boolean includeWildcards, UserModel user) {
+ // build list of access-restricted projects
+ String lastProject = null;
+ List<String> repos = new ArrayList<String>();
+ if (includeWildcards) {
+ // all repositories
+ repos.add(".*");
+ // all repositories excluding personal repositories
+ repos.add("[^~].*");
+ }
+
+ for (String repo : GitBlit.self().getRepositoryList()) {
+ RepositoryModel repositoryModel = GitBlit.self().getRepositoryModel(repo);
+ if (repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE)
+ && repositoryModel.authorizationControl.equals(AuthorizationControl.NAMED)) {
+ if (user != null &&
+ (repositoryModel.isOwner(user.username) || repositoryModel.isUsersPersonalRepository(user.username))) {
+ // exclude Owner or personal repositories
+ continue;
+ }
+ if (includeWildcards) {
+ if (lastProject == null || !lastProject.equalsIgnoreCase(repositoryModel.projectPath)) {
+ lastProject = repositoryModel.projectPath.toLowerCase();
+ if (!StringUtils.isEmpty(repositoryModel.projectPath)) {
+ // regex for all repositories within a project
+ repos.add(repositoryModel.projectPath + "/.*");
+ }
+ }
+ }
+ repos.add(repo.toLowerCase());
+ }
+ }
+ return repos;
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/SendProposalPage.html b/src/main/java/com/gitblit/wicket/pages/SendProposalPage.html
new file mode 100644
index 00000000..cb9f3539
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/SendProposalPage.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<wicket:extend>
+<body onload="document.getElementById('myUrl').focus();">
+ <!-- proposal info -->
+ <form wicket:id="editForm">
+ <table class="plain">
+ <tr><th><wicket:message key="gb.url">url</wicket:message></th><td class="edit"><input class="span6" type="text" wicket:id="myUrl" id="myUrl" size="60" /> &nbsp;<i><wicket:message key="gb.myUrlDescription"></wicket:message></i></td></tr>
+ <tr><th><wicket:message key="gb.destinationUrl">destination url</wicket:message></th><td class="edit"><input class="span6" type="text" wicket:id="destinationUrl" size="60" /> &nbsp;<i><wicket:message key="gb.destinationUrlDescription"></wicket:message></i></td></tr>
+ <tr><th valign="top"><wicket:message key="gb.message">message</wicket:message></th><td class="edit"><input class="span8" type="text" wicket:id="message" size="80" /></td></tr>
+ <tr><th><wicket:message key="gb.type">type</wicket:message></th><td><span wicket:id="tokenType">[token type]</span></td></tr>
+ <tr><th><wicket:message key="gb.token">token</wicket:message></th><td><span class="sha1" wicket:id="token">[token]</span></td></tr>
+ <tr><th></th><td class="editButton"><input class="btn btn-primary" type="submit" value="propose" wicket:message="value:gb.sendProposal" wicket:id="save" /> &nbsp; <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /></td></tr>
+ </table>
+ </form>
+
+ <div style="padding-top:10px;" wicket:id="repositoriesPanel"></div>
+</body>
+</wicket:extend>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/SendProposalPage.java b/src/main/java/com/gitblit/wicket/pages/SendProposalPage.java
new file mode 100644
index 00000000..fc5f95b5
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/SendProposalPage.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Button;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.model.CompoundPropertyModel;
+
+import com.gitblit.Constants.FederationProposalResult;
+import com.gitblit.GitBlit;
+import com.gitblit.models.FederationProposal;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.utils.FederationUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.RequiresAdminRole;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.RepositoriesPanel;
+
+@RequiresAdminRole
+public class SendProposalPage extends RootSubPage {
+
+ public String myUrl;
+
+ public String destinationUrl;
+
+ public String message;
+
+ public SendProposalPage(PageParameters params) {
+ super(params);
+
+ setupPage(getString("gb.sendProposal"), "");
+ setStatelessHint(true);
+
+ final String token = WicketUtils.getToken(params);
+
+ myUrl = WicketUtils.getGitblitURL(getRequest());
+ destinationUrl = "https://";
+
+ // temporary proposal
+ FederationProposal proposal = GitBlit.self().createFederationProposal(myUrl, token);
+ if (proposal == null) {
+ error(getString("gb.couldNotCreateFederationProposal"), true);
+ }
+
+ CompoundPropertyModel<SendProposalPage> model = new CompoundPropertyModel<SendProposalPage>(
+ this);
+
+ Form<SendProposalPage> form = new Form<SendProposalPage>("editForm", model) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void onSubmit() {
+ // confirm a repository name was entered
+ if (StringUtils.isEmpty(myUrl)) {
+ error(getString("gb.pleaseSetGitblitUrl"));
+ return;
+ }
+ if (StringUtils.isEmpty(destinationUrl)) {
+ error(getString("gb.pleaseSetDestinationUrl"));
+ return;
+ }
+
+ // build new proposal
+ FederationProposal proposal = GitBlit.self().createFederationProposal(myUrl, token);
+ proposal.url = myUrl;
+ proposal.message = message;
+ try {
+ FederationProposalResult res = FederationUtils
+ .propose(destinationUrl, proposal);
+ switch (res) {
+ case ACCEPTED:
+ info(MessageFormat.format(getString("gb.proposalReceived"),
+ destinationUrl));
+ setResponsePage(RepositoriesPage.class);
+ break;
+ case NO_POKE:
+ error(MessageFormat.format(getString("noGitblitFound"),
+ destinationUrl, myUrl));
+ break;
+ case NO_PROPOSALS:
+ error(MessageFormat.format(getString("gb.noProposals"),
+ destinationUrl));
+ break;
+ case FEDERATION_DISABLED:
+ error(MessageFormat
+ .format(getString("gb.noFederation"),
+ destinationUrl));
+ break;
+ case MISSING_DATA:
+ error(MessageFormat.format(getString("gb.proposalFailed"),
+ destinationUrl));
+ break;
+ case ERROR:
+ error(MessageFormat.format(getString("gb.proposalError"),
+ destinationUrl));
+ break;
+ }
+ } catch (Exception e) {
+ if (!StringUtils.isEmpty(e.getMessage())) {
+ error(e.getMessage());
+ } else {
+ error(getString("gb.failedToSendProposal"));
+ }
+ }
+ }
+ };
+ form.add(new TextField<String>("myUrl"));
+ form.add(new TextField<String>("destinationUrl"));
+ form.add(new TextField<String>("message"));
+ form.add(new Label("tokenType", proposal.tokenType.name()));
+ form.add(new Label("token", proposal.token));
+
+ form.add(new Button("save"));
+ Button cancel = new Button("cancel") {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void onSubmit() {
+ setResponsePage(FederationPage.class);
+ }
+ };
+ cancel.setDefaultFormProcessing(false);
+ form.add(cancel);
+ add(form);
+
+ List<RepositoryModel> repositories = new ArrayList<RepositoryModel>(
+ proposal.repositories.values());
+ RepositoriesPanel repositoriesPanel = new RepositoriesPanel("repositoriesPanel", false,
+ false, repositories, false, getAccessRestrictions());
+ add(repositoriesPanel);
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/SummaryPage.html b/src/main/java/com/gitblit/wicket/pages/SummaryPage.html
new file mode 100644
index 00000000..3e85df99
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/SummaryPage.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+<body>
+<wicket:extend>
+
+ <div style="clear:both;">
+ <!-- Repository Activity Chart -->
+ <div class="hidden-phone" style="float:right;">
+ <img class="activityGraph" wicket:id="commitsChart" />
+ </div>
+
+ <!-- Repository info -->
+ <div class="hidden-phone" style="padding-bottom: 10px;">
+ <table class="plain">
+ <tr><th><wicket:message key="gb.description">[description]</wicket:message></th><td><span wicket:id="repositoryDescription">[repository description]</span></td></tr>
+ <tr><th><wicket:message key="gb.owners">[owner]</wicket:message></th><td><span wicket:id="repositoryOwners"><span wicket:id="owner"></span><span wicket:id="comma"></span></span></td></tr>
+ <tr><th><wicket:message key="gb.lastChange">[last change]</wicket:message></th><td><span wicket:id="repositoryLastChange">[repository last change]</span></td></tr>
+ <tr><th><wicket:message key="gb.stats">[stats]</wicket:message></th><td><span wicket:id="branchStats">[branch stats]</span> <span class="link"><a wicket:id="metrics"><wicket:message key="gb.metrics">[metrics]</wicket:message></a></span></td></tr>
+ <tr><th style="vertical-align:top;"><wicket:message key="gb.repositoryUrl">[URL]</wicket:message>&nbsp;<img style="vertical-align: top;padding-left:3px;" wicket:id="accessRestrictionIcon" /></th><td><span wicket:id="repositoryCloneUrl">[repository clone url]</span><div wicket:id="otherUrls"></div></td></tr>
+ </table>
+ </div>
+ </div>
+
+ <!-- commits -->
+ <div style="padding-bottom:15px;" wicket:id="commitsPanel">[commits panel]</div>
+
+ <!-- tags -->
+ <div style="padding-bottom:15px;" wicket:id="tagsPanel">[tags panel]</div>
+
+ <!-- branches -->
+ <div style="padding-bottom:15px;" wicket:id="branchesPanel">[branches panel]</div>
+
+ <!-- markdown readme -->
+ <div wicket:id="readme"></div>
+
+ <wicket:fragment wicket:id="markdownPanel">
+ <div class="header" style="margin-top:0px;" >
+ <i style="vertical-align: middle;" class="icon-book"></i>
+ <span style="font-weight:bold;vertical-align:middle;" wicket:id="readmeFile"></span>
+ </div>
+ <div style="border:1px solid #ddd;border-radius: 0 0 3px 3px;padding: 20px;">
+ <div wicket:id="readmeContent" class="markdown"></div>
+ </div>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="ownersFragment">
+
+ </wicket:fragment>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/SummaryPage.java b/src/main/java/com/gitblit/wicket/pages/SummaryPage.java
new file mode 100644
index 00000000..bd40a1b7
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/SummaryPage.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.text.MessageFormat;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.wicketstuff.googlecharts.Chart;
+import org.wicketstuff.googlecharts.ChartAxis;
+import org.wicketstuff.googlecharts.ChartAxisType;
+import org.wicketstuff.googlecharts.ChartProvider;
+import org.wicketstuff.googlecharts.ChartType;
+import org.wicketstuff.googlecharts.IChartData;
+import org.wicketstuff.googlecharts.LineStyle;
+import org.wicketstuff.googlecharts.MarkerType;
+import org.wicketstuff.googlecharts.ShapeMarker;
+
+import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.Metric;
+import com.gitblit.models.PathModel;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.MarkdownUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.BranchesPanel;
+import com.gitblit.wicket.panels.LinkPanel;
+import com.gitblit.wicket.panels.LogPanel;
+import com.gitblit.wicket.panels.RepositoryUrlPanel;
+import com.gitblit.wicket.panels.TagsPanel;
+
+public class SummaryPage extends RepositoryPage {
+
+ public SummaryPage(PageParameters params) {
+ super(params);
+
+ int numberCommits = GitBlit.getInteger(Keys.web.summaryCommitCount, 20);
+ if (numberCommits <= 0) {
+ numberCommits = 20;
+ }
+ int numberRefs = GitBlit.getInteger(Keys.web.summaryRefsCount, 5);
+
+ Repository r = getRepository();
+ RepositoryModel model = getRepositoryModel();
+
+ List<Metric> metrics = null;
+ Metric metricsTotal = null;
+ if (!model.skipSummaryMetrics && GitBlit.getBoolean(Keys.web.generateActivityGraph, true)) {
+ metrics = GitBlit.self().getRepositoryDefaultMetrics(model, r);
+ metricsTotal = metrics.remove(0);
+ }
+
+ addSyndicationDiscoveryLink();
+
+ // repository description
+ add(new Label("repositoryDescription", getRepositoryModel().description));
+
+ // owner links
+ final List<String> owners = new ArrayList<String>(getRepositoryModel().owners);
+ ListDataProvider<String> ownersDp = new ListDataProvider<String>(owners);
+ DataView<String> ownersView = new DataView<String>("repositoryOwners", ownersDp) {
+ private static final long serialVersionUID = 1L;
+ int counter = 0;
+ public void populateItem(final Item<String> item) {
+ UserModel ownerModel = GitBlit.self().getUserModel(item.getModelObject());
+ if (ownerModel != null) {
+ item.add(new LinkPanel("owner", null, ownerModel.getDisplayName(), UserPage.class,
+ WicketUtils.newUsernameParameter(ownerModel.username)).setRenderBodyOnly(true));
+ } else {
+ item.add(new Label("owner").setVisible(false));
+ }
+ counter++;
+ item.add(new Label("comma", ",").setVisible(counter < owners.size()));
+ item.setRenderBodyOnly(true);
+ }
+ };
+ ownersView.setRenderBodyOnly(true);
+ add(ownersView);
+
+ add(WicketUtils.createTimestampLabel("repositoryLastChange",
+ JGitUtils.getLastChange(r), getTimeZone(), getTimeUtils()));
+ if (metricsTotal == null) {
+ add(new Label("branchStats", ""));
+ } else {
+ add(new Label("branchStats",
+ MessageFormat.format(getString("gb.branchStats"), metricsTotal.count,
+ metricsTotal.tag, getTimeUtils().duration(metricsTotal.duration))));
+ }
+ add(new BookmarkablePageLink<Void>("metrics", MetricsPage.class,
+ WicketUtils.newRepositoryParameter(repositoryName)));
+
+ List<String> repositoryUrls = new ArrayList<String>();
+
+ if (GitBlit.getBoolean(Keys.git.enableGitServlet, true)) {
+ AccessRestrictionType accessRestriction = getRepositoryModel().accessRestriction;
+ switch (accessRestriction) {
+ case NONE:
+ add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false));
+ break;
+ case PUSH:
+ add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png",
+ getAccessRestrictions().get(accessRestriction)));
+ break;
+ case CLONE:
+ add(WicketUtils.newImage("accessRestrictionIcon", "lock_pull_16x16.png",
+ getAccessRestrictions().get(accessRestriction)));
+ break;
+ case VIEW:
+ add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png",
+ getAccessRestrictions().get(accessRestriction)));
+ break;
+ default:
+ add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false));
+ }
+ // add the Gitblit repository url
+ repositoryUrls.add(getRepositoryUrl(getRepositoryModel()));
+ } else {
+ add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false));
+ }
+ repositoryUrls.addAll(GitBlit.self().getOtherCloneUrls(repositoryName));
+
+ String primaryUrl = ArrayUtils.isEmpty(repositoryUrls) ? "" : repositoryUrls.remove(0);
+ add(new RepositoryUrlPanel("repositoryCloneUrl", primaryUrl));
+
+ add(new Label("otherUrls", StringUtils.flattenStrings(repositoryUrls, "<br/>"))
+ .setEscapeModelStrings(false));
+
+ add(new LogPanel("commitsPanel", repositoryName, getRepositoryModel().HEAD, r, numberCommits, 0, getRepositoryModel().showRemoteBranches));
+ add(new TagsPanel("tagsPanel", repositoryName, r, numberRefs).hideIfEmpty());
+ add(new BranchesPanel("branchesPanel", getRepositoryModel(), r, numberRefs, false).hideIfEmpty());
+
+ if (getRepositoryModel().showReadme) {
+ String htmlText = null;
+ String markdownText = null;
+ String readme = null;
+ try {
+ RevCommit head = JGitUtils.getCommit(r, null);
+ List<String> markdownExtensions = GitBlit.getStrings(Keys.web.markdownExtensions);
+ List<PathModel> paths = JGitUtils.getFilesInPath(r, null, head);
+ for (PathModel path : paths) {
+ if (!path.isTree()) {
+ String name = path.name.toLowerCase();
+
+ if (name.startsWith("readme")) {
+ if (name.indexOf('.') > -1) {
+ String ext = name.substring(name.lastIndexOf('.') + 1);
+ if (markdownExtensions.contains(ext)) {
+ readme = path.name;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!StringUtils.isEmpty(readme)) {
+ String [] encodings = GitBlit.getEncodings();
+ markdownText = JGitUtils.getStringContent(r, head.getTree(), readme, encodings);
+ htmlText = MarkdownUtils.transformMarkdown(markdownText);
+ }
+ } catch (ParseException p) {
+ markdownText = MessageFormat.format("<div class=\"alert alert-error\"><strong>{0}:</strong> {1}</div>{2}", getString("gb.error"), getString("gb.markdownFailure"), markdownText);
+ htmlText = StringUtils.breakLinesForHtml(markdownText);
+ }
+ Fragment fragment = new Fragment("readme", "markdownPanel");
+ fragment.add(new Label("readmeFile", readme));
+ // Add the html to the page
+ Component content = new Label("readmeContent", htmlText).setEscapeModelStrings(false);
+ fragment.add(content.setVisible(!StringUtils.isEmpty(htmlText)));
+ add(fragment);
+ } else {
+ add(new Label("readme").setVisible(false));
+ }
+
+ // Display an activity line graph
+ insertActivityGraph(metrics);
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.summary");
+ }
+
+ private void insertActivityGraph(List<Metric> metrics) {
+ if ((metrics != null) && (metrics.size() > 0)
+ && GitBlit.getBoolean(Keys.web.generateActivityGraph, true)) {
+ IChartData data = WicketUtils.getChartData(metrics);
+
+ ChartProvider provider = new ChartProvider(new Dimension(290, 100), ChartType.LINE,
+ data);
+ ChartAxis dateAxis = new ChartAxis(ChartAxisType.BOTTOM);
+ dateAxis.setLabels(new String[] { metrics.get(0).name,
+ metrics.get(metrics.size() / 2).name, metrics.get(metrics.size() - 1).name });
+ provider.addAxis(dateAxis);
+
+ ChartAxis commitAxis = new ChartAxis(ChartAxisType.LEFT);
+ commitAxis.setLabels(new String[] { "",
+ String.valueOf((int) WicketUtils.maxValue(metrics)) });
+ provider.addAxis(commitAxis);
+ provider.setLineStyles(new LineStyle[] { new LineStyle(2, 4, 0), new LineStyle(0, 4, 1) });
+ provider.addShapeMarker(new ShapeMarker(MarkerType.CIRCLE, Color.BLUE, 1, -1, 5));
+
+ add(new Chart("commitsChart", provider));
+ } else {
+ add(WicketUtils.newBlankImage("commitsChart"));
+ }
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/TagPage.html b/src/main/java/com/gitblit/wicket/pages/TagPage.html
new file mode 100644
index 00000000..19f828e3
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/TagPage.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <!-- summary header -->
+ <div style="margin-top: 5px;" class="header" wicket:id="commit">[shortlog header]</div>
+
+ <!-- Tagger Gravatar -->
+ <span style="float:right;vertical-align: top;" wicket:id="taggerAvatar" />
+
+ <!-- commit info -->
+ <table class="plain">
+ <tr><th><wicket:message key="gb.name">[name]</wicket:message></th><td><span wicket:id="tagName">[tag name]</span></td></tr>
+ <tr><th><wicket:message key="gb.tag">[tag]</wicket:message></th><td><span class="sha1" wicket:id="tagId">[tag id]</span></td></tr>
+ <tr><th><wicket:message key="gb.object">[object]</wicket:message></th><td><span class="sha1" wicket:id="taggedObject">[tagged object]</span> <span class="link" wicket:id="taggedObjectType"></span></td></tr>
+ <tr><th><wicket:message key="gb.tagger">[tagger]</wicket:message></th><td><span class="sha1" wicket:id="tagger">[tagger]</span></td></tr>
+ <tr><th></th><td><span class="sha1" wicket:id="tagDate">[tag date]</span></td></tr>
+ </table>
+
+ <!-- full message -->
+ <div style="border-bottom:0px;" class="commit_message" wicket:id="fullMessage">[tag full message]</div>
+
+ <wicket:fragment wicket:id="fullPersonIdent">
+ <span wicket:id="personName"></span><span wicket:id="personAddress"></span>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="partialPersonIdent">
+ <span wicket:id="personName"></span>
+ </wicket:fragment>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/TagPage.java b/src/main/java/com/gitblit/wicket/pages/TagPage.java
new file mode 100644
index 00000000..91c913d2
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/TagPage.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+
+import com.gitblit.models.RefModel;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.GravatarImage;
+import com.gitblit.wicket.panels.LinkPanel;
+import com.gitblit.wicket.panels.RefsPanel;
+
+public class TagPage extends RepositoryPage {
+
+ public TagPage(PageParameters params) {
+ super(params);
+
+ Repository r = getRepository();
+
+ // Find tag in repository
+ List<RefModel> tags = JGitUtils.getTags(r, true, -1);
+ RefModel tagRef = null;
+ for (RefModel tag : tags) {
+ if (tag.getName().equals(objectId) || tag.getObjectId().getName().equals(objectId)) {
+ tagRef = tag;
+ break;
+ }
+ }
+
+ // Failed to find tag!
+ if (tagRef == null) {
+ error(MessageFormat.format(getString("gb.couldNotFindTag"), objectId), true);
+ }
+
+ // Display tag.
+ Class<? extends RepositoryPage> linkClass;
+ PageParameters linkParameters = newCommitParameter(tagRef.getReferencedObjectId().getName());
+ String typeKey;
+ switch (tagRef.getReferencedObjectType()) {
+ case Constants.OBJ_BLOB:
+ typeKey = "gb.blob";
+ linkClass = BlobPage.class;
+ break;
+ case Constants.OBJ_TREE:
+ typeKey = "gb.tree";
+ linkClass = TreePage.class;
+ break;
+ case Constants.OBJ_COMMIT:
+ default:
+ typeKey = "gb.commit";
+ linkClass = CommitPage.class;
+ break;
+ }
+ add(new LinkPanel("commit", "title", tagRef.displayName, linkClass, linkParameters));
+ add(new GravatarImage("taggerAvatar", tagRef.getAuthorIdent()));
+
+ add(new RefsPanel("tagName", repositoryName, Arrays.asList(tagRef)));
+ add(new Label("tagId", tagRef.getObjectId().getName()));
+ add(new LinkPanel("taggedObject", "list", tagRef.getReferencedObjectId().getName(),
+ linkClass, linkParameters));
+ add(new Label("taggedObjectType", getString(typeKey)));
+
+ add(createPersonPanel("tagger", tagRef.getAuthorIdent(), com.gitblit.Constants.SearchType.AUTHOR));
+ Date when = new Date(0);
+ if (tagRef.getAuthorIdent() != null) {
+ when = tagRef.getAuthorIdent().getWhen();
+ }
+ add(WicketUtils.createTimestampLabel("tagDate", when, getTimeZone(), getTimeUtils()));
+
+ addFullText("fullMessage", tagRef.getFullMessage(), true);
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.tag");
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/TagsPage.html b/src/main/java/com/gitblit/wicket/pages/TagsPage.html
new file mode 100644
index 00000000..03f1a0d7
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/TagsPage.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <!-- tags panel -->
+ <div style="margin-top:5px;" wicket:id="tagsPanel">[tags panel]</div>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/TagsPage.java b/src/main/java/com/gitblit/wicket/pages/TagsPage.java
new file mode 100644
index 00000000..3ddbde9b
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/TagsPage.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import org.apache.wicket.PageParameters;
+
+import com.gitblit.wicket.panels.TagsPanel;
+
+public class TagsPage extends RepositoryPage {
+
+ public TagsPage(PageParameters params) {
+ super(params);
+
+ add(new TagsPanel("tagsPanel", repositoryName, getRepository(), -1));
+
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.tags");
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketPage.html b/src/main/java/com/gitblit/wicket/pages/TicketPage.html
new file mode 100644
index 00000000..ed3eb229
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/TicketPage.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <!-- ticket title -->
+ <div style="font-size:150%;padding-top:5px;padding-bottom:5px;" wicket:id="ticketTitle">[ticket title]</div>
+
+ <!-- ticket info -->
+ <table class="plain">
+ <tr><th><wicket:message key="gb.ticketId">ticket id</wicket:message></th><td><span class="sha1" wicket:id="ticketId">[ticket id]</span></td></tr>
+ <tr><th><wicket:message key="gb.ticketAssigned">assigned</wicket:message></th><td><span wicket:id=ticketHandler>[ticket title]</span></td></tr>
+ <tr><th><wicket:message key="gb.ticketOpenDate">open date</wicket:message></th><td><span wicket:id="ticketOpenDate">[ticket open date]</span></td></tr>
+ <tr><th><wicket:message key="gb.ticketState">state</wicket:message></th><td><span wicket:id="ticketState">[ticket state]</span></td></tr>
+ <tr><th><wicket:message key="gb.tags">tags</wicket:message></th><td><span wicket:id="ticketTags">[ticket tags]</span></td></tr>
+ </table>
+
+ <!-- comments header -->
+ <div class="header"><wicket:message key="gb.ticketComments">comments</wicket:message></div>
+
+ <!-- comments -->
+ <table class="comments">
+ <tbody>
+ <tr valign="top" wicket:id="comment">
+ <td><span class="author" wicket:id="commentAuthor">[comment author]</span><br/>
+ <span class="date" wicket:id="commentDate">[comment date]</span>
+ </td>
+ <td><span wicket:id="commentText">[comment text]</span></td>
+ </tr>
+ </tbody>
+ </table>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketPage.java b/src/main/java/com/gitblit/wicket/pages/TicketPage.java
new file mode 100644
index 00000000..57233867
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/TicketPage.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+import org.eclipse.jgit.lib.Repository;
+
+import com.gitblit.models.TicketModel;
+import com.gitblit.models.TicketModel.Comment;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.utils.TicgitUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.WicketUtils;
+
+public class TicketPage extends RepositoryPage {
+
+ public TicketPage(PageParameters params) {
+ super(params);
+
+ final String ticketFolder = WicketUtils.getPath(params);
+
+ Repository r = getRepository();
+ TicketModel t = TicgitUtils.getTicket(r, ticketFolder);
+
+ add(new Label("ticketTitle", t.title));
+ add(new Label("ticketId", t.id));
+ add(new Label("ticketHandler", t.handler.toLowerCase()));
+ add(WicketUtils.createTimestampLabel("ticketOpenDate", t.date, getTimeZone(), getTimeUtils()));
+ Label stateLabel = new Label("ticketState", t.state);
+ WicketUtils.setTicketCssClass(stateLabel, t.state);
+ add(stateLabel);
+ add(new Label("ticketTags", StringUtils.flattenStrings(t.tags)));
+
+ ListDataProvider<Comment> commentsDp = new ListDataProvider<Comment>(t.comments);
+ DataView<Comment> commentsView = new DataView<Comment>("comment", commentsDp) {
+ private static final long serialVersionUID = 1L;
+ int counter;
+
+ public void populateItem(final Item<Comment> item) {
+ final Comment entry = item.getModelObject();
+ item.add(WicketUtils.createDateLabel("commentDate", entry.date, GitBlitWebSession
+ .get().getTimezone(), getTimeUtils()));
+ item.add(new Label("commentAuthor", entry.author.toLowerCase()));
+ item.add(new Label("commentText", prepareComment(entry.text))
+ .setEscapeModelStrings(false));
+ WicketUtils.setAlternatingBackground(item, counter);
+ counter++;
+ }
+ };
+ add(commentsView);
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.ticket");
+ }
+
+ private String prepareComment(String comment) {
+ String html = StringUtils.escapeForHtml(comment, false);
+ html = StringUtils.breakLinesForHtml(comment).trim();
+ return html.replaceAll("\\bcommit\\s*([A-Za-z0-9]*)\\b", "<a href=\"/commit/"
+ + repositoryName + "/$1\">commit $1</a>");
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketsPage.html b/src/main/java/com/gitblit/wicket/pages/TicketsPage.html
new file mode 100644
index 00000000..0913dc2c
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/TicketsPage.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <!-- header -->
+ <div style="margin-top:5px;" class="header" wicket:id="header">[header]</div>
+
+ <!-- tickets -->
+ <table class="pretty">
+ <tbody>
+ <tr wicket:id="ticket">
+ <td style="padding:0; margin:0;"><div wicket:id="ticketState">[ticket state]</div></td>
+ <td class="date"><span wicket:id="ticketDate">[ticket date]</span></td>
+ <td class="author"><div wicket:id="ticketHandler">[ticket handler]</div></td>
+ <td><div wicket:id="ticketTitle">[ticket title]</div></td>
+ </tr>
+ </tbody>
+ </table>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketsPage.java b/src/main/java/com/gitblit/wicket/pages/TicketsPage.java
new file mode 100644
index 00000000..b68b7e42
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/TicketsPage.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+
+import com.gitblit.models.TicketModel;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.utils.TicgitUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.LinkPanel;
+
+public class TicketsPage extends RepositoryPage {
+
+ public TicketsPage(PageParameters params) {
+ super(params);
+
+ List<TicketModel> tickets = TicgitUtils.getTickets(getRepository());
+
+ // header
+ add(new LinkPanel("header", "title", repositoryName, SummaryPage.class,
+ newRepositoryParameter()));
+
+ ListDataProvider<TicketModel> ticketsDp = new ListDataProvider<TicketModel>(tickets);
+ DataView<TicketModel> ticketsView = new DataView<TicketModel>("ticket", ticketsDp) {
+ private static final long serialVersionUID = 1L;
+ int counter;
+
+ public void populateItem(final Item<TicketModel> item) {
+ final TicketModel entry = item.getModelObject();
+ Label stateLabel = new Label("ticketState", entry.state);
+ WicketUtils.setTicketCssClass(stateLabel, entry.state);
+ item.add(stateLabel);
+ item.add(WicketUtils.createDateLabel("ticketDate", entry.date, GitBlitWebSession
+ .get().getTimezone(), getTimeUtils()));
+ item.add(new Label("ticketHandler", StringUtils.trimString(
+ entry.handler.toLowerCase(), 30)));
+ item.add(new LinkPanel("ticketTitle", "list subject", StringUtils.trimString(
+ entry.title, 80), TicketPage.class, newPathParameter(entry.name)));
+
+ WicketUtils.setAlternatingBackground(item, counter);
+ counter++;
+ }
+ };
+ add(ticketsView);
+ }
+
+ protected PageParameters newPathParameter(String path) {
+ return WicketUtils.newPathParameter(repositoryName, objectId, path);
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.tickets");
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/TreePage.html b/src/main/java/com/gitblit/wicket/pages/TreePage.html
new file mode 100644
index 00000000..b7e55ed6
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/TreePage.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <!-- blob nav links -->
+ <div class="page_nav2">
+ <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="headLink"><wicket:message key="gb.head"></wicket:message></a> | <span wicket:id="compressedLinks"></span>
+ </div>
+
+ <!-- commit header -->
+ <div wicket:id="commitHeader">[commit header]</div>
+
+ <!-- breadcrumbs -->
+ <div wicket:id="breadcrumbs">[breadcrumbs]</div>
+
+ <!-- changed paths -->
+ <table style="width:100%" class="pretty">
+ <tr wicket:id="changedPath">
+ <td class="icon"><img wicket:id="pathIcon" /></td>
+ <td><span wicket:id="pathName"></span></td>
+ <td class="hidden-phone size"><span wicket:id="pathSize">[path size]</span></td>
+ <td class="hidden-phone mode"><span wicket:id="pathPermissions">[path permissions]</span></td>
+ <td class="treeLinks"><span wicket:id="pathLinks">[path links]</span></td>
+ </tr>
+ </table>
+
+ <!-- submodule links -->
+ <wicket:fragment wicket:id="submoduleLinks">
+ <span class="link">
+ <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <span class="hidden-phone"><a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | </span><a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a> | <span wicket:id="compressedLinks"></span>
+ </span>
+ </wicket:fragment>
+
+ <!-- tree links -->
+ <wicket:fragment wicket:id="treeLinks">
+ <span class="link">
+ <span class="hidden-phone"><a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | </span><a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a> | <span wicket:id="compressedLinks"></span>
+ </span>
+ </wicket:fragment>
+
+ <!-- blob links -->
+ <wicket:fragment wicket:id="blobLinks">
+ <span class="link">
+ <span class="hidden-phone"><a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="raw"><wicket:message key="gb.raw"></wicket:message></a> | <a wicket:id="blame"><wicket:message key="gb.blame"></wicket:message></a> | </span> <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a>
+ </span>
+ </wicket:fragment>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/TreePage.java b/src/main/java/com/gitblit/wicket/pages/TreePage.java
new file mode 100644
index 00000000..bc27f0c2
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/TreePage.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import com.gitblit.models.PathModel;
+import com.gitblit.models.SubmoduleModel;
+import com.gitblit.utils.ByteFormat;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.CommitHeaderPanel;
+import com.gitblit.wicket.panels.CompressedDownloadsPanel;
+import com.gitblit.wicket.panels.LinkPanel;
+import com.gitblit.wicket.panels.PathBreadcrumbsPanel;
+
+public class TreePage extends RepositoryPage {
+
+ public TreePage(PageParameters params) {
+ super(params);
+
+ final String path = WicketUtils.getPath(params);
+
+ Repository r = getRepository();
+ RevCommit commit = getCommit();
+ List<PathModel> paths = JGitUtils.getFilesInPath(r, path, commit);
+
+ // tree page links
+ add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, path)));
+ add(new BookmarkablePageLink<Void>("headLink", TreePage.class,
+ WicketUtils.newPathParameter(repositoryName, Constants.HEAD, path)));
+ add(new CompressedDownloadsPanel("compressedLinks", getRequest()
+ .getRelativePathPrefixToContextRoot(), repositoryName, objectId, path));
+
+ add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
+
+ // breadcrumbs
+ add(new PathBreadcrumbsPanel("breadcrumbs", repositoryName, path, objectId));
+ if (path != null && path.trim().length() > 0) {
+ // add .. parent path entry
+ String parentPath = null;
+ if (path.lastIndexOf('/') > -1) {
+ parentPath = path.substring(0, path.lastIndexOf('/'));
+ }
+ PathModel model = new PathModel("..", parentPath, 0, FileMode.TREE.getBits(), null, objectId);
+ model.isParentPath = true;
+ paths.add(0, model);
+ }
+
+ final ByteFormat byteFormat = new ByteFormat();
+
+ final String baseUrl = WicketUtils.getGitblitURL(getRequest());
+
+ // changed paths list
+ ListDataProvider<PathModel> pathsDp = new ListDataProvider<PathModel>(paths);
+ DataView<PathModel> pathsView = new DataView<PathModel>("changedPath", pathsDp) {
+ private static final long serialVersionUID = 1L;
+ int counter;
+
+ public void populateItem(final Item<PathModel> item) {
+ PathModel entry = item.getModelObject();
+ item.add(new Label("pathPermissions", JGitUtils.getPermissionsFromMode(entry.mode)));
+ if (entry.isParentPath) {
+ // parent .. path
+ item.add(WicketUtils.newBlankImage("pathIcon"));
+ item.add(new Label("pathSize", ""));
+ item.add(new LinkPanel("pathName", null, entry.name, TreePage.class,
+ WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, entry.path)));
+ item.add(new Label("pathLinks", ""));
+ } else {
+ if (entry.isTree()) {
+ // folder/tree link
+ item.add(WicketUtils.newImage("pathIcon", "folder_16x16.png"));
+ item.add(new Label("pathSize", ""));
+ item.add(new LinkPanel("pathName", "list", entry.name, TreePage.class,
+ WicketUtils.newPathParameter(repositoryName, entry.commitId,
+ entry.path)));
+
+ // links
+ Fragment links = new Fragment("pathLinks", "treeLinks", this);
+ links.add(new BookmarkablePageLink<Void>("tree", TreePage.class,
+ WicketUtils.newPathParameter(repositoryName, entry.commitId,
+ entry.path)));
+ links.add(new BookmarkablePageLink<Void>("history", HistoryPage.class,
+ WicketUtils.newPathParameter(repositoryName, entry.commitId,
+ entry.path)));
+ links.add(new CompressedDownloadsPanel("compressedLinks", baseUrl,
+ repositoryName, objectId, entry.path));
+
+ item.add(links);
+ } else if (entry.isSubmodule()) {
+ // submodule
+ String submoduleId = entry.objectId;
+ String submodulePath;
+ boolean hasSubmodule = false;
+ SubmoduleModel submodule = getSubmodule(entry.path);
+ submodulePath = submodule.gitblitPath;
+ hasSubmodule = submodule.hasSubmodule;
+
+ item.add(WicketUtils.newImage("pathIcon", "git-orange-16x16.png"));
+ item.add(new Label("pathSize", ""));
+ item.add(new LinkPanel("pathName", "list", entry.name + " @ " +
+ getShortObjectId(submoduleId), TreePage.class,
+ WicketUtils.newPathParameter(submodulePath, submoduleId, "")).setEnabled(hasSubmodule));
+
+ Fragment links = new Fragment("pathLinks", "submoduleLinks", this);
+ links.add(new BookmarkablePageLink<Void>("view", SummaryPage.class,
+ WicketUtils.newRepositoryParameter(submodulePath)).setEnabled(hasSubmodule));
+ links.add(new BookmarkablePageLink<Void>("tree", TreePage.class,
+ WicketUtils.newPathParameter(submodulePath, submoduleId,
+ "")).setEnabled(hasSubmodule));
+ links.add(new BookmarkablePageLink<Void>("history", HistoryPage.class,
+ WicketUtils.newPathParameter(repositoryName, entry.commitId,
+ entry.path)));
+ links.add(new CompressedDownloadsPanel("compressedLinks", baseUrl,
+ submodulePath, submoduleId, "").setEnabled(hasSubmodule));
+ item.add(links);
+ } else {
+ // blob link
+ String displayPath = entry.name;
+ String path = entry.path;
+ if (entry.isSymlink()) {
+ path = JGitUtils.getStringContent(getRepository(), getCommit().getTree(), path);
+ displayPath = entry.name + " -> " + path;
+ }
+ item.add(WicketUtils.getFileImage("pathIcon", entry.name));
+ item.add(new Label("pathSize", byteFormat.format(entry.size)));
+ item.add(new LinkPanel("pathName", "list", displayPath, BlobPage.class,
+ WicketUtils.newPathParameter(repositoryName, entry.commitId,
+ path)));
+
+ // links
+ Fragment links = new Fragment("pathLinks", "blobLinks", this);
+ links.add(new BookmarkablePageLink<Void>("view", BlobPage.class,
+ WicketUtils.newPathParameter(repositoryName, entry.commitId,
+ path)));
+ links.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils
+ .newPathParameter(repositoryName, entry.commitId, path)));
+ links.add(new BookmarkablePageLink<Void>("blame", BlamePage.class,
+ WicketUtils.newPathParameter(repositoryName, entry.commitId,
+ path)));
+ links.add(new BookmarkablePageLink<Void>("history", HistoryPage.class,
+ WicketUtils.newPathParameter(repositoryName, entry.commitId,
+ path)));
+ item.add(links);
+ }
+ }
+ WicketUtils.setAlternatingBackground(item, counter);
+ counter++;
+ }
+ };
+ add(pathsView);
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.tree");
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/UserPage.html b/src/main/java/com/gitblit/wicket/pages/UserPage.html
new file mode 100644
index 00000000..c7131c09
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/UserPage.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:extend>
+
+ <div class="row">
+ <div class="span4">
+ <div wicket:id="gravatar"></div>
+ <div style="text-align: left;">
+ <h2><span wicket:id="userDisplayName"></span></h2>
+ <div><i class="icon-user"></i> <span wicket:id="userUsername"></span></div>
+ <div><i class="icon-envelope"></i><span wicket:id="userEmail"></span></div>
+ </div>
+ </div>
+
+ <div class="span8">
+ <div class="pull-right">
+ <a class="btn-small" wicket:id="newRepository" style="padding-right:0px;">
+ <i class="icon icon-plus-sign"></i>
+ <wicket:message key="gb.newRepository"></wicket:message>
+ </a>
+ </div>
+ <div class="tabbable">
+ <!-- tab titles -->
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="#repositories" data-toggle="tab"><wicket:message key="gb.repositories"></wicket:message></a></li>
+ </ul>
+
+ <!-- tab content -->
+ <div class="tab-content">
+
+ <!-- repositories tab -->
+ <div class="tab-pane active" id="repositories">
+ <table width="100%">
+ <tbody>
+ <tr wicket:id="repositoryList"><td style="border-bottom:1px solid #eee;"><span wicket:id="repository"></span></td></tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/UserPage.java b/src/main/java/com/gitblit/wicket/pages/UserPage.java
new file mode 100644
index 00000000..f4331dd1
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/UserPage.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2012 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+import org.eclipse.jgit.lib.PersonIdent;
+
+import com.gitblit.GitBlit;
+import com.gitblit.Keys;
+import com.gitblit.models.ProjectModel;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebApp;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.GitblitRedirectException;
+import com.gitblit.wicket.PageRegistration;
+import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
+import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.GravatarImage;
+import com.gitblit.wicket.panels.LinkPanel;
+import com.gitblit.wicket.panels.ProjectRepositoryPanel;
+
+public class UserPage extends RootPage {
+
+ List<ProjectModel> projectModels = new ArrayList<ProjectModel>();
+
+ public UserPage() {
+ super();
+ throw new GitblitRedirectException(GitBlitWebApp.get().getHomePage());
+ }
+
+ public UserPage(PageParameters params) {
+ super(params);
+ setup(params);
+ }
+
+ @Override
+ protected boolean reusePageParameters() {
+ return true;
+ }
+
+ private void setup(PageParameters params) {
+ setupPage("", "");
+ // check to see if we should display a login message
+ boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, true);
+ if (authenticateView && !GitBlitWebSession.get().isLoggedIn()) {
+ authenticationError("Please login");
+ return;
+ }
+
+ String userName = WicketUtils.getUsername(params);
+ if (StringUtils.isEmpty(userName)) {
+ throw new GitblitRedirectException(GitBlitWebApp.get().getHomePage());
+ }
+
+ UserModel user = GitBlit.self().getUserModel(userName);
+ if (user == null) {
+ // construct a temporary user model
+ user = new UserModel(userName);
+ }
+
+ String projectName = "~" + userName;
+
+ ProjectModel project = GitBlit.self().getProjectModel(projectName);
+ if (project == null) {
+ project = new ProjectModel(projectName);
+ }
+
+ add(new Label("userDisplayName", user.getDisplayName()));
+ add(new Label("userUsername", user.username));
+ LinkPanel email = new LinkPanel("userEmail", null, user.emailAddress, "mailto:#");
+ email.setRenderBodyOnly(true);
+ add(email.setVisible(GitBlit.getBoolean(Keys.web.showEmailAddresses, true) && !StringUtils.isEmpty(user.emailAddress)));
+
+ PersonIdent person = new PersonIdent(user.getDisplayName(), user.emailAddress == null ? user.getDisplayName() : user.emailAddress);
+ add(new GravatarImage("gravatar", person, 210));
+
+ UserModel sessionUser = GitBlitWebSession.get().getUser();
+ if (sessionUser != null && user.canCreate() && sessionUser.equals(user)) {
+ // user can create personal repositories
+ add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class));
+ } else {
+ add(new Label("newRepository").setVisible(false));
+ }
+
+ List<RepositoryModel> repositories = getRepositories(params);
+
+ Collections.sort(repositories, new Comparator<RepositoryModel>() {
+ @Override
+ public int compare(RepositoryModel o1, RepositoryModel o2) {
+ // reverse-chronological sort
+ return o2.lastChange.compareTo(o1.lastChange);
+ }
+ });
+
+ final ListDataProvider<RepositoryModel> dp = new ListDataProvider<RepositoryModel>(repositories);
+ DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("repositoryList", dp) {
+ private static final long serialVersionUID = 1L;
+
+ public void populateItem(final Item<RepositoryModel> item) {
+ final RepositoryModel entry = item.getModelObject();
+
+ ProjectRepositoryPanel row = new ProjectRepositoryPanel("repository",
+ getLocalizer(), this, showAdmin, entry, getAccessRestrictions());
+ item.add(row);
+ }
+ };
+ add(dataView);
+ }
+
+ @Override
+ protected void addDropDownMenus(List<PageRegistration> pages) {
+ PageParameters params = getPageParameters();
+
+ DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",
+ UserPage.class);
+ // preserve time filter option on repository choices
+ menu.menuItems.addAll(getRepositoryFilterItems(params));
+
+ // preserve repository filter option on time choices
+ menu.menuItems.addAll(getTimeFilterItems(params));
+
+ if (menu.menuItems.size() > 0) {
+ // Reset Filter
+ menu.menuItems.add(new DropDownMenuItem(getString("gb.reset"), null, null));
+ }
+
+ pages.add(menu);
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/UsersPage.html b/src/main/java/com/gitblit/wicket/pages/UsersPage.html
new file mode 100644
index 00000000..edb85f7d
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/UsersPage.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+<body>
+<wicket:extend>
+ <div wicket:id="teamsPanel">[teams panel]</div>
+
+ <div wicket:id="usersPanel">[users panel]</div>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/UsersPage.java b/src/main/java/com/gitblit/wicket/pages/UsersPage.java
new file mode 100644
index 00000000..9526deae
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/UsersPage.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import com.gitblit.wicket.RequiresAdminRole;
+import com.gitblit.wicket.panels.TeamsPanel;
+import com.gitblit.wicket.panels.UsersPanel;
+
+@RequiresAdminRole
+public class UsersPage extends RootPage {
+
+ public UsersPage() {
+ super();
+ setupPage("", "");
+
+ add(new TeamsPanel("teamsPanel", showAdmin).setVisible(showAdmin));
+
+ add(new UsersPanel("usersPanel", showAdmin).setVisible(showAdmin));
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/prettify/lang-apollo.js b/src/main/java/com/gitblit/wicket/pages/prettify/lang-apollo.js
new file mode 100644
index 00000000..bfc0014c
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/prettify/lang-apollo.js
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["com",/^#[^\r\n]*/,null,"#"],["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"']],[["kwd",/^(?:ADS|AD|AUG|BZF|BZMF|CAE|CAF|CA|CCS|COM|CS|DAS|DCA|DCOM|DCS|DDOUBL|DIM|DOUBLE|DTCB|DTCF|DV|DXCH|EDRUPT|EXTEND|INCR|INDEX|NDX|INHINT|LXCH|MASK|MSK|MP|MSU|NOOP|OVSK|QXCH|RAND|READ|RELINT|RESUME|RETURN|ROR|RXOR|SQUARE|SU|TCR|TCAA|OVSK|TCF|TC|TS|WAND|WOR|WRITE|XCH|XLQ|XXALQ|ZL|ZQ|ADD|ADZ|SUB|SUZ|MPY|MPR|MPZ|DVP|COM|ABS|CLA|CLZ|LDQ|STO|STQ|ALS|LLS|LRS|TRA|TSQ|TMI|TOV|AXT|TIX|DLY|INP|OUT)\s/,
+null],["typ",/^(?:-?GENADR|=MINUS|2BCADR|VN|BOF|MM|-?2CADR|-?[1-6]DNADR|ADRES|BBCON|[SE]?BANK\=?|BLOCK|BNKSUM|E?CADR|COUNT\*?|2?DEC\*?|-?DNCHAN|-?DNPTR|EQUALS|ERASE|MEMORY|2?OCT|REMADR|SETLOC|SUBRO|ORG|BSS|BES|SYN|EQU|DEFINE|END)\s/,null],["lit",/^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/],["pln",/^-*(?:[!-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i],["pun",/^[^\w\t\n\r \xA0()\"\\\';]+/]]),["apollo","agc","aea"]) \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/prettify/lang-css.js b/src/main/java/com/gitblit/wicket/pages/prettify/lang-css.js
new file mode 100644
index 00000000..61157f38
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/prettify/lang-css.js
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[ \t\r\n\f]+/,null," \t\r\n\u000c"]],[["str",/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],["str",/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],["kwd",/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],
+["com",/^(?:<!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#(?:[0-9a-f]{3}){1,2}/i],["pln",/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],["pun",/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^\)\"\']+/]]),["css-str"]) \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/prettify/lang-hs.js b/src/main/java/com/gitblit/wicket/pages/prettify/lang-hs.js
new file mode 100644
index 00000000..00cea7cf
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/prettify/lang-hs.js
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\x0B\x0C\r ]+/,null,"\t\n\u000b\u000c\r "],["str",/^\"(?:[^\"\\\n\x0C\r]|\\[\s\S])*(?:\"|$)/,null,'"'],["str",/^\'(?:[^\'\\\n\x0C\r]|\\[^&])\'?/,null,"'"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+\-]?\d+)?)/i,null,"0123456789"]],[["com",/^(?:(?:--+(?:[^\r\n\x0C]*)?)|(?:\{-(?:[^-]|-+[^-\}])*-\}))/],["kwd",/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^a-zA-Z0-9\']|$)/,
+null],["pln",/^(?:[A-Z][\w\']*\.)*[a-zA-Z][\w\']*/],["pun",/^[^\t\n\x0B\x0C\r a-zA-Z0-9\'\"]+/]]),["hs"]) \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/prettify/lang-lisp.js b/src/main/java/com/gitblit/wicket/pages/prettify/lang-lisp.js
new file mode 100644
index 00000000..fab992b8
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/prettify/lang-lisp.js
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(/,null,"("],["clo",/^\)/,null,")"],["com",/^;[^\r\n]*/,null,";"],["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,
+null],["lit",/^[+\-]?(?:0x[0-9a-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[ed][+\-]?\d+)?)/i],["lit",/^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/],["pln",/^-*(?:[a-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i],["pun",/^[^\w\t\n\r \xA0()\"\\\';]+/]]),["cl","el","lisp","scm"]) \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/prettify/lang-lua.js b/src/main/java/com/gitblit/wicket/pages/prettify/lang-lua.js
new file mode 100644
index 00000000..45d0ba28
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/prettify/lang-lua.js
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])*(?:\'|$))/,null,"\"'"]],[["com",/^--(?:\[(=*)\[[\s\S]*?(?:\]\1\]|$)|[^\r\n]*)/],["str",/^\[(=*)\[[\s\S]*?(?:\]\1\]|$)/],["kwd",/^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,null],["lit",/^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],
+["pln",/^[a-z_]\w*/i],["pun",/^[^\w\t\n\r \xA0][^\w\t\n\r \xA0\"\'\-\+=]*/]]),["lua"]) \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/prettify/lang-ml.js b/src/main/java/com/gitblit/wicket/pages/prettify/lang-ml.js
new file mode 100644
index 00000000..5879726e
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/prettify/lang-ml.js
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["com",/^#(?:if[\t\n\r \xA0]+(?:[a-z_$][\w\']*|``[^\r\n\t`]*(?:``|$))|else|endif|light)/i,null,"#"],["str",/^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])*(?:\'|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\r\n]*|\(\*[\s\S]*?\*\))/],["kwd",/^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\b/],
+["lit",/^[+\-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],["pln",/^(?:[a-z_]\w*[!?#]?|``[^\r\n\t`]*(?:``|$))/i],["pun",/^[^\t\n\r \xA0\"\'\w]+/]]),["fs","ml"]) \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/prettify/lang-proto.js b/src/main/java/com/gitblit/wicket/pages/prettify/lang-proto.js
new file mode 100644
index 00000000..f713420c
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/prettify/lang-proto.js
@@ -0,0 +1 @@
+PR.registerLangHandler(PR.sourceDecorator({keywords:"bool bytes default double enum extend extensions false fixed32 fixed64 float group import int32 int64 max message option optional package repeated required returns rpc service sfixed32 sfixed64 sint32 sint64 string syntax to true uint32 uint64",cStyleComments:true}),["proto"]) \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/prettify/lang-scala.js b/src/main/java/com/gitblit/wicket/pages/prettify/lang-scala.js
new file mode 100644
index 00000000..00f4e0c2
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/prettify/lang-scala.js
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^(?:"(?:(?:""(?:""?(?!")|[^\\"]|\\.)*"{0,3})|(?:[^"\r\n\\]|\\.)*"?))/,null,'"'],["lit",/^`(?:[^\r\n\\`]|\\.)*`?/,null,"`"],["pun",/^[!#%&()*+,\-:;<=>?@\[\\\]^{|}~]+/,null,"!#%&()*+,-:;<=>?@[\\]^{|}~"]],[["str",/^'(?:[^\r\n\\']|\\(?:'|[^\r\n']+))'/],["lit",/^'[a-zA-Z_$][\w$]*(?!['$\w])/],["kwd",/^(?:abstract|case|catch|class|def|do|else|extends|final|finally|for|forSome|if|implicit|import|lazy|match|new|object|override|package|private|protected|requires|return|sealed|super|throw|trait|try|type|val|var|while|with|yield)\b/],
+["lit",/^(?:true|false|null|this)\b/],["lit",/^(?:(?:0(?:[0-7]+|X[0-9A-F]+))L?|(?:(?:0|[1-9][0-9]*)(?:(?:\.[0-9]+)?(?:E[+\-]?[0-9]+)?F?|L?))|\\.[0-9]+(?:E[+\-]?[0-9]+)?F?)/i],["typ",/^[$_]*[A-Z][_$A-Z0-9]*[a-z][\w$]*/],["pln",/^[$a-zA-Z_][\w$]*/],["com",/^\/(?:\/.*|\*(?:\/|\**[^*/])*(?:\*+\/?)?)/],["pun",/^(?:\.+|\/)/]]),["scala"]) \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/prettify/lang-sql.js b/src/main/java/com/gitblit/wicket/pages/prettify/lang-sql.js
new file mode 100644
index 00000000..800b13ea
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/prettify/lang-sql.js
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^(?:"(?:[^\"\\]|\\.)*"|'(?:[^\'\\]|\\.)*')/,null,"\"'"]],[["com",/^(?:--[^\r\n]*|\/\*[\s\S]*?(?:\*\/|$))/],["kwd",/^(?:ADD|ALL|ALTER|AND|ANY|AS|ASC|AUTHORIZATION|BACKUP|BEGIN|BETWEEN|BREAK|BROWSE|BULK|BY|CASCADE|CASE|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMN|COMMIT|COMPUTE|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DBCC|DEALLOCATE|DECLARE|DEFAULT|DELETE|DENY|DESC|DISK|DISTINCT|DISTRIBUTED|DOUBLE|DROP|DUMMY|DUMP|ELSE|END|ERRLVL|ESCAPE|EXCEPT|EXEC|EXECUTE|EXISTS|EXIT|FETCH|FILE|FILLFACTOR|FOR|FOREIGN|FREETEXT|FREETEXTTABLE|FROM|FULL|FUNCTION|GOTO|GRANT|GROUP|HAVING|HOLDLOCK|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IN|INDEX|INNER|INSERT|INTERSECT|INTO|IS|JOIN|KEY|KILL|LEFT|LIKE|LINENO|LOAD|NATIONAL|NOCHECK|NONCLUSTERED|NOT|NULL|NULLIF|OF|OFF|OFFSETS|ON|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTION|OR|ORDER|OUTER|OVER|PERCENT|PLAN|PRECISION|PRIMARY|PRINT|PROC|PROCEDURE|PUBLIC|RAISERROR|READ|READTEXT|RECONFIGURE|REFERENCES|REPLICATION|RESTORE|RESTRICT|RETURN|REVOKE|RIGHT|ROLLBACK|ROWCOUNT|ROWGUIDCOL|RULE|SAVE|SCHEMA|SELECT|SESSION_USER|SET|SETUSER|SHUTDOWN|SOME|STATISTICS|SYSTEM_USER|TABLE|TEXTSIZE|THEN|TO|TOP|TRAN|TRANSACTION|TRIGGER|TRUNCATE|TSEQUAL|UNION|UNIQUE|UPDATE|UPDATETEXT|USE|USER|VALUES|VARYING|VIEW|WAITFOR|WHEN|WHERE|WHILE|WITH|WRITETEXT)(?=[^\w-]|$)/i,
+null],["lit",/^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],["pln",/^[a-z_][\w-]*/i],["pun",/^[^\w\t\n\r \xA0\"\'][^\w\t\n\r \xA0+\-\"\']*/]]),["sql"]) \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/prettify/lang-vb.js b/src/main/java/com/gitblit/wicket/pages/prettify/lang-vb.js
new file mode 100644
index 00000000..c479c11e
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/prettify/lang-vb.js
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0\u2028\u2029]+/,null,"\t\n\r \u00a0\u2028\u2029"],["str",/^(?:[\"\u201C\u201D](?:[^\"\u201C\u201D]|[\"\u201C\u201D]{2})(?:[\"\u201C\u201D]c|$)|[\"\u201C\u201D](?:[^\"\u201C\u201D]|[\"\u201C\u201D]{2})*(?:[\"\u201C\u201D]|$))/i,null,'"\u201c\u201d'],["com",/^[\'\u2018\u2019][^\r\n\u2028\u2029]*/,null,"'\u2018\u2019"]],[["kwd",/^(?:AddHandler|AddressOf|Alias|And|AndAlso|Ansi|As|Assembly|Auto|Boolean|ByRef|Byte|ByVal|Call|Case|Catch|CBool|CByte|CChar|CDate|CDbl|CDec|Char|CInt|Class|CLng|CObj|Const|CShort|CSng|CStr|CType|Date|Decimal|Declare|Default|Delegate|Dim|DirectCast|Do|Double|Each|Else|ElseIf|End|EndIf|Enum|Erase|Error|Event|Exit|Finally|For|Friend|Function|Get|GetType|GoSub|GoTo|Handles|If|Implements|Imports|In|Inherits|Integer|Interface|Is|Let|Lib|Like|Long|Loop|Me|Mod|Module|MustInherit|MustOverride|MyBase|MyClass|Namespace|New|Next|Not|NotInheritable|NotOverridable|Object|On|Option|Optional|Or|OrElse|Overloads|Overridable|Overrides|ParamArray|Preserve|Private|Property|Protected|Public|RaiseEvent|ReadOnly|ReDim|RemoveHandler|Resume|Return|Select|Set|Shadows|Shared|Short|Single|Static|Step|Stop|String|Structure|Sub|SyncLock|Then|Throw|To|Try|TypeOf|Unicode|Until|Variant|Wend|When|While|With|WithEvents|WriteOnly|Xor|EndIf|GoSub|Let|Variant|Wend)\b/i,
+null],["com",/^REM[^\r\n\u2028\u2029]*/i],["lit",/^(?:True\b|False\b|Nothing\b|\d+(?:E[+\-]?\d+[FRD]?|[FRDSIL])?|(?:&H[0-9A-F]+|&O[0-7]+)[SIL]?|\d*\.\d+(?:E[+\-]?\d+)?[FRD]?|#\s+(?:\d+[\-\/]\d+[\-\/]\d+(?:\s+\d+:\d+(?::\d+)?(\s*(?:AM|PM))?)?|\d+:\d+(?::\d+)?(\s*(?:AM|PM))?)\s+#)/i],["pln",/^(?:(?:[a-z]|_\w)\w*|\[(?:[a-z]|_\w)\w*\])/i],["pun",/^[^\w\t\n\r \"\'\[\]\xA0\u2018\u2019\u201C\u201D\u2028\u2029]+/],["pun",/^(?:\[|\])/]]),["vb","vbs"]) \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/prettify/lang-vhdl.js b/src/main/java/com/gitblit/wicket/pages/prettify/lang-vhdl.js
new file mode 100644
index 00000000..dc81a3fe
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/prettify/lang-vhdl.js
@@ -0,0 +1,3 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"]],[["str",/^(?:[BOX]?"(?:[^\"]|"")*"|'.')/i],["com",/^--[^\r\n]*/],["kwd",/^(?:abs|access|after|alias|all|and|architecture|array|assert|attribute|begin|block|body|buffer|bus|case|component|configuration|constant|disconnect|downto|else|elsif|end|entity|exit|file|for|function|generate|generic|group|guarded|if|impure|in|inertial|inout|is|label|library|linkage|literal|loop|map|mod|nand|new|next|nor|not|null|of|on|open|or|others|out|package|port|postponed|procedure|process|pure|range|record|register|reject|rem|report|return|rol|ror|select|severity|shared|signal|sla|sll|sra|srl|subtype|then|to|transport|type|unaffected|units|until|use|variable|wait|when|while|with|xnor|xor)(?=[^\w-]|$)/i,
+null],["typ",/^(?:bit|bit_vector|character|boolean|integer|real|time|string|severity_level|positive|natural|signed|unsigned|line|text|std_u?logic(?:_vector)?)(?=[^\w-]|$)/i,null],["typ",/^\'(?:ACTIVE|ASCENDING|BASE|DELAYED|DRIVING|DRIVING_VALUE|EVENT|HIGH|IMAGE|INSTANCE_NAME|LAST_ACTIVE|LAST_EVENT|LAST_VALUE|LEFT|LEFTOF|LENGTH|LOW|PATH_NAME|POS|PRED|QUIET|RANGE|REVERSE_RANGE|RIGHT|RIGHTOF|SIMPLE_NAME|STABLE|SUCC|TRANSACTION|VAL|VALUE)(?=[^\w-]|$)/i,null],["lit",/^\d+(?:_\d+)*(?:#[\w\\.]+#(?:[+\-]?\d+(?:_\d+)*)?|(?:\.\d+(?:_\d+)*)?(?:E[+\-]?\d+(?:_\d+)*)?)/i],
+["pln",/^(?:[a-z]\w*|\\[^\\]*\\)/i],["pun",/^[^\w\t\n\r \xA0\"\'][^\w\t\n\r \xA0\-\"\']*/]]),["vhdl","vhd"]) \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/prettify/lang-wiki.js b/src/main/java/com/gitblit/wicket/pages/prettify/lang-wiki.js
new file mode 100644
index 00000000..3b8fb500
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/prettify/lang-wiki.js
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t \xA0a-gi-z0-9]+/,null,"\t \u00a0abcdefgijklmnopqrstuvwxyz0123456789"],["pun",/^[=*~\^\[\]]+/,null,"=*~^[]"]],[["lang-wiki.meta",/(?:^^|\r\n?|\n)(#[a-z]+)\b/],["lit",/^(?:[A-Z][a-z][a-z0-9]+[A-Z][a-z][a-zA-Z0-9]+)\b/],["lang-",/^\{\{\{([\s\S]+?)\}\}\}/],["lang-",/^`([^\r\n`]+)`/],["str",/^https?:\/\/[^\/?#\s]*(?:\/[^?#\s]*)?(?:\?[^#\s]*)?(?:#\S*)?/i],["pln",/^(?:\r\n|[\s\S])[^#=*~^A-Zh\{`\[\r\n]*/]]),["wiki"]);
+PR.registerLangHandler(PR.createSimpleLexer([["kwd",/^#[a-z]+/i,null,"#"]],[]),["wiki.meta"]) \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/prettify/lang-yaml.js b/src/main/java/com/gitblit/wicket/pages/prettify/lang-yaml.js
new file mode 100644
index 00000000..f2f36070
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/prettify/lang-yaml.js
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pun",/^[:|>?]+/,null,":|>?"],["dec",/^%(?:YAML|TAG)[^#\r\n]+/,null,"%"],["typ",/^[&]\S+/,null,"&"],["typ",/^!\S*/,null,"!"],["str",/^"(?:[^\\"]|\\.)*(?:"|$)/,null,'"'],["str",/^'(?:[^']|'')*(?:'|$)/,null,"'"],["com",/^#[^\r\n]*/,null,"#"],["pln",/^\s+/,null," \t\r\n"]],[["dec",/^(?:---|\.\.\.)(?:[\r\n]|$)/],["pun",/^-/],["kwd",/^\w+:[ \r\n]/],["pln",/^\w+/]]),
+["yaml","yml"]) \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/prettify/prettify.css b/src/main/java/com/gitblit/wicket/pages/prettify/prettify.css
new file mode 100644
index 00000000..2925d13a
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/prettify/prettify.css
@@ -0,0 +1 @@
+.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun{color:#660}.pln{color:#000}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec{color:#606}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}@media print{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun{color:#440}.pln{color:#000}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}} \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/prettify/prettify.js b/src/main/java/com/gitblit/wicket/pages/prettify/prettify.js
new file mode 100644
index 00000000..c9161da9
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/prettify/prettify.js
@@ -0,0 +1,33 @@
+window.PR_SHOULD_USE_CONTINUATION=true;window.PR_TAB_WIDTH=8;window.PR_normalizedHtml=window.PR=window.prettyPrintOne=window.prettyPrint=void 0;window._pr_isIE6=function(){var y=navigator&&navigator.userAgent&&navigator.userAgent.match(/\bMSIE ([678])\./);y=y?+y[1]:false;window._pr_isIE6=function(){return y};return y};
+(function(){function y(b){return b.replace(L,"&amp;").replace(M,"&lt;").replace(N,"&gt;")}function H(b,f,i){switch(b.nodeType){case 1:var o=b.tagName.toLowerCase();f.push("<",o);var l=b.attributes,n=l.length;if(n){if(i){for(var r=[],j=n;--j>=0;)r[j]=l[j];r.sort(function(q,m){return q.name<m.name?-1:q.name===m.name?0:1});l=r}for(j=0;j<n;++j){r=l[j];r.specified&&f.push(" ",r.name.toLowerCase(),'="',r.value.replace(L,"&amp;").replace(M,"&lt;").replace(N,"&gt;").replace(X,"&quot;"),'"')}}f.push(">");
+for(l=b.firstChild;l;l=l.nextSibling)H(l,f,i);if(b.firstChild||!/^(?:br|link|img)$/.test(o))f.push("</",o,">");break;case 3:case 4:f.push(y(b.nodeValue));break}}function O(b){function f(c){if(c.charAt(0)!=="\\")return c.charCodeAt(0);switch(c.charAt(1)){case "b":return 8;case "t":return 9;case "n":return 10;case "v":return 11;case "f":return 12;case "r":return 13;case "u":case "x":return parseInt(c.substring(2),16)||c.charCodeAt(1);case "0":case "1":case "2":case "3":case "4":case "5":case "6":case "7":return parseInt(c.substring(1),
+8);default:return c.charCodeAt(1)}}function i(c){if(c<32)return(c<16?"\\x0":"\\x")+c.toString(16);c=String.fromCharCode(c);if(c==="\\"||c==="-"||c==="["||c==="]")c="\\"+c;return c}function o(c){var d=c.substring(1,c.length-1).match(RegExp("\\\\u[0-9A-Fa-f]{4}|\\\\x[0-9A-Fa-f]{2}|\\\\[0-3][0-7]{0,2}|\\\\[0-7]{1,2}|\\\\[\\s\\S]|-|[^-\\\\]","g"));c=[];for(var a=[],k=d[0]==="^",e=k?1:0,h=d.length;e<h;++e){var g=d[e];switch(g){case "\\B":case "\\b":case "\\D":case "\\d":case "\\S":case "\\s":case "\\W":case "\\w":c.push(g);
+continue}g=f(g);var s;if(e+2<h&&"-"===d[e+1]){s=f(d[e+2]);e+=2}else s=g;a.push([g,s]);if(!(s<65||g>122)){s<65||g>90||a.push([Math.max(65,g)|32,Math.min(s,90)|32]);s<97||g>122||a.push([Math.max(97,g)&-33,Math.min(s,122)&-33])}}a.sort(function(v,w){return v[0]-w[0]||w[1]-v[1]});d=[];g=[NaN,NaN];for(e=0;e<a.length;++e){h=a[e];if(h[0]<=g[1]+1)g[1]=Math.max(g[1],h[1]);else d.push(g=h)}a=["["];k&&a.push("^");a.push.apply(a,c);for(e=0;e<d.length;++e){h=d[e];a.push(i(h[0]));if(h[1]>h[0]){h[1]+1>h[0]&&a.push("-");
+a.push(i(h[1]))}}a.push("]");return a.join("")}function l(c){for(var d=c.source.match(RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g")),a=d.length,k=[],e=0,h=0;e<a;++e){var g=d[e];if(g==="(")++h;else if("\\"===g.charAt(0))if((g=+g.substring(1))&&g<=h)k[g]=-1}for(e=1;e<k.length;++e)if(-1===k[e])k[e]=++n;for(h=e=0;e<a;++e){g=d[e];if(g==="("){++h;if(k[h]===undefined)d[e]="(?:"}else if("\\"===
+g.charAt(0))if((g=+g.substring(1))&&g<=h)d[e]="\\"+k[h]}for(h=e=0;e<a;++e)if("^"===d[e]&&"^"!==d[e+1])d[e]="";if(c.ignoreCase&&r)for(e=0;e<a;++e){g=d[e];c=g.charAt(0);if(g.length>=2&&c==="[")d[e]=o(g);else if(c!=="\\")d[e]=g.replace(/[a-zA-Z]/g,function(s){s=s.charCodeAt(0);return"["+String.fromCharCode(s&-33,s|32)+"]"})}return d.join("")}for(var n=0,r=false,j=false,q=0,m=b.length;q<m;++q){var t=b[q];if(t.ignoreCase)j=true;else if(/[a-z]/i.test(t.source.replace(/\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi,
+""))){r=true;j=false;break}}var p=[];q=0;for(m=b.length;q<m;++q){t=b[q];if(t.global||t.multiline)throw Error(""+t);p.push("(?:"+l(t)+")")}return RegExp(p.join("|"),j?"gi":"g")}function Y(b){var f=0;return function(i){for(var o=null,l=0,n=0,r=i.length;n<r;++n)switch(i.charAt(n)){case "\t":o||(o=[]);o.push(i.substring(l,n));l=b-f%b;for(f+=l;l>=0;l-=16)o.push(" ".substring(0,l));l=n+1;break;case "\n":f=0;break;default:++f}if(!o)return i;o.push(i.substring(l));return o.join("")}}function I(b,
+f,i,o){if(f){b={source:f,c:b};i(b);o.push.apply(o,b.d)}}function B(b,f){var i={},o;(function(){for(var r=b.concat(f),j=[],q={},m=0,t=r.length;m<t;++m){var p=r[m],c=p[3];if(c)for(var d=c.length;--d>=0;)i[c.charAt(d)]=p;p=p[1];c=""+p;if(!q.hasOwnProperty(c)){j.push(p);q[c]=null}}j.push(/[\0-\uffff]/);o=O(j)})();var l=f.length;function n(r){for(var j=r.c,q=[j,z],m=0,t=r.source.match(o)||[],p={},c=0,d=t.length;c<d;++c){var a=t[c],k=p[a],e=void 0,h;if(typeof k==="string")h=false;else{var g=i[a.charAt(0)];
+if(g){e=a.match(g[1]);k=g[0]}else{for(h=0;h<l;++h){g=f[h];if(e=a.match(g[1])){k=g[0];break}}e||(k=z)}if((h=k.length>=5&&"lang-"===k.substring(0,5))&&!(e&&typeof e[1]==="string")){h=false;k=P}h||(p[a]=k)}g=m;m+=a.length;if(h){h=e[1];var s=a.indexOf(h),v=s+h.length;if(e[2]){v=a.length-e[2].length;s=v-h.length}k=k.substring(5);I(j+g,a.substring(0,s),n,q);I(j+g+s,h,Q(k,h),q);I(j+g+v,a.substring(v),n,q)}else q.push(j+g,k)}r.d=q}return n}function x(b){var f=[],i=[];if(b.tripleQuotedStrings)f.push([A,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
+null,"'\""]);else b.multiLineStrings?f.push([A,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"]):f.push([A,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"]);b.verbatimStrings&&i.push([A,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null]);if(b.hashComments)if(b.cStyleComments){f.push([C,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"]);i.push([A,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,
+null])}else f.push([C,/^#[^\r\n]*/,null,"#"]);if(b.cStyleComments){i.push([C,/^\/\/[^\r\n]*/,null]);i.push([C,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}b.regexLiterals&&i.push(["lang-regex",RegExp("^"+Z+"(/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/)")]);b=b.keywords.replace(/^\s+|\s+$/g,"");b.length&&i.push([R,RegExp("^(?:"+b.replace(/\s+/g,"|")+")\\b"),null]);f.push([z,/^\s+/,null," \r\n\t\u00a0"]);i.push([J,/^@[a-z_$][a-z_$@0-9]*/i,null],[S,/^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/,
+null],[z,/^[a-z_$][a-z_$@0-9]*/i,null],[J,/^(?:0x[a-f0-9]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+\-]?\d+)?)[a-z]*/i,null,"0123456789"],[E,/^.[^\s\w\.$@\'\"\`\/\#]*/,null]);return B(f,i)}function $(b){function f(D){if(D>r){if(j&&j!==q){n.push("</span>");j=null}if(!j&&q){j=q;n.push('<span class="',j,'">')}var T=y(p(i.substring(r,D))).replace(e?d:c,"$1&#160;");e=k.test(T);n.push(T.replace(a,s));r=D}}var i=b.source,o=b.g,l=b.d,n=[],r=0,j=null,q=null,m=0,t=0,p=Y(window.PR_TAB_WIDTH),c=/([\r\n ]) /g,
+d=/(^| ) /gm,a=/\r\n?|\n/g,k=/[ \r\n]$/,e=true,h=window._pr_isIE6();h=h?b.b.tagName==="PRE"?h===6?"&#160;\r\n":h===7?"&#160;<br>\r":"&#160;\r":"&#160;<br />":"<br />";var g=b.b.className.match(/\blinenums\b(?::(\d+))?/),s;if(g){for(var v=[],w=0;w<10;++w)v[w]=h+'</li><li class="L'+w+'">';var F=g[1]&&g[1].length?g[1]-1:0;n.push('<ol class="linenums"><li class="L',F%10,'"');F&&n.push(' value="',F+1,'"');n.push(">");s=function(){var D=v[++F%10];return j?"</span>"+D+'<span class="'+j+'">':D}}else s=h;
+for(;;)if(m<o.length?t<l.length?o[m]<=l[t]:true:false){f(o[m]);if(j){n.push("</span>");j=null}n.push(o[m+1]);m+=2}else if(t<l.length){f(l[t]);q=l[t+1];t+=2}else break;f(i.length);j&&n.push("</span>");g&&n.push("</li></ol>");b.a=n.join("")}function u(b,f){for(var i=f.length;--i>=0;){var o=f[i];if(G.hasOwnProperty(o))"console"in window&&console.warn("cannot override language handler %s",o);else G[o]=b}}function Q(b,f){b&&G.hasOwnProperty(b)||(b=/^\s*</.test(f)?"default-markup":"default-code");return G[b]}
+function U(b){var f=b.f,i=b.e;b.a=f;try{var o,l=f.match(aa);f=[];var n=0,r=[];if(l)for(var j=0,q=l.length;j<q;++j){var m=l[j];if(m.length>1&&m.charAt(0)==="<"){if(!ba.test(m))if(ca.test(m)){f.push(m.substring(9,m.length-3));n+=m.length-12}else if(da.test(m)){f.push("\n");++n}else if(m.indexOf(V)>=0&&m.replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,' $1="$2$3$4"').match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/)){var t=m.match(W)[2],p=1,c;c=j+1;a:for(;c<q;++c){var d=l[c].match(W);if(d&&
+d[2]===t)if(d[1]==="/"){if(--p===0)break a}else++p}if(c<q){r.push(n,l.slice(j,c+1).join(""));j=c}else r.push(n,m)}else r.push(n,m)}else{var a;p=m;var k=p.indexOf("&");if(k<0)a=p;else{for(--k;(k=p.indexOf("&#",k+1))>=0;){var e=p.indexOf(";",k);if(e>=0){var h=p.substring(k+3,e),g=10;if(h&&h.charAt(0)==="x"){h=h.substring(1);g=16}var s=parseInt(h,g);isNaN(s)||(p=p.substring(0,k)+String.fromCharCode(s)+p.substring(e+1))}}a=p.replace(ea,"<").replace(fa,">").replace(ga,"'").replace(ha,'"').replace(ia," ").replace(ja,
+"&")}f.push(a);n+=a.length}}o={source:f.join(""),h:r};var v=o.source;b.source=v;b.c=0;b.g=o.h;Q(i,v)(b);$(b)}catch(w){if("console"in window)console.log(w&&w.stack?w.stack:w)}}var A="str",R="kwd",C="com",S="typ",J="lit",E="pun",z="pln",P="src",V="nocode",Z=function(){for(var b=["!","!=","!==","#","%","%=","&","&&","&&=","&=","(","*","*=","+=",",","-=","->","/","/=",":","::",";","<","<<","<<=","<=","=","==","===",">",">=",">>",">>=",">>>",">>>=","?","@","[","^","^=","^^","^^=","{","|","|=","||","||=",
+"~","break","case","continue","delete","do","else","finally","instanceof","return","throw","try","typeof"],f="(?:^^|[+-]",i=0;i<b.length;++i)f+="|"+b[i].replace(/([^=<>:&a-z])/g,"\\$1");f+=")\\s*";return f}(),L=/&/g,M=/</g,N=/>/g,X=/\"/g,ea=/&lt;/g,fa=/&gt;/g,ga=/&apos;/g,ha=/&quot;/g,ja=/&amp;/g,ia=/&nbsp;/g,ka=/[\r\n]/g,K=null,aa=RegExp("[^<]+|<!--[\\s\\S]*?--\>|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>|</?[a-zA-Z](?:[^>\"']|'[^']*'|\"[^\"]*\")*>|<","g"),ba=/^<\!--/,ca=/^<!\[CDATA\[/,da=/^<br\b/i,W=/^<(\/?)([a-zA-Z][a-zA-Z0-9]*)/,
+la=x({keywords:"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof alignof align_union asm axiom bool concept concept_map const_cast constexpr decltype dynamic_cast explicit export friend inline late_check mutable namespace nullptr reinterpret_cast static_assert static_cast template typeid typename using virtual wchar_t where break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof abstract boolean byte extends final finally implements import instanceof null native package strictfp super synchronized throws transient as base by checked decimal delegate descending event fixed foreach from group implicit in interface internal into is lock object out override orderby params partial readonly ref sbyte sealed stackalloc string select uint ulong unchecked unsafe ushort var break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof debugger eval export function get null set undefined var with Infinity NaN caller delete die do dump elsif eval exit foreach for goto if import last local my next no our print package redo require sub undef unless until use wantarray while BEGIN END break continue do else for if return while and as assert class def del elif except exec finally from global import in is lambda nonlocal not or pass print raise try with yield False True None break continue do else for if return while alias and begin case class def defined elsif end ensure false in module next nil not or redo rescue retry self super then true undef unless until when yield BEGIN END break continue do else for if return while case done elif esac eval fi function in local set then until ",
+hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true}),G={};u(la,["default-code"]);u(B([],[[z,/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],[C,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[E,/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup",
+"htm","html","mxml","xhtml","xml","xsl"]);u(B([[z,/^[\s]+/,null," \t\r\n"],["atv",/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[E,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],
+["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);u(B([],[["atv",/^[\s\S]+/]]),["uq.val"]);u(x({keywords:"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof alignof align_union asm axiom bool concept concept_map const_cast constexpr decltype dynamic_cast explicit export friend inline late_check mutable namespace nullptr reinterpret_cast static_assert static_cast template typeid typename using virtual wchar_t where ",
+hashComments:true,cStyleComments:true}),["c","cc","cpp","cxx","cyc","m"]);u(x({keywords:"null true false"}),["json"]);u(x({keywords:"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof abstract boolean byte extends final finally implements import instanceof null native package strictfp super synchronized throws transient as base by checked decimal delegate descending event fixed foreach from group implicit in interface internal into is lock object out override orderby params partial readonly ref sbyte sealed stackalloc string select uint ulong unchecked unsafe ushort var ",
+hashComments:true,cStyleComments:true,verbatimStrings:true}),["cs"]);u(x({keywords:"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof abstract boolean byte extends final finally implements import instanceof null native package strictfp super synchronized throws transient ",
+cStyleComments:true}),["java"]);u(x({keywords:"break continue do else for if return while case done elif esac eval fi function in local set then until ",hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);u(x({keywords:"break continue do else for if return while and as assert class def del elif except exec finally from global import in is lambda nonlocal not or pass print raise try with yield False True None ",hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);
+u(x({keywords:"caller delete die do dump elsif eval exit foreach for goto if import last local my next no our print package redo require sub undef unless until use wantarray while BEGIN END ",hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);u(x({keywords:"break continue do else for if return while alias and begin case class def defined elsif end ensure false in module next nil not or redo rescue retry self super then true undef unless until when yield BEGIN END ",hashComments:true,
+multiLineStrings:true,regexLiterals:true}),["rb"]);u(x({keywords:"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof debugger eval export function get null set undefined var with Infinity NaN ",cStyleComments:true,regexLiterals:true}),["js"]);u(B([],[[A,/^[\s\S]+/]]),
+["regex"]);window.PR_normalizedHtml=H;window.prettyPrintOne=function(b,f){var i={f:b,e:f};U(i);return i.a};window.prettyPrint=function(b){function f(){for(var t=window.PR_SHOULD_USE_CONTINUATION?j.now()+250:Infinity;q<o.length&&j.now()<t;q++){var p=o[q];if(p.className&&p.className.indexOf("prettyprint")>=0){var c=p.className.match(/\blang-(\w+)\b/);if(c)c=c[1];for(var d=false,a=p.parentNode;a;a=a.parentNode)if((a.tagName==="pre"||a.tagName==="code"||a.tagName==="xmp")&&a.className&&a.className.indexOf("prettyprint")>=
+0){d=true;break}if(!d){a=p;if(null===K){d=document.createElement("PRE");d.appendChild(document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'));K=!/</.test(d.innerHTML)}if(K){d=a.innerHTML;if("XMP"===a.tagName)d=y(d);else{a=a;if("PRE"===a.tagName)a=true;else if(ka.test(d)){var k="";if(a.currentStyle)k=a.currentStyle.whiteSpace;else if(window.getComputedStyle)k=window.getComputedStyle(a,null).whiteSpace;a=!k||k==="pre"}else a=true;a||(d=d.replace(/(<br\s*\/?>)[\r\n]+/g,"$1").replace(/(?:[\r\n]+[ \t]*)+/g,
+" "))}d=d}else{d=[];for(a=a.firstChild;a;a=a.nextSibling)H(a,d);d=d.join("")}d=d.replace(/(?:\r\n?|\n)$/,"");m={f:d,e:c,b:p};U(m);if(p=m.a){c=m.b;if("XMP"===c.tagName){d=document.createElement("PRE");for(a=0;a<c.attributes.length;++a){k=c.attributes[a];if(k.specified)if(k.name.toLowerCase()==="class")d.className=k.value;else d.setAttribute(k.name,k.value)}d.innerHTML=p;c.parentNode.replaceChild(d,c)}else c.innerHTML=p}}}}if(q<o.length)setTimeout(f,250);else b&&b()}for(var i=[document.getElementsByTagName("pre"),
+document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],o=[],l=0;l<i.length;++l)for(var n=0,r=i[l].length;n<r;++n)o.push(i[l][n]);i=null;var j=Date;j.now||(j={now:function(){return(new Date).getTime()}});var q=0,m;f()};window.PR={combinePrefixPatterns:O,createSimpleLexer:B,registerLangHandler:u,sourceDecorator:x,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:C,PR_DECLARATION:"dec",PR_KEYWORD:R,PR_LITERAL:J,PR_NOCODE:V,PR_PLAIN:z,PR_PUNCTUATION:E,PR_SOURCE:P,PR_STRING:A,
+PR_TAG:"tag",PR_TYPE:S}})() \ No newline at end of file