| /* | |
| * 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; | |
| } | |
| } |