James Moger
2014-05-27 0047fbba99b804d268a66ed7504a568596de6168
Simplified repository creation with a NewRepositoryPage
4 files added
12 files modified
689 ■■■■■ changed files
.gitmodules 3 ●●●●● patch | view | raw | blame | history
build.xml 6 ●●●●● patch | view | raw | blame | history
releases.moxie 2 ●●●●● patch | view | raw | blame | history
src/main/distrib/data/gitblit.properties 5 ●●●●● patch | view | raw | blame | history
src/main/distrib/data/gitignore @ 097db8 1 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/Constants.java 8 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/servlet/GitblitContext.java 16 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/GitBlitWebApp.java 10 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties 17 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java 16 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.html 98 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.java 493 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/RootPage.java 2 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/UserPage.java 4 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/FilterableRepositoryList.java 3 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.java 5 ●●●●● patch | view | raw | blame | history
.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
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
@@ -36,6 +36,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 +56,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
@@ -685,4 +685,19 @@
gb.administration = administration
gb.plugins = plugins
gb.extensions = extensions
gb.anonymous = Anonymous
gb.anonymousRepoDescription = Anyone can see, clone, and push to this repository.
gb.public = Public
gb.publicRepoDescription = Anyone can see and clone this repository. You choose who can push.
gb.protected = Protected
gb.protectedRepoDescription = Anyone can see this repository. You choose who can clone and push.
gb.private = Private
gb.privateRepoDescription = 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.initWithGitflow = Include a .gitflow file
gb.initWithGitflowDescription = This will generate a config file which guides Git clients in setting up Gitflow branches.
src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java
@@ -429,11 +429,7 @@
                    return;
                }
                setRedirect(false);
                if (isCreate) {
                    setResponsePage(RepositoriesPage.class);
                } else {
                    setResponsePage(SummaryPage.class, WicketUtils.newRepositoryParameter(repositoryModel.name));
                }
                setResponsePage(SummaryPage.class, WicketUtils.newRepositoryParameter(repositoryModel.name));
            }
        };
@@ -632,7 +628,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));
                    }
src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.html
New file
@@ -0,0 +1,98 @@
<!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">
        <table class="plain">
            <tbody class="settings">
                <tr>
                    <th><wicket:message key="gb.project"></wicket:message></th>
                    <td></td>
                    <th><wicket:message key="gb.name"></wicket:message></th>
                </tr>
                <tr>
                    <td><select class="span2" wicket:id="projectPath" /></td>
                    <td style="font-size:24px;color:#ccc;">/</td>
                    <td class="edit"><input class="span3" type="text" wicket:id="name" id="name" /> &nbsp;<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="span6" type="text" wicket:id="description" />
        </div>
        <hr/>
        <span wicket:id="permissionsGroup">
            <div wicket:id="permissions">
                <div style="display: inline-block;vertical-align: top;">
                    <input type="radio" wicket:id="radio" />
                    <img wicket:id="image"></img>
                </div>
                <div style="display: inline-block;vertical-align: top;">
                    <b><span wicket:id="name"></span></b><br/>
                    <span wicket:id="description"></span>
                </div>
            </div>
        </span>
        <hr/>
        <h4><wicket:message key="gb.initialCommit"></wicket:message></h4>
        <div>
            <p><wicket:message key="gb.initialCommitDescription"></wicket:message></p>
        </div>
        <div style="clear:both;padding-top:10px;">
            <div style="display: inline-block;vertical-align: top;">
                <input type="checkbox" wicket:id="addReadme" />
            </div>
            <div style="display: inline-block;">
                <b><wicket:message key="gb.initWithReadme"></wicket:message></b><br/>
                <p><wicket:message key="gb.initWithReadmeDescription"></wicket:message></p>
            </div>
        </div>
        <div style="clear:both;padding-top:10px;display:none;">
            <!-- future GitFlow -->
            <div style="display: inline-block;vertical-align: top;">
                <input type="checkbox" wicket:id="addGitflow" />
            </div>
            <div style="display: inline-block;">
                <b><wicket:message key="gb.initWithGitflow"></wicket:message></b>
                <p><wicket:message key="gb.initWithGitflowDescription"></wicket:message></p>
            </div>
        </div>
        <div style="clear:both;padding-top:10px;">
            <div style="display: inline-block;vertical-align: top;">
                <input type="checkbox" wicket:id="addGitignore" />
            </div>
            <div style="display:inline-block;">
                <b><wicket:message key="gb.initWithGitignore"></wicket:message></b><br/>
                <p><wicket:message key="gb.initWithGitignoreDescription"></wicket:message></p>
                <p style="padding-top:5px;"><select class="span2" wicket:id="gitignore" /></p>
            </div>
        </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,493 @@
/*
 * 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.Serializable;
import java.io.UnsupportedEncodingException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.behavior.SimpleAttributeModifier;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.Radio;
import org.apache.wicket.markup.html.form.RadioGroup;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.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;
public class NewRepositoryPage extends RootSubPage {
    private final RepositoryModel repositoryModel;
    private RadioGroup<Permission> permissionGroup;
    private IModel<Boolean> addReadmeModel;
    private Model<String> gitignoreModel;
    private IModel<Boolean> addGitflowModel;
    private IModel<Boolean> addGitignoreModel;
    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() {
                // confirm a repository name was entered
                if (StringUtils.isEmpty(repositoryModel.name)) {
                    error(getString("gb.pleaseSetRepositoryName"));
                    return;
                }
                String project = repositoryModel.projectPath;
                String 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);
                }
                try {
                    if (fullName.contains("../")) {
                        error(getString("gb.illegalRelativeSlash"));
                        return;
                    }
                    if (fullName.contains("/../")) {
                        error(getString("gb.illegalRelativeSlash"));
                        return;
                    }
                    // confirm valid characters in repository name
                    Character c = StringUtils.findInvalidCharacter(fullName);
                    if (c != null) {
                        error(MessageFormat.format(getString("gb.illegalCharacterRepositoryName"),
                                c));
                        return;
                    }
                    repositoryModel.name = fullName;
                    repositoryModel.projectPath = null;
                    Permission permisison = permissionGroup.getModelObject();
                    repositoryModel.accessRestriction = permisison.type;
                    repositoryModel.authorizationControl = AuthorizationControl.NAMED;
                    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("Please select a .gitignore file");
                        }
                    }
                    // 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());
                    // restore project and name fields on error condition
                    repositoryModel.projectPath = StringUtils.getFirstPathElement(fullName);
                    if (!StringUtils.isEmpty(repositoryModel.projectPath)) {
                        repositoryModel.name = fullName.substring(repositoryModel.projectPath.length() + 1);
                    }
                    return;
                }
                setRedirect(true);
                setResponsePage(SummaryPage.class, WicketUtils.newRepositoryParameter(fullName));
            }
        };
        GitBlitWebSession session = GitBlitWebSession.get();
        UserModel user = session.getUser();
        // build project list for repository destination
        String defaultProject = null;
        List<String> projects = new ArrayList<String>();
        if (user.canAdmin()) {
            String main = app().settings().getString(Keys.web.repositoryRootGroupName, "main");
            projects.add(main);
            defaultProject = main;
        }
        if (user.canCreate()) {
            projects.add(user.getPersonalPath());
            if (defaultProject == null) {
                // only prefer personal namespace if default is not already set
                defaultProject = user.getPersonalPath();
            }
        }
        repositoryModel.projectPath = defaultProject;
        // do not let the browser pre-populate these fields
        form.add(new SimpleAttributeModifier("autocomplete", "off"));
        form.add(new DropDownChoice<String>("projectPath", projects));
        form.add(new TextField<String>("name"));
        form.add(new TextField<String>("description"));
        Permission anonymousPermission = new Permission(getString("gb.anonymous"), getString("gb.anonymousRepoDescription"), "blank.png", AccessRestrictionType.NONE);
        Permission publicPermission = new Permission(getString("gb.public"), getString("gb.publicRepoDescription"), "lock_go_16x16.png", AccessRestrictionType.PUSH);
        Permission protectedPermission = new Permission(getString("gb.protected"), getString("gb.protectedRepoDescription"), "lock_pull_16x16.png", AccessRestrictionType.CLONE);
        Permission privatePermission = new Permission(getString("gb.private"), getString("gb.privateRepoDescription"), "shield_16x16.png", AccessRestrictionType.VIEW);
        List<Permission> permissions = new ArrayList<Permission>();
        if (app().settings().getBoolean(Keys.git.allowAnonymousPushes, false)) {
            permissions.add(anonymousPermission);
        }
        permissions.add(publicPermission);
        permissions.add(protectedPermission);
        permissions.add(privatePermission);
        // determine default permission selection
        AccessRestrictionType defaultRestriction = AccessRestrictionType.fromName(
                app().settings().getString(Keys.git.defaultAccessRestriction, AccessRestrictionType.PUSH.name()));
        if (AccessRestrictionType.NONE == defaultRestriction) {
            defaultRestriction = AccessRestrictionType.PUSH;
        }
        Permission defaultPermission = publicPermission;
        for (Permission permission : permissions) {
            if (permission.type == defaultRestriction) {
                defaultPermission = permission;
            }
        }
        permissionGroup = new RadioGroup<>("permissionsGroup", new Model<Permission>(defaultPermission));
        form.add(permissionGroup);
        ListView<Permission> permissionsList = new ListView<Permission>("permissions", permissions) {
            private static final long serialVersionUID = 1L;
            @Override
            protected void populateItem(ListItem<Permission> item) {
                Permission p = item.getModelObject();
                item.add(new Radio<Permission>("radio", item.getModel()));
                item.add(WicketUtils.newImage("image",  p.image));
                item.add(new Label("name", p.name));
                item.add(new Label("description", p.description));
            }
        };
        permissionGroup.add(permissionsList);
        //
        // initial commit options
        //
        // add README
        addReadmeModel = Model.of(false);
        form.add(new CheckBox("addReadme", 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("");
        final DropDownChoice<String> gitignoreChoice = new DropDownChoice<String>("gitignore", gitignoreModel, gitignores);
        gitignoreChoice.setOutputMarkupId(true);
        form.add(gitignoreChoice.setEnabled(false));
        addGitignoreModel = Model.of(false);
        final CheckBox gitignoreCheckbox = new CheckBox("addGitignore", addGitignoreModel);
        form.add(gitignoreCheckbox);
        gitignoreCheckbox.add(new AjaxFormComponentUpdatingBehavior("onchange") {
            private static final long serialVersionUID = 1L;
            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                gitignoreChoice.setEnabled(addGitignoreModel.getObject());
                target.addComponent(gitignoreChoice);
            }
        });
        // TODO add .gitflow
        addGitflowModel = Model.of(false);
        form.add(new CheckBox("addGitflow", addGitflowModel));
        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;
    }
    private static class Permission implements Serializable {
        private static final long serialVersionUID = 1L;
        final String name;
        final String description;
        final String image;
        final AccessRestrictionType type;
        Permission(String name, String description, String img, AccessRestrictionType type) {
            this.name = name;
            this.description = description;
            this.image = img;
            this.type = type;
        }
    }
}
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/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/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