.gitmodules
New file @@ -0,0 +1,3 @@ [submodule "src/main/distrib/data/gitignore"] path = src/main/distrib/data/gitignore url = https://github.com/github/gitignore.git README.markdown
@@ -4,13 +4,18 @@ Gitblit is an open source, pure Java Git solution for managing, viewing, and serving [Git](http://git-scm.com) repositories. More information about Gitblit can be found [here](http://gitblit.com). [  ](https://bintray.com/gitblit/releases/stable/_latestVersion) <a href='https://bintray.com/gitblit/releases/gitblit/_latestVersion'><img src='https://api.bintray.com/packages/gitblit/releases/gitblit/images/download.png'></a> License ------- Gitblit is distributed under the terms of the [Apache Software Foundation license, version 2.0](http://www.apache.org/licenses/LICENSE-2.0). The text of the license is included in the file LICENSE in the root of the project. Contributing ------------ GitHub pull requests are preferred. Any contributions must be distributed under the terms of the [Apache Software Foundation license, version 2.0](http://www.apache.org/licenses/LICENSE-2.0). Java Runtime Requirement ------------------------------------ @@ -25,6 +30,10 @@ Building Gitblit ---------------- Gitblit uses submodules. Make sure to clone using `--recursive` OR to execute `git submodule update --init --recursive`. [Eclipse](http://eclipse.org) is recommended for development as the project settings are preconfigured. 1. Import the gitblit project into your Eclipse workspace. @@ -39,4 +48,4 @@ Building Tips & Tricks ---------------------- 1. If you are running Ant from an ANSI-capable console, consider setting the `MX_COLOR` environment variable before executing Ant.<pre>set MX_COLOR=true</pre> 2. The build script will honor your Maven proxy settings. If you need to fine-tune this, please review the [settings.moxie](http://gitblit.github.io/moxie/settings.html) documentation. 2. The build script will honor your Maven proxy settings. If you need to fine-tune this, please review the [settings.moxie](http://gitblit.github.io/moxie/settings.html) documentation. build.xml
@@ -919,6 +919,12 @@ <include name="subgit.groovy" /> </fileset> </copy> <mkdir dir="@{toDir}/gitignore" /> <copy todir="@{toDir}/gitignore"> <fileset dir="${project.distrib.dir}/data/gitignore"> <include name="*.gitignore" /> </fileset> </copy> </sequential> </macrodef> releases.moxie
@@ -27,6 +27,7 @@ - Move repository deletion functions to the edit repository page AND allow deletion to be disabled (pr-180, ticket-67) - Update the Korean translation (pr-184, ticket-69) - Overhaul the EmptyRepositoryPage (ticket-73) - Overhauled the edit repository page (ticket-76) - Process bugtraq links in the ticket description and comments (ticket-78) additions: - Add My Tickets page (issue-215, ticket-15) @@ -36,6 +37,7 @@ - Add FORK_REPOSITORY RPC request type (issue-371, pr-161, ticket-65) - Add object type (ot) parameter for RSS queries to retrieve tag details (pr-165, ticket-66) - Add setting to allow STARTTLS without requiring SMTPS (pr-183) - Simplified repository creation, offer simple README generation, and insertion of a pre-defined .gitignore file (ticket-76) - Added an extension point for monitoring onStartup and onShutdown (ticket-79) - Tag server-side merges when incremental push tags are enabled (issue-432, ticket-85) - Add setting to control default thread pool size for miscellaneous background tasks (ticket-92) @@ -55,6 +57,7 @@ - { name: 'web.allowDeletingNonEmptyRepositories', defaultValue: 'true' } - { name: 'mail.starttls', defaultValue: 'false' } - { name: 'execution.defaultThreadPoolSize', defaultValue: '1' } - { name: 'git.gitignoreFolder', defaultValue: '${baseFolder}/gitignore' } } # src/main/distrib/data/gitblit.properties
@@ -271,6 +271,11 @@ # SINCE 1.4.0 git.createRepositoriesShared = false # Directory for gitignore templates used during repository creation. # # SINCE 1.6.0 git.gitignoreFolder = ${baseFolder}/gitignore # Enable JGit-based garbage collection. (!!EXPERIMENTAL!!) # # USE AT YOUR OWN RISK! src/main/distrib/data/gitignore
New file @@ -0,0 +1 @@ Subproject commit 097db81c08b138dea7cb031eb18eeb16afe44bdf src/main/java/com/gitblit/Constants.java
@@ -122,6 +122,14 @@ public static final String R_TICKETS_PATCHSETS = "refs/tickets/"; public static final String R_MASTER = "refs/heads/master"; public static final String MASTER = "master"; public static final String R_DEVELOP = "refs/heads/develop"; public static final String DEVELOP = "develop"; public static String getVersion() { String v = Constants.class.getPackage().getImplementationVersion(); if (v == null) { src/main/java/com/gitblit/servlet/GitblitContext.java
@@ -372,6 +372,22 @@ } } // Copy the included gitignore files to the configured gitignore folder String gitignorePath = webxmlSettings.getString(Keys.git.gitignoreFolder, "gitignore"); File localGitignores = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, base, gitignorePath); if (!localGitignores.exists()) { File warGitignores = new File(contextFolder, "/WEB-INF/data/gitignore"); if (!warGitignores.equals(localGitignores)) { try { com.gitblit.utils.FileUtils.copy(localGitignores, warGitignores.listFiles()); } catch (IOException e) { logger.error(MessageFormat.format( "Failed to copy included .gitignore files from {0} to {1}", warGitignores, localGitignores)); } } } // merge the WebXmlSettings into the runtime settings (for backwards-compatibilty) runtimeSettings.merge(webxmlSettings); src/main/java/com/gitblit/wicket/GitBlitWebApp.java
@@ -57,6 +57,7 @@ import com.gitblit.wicket.pages.DocPage; import com.gitblit.wicket.pages.DocsPage; import com.gitblit.wicket.pages.EditMilestonePage; import com.gitblit.wicket.pages.EditRepositoryPage; import com.gitblit.wicket.pages.EditTicketPage; import com.gitblit.wicket.pages.ExportTicketPage; import com.gitblit.wicket.pages.FederationRegistrationPage; @@ -71,6 +72,7 @@ import com.gitblit.wicket.pages.MyDashboardPage; import com.gitblit.wicket.pages.MyTicketsPage; import com.gitblit.wicket.pages.NewMilestonePage; import com.gitblit.wicket.pages.NewRepositoryPage; import com.gitblit.wicket.pages.NewTicketPage; import com.gitblit.wicket.pages.OverviewPage; import com.gitblit.wicket.pages.PatchPage; @@ -91,6 +93,8 @@ public class GitBlitWebApp extends WebApplication implements GitblitWicketApp { private final Class<? extends WebPage> homePageClass = MyDashboardPage.class; private final Class<? extends WebPage> newRepositoryPageClass = NewRepositoryPage.class; private final Map<String, CacheControl> cacheablePages = new HashMap<String, CacheControl>(); @@ -207,6 +211,8 @@ mount("/proposal", ReviewProposalPage.class, "t"); mount("/registration", FederationRegistrationPage.class, "u", "n"); mount("/new", NewRepositoryPage.class); mount("/edit", EditRepositoryPage.class, "r"); mount("/activity", ActivityPage.class, "r", "h"); mount("/lucene", LuceneSearchPage.class); mount("/project", ProjectPage.class, "p"); @@ -262,6 +268,10 @@ return homePageClass; } public Class<? extends WebPage> getNewRepositoryPage() { return newRepositoryPageClass; } /* (non-Javadoc) * @see com.gitblit.wicket.Webapp#isCacheablePage(java.lang.String) */ src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
@@ -133,7 +133,7 @@ gb.status = status gb.origin = origin gb.headRef = default branch (HEAD) gb.headRefDescription = change the ref that HEAD links to. e.g. refs/heads/master gb.headRefDescription = The default branch that will be cloned and displayed on the Summary page. gb.federationStrategy = federation strategy gb.federationRegistration = federation registration gb.federationResults = federation pull results @@ -223,8 +223,8 @@ gb.noHits = no hits gb.authored = authored gb.committed = committed gb.indexedBranches = indexed branches gb.indexedBranchesDescription = select the branches to include in your Lucene index gb.indexedBranches = Indexed Branches gb.indexedBranchesDescription = Select the branches to be indexed by Lucene gb.noIndexedRepositoriesWarning = none of your repositories are configured for Lucene indexing gb.undefinedQueryWarning = query is undefined! gb.noSelectedRepositoriesWarning = please select one or more repositories! @@ -685,4 +685,41 @@ gb.administration = administration gb.plugins = plugins gb.extensions = extensions gb.pleaseSelectProject = Please select the project! gb.accessPolicy = Access Policy gb.accessPolicyDescription = Choose an access policy to control repository visibility and git permissions. gb.anonymousPolicy = Anonymous View, Clone, & Push gb.anonymousPolicyDescription = Anyone can see, clone, and push to this repository. gb.authenticatedPushPolicy = Restrict Push (Authenticated) gb.authenticatedPushPolicyDescription = Anyone can see and clone this repository. All authenticated users have RW+ push permission. gb.namedPushPolicy = Restrict Push (Named) gb.namedPushPolicyDescription = Anyone can see and clone this repository. You choose who can push. gb.clonePolicy = Restrict Clone & Push gb.clonePolicyDescription = Anyone can see this repository. You choose who can clone and push. gb.viewPolicy = Restrict View, Clone, & Push gb.viewPolicyDescription = You choose who can see, clone, and push to this repository. gb.initialCommit = Initial Commit gb.initialCommitDescription = This will allow you to <code>git clone</code> this repository immediately. Skip this step if you have already run <code>git init</code> locally. gb.initWithReadme = Include a README gb.initWithReadmeDescription = This will generate a simple README document for your repository. gb.initWithGitignore = Include a .gitignore file gb.initWithGitignoreDescription = This will insert a config file that instructs your Git clients to ignore files or directories that match defined patterns. gb.pleaseSelectGitIgnore = Please select a .gitignore file gb.receive = receive gb.permissions = permissions gb.ownersDescription = Owners can manage all repository settings but they are not allowed to rename a repository unless it is their personal repository. gb.userPermissionsDescription = You can specify individual user permissions. These settings will override team or regex permissions. gb.teamPermissionsDescription = You can specify individual team permissions. These settings will override regex permissions. gb.ticketSettings = Ticket Settings gb.receiveSettings = Receive Settings gb.receiveSettingsDescription = The receive settings control pushes to the repository. gb.preReceiveDescription = Pre-receive hooks are executed after commits are received but <em>BEFORE</em> the refs are updated.<p>This is the appropriate hook for rejecting a push.</p> gb.postReceiveDescription = Post-receive hooks are executed after commits are received but <em>AFTER</em> the refs are updated.<p>This is the appropriate hook for notifications, build triggers, etc.</p> gb.federationStrategyDescription = Control if and how to federate this repository with another Gitblit. gb.federationSetsDescription = This repository will be included in the selected federation sets. gb.miscellaneous = miscellaneous gb.originDescription = The url from which this repository was cloned. gb.gc = GC gb.garbageCollection = Garbage Collection gb.garbageCollectionDescription = The garbage collector will pack loose objects pushed from clients and will remove unreferenced objects from the repository. gb.commitMessageRendererDescription = Commit messages can be displayed as plaintext or as rendered markup. src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html
@@ -9,14 +9,17 @@ <form style="padding-top:5px;" wicket:id="editForm"> <div class="row"> <div class="span12"> <div class="tabbable"> <div class="tabbable tabs-left"> <!-- tab titles --> <ul class="nav nav-tabs"> <li class="active"><a href="#general" data-toggle="tab"><wicket:message key="gb.general"></wicket:message></a></li> <li><a href="#permissions" data-toggle="tab"><wicket:message key="gb.accessPermissions"></wicket:message></a></li> <li><a href="#permissions" data-toggle="tab"><wicket:message key="gb.permissions"></wicket:message></a></li> <li><a href="#receive" data-toggle="tab"><wicket:message key="gb.receive"></wicket:message></a></li> <li><a href="#tickets" data-toggle="tab"><wicket:message key="gb.tickets"></wicket:message></a></li> <li><a href="#federation" data-toggle="tab"><wicket:message key="gb.federation"></wicket:message></a></li> <li><a href="#search" data-toggle="tab"><wicket:message key="gb.search"></wicket:message></a></li> <li><a href="#hooks" data-toggle="tab"><wicket:message key="gb.hookScripts"></wicket:message></a></li> <li><a href="#gc" data-toggle="tab"><wicket:message key="gb.gc"></wicket:message></a></li> <li><a href="#misc" data-toggle="tab"><wicket:message key="gb.miscellaneous"></wicket:message></a></li> </ul> <!-- tab content --> @@ -24,99 +27,167 @@ <!-- general tab --> <div class="tab-pane active" id="general"> <table class="plain"> <tbody class="settings"> <tr><th><wicket:message key="gb.name"></wicket:message></th><td class="edit"><input class="span4" type="text" wicket:id="name" id="name" size="40" tabindex="1" /> <span class="help-inline"><wicket:message key="gb.nameDescription"></wicket:message></span></td></tr> <tr><th><wicket:message key="gb.description"></wicket:message></th><td class="edit"><input class="span4" type="text" wicket:id="description" size="40" tabindex="2" /></td></tr> <tr><th colspan="2"><hr/></th></tr> <tr><th><wicket:message key="gb.origin"></wicket:message></th><td class="edit"><input class="span5" type="text" wicket:id="origin" size="80" tabindex="3" /></td></tr> <tr><th><wicket:message key="gb.headRef"></wicket:message></th><td class="edit"><select class="span3" wicket:id="HEAD" tabindex="4" /> <span class="help-inline"><wicket:message key="gb.headRefDescription"></wicket:message></span></td></tr> <tr><th><wicket:message key="gb.gcPeriod"></wicket:message></th><td class="edit"><select class="span2" wicket:id="gcPeriod" tabindex="5" /> <span class="help-inline"><wicket:message key="gb.gcPeriodDescription"></wicket:message></span></td></tr> <tr><th><wicket:message key="gb.gcThreshold"></wicket:message></th><td class="edit"><input class="span1" type="text" wicket:id="gcThreshold" tabindex="6" /> <span class="help-inline"><wicket:message key="gb.gcThresholdDescription"></wicket:message></span></td></tr> <tr><th colspan="2"><hr/></th></tr> <tr><th><wicket:message key="gb.acceptNewTickets"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="acceptNewTickets" tabindex="7" /> <span class="help-inline"><wicket:message key="gb.acceptNewTicketsDescription"></wicket:message></span></label></td></tr> <tr><th><wicket:message key="gb.acceptNewPatchsets"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="acceptNewPatchsets" tabindex="8" /> <span class="help-inline"><wicket:message key="gb.acceptNewPatchsetsDescription"></wicket:message></span></label></td></tr> <tr><th><wicket:message key="gb.requireApproval"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="requireApproval" tabindex="9" /> <span class="help-inline"><wicket:message key="gb.requireApprovalDescription"></wicket:message></span></label></td></tr> <tr><th><wicket:message key="gb.mergeTo"></wicket:message></th><td class="edit"><select class="span2" wicket:id="mergeTo" tabindex="10" /> <span class="help-inline"><wicket:message key="gb.mergeToDescription"></wicket:message></span></td></tr> <tr><th colspan="2"><hr/></th></tr> <tr><th><wicket:message key="gb.enableIncrementalPushTags"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="useIncrementalPushTags" tabindex="11" /> <span class="help-inline"><wicket:message key="gb.useIncrementalPushTagsDescription"></wicket:message></span></label></td></tr> <tr><th><wicket:message key="gb.showRemoteBranches"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="showRemoteBranches" tabindex="12" /> <span class="help-inline"><wicket:message key="gb.showRemoteBranchesDescription"></wicket:message></span></label></td></tr> <tr><th><wicket:message key="gb.skipSizeCalculation"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="skipSizeCalculation" tabindex="13" /> <span class="help-inline"><wicket:message key="gb.skipSizeCalculationDescription"></wicket:message></span></label></td></tr> <tr><th><wicket:message key="gb.skipSummaryMetrics"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="skipSummaryMetrics" tabindex="14" /> <span class="help-inline"><wicket:message key="gb.skipSummaryMetricsDescription"></wicket:message></span></label></td></tr> <tr><th><wicket:message key="gb.maxActivityCommits"></wicket:message></th><td class="edit"><select class="span2" wicket:id="maxActivityCommits" tabindex="15" /> <span class="help-inline"><wicket:message key="gb.maxActivityCommitsDescription"></wicket:message></span></td></tr> <tr><th><wicket:message key="gb.metricAuthorExclusions"></wicket:message></th><td class="edit"><input class="span8" type="text" wicket:id="metricAuthorExclusions" size="40" tabindex="16" /></td></tr> <tr><th><wicket:message key="gb.commitMessageRenderer"></wicket:message></th><td class="edit"><select class="span2" wicket:id="commitMessageRenderer" tabindex="17" /></td></tr> <tr><th colspan="2"><hr/></th></tr> <tr><th><wicket:message key="gb.mailingLists"></wicket:message></th><td class="edit"><input class="span8" type="text" wicket:id="mailingLists" size="40" tabindex="18" /></td></tr> </tbody> </table> <div wicket:id="namePanel"></div> <hr/> <div wicket:id="accessPolicyPanel"></div> </div> <!-- access permissions --> <div class="tab-pane" id="permissions"> <table class="plain"> <tbody class="settings"> <tr><th><wicket:message key="gb.owners"></wicket:message></th><td class="edit"><span wicket:id="owners" tabindex="19" /> </td></tr> <tr><th colspan="2"><hr/></th></tr> <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select class="span4" wicket:id="accessRestriction" tabindex="20" /></td></tr> <tr><th colspan="2"><hr/></th></tr> <tr><th><wicket:message key="gb.authorizationControl"></wicket:message></th><td style="padding:2px;"><span class="authorizationControl" wicket:id="authorizationControl"></span></td></tr> <tr><th colspan="2"><hr/></th></tr> <tr><th><wicket:message key="gb.isFrozen"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="isFrozen" tabindex="21" /> <span class="help-inline"><wicket:message key="gb.isFrozenDescription"></wicket:message></span></label></td></tr> <tr><th><wicket:message key="gb.allowForks"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="allowForks" tabindex="22" /> <span class="help-inline"><wicket:message key="gb.allowForksDescription"></wicket:message></span></label></td></tr> <tr><th><wicket:message key="gb.verifyCommitter"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="verifyCommitter" tabindex="23" /> <span class="help-inline"><wicket:message key="gb.verifyCommitterDescription"></wicket:message></span><br/><span class="help-inline" style="padding-left:10px;"><wicket:message key="gb.verifyCommitterNote"></wicket:message></span></label></td></tr> <tr><th colspan="2"><hr/></th></tr> <tr><th><wicket:message key="gb.userPermissions"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr> <tr><th colspan="2"><hr/></th></tr> <tr><th><wicket:message key="gb.teamPermissions"></wicket:message></th><td style="padding:2px;"><span wicket:id="teams"></span></td></tr> </tbody> </table> <h4><wicket:message key="gb.owners"></wicket:message></h4> <p><wicket:message key="gb.ownersDescription"></wicket:message></p> <div wicket:id="owners"></div> <hr /> <h4><wicket:message key="gb.userPermissions"></wicket:message></h4> <p><wicket:message key="gb.userPermissionsDescription"></wicket:message></p> <div wicket:id="users"></div> <hr /> <h4><wicket:message key="gb.teamPermissions"></wicket:message></h4> <p><wicket:message key="gb.teamPermissionsDescription"></wicket:message></p> <div wicket:id="teams"></div> </div> <!-- federation --> <div class="tab-pane" id="federation"> <!-- receive --> <div class="tab-pane" id="receive"> <h4><wicket:message key="gb.receiveSettings"></wicket:message></h4> <p><wicket:message key="gb.receiveSettingsDescription"></wicket:message></p> <hr/> <div wicket:id="isFrozen"></div> <div wicket:id="verifyCommitter"></div> <div wicket:id="incrementalPushTags"></div> <hr /> <h4><wicket:message key="gb.preReceiveScripts"></wicket:message></h4> <p><wicket:message key="gb.preReceiveDescription"></wicket:message></p> <table class="plain"> <tbody class="settings"> <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span4" wicket:id="federationStrategy" tabindex="24" /></td></tr> <tr><th><wicket:message key="gb.federationSets"></wicket:message></th><td style="padding:2px;"><span wicket:id="federationSets"></span></td></tr> <tr> <td style="padding:2px;"><span wicket:id="preReceiveScripts"></span></td> <td style="vertical-align: top;"><span wicket:id="inheritedPreReceive"></span></td> </tr> </tbody> </table> <hr /> <h4><wicket:message key="gb.postReceiveScripts"></wicket:message></h4> <p><wicket:message key="gb.postReceiveDescription"></wicket:message></p> <table class="plain"> <tbody class="settings"> <tr> <td style="padding:2px;"><span wicket:id="postReceiveScripts"></span></td> <td style="vertical-align: top;"><span wicket:id="inheritedPostReceive"></span></td> </tr> </tbody> </table> <div wicket:id="customFieldsSection"> <hr /> <h4><wicket:message key="gb.customFields"></wicket:message></h4> <p><wicket:message key="gb.customFieldsDescription"></wicket:message></p> <table class="plain"> <tbody class="settings"> <tr wicket:id="customFieldsListView"><th><span wicket:id="customFieldLabel"></span></th><td class="edit"><input class="span8" type="text" wicket:id="customFieldValue" /></td></tr> </tbody> </table> </div> </div> <!-- tickets tab --> <div class="tab-pane" id="tickets"> <h4><wicket:message key="gb.ticketSettings"></wicket:message></h4> <p><wicket:message key="gb.ticketsWelcome"></wicket:message></p> <hr/> <div wicket:id="acceptNewPatchsets"></div> <div wicket:id="acceptNewTickets"></div> <div wicket:id="requireApproval"></div> <div wicket:id="mergeTo"></div> </div> <!-- federation --> <div class="tab-pane" id="federation"> <h4><wicket:message key="gb.federation"></wicket:message></h4> <p><wicket:message key="gb.federationRepositoryDescription"></wicket:message></p> <hr/> <div wicket:id="federationStrategy"></div> <hr /> <h4><wicket:message key="gb.federationSets"></wicket:message></h4> <p><wicket:message key="gb.federationSetsDescription"></wicket:message></p> <div wicket:id="federationSets"></div> </div> <!-- search --> <div class="tab-pane" id="search"> <table class="plain"> <tbody class="settings"> <tr><th><wicket:message key="gb.indexedBranches"></wicket:message></th><td style="padding:2px;"><span wicket:id="indexedBranches"></span></td></tr> </tbody> </table> <h4><wicket:message key="gb.indexedBranches"></wicket:message></h4> <p><wicket:message key="gb.indexedBranchesDescription"></wicket:message></p> <div wicket:id="indexedBranches"></div> </div> <!-- hooks --> <div class="tab-pane" id="hooks"> <table class="plain"> <tbody class="settings"> <tr><th><wicket:message key="gb.preReceiveScripts"></wicket:message><p></p><span wicket:id="inheritedPreReceive"></span></th><td style="padding:2px;"><span wicket:id="preReceiveScripts"></span></td></tr> <tr><th><wicket:message key="gb.postReceiveScripts"></wicket:message><p></p><span wicket:id="inheritedPostReceive"></span></th><td style="padding:2px;"><span wicket:id="postReceiveScripts"></span></td></tr> <div wicket:id="customFieldsSection"> <tr><td colspan="2"><h3><wicket:message key="gb.customFields"></wicket:message> <small><wicket:message key="gb.customFieldsDescription"></wicket:message></small></h3></td></tr> <tr wicket:id="customFieldsListView"><th><span wicket:id="customFieldLabel"></span></th><td class="edit"><input class="span8" type="text" wicket:id="customFieldValue" /></td></tr> </div> </tbody> </table> <!-- garbage collection --> <div class="tab-pane" id="gc"> <h4><wicket:message key="gb.garbageCollection"></wicket:message></h4> <p><wicket:message key="gb.garbageCollectionDescription"></wicket:message></p> <div wicket:id="gcPeriod"></div> <div wicket:id="gcThreshold"></div> </div> <!-- misc --> <div class="tab-pane" id="misc"> <div wicket:id="origin"></div> <div wicket:id="head"></div> <hr/> <div wicket:id="showRemoteBranches"></div> <div wicket:id="skipSizeCalculation"></div> <div wicket:id="skipSummaryMetrics"></div> <div wicket:id="maxActivityCommits"></div> <div wicket:id="commitMessageRenderer"></div> <div wicket:id="metricAuthorExclusions"></div> <div wicket:id="mailingLists"></div> </div> <div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" /> <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /> <input class="btn btn-danger" type="submit" value="Delete" wicket:message="value:gb.delete" wicket:id="delete" /></div> </div> </div> </div> </div> </div> <div class="row"> <div class="span12"> <div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" /> <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /> <input class="btn btn-danger" type="submit" value="Delete" wicket:message="value:gb.delete" wicket:id="delete" /></div> </div> </div> </form> </body> </wicket:extend> </html> src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java
@@ -29,7 +29,6 @@ import org.apache.wicket.PageParameters; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.behavior.SimpleAttributeModifier; import org.apache.wicket.extensions.markup.html.form.palette.Palette; import org.apache.wicket.markup.html.WebMarkupContainer; @@ -40,7 +39,6 @@ import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.IChoiceRenderer; import org.apache.wicket.markup.html.form.RadioChoice; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.link.Link; import org.apache.wicket.markup.html.list.ListItem; @@ -48,6 +46,7 @@ import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.model.util.CollectionModel; import org.apache.wicket.model.util.ListModel; import org.eclipse.jgit.lib.Repository; @@ -69,13 +68,22 @@ import com.gitblit.wicket.GitBlitWebSession; import com.gitblit.wicket.StringChoiceRenderer; import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.panels.AccessPolicyPanel; import com.gitblit.wicket.panels.BasePanel.JavascriptEventConfirmation; import com.gitblit.wicket.panels.BulletListPanel; import com.gitblit.wicket.panels.CheckboxOption; import com.gitblit.wicket.panels.ChoiceOption; import com.gitblit.wicket.panels.RegistrantPermissionsPanel; import com.gitblit.wicket.panels.RepositoryNamePanel; import com.gitblit.wicket.panels.TextOption; public class EditRepositoryPage extends RootSubPage { private final boolean isCreate; RepositoryNamePanel namePanel; AccessPolicyPanel accessPolicyPanel; private boolean isAdmin; @@ -196,7 +204,7 @@ } } final Palette<UserChoice> ownersPalette = new Palette<UserChoice>("owners", new ListModel<UserChoice>(owners), new CollectionModel<UserChoice>( persons), new ChoiceRenderer<UserChoice>(null, "userId"), 12, true); persons), new ChoiceRenderer<UserChoice>(null, "userId"), 12, false); // indexed local branches palette List<String> allLocalBranches = new ArrayList<String>(); @@ -263,59 +271,8 @@ @Override protected void onSubmit() { try { // confirm a repository name was entered if (repositoryModel.name == null && StringUtils.isEmpty(repositoryModel.name)) { error(getString("gb.pleaseSetRepositoryName")); if (!namePanel.updateModel(repositoryModel)) { return; } // ensure name is trimmed repositoryModel.name = repositoryModel.name.trim(); // automatically convert backslashes to forward slashes repositoryModel.name = repositoryModel.name.replace('\\', '/'); // Automatically replace // with / repositoryModel.name = repositoryModel.name.replace("//", "/"); // prohibit folder paths if (repositoryModel.name.startsWith("/")) { error(getString("gb.illegalLeadingSlash")); return; } if (repositoryModel.name.startsWith("../")) { error(getString("gb.illegalRelativeSlash")); return; } if (repositoryModel.name.contains("/../")) { error(getString("gb.illegalRelativeSlash")); return; } if (repositoryModel.name.endsWith("/")) { repositoryModel.name = repositoryModel.name.substring(0, repositoryModel.name.length() - 1); } // confirm valid characters in repository name Character c = StringUtils.findInvalidCharacter(repositoryModel.name); if (c != null) { error(MessageFormat.format(getString("gb.illegalCharacterRepositoryName"), c)); return; } if (user.canCreate() && !user.canAdmin() && allowEditName) { // ensure repository name begins with the user's path if (!repositoryModel.name.startsWith(user.getPersonalPath())) { error(MessageFormat.format(getString("gb.illegalPersonalRepositoryLocation"), user.getPersonalPath())); return; } if (repositoryModel.name.equals(user.getPersonalPath())) { // reset path prefix and show error repositoryModel.name = user.getPersonalPath() + "/"; error(getString("gb.pleaseSetRepositoryName")); return; } } // confirm access restriction selection @@ -426,33 +383,15 @@ } } catch (GitBlitException e) { error(e.getMessage()); namePanel.resetModel(repositoryModel); return; } setRedirect(false); if (isCreate) { setResponsePage(RepositoriesPage.class); } else { setResponsePage(SummaryPage.class, WicketUtils.newRepositoryParameter(repositoryModel.name)); } setResponsePage(SummaryPage.class, WicketUtils.newRepositoryParameter(repositoryModel.name)); } }; // do not let the browser pre-populate these fields form.add(new SimpleAttributeModifier("autocomplete", "off")); // field names reflective match RepositoryModel fields form.add(new TextField<String>("name").setEnabled(allowEditName)); form.add(new TextField<String>("description")); form.add(ownersPalette); form.add(new CheckBox("allowForks").setEnabled(app().settings().getBoolean(Keys.web.allowForking, true))); DropDownChoice<AccessRestrictionType> accessRestriction = new DropDownChoice<AccessRestrictionType>("accessRestriction", AccessRestrictionType.choices(app().settings().getBoolean(Keys.git.allowAnonymousPushes, false)), new AccessRestrictionRenderer()); form.add(accessRestriction); form.add(new CheckBox("isFrozen")); // TODO enable origin definition form.add(new TextField<String>("origin").setEnabled(false/* isCreate */)); // allow relinking HEAD to a branch or tag other than master on edit repository // Determine available refs & branches List<String> availableRefs = new ArrayList<String>(); List<String> availableBranches = new ArrayList<String>(); if (!ArrayUtils.isEmpty(repositoryModel.availableRefs)) { @@ -465,58 +404,79 @@ } } } form.add(new DropDownChoice<String>("HEAD", availableRefs).setEnabled(availableRefs.size() > 0)); boolean gcEnabled = app().settings().getBoolean(Keys.git.enableGarbageCollection, false); int defaultGcPeriod = app().settings().getInteger(Keys.git.defaultGarbageCollectionPeriod, 7); if (repositoryModel.gcPeriod == 0) { repositoryModel.gcPeriod = defaultGcPeriod; } List<Integer> gcPeriods = Arrays.asList(1, 2, 3, 4, 5, 7, 10, 14 ); form.add(new DropDownChoice<Integer>("gcPeriod", gcPeriods, new GCPeriodRenderer()).setEnabled(gcEnabled)); form.add(new TextField<String>("gcThreshold").setEnabled(gcEnabled)); // do not let the browser pre-populate these fields form.add(new SimpleAttributeModifier("autocomplete", "off")); // federation strategies - remove ORIGIN choice if this repository has // no origin. List<FederationStrategy> federationStrategies = new ArrayList<FederationStrategy>( Arrays.asList(FederationStrategy.values())); if (StringUtils.isEmpty(repositoryModel.origin)) { federationStrategies.remove(FederationStrategy.FEDERATE_ORIGIN); } form.add(new DropDownChoice<FederationStrategy>("federationStrategy", federationStrategies, new FederationTypeRenderer())); form.add(new CheckBox("acceptNewPatchsets")); form.add(new CheckBox("acceptNewTickets")); form.add(new CheckBox("requireApproval")); form.add(new DropDownChoice<String>("mergeTo", availableBranches).setEnabled(availableBranches.size() > 0)); form.add(new CheckBox("useIncrementalPushTags")); form.add(new CheckBox("showRemoteBranches")); form.add(new CheckBox("skipSizeCalculation")); form.add(new CheckBox("skipSummaryMetrics")); List<Integer> maxActivityCommits = Arrays.asList(-1, 0, 25, 50, 75, 100, 150, 200, 250, 500); form.add(new DropDownChoice<Integer>("maxActivityCommits", maxActivityCommits, new MaxActivityCommitsRenderer())); metricAuthorExclusions = new Model<String>(ArrayUtils.isEmpty(repositoryModel.metricAuthorExclusions) ? "" : StringUtils.flattenStrings(repositoryModel.metricAuthorExclusions, " ")); form.add(new TextField<String>("metricAuthorExclusions", metricAuthorExclusions)); // // // GENERAL // namePanel = new RepositoryNamePanel("namePanel", repositoryModel); namePanel.setEditable(allowEditName); form.add(namePanel); mailingLists = new Model<String>(ArrayUtils.isEmpty(repositoryModel.mailingLists) ? "" : StringUtils.flattenStrings(repositoryModel.mailingLists, " ")); form.add(new TextField<String>("mailingLists", mailingLists)); form.add(indexedBranchesPalette); // XXX AccessPolicyPanel is defined later. List<AuthorizationControl> acList = Arrays.asList(AuthorizationControl.values()); final RadioChoice<AuthorizationControl> authorizationControl = new RadioChoice<Constants.AuthorizationControl>( "authorizationControl", acList, new AuthorizationControlRenderer()); form.add(authorizationControl); form.add(new ChoiceOption<String>("head", getString("gb.headRef"), getString("gb.headRefDescription"), new PropertyModel<String>(repositoryModel, "HEAD"), availableRefs)); final CheckBox verifyCommitter = new CheckBox("verifyCommitter"); verifyCommitter.setOutputMarkupId(true); form.add(verifyCommitter); // // PERMISSIONS // form.add(ownersPalette); form.add(usersPalette); form.add(teamsPalette); form.add(federationSetsPalette); // // TICKETS // form.add(new CheckboxOption("acceptNewPatchsets", getString("gb.acceptNewPatchsets"), getString("gb.acceptNewPatchsetsDescription"), new PropertyModel<Boolean>(repositoryModel, "acceptNewPatchsets"))); form.add(new CheckboxOption("acceptNewTickets", getString("gb.acceptNewTickets"), getString("gb.acceptNewTicketsDescription"), new PropertyModel<Boolean>(repositoryModel, "acceptNewPatchsets"))); form.add(new CheckboxOption("requireApproval", getString("gb.requireApproval"), getString("gb.requireApprovalDescription"), new PropertyModel<Boolean>(repositoryModel, "requireApproval"))); form.add(new ChoiceOption<String>("mergeTo", getString("gb.mergeTo"), getString("gb.mergeToDescription"), new PropertyModel<String>(repositoryModel, "mergeTo"), availableBranches)); // // RECEIVE // form.add(new CheckboxOption("isFrozen", getString("gb.isFrozen"), getString("gb.isFrozenDescription"), new PropertyModel<Boolean>(repositoryModel, "isFrozen"))); form.add(new CheckboxOption("incrementalPushTags", getString("gb.enableIncrementalPushTags"), getString("gb.useIncrementalPushTagsDescription"), new PropertyModel<Boolean>(repositoryModel, "useIncrementalPushTags"))); final CheckBox verifyCommitter = new CheckBox("checkbox", new PropertyModel<Boolean>(repositoryModel, "verifyCommitter")); verifyCommitter.setOutputMarkupId(true); form.add(new CheckboxOption("verifyCommitter", getString("gb.verifyCommitter"), getString("gb.verifyCommitterDescription") + "<br/>" + getString("gb.verifyCommitterNote"), verifyCommitter).setIsHtmlDescription(true)); form.add(preReceivePalette); form.add(new BulletListPanel("inheritedPreReceive", getString("gb.inherited"), app().repositories() .getPreReceiveScriptsInherited(repositoryModel))); @@ -528,17 +488,125 @@ customFieldsSection.add(customFieldsListView); form.add(customFieldsSection.setVisible(!app().settings().getString(Keys.groovy.customFields, "").isEmpty())); // // FEDERATION // List<FederationStrategy> federationStrategies = new ArrayList<FederationStrategy>( Arrays.asList(FederationStrategy.values())); // federation strategies - remove ORIGIN choice if this repository has no origin. if (StringUtils.isEmpty(repositoryModel.origin)) { federationStrategies.remove(FederationStrategy.FEDERATE_ORIGIN); } form.add(new ChoiceOption<FederationStrategy>("federationStrategy", getString("gb.federationStrategy"), getString("gb.federationStrategyDescription"), new DropDownChoice<FederationStrategy>( "choice", new PropertyModel<FederationStrategy>(repositoryModel, "federationStrategy"), federationStrategies, new FederationTypeRenderer()))); form.add(federationSetsPalette); // // SEARCH // form.add(indexedBranchesPalette); // // GARBAGE COLLECTION // boolean gcEnabled = app().settings().getBoolean(Keys.git.enableGarbageCollection, false); int defaultGcPeriod = app().settings().getInteger(Keys.git.defaultGarbageCollectionPeriod, 7); if (repositoryModel.gcPeriod == 0) { repositoryModel.gcPeriod = defaultGcPeriod; } List<Integer> gcPeriods = Arrays.asList(1, 2, 3, 4, 5, 7, 10, 14 ); form.add(new ChoiceOption<Integer>("gcPeriod", getString("gb.gcPeriod"), getString("gb.gcPeriodDescription"), new DropDownChoice<Integer>("choice", new PropertyModel<Integer>(repositoryModel, "gcPeriod"), gcPeriods, new GCPeriodRenderer())).setEnabled(gcEnabled)); form.add(new TextOption("gcThreshold", getString("gb.gcThreshold"), getString("gb.gcThresholdDescription"), "span1", new PropertyModel<String>(repositoryModel, "gcThreshold")).setEnabled(gcEnabled)); // // MISCELLANEOUS // form.add(new TextOption("origin", getString("gb.origin"), getString("gb.originDescription"), "span6", new PropertyModel<String>(repositoryModel, "origin")).setEnabled(false)); form.add(new CheckboxOption("showRemoteBranches", getString("gb.showRemoteBranches"), getString("gb.showRemoteBranchesDescription"), new PropertyModel<Boolean>(repositoryModel, "showRemoteBranches"))); form.add(new CheckboxOption("skipSizeCalculation", getString("gb.skipSizeCalculation"), getString("gb.skipSizeCalculationDescription"), new PropertyModel<Boolean>(repositoryModel, "skipSizeCalculation"))); form.add(new CheckboxOption("skipSummaryMetrics", getString("gb.skipSummaryMetrics"), getString("gb.skipSummaryMetricsDescription"), new PropertyModel<Boolean>(repositoryModel, "skipSummaryMetrics"))); List<Integer> maxActivityCommits = Arrays.asList(-1, 0, 25, 50, 75, 100, 150, 200, 250, 500); form.add(new ChoiceOption<Integer>("maxActivityCommits", getString("gb.maxActivityCommits"), getString("gb.maxActivityCommitsDescription"), new DropDownChoice<Integer>("choice", new PropertyModel<Integer>(repositoryModel, "maxActivityCommits"), maxActivityCommits, new MaxActivityCommitsRenderer()))); List<CommitMessageRenderer> renderers = Arrays.asList(CommitMessageRenderer.values()); form.add(new ChoiceOption<CommitMessageRenderer>("commitMessageRenderer", getString("gb.commitMessageRenderer"), getString("gb.commitMessageRendererDescription"), new DropDownChoice<CommitMessageRenderer>("choice", new PropertyModel<CommitMessageRenderer>(repositoryModel, "commitMessageRenderer"), renderers))); metricAuthorExclusions = new Model<String>(ArrayUtils.isEmpty(repositoryModel.metricAuthorExclusions) ? "" : StringUtils.flattenStrings(repositoryModel.metricAuthorExclusions, " ")); form.add(new TextOption("metricAuthorExclusions", getString("gb.metricAuthorExclusions"), getString("gb.metricAuthorExclusions"), "span6", metricAuthorExclusions)); mailingLists = new Model<String>(ArrayUtils.isEmpty(repositoryModel.mailingLists) ? "" : StringUtils.flattenStrings(repositoryModel.mailingLists, " ")); form.add(new TextOption("mailingLists", getString("gb.mailingLists"), getString("gb.mailingLists"), "span6", mailingLists)); // initial enable/disable of permission controls if (repositoryModel.accessRestriction.equals(AccessRestrictionType.NONE)) { // anonymous everything, disable all controls usersPalette.setEnabled(false); teamsPalette.setEnabled(false); authorizationControl.setEnabled(false); verifyCommitter.setEnabled(false); } else { // authenticated something // enable authorization controls authorizationControl.setEnabled(true); verifyCommitter.setEnabled(true); boolean allowFineGrainedControls = repositoryModel.authorizationControl.equals(AuthorizationControl.NAMED); @@ -546,15 +614,18 @@ teamsPalette.setEnabled(allowFineGrainedControls); } accessRestriction.add(new AjaxFormComponentUpdatingBehavior("onchange") { // // ACCESS POLICY PANEL (GENERAL) // AjaxFormChoiceComponentUpdatingBehavior callback = new AjaxFormChoiceComponentUpdatingBehavior() { private static final long serialVersionUID = 1L; @Override protected void onUpdate(AjaxRequestTarget target) { // enable/disable permissions panel based on access restriction accessPolicyPanel.updateModel(repositoryModel); boolean allowAuthorizationControl = repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE); authorizationControl.setEnabled(allowAuthorizationControl); verifyCommitter.setEnabled(allowAuthorizationControl); boolean allowFineGrainedControls = allowAuthorizationControl && repositoryModel.authorizationControl.equals(AuthorizationControl.NAMED); @@ -565,41 +636,19 @@ repositoryModel.authorizationControl = AuthorizationControl.NAMED; } target.addComponent(authorizationControl); target.addComponent(verifyCommitter); target.addComponent(usersPalette); target.addComponent(teamsPalette); } }); }; authorizationControl.add(new AjaxFormChoiceComponentUpdatingBehavior() { accessPolicyPanel = new AccessPolicyPanel("accessPolicyPanel", repositoryModel, callback); form.add(accessPolicyPanel); private static final long serialVersionUID = 1L; @Override protected void onUpdate(AjaxRequestTarget target) { // enable/disable permissions panel based on access restriction boolean allowAuthorizationControl = repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE); authorizationControl.setEnabled(allowAuthorizationControl); boolean allowFineGrainedControls = allowAuthorizationControl && repositoryModel.authorizationControl.equals(AuthorizationControl.NAMED); usersPalette.setEnabled(allowFineGrainedControls); teamsPalette.setEnabled(allowFineGrainedControls); if (allowFineGrainedControls) { repositoryModel.authorizationControl = AuthorizationControl.NAMED; } target.addComponent(authorizationControl); target.addComponent(usersPalette); target.addComponent(teamsPalette); } }); List<CommitMessageRenderer> renderers = Arrays.asList(CommitMessageRenderer.values()); DropDownChoice<CommitMessageRenderer> messageRendererChoice = new DropDownChoice<CommitMessageRenderer>("commitMessageRenderer", renderers); form.add(messageRendererChoice); // // FORM CONTROLS // form.add(new Button("save")); Button cancel = new Button("cancel") { private static final long serialVersionUID = 1L; @@ -632,7 +681,15 @@ if (canDelete) { if (app().repositories().deleteRepositoryModel(latestModel)) { info(MessageFormat.format(getString("gb.repositoryDeleted"), latestModel)); setResponsePage(RepositoriesPage.class); if (latestModel.isPersonalRepository()) { // redirect to user's profile page String prefix = app().settings().getString(Keys.git.userRepositoryPrefix, "~"); String username = latestModel.projectPath.substring(prefix.length()); setResponsePage(UserPage.class, WicketUtils.newUsernameParameter(username)); } else { // redirect to server repositories page setResponsePage(RepositoriesPage.class); } } else { error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), latestModel)); } @@ -697,26 +754,6 @@ } } private class AccessRestrictionRenderer implements IChoiceRenderer<AccessRestrictionType> { private static final long serialVersionUID = 1L; private final Map<AccessRestrictionType, String> map; public AccessRestrictionRenderer() { map = getAccessRestrictions(); } @Override public String getDisplayValue(AccessRestrictionType type) { return map.get(type); } @Override public String getIdValue(AccessRestrictionType type, int index) { return Integer.toString(index); } } private class FederationTypeRenderer implements IChoiceRenderer<FederationStrategy> { @@ -735,27 +772,6 @@ @Override public String getIdValue(FederationStrategy type, int index) { return Integer.toString(index); } } private class AuthorizationControlRenderer implements IChoiceRenderer<AuthorizationControl> { private static final long serialVersionUID = 1L; private final Map<AuthorizationControl, String> map; public AuthorizationControlRenderer() { map = getAuthorizationControls(); } @Override public String getDisplayValue(AuthorizationControl type) { return map.get(type); } @Override public String getIdValue(AuthorizationControl type, int index) { return Integer.toString(index); } } src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.html
@@ -10,7 +10,7 @@ <div class="markdown"> <div class="row"> <div class="span10 offset1"> <h2><center>Empty Repository</center></h2> <h3><center>Empty Repository</center></h3> <div class="alert alert-info"> <span wicket:id="repository" style="font-weight: bold;">[repository]</span> is an empty repository and can not be viewed by Gitblit. <p></p> src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_es.html
@@ -12,7 +12,7 @@ <div class="markdown"> <div class="row"> <div class="span10 offset1"> <h2><center>Repositorio Vacío</center></h2> <h3><center>Repositorio Vacío</center></h3> <div class="alert alert-info"> <span wicket:id="repository" style="font-weight: bold;">[repository]</span> es un repositorio vacío y no puede ser visto en Gitblit. <p></p> src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_ko.html
@@ -10,7 +10,7 @@ <div class="markdown"> <div class="row"> <div class="span10 offset1"> <h2><center>비어있는 저장소</center></h2> <h3><center>비어있는 저장소</center></h3> <div class="alert alert-info"> <span wicket:id="repository" style="font-weight: bold;">[repository]</span> 저장소는 비어 있어서 Gitblit 에서 볼 수 없습니다. <p></p> src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_nl.html
@@ -10,7 +10,7 @@ <div class="markdown"> <div class="row"> <div class="span10 offset1"> <h2><center>Lege Repository</center></h2> <h3><center>Lege Repository</center></h3> <div class="alert alert-info"> <span wicket:id="repository" style="font-weight: bold;">[repository]</span> is een lege repository en kan niet bekeken worden door Gitblit. <p></p> src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pl.html
@@ -12,7 +12,7 @@ <div class="markdown"> <div class="row"> <div class="span10 offset1"> <h2><center>Puste repozytorium</center></h2> <h3><center>Puste repozytorium</center></h3> <div class="alert alert-info"> <span wicket:id="repository" style="font-weight: bold;">[repository]</span> jest pustym repozytorium i nie może być zaprezentowane przez Gitblit. <p></p> src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_BR.html
@@ -10,7 +10,7 @@ <div class="markdown"> <div class="row"> <div class="span10 offset1"> <h2><center>Repositório Vazio</center></h2> <h3><center>Repositório Vazio</center></h3> <div class="alert alert-info"> <span wicket:id="repository" style="font-weight: bold;">[repository]</span> é um repositório vazio e não pode ser visualizado pelo Gitblit. <p></p> src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_zh_CN.html
@@ -10,7 +10,7 @@ <div class="markdown"> <div class="row"> <div class="span10 offset1"> <h2><center>空版本库</center></h2> <h3><center>空版本库</center></h3> <div class="alert alert-info"> <span wicket:id="repository" style="font-weight: bold;">[repository]</span> 版本库目前为空。 Gitblit 无法查看。 src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.html
New file @@ -0,0 +1,41 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" xml:lang="en" lang="en"> <wicket:extend> <body onload="document.getElementById('name').focus();"> <form style="padding-top:5px;" wicket:id="editForm"> <div class="row"> <div class="span12"> <div wicket:id="namePanel"></div> <hr/> <div wicket:id="accessPolicyPanel"></div> <hr/> <h4><wicket:message key="gb.initialCommit"></wicket:message></h4> <p><wicket:message key="gb.initialCommitDescription"></wicket:message></p> <div wicket:id="addReadme"></div> <div wicket:id="addGitIgnore"></div> <div wicket:id="addGitFlow"></div> </div> </div> <div class="row"> <div class="span12"> <div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Create" wicket:message="value:gb.create" wicket:id="create" /></div> </div> </div> </form> </body> </wicket:extend> </html> src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.java
New file @@ -0,0 +1,373 @@ /* * Copyright 2014 gitblit.com. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gitblit.wicket.pages; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.wicket.behavior.SimpleAttributeModifier; import org.apache.wicket.markup.html.form.Button; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheBuilder; import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.lib.CommitBuilder; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.RefUpdate.Result; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import com.gitblit.Constants; import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.Constants.AuthorizationControl; import com.gitblit.GitBlitException; import com.gitblit.Keys; import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.FileUtils; import com.gitblit.utils.StringUtils; import com.gitblit.wicket.GitBlitWebSession; import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.panels.AccessPolicyPanel; import com.gitblit.wicket.panels.CheckboxOption; import com.gitblit.wicket.panels.ConditionalChoiceOption; import com.gitblit.wicket.panels.RepositoryNamePanel; public class NewRepositoryPage extends RootSubPage { private final RepositoryModel repositoryModel; private IModel<Boolean> addReadmeModel; private Model<String> gitignoreModel; private IModel<Boolean> addGitflowModel; private IModel<Boolean> addGitignoreModel; private AccessPolicyPanel accessPolicyPanel; private RepositoryNamePanel namePanel; public NewRepositoryPage() { // create constructor super(); repositoryModel = new RepositoryModel(); setupPage(getString("gb.newRepository"), ""); setStatelessHint(false); setOutputMarkupId(true); } @Override protected boolean requiresPageMap() { return true; } @Override protected Class<? extends BasePage> getRootNavPageClass() { return RepositoriesPage.class; } @Override protected void onInitialize() { super.onInitialize(); CompoundPropertyModel<RepositoryModel> rModel = new CompoundPropertyModel<>(repositoryModel); Form<RepositoryModel> form = new Form<RepositoryModel>("editForm", rModel) { private static final long serialVersionUID = 1L; @Override protected void onSubmit() { try { if (!namePanel.updateModel(repositoryModel)) { return; } accessPolicyPanel.updateModel(repositoryModel); repositoryModel.owners = new ArrayList<String>(); repositoryModel.owners.add(GitBlitWebSession.get().getUsername()); // setup branch defaults boolean useGitFlow = addGitflowModel.getObject(); repositoryModel.HEAD = Constants.R_MASTER; repositoryModel.mergeTo = Constants.MASTER; if (useGitFlow) { // tickets normally merge to develop unless they are hotfixes repositoryModel.mergeTo = Constants.DEVELOP; } repositoryModel.allowForks = app().settings().getBoolean(Keys.web.allowForking, true); // optionally generate an initial commit boolean addReadme = addReadmeModel.getObject(); String gitignore = null; boolean addGitignore = addGitignoreModel.getObject(); if (addGitignore) { gitignore = gitignoreModel.getObject(); if (StringUtils.isEmpty(gitignore)) { throw new GitBlitException(getString("gb.pleaseSelectGitIgnore")); } } // init the repository app().gitblit().updateRepositoryModel(repositoryModel.name, repositoryModel, true); // optionally create an initial commit initialCommit(repositoryModel, addReadme, gitignore, useGitFlow); } catch (GitBlitException e) { error(e.getMessage()); namePanel.resetModel(repositoryModel); return; } setRedirect(true); setResponsePage(SummaryPage.class, WicketUtils.newRepositoryParameter(repositoryModel.name)); } }; // do not let the browser pre-populate these fields form.add(new SimpleAttributeModifier("autocomplete", "off")); namePanel = new RepositoryNamePanel("namePanel", repositoryModel); form.add(namePanel); // prepare the default access controls AccessRestrictionType defaultRestriction = AccessRestrictionType.fromName( app().settings().getString(Keys.git.defaultAccessRestriction, AccessRestrictionType.PUSH.name())); if (AccessRestrictionType.NONE == defaultRestriction) { defaultRestriction = AccessRestrictionType.PUSH; } AuthorizationControl defaultControl = AuthorizationControl.fromName( app().settings().getString(Keys.git.defaultAuthorizationControl, AuthorizationControl.NAMED.name())); if (AuthorizationControl.AUTHENTICATED == defaultControl) { defaultRestriction = AccessRestrictionType.PUSH; } repositoryModel.authorizationControl = defaultControl; repositoryModel.accessRestriction = defaultRestriction; accessPolicyPanel = new AccessPolicyPanel("accessPolicyPanel", repositoryModel); form.add(accessPolicyPanel); // // initial commit options // // add README addReadmeModel = Model.of(false); form.add(new CheckboxOption("addReadme", getString("gb.initWithReadme"), getString("gb.initWithReadmeDescription"), addReadmeModel)); // add .gitignore File gitignoreDir = app().runtime().getFileOrFolder(Keys.git.gitignoreFolder, "${baseFolder}/gitignore"); File [] files = gitignoreDir.listFiles(); if (files == null) { files = new File[0]; } List<String> gitignores = new ArrayList<String>(); for (File file : files) { if (file.isFile() && file.getName().endsWith(".gitignore")) { gitignores.add(StringUtils.stripFileExtension(file.getName())); } } Collections.sort(gitignores); gitignoreModel = Model.of(""); addGitignoreModel = Model.of(false); form.add(new ConditionalChoiceOption<String>("addGitIgnore", getString("gb.initWithGitignore"), getString("gb.initWithGitignoreDescription"), addGitignoreModel, gitignoreModel, gitignores)); // TODO consider gitflow at creation (ticket-55) addGitflowModel = Model.of(false); form.add(new CheckboxOption("addGitFlow", "Include a .gitflow file", "This will generate a config file which guides Git clients in setting up Gitflow branches.", addGitflowModel).setVisible(false)); form.add(new Button("create")); add(form); } /** * Prepare the initial commit for the repository. * * @param repository * @param addReadme * @param gitignore * @param addGitFlow * @return true if an initial commit was created */ protected boolean initialCommit(RepositoryModel repository, boolean addReadme, String gitignore, boolean addGitFlow) { boolean initialCommit = addReadme || !StringUtils.isEmpty(gitignore) || addGitFlow; if (!initialCommit) { return false; } // build an initial commit boolean success = false; Repository db = app().repositories().getRepository(repositoryModel.name); ObjectInserter odi = db.newObjectInserter(); try { UserModel user = GitBlitWebSession.get().getUser(); PersonIdent author = new PersonIdent(user.getDisplayName(), user.emailAddress); DirCache newIndex = DirCache.newInCore(); DirCacheBuilder indexBuilder = newIndex.builder(); if (addReadme) { // insert a README String title = StringUtils.stripDotGit(StringUtils.getLastPathElement(repositoryModel.name)); String description = repositoryModel.description == null ? "" : repositoryModel.description; String readme = String.format("## %s\n\n%s\n\n", title, description); byte [] bytes = readme.getBytes(Constants.ENCODING); DirCacheEntry entry = new DirCacheEntry("README.md"); entry.setLength(bytes.length); entry.setLastModified(System.currentTimeMillis()); entry.setFileMode(FileMode.REGULAR_FILE); entry.setObjectId(odi.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, bytes)); indexBuilder.add(entry); } if (!StringUtils.isEmpty(gitignore)) { // insert a .gitignore file File dir = app().runtime().getFileOrFolder(Keys.git.gitignoreFolder, "${baseFolder}/gitignore"); File file = new File(dir, gitignore + ".gitignore"); if (file.exists() && file.length() > 0) { byte [] bytes = FileUtils.readContent(file); if (!ArrayUtils.isEmpty(bytes)) { DirCacheEntry entry = new DirCacheEntry(".gitignore"); entry.setLength(bytes.length); entry.setLastModified(System.currentTimeMillis()); entry.setFileMode(FileMode.REGULAR_FILE); entry.setObjectId(odi.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, bytes)); indexBuilder.add(entry); } } } if (addGitFlow) { // insert a .gitflow file Config config = new Config(); config.setString("gitflow", null, "masterBranch", Constants.MASTER); config.setString("gitflow", null, "developBranch", Constants.DEVELOP); config.setString("gitflow", null, "featureBranchPrefix", "feature/"); config.setString("gitflow", null, "releaseBranchPrefix", "release/"); config.setString("gitflow", null, "hotfixBranchPrefix", "hotfix/"); config.setString("gitflow", null, "supportBranchPrefix", "support/"); config.setString("gitflow", null, "versionTagPrefix", ""); byte [] bytes = config.toText().getBytes(Constants.ENCODING); DirCacheEntry entry = new DirCacheEntry(".gitflow"); entry.setLength(bytes.length); entry.setLastModified(System.currentTimeMillis()); entry.setFileMode(FileMode.REGULAR_FILE); entry.setObjectId(odi.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, bytes)); indexBuilder.add(entry); } indexBuilder.finish(); if (newIndex.getEntryCount() == 0) { // nothing to commit return false; } ObjectId treeId = newIndex.writeTree(odi); // Create a commit object CommitBuilder commit = new CommitBuilder(); commit.setAuthor(author); commit.setCommitter(author); commit.setEncoding(Constants.ENCODING); commit.setMessage("Initial commit"); commit.setTreeId(treeId); // Insert the commit into the repository ObjectId commitId = odi.insert(commit); odi.flush(); // set the branch refs RevWalk revWalk = new RevWalk(db); try { // set the master branch RevCommit revCommit = revWalk.parseCommit(commitId); RefUpdate masterRef = db.updateRef(Constants.R_MASTER); masterRef.setNewObjectId(commitId); masterRef.setRefLogMessage("commit: " + revCommit.getShortMessage(), false); Result masterRC = masterRef.update(); switch (masterRC) { case NEW: success = true; break; default: success = false; } if (addGitFlow) { // set the develop branch for git-flow RefUpdate developRef = db.updateRef(Constants.R_DEVELOP); developRef.setNewObjectId(commitId); developRef.setRefLogMessage("commit: " + revCommit.getShortMessage(), false); Result developRC = developRef.update(); switch (developRC) { case NEW: success = true; break; default: success = false; } } } finally { revWalk.release(); } } catch (UnsupportedEncodingException e) { logger().error(null, e); } catch (IOException e) { logger().error(null, e); } finally { odi.release(); db.close(); } return success; } } src/main/java/com/gitblit/wicket/pages/RootPage.java
@@ -607,7 +607,7 @@ List<MenuItem> standardItems = new ArrayList<MenuItem>(); standardItems.add(new MenuDivider()); if (user.canAdmin() || user.canCreate()) { standardItems.add(new PageLinkMenuItem("gb.newRepository", EditRepositoryPage.class)); standardItems.add(new PageLinkMenuItem("gb.newRepository", app().getNewRepositoryPage())); } standardItems.add(new PageLinkMenuItem("gb.myProfile", UserPage.class, WicketUtils.newUsernameParameter(user.username))); src/main/java/com/gitblit/wicket/pages/UserPage.java
@@ -30,8 +30,8 @@ import com.gitblit.Keys; import com.gitblit.models.Menu.ParameterMenuItem; import com.gitblit.models.NavLink.DropDownPageMenuNavLink; import com.gitblit.models.NavLink; import com.gitblit.models.NavLink.DropDownPageMenuNavLink; import com.gitblit.models.ProjectModel; import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; @@ -95,7 +95,7 @@ UserModel sessionUser = GitBlitWebSession.get().getUser(); if (sessionUser != null && user.canCreate() && sessionUser.equals(user)) { // user can create personal repositories add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class)); add(new BookmarkablePageLink<Void>("newRepository", app().getNewRepositoryPage())); } else { add(new Label("newRepository").setVisible(false)); } src/main/java/com/gitblit/wicket/panels/AccessPolicyPanel.html
New file @@ -0,0 +1,28 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" xml:lang="en" lang="en"> <body> <wicket:panel> <h4><wicket:message key="gb.accessPolicy"></wicket:message></h4> <p><wicket:message key="gb.accessPolicyDescription"></wicket:message></p> <div wicket:id="policiesGroup"> <div wicket:id="policies" style="padding-top:4px;"> <div> <label style="font-weight:bold;"><input type="radio" wicket:id="radio" /> <img wicket:id="image"></img> <span wicket:id="name"></span></label> </div> <label class="checkbox" style="color:#777;" wicket:id="description"></label> </div> </div> <hr /> <div wicket:id="allowForks"></div> </wicket:panel> </body> </html> src/main/java/com/gitblit/wicket/panels/AccessPolicyPanel.java
New file @@ -0,0 +1,192 @@ /* * Copyright 2014 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.panels; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.Radio; import org.apache.wicket.markup.html.form.RadioGroup; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.Constants.AuthorizationControl; import com.gitblit.Keys; import com.gitblit.models.RepositoryModel; import com.gitblit.wicket.WicketUtils; /** * A radio group panel of the 5 available authorization/access restriction combinations. * * @author James Moger * */ public class AccessPolicyPanel extends BasePanel { private static final long serialVersionUID = 1L; private final RepositoryModel repository; private final AjaxFormChoiceComponentUpdatingBehavior callback; private RadioGroup<AccessPolicy> policiesGroup; private IModel<Boolean> allowForks; public AccessPolicyPanel(String wicketId, RepositoryModel repository) { this(wicketId, repository, null); } public AccessPolicyPanel(String wicketId, RepositoryModel repository, AjaxFormChoiceComponentUpdatingBehavior callback) { super(wicketId); this.repository = repository; this.callback = callback; } @Override protected void onInitialize() { super.onInitialize(); AccessPolicy anonymousPolicy = new AccessPolicy(getString("gb.anonymousPolicy"), getString("gb.anonymousPolicyDescription"), "blank.png", AuthorizationControl.AUTHENTICATED, AccessRestrictionType.NONE); AccessPolicy authenticatedPushPolicy = new AccessPolicy(getString("gb.authenticatedPushPolicy"), getString("gb.authenticatedPushPolicyDescription"), "lock_go_16x16.png", AuthorizationControl.AUTHENTICATED, AccessRestrictionType.PUSH); AccessPolicy namedPushPolicy = new AccessPolicy(getString("gb.namedPushPolicy"), getString("gb.namedPushPolicyDescription"), "lock_go_16x16.png", AuthorizationControl.NAMED, AccessRestrictionType.PUSH); AccessPolicy clonePolicy = new AccessPolicy(getString("gb.clonePolicy"), getString("gb.clonePolicyDescription"), "lock_pull_16x16.png", AuthorizationControl.NAMED, AccessRestrictionType.CLONE); AccessPolicy viewPolicy = new AccessPolicy(getString("gb.viewPolicy"), getString("gb.viewPolicyDescription"), "shield_16x16.png", AuthorizationControl.NAMED, AccessRestrictionType.VIEW); List<AccessPolicy> policies = new ArrayList<AccessPolicy>(); if (app().settings().getBoolean(Keys.git.allowAnonymousPushes, false)) { policies.add(anonymousPolicy); } policies.add(authenticatedPushPolicy); policies.add(namedPushPolicy); policies.add(clonePolicy); policies.add(viewPolicy); AccessRestrictionType defaultRestriction = repository.accessRestriction; if (defaultRestriction == null) { defaultRestriction = AccessRestrictionType.fromName(app().settings().getString(Keys.git.defaultAccessRestriction, AccessRestrictionType.PUSH.name())); } AuthorizationControl defaultControl = repository.authorizationControl; if (defaultControl == null) { defaultControl = AuthorizationControl.fromName(app().settings().getString(Keys.git.defaultAuthorizationControl, AuthorizationControl.NAMED.name())); } AccessPolicy defaultPolicy = namedPushPolicy; for (AccessPolicy policy : policies) { if (policy.type == defaultRestriction && policy.control == defaultControl) { defaultPolicy = policy; } } policiesGroup = new RadioGroup<>("policiesGroup", new Model<AccessPolicy>(defaultPolicy)); ListView<AccessPolicy> policiesList = new ListView<AccessPolicy>("policies", policies) { private static final long serialVersionUID = 1L; @Override protected void populateItem(ListItem<AccessPolicy> item) { AccessPolicy p = item.getModelObject(); item.add(new Radio<AccessPolicy>("radio", item.getModel())); item.add(WicketUtils.newImage("image", p.image)); item.add(new Label("name", p.name)); item.add(new Label("description", p.description)); } }; policiesGroup.add(policiesList); if (callback != null) { policiesGroup.add(callback); policiesGroup.setOutputMarkupId(true); } add(policiesGroup); allowForks = Model.of(true); add(new CheckboxOption("allowForks", getString("gb.allowForks"), getString("gb.allowForksDescription"), allowForks).setEnabled(app().settings().getBoolean(Keys.web.allowForking, true))); setOutputMarkupId(true); } public void updateModel(RepositoryModel repository) { AccessPolicy policy = policiesGroup.getModelObject(); repository.authorizationControl = policy.control; repository.accessRestriction = policy.type; repository.allowForks = allowForks.getObject(); } @Override protected boolean getStatelessHint() { return false; } public static class AccessPolicy implements Serializable { private static final long serialVersionUID = 1L; final String name; final String description; final String image; final AuthorizationControl control; final AccessRestrictionType type; AccessPolicy(String name, String description, String img, AuthorizationControl control, AccessRestrictionType type) { this.name = name; this.description = description; this.image = img; this.control = control; this.type = type; } @Override public String toString() { return name; } } } src/main/java/com/gitblit/wicket/panels/CheckboxOption.html
New file @@ -0,0 +1,17 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" xml:lang="en" lang="en"> <body> <wicket:panel> <div style="padding-top:4px;"> <div> <label style="font-weight:bold;" class="checkbox"><input type="checkbox" wicket:id="checkbox" /> <span wicket:id="name"></span></label> </div> <label class="checkbox" style="color:#777;" wicket:id="description"></label> </div> </wicket:panel> </body> </html> src/main/java/com/gitblit/wicket/panels/CheckboxOption.java
New file @@ -0,0 +1,54 @@ /* * Copyright 2014 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.panels; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.CheckBox; import org.apache.wicket.model.IModel; import org.parboiled.common.StringUtils; /** * A re-usable checkbox option panel. * * [x] title * description * * @author James Moger * */ public class CheckboxOption extends BasePanel { private static final long serialVersionUID = 1L; public CheckboxOption(String wicketId, String title, String description, IModel<Boolean> model) { super(wicketId); add(new Label("name", title)); add(new Label("description", description).setVisible(!StringUtils.isEmpty(description))); add(new CheckBox("checkbox", model)); } public CheckboxOption(String wicketId, String title, String description, CheckBox checkbox) { super(wicketId); add(new Label("name", title)); add(new Label("description", description).setVisible(!StringUtils.isEmpty(description))); add(checkbox.setMarkupId("checkbox")); } public CheckboxOption setIsHtmlDescription(boolean val) { ((Label) get("description")).setEscapeModelStrings(!val); return this; } } src/main/java/com/gitblit/wicket/panels/ChoiceOption.html
New file @@ -0,0 +1,19 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" xml:lang="en" lang="en"> <body> <wicket:panel> <div style="padding-top:4px;"> <div> <b><span wicket:id="name"></span></b> </div> <label class="checkbox" style="color:#777;"> <span wicket:id="description"></span> <p style="padding-top:5px;"><select class="span3" wicket:id="choice" /></p> </label> </div> </wicket:panel> </body> </html> src/main/java/com/gitblit/wicket/panels/ChoiceOption.java
New file @@ -0,0 +1,52 @@ /* * Copyright 2014 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.panels; import java.util.List; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.model.IModel; import org.parboiled.common.StringUtils; /** * A re-usable choice option panel. * * title * description * [choices] * * @author James Moger * */ public class ChoiceOption<T> extends BasePanel { private static final long serialVersionUID = 1L; public ChoiceOption(String wicketId, String title, String description, IModel<T> model, List<T> choices) { super(wicketId); add(new Label("name", title)); add(new Label("description", description).setVisible(!StringUtils.isEmpty(description))); add(new DropDownChoice<>("choice", model, choices).setEnabled(choices.size() > 0)); } public ChoiceOption(String wicketId, String title, String description, DropDownChoice<?> choice) { super(wicketId); add(new Label("name", title)); add(new Label("description", description).setVisible(!StringUtils.isEmpty(description))); add(choice.setMarkupId("choice").setEnabled(choice.getChoices().size() > 0)); } } src/main/java/com/gitblit/wicket/panels/ConditionalChoiceOption.html
New file @@ -0,0 +1,19 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" xml:lang="en" lang="en"> <body> <wicket:panel> <div style="padding-top:4px;"> <div> <label style="font-weight:bold;" class="checkbox"><input type="checkbox" wicket:id="checkbox" /> <span wicket:id="name"></span></label> </div> <label class="checkbox" style="color:#777;"> <span wicket:id="description"></span> <p style="padding-top:5px;"><select class="span3" wicket:id="choice" /></p> </label> </div> </wicket:panel> </body> </html> src/main/java/com/gitblit/wicket/panels/ConditionalChoiceOption.java
New file @@ -0,0 +1,78 @@ /* * Copyright 2014 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.panels; import java.util.List; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.CheckBox; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.model.IModel; import org.parboiled.common.StringUtils; /** * A re-usable conditional choice option panel. * * [x] title * description * [choices] * * @author James Moger * */ public class ConditionalChoiceOption<T> extends BasePanel { private static final long serialVersionUID = 1L; final CheckBox checkbox; final DropDownChoice<T> choice; public ConditionalChoiceOption(String wicketId, String title, String description, IModel<Boolean> checkboxModel, IModel<T> choiceModel, List<T> choices) { super(wicketId); add(new Label("name", title)); add(new Label("description", description).setVisible(!StringUtils.isEmpty(description))); this.checkbox = new CheckBox("checkbox", checkboxModel); checkbox.setOutputMarkupId(true); this.choice = new DropDownChoice<T>("choice", choiceModel, choices); choice.setOutputMarkupId(true); setup(); } private void setup() { add(checkbox); add(choice.setMarkupId("choice").setEnabled(choice.getChoices().size() > 0)); choice.setEnabled(checkbox.getModelObject()); checkbox.add(new AjaxFormComponentUpdatingBehavior("onchange") { private static final long serialVersionUID = 1L; @Override protected void onUpdate(AjaxRequestTarget target) { choice.setEnabled(checkbox.getModelObject()); target.addComponent(choice); if (!choice.isEnabled()) { choice.setModelObject(null); } } }); } } src/main/java/com/gitblit/wicket/panels/FilterableRepositoryList.java
@@ -33,7 +33,6 @@ import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.freemarker.FreemarkerPanel; import com.gitblit.wicket.ng.NgController; import com.gitblit.wicket.pages.EditRepositoryPage; /** * A client-side filterable rich repository list which uses Freemarker, Wicket, @@ -98,7 +97,7 @@ } if (allowCreate) { panel.add(new LinkPanel(ngList + "Button", "btn btn-mini", getString("gb.newRepository"), EditRepositoryPage.class)); panel.add(new LinkPanel(ngList + "Button", "btn btn-mini", getString("gb.newRepository"), app().getNewRepositoryPage())); } else { panel.add(new Label(ngList + "Button").setVisible(false)); } src/main/java/com/gitblit/wicket/panels/RegistrantPermissionsPanel.html
@@ -16,7 +16,7 @@ </form> <div style="clear:both;" wicket:id="permissionRow"> <div style="padding-top:10px;border-left:1px solid #ccc;border-right:1px solid #ccc;" class="row-fluid"> <div style="padding-top:10px;" class="row-fluid"> <div style="padding-top:5px;padding-left:5px" class="span6"><span wicket:id="registrant"></span></div><div style="padding-top:5px;padding-right:5px;text-align:right;" class="span3"><span class="label" wicket:id="pType">[permission type]</span></div> <select class="input-medium" wicket:id="permission"></select> </div> </div> src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.java
@@ -51,7 +51,6 @@ import com.gitblit.wicket.GitBlitWebSession; import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.pages.BasePage; import com.gitblit.wicket.pages.EditRepositoryPage; import com.gitblit.wicket.pages.ProjectPage; import com.gitblit.wicket.pages.RepositoriesPage; import com.gitblit.wicket.pages.SummaryPage; @@ -87,12 +86,12 @@ setResponsePage(RepositoriesPage.class); } }.setVisible(app().settings().getBoolean(Keys.git.cacheRepositoryList, true))); managementLinks.add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class)); managementLinks.add(new BookmarkablePageLink<Void>("newRepository", app().getNewRepositoryPage())); add(managementLinks); } else if (showManagement && user != null && user.canCreate()) { // user can create personal repositories managementLinks = new Fragment("managementPanel", "personalLinks", this); managementLinks.add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class)); managementLinks.add(new BookmarkablePageLink<Void>("newRepository", app().getNewRepositoryPage())); add(managementLinks); } else { // user has no management permissions src/main/java/com/gitblit/wicket/panels/RepositoryNamePanel.html
New file @@ -0,0 +1,30 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" xml:lang="en" lang="en"> <body> <wicket:panel> <table class="plain"> <tbody class="settings"> <tr> <th><wicket:message key="gb.project"></wicket:message></th> <th><wicket:message key="gb.name"></wicket:message></th> </tr> <tr> <td><select class="span2" wicket:id="projectPath" /></td> <td class="edit"><input class="span4" type="text" wicket:id="name" id="name" /> <span class="help-inline"><wicket:message key="gb.nameDescription"></wicket:message></span></td> </tr> </tbody> </table> <div> <b><wicket:message key="gb.description"></wicket:message></b><br/> <input class="span5" type="text" wicket:id="description" /> </div> </wicket:panel> </body> </html> src/main/java/com/gitblit/wicket/panels/RepositoryNamePanel.java
New file @@ -0,0 +1,176 @@ /* * Copyright 2014 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.panels; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Set; import java.util.TreeSet; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.TextField; import com.gitblit.models.ProjectModel; import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; import com.gitblit.utils.StringUtils; import com.gitblit.wicket.GitBlitWebSession; /** * A panel for naming a repository, specifying it's project, and entering a description. * * @author James Moger * */ public class RepositoryNamePanel extends BasePanel { private static final long serialVersionUID = 1L; private String fullName; private DropDownChoice<String> projectChoice; private TextField<String> nameField; public RepositoryNamePanel(String wicketId, RepositoryModel repository) { super(wicketId); GitBlitWebSession session = GitBlitWebSession.get(); UserModel user = session.getUser(); // build project set for repository destination String defaultProject = null; Set<String> projectNames = new TreeSet<String>(); // add the registered/known projects for (ProjectModel project : app().projects().getProjectModels(user, false)) { // TODO issue-351: user.canAdmin(project) if (user.canAdmin()) { if (project.isRoot) { projectNames.add("/"); } else { projectNames.add(project.name + "/"); } } } // add the user's personal project namespace if (user.canAdmin() || user.canCreate()) { projectNames.add(user.getPersonalPath() + "/"); } if (!StringUtils.isEmpty(repository.name)) { // editing a repository name // set the defaultProject to the current repository project defaultProject = repository.projectPath; if (StringUtils.isEmpty(defaultProject)) { defaultProject = "/"; } else { defaultProject += "/"; } projectNames.add(defaultProject); } // if default project is not already set, set preference based on the user permissions if (defaultProject == null) { if (user.canAdmin()) { defaultProject = "/"; } else if (user.canCreate()) { defaultProject = user.getPersonalPath() + "/"; } } // update the model which is reflectively mapped to the Wicket fields by name repository.projectPath = defaultProject; if (repository.projectPath.length() > 1 && !StringUtils.isEmpty(repository.name)) { repository.name = repository.name.substring(repository.projectPath.length()); } projectChoice = new DropDownChoice<String>("projectPath", new ArrayList<String>(projectNames)); nameField = new TextField<String>("name"); // only enable project selection if we actually have multiple choices add(projectChoice.setEnabled(projectNames.size() > 1)); add(nameField); add(new TextField<String>("description")); } public void setEditable(boolean editable) { // only enable project selection if we actually have multiple choices projectChoice.setEnabled(projectChoice.getChoices().size() > 1 && editable); nameField.setEnabled(editable); } public boolean updateModel(RepositoryModel repositoryModel) { // confirm a project was selected if (StringUtils.isEmpty(repositoryModel.projectPath)) { error(getString("gb.pleaseSelectProject")); return false; } // confirm a repository name was entered if (StringUtils.isEmpty(repositoryModel.name)) { error(getString("gb.pleaseSetRepositoryName")); return false; } String project = repositoryModel.projectPath; fullName = (project + repositoryModel.name).trim(); fullName = fullName.replace('\\', '/'); fullName = fullName.replace("//", "/"); if (fullName.charAt(0) == '/') { fullName = fullName.substring(1); } if (fullName.endsWith("/")) { fullName = fullName.substring(0, fullName.length() - 1); } if (fullName.contains("../")) { error(getString("gb.illegalRelativeSlash")); return false; } if (fullName.contains("/../")) { error(getString("gb.illegalRelativeSlash")); return false; } // confirm valid characters in repository name Character c = StringUtils.findInvalidCharacter(fullName); if (c != null) { error(MessageFormat.format(getString("gb.illegalCharacterRepositoryName"), c)); return false; } repositoryModel.name = fullName; repositoryModel.projectPath = null; return true; } public void resetModel(RepositoryModel repositoryModel) { // restore project and name fields on error condition repositoryModel.projectPath = StringUtils.getFirstPathElement(fullName) + "/"; if (repositoryModel.projectPath.length() > 1) { repositoryModel.name = fullName.substring(repositoryModel.projectPath.length()); } } @Override protected boolean getStatelessHint() { return false; } } src/main/java/com/gitblit/wicket/panels/TextOption.html
New file @@ -0,0 +1,20 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" xml:lang="en" lang="en"> <body> <wicket:panel> <div style="padding-top:4px;"> <div> <b><span wicket:id="name"></span></b> </div> <label class="checkbox" style="color:#777;"> <span wicket:id="description"></span> <p style="padding-top:5px;"><input class="span2" type="text" wicket:id="text" /></p> </label> </div> </wicket:panel> </body> </html> src/main/java/com/gitblit/wicket/panels/TextOption.java
New file @@ -0,0 +1,53 @@ /* * Copyright 2014 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.panels; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.model.IModel; import com.gitblit.utils.StringUtils; import com.gitblit.wicket.WicketUtils; /** * A re-usable textfield option panel. * * title * description * [textfield] * * @author James Moger * */ public class TextOption extends BasePanel { private static final long serialVersionUID = 1L; public TextOption(String wicketId, String title, String description, IModel<String> model) { this(wicketId, title, description, null, model); } public TextOption(String wicketId, String title, String description, String css, IModel<String> model) { super(wicketId); add(new Label("name", title)); add(new Label("description", description).setVisible(!StringUtils.isEmpty(description))); TextField<String> tf = new TextField<String>("text", model); if (!StringUtils.isEmpty(css)) { WicketUtils.setCssClass(tf, css); } add(tf); } }