-<?xml version="1.0" encoding="UTF-8"?>\r
-<projectDescription>\r
- <name>gitblit</name>\r
- <comment></comment>\r
- <projects>\r
- </projects>\r
- <buildSpec>\r
- <buildCommand>\r
- <name>org.eclipse.jdt.core.javabuilder</name>\r
- <arguments>\r
- </arguments>\r
- </buildCommand>\r
- <buildCommand>\r
- <name>net.sf.eclipsecs.core.CheckstyleBuilder</name>\r
- <arguments>\r
- </arguments>\r
- </buildCommand>\r
- </buildSpec>\r
- <natures>\r
- <nature>org.eclipse.jdt.core.javanature</nature>\r
- <nature>net.sf.eclipsecs.core.CheckstyleNature</nature>\r
- </natures>\r
-</projectDescription>\r
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>Gitblit</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
+ </natures>
+</projectDescription>
\r
#### additions\r
\r
-- added support for X-Forwarded-Context for Apache subdomain proxy configurations (issue 135)\r
-- delete branch feature (issue 121, Github/ajermakovics)\r
-- added line links to blob view at the expense of zebra striping (issue 130)\r
-- added RedmineUserService (github/mallowlabs)\r
+- Added simple project pages. A project is a subfolder off the *git.repositoriesFolder*.\r
+- Added support for personal repositories. This builds on the simple project pages. \r
+Personal repositories are stored in *git.repositoriesFolder*/*~username*. Each user with personal repositories will have a user page, something like the GitHub profile page. Personal repositories have all the same features as common repositories.\r
+- Added support for server-side forking of a repository to a personal repository (issue 137) \r
+In order to fork a repository to a personal clone, the user account must have the *fork* permission **and** the repository must *allow forks*. The clone inherits the access restrictions of its origin. i.e. if Team A has access to the origin repository, then by default Team A also has access to the fork. This is to facilitate collaboration. However, the fork owner may change access to the fork and add/remove users/teams, etc as required.\r
+- Added support for X-Forwarded-Context for Apache subdomain proxy configurations (issue 135)\r
+- Delete branch feature (issue 121, Github/ajermakovics)\r
+- Added line links to blob view at the expense of zebra striping (issue 130)\r
+- Added RedmineUserService (github/mallowlabs)\r
\r
#### changes\r
\r
### IDEAS\r
\r
* Gitblit: Re-use the EGit branch visualization table cell renderer as some sort of servlet\r
-* Gitblit: Support personal repositories (~username/repo)\r
* Gitblit: diff should highlight inserted/removed fragment compared to original line\r
* Gitblit: implement branch permission controls as Groovy pre-receive script. \r
*Maintain permissions text file similar to a gitolite configuration file or svn authz file.*\r
outline: none;\r
}\r
\r
+[class^="icon-"], [class*=" icon-"] a i {\r
+ /* override for a links that look like bootstrap buttons */\r
+ vertical-align: text-bottom;\r
+}\r
+\r
hr {\r
margin-top: 10px;\r
margin-bottom: 10px;\r
font-weight: bold;\r
}\r
\r
+.pageTitle {\r
+ color: #888;\r
+ font-size: 18px;\r
+ line-height: 27px;\r
+}\r
+.pageTitle .project, .pageTitle .repository {\r
+ font-family: Helvetica, arial, freesans, clean, sans-serif;\r
+ font-size: 22px;\r
+}\r
+\r
+.pageTitle .controls {\r
+ font-size: 12px;\r
+}\r
+\r
+.pageTitle .repository {\r
+ font-weight: bold;\r
+}\r
+\r
+.originRepository {\r
+ font-family: Helvetica, arial, freesans, clean, sans-serif;\r
+ color: #888;\r
+ font-size: 12px;\r
+ line-height: 14px;\r
+ margin: 0px;\r
+}\r
+\r
+.forkSource, .forkEntry {\r
+ color: #888;\r
+}\r
+\r
+.forkSource {\r
+ font-size: 18px;\r
+ line-height: 20px;\r
+ padding: 5px 0px;\r
+}\r
+\r
+.forkEntry {\r
+ font-size: 14px;\r
+ padding: 2px 0px;\r
+}\r
+\r
div.page_footer {\r
clear: both;\r
height: 17px;\r
if (model.canAdmin) {\r
roles.add(Constants.ADMIN_ROLE);\r
}\r
+ if (model.canFork) {\r
+ roles.add(Constants.FORK_ROLE);\r
+ }\r
if (model.excludeFromFederation) {\r
roles.add(Constants.NOT_FEDERATED_ROLE);\r
}\r
Set<String> roles = new HashSet<String>(Arrays.asList(config.getStringList(\r
USER, username, ROLE)));\r
user.canAdmin = roles.contains(Constants.ADMIN_ROLE);\r
+ user.canFork = roles.contains(Constants.FORK_ROLE);\r
user.excludeFromFederation = roles.contains(Constants.NOT_FEDERATED_ROLE);\r
\r
// repository memberships\r
public static final String JGIT_VERSION = "JGit 2.1.0 (201209190230-r)";\r
\r
public static final String ADMIN_ROLE = "#admin";\r
+ \r
+ public static final String FORK_ROLE = "#fork";\r
\r
public static final String NOT_FEDERATED_ROLE = "#notfederated";\r
\r
// Permissions\r
if (role.equalsIgnoreCase(Constants.ADMIN_ROLE)) {\r
model.canAdmin = true;\r
+ } else if (role.equalsIgnoreCase(Constants.FORK_ROLE)) {\r
+ model.canFork = true;\r
} else if (role.equalsIgnoreCase(Constants.NOT_FEDERATED_ROLE)) {\r
model.excludeFromFederation = true;\r
}\r
if (model.canAdmin) {\r
roles.add(Constants.ADMIN_ROLE);\r
}\r
+ if (model.canFork) {\r
+ roles.add(Constants.FORK_ROLE);\r
+ }\r
if (model.excludeFromFederation) {\r
roles.add(Constants.NOT_FEDERATED_ROLE);\r
}\r
import java.io.InputStream;\r
import java.io.InputStreamReader;\r
import java.lang.reflect.Field;\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
import java.text.MessageFormat;\r
import java.text.SimpleDateFormat;\r
import java.util.ArrayList;\r
private void addToCachedRepositoryList(String name, RepositoryModel model) {\r
if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) {\r
repositoryListCache.put(name, model);\r
+ \r
+ // update the fork origin repository with this repository clone\r
+ if (!StringUtils.isEmpty(model.originRepository)) {\r
+ if (repositoryListCache.containsKey(model.originRepository)) {\r
+ RepositoryModel origin = repositoryListCache.get(model.originRepository);\r
+ origin.addFork(name);\r
+ }\r
+ }\r
}\r
}\r
\r
* Removes the repository from the list of cached repositories.\r
* \r
* @param name\r
+ * @return the model being removed\r
*/\r
- private void removeFromCachedRepositoryList(String name) {\r
+ private RepositoryModel removeFromCachedRepositoryList(String name) {\r
if (StringUtils.isEmpty(name)) {\r
- return;\r
+ return null;\r
}\r
- repositoryListCache.remove(name);\r
+ return repositoryListCache.remove(name);\r
}\r
\r
/**\r
if (model == null) {\r
return null;\r
}\r
- addToCachedRepositoryList(repositoryName, model); \r
+ addToCachedRepositoryList(repositoryName, model);\r
return model;\r
}\r
\r
* @return project config map\r
*/\r
private Map<String, ProjectModel> getProjectConfigs() {\r
- if (projectConfigs.isOutdated()) {\r
+ if (projectCache.isEmpty() || projectConfigs.isOutdated()) {\r
\r
try {\r
projectConfigs.load();\r
* Returns a list of project models for the user.\r
* \r
* @param user\r
+ * @param includeUsers\r
* @return list of projects that are accessible to the user\r
*/\r
- public List<ProjectModel> getProjectModels(UserModel user) {\r
+ public List<ProjectModel> getProjectModels(UserModel user, boolean includeUsers) {\r
Map<String, ProjectModel> configs = getProjectConfigs();\r
\r
// per-user project lists, this accounts for security and visibility\r
}\r
\r
// sort projects, root project first\r
- List<ProjectModel> projects = new ArrayList<ProjectModel>(map.values());\r
- Collections.sort(projects);\r
- projects.remove(map.get(""));\r
- projects.add(0, map.get(""));\r
+ List<ProjectModel> projects;\r
+ if (includeUsers) {\r
+ // all projects\r
+ projects = new ArrayList<ProjectModel>(map.values());\r
+ Collections.sort(projects);\r
+ projects.remove(map.get(""));\r
+ projects.add(0, map.get(""));\r
+ } else {\r
+ // all non-user projects\r
+ projects = new ArrayList<ProjectModel>();\r
+ ProjectModel root = map.remove("");\r
+ for (ProjectModel model : map.values()) {\r
+ if (!model.isUserProject()) {\r
+ projects.add(model);\r
+ }\r
+ }\r
+ Collections.sort(projects);\r
+ projects.add(0, root);\r
+ }\r
return projects;\r
}\r
\r
* @return a project model, or null if it does not exist\r
*/\r
public ProjectModel getProjectModel(String name, UserModel user) {\r
- for (ProjectModel project : getProjectModels(user)) {\r
+ for (ProjectModel project : getProjectModels(user, true)) {\r
if (project.name.equalsIgnoreCase(name)) {\r
return project;\r
}\r
Map<String, ProjectModel> configs = getProjectConfigs();\r
ProjectModel project = configs.get(name.toLowerCase());\r
if (project == null) {\r
- return null;\r
+ project = new ProjectModel(name);\r
+ if (name.length() > 0 && name.charAt(0) == '~') {\r
+ UserModel user = getUserModel(name.substring(1));\r
+ if (user != null) {\r
+ project.title = user.getDisplayName();\r
+ project.description = "personal repositories";\r
+ }\r
+ }\r
+ } else {\r
+ // clone the object\r
+ project = DeepCopier.copy(project);\r
}\r
- // clone the object\r
- project = DeepCopier.copy(project);\r
- String folder = name.toLowerCase() + "/";\r
- for (String repository : getRepositoryList()) {\r
- if (repository.toLowerCase().startsWith(folder)) {\r
- project.addRepository(repository);\r
+ if (StringUtils.isEmpty(name)) {\r
+ // get root repositories\r
+ for (String repository : getRepositoryList()) {\r
+ if (repository.indexOf('/') == -1) {\r
+ project.addRepository(repository);\r
+ }\r
}\r
+ } else {\r
+ // get repositories in subfolder\r
+ String folder = name.toLowerCase() + "/";\r
+ for (String repository : getRepositoryList()) {\r
+ if (repository.toLowerCase().startsWith(folder)) {\r
+ project.addRepository(repository);\r
+ }\r
+ }\r
+ }\r
+ if (project.repositories.size() == 0) {\r
+ // no repositories == no project\r
+ return null;\r
}\r
return project;\r
}\r
model.hasCommits = JGitUtils.hasCommits(r);\r
model.lastChange = JGitUtils.getLastChange(r);\r
model.isBare = r.isBare();\r
+ if (repositoryName.indexOf('/') == -1) {\r
+ model.projectPath = "";\r
+ } else {\r
+ model.projectPath = repositoryName.substring(0, repositoryName.indexOf('/'));\r
+ }\r
\r
StoredConfig config = r.getConfig();\r
+ boolean hasOrigin = !StringUtils.isEmpty(config.getString("remote", "origin", "url"));\r
+ \r
if (config != null) {\r
model.description = getConfig(config, "description", "");\r
model.owner = getConfig(config, "owner", "");\r
model.useTickets = getConfig(config, "useTickets", false);\r
model.useDocs = getConfig(config, "useDocs", false);\r
+ model.allowForks = getConfig(config, "allowForks", true);\r
model.accessRestriction = AccessRestrictionType.fromName(getConfig(config,\r
"accessRestriction", settings.getString(Keys.git.defaultAccessRestriction, null)));\r
model.authorizationControl = AuthorizationControl.fromName(getConfig(config,\r
"authorizationControl", settings.getString(Keys.git.defaultAuthorizationControl, null)));\r
- model.showRemoteBranches = getConfig(config, "showRemoteBranches", false);\r
+ model.showRemoteBranches = getConfig(config, "showRemoteBranches", hasOrigin);\r
model.isFrozen = getConfig(config, "isFrozen", false);\r
model.showReadme = getConfig(config, "showReadme", false);\r
model.skipSizeCalculation = getConfig(config, "skipSizeCalculation", false);\r
model.HEAD = JGitUtils.getHEADRef(r);\r
model.availableRefs = JGitUtils.getAvailableHeadTargets(r);\r
r.close();\r
+ \r
+ if (model.origin != null && model.origin.startsWith("file://")) {\r
+ // repository was cloned locally... perhaps as a fork\r
+ try {\r
+ File folder = new File(new URI(model.origin));\r
+ String originRepo = com.gitblit.utils.FileUtils.getRelativePath(getRepositoriesFolder(), folder);\r
+ if (!StringUtils.isEmpty(originRepo)) {\r
+ // ensure origin still exists\r
+ File repoFolder = new File(getRepositoriesFolder(), originRepo);\r
+ if (repoFolder.exists()) {\r
+ model.originRepository = originRepo;\r
+ }\r
+ }\r
+ } catch (URISyntaxException e) {\r
+ logger.error("Failed to determine fork for " + model, e);\r
+ }\r
+ }\r
return model;\r
}\r
\r
"Failed to rename repository permissions ''{0}'' to ''{1}''.",\r
repositoryName, repository.name));\r
}\r
+ \r
+ // rename fork origins in their configs\r
+ if (!ArrayUtils.isEmpty(repository.forks)) {\r
+ for (String fork : repository.forks) {\r
+ Repository rf = getRepository(fork);\r
+ try {\r
+ StoredConfig config = rf.getConfig();\r
+ String origin = config.getString("remote", "origin", "url");\r
+ origin = origin.replace(repositoryName, repository.name);\r
+ config.setString("remote", "origin", "url", origin);\r
+ config.save();\r
+ } catch (Exception e) {\r
+ logger.error("Failed to update repository fork config for " + fork, e);\r
+ }\r
+ rf.close();\r
+ }\r
+ }\r
\r
// clear the cache\r
clearRepositoryMetadataCache(repositoryName);\r
+ repository.resetDisplayName();\r
}\r
\r
// load repository\r
config.setString(Constants.CONFIG_GITBLIT, null, "owner", repository.owner);\r
config.setBoolean(Constants.CONFIG_GITBLIT, null, "useTickets", repository.useTickets);\r
config.setBoolean(Constants.CONFIG_GITBLIT, null, "useDocs", repository.useDocs);\r
+ config.setBoolean(Constants.CONFIG_GITBLIT, null, "allowForks", repository.allowForks);\r
config.setString(Constants.CONFIG_GITBLIT, null, "accessRestriction", repository.accessRestriction.name());\r
config.setString(Constants.CONFIG_GITBLIT, null, "authorizationControl", repository.authorizationControl.name());\r
config.setBoolean(Constants.CONFIG_GITBLIT, null, "showRemoteBranches", repository.showRemoteBranches);\r
closeRepository(repositoryName);\r
// clear the repository cache\r
clearRepositoryMetadataCache(repositoryName);\r
- removeFromCachedRepositoryList(repositoryName);\r
+ \r
+ RepositoryModel model = removeFromCachedRepositoryList(repositoryName);\r
+ if (!ArrayUtils.isEmpty(model.forks)) {\r
+ resetRepositoryListCache();\r
+ }\r
\r
File folder = new File(repositoriesFolder, repositoryName);\r
if (folder.exists() && folder.isDirectory()) {\r
scheduledExecutor.shutdownNow();\r
luceneExecutor.close();\r
}\r
+ \r
+ /**\r
+ * Creates a personal fork of the specified repository. The clone is view\r
+ * restricted by default and the owner of the source repository is given\r
+ * access to the clone. \r
+ * \r
+ * @param repository\r
+ * @param user\r
+ * @return true, if successful\r
+ */\r
+ public boolean fork(RepositoryModel repository, UserModel user) {\r
+ String cloneName = MessageFormat.format("~{0}/{1}.git", user.username, StringUtils.stripDotGit(StringUtils.getLastPathElement(repository.name)));\r
+ String fromUrl = MessageFormat.format("file://{0}/{1}", repositoriesFolder.getAbsolutePath(), repository.name);\r
+ try {\r
+ // clone the repository\r
+ JGitUtils.cloneRepository(repositoriesFolder, cloneName, fromUrl, true, null);\r
+ \r
+ // create a Gitblit repository model for the clone\r
+ RepositoryModel cloneModel = repository.cloneAs(cloneName);\r
+ cloneModel.owner = user.username;\r
+ updateRepositoryModel(cloneName, cloneModel, false);\r
+ \r
+ if (AuthorizationControl.NAMED.equals(cloneModel.authorizationControl)) {\r
+ // add the owner of the source repository to the clone's access list\r
+ if (!StringUtils.isEmpty(repository.owner)) {\r
+ UserModel owner = getUserModel(repository.owner);\r
+ if (owner != null) {\r
+ owner.repositories.add(cloneName);\r
+ updateUserModel(owner.username, owner, false);\r
+ }\r
+ }\r
+ \r
+ // inherit origin's access lists\r
+ List<String> users = getRepositoryUsers(repository);\r
+ setRepositoryUsers(cloneModel, users);\r
+ \r
+ List<String> teams = getRepositoryTeams(repository);\r
+ setRepositoryTeams(cloneModel, teams);\r
+ }\r
+ \r
+ // add this clone to the cached model\r
+ addToCachedRepositoryList(cloneModel.name, cloneModel);\r
+ return true;\r
+ } catch (Exception e) {\r
+ logger.error("failed to fork", e);\r
+ }\r
+ return false;\r
+ }\r
}\r
commits = JGitUtils.searchRevlogs(repository, objectId, searchString, searchType,\r
offset, length);\r
}\r
- Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository);\r
+ Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository, model.showRemoteBranches);\r
\r
// convert RevCommit to SyndicatedEntryModel\r
for (RevCommit commit : commits) {\r
this.title = "";\r
this.description = "";\r
}\r
+ \r
+ public boolean isUserProject() {\r
+ return name.charAt(0) == '~';\r
+ }\r
\r
public boolean hasRepository(String name) {\r
return repositories.contains(name.toLowerCase());\r
import java.util.Date;\r
import java.util.List;\r
import java.util.Map;\r
+import java.util.Set;\r
+import java.util.TreeSet;\r
\r
import com.gitblit.Constants.AccessRestrictionType;\r
import com.gitblit.Constants.AuthorizationControl;\r
public List<String> postReceiveScripts;\r
public List<String> mailingLists;\r
public Map<String, String> customFields;\r
+ public String projectPath;\r
private String displayName;\r
+ public boolean allowForks;\r
+ public Set<String> forks;\r
+ public String originRepository;\r
\r
public RepositoryModel() {\r
this("", "", "", new Date(0));\r
}\r
return localBranches;\r
}\r
+ \r
+ public void addFork(String repository) {\r
+ if (forks == null) {\r
+ forks = new TreeSet<String>();\r
+ }\r
+ forks.add(repository);\r
+ }\r
+ \r
+ public void removeFork(String repository) {\r
+ if (forks == null) {\r
+ return;\r
+ }\r
+ forks.remove(repository);\r
+ }\r
+ \r
+ public void resetDisplayName() {\r
+ displayName = null;\r
+ }\r
\r
@Override\r
public String toString() {\r
public int compareTo(RepositoryModel o) {\r
return StringUtils.compareRepositoryNames(name, o.name);\r
}\r
+ \r
+ public boolean isPersonalRepository() {\r
+ return !StringUtils.isEmpty(projectPath) && projectPath.charAt(0) == '~';\r
+ }\r
+ \r
+ public boolean isUsersPersonalRepository(String username) {\r
+ return !StringUtils.isEmpty(projectPath) && projectPath.equalsIgnoreCase("~" + username);\r
+ }\r
+ \r
+ public RepositoryModel cloneAs(String cloneName) {\r
+ RepositoryModel clone = new RepositoryModel();\r
+ clone.name = cloneName;\r
+ clone.description = description;\r
+ clone.accessRestriction = accessRestriction;\r
+ clone.authorizationControl = authorizationControl;\r
+ clone.federationStrategy = federationStrategy;\r
+ clone.showReadme = showReadme;\r
+ clone.showRemoteBranches = false;\r
+ clone.allowForks = false;\r
+ clone.useDocs = useDocs;\r
+ clone.useTickets = useTickets;\r
+ clone.skipSizeCalculation = skipSizeCalculation;\r
+ clone.skipSummaryMetrics = skipSummaryMetrics;\r
+ return clone;\r
+ }\r
}
\ No newline at end of file
import java.util.HashSet;\r
import java.util.Set;\r
\r
+import com.gitblit.Constants.AccessRestrictionType;\r
import com.gitblit.Constants.AuthorizationControl;\r
import com.gitblit.utils.StringUtils;\r
\r
public String displayName;\r
public String emailAddress;\r
public boolean canAdmin;\r
+ public boolean canFork;\r
public boolean excludeFromFederation;\r
public final Set<String> repositories = new HashSet<String>();\r
public final Set<TeamModel> teams = new HashSet<TeamModel>();\r
}\r
return false;\r
}\r
+ \r
+ public boolean canForkRepository(RepositoryModel repository) {\r
+ if (canAdmin) {\r
+ return true;\r
+ }\r
+ if (!canFork) {\r
+ // user has been prohibited from forking\r
+ return false;\r
+ }\r
+ if (!isAuthenticated) {\r
+ // unauthenticated user model\r
+ return false;\r
+ }\r
+ if (("~" + username).equalsIgnoreCase(repository.projectPath)) {\r
+ // this repository is already a personal repository\r
+ return false;\r
+ }\r
+ if (!repository.allowForks) {\r
+ // repository prohibits forks\r
+ return false;\r
+ }\r
+ if (repository.accessRestriction.atLeast(AccessRestrictionType.CLONE)) {\r
+ return canAccessRepository(repository);\r
+ }\r
+ // repository is not clone-restricted\r
+ return true;\r
+ }\r
\r
public boolean hasRepository(String name) {\r
return repositories.contains(name.toLowerCase());\r
branches.add(objectId);\r
}\r
Map<ObjectId, List<RefModel>> allRefs = JGitUtils\r
- .getAllRefs(repository);\r
+ .getAllRefs(repository, model.showRemoteBranches);\r
\r
for (String branch : branches) {\r
String shortName = branch;\r
* @return all refs grouped by their referenced object id\r
*/\r
public static Map<ObjectId, List<RefModel>> getAllRefs(Repository repository) {\r
+ return getAllRefs(repository, true);\r
+ }\r
+ \r
+ /**\r
+ * Returns all refs grouped by their associated object id.\r
+ * \r
+ * @param repository\r
+ * @param includeRemoteRefs\r
+ * @return all refs grouped by their referenced object id\r
+ */\r
+ public static Map<ObjectId, List<RefModel>> getAllRefs(Repository repository, boolean includeRemoteRefs) {\r
List<RefModel> list = getRefs(repository, org.eclipse.jgit.lib.RefDatabase.ALL, true, -1);\r
Map<ObjectId, List<RefModel>> refs = new HashMap<ObjectId, List<RefModel>>();\r
for (RefModel ref : list) {\r
+ if (!includeRemoteRefs && ref.getName().startsWith(Constants.R_REMOTES)) {\r
+ continue;\r
+ }\r
ObjectId objectid = ref.getReferencedObjectId();\r
if (!refs.containsKey(objectid)) {\r
refs.put(objectid, new ArrayList<RefModel>());\r
* @return the first invalid character found or null if string is acceptable\r
*/\r
public static Character findInvalidCharacter(String name) {\r
- char[] validChars = { '/', '.', '_', '-' };\r
+ char[] validChars = { '/', '.', '_', '-', '~' };\r
for (char c : name.toCharArray()) {\r
if (!Character.isLetterOrDigit(c)) {\r
boolean ok = false;\r
}\r
return input;\r
}\r
+ \r
+ /**\r
+ * Returns the first path element of a path string. If no path separator is\r
+ * found in the path, an empty string is returned. \r
+ * \r
+ * @param path\r
+ * @return the first element in the path\r
+ */\r
+ public static String getFirstPathElement(String path) {\r
+ if (path.indexOf('/') > -1) {\r
+ return path.substring(0, path.indexOf('/')).trim();\r
+ }\r
+ return "";\r
+ }\r
+ \r
+ /**\r
+ * Returns the last path element of a path string\r
+ * \r
+ * @param path\r
+ * @return the last element in the path\r
+ */\r
+ public static String getLastPathElement(String path) {\r
+ if (path.indexOf('/') > -1) {\r
+ return path.substring(path.lastIndexOf('/') + 1);\r
+ }\r
+ return path;\r
+ }\r
}
\ No newline at end of file
import com.gitblit.wicket.pages.CommitPage;\r
import com.gitblit.wicket.pages.DocsPage;\r
import com.gitblit.wicket.pages.FederationRegistrationPage;\r
+import com.gitblit.wicket.pages.ForksPage;\r
import com.gitblit.wicket.pages.GitSearchPage;\r
import com.gitblit.wicket.pages.GravatarProfilePage;\r
import com.gitblit.wicket.pages.HistoryPage;\r
import com.gitblit.wicket.pages.TicketPage;\r
import com.gitblit.wicket.pages.TicketsPage;\r
import com.gitblit.wicket.pages.TreePage;\r
+import com.gitblit.wicket.pages.UserPage;\r
import com.gitblit.wicket.pages.UsersPage;\r
\r
public class GitBlitWebApp extends WebApplication {\r
mount("/lucene", LuceneSearchPage.class);\r
mount("/project", ProjectPage.class, "p");\r
mount("/projects", ProjectsPage.class);\r
+ mount("/user", UserPage.class, "user");\r
+ mount("/forks", ForksPage.class, "r");\r
}\r
\r
private void mount(String location, Class<? extends WebPage> clazz, String... parameters) {\r
gb.projects = projects\r
gb.project = project\r
gb.allProjects = all projects\r
-gb.copyToClipboard = copy to clipboard
\ No newline at end of file
+gb.copyToClipboard = copy to clipboard\r
+gb.fork = fork\r
+gb.forks = forks\r
+gb.forkRepository = fork {0}?\r
+gb.repositoryForked = {0} has been forked\r
+gb.repositoryForkFailed= failed to fork {1}\r
+gb.personalRepositories = personal repositories\r
+gb.allowForks = allow forks\r
+gb.allowForksDescription = allow authorized users to fork this repository\r
+gb.forkedFrom = forked from\r
+gb.canFork = can fork\r
+gb.canForkDescription = user is permitted to fork authorized repositories\r
+gb.myFork = view my fork\r
+gb.forksProhibited = forks prohibited\r
+gb.forksProhibitedWarning = this repository forbids forks
\ No newline at end of file
import java.util.ArrayList;\r
import java.util.Calendar;\r
import java.util.Collections;\r
-import java.util.Comparator;\r
import java.util.Date;\r
import java.util.HashSet;\r
import java.util.LinkedHashMap;\r
import org.apache.wicket.MarkupContainer;\r
import org.apache.wicket.PageParameters;\r
import org.apache.wicket.RedirectToUrlException;\r
+import org.apache.wicket.RequestCycle;\r
import org.apache.wicket.RestartResponseException;\r
import org.apache.wicket.markup.html.CSSPackageResource;\r
import org.apache.wicket.markup.html.WebPage;\r
import com.gitblit.utils.StringUtils;\r
import com.gitblit.utils.TimeUtils;\r
import com.gitblit.wicket.GitBlitWebSession;\r
-import com.gitblit.wicket.PageRegistration.DropDownMenuItem;\r
import com.gitblit.wicket.WicketUtils;\r
import com.gitblit.wicket.panels.LinkPanel;\r
\r
return req.getServerName();\r
}\r
\r
- protected String getRepositoryUrl(RepositoryModel repository) {\r
+ public static String getRepositoryUrl(RepositoryModel repository) {\r
StringBuilder sb = new StringBuilder();\r
- sb.append(WicketUtils.getGitblitURL(getRequestCycle().getRequest()));\r
+ sb.append(WicketUtils.getGitblitURL(RequestCycle.get().getRequest()));\r
sb.append(Constants.GIT_PATH);\r
sb.append(repository.name);\r
\r
\r
protected List<ProjectModel> getProjectModels() {\r
final UserModel user = GitBlitWebSession.get().getUser();\r
- List<ProjectModel> projects = GitBlit.self().getProjectModels(user);\r
+ List<ProjectModel> projects = GitBlit.self().getProjectModels(user, true);\r
return projects;\r
}\r
\r
</wicket:container>\r
</td></tr>\r
<tr><th colspan="2"><hr/></th></tr>\r
+ <tr><th><wicket:message key="gb.allowForks"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="allowForks" tabindex="17" /> <span class="help-inline"><wicket:message key="gb.allowForksDescription"></wicket:message></span></label></td></tr>\r
+ <tr><th colspan="2"><hr/></th></tr>\r
<tr><th style="vertical-align: top;"><wicket:message key="gb.permittedUsers"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>\r
<tr><th style="vertical-align: top;"><wicket:message key="gb.permittedTeams"></wicket:message></th><td style="padding:2px;"><span wicket:id="teams"></span></td></tr>\r
<tr><td colspan="2"><h3><wicket:message key="gb.federation"></wicket:message> <small><wicket:message key="gb.federationRepositoryDescription"></wicket:message></small></h3></td></tr> \r
- <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span4" wicket:id="federationStrategy" tabindex="17" /></td></tr>\r
+ <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span4" wicket:id="federationStrategy" tabindex="18" /></td></tr>\r
<tr><th style="vertical-align: top;"><wicket:message key="gb.federationSets"></wicket:message></th><td style="padding:2px;"><span wicket:id="federationSets"></span></td></tr>\r
<tr><td colspan="2"><h3><wicket:message key="gb.search"></wicket:message> <small><wicket:message key="gb.indexedBranchesDescription"></wicket:message></small></h3></td></tr> \r
<tr><th style="vertical-align: top;"><wicket:message key="gb.indexedBranches"></wicket:message></th><td style="padding:2px;"><span wicket:id="indexedBranches"></span></td></tr>\r
form.add(new TextField<String>("description"));\r
form.add(new DropDownChoice<String>("owner", GitBlit.self().getAllUsernames())\r
.setEnabled(GitBlitWebSession.get().canAdmin()));\r
+ form.add(new CheckBox("allowForks"));\r
form.add(new DropDownChoice<AccessRestrictionType>("accessRestriction", Arrays\r
.asList(AccessRestrictionType.values()), new AccessRestrictionRenderer()));\r
form.add(new CheckBox("isFrozen"));\r
<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>\r
<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>\r
<tr><th><wicket:message key="gb.canAdmin"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canAdmin" tabindex="6" /> <span class="help-inline"><wicket:message key="gb.canAdminDescription"></wicket:message></span></label></td></tr> \r
- <tr><th><wicket:message key="gb.excludeFromFederation"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="excludeFromFederation" tabindex="7" /> <span class="help-inline"><wicket:message key="gb.excludeFromFederationDescription"></wicket:message></span></label></td></tr> \r
+ <tr><th><wicket:message key="gb.canFork"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canFork" tabindex="7" /> <span class="help-inline"><wicket:message key="gb.canForkDescription"></wicket:message></span></label></td></tr> \r
+ <tr><th><wicket:message key="gb.excludeFromFederation"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="excludeFromFederation" tabindex="8" /> <span class="help-inline"><wicket:message key="gb.excludeFromFederationDescription"></wicket:message></span></label></td></tr> \r
<tr><td colspan="2" style="padding-top:15px;"><h3><wicket:message key="gb.accessPermissions"></wicket:message> <small><wicket:message key="gb.accessPermissionsForUserDescription"></wicket:message></small></h3></td></tr> \r
<tr><th style="vertical-align: top;"><wicket:message key="gb.teamMemberships"></wicket:message></th><td style="padding:2px;"><span wicket:id="teams"></span></td></tr>\r
<tr><td colspan="2"><hr></hr></td></tr>\r
<tr><th style="vertical-align: top;"><wicket:message key="gb.restrictedRepositories"></wicket:message></th><td style="padding:2px;"><span wicket:id="repositories"></span></td></tr>\r
- <tr><td colspan='2'><div class="form-actions"><input class="btn btn-primary" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" tabindex="8" /> <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" tabindex="9" /></div></td></tr>\r
+ <tr><td colspan='2'><div class="form-actions"><input class="btn btn-primary" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" tabindex="9" /> <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" tabindex="10" /></div></td></tr>\r
</tbody>\r
</table>\r
</form> \r
form.add(new TextField<String>("displayName").setEnabled(editDisplayName));\r
form.add(new TextField<String>("emailAddress").setEnabled(editEmailAddress));\r
form.add(new CheckBox("canAdmin"));\r
+ form.add(new CheckBox("canFork"));\r
form.add(new CheckBox("excludeFromFederation"));\r
form.add(repositories);\r
form.add(teams.setEnabled(editTeams));\r
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" \r
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" \r
+ xml:lang="en" \r
+ lang="en"> \r
+\r
+<body>\r
+<wicket:extend>\r
+\r
+ <div class="forkSource">\r
+ <b><span class="repositorySwatch" wicket:id="forkSourceSwatch"></span></b>\r
+ <span wicket:id="forkSourceAvatar" style="vertical-align: baseline;"></span>\r
+ <span wicket:id="forkSourceProject">[a project]</span> / <span wicket:id="forkSource">[a fork]</span> \r
+ </div>\r
+ \r
+ <div wicket:id="fork">\r
+ <div class="forkEntry">\r
+ <span wicket:id="anAvatar" style="vertical-align: baseline;"></span>\r
+ <span wicket:id="aProject">[a project]</span> / <span wicket:id="aFork">[a fork]</span>\r
+ </div>\r
+ </div>\r
+</wicket:extend> \r
+</body>\r
+</html>
\ No newline at end of file
--- /dev/null
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.wicket.pages;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import org.apache.wicket.Component;\r
+import org.apache.wicket.PageParameters;\r
+import org.apache.wicket.markup.html.basic.Label;\r
+import org.apache.wicket.markup.repeater.Item;\r
+import org.apache.wicket.markup.repeater.data.DataView;\r
+import org.apache.wicket.markup.repeater.data.ListDataProvider;\r
+import org.eclipse.jgit.lib.PersonIdent;\r
+\r
+import com.gitblit.GitBlit;\r
+import com.gitblit.Keys;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.ArrayUtils;\r
+import com.gitblit.utils.StringUtils;\r
+import com.gitblit.wicket.GitBlitWebSession;\r
+import com.gitblit.wicket.WicketUtils;\r
+import com.gitblit.wicket.panels.GravatarImage;\r
+import com.gitblit.wicket.panels.LinkPanel;\r
+\r
+public class ForksPage extends RepositoryPage {\r
+\r
+ public ForksPage(PageParameters params) {\r
+ super(params);\r
+ \r
+ RepositoryModel model = getRepositoryModel();\r
+ RepositoryModel origin;\r
+ List<String> list;\r
+ if (ArrayUtils.isEmpty(model.forks)) {\r
+ // origin repository has forks\r
+ origin = GitBlit.self().getRepositoryModel(model.originRepository);\r
+ list = new ArrayList<String>(origin.forks);\r
+ } else {\r
+ // this repository has forks\r
+ origin = model;\r
+ list = new ArrayList<String>(model.forks);\r
+ }\r
+ \r
+ if (origin.isPersonalRepository()) {\r
+ // personal repository\r
+ UserModel user = GitBlit.self().getUserModel(origin.projectPath.substring(1));\r
+ PersonIdent ident = new PersonIdent(user.getDisplayName(), user.emailAddress);\r
+ add(new GravatarImage("forkSourceAvatar", ident, 20));\r
+ add(new Label("forkSourceSwatch").setVisible(false));\r
+ add(new LinkPanel("forkSourceProject", null, user.getDisplayName(), UserPage.class, WicketUtils.newUsernameParameter(user.username)));\r
+ } else {\r
+ // standard repository\r
+ add(new GravatarImage("forkSourceAvatar", new PersonIdent("", ""), 20).setVisible(false));\r
+ Component swatch;\r
+ if (origin.isBare){\r
+ swatch = new Label("forkSourceSwatch", " ").setEscapeModelStrings(false);\r
+ } else {\r
+ swatch = new Label("forkSourceSwatch", "!");\r
+ WicketUtils.setHtmlTooltip(swatch, getString("gb.workingCopyWarning"));\r
+ }\r
+ WicketUtils.setCssBackground(swatch, origin.toString());\r
+ add(swatch);\r
+ final boolean showSwatch = GitBlit.getBoolean(Keys.web.repositoryListSwatches, true);\r
+ swatch.setVisible(showSwatch);\r
+ \r
+ String projectName = origin.projectPath;\r
+ if (StringUtils.isEmpty(projectName)) {\r
+ projectName = GitBlit.getString(Keys.web.repositoryRootGroupName, "main");\r
+ }\r
+ add(new LinkPanel("forkSourceProject", null, projectName, ProjectPage.class, WicketUtils.newProjectParameter(origin.projectPath)));\r
+ }\r
+ \r
+ String source = StringUtils.getLastPathElement(origin.name);\r
+ add(new LinkPanel("forkSource", null, StringUtils.stripDotGit(source), SummaryPage.class, WicketUtils.newRepositoryParameter(origin.name)));\r
+\r
+ // only display user-accessible forks\r
+ UserModel user = GitBlitWebSession.get().getUser();\r
+ List<RepositoryModel> forks = new ArrayList<RepositoryModel>();\r
+ for (String aFork : list) {\r
+ RepositoryModel fork = GitBlit.self().getRepositoryModel(user, aFork);\r
+ if (fork != null) {\r
+ forks.add(fork);\r
+ }\r
+ }\r
+ \r
+ ListDataProvider<RepositoryModel> forksDp = new ListDataProvider<RepositoryModel>(forks);\r
+ DataView<RepositoryModel> forksList = new DataView<RepositoryModel>("fork", forksDp) {\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ public void populateItem(final Item<RepositoryModel> item) {\r
+ RepositoryModel fork = item.getModelObject();\r
+ \r
+ if (fork.isPersonalRepository()) {\r
+ UserModel user = GitBlit.self().getUserModel(fork.projectPath.substring(1));\r
+ PersonIdent ident = new PersonIdent(user.getDisplayName(), user.emailAddress);\r
+ item.add(new GravatarImage("anAvatar", ident, 20));\r
+ item.add(new LinkPanel("aProject", null, user.getDisplayName(), UserPage.class, WicketUtils.newUsernameParameter(user.username)));\r
+ } else {\r
+ PersonIdent ident = new PersonIdent(fork.name, fork.name);\r
+ item.add(new GravatarImage("anAvatar", ident, 20));\r
+ item.add(new LinkPanel("aProject", null, fork.projectPath, ProjectPage.class, WicketUtils.newProjectParameter(fork.projectPath)));\r
+ }\r
+ \r
+ String repo = StringUtils.getLastPathElement(fork.name);\r
+ item.add(new LinkPanel("aFork", null, StringUtils.stripDotGit(repo), SummaryPage.class, WicketUtils.newRepositoryParameter(fork.name)));\r
+ \r
+ WicketUtils.setCssStyle(item, "margin-left:25px;");\r
+ }\r
+ };\r
+ \r
+ add(forksList);\r
+\r
+ }\r
+\r
+ @Override\r
+ protected String getPageName() {\r
+ return getString("gb.forks");\r
+ }\r
+}\r
int nextPage = pageNumber + 1;\r
\r
SearchPanel search = new SearchPanel("searchPanel", repositoryName, objectId, value,\r
- searchType, getRepository(), -1, pageNumber - 1);\r
+ searchType, getRepository(), -1, pageNumber - 1, getRepositoryModel().showRemoteBranches);\r
boolean hasMore = search.hasMore();\r
add(search);\r
\r
int nextPage = pageNumber + 1;\r
\r
HistoryPanel history = new HistoryPanel("historyPanel", repositoryName, objectId, path,\r
- getRepository(), -1, pageNumber - 1);\r
+ getRepository(), -1, pageNumber - 1, getRepositoryModel().showRemoteBranches);\r
boolean hasMore = history.hasMore();\r
add(history);\r
\r
refid = getRepositoryModel().HEAD;\r
}\r
LogPanel logPanel = new LogPanel("logPanel", repositoryName, refid, getRepository(), -1,\r
- pageNumber - 1);\r
+ pageNumber - 1, getRepositoryModel().showRemoteBranches);\r
boolean hasMore = logPanel.hasMore();\r
add(logPanel);\r
\r
<body>\r
<wicket:extend>\r
\r
- <wicket:fragment wicket:id="repositoryAdminLinks">\r
- <span class="link">\r
- <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>\r
- | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>\r
- | <a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a>\r
- | <a wicket:id="deleteRepository"><wicket:message key="gb.delete">[delete]</wicket:message></a>\r
- </span>\r
- </wicket:fragment>\r
-\r
- <wicket:fragment wicket:id="repositoryOwnerLinks">\r
- <span class="link">\r
- <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>\r
- | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>\r
- | <a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a>\r
- </span>\r
- </wicket:fragment>\r
-\r
- <wicket:fragment wicket:id="repositoryUserLinks">\r
- <span class="link">\r
- <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>\r
- | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>\r
- </span>\r
- </wicket:fragment>\r
\r
<div class="row">\r
<div class="span12">\r
<div class="markdown" wicket:id="repositoriesMessage">[repositories message]</div>\r
</div>\r
</div>\r
- \r
- <!-- repositories -->\r
<div class="row">\r
- <div class="span6" style="border-top:1px solid #eee;" wicket:id="repository">\r
- <div style="padding-top:15px;padding-bottom:15px;margin-right:15px;">\r
- <div class="pull-right" style="text-align:right;padding-right:15px;">\r
- <span wicket:id="repositoryLinks"></span>\r
-\r
- <div>\r
- <img class="inlineIcon" wicket:id="frozenIcon" />\r
- <img class="inlineIcon" wicket:id="federatedIcon" />\r
- \r
- <a style="text-decoration: none;" wicket:id="tickets" wicket:message="title:gb.tickets">\r
- <img style="border:0px;vertical-align:middle;" src="bug_16x16.png"></img>\r
- </a>\r
- <a style="text-decoration: none;" wicket:id="docs" wicket:message="title:gb.docs">\r
- <img style="border:0px;vertical-align:middle;" src="book_16x16.png"></img>\r
- </a>\r
- <a style="text-decoration: none;" wicket:id="syndication" wicket:message="title:gb.feed">\r
- <img style="border:0px;vertical-align:middle;" src="feed_16x16.png"></img>\r
- </a>\r
- </div>\r
- <span style="color: #999;font-style:italic;font-size:0.8em;" wicket:id="repositoryOwner">[owner]</span>\r
- </div> \r
- \r
- <h3><span class="repositorySwatch" wicket:id="repositorySwatch"></span>\r
- <span style="padding-left:3px;" wicket:id="repositoryName">[repository name]</span>\r
- <img class="inlineIcon" wicket:id="accessRestrictionIcon" />\r
- </h3>\r
- \r
- <div style="padding-left:20px;">\r
-\r
- <div style="padding-bottom:10px" wicket:id="repositoryDescription">[repository description]</div>\r
-\r
- <div style="color: #999;">\r
- <wicket:message key="gb.lastChange">[last change]</wicket:message> <span wicket:id="repositoryLastChange">[last change]</span>,\r
- <span style="font-size:0.8em;" wicket:id="repositorySize">[repository size]</span>\r
- </div>\r
- \r
- <div class="hidden-phone hidden-tablet" wicket:id="repositoryCloneUrl">[repository clone url]</div>\r
- </div>\r
- </div>\r
- </div> \r
- </div>\r
+ <div class="span6" style="border-bottom:1px solid #eee;" wicket:id="repositoryList">\r
+ <span wicket:id="repository"></span>\r
+ </div>\r
+ </div> \r
</div>\r
\r
<!-- activity tab -->\r
import org.apache.wicket.RedirectException;\r
import org.apache.wicket.behavior.HeaderContributor;\r
import org.apache.wicket.markup.html.basic.Label;\r
-import org.apache.wicket.markup.html.link.BookmarkablePageLink;\r
import org.apache.wicket.markup.html.link.ExternalLink;\r
-import org.apache.wicket.markup.html.link.Link;\r
-import org.apache.wicket.markup.html.panel.Fragment;\r
import org.apache.wicket.markup.repeater.Item;\r
import org.apache.wicket.markup.repeater.data.DataView;\r
import org.apache.wicket.markup.repeater.data.ListDataProvider;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.UserModel;\r
import com.gitblit.utils.ActivityUtils;\r
-import com.gitblit.utils.ArrayUtils;\r
import com.gitblit.utils.MarkdownUtils;\r
import com.gitblit.utils.StringUtils;\r
import com.gitblit.wicket.GitBlitWebApp;\r
import com.gitblit.wicket.charting.GoogleLineChart;\r
import com.gitblit.wicket.charting.GooglePieChart;\r
import com.gitblit.wicket.panels.ActivityPanel;\r
-import com.gitblit.wicket.panels.BasePanel.JavascriptEventConfirmation;\r
-import com.gitblit.wicket.panels.LinkPanel;\r
-import com.gitblit.wicket.panels.RepositoryUrlPanel;\r
+import com.gitblit.wicket.panels.ProjectRepositoryPanel;\r
\r
public class ProjectPage extends RootPage {\r
\r
}\r
});\r
\r
- final boolean showSwatch = GitBlit.getBoolean(Keys.web.repositoryListSwatches, true);\r
- final boolean gitServlet = GitBlit.getBoolean(Keys.git.enableGitServlet, true);\r
- final boolean showSize = GitBlit.getBoolean(Keys.web.showRepositorySizes, true);\r
- \r
final ListDataProvider<RepositoryModel> dp = new ListDataProvider<RepositoryModel>(repositories);\r
- DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("repository", dp) {\r
+ DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("repositoryList", dp) {\r
private static final long serialVersionUID = 1L;\r
\r
public void populateItem(final Item<RepositoryModel> item) {\r
final RepositoryModel entry = item.getModelObject();\r
-\r
- // repository swatch\r
- Component swatch;\r
- if (entry.isBare){\r
- swatch = new Label("repositorySwatch", " ").setEscapeModelStrings(false);\r
- } else {\r
- swatch = new Label("repositorySwatch", "!");\r
- WicketUtils.setHtmlTooltip(swatch, getString("gb.workingCopyWarning"));\r
- }\r
- WicketUtils.setCssBackground(swatch, entry.toString());\r
- item.add(swatch);\r
- swatch.setVisible(showSwatch);\r
- \r
- PageParameters pp = WicketUtils.newRepositoryParameter(entry.name);\r
- item.add(new LinkPanel("repositoryName", "list", StringUtils.getRelativePath(projectPath, StringUtils.stripDotGit(entry.name)), SummaryPage.class, pp));\r
- item.add(new Label("repositoryDescription", entry.description).setVisible(!StringUtils.isEmpty(entry.description)));\r
- \r
- item.add(new BookmarkablePageLink<Void>("tickets", TicketsPage.class, pp).setVisible(entry.useTickets));\r
- item.add(new BookmarkablePageLink<Void>("docs", DocsPage.class, pp).setVisible(entry.useDocs));\r
-\r
- if (entry.isFrozen) {\r
- item.add(WicketUtils.newImage("frozenIcon", "cold_16x16.png",\r
- getString("gb.isFrozen")));\r
- } else {\r
- item.add(WicketUtils.newClearPixel("frozenIcon").setVisible(false));\r
- }\r
-\r
- if (entry.isFederated) {\r
- item.add(WicketUtils.newImage("federatedIcon", "federated_16x16.png",\r
- getString("gb.isFederated")));\r
- } else {\r
- item.add(WicketUtils.newClearPixel("federatedIcon").setVisible(false));\r
- }\r
- switch (entry.accessRestriction) {\r
- case NONE:\r
- item.add(WicketUtils.newBlankImage("accessRestrictionIcon").setVisible(false));\r
- break;\r
- case PUSH:\r
- item.add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png",\r
- getAccessRestrictions().get(entry.accessRestriction)));\r
- break;\r
- case CLONE:\r
- item.add(WicketUtils.newImage("accessRestrictionIcon", "lock_pull_16x16.png",\r
- getAccessRestrictions().get(entry.accessRestriction)));\r
- break;\r
- case VIEW:\r
- item.add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png",\r
- getAccessRestrictions().get(entry.accessRestriction)));\r
- break;\r
- default:\r
- item.add(WicketUtils.newBlankImage("accessRestrictionIcon"));\r
- }\r
-\r
- item.add(new Label("repositoryOwner", StringUtils.isEmpty(entry.owner) ? "" : (entry.owner + " (" + getString("gb.owner") + ")")));\r
- \r
- \r
- UserModel user = GitBlitWebSession.get().getUser();\r
- Fragment repositoryLinks; \r
- boolean showOwner = user != null && user.username.equalsIgnoreCase(entry.owner);\r
- if (showAdmin || showOwner) {\r
- repositoryLinks = new Fragment("repositoryLinks",\r
- showAdmin ? "repositoryAdminLinks" : "repositoryOwnerLinks", this);\r
- repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository",\r
- EditRepositoryPage.class, WicketUtils\r
- .newRepositoryParameter(entry.name)));\r
- if (showAdmin) {\r
- Link<Void> deleteLink = new Link<Void>("deleteRepository") {\r
-\r
- private static final long serialVersionUID = 1L;\r
-\r
- @Override\r
- public void onClick() {\r
- if (GitBlit.self().deleteRepositoryModel(entry)) {\r
- info(MessageFormat.format(getString("gb.repositoryDeleted"), entry));\r
- // TODO dp.remove(entry);\r
- } else {\r
- error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), entry));\r
- }\r
- }\r
- };\r
- deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(\r
- getString("gb.deleteRepository"), entry)));\r
- repositoryLinks.add(deleteLink);\r
- }\r
- } else {\r
- repositoryLinks = new Fragment("repositoryLinks", "repositoryUserLinks", this);\r
- }\r
\r
- repositoryLinks.add(new BookmarkablePageLink<Void>("tree", TreePage.class,\r
- WicketUtils.newRepositoryParameter(entry.name)).setEnabled(entry.hasCommits));\r
-\r
- repositoryLinks.add(new BookmarkablePageLink<Void>("log", LogPage.class,\r
- WicketUtils.newRepositoryParameter(entry.name)).setEnabled(entry.hasCommits));\r
-\r
- item.add(repositoryLinks);\r
- \r
- String lastChange;\r
- if (entry.lastChange.getTime() == 0) {\r
- lastChange = "--";\r
- } else {\r
- lastChange = getTimeUtils().timeAgo(entry.lastChange);\r
- }\r
- Label lastChangeLabel = new Label("repositoryLastChange", lastChange);\r
- item.add(lastChangeLabel);\r
- WicketUtils.setCssClass(lastChangeLabel, getTimeUtils().timeAgoCss(entry.lastChange));\r
- \r
- if (entry.hasCommits) {\r
- // Existing repository\r
- item.add(new Label("repositorySize", entry.size).setVisible(showSize));\r
- } else {\r
- // New repository\r
- item.add(new Label("repositorySize", getString("gb.empty"))\r
- .setEscapeModelStrings(false));\r
- }\r
- \r
- item.add(new ExternalLink("syndication", SyndicationServlet.asLink("",\r
- entry.name, null, 0)));\r
- \r
- List<String> repositoryUrls = new ArrayList<String>();\r
- if (gitServlet) {\r
- // add the Gitblit repository url\r
- repositoryUrls.add(getRepositoryUrl(entry));\r
- }\r
- repositoryUrls.addAll(GitBlit.self().getOtherCloneUrls(entry.name));\r
- \r
- String primaryUrl = ArrayUtils.isEmpty(repositoryUrls) ? "" : repositoryUrls.remove(0);\r
- item.add(new RepositoryUrlPanel("repositoryCloneUrl", primaryUrl));\r
+ ProjectRepositoryPanel row = new ProjectRepositoryPanel("repository", \r
+ getLocalizer(), this, showAdmin, entry, getAccessRestrictions());\r
+ item.add(row);\r
}\r
};\r
add(dataView);\r
protected List<ProjectModel> getProjectModels() {\r
if (projectModels.isEmpty()) {\r
final UserModel user = GitBlitWebSession.get().getUser();\r
- List<ProjectModel> projects = GitBlit.self().getProjectModels(user);\r
+ List<ProjectModel> projects = GitBlit.self().getProjectModels(user, false);\r
projectModels.addAll(projects);\r
}\r
return projectModels;\r
\r
protected List<DropDownMenuItem> getProjectsMenu() {\r
List<DropDownMenuItem> menu = new ArrayList<DropDownMenuItem>();\r
- List<ProjectModel> projects = getProjectModels();\r
+ List<ProjectModel> projects = new ArrayList<ProjectModel>();\r
+ for (ProjectModel model : getProjectModels()) {\r
+ if (!model.isUserProject()) {\r
+ projects.add(model);\r
+ }\r
+ }\r
int maxProjects = 15;\r
boolean showAllProjects = projects.size() > maxProjects;\r
if (showAllProjects) {\r
import com.gitblit.GitBlit;\r
import com.gitblit.Keys;\r
import com.gitblit.models.ProjectModel;\r
+import com.gitblit.models.UserModel;\r
import com.gitblit.utils.MarkdownUtils;\r
import com.gitblit.utils.StringUtils;\r
import com.gitblit.wicket.GitBlitWebSession;\r
protected boolean reusePageParameters() {\r
return true;\r
}\r
+ \r
+ @Override\r
+ protected List<ProjectModel> getProjectModels() {\r
+ final UserModel user = GitBlitWebSession.get().getUser();\r
+ List<ProjectModel> projects = GitBlit.self().getProjectModels(user, false);\r
+ return projects;\r
+ }\r
\r
private void setup(PageParameters params) {\r
setupPage("", "");\r
<!-- page header -->\r
<div class="pageTitle">\r
<div class="row">\r
- <div wicket:id="workingCopy"></div>\r
- <div class="span9">\r
- <h2><span wicket:id="repositoryName">[repository name]</span> <small><span wicket:id="pageName">[page name]</span></small></h2>\r
+ <div class="controls">\r
+ <span wicket:id="workingCopyIndicator"></span>\r
+ <span wicket:id="forksProhibitedIndicator"></span>\r
+ <div class="hidden-phone btn-group pull-right">\r
+ <!-- future spot for other repo buttons -->\r
+ <a class="btn" wicket:id="myForkLink"><i class="icon-random"></i> <wicket:message key="gb.myFork"></wicket:message></a>\r
+ <a class="btn" wicket:id="forkLink"><i class="icon-random"></i> <wicket:message key="gb.fork"></wicket:message></a>\r
+ </div>\r
+ </div>\r
+ <div class="span7">\r
+ <div><span class="project" wicket:id="projectTitle">[project title]</span>/<span class="repository" wicket:id="repositoryName">[repository name]</span> <span wicket:id="pageName">[page name]</span></div>\r
+ <span wicket:id="originRepository">[origin repository]</span>\r
</div>\r
</div>\r
</div>\r
<wicket:child />\r
</div>\r
\r
+ <wicket:fragment wicket:id="originFragment">\r
+ <p class="originRepository"><wicket:message key="gb.forkedFrom">[forked from]</wicket:message> <span wicket:id="originRepository">[origin repository]</span></p>\r
+ </wicket:fragment>\r
+ \r
<wicket:fragment wicket:id="workingCopyFragment">\r
- <p class="pull-right" style="padding-top:5px;">\r
- <span class="alert alert-info" style="padding: 8px 14px 8px 14px;vertical-align: middle;"><i class="icon-exclamation-sign" style="vertical-align: middle;"></i> <span class="hidden-phone" wicket:id="workingCopy" style="font-weight:bold;">[working copy]</span></span>\r
- </p>\r
+ <div class="pull-right" style="padding-top:0px;margin-bottom:0px;padding-left:5px">\r
+ <span class="alert alert-info" style="padding: 6px 14px 6px 14px;vertical-align: middle;"><i class="icon-exclamation-sign"></i> <span class="hidden-phone" wicket:id="workingCopy" style="font-weight:bold;">[working copy]</span></span>\r
+ </div>\r
</wicket:fragment>\r
+\r
+ <wicket:fragment wicket:id="forksProhibitedFragment">\r
+ <div class="pull-right" style="padding-top:0px;margin-bottom:0px;padding-left:5px">\r
+ <span class="alert alert-error" style="padding: 6px 14px 6px 14px;vertical-align: middle;"><i class="icon-ban-circle"></i> <span class="hidden-phone" wicket:id="forksProhibited" style="font-weight:bold;">[forks prohibited]</span></span>\r
+ </div>\r
+ </wicket:fragment>\r
+ \r
</wicket:extend>\r
</body>\r
</html>
\ No newline at end of file
\r
import org.apache.wicket.Component;\r
import org.apache.wicket.PageParameters;\r
+import org.apache.wicket.RedirectException;\r
import org.apache.wicket.markup.html.basic.Label;\r
import org.apache.wicket.markup.html.form.DropDownChoice;\r
import org.apache.wicket.markup.html.form.TextField;\r
import org.apache.wicket.markup.html.link.ExternalLink;\r
+import org.apache.wicket.markup.html.link.Link;\r
import org.apache.wicket.markup.html.panel.Fragment;\r
import org.apache.wicket.model.IModel;\r
import org.apache.wicket.model.Model;\r
import com.gitblit.Keys;\r
import com.gitblit.PagesServlet;\r
import com.gitblit.SyndicationServlet;\r
+import com.gitblit.models.ProjectModel;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.SubmoduleModel;\r
+import com.gitblit.models.UserModel;\r
import com.gitblit.utils.ArrayUtils;\r
import com.gitblit.utils.JGitUtils;\r
import com.gitblit.utils.StringUtils;\r
import com.gitblit.wicket.PageRegistration.OtherPageLink;\r
import com.gitblit.wicket.SessionlessForm;\r
import com.gitblit.wicket.WicketUtils;\r
+import com.gitblit.wicket.panels.BasePanel.JavascriptEventConfirmation;\r
import com.gitblit.wicket.panels.LinkPanel;\r
import com.gitblit.wicket.panels.NavigationPanel;\r
import com.gitblit.wicket.panels.RefsPanel;\r
public RepositoryPage(PageParameters params) {\r
super(params);\r
repositoryName = WicketUtils.getRepositoryName(params);\r
- if (repositoryName.indexOf('/') > -1) {\r
- projectName = repositoryName.substring(0, repositoryName.indexOf('/'));\r
- } else {\r
+ String root =StringUtils.getFirstPathElement(repositoryName);\r
+ if (StringUtils.isEmpty(root)) {\r
projectName = GitBlit.getString(Keys.web.repositoryRootGroupName, "main");\r
+ } else {\r
+ projectName = root;\r
}\r
objectId = WicketUtils.getObject(params);\r
\r
\r
// standard links\r
pages.put("repositories", new PageRegistration("gb.repositories", RepositoriesPage.class));\r
- pages.put("project", new PageRegistration("gb.project", ProjectPage.class, WicketUtils.newProjectParameter(projectName)));\r
pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));\r
pages.put("log", new PageRegistration("gb.log", LogPage.class, params));\r
pages.put("branches", new PageRegistration("gb.branches", BranchesPage.class, params));\r
Repository r = getRepository();\r
RepositoryModel model = getRepositoryModel();\r
\r
+ // forks list button\r
+ if (StringUtils.isEmpty(model.originRepository)) {\r
+ if (!ArrayUtils.isEmpty(model.forks)) {\r
+ // this origin repository has forks\r
+ pages.put("forks", new PageRegistration("gb.forks", ForksPage.class, params));\r
+ }\r
+ } else {\r
+ // this is a fork of another repository\r
+ pages.put("forks", new PageRegistration("gb.forks", ForksPage.class, params));\r
+ }\r
+ \r
// per-repository extra page links\r
if (model.useTickets && TicgitUtils.getTicketsBranch(r) != null) {\r
pages.put("tickets", new PageRegistration("gb.tickets", TicketsPage.class, params));\r
\r
@Override\r
protected void setupPage(String repositoryName, String pageName) {\r
- add(new LinkPanel("repositoryName", null, StringUtils.stripDotGit(repositoryName),\r
- SummaryPage.class, WicketUtils.newRepositoryParameter(repositoryName)));\r
+ String projectName = StringUtils.getFirstPathElement(repositoryName);\r
+ ProjectModel project = GitBlit.self().getProjectModel(projectName);\r
+ if (project.isUserProject()) {\r
+ // user-as-project\r
+ add(new LinkPanel("projectTitle", null, project.getDisplayName(),\r
+ UserPage.class, WicketUtils.newUsernameParameter(project.name.substring(1))));\r
+ } else {\r
+ // project\r
+ add(new LinkPanel("projectTitle", null, project.name,\r
+ ProjectPage.class, WicketUtils.newProjectParameter(project.name)));\r
+ }\r
+ \r
+ String name = StringUtils.stripDotGit(repositoryName);\r
+ if (!StringUtils.isEmpty(projectName) && name.startsWith(projectName)) {\r
+ name = name.substring(projectName.length() + 1);\r
+ }\r
+ add(new LinkPanel("repositoryName", null, name, SummaryPage.class,\r
+ WicketUtils.newRepositoryParameter(repositoryName)));\r
add(new Label("pageName", pageName).setRenderBodyOnly(true));\r
+ \r
+ // indicate origin repository\r
+ RepositoryModel model = getRepositoryModel();\r
+ if (StringUtils.isEmpty(model.originRepository)) {\r
+ add(new Label("originRepository").setVisible(false));\r
+ } else {\r
+ Fragment forkFrag = new Fragment("originRepository", "originFragment", this);\r
+ forkFrag.add(new LinkPanel("originRepository", null, StringUtils.stripDotGit(model.originRepository), \r
+ SummaryPage.class, WicketUtils.newRepositoryParameter(model.originRepository)));\r
+ add(forkFrag);\r
+ }\r
+ \r
if (getRepositoryModel().isBare) {\r
- add(new Label("workingCopy").setVisible(false));\r
+ add(new Label("workingCopyIndicator").setVisible(false));\r
} else {\r
- Fragment fragment = new Fragment("workingCopy", "workingCopyFragment", this);\r
+ Fragment wc = new Fragment("workingCopyIndicator", "workingCopyFragment", this);\r
Label lbl = new Label("workingCopy", getString("gb.workingCopy"));\r
WicketUtils.setHtmlTooltip(lbl, getString("gb.workingCopyWarning"));\r
- fragment.add(lbl);\r
- add(fragment);\r
+ wc.add(lbl);\r
+ add(wc);\r
}\r
-\r
+ \r
+ if (getRepositoryModel().allowForks) {\r
+ add(new Label("forksProhibitedIndicator").setVisible(false));\r
+ } else {\r
+ Fragment wc = new Fragment("forksProhibitedIndicator", "forksProhibitedFragment", this);\r
+ Label lbl = new Label("forksProhibited", getString("gb.forksProhibited"));\r
+ WicketUtils.setHtmlTooltip(lbl, getString("gb.forksProhibitedWarning"));\r
+ wc.add(lbl);\r
+ add(wc);\r
+ }\r
+ \r
+ UserModel user = GitBlitWebSession.get().getUser();\r
+ \r
+ // fork button\r
+ if (user != null) { \r
+ final String clonedRepo = MessageFormat.format("~{0}/{1}.git", user.username, StringUtils.stripDotGit(StringUtils.getLastPathElement(model.name)));\r
+ boolean hasClone = GitBlit.self().hasRepository(clonedRepo) && !getRepositoryModel().name.equals(clonedRepo);\r
+ if (user.canForkRepository(model) && !hasClone) {\r
+ Link<Void> forkLink = new Link<Void>("forkLink") {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ @Override\r
+ public void onClick() {\r
+ RepositoryModel model = getRepositoryModel();\r
+ if (GitBlit.self().fork(model, GitBlitWebSession.get().getUser())) {\r
+ throw new RedirectException(SummaryPage.class, WicketUtils.newRepositoryParameter(clonedRepo));\r
+ } else {\r
+ error(MessageFormat.format(getString("gb.repositoryForkFailed"), model));\r
+ }\r
+ }\r
+ };\r
+ forkLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(\r
+ getString("gb.forkRepository"), getRepositoryModel())));\r
+ add(forkLink);\r
+ } else {\r
+ // user not allowed to fork or fork already exists or repo forbids forking\r
+ add(new ExternalLink("forkLink", "").setVisible(false));\r
+ }\r
+ \r
+ if (hasClone) {\r
+ // user has clone\r
+ String url = getRequestCycle().urlFor(SummaryPage.class, WicketUtils.newRepositoryParameter(clonedRepo)).toString();\r
+ add(new ExternalLink("myForkLink", url));\r
+ } else {\r
+ // user does not have clone\r
+ add(new ExternalLink("myForkLink", "").setVisible(false));\r
+ }\r
+ } else {\r
+ // server prohibits forking\r
+ add(new ExternalLink("forkLink", "").setVisible(false));\r
+ add(new ExternalLink("myForkLink", "").setVisible(false));\r
+ }\r
+ \r
super.setupPage(repositoryName, pageName);\r
}\r
\r
}\r
\r
protected void addRefs(Repository r, RevCommit c) {\r
- add(new RefsPanel("refsPanel", repositoryName, c, JGitUtils.getAllRefs(r)));\r
+ add(new RefsPanel("refsPanel", repositoryName, c, JGitUtils.getAllRefs(r, getRepositoryModel().showRemoteBranches)));\r
}\r
\r
protected void addFullText(String wicketId, String text, boolean substituteRegex) {\r
// remove named repository parameter\r
params.remove("r");\r
\r
+ // remove named user parameter\r
+ params.remove("user");\r
+\r
// remove days back parameter if it is the default value\r
if (params.containsKey("db")\r
&& params.getInt("db") == GitBlit.getInteger(Keys.web.activityDuration, 14)) {\r
\r
boolean hasParameter = false;\r
String projectName = WicketUtils.getProjectName(params);\r
+ String userName = WicketUtils.getUsername(params);\r
+ if (StringUtils.isEmpty(projectName)) {\r
+ if (!StringUtils.isEmpty(userName)) {\r
+ projectName = "~" + userName;\r
+ }\r
+ }\r
String repositoryName = WicketUtils.getRepositoryName(params);\r
String set = WicketUtils.getSet(params);\r
String regex = WicketUtils.getRegEx(params);\r
add(new Label("otherUrls", StringUtils.flattenStrings(repositoryUrls, "<br/>"))\r
.setEscapeModelStrings(false));\r
\r
- add(new LogPanel("commitsPanel", repositoryName, getRepositoryModel().HEAD, r, numberCommits, 0));\r
+ add(new LogPanel("commitsPanel", repositoryName, getRepositoryModel().HEAD, r, numberCommits, 0, getRepositoryModel().showRemoteBranches));\r
add(new TagsPanel("tagsPanel", repositoryName, r, numberRefs).hideIfEmpty());\r
add(new BranchesPanel("branchesPanel", getRepositoryModel(), r, numberRefs, false).hideIfEmpty());\r
\r
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" \r
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" \r
+ xml:lang="en" \r
+ lang="en"> \r
+\r
+<body>\r
+<wicket:extend>\r
+\r
+ <div class="row">\r
+ <div class="span4">\r
+ <div wicket:id="gravatar"></div>\r
+ <div style="text-align: left;">\r
+ <h2><span wicket:id="userDisplayName"></span></h2>\r
+ <div><i class="icon-user"></i> <span wicket:id="userUsername"></span></div>\r
+ <div><i class="icon-envelope"></i><span wicket:id="userEmail"></span></div>\r
+ </div>\r
+ </div>\r
+ \r
+ <div class="span8">\r
+ <div class="tabbable">\r
+ <!-- tab titles -->\r
+ <ul class="nav nav-tabs">\r
+ <li class="active"><a href="#repositories" data-toggle="tab"><wicket:message key="gb.repositories"></wicket:message></a></li>\r
+ </ul>\r
+ \r
+ <!-- tab content -->\r
+ <div class="tab-content">\r
+\r
+ <!-- repositories tab -->\r
+ <div class="tab-pane active" id="repositories">\r
+ <table width="100%">\r
+ <tbody>\r
+ <tr wicket:id="repositoryList"><td style="border-bottom:1px solid #eee;"><span wicket:id="repository"></span></td></tr>\r
+ </tbody>\r
+ </table>\r
+ </div>\r
+ </div>\r
+ </div>\r
+ </div>\r
+ </div>\r
+</wicket:extend>\r
+</body>\r
+</html>
\ No newline at end of file
--- /dev/null
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.wicket.pages;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.List;\r
+\r
+import org.apache.wicket.PageParameters;\r
+import org.apache.wicket.RedirectException;\r
+import org.apache.wicket.markup.html.basic.Label;\r
+import org.apache.wicket.markup.repeater.Item;\r
+import org.apache.wicket.markup.repeater.data.DataView;\r
+import org.apache.wicket.markup.repeater.data.ListDataProvider;\r
+import org.eclipse.jgit.lib.PersonIdent;\r
+\r
+import com.gitblit.GitBlit;\r
+import com.gitblit.Keys;\r
+import com.gitblit.models.ProjectModel;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.StringUtils;\r
+import com.gitblit.wicket.GitBlitWebApp;\r
+import com.gitblit.wicket.GitBlitWebSession;\r
+import com.gitblit.wicket.PageRegistration;\r
+import com.gitblit.wicket.PageRegistration.DropDownMenuItem;\r
+import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;\r
+import com.gitblit.wicket.WicketUtils;\r
+import com.gitblit.wicket.panels.GravatarImage;\r
+import com.gitblit.wicket.panels.LinkPanel;\r
+import com.gitblit.wicket.panels.ProjectRepositoryPanel;\r
+\r
+public class UserPage extends RootPage {\r
+ \r
+ List<ProjectModel> projectModels = new ArrayList<ProjectModel>();\r
+\r
+ public UserPage() {\r
+ super();\r
+ throw new RedirectException(GitBlitWebApp.get().getHomePage());\r
+ }\r
+\r
+ public UserPage(PageParameters params) {\r
+ super(params);\r
+ setup(params);\r
+ }\r
+\r
+ @Override\r
+ protected boolean reusePageParameters() {\r
+ return true;\r
+ }\r
+\r
+ private void setup(PageParameters params) {\r
+ setupPage("", "");\r
+ // check to see if we should display a login message\r
+ boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, true);\r
+ if (authenticateView && !GitBlitWebSession.get().isLoggedIn()) {\r
+ authenticationError("Please login");\r
+ return;\r
+ }\r
+\r
+ String userName = WicketUtils.getUsername(params);\r
+ if (StringUtils.isEmpty(userName)) {\r
+ throw new RedirectException(GitBlitWebApp.get().getHomePage());\r
+ }\r
+\r
+ UserModel user = GitBlit.self().getUserModel(userName);\r
+ if (user == null) {\r
+ // construct a temporary user model\r
+ user = new UserModel(userName);\r
+ }\r
+ \r
+ String projectName = "~" + userName;\r
+ \r
+ ProjectModel project = GitBlit.self().getProjectModel(projectName);\r
+ if (project == null) {\r
+ throw new RedirectException(GitBlitWebApp.get().getHomePage());\r
+ }\r
+ \r
+ add(new Label("userDisplayName", user.getDisplayName()));\r
+ add(new Label("userUsername", user.username));\r
+ LinkPanel email = new LinkPanel("userEmail", null, user.emailAddress, "mailto:#");\r
+ email.setRenderBodyOnly(true);\r
+ add(email.setVisible(GitBlit.getBoolean(Keys.web.showEmailAddresses, true) && !StringUtils.isEmpty(user.emailAddress)));\r
+ \r
+ PersonIdent person = new PersonIdent(user.getDisplayName(), user.emailAddress);\r
+ add(new GravatarImage("gravatar", person, 210));\r
+ \r
+ List<RepositoryModel> repositories = getRepositories(params);\r
+ \r
+ Collections.sort(repositories, new Comparator<RepositoryModel>() {\r
+ @Override\r
+ public int compare(RepositoryModel o1, RepositoryModel o2) {\r
+ // reverse-chronological sort\r
+ return o2.lastChange.compareTo(o1.lastChange);\r
+ }\r
+ });\r
+\r
+ final ListDataProvider<RepositoryModel> dp = new ListDataProvider<RepositoryModel>(repositories);\r
+ DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("repositoryList", dp) {\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ public void populateItem(final Item<RepositoryModel> item) {\r
+ final RepositoryModel entry = item.getModelObject();\r
+ \r
+ ProjectRepositoryPanel row = new ProjectRepositoryPanel("repository", \r
+ getLocalizer(), this, showAdmin, entry, getAccessRestrictions());\r
+ item.add(row);\r
+ }\r
+ };\r
+ add(dataView);\r
+ }\r
+\r
+ @Override\r
+ protected void addDropDownMenus(List<PageRegistration> pages) {\r
+ PageParameters params = getPageParameters();\r
+\r
+ DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",\r
+ UserPage.class);\r
+ // preserve time filter option on repository choices\r
+ menu.menuItems.addAll(getRepositoryFilterItems(params));\r
+\r
+ // preserve repository filter option on time choices\r
+ menu.menuItems.addAll(getTimeFilterItems(params));\r
+\r
+ if (menu.menuItems.size() > 0) {\r
+ // Reset Filter\r
+ menu.menuItems.add(new DropDownMenuItem(getString("gb.reset"), null, null));\r
+ }\r
+\r
+ pages.add(menu);\r
+ }\r
+}\r
public GravatarImage(String id, PersonIdent person, int width) {\r
super(id);\r
\r
- String email = person.getEmailAddress().toLowerCase();\r
+ String email = person.getEmailAddress() == null ? person.getName().toLowerCase() : person.getEmailAddress().toLowerCase();\r
String hash = StringUtils.getMD5(email);\r
Link<Void> link = new BookmarkablePageLink<Void>("link", GravatarProfilePage.class,\r
WicketUtils.newObjectParameter(hash));\r
private boolean hasMore;\r
\r
public HistoryPanel(String wicketId, final String repositoryName, final String objectId,\r
- final String path, Repository r, int limit, int pageOffset) {\r
+ final String path, Repository r, int limit, int pageOffset, boolean showRemoteRefs) {\r
super(wicketId);\r
boolean pageResults = limit <= 0;\r
int itemsPerPage = GitBlit.getInteger(Keys.web.itemsPerPage, 50);\r
}\r
final boolean isTree = matchingPath == null ? true : matchingPath.isTree();\r
\r
- final Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(r);\r
+ final Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(r, showRemoteRefs);\r
List<RevCommit> commits;\r
if (pageResults) {\r
// Paging result set\r
private boolean hasMore;\r
\r
public LogPanel(String wicketId, final String repositoryName, final String objectId,\r
- Repository r, int limit, int pageOffset) {\r
+ Repository r, int limit, int pageOffset, boolean showRemoteRefs) {\r
super(wicketId);\r
boolean pageResults = limit <= 0;\r
int itemsPerPage = GitBlit.getInteger(Keys.web.itemsPerPage, 50);\r
itemsPerPage = 50;\r
}\r
\r
- final Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(r);\r
+ final Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(r, showRemoteRefs);\r
List<RevCommit> commits;\r
if (pageResults) {\r
// Paging result set\r
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" \r
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" \r
+ xml:lang="en" \r
+ lang="en"> \r
+\r
+<wicket:panel>\r
+ <wicket:fragment wicket:id="repositoryAdminLinks">\r
+ <span class="link">\r
+ <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>\r
+ | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>\r
+ | <a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a>\r
+ | <a wicket:id="deleteRepository"><wicket:message key="gb.delete">[delete]</wicket:message></a>\r
+ </span>\r
+ </wicket:fragment>\r
+\r
+ <wicket:fragment wicket:id="repositoryOwnerLinks">\r
+ <span class="link">\r
+ <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>\r
+ | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>\r
+ | <a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a>\r
+ </span>\r
+ </wicket:fragment>\r
+\r
+ <wicket:fragment wicket:id="repositoryUserLinks">\r
+ <span class="link">\r
+ <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>\r
+ | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>\r
+ </span>\r
+ </wicket:fragment>\r
+\r
+ <wicket:fragment wicket:id="originFragment">\r
+ <p class="originRepository" style="margin-left:20px;" ><wicket:message key="gb.forkedFrom">[forked from]</wicket:message> <span wicket:id="originRepository">[origin repository]</span></p>\r
+ </wicket:fragment>\r
+\r
+ <div>\r
+ <div style="padding-top:15px;padding-bottom:15px;margin-right:15px;">\r
+ <div class="pull-right" style="text-align:right;padding-right:15px;">\r
+ <span wicket:id="repositoryLinks"></span>\r
+ <div>\r
+ <img class="inlineIcon" wicket:id="frozenIcon" />\r
+ <img class="inlineIcon" wicket:id="federatedIcon" />\r
+ \r
+ <a style="text-decoration: none;" wicket:id="tickets" wicket:message="title:gb.tickets">\r
+ <img style="border:0px;vertical-align:middle;" src="bug_16x16.png"></img>\r
+ </a>\r
+ <a style="text-decoration: none;" wicket:id="docs" wicket:message="title:gb.docs">\r
+ <img style="border:0px;vertical-align:middle;" src="book_16x16.png"></img>\r
+ </a>\r
+ <a style="text-decoration: none;" wicket:id="syndication" wicket:message="title:gb.feed">\r
+ <img style="border:0px;vertical-align:middle;" src="feed_16x16.png"></img>\r
+ </a>\r
+ </div>\r
+ <span style="color: #999;font-style:italic;font-size:0.8em;" wicket:id="repositoryOwner">[owner]</span>\r
+ </div> \r
+ \r
+ <div class="pageTitle" style="border:0px;">\r
+ <div>\r
+ <span class="repositorySwatch" wicket:id="repositorySwatch"></span>\r
+ <span class="repository" style="padding-left:3px;color:black;" wicket:id="repositoryName">[repository name]</span>\r
+ <img class="inlineIcon" style="vertical-align:baseline" wicket:id="accessRestrictionIcon" />\r
+ </div>\r
+ <span wicket:id="originRepository">[origin repository]</span>\r
+ </div>\r
+ \r
+ <div style="padding-left:20px;">\r
+ <div style="padding-bottom:10px" wicket:id="repositoryDescription">[repository description]</div>\r
+\r
+ <div style="color: #999;">\r
+ <wicket:message key="gb.lastChange">[last change]</wicket:message> <span wicket:id="repositoryLastChange">[last change]</span>,\r
+ <span style="font-size:0.8em;" wicket:id="repositorySize">[repository size]</span>\r
+ </div>\r
+ \r
+ <div class="hidden-phone hidden-tablet" wicket:id="repositoryCloneUrl">[repository clone url]</div>\r
+ </div>\r
+ </div>\r
+ </div>\r
+</wicket:panel>\r
+</html>
\ No newline at end of file
--- /dev/null
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.wicket.panels;\r
+\r
+import java.text.MessageFormat;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import org.apache.wicket.Component;\r
+import org.apache.wicket.Localizer;\r
+import org.apache.wicket.PageParameters;\r
+import org.apache.wicket.markup.html.basic.Label;\r
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;\r
+import org.apache.wicket.markup.html.link.ExternalLink;\r
+import org.apache.wicket.markup.html.link.Link;\r
+import org.apache.wicket.markup.html.panel.Fragment;\r
+\r
+import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.GitBlit;\r
+import com.gitblit.Keys;\r
+import com.gitblit.SyndicationServlet;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.ArrayUtils;\r
+import com.gitblit.utils.StringUtils;\r
+import com.gitblit.wicket.GitBlitWebSession;\r
+import com.gitblit.wicket.WicketUtils;\r
+import com.gitblit.wicket.pages.BasePage;\r
+import com.gitblit.wicket.pages.DocsPage;\r
+import com.gitblit.wicket.pages.EditRepositoryPage;\r
+import com.gitblit.wicket.pages.LogPage;\r
+import com.gitblit.wicket.pages.SummaryPage;\r
+import com.gitblit.wicket.pages.TicketsPage;\r
+import com.gitblit.wicket.pages.TreePage;\r
+\r
+public class ProjectRepositoryPanel extends BasePanel {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ public ProjectRepositoryPanel(String wicketId, Localizer localizer, Component owner,\r
+ final boolean isAdmin, final RepositoryModel entry,\r
+ final Map<AccessRestrictionType, String> accessRestrictions) {\r
+ super(wicketId);\r
+\r
+ final boolean showSwatch = GitBlit.getBoolean(Keys.web.repositoryListSwatches, true);\r
+ final boolean gitServlet = GitBlit.getBoolean(Keys.git.enableGitServlet, true);\r
+ final boolean showSize = GitBlit.getBoolean(Keys.web.showRepositorySizes, true);\r
+\r
+ // repository swatch\r
+ Component swatch;\r
+ if (entry.isBare) {\r
+ swatch = new Label("repositorySwatch", " ").setEscapeModelStrings(false);\r
+ } else {\r
+ swatch = new Label("repositorySwatch", "!");\r
+ WicketUtils.setHtmlTooltip(swatch, localizer.getString("gb.workingCopyWarning", owner));\r
+ }\r
+ WicketUtils.setCssBackground(swatch, entry.toString());\r
+ add(swatch);\r
+ swatch.setVisible(showSwatch);\r
+\r
+ PageParameters pp = WicketUtils.newRepositoryParameter(entry.name);\r
+ add(new LinkPanel("repositoryName", "list", StringUtils.getRelativePath(entry.projectPath,\r
+ StringUtils.stripDotGit(entry.name)), SummaryPage.class, pp));\r
+ add(new Label("repositoryDescription", entry.description).setVisible(!StringUtils\r
+ .isEmpty(entry.description)));\r
+\r
+ if (StringUtils.isEmpty(entry.originRepository)) {\r
+ add(new Label("originRepository").setVisible(false));\r
+ } else {\r
+ Fragment forkFrag = new Fragment("originRepository", "originFragment", this);\r
+ forkFrag.add(new LinkPanel("originRepository", null, StringUtils.stripDotGit(entry.originRepository), \r
+ SummaryPage.class, WicketUtils.newRepositoryParameter(entry.originRepository)));\r
+ add(forkFrag);\r
+ }\r
+\r
+ add(new BookmarkablePageLink<Void>("tickets", TicketsPage.class, pp).setVisible(entry.useTickets));\r
+ add(new BookmarkablePageLink<Void>("docs", DocsPage.class, pp).setVisible(entry.useDocs));\r
+\r
+ if (entry.isFrozen) {\r
+ add(WicketUtils.newImage("frozenIcon", "cold_16x16.png", localizer.getString("gb.isFrozen", owner)));\r
+ } else {\r
+ add(WicketUtils.newClearPixel("frozenIcon").setVisible(false));\r
+ }\r
+\r
+ if (entry.isFederated) {\r
+ add(WicketUtils.newImage("federatedIcon", "federated_16x16.png", localizer.getString("gb.isFederated", owner)));\r
+ } else {\r
+ add(WicketUtils.newClearPixel("federatedIcon").setVisible(false));\r
+ }\r
+ switch (entry.accessRestriction) {\r
+ case NONE:\r
+ add(WicketUtils.newBlankImage("accessRestrictionIcon").setVisible(false));\r
+ break;\r
+ case PUSH:\r
+ add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png",\r
+ accessRestrictions.get(entry.accessRestriction)));\r
+ break;\r
+ case CLONE:\r
+ add(WicketUtils.newImage("accessRestrictionIcon", "lock_pull_16x16.png",\r
+ accessRestrictions.get(entry.accessRestriction)));\r
+ break;\r
+ case VIEW:\r
+ add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png",\r
+ accessRestrictions.get(entry.accessRestriction)));\r
+ break;\r
+ default:\r
+ add(WicketUtils.newBlankImage("accessRestrictionIcon"));\r
+ }\r
+\r
+ add(new Label("repositoryOwner", StringUtils.isEmpty(entry.owner) ? "" : (entry.owner + " ("\r
+ + localizer.getString("gb.owner", owner) + ")")));\r
+\r
+ UserModel user = GitBlitWebSession.get().getUser();\r
+ Fragment repositoryLinks;\r
+ boolean showOwner = user != null && user.username.equalsIgnoreCase(entry.owner);\r
+ // owner of personal repository gets admin powers\r
+ boolean showAdmin = isAdmin || entry.isUsersPersonalRepository(user.username);\r
+\r
+ if (showAdmin || showOwner) {\r
+ repositoryLinks = new Fragment("repositoryLinks", showAdmin ? "repositoryAdminLinks"\r
+ : "repositoryOwnerLinks", this);\r
+ repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", EditRepositoryPage.class,\r
+ WicketUtils.newRepositoryParameter(entry.name)));\r
+ if (showAdmin) {\r
+ Link<Void> deleteLink = new Link<Void>("deleteRepository") {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ @Override\r
+ public void onClick() {\r
+ if (GitBlit.self().deleteRepositoryModel(entry)) {\r
+ info(MessageFormat.format(getString("gb.repositoryDeleted"), entry));\r
+ // TODO dp.remove(entry);\r
+ } else {\r
+ error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), entry));\r
+ }\r
+ }\r
+ };\r
+ deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(\r
+ localizer.getString("gb.deleteRepository", owner), entry)));\r
+ repositoryLinks.add(deleteLink);\r
+ }\r
+ } else {\r
+ repositoryLinks = new Fragment("repositoryLinks", "repositoryUserLinks", this);\r
+ }\r
+\r
+ repositoryLinks.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils\r
+ .newRepositoryParameter(entry.name)).setEnabled(entry.hasCommits));\r
+\r
+ repositoryLinks.add(new BookmarkablePageLink<Void>("log", LogPage.class, WicketUtils\r
+ .newRepositoryParameter(entry.name)).setEnabled(entry.hasCommits));\r
+\r
+ add(repositoryLinks);\r
+\r
+ String lastChange;\r
+ if (entry.lastChange.getTime() == 0) {\r
+ lastChange = "--";\r
+ } else {\r
+ lastChange = getTimeUtils().timeAgo(entry.lastChange);\r
+ }\r
+ Label lastChangeLabel = new Label("repositoryLastChange", lastChange);\r
+ add(lastChangeLabel);\r
+ WicketUtils.setCssClass(lastChangeLabel, getTimeUtils().timeAgoCss(entry.lastChange));\r
+\r
+ if (entry.hasCommits) {\r
+ // Existing repository\r
+ add(new Label("repositorySize", entry.size).setVisible(showSize));\r
+ } else {\r
+ // New repository\r
+ add(new Label("repositorySize", localizer.getString("gb.empty", owner)).setEscapeModelStrings(false));\r
+ }\r
+\r
+ add(new ExternalLink("syndication", SyndicationServlet.asLink("", entry.name, null, 0)));\r
+\r
+ List<String> repositoryUrls = new ArrayList<String>();\r
+ if (gitServlet) {\r
+ // add the Gitblit repository url\r
+ repositoryUrls.add(BasePage.getRepositoryUrl(entry));\r
+ }\r
+ repositoryUrls.addAll(GitBlit.self().getOtherCloneUrls(entry.name));\r
+\r
+ String primaryUrl = ArrayUtils.isEmpty(repositoryUrls) ? "" : repositoryUrls.remove(0);\r
+ add(new RepositoryUrlPanel("repositoryCloneUrl", primaryUrl));\r
+ }\r
+}\r
\r
<wicket:fragment wicket:id="groupRepositoryRow">\r
<td colspan="1"><span wicket:id="groupName">[group name]</span></td>\r
- <td colspan="6"><span class="hidden-phone" style="font-weight:normal;color:#666;" wicket:id="groupDescription">[description]</span></td>\r
+ <td colspan="6" style="padding: 2px;"><span class="hidden-phone" style="font-weight:normal;color:#666;" wicket:id="groupDescription">[description]</span></td>\r
</wicket:fragment>\r
\r
<wicket:fragment wicket:id="repositoryRow">\r
import com.gitblit.wicket.pages.ProjectPage;\r
import com.gitblit.wicket.pages.RepositoriesPage;\r
import com.gitblit.wicket.pages.SummaryPage;\r
+import com.gitblit.wicket.pages.UserPage;\r
\r
public class RepositoriesPanel extends BasePanel {\r
\r
}\r
\r
Map<String, ProjectModel> projects = new HashMap<String, ProjectModel>();\r
- for (ProjectModel project : GitBlit.self().getProjectModels(user)) {\r
+ for (ProjectModel project : GitBlit.self().getProjectModels(user, true)) {\r
projects.put(project.name, project);\r
}\r
List<RepositoryModel> groupedModels = new ArrayList<RepositoryModel>();\r
\r
final String baseUrl = WicketUtils.getGitblitURL(getRequest());\r
final boolean showSwatch = GitBlit.getBoolean(Keys.web.repositoryListSwatches, true);\r
-\r
+ \r
DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("row", dp) {\r
private static final long serialVersionUID = 1L;\r
int counter;\r
currGroupName = entry.name;\r
Fragment row = new Fragment("rowContent", "groupRepositoryRow", this);\r
item.add(row);\r
- row.add(new LinkPanel("groupName", null, entry.toString(), ProjectPage.class, WicketUtils.newProjectParameter(entry.name)));\r
- row.add(new Label("groupDescription", entry.description == null ? "":entry.description));\r
+ \r
+ String name = entry.toString();\r
+ if (name.charAt(0) == '~') {\r
+ // user page\r
+ String username = name.substring(1);\r
+ UserModel user = GitBlit.self().getUserModel(username);\r
+ row.add(new LinkPanel("groupName", null, user == null ? username : user.getDisplayName(), UserPage.class, WicketUtils.newUsernameParameter(username)));\r
+ row.add(new Label("groupDescription", getString("gb.personalRepositories")));\r
+ } else {\r
+ // project page\r
+ row.add(new LinkPanel("groupName", null, name, ProjectPage.class, WicketUtils.newProjectParameter(name)));\r
+ row.add(new Label("groupDescription", entry.description == null ? "":entry.description));\r
+ }\r
WicketUtils.setCssClass(item, "group");\r
// reset counter so that first row is light background\r
counter = 0;\r
WicketUtils.setCssClass(lastChangeLabel, getTimeUtils().timeAgoCss(entry.lastChange));\r
\r
boolean showOwner = user != null && user.username.equalsIgnoreCase(entry.owner);\r
- if (showAdmin) {\r
+ boolean myPersonalRepository = showOwner && entry.isUsersPersonalRepository(user.username);\r
+ if (showAdmin || myPersonalRepository) {\r
Fragment repositoryLinks = new Fragment("repositoryLinks",\r
"repositoryAdminLinks", this);\r
repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository",\r
private boolean hasMore;\r
\r
public SearchPanel(String wicketId, final String repositoryName, final String objectId,\r
- final String value, Constants.SearchType searchType, Repository r, int limit, int pageOffset) {\r
+ final String value, Constants.SearchType searchType, Repository r, int limit, int pageOffset,\r
+ boolean showRemoteRefs) {\r
super(wicketId);\r
boolean pageResults = limit <= 0;\r
int itemsPerPage = GitBlit.getInteger(Keys.web.itemsPerPage, 50);\r
\r
RevCommit commit = JGitUtils.getCommit(r, objectId);\r
\r
- final Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(r);\r
+ final Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(r, showRemoteRefs);\r
List<RevCommit> commits;\r
if (pageResults) {\r
// Paging result set\r