diff options
Diffstat (limited to 'src/main/java/com/gitblit/wicket/pages')
29 files changed, 744 insertions, 67 deletions
diff --git a/src/main/java/com/gitblit/wicket/pages/BasePage.html b/src/main/java/com/gitblit/wicket/pages/BasePage.html index 89a28b80..b998428c 100644 --- a/src/main/java/com/gitblit/wicket/pages/BasePage.html +++ b/src/main/java/com/gitblit/wicket/pages/BasePage.html @@ -15,6 +15,7 @@ <link rel="stylesheet" href="bootstrap/css/bootstrap.css"/>
<link rel="stylesheet" href="bootstrap/css/iconic.css"/>
<link rel="stylesheet" href="fontawesome/css/font-awesome.min.css"/>
+ <link rel="stylesheet" href="octicons/octicons.css"/>
<link rel="stylesheet" type="text/css" href="gitblit.css"/>
</wicket:head>
@@ -50,5 +51,6 @@ <!-- Include scripts at end for faster page loading -->
<script type="text/javascript" src="bootstrap/js/jquery.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.js"></script>
+ <wicket:container wicket:id="bottomScripts"></wicket:container>
</body>
</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/BasePage.java b/src/main/java/com/gitblit/wicket/pages/BasePage.java index b6967003..0d99f5e5 100644 --- a/src/main/java/com/gitblit/wicket/pages/BasePage.java +++ b/src/main/java/com/gitblit/wicket/pages/BasePage.java @@ -42,6 +42,8 @@ import org.apache.wicket.markup.html.CSSPackageResource; import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
+import org.apache.wicket.markup.html.resources.JavascriptResourceReference;
+import org.apache.wicket.markup.repeater.RepeatingView;
import org.apache.wicket.protocol.http.RequestUtils;
import org.apache.wicket.protocol.http.WebResponse;
import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
@@ -242,7 +244,7 @@ public abstract class BasePage extends SessionPage { protected void setupPage(String repositoryName, String pageName) {
add(new Label("title", getPageTitle(repositoryName)));
-
+ getBottomScriptContainer();
String rootLinkUrl = app().settings().getString(Keys.web.rootLink, urlFor(GitBlitWebApp.get().getHomePage(), null).toString());
ExternalLink rootLink = new ExternalLink("rootLink", rootLinkUrl);
WicketUtils.setHtmlTooltip(rootLink, app().settings().getString(Keys.web.siteName, Constants.NAME));
@@ -506,4 +508,40 @@ public abstract class BasePage extends SessionPage { return sb.toString();
}
+ private RepeatingView getBottomScriptContainer() {
+ RepeatingView bottomScriptContainer = (RepeatingView) get("bottomScripts");
+ if (bottomScriptContainer == null) {
+ bottomScriptContainer = new RepeatingView("bottomScripts");
+ bottomScriptContainer.setRenderBodyOnly(true);
+ add(bottomScriptContainer);
+ }
+ return bottomScriptContainer;
+ }
+
+ /**
+ * Adds a HTML script element loading the javascript designated by the given path.
+ *
+ * @param scriptPath
+ * page-relative path to the Javascript resource; normally starts with "scripts/"
+ */
+ protected void addBottomScript(String scriptPath) {
+ RepeatingView bottomScripts = getBottomScriptContainer();
+ Label script = new Label(bottomScripts.newChildId(), "<script type='text/javascript' src='"
+ + urlFor(new JavascriptResourceReference(this.getClass(), scriptPath)) + "'></script>\n");
+ bottomScripts.add(script.setEscapeModelStrings(false).setRenderBodyOnly(true));
+ }
+
+ /**
+ * Adds a HTML script element containing the given code.
+ *
+ * @param code
+ * inline script code
+ */
+ protected void addBottomScriptInline(String code) {
+ RepeatingView bottomScripts = getBottomScriptContainer();
+ Label script = new Label(bottomScripts.newChildId(),
+ "<script type='text/javascript'>/*<![CDATA[*/\n" + code + "\n//]]>\n</script>\n");
+ bottomScripts.add(script.setEscapeModelStrings(false).setRenderBodyOnly(true));
+ }
+
}
diff --git a/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java b/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java index 9cc3eae1..ae737a53 100644 --- a/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java +++ b/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java @@ -15,12 +15,15 @@ */
package com.gitblit.wicket.pages;
+import java.util.List;
+
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
+import com.gitblit.Keys;
import com.gitblit.utils.DiffUtils;
import com.gitblit.utils.DiffUtils.DiffOutputType;
import com.gitblit.utils.JGitUtils;
@@ -43,16 +46,29 @@ public class BlobDiffPage extends RepositoryPage { Repository r = getRepository();
RevCommit commit = getCommit();
+ final List<String> imageExtensions = app().settings().getStrings(Keys.web.imageExtensions);
+
String diff;
if (StringUtils.isEmpty(baseObjectId)) {
// use first parent
- diff = DiffUtils.getDiff(r, commit, blobPath, DiffOutputType.HTML).content;
+ RevCommit parent = commit.getParentCount() == 0 ? null : commit.getParent(0);
+ ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName,
+ parent.getName(), commit.getName(), imageExtensions);
+ diff = DiffUtils.getDiff(r, commit, blobPath, DiffOutputType.HTML, handler).content;
+ if (handler.getImgDiffCount() > 0) {
+ addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs
+ }
add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class,
WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
} else {
// base commit specified
RevCommit baseCommit = JGitUtils.getCommit(r, baseObjectId);
- diff = DiffUtils.getDiff(r, baseCommit, commit, blobPath, DiffOutputType.HTML).content;
+ ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName,
+ baseCommit.getName(), commit.getName(), imageExtensions);
+ diff = DiffUtils.getDiff(r, baseCommit, commit, blobPath, DiffOutputType.HTML, handler).content;
+ if (handler.getImgDiffCount() > 0) {
+ addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs
+ }
add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class,
WicketUtils.newBlobDiffParameter(repositoryName, baseObjectId, objectId,
blobPath)));
diff --git a/src/main/java/com/gitblit/wicket/pages/BlobPage.html b/src/main/java/com/gitblit/wicket/pages/BlobPage.html index 3f9a959a..289c1498 100644 --- a/src/main/java/com/gitblit/wicket/pages/BlobPage.html +++ b/src/main/java/com/gitblit/wicket/pages/BlobPage.html @@ -40,9 +40,8 @@ </wicket:link>
</wicket:head>
+<body>
<wicket:extend>
-<!-- need to specify body.onload -->
-<body onload="prettyPrint()">
<!-- blob nav links -->
<div class="page_nav2">
@@ -61,6 +60,6 @@ <!-- blob image -->
<img wicket:id="blobImage" style="padding-top:5px;"></img>
-</body>
</wicket:extend>
+</body>
</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/BlobPage.java b/src/main/java/com/gitblit/wicket/pages/BlobPage.java index 3c244f9a..df30e4cc 100644 --- a/src/main/java/com/gitblit/wicket/pages/BlobPage.java +++ b/src/main/java/com/gitblit/wicket/pages/BlobPage.java @@ -137,6 +137,7 @@ public class BlobPage extends RepositoryPage { table = missingBlob(blobPath, commit);
} else {
table = generateSourceView(source, extension, type == 1);
+ addBottomScriptInline("jQuery(prettyPrint);");
}
add(new Label("blobText", table).setEscapeModelStrings(false));
add(new Image("blobImage").setVisible(false));
@@ -150,6 +151,7 @@ public class BlobPage extends RepositoryPage { table = missingBlob(blobPath, commit);
} else {
table = generateSourceView(source, null, false);
+ addBottomScriptInline("jQuery(prettyPrint);");
}
add(new Label("blobText", table).setEscapeModelStrings(false));
add(new Image("blobImage").setVisible(false));
diff --git a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java index d827c449..c838dab5 100644 --- a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java +++ b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java @@ -31,6 +31,7 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import com.gitblit.Constants; +import com.gitblit.Keys; import com.gitblit.models.GitNote; import com.gitblit.models.PathModel.PathChangeModel; import com.gitblit.models.SubmoduleModel; @@ -59,8 +60,6 @@ public class CommitDiffPage extends RepositoryPage { RevCommit commit = getCommit(); - final DiffOutput diff = DiffUtils.getCommitDiff(r, commit, DiffOutputType.HTML); - List<String> parents = new ArrayList<String>(); if (commit.getParentCount() > 0) { for (RevCommit parent : commit.getParents()) { @@ -82,6 +81,14 @@ public class CommitDiffPage extends RepositoryPage { add(new CommitHeaderPanel("commitHeader", repositoryName, commit)); + final List<String> imageExtensions = app().settings().getStrings(Keys.web.imageExtensions); + final ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName, + parents.isEmpty() ? null : parents.get(0), commit.getName(), imageExtensions); + final DiffOutput diff = DiffUtils.getCommitDiff(r, commit, DiffOutputType.HTML, handler); + if (handler.getImgDiffCount() > 0) { + addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs + } + // add commit diffstat int insertions = 0; int deletions = 0; @@ -145,10 +152,10 @@ public class CommitDiffPage extends RepositoryPage { hasSubmodule = submodule.hasSubmodule; // add relative link - item.add(new LinkPanel("pathName", "list", entry.path + " @ " + getShortObjectId(submoduleId), "#" + entry.path)); + item.add(new LinkPanel("pathName", "list", entry.path + " @ " + getShortObjectId(submoduleId), "#n" + entry.objectId)); } else { // add relative link - item.add(new LinkPanel("pathName", "list", entry.path, "#" + entry.path)); + item.add(new LinkPanel("pathName", "list", entry.path, "#n" + entry.objectId)); } // quick links diff --git a/src/main/java/com/gitblit/wicket/pages/ComparePage.java b/src/main/java/com/gitblit/wicket/pages/ComparePage.java index 1ec66133..62ae7c25 100644 --- a/src/main/java/com/gitblit/wicket/pages/ComparePage.java +++ b/src/main/java/com/gitblit/wicket/pages/ComparePage.java @@ -37,6 +37,7 @@ import org.eclipse.jgit.diff.DiffEntry.ChangeType; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; +import com.gitblit.Keys; import com.gitblit.models.PathModel.PathChangeModel; import com.gitblit.models.RefModel; import com.gitblit.models.RepositoryModel; @@ -111,7 +112,14 @@ public class ComparePage extends RepositoryPage { fromCommitId.setObject(startId); toCommitId.setObject(endId); - final DiffOutput diff = DiffUtils.getDiff(r, fromCommit, toCommit, DiffOutputType.HTML); + final List<String> imageExtensions = app().settings().getStrings(Keys.web.imageExtensions); + final ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName, + fromCommit.getName(), toCommit.getName(), imageExtensions); + + final DiffOutput diff = DiffUtils.getDiff(r, fromCommit, toCommit, DiffOutputType.HTML, handler); + if (handler.getImgDiffCount() > 0) { + addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs + } // add compare diffstat int insertions = 0; @@ -160,10 +168,10 @@ public class ComparePage extends RepositoryPage { hasSubmodule = submodule.hasSubmodule; // add relative link - item.add(new LinkPanel("pathName", "list", entry.path + " @ " + getShortObjectId(submoduleId), "#" + entry.path)); + item.add(new LinkPanel("pathName", "list", entry.path + " @ " + getShortObjectId(submoduleId), "#n" + entry.objectId)); } else { // add relative link - item.add(new LinkPanel("pathName", "list", entry.path, "#" + entry.path)); + item.add(new LinkPanel("pathName", "list", entry.path, "#n" + entry.objectId)); } // quick links diff --git a/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java b/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java index f537f33d..a43d8db0 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java +++ b/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java @@ -39,6 +39,7 @@ import org.apache.wicket.model.util.CollectionModel; import org.apache.wicket.model.util.ListModel;
import com.gitblit.Constants.RegistrantType;
+import com.gitblit.Constants.Role;
import com.gitblit.GitBlitException;
import com.gitblit.Keys;
import com.gitblit.models.RegistrantAccessPermission;
@@ -221,14 +222,23 @@ public class EditTeamPage extends RootSubPage { // do not let the browser pre-populate these fields
form.add(new SimpleAttributeModifier("autocomplete", "off"));
- // not all user services support manipulating team memberships
+ // not all user providers support manipulating team memberships
boolean editMemberships = app().authentication().supportsTeamMembershipChanges(teamModel);
+ // not all user providers support manipulating the admin role
+ boolean changeAdminRole = app().authentication().supportsRoleChanges(teamModel, Role.ADMIN);
+
+ // not all user providers support manipulating the create role
+ boolean changeCreateRole = app().authentication().supportsRoleChanges(teamModel, Role.CREATE);
+
+ // not all user providers support manipulating the fork role
+ boolean changeForkRole = app().authentication().supportsRoleChanges(teamModel, Role.FORK);
+
// field names reflective match TeamModel fields
form.add(new TextField<String>("name"));
- form.add(new CheckBox("canAdmin"));
- form.add(new CheckBox("canFork").setEnabled(app().settings().getBoolean(Keys.web.allowForking, true)));
- form.add(new CheckBox("canCreate"));
+ form.add(new CheckBox("canAdmin").setEnabled(changeAdminRole));
+ form.add(new CheckBox("canFork").setEnabled(app().settings().getBoolean(Keys.web.allowForking, true) && changeForkRole));
+ form.add(new CheckBox("canCreate").setEnabled(changeCreateRole));
form.add(users.setEnabled(editMemberships));
mailingLists = new Model<String>(teamModel.mailingLists == null ? ""
: StringUtils.flattenStrings(teamModel.mailingLists, " "));
diff --git a/src/main/java/com/gitblit/wicket/pages/EditTicketPage.html b/src/main/java/com/gitblit/wicket/pages/EditTicketPage.html index b5fe0ae5..b12d0c77 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditTicketPage.html +++ b/src/main/java/com/gitblit/wicket/pages/EditTicketPage.html @@ -39,6 +39,8 @@ </div>
</td></tr>
<tr wicket:id="status"></tr>
+ <tr><th><wicket:message key="gb.severity"></wicket:message></th><td class="edit"><select class="input-large" wicket:id="severity"></select></td></tr>
+ <tr wicket:id="priority"></tr>
<tr wicket:id="responsible"></tr>
<tr wicket:id="milestone"></tr>
<tr wicket:id="mergeto"></tr>
@@ -71,5 +73,9 @@ <th><wicket:message key="gb.mergeTo"></wicket:message></th><td class="edit"><select class="input-large" wicket:id="mergeto"></select></td>
</wicket:fragment>
+<wicket:fragment wicket:id="priorityFragment">
+ <th><wicket:message key="gb.priority"></wicket:message></th><td class="edit"><select class="input-large" wicket:id="priority"></select></td>
+</wicket:fragment>
+
</wicket:extend>
</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/EditTicketPage.java b/src/main/java/com/gitblit/wicket/pages/EditTicketPage.java index c3d405bc..192b48ca 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditTicketPage.java +++ b/src/main/java/com/gitblit/wicket/pages/EditTicketPage.java @@ -84,6 +84,10 @@ public class EditTicketPage extends RepositoryPage { private Label descriptionPreview;
+ private IModel<TicketModel.Priority> priorityModel;
+
+ private IModel<TicketModel.Severity> severityModel;
+
public EditTicketPage(PageParameters params) {
super(params);
@@ -117,6 +121,8 @@ public class EditTicketPage extends RepositoryPage { milestoneModel = Model.of();
mergeToModel = Model.of(ticket.mergeTo == null ? getRepositoryModel().mergeTo : ticket.mergeTo);
statusModel = Model.of(ticket.status);
+ priorityModel = Model.of(ticket.priority);
+ severityModel = Model.of(ticket.severity);
setStatelessHint(false);
setOutputMarkupId(true);
@@ -161,6 +167,9 @@ public class EditTicketPage extends RepositoryPage { status.add(new DropDownChoice<TicketModel.Status>("status", statusModel, statusChoices));
form.add(status);
+ List<TicketModel.Severity> severityChoices = Arrays.asList(TicketModel.Severity.choices());
+ form.add(new DropDownChoice<TicketModel.Severity>("severity", severityModel, severityChoices));
+
if (currentUser.canAdmin(ticket, getRepositoryModel())) {
// responsible
Set<String> userlist = new TreeSet<String>(ticket.getParticipants());
@@ -214,11 +223,17 @@ public class EditTicketPage extends RepositoryPage { milestones.add(new TicketMilestone(NIL));
}
+ // milestone
Fragment milestone = new Fragment("milestone", "milestoneFragment", this);
-
milestone.add(new DropDownChoice<TicketMilestone>("milestone", milestoneModel, milestones));
form.add(milestone.setVisible(!milestones.isEmpty()));
+ // priority
+ Fragment priority = new Fragment("priority", "priorityFragment", this);
+ List<TicketModel.Priority> priorityChoices = Arrays.asList(TicketModel.Priority.choices());
+ priority.add(new DropDownChoice<TicketModel.Priority>("priority", priorityModel, priorityChoices));
+ form.add(priority);
+
// mergeTo (integration branch)
List<String> branches = new ArrayList<String>();
for (String branch : getRepositoryModel().getLocalBranches()) {
@@ -238,6 +253,7 @@ public class EditTicketPage extends RepositoryPage { form.add(new Label("responsible").setVisible(false));
form.add(new Label("milestone").setVisible(false));
form.add(new Label("mergeto").setVisible(false));
+ form.add(new Label("priority").setVisible(false));
}
form.add(new AjaxButton("update") {
@@ -316,6 +332,18 @@ public class EditTicketPage extends RepositoryPage { }
}
+ TicketModel.Priority priority = priorityModel.getObject();
+ if (!ticket.priority.equals(priority))
+ {
+ change.setField(Field.priority, priority);
+ }
+
+ TicketModel.Severity severity = severityModel.getObject();
+ if (!ticket.severity.equals(severity))
+ {
+ change.setField(Field.severity, severity);
+ }
+
String mergeTo = mergeToModel.getObject();
if ((StringUtils.isEmpty(ticket.mergeTo) && !StringUtils.isEmpty(mergeTo))
|| (!StringUtils.isEmpty(mergeTo) && !mergeTo.equals(ticket.mergeTo))) {
diff --git a/src/main/java/com/gitblit/wicket/pages/EditUserPage.java b/src/main/java/com/gitblit/wicket/pages/EditUserPage.java index 454aa619..c6b5c3c7 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditUserPage.java +++ b/src/main/java/com/gitblit/wicket/pages/EditUserPage.java @@ -35,6 +35,7 @@ import org.apache.wicket.model.util.CollectionModel; import org.apache.wicket.model.util.ListModel;
import com.gitblit.Constants.RegistrantType;
+import com.gitblit.Constants.Role;
import com.gitblit.GitBlitException;
import com.gitblit.Keys;
import com.gitblit.models.RegistrantAccessPermission;
@@ -177,7 +178,9 @@ public class EditUserPage extends RootSubPage { // update user permissions
for (RegistrantAccessPermission repositoryPermission : permissions) {
- userModel.setRepositoryPermission(repositoryPermission.registrant, repositoryPermission.permission);
+ if (repositoryPermission.mutable) {
+ userModel.setRepositoryPermission(repositoryPermission.registrant, repositoryPermission.permission);
+ }
}
Iterator<String> selectedTeams = teams.getSelectedChoices();
@@ -216,18 +219,27 @@ public class EditUserPage extends RootSubPage { // do not let the browser pre-populate these fields
form.add(new SimpleAttributeModifier("autocomplete", "off"));
- // not all user services support manipulating username and password
+ // not all user providers support manipulating username and password
boolean editCredentials = app().authentication().supportsCredentialChanges(userModel);
- // not all user services support manipulating display name
+ // not all user providers support manipulating display name
boolean editDisplayName = app().authentication().supportsDisplayNameChanges(userModel);
- // not all user services support manipulating email address
+ // not all user providers support manipulating email address
boolean editEmailAddress = app().authentication().supportsEmailAddressChanges(userModel);
- // not all user services support manipulating team memberships
+ // not all user providers support manipulating team memberships
boolean editTeams = app().authentication().supportsTeamMembershipChanges(userModel);
+ // not all user providers support manipulating the admin role
+ boolean changeAdminRole = app().authentication().supportsRoleChanges(userModel, Role.ADMIN);
+
+ // not all user providers support manipulating the create role
+ boolean changeCreateRole = app().authentication().supportsRoleChanges(userModel, Role.CREATE);
+
+ // not all user providers support manipulating the fork role
+ boolean changeForkRole = app().authentication().supportsRoleChanges(userModel, Role.FORK);
+
// field names reflective match UserModel fields
form.add(new TextField<String>("username").setEnabled(editCredentials));
PasswordTextField passwordField = new PasswordTextField("password");
@@ -245,7 +257,7 @@ public class EditUserPage extends RootSubPage { // display a disabled-yet-checked checkbox
form.add(new CheckBox("canAdmin", Model.of(true)).setEnabled(false));
} else {
- form.add(new CheckBox("canAdmin"));
+ form.add(new CheckBox("canAdmin").setEnabled(changeAdminRole));
}
if (userModel.canFork() && !userModel.canFork) {
@@ -254,7 +266,7 @@ public class EditUserPage extends RootSubPage { form.add(new CheckBox("canFork", Model.of(true)).setEnabled(false));
} else {
final boolean forkingAllowed = app().settings().getBoolean(Keys.web.allowForking, true);
- form.add(new CheckBox("canFork").setEnabled(forkingAllowed));
+ form.add(new CheckBox("canFork").setEnabled(forkingAllowed && changeForkRole));
}
if (userModel.canCreate() && !userModel.canCreate) {
@@ -262,7 +274,7 @@ public class EditUserPage extends RootSubPage { // display a disabled-yet-checked checkbox
form.add(new CheckBox("canCreate", Model.of(true)).setEnabled(false));
} else {
- form.add(new CheckBox("canCreate"));
+ form.add(new CheckBox("canCreate").setEnabled(changeCreateRole));
}
form.add(new CheckBox("excludeFromFederation"));
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java index b3c52436..72d1e1a4 100644 --- a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java +++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java @@ -55,7 +55,7 @@ public class EmptyRepositoryPage extends RepositoryPage { }
HttpServletRequest req = ((WebRequest) getRequest()).getHttpServletRequest();
- List<RepositoryUrl> repositoryUrls = app().gitblit().getRepositoryUrls(req, user, repository);
+ List<RepositoryUrl> repositoryUrls = app().services().getRepositoryUrls(req, user, repository);
RepositoryUrl primaryUrl = repositoryUrls.size() == 0 ? null : repositoryUrls.get(0);
String url = primaryUrl != null ? primaryUrl.url : "";
diff --git a/src/main/java/com/gitblit/wicket/pages/ImageDiffHandler.java b/src/main/java/com/gitblit/wicket/pages/ImageDiffHandler.java new file mode 100644 index 00000000..dc0c5ae8 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/ImageDiffHandler.java @@ -0,0 +1,170 @@ +/* + * Copyright 2014 Tom <tw201207@gmail.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.nio.charset.StandardCharsets; +import java.util.List; + +import org.apache.wicket.protocol.http.WebApplication; +import org.apache.wicket.protocol.http.WicketURLEncoder; +import org.eclipse.jgit.diff.DiffEntry; +import org.eclipse.jgit.diff.DiffEntry.Side; +import org.jsoup.nodes.Element; + +import com.gitblit.servlet.RawServlet; +import com.gitblit.utils.DiffUtils; +import com.gitblit.utils.HtmlBuilder; + +/** + * A {@link DiffUtils.BinaryDiffHandler BinaryDiffHandler} for images. + * + * @author Tom <tw201207@gmail.com> + */ +public class ImageDiffHandler implements DiffUtils.BinaryDiffHandler { + + private final String oldCommitId; + private final String newCommitId; + private final String repositoryName; + private final BasePage page; + private final List<String> imageExtensions; + + private int imgDiffCount = 0; + + public ImageDiffHandler(final BasePage page, final String repositoryName, final String oldCommitId, final String newCommitId, + final List<String> imageExtensions) { + this.page = page; + this.repositoryName = repositoryName; + this.oldCommitId = oldCommitId; + this.newCommitId = newCommitId; + this.imageExtensions = imageExtensions; + } + + /** {@inheritDoc} */ + @Override + public String renderBinaryDiff(DiffEntry diffEntry) { + switch (diffEntry.getChangeType()) { + case MODIFY: + case RENAME: + case COPY: + // TODO: for very small images such as icons, the slider doesn't really help. Two possible + // approaches: either upscale them for display (may show blurry upscaled images), or show + // them side by side (may still be too small to really make out the differences). + String oldUrl = getImageUrl(diffEntry, Side.OLD); + String newUrl = getImageUrl(diffEntry, Side.NEW); + if (oldUrl != null && newUrl != null) { + imgDiffCount++; + String id = "imgdiff" + imgDiffCount; + HtmlBuilder builder = new HtmlBuilder("div"); + Element wrapper = builder.root().attr("class", "imgdiff-container").attr("id", "imgdiff-" + id); + Element container = wrapper.appendElement("div").attr("class", "imgdiff-ovr-slider").appendElement("div").attr("class", "imgdiff"); + Element old = container.appendElement("div").attr("class", "imgdiff-left"); + // style='max-width:640px;' is necessary for ensuring that the browser limits large images + // to some reasonable width, and to override the "img { max-width: 100%; }" from bootstrap.css, + // which would scale the left image to the width of its resizeable container, which isn't what + // we want here. Note that the max-width must be defined directly as inline style on the element, + // otherwise browsers ignore it if the image is larger, and we end up with an image display that + // is too wide. + // XXX: Maybe add a max-height, too, to limit portrait-oriented images to some reasonable height? + // (Like a 300x10000px image...) + old.appendElement("img").attr("class", "imgdiff-old").attr("id", id).attr("style", "max-width:640px;").attr("src", oldUrl); + container.appendElement("img").attr("class", "imgdiff").attr("style", "max-width:640px;").attr("src", newUrl); + wrapper.appendElement("br"); + Element controls = wrapper.appendElement("div"); + // Opacity slider + controls.appendElement("div").attr("class", "imgdiff-opa-container").appendElement("a").attr("class", "imgdiff-opa-slider") + .attr("href", "#").attr("title", page.getString("gb.opacityAdjust")); + // Blink comparator: find Pluto! + controls.appendElement("a").attr("class", "imgdiff-link imgdiff-blink").attr("href", "#") + .attr("title", page.getString("gb.blinkComparator")) + .appendElement("img").attr("src", getStaticResourceUrl("blink32.png")).attr("width", "20"); + // Pixel subtraction, initially not displayed, will be shown by imgdiff.js depending on feature test. + // (Uses CSS mix-blend-mode, which isn't supported on all browsers yet). + controls.appendElement("a").attr("class", "imgdiff-link imgdiff-subtract").attr("href", "#") + .attr("title", page.getString("gb.imgdiffSubtract")).attr("style", "display:none;") + .appendElement("img").attr("src", getStaticResourceUrl("sub32.png")).attr("width", "20"); + return builder.toString(); + } + break; + case ADD: + String url = getImageUrl(diffEntry, Side.NEW); + if (url != null) { + return new HtmlBuilder("img").root().attr("class", "diff-img").attr("src", url).toString(); + } + break; + default: + break; + } + return null; + } + + /** Returns the number of image diffs generated so far by this {@link ImageDiffHandler}. */ + public int getImgDiffCount() { + return imgDiffCount; + } + + /** + * Constructs a URL that will fetch the designated resource in the git repository. The returned string will + * contain the URL fully URL-escaped, but note that it may still contain unescaped ampersands, so the result + * must still be run through HTML escaping if it is to be used in HTML. + * + * @return the URL to the image, if the given {@link DiffEntry} and {@link Side} refers to an image, or {@code null} otherwise. + */ + protected String getImageUrl(DiffEntry entry, Side side) { + String path = entry.getPath(side); + int i = path.lastIndexOf('.'); + if (i > 0) { + String extension = path.substring(i + 1); + for (String ext : imageExtensions) { + if (ext.equalsIgnoreCase(extension)) { + String commitId = Side.NEW.equals(side) ? newCommitId : oldCommitId; + if (commitId != null) { + return RawServlet.asLink(page.getContextUrl(), urlencode(repositoryName), commitId, urlencode(path)); + } else { + return null; + } + } + } + } + return null; + } + + /** + * Returns a URL that will fetch the designated static resource from within GitBlit. + */ + protected String getStaticResourceUrl(String contextRelativePath) { + return WebApplication.get().getRequestCycleProcessor().getRequestCodingStrategy().rewriteStaticRelativeUrl(contextRelativePath); + } + + /** + * Encode a URL component of a {@link RawServlet} URL in the special way that the servlet expects it. Note that + * the %-encoding used does not encode '&' or '<'. Slashes are not encoded in the result. + * + * @param component + * to encode using %-encoding + * @return the encoded component + */ + protected String urlencode(final String component) { + // RawServlet handles slashes itself. Note that only the PATH_INSTANCE fits the bill here: it encodes + // spaces as %20, and we just have to correct for encoded slashes. Java's standard URLEncoder would + // encode spaces as '+', and I don't know what effects that would have on other parts of GitBlit. It + // would also be wrong for path components (but fine for a query part), so we'd have to correct it, too. + // + // Actually, this should be done in RawServlet.asLink(). As it is now, this may be incorrect if that + // operation ever uses query parameters instead of paths, or if it is fixed to urlencode its path + // components. But I don't want to touch that static method in RawServlet. + return WicketURLEncoder.PATH_INSTANCE.encode(component, StandardCharsets.UTF_8.name()).replaceAll("%2[fF]", "/"); + } +} diff --git a/src/main/java/com/gitblit/wicket/pages/LogoutPage.html b/src/main/java/com/gitblit/wicket/pages/LogoutPage.html index d4077830..ab3c0dae 100644 --- a/src/main/java/com/gitblit/wicket/pages/LogoutPage.html +++ b/src/main/java/com/gitblit/wicket/pages/LogoutPage.html @@ -14,7 +14,7 @@ <span class="icon-bar"></span> </a> <a class="brand" wicket:id="rootLink"> - <img src="gitblt_25_white.png" width="79" height="25" alt="gitblit" class="logo"/> + <img src="gitblt_25_white.png" width="79" alt="gitblit" class="logo"/> </a> </div> diff --git a/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html index b0bc1949..cf94f3ca 100644 --- a/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html +++ b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html @@ -25,18 +25,19 @@ <!-- query list --> <ul class="nav nav-list"> <li class="nav-header"><wicket:message key="gb.queries"></wicket:message></li> - <li><a wicket:id="changesQuery"><i class="fa fa-code-fork"></i> <wicket:message key="gb.proposalTickets"></wicket:message></a></li> - <li><a wicket:id="bugsQuery"><i class="fa fa-bug"></i> <wicket:message key="gb.bugTickets"></wicket:message></a></li> - <li><a wicket:id="enhancementsQuery"><i class="fa fa-magic"></i> <wicket:message key="gb.enhancementTickets"></wicket:message></a></li> - <li><a wicket:id="tasksQuery"><i class="fa fa-ticket"></i> <wicket:message key="gb.taskTickets"></wicket:message></a></li> - <li><a wicket:id="questionsQuery"><i class="fa fa-question"></i> <wicket:message key="gb.questionTickets"></wicket:message></a></li> + <li><a wicket:id="changesQuery"><i class="fa fa-code-fork fa-fw"></i> <wicket:message key="gb.proposalTickets"></wicket:message></a></li> + <li><a wicket:id="bugsQuery"><i class="fa fa-bug fa-fw"></i> <wicket:message key="gb.bugTickets"></wicket:message></a></li> + <li><a wicket:id="enhancementsQuery"><i class="fa fa-magic fa-fw"></i> <wicket:message key="gb.enhancementTickets"></wicket:message></a></li> + <li><a wicket:id="tasksQuery"><i class="fa fa-ticket fa-fw"></i> <wicket:message key="gb.taskTickets"></wicket:message></a></li> + <li><a wicket:id="questionsQuery"><i class="fa fa-question fa-fw"></i> <wicket:message key="gb.questionTickets"></wicket:message></a></li> + <li><a wicket:id="maintenanceQuery"><i class="fa fa-cogs fa-fw"></i> <wicket:message key="gb.maintenanceTickets"></wicket:message></a></li> <li wicket:id="userDivider" class="divider"></li> - <li><a wicket:id="createdQuery"><i class="fa fa-user"></i> <wicket:message key="gb.yourCreatedTickets"></wicket:message></a></li> - <li><a wicket:id="responsibleQuery"><i class="fa fa-user"></i> <wicket:message key="gb.yourAssignedTickets"></wicket:message></a></li> - <li><a wicket:id="watchedQuery"><i class="fa fa-eye"></i> <wicket:message key="gb.yourWatchedTickets"></wicket:message></a></li> - <li><a wicket:id="mentionsQuery"><i class="fa fa-comment"></i> <wicket:message key="gb.mentionsMeTickets"></wicket:message></a></li> + <li><a wicket:id="createdQuery"><i class="fa fa-user fa-fw"></i> <wicket:message key="gb.yourCreatedTickets"></wicket:message></a></li> + <li><a wicket:id="responsibleQuery"><i class="fa fa-user fa-fw"></i> <wicket:message key="gb.yourAssignedTickets"></wicket:message></a></li> + <li><a wicket:id="watchedQuery"><i class="fa fa-eye fa-fw"></i> <wicket:message key="gb.yourWatchedTickets"></wicket:message></a></li> + <li><a wicket:id="mentionsQuery"><i class="fa fa-comment fa-fw"></i> <wicket:message key="gb.mentionsMeTickets"></wicket:message></a></li> <li class="divider"></li> - <li><a wicket:id="resetQuery"><i class="fa fa-bolt"></i> <wicket:message key="gb.reset"></wicket:message></a></li> + <li><a wicket:id="resetQuery"><i class="fa fa-bolt fa-fw"></i> <wicket:message key="gb.reset"></wicket:message></a></li> </ul> </div> diff --git a/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java index c207d561..187302f5 100644 --- a/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java +++ b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java @@ -132,6 +132,16 @@ public class MyTicketsPage extends RootPage { sortBy, desc, 1))); + + add(new BookmarkablePageLink<Void>("maintenanceQuery", MyTicketsPage.class, + queryParameters( + Lucene.type.matches(TicketModel.Type.Maintenance.name()), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + 1))); add(new BookmarkablePageLink<Void>("resetQuery", MyTicketsPage.class, queryParameters( @@ -220,6 +230,10 @@ public class MyTicketsPage extends RootPage { sortChoices.add(new TicketSort(getString("gb.sortLeastPatchsetRevisions"), Lucene.patchsets.name(), false)); sortChoices.add(new TicketSort(getString("gb.sortMostVotes"), Lucene.votes.name(), true)); sortChoices.add(new TicketSort(getString("gb.sortLeastVotes"), Lucene.votes.name(), false)); + sortChoices.add(new TicketSort(getString("gb.sortHighestPriority"), Lucene.priority.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortLowestPriority"), Lucene.priority.name(), false)); + sortChoices.add(new TicketSort(getString("gb.sortHighestSeverity"), Lucene.severity.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortLowestSeverity"), Lucene.severity.name(), false)); TicketSort currentSort = sortChoices.get(0); for (TicketSort ts : sortChoices) { diff --git a/src/main/java/com/gitblit/wicket/pages/NewTicketPage.html b/src/main/java/com/gitblit/wicket/pages/NewTicketPage.html index 447c6aa4..9b5af023 100644 --- a/src/main/java/com/gitblit/wicket/pages/NewTicketPage.html +++ b/src/main/java/com/gitblit/wicket/pages/NewTicketPage.html @@ -39,6 +39,8 @@ </div>
</td></tr>
<tr><th><wicket:message key="gb.type"></wicket:message><span style="color:red;">*</span></th><td class="edit"><select class="input-large" wicket:id="type"></select></td></tr>
+ <tr><th><wicket:message key="gb.severity"></wicket:message></th><td class="edit"><select class="input-large" wicket:id="severity"></select></td></tr>
+ <tr wicket:id="priority"></tr>
<tr wicket:id="responsible"></tr>
<tr wicket:id="milestone"></tr>
<tr wicket:id="mergeto"></tr>
@@ -67,5 +69,9 @@ <th><wicket:message key="gb.mergeTo"></wicket:message></th><td class="edit"><select class="input-large" wicket:id="mergeto"></select></td>
</wicket:fragment>
+<wicket:fragment wicket:id="priorityFragment">
+ <th><wicket:message key="gb.priority"></wicket:message></th><td class="edit"><select class="input-large" wicket:id="priority"></select></td>
+</wicket:fragment>
+
</wicket:extend>
</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/NewTicketPage.java b/src/main/java/com/gitblit/wicket/pages/NewTicketPage.java index 8f28055a..0c52505c 100644 --- a/src/main/java/com/gitblit/wicket/pages/NewTicketPage.java +++ b/src/main/java/com/gitblit/wicket/pages/NewTicketPage.java @@ -76,6 +76,10 @@ public class NewTicketPage extends RepositoryPage { private Label descriptionPreview;
+ private IModel<TicketModel.Priority> priorityModel;
+
+ private IModel<TicketModel.Severity> severityModel;
+
public NewTicketPage(PageParameters params) {
super(params);
@@ -95,6 +99,8 @@ public class NewTicketPage extends RepositoryPage { mergeToModel = Model.of(Repository.shortenRefName(getRepositoryModel().mergeTo));
responsibleModel = Model.of();
milestoneModel = Model.of();
+ severityModel = Model.of(TicketModel.Severity.defaultSeverity);
+ priorityModel = Model.of(TicketModel.Priority.defaultPriority);
setStatelessHint(false);
setOutputMarkupId(true);
@@ -105,6 +111,7 @@ public class NewTicketPage extends RepositoryPage { form.add(new DropDownChoice<TicketModel.Type>("type", typeModel, Arrays.asList(TicketModel.Type.choices())));
form.add(new TextField<String>("title", titleModel));
form.add(new TextField<String>("topic", topicModel));
+ form.add(new DropDownChoice<TicketModel.Severity>("severity", severityModel, Arrays.asList(TicketModel.Severity.choices())));
final IModel<String> markdownPreviewModel = Model.of();
descriptionPreview = new Label("descriptionPreview", markdownPreviewModel);
@@ -152,6 +159,11 @@ public class NewTicketPage extends RepositoryPage { milestone.add(new DropDownChoice<TicketMilestone>("milestone", milestoneModel, milestones));
form.add(milestone.setVisible(!milestones.isEmpty()));
+ // priority
+ Fragment priority = new Fragment("priority", "priorityFragment", this);
+ priority.add(new DropDownChoice<TicketModel.Priority>("priority", priorityModel, Arrays.asList(TicketModel.Priority.choices())));
+ form.add(priority);
+
// integration branch
List<String> branches = new ArrayList<String>();
for (String branch : getRepositoryModel().getLocalBranches()) {
@@ -171,6 +183,7 @@ public class NewTicketPage extends RepositoryPage { form.add(new Label("responsible").setVisible(false));
form.add(new Label("milestone").setVisible(false));
form.add(new Label("mergeto").setVisible(false));
+ form.add(new Label("priority").setVisible(false));
}
form.add(new AjaxButton("create") {
@@ -212,6 +225,20 @@ public class NewTicketPage extends RepositoryPage { change.setField(Field.milestone, milestone.name);
}
+ // severity
+ TicketModel.Severity severity = TicketModel.Severity.defaultSeverity;
+ if (severityModel.getObject() != null) {
+ severity = severityModel.getObject();
+ }
+ change.setField(Field.severity, severity);
+
+ // priority
+ TicketModel.Priority priority = TicketModel.Priority.defaultPriority;
+ if (priorityModel.getObject() != null) {
+ priority = priorityModel.getObject();
+ }
+ change.setField(Field.priority, priority);
+
// integration branch
String mergeTo = mergeToModel.getObject();
if (!StringUtils.isEmpty(mergeTo)) {
diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.html b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.html index 22544bc8..d4132421 100644 --- a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.html +++ b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.html @@ -24,13 +24,20 @@ </div>
<div class="span7">
- <div>
- <span class="project" wicket:id="projectTitle">[project title]</span>/<span class="repository" wicket:id="repositoryName">[repository name]</span>
- <a class="hidden-phone hidden-tablet" style="text-decoration: none;" wicket:id="syndication" wicket:message="title:gb.feed">
- <img style="border:0px;vertical-align:baseline;" src="feed_16x16.png"></img>
- </a>
+ <div style="display:inline-block;vertical-align:top;padding-right:5px;">
+ <span wicket:id="repoIcon"></span>
+ </div>
+ <div style="display:inline-block;">
+ <div>
+ <span class="project" wicket:id="projectTitle">[project title]</span>/<span class="repository" wicket:id="repositoryName">[repository name]</span>
+ <a class="hidden-phone hidden-tablet" style="text-decoration: none;" wicket:id="syndication" wicket:message="title:gb.feed">
+ <img style="border:0px;vertical-align:baseline;" src="feed_16x16.png"></img>
+ </a>
+ </div>
+ <div>
+ <span class="gray" wicket:id="originRepository">[origin repository]</span>
+ </div>
</div>
- <span wicket:id="originRepository">[origin repository]</span>
</div>
</div>
</div>
@@ -60,6 +67,22 @@ <i wicket:id="icon" style="padding-right:3px;"></i><span wicket:id="label"></span>
</wicket:fragment>
+ <wicket:fragment wicket:id="repoIconFragment">
+ <span class="gray mega-octicon octicon-repo"></span>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="mirrorIconFragment">
+ <span class="gray mega-octicon octicon-mirror"></span>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="forkIconFragment">
+ <span class="gray mega-octicon octicon-repo-forked"></span>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="cloneIconFragment">
+ <span class="gray mega-octicon octicon-repo-push" wicket:message="title:gb.workingCopyWarning"></span>
+ </wicket:fragment>
+
<wicket:fragment wicket:id="originFragment">
<p class="originRepository"><wicket:message key="gb.forkedFrom">[forked from]</wicket:message> <span wicket:id="originRepository">[origin repository]</span></p>
</wicket:fragment>
diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java index 134ee044..9639a0c1 100644 --- a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java +++ b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java @@ -73,6 +73,7 @@ import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.panels.LinkPanel;
import com.gitblit.wicket.panels.NavigationPanel;
import com.gitblit.wicket.panels.RefsPanel;
+import com.google.common.base.Optional;
public abstract class RepositoryPage extends RootPage {
@@ -295,25 +296,38 @@ public abstract class RepositoryPage extends RootPage { RepositoryModel model = getRepositoryModel();
if (StringUtils.isEmpty(model.originRepository)) {
if (model.isMirror) {
+ add(new Fragment("repoIcon", "mirrorIconFragment", this));
Fragment mirrorFrag = new Fragment("originRepository", "mirrorFragment", this);
Label lbl = new Label("originRepository", MessageFormat.format(getString("gb.mirrorOf"), "<b>" + model.origin + "</b>"));
mirrorFrag.add(lbl.setEscapeModelStrings(false));
add(mirrorFrag);
} else {
- add(new Label("originRepository").setVisible(false));
+ if (model.isBare) {
+ add(new Fragment("repoIcon", "repoIconFragment", this));
+ } else {
+ add(new Fragment("repoIcon", "cloneIconFragment", this));
+ }
+ add(new Label("originRepository", Optional.of(model.description).or("")));
}
} else {
RepositoryModel origin = app().repositories().getRepositoryModel(model.originRepository);
if (origin == null) {
- // no origin repository
- add(new Label("originRepository").setVisible(false));
+ // no origin repository, show description if available
+ if (model.isBare) {
+ add(new Fragment("repoIcon", "repoIconFragment", this));
+ } else {
+ add(new Fragment("repoIcon", "cloneIconFragment", this));
+ }
+ add(new Label("originRepository", Optional.of(model.description).or("")));
} else if (!user.canView(origin)) {
// show origin repository without link
+ add(new Fragment("repoIcon", "forkIconFragment", this));
Fragment forkFrag = new Fragment("originRepository", "originFragment", this);
forkFrag.add(new Label("originRepository", StringUtils.stripDotGit(model.originRepository)));
add(forkFrag);
} else {
// link to origin repository
+ add(new Fragment("repoIcon", "forkIconFragment", this));
Fragment forkFrag = new Fragment("originRepository", "originFragment", this);
forkFrag.add(new LinkPanel("originRepository", null, StringUtils.stripDotGit(model.originRepository),
SummaryPage.class, WicketUtils.newRepositoryParameter(model.originRepository)));
diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.html b/src/main/java/com/gitblit/wicket/pages/RootPage.html index 2ff305f2..c51db819 100644 --- a/src/main/java/com/gitblit/wicket/pages/RootPage.html +++ b/src/main/java/com/gitblit/wicket/pages/RootPage.html @@ -14,7 +14,7 @@ <span class="icon-bar"></span>
</a>
<a class="brand" wicket:id="rootLink">
- <img src="logo.png" height="45" width="120" class="logo"/>
+ <img src="logo.png" width="120" class="logo"/>
</a>
<div class="nav-collapse">
diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.java b/src/main/java/com/gitblit/wicket/pages/RootPage.java index c4d4dd11..37e98702 100644 --- a/src/main/java/com/gitblit/wicket/pages/RootPage.java +++ b/src/main/java/com/gitblit/wicket/pages/RootPage.java @@ -151,6 +151,7 @@ public abstract class RootPage extends BasePage { boolean authenticateAdmin = app().settings().getBoolean(Keys.web.authenticateAdminPages, true); boolean allowAdmin = app().settings().getBoolean(Keys.web.allowAdministration, true); boolean allowLucene = app().settings().getBoolean(Keys.web.allowLuceneIndexing, true); + boolean displayUserPanel = app().settings().getBoolean(Keys.web.displayUserPanel, true); boolean isLoggedIn = GitBlitWebSession.get().isLoggedIn(); if (authenticateAdmin) { @@ -168,7 +169,7 @@ public abstract class RootPage extends BasePage { } } - if (authenticateView || authenticateAdmin) { + if (displayUserPanel && (authenticateView || authenticateAdmin)) { if (isLoggedIn) { UserMenu userFragment = new UserMenu("userPanel", "userMenuFragment", RootPage.this); add(userFragment); diff --git a/src/main/java/com/gitblit/wicket/pages/TicketPage.html b/src/main/java/com/gitblit/wicket/pages/TicketPage.html index f3f38ec3..5ae005eb 100644 --- a/src/main/java/com/gitblit/wicket/pages/TicketPage.html +++ b/src/main/java/com/gitblit/wicket/pages/TicketPage.html @@ -67,6 +67,8 @@ <div style="border: 1px solid #ccc;padding: 10px;margin: 5px 0px;">
<table class="summary" style="width: 100%">
<tr><th><wicket:message key="gb.type"></wicket:message></th><td><span wicket:id="ticketType">[type]</span></td></tr>
+ <tr><th><wicket:message key="gb.priority"></wicket:message></th><td><span wicket:id="priority">[priority]</span></td></tr>
+ <tr><th><wicket:message key="gb.severity"></wicket:message></th><td><span wicket:id="severity">[severity]</span></td></tr>
<tr><th><wicket:message key="gb.topic"></wicket:message></th><td><span wicket:id="ticketTopic">[topic]</span></td></tr>
<tr><th><wicket:message key="gb.responsible"></wicket:message></th><td><span wicket:id="responsible">[responsible]</span></td></tr>
<tr><th><wicket:message key="gb.milestone"></wicket:message></th><td><span wicket:id="milestone">[milestone]</span></td></tr>
@@ -555,7 +557,6 @@ pt push</pre> </div>
</wicket:fragment>
-
<!-- VETOED PATCHSET FRAGMENT -->
<wicket:fragment wicket:id="vetoedFragment">
<div class="alert alert-error submit-info" style="padding:4px;">
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketPage.java b/src/main/java/com/gitblit/wicket/pages/TicketPage.java index 19788f27..254d3c9b 100644 --- a/src/main/java/com/gitblit/wicket/pages/TicketPage.java +++ b/src/main/java/com/gitblit/wicket/pages/TicketPage.java @@ -519,6 +519,10 @@ public class TicketPage extends RepositoryPage { * TICKET METADATA
*/
add(new Label("ticketType", ticket.type.toString()));
+
+ add(new Label("priority", ticket.priority.toString()));
+ add(new Label("severity", ticket.severity.toString()));
+
if (StringUtils.isEmpty(ticket.topic)) {
add(new Label("ticketTopic").setVisible(false));
} else {
@@ -529,6 +533,8 @@ public class TicketPage extends RepositoryPage { }
+
+
/*
* VOTERS
*/
@@ -1425,6 +1431,12 @@ public class TicketPage extends RepositoryPage { Fragment mergePanel = new Fragment("mergePanel", "alreadyMergedFragment", this);
mergePanel.add(new Label("mergeTitle", MessageFormat.format(getString("gb.patchsetAlreadyMerged"), ticket.mergeTo)));
return mergePanel;
+ } else if (MergeStatus.MISSING_INTEGRATION_BRANCH == mergeStatus) {
+ // target/integration branch is missing
+ Fragment mergePanel = new Fragment("mergePanel", "notMergeableFragment", this);
+ mergePanel.add(new Label("mergeTitle", MessageFormat.format(getString("gb.patchsetNotMergeable"), ticket.mergeTo)));
+ mergePanel.add(new Label("mergeMore", MessageFormat.format(getString("gb.missingIntegrationBranchMore"), ticket.mergeTo)));
+ return mergePanel;
} else {
// patchset can not be cleanly merged
Fragment mergePanel = new Fragment("mergePanel", "notMergeableFragment", this);
@@ -1503,7 +1515,7 @@ public class TicketPage extends RepositoryPage { */
protected RepositoryUrl getRepositoryUrl(UserModel user, RepositoryModel repository) {
HttpServletRequest req = ((WebRequest) getRequest()).getHttpServletRequest();
- List<RepositoryUrl> urls = app().gitblit().getRepositoryUrls(req, user, repository);
+ List<RepositoryUrl> urls = app().services().getRepositoryUrls(req, user, repository);
if (ArrayUtils.isEmpty(urls)) {
return null;
}
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketsPage.html b/src/main/java/com/gitblit/wicket/pages/TicketsPage.html index 3a3d977a..c686d1ff 100644 --- a/src/main/java/com/gitblit/wicket/pages/TicketsPage.html +++ b/src/main/java/com/gitblit/wicket/pages/TicketsPage.html @@ -31,17 +31,18 @@ <div class="hidden-phone">
<ul class="nav nav-list">
<li class="nav-header"><wicket:message key="gb.queries"></wicket:message></li>
- <li><a wicket:id="changesQuery"><i class="fa fa-code-fork"></i> <wicket:message key="gb.proposalTickets"></wicket:message></a></li>
- <li><a wicket:id="bugsQuery"><i class="fa fa-bug"></i> <wicket:message key="gb.bugTickets"></wicket:message></a></li>
- <li><a wicket:id="enhancementsQuery"><i class="fa fa-magic"></i> <wicket:message key="gb.enhancementTickets"></wicket:message></a></li>
- <li><a wicket:id="tasksQuery"><i class="fa fa-ticket"></i> <wicket:message key="gb.taskTickets"></wicket:message></a></li>
- <li><a wicket:id="questionsQuery"><i class="fa fa-question"></i> <wicket:message key="gb.questionTickets"></wicket:message></a></li>
+ <li><a wicket:id="changesQuery"><i class="fa fa-code-fork fa-fw"></i> <wicket:message key="gb.proposalTickets"></wicket:message></a></li>
+ <li><a wicket:id="bugsQuery"><i class="fa fa-bug fa-fw"></i> <wicket:message key="gb.bugTickets"></wicket:message></a></li>
+ <li><a wicket:id="enhancementsQuery"><i class="fa fa-magic fa-fw"></i> <wicket:message key="gb.enhancementTickets"></wicket:message></a></li>
+ <li><a wicket:id="tasksQuery"><i class="fa fa-ticket fa-fw"></i> <wicket:message key="gb.taskTickets"></wicket:message></a></li>
+ <li><a wicket:id="questionsQuery"><i class="fa fa-question fa-fw"></i> <wicket:message key="gb.questionTickets"></wicket:message></a></li>
+ <li><a wicket:id="maintenanceQuery"><i class="fa fa-cogs fa-fw"></i> <wicket:message key="gb.maintenanceTickets"></wicket:message></a></li>
<li wicket:id="userDivider" class="divider"></li>
- <li><a wicket:id="createdQuery"><i class="fa fa-user"></i> <wicket:message key="gb.yourCreatedTickets"></wicket:message></a></li>
- <li><a wicket:id="watchedQuery"><i class="fa fa-eye"></i> <wicket:message key="gb.yourWatchedTickets"></wicket:message></a></li>
- <li><a wicket:id="mentionsQuery"><i class="fa fa-comment"></i> <wicket:message key="gb.mentionsMeTickets"></wicket:message></a></li>
+ <li><a wicket:id="createdQuery"><i class="fa fa-user fa-fw"></i> <wicket:message key="gb.yourCreatedTickets"></wicket:message></a></li>
+ <li><a wicket:id="watchedQuery"><i class="fa fa-eye fa-fw"></i> <wicket:message key="gb.yourWatchedTickets"></wicket:message></a></li>
+ <li><a wicket:id="mentionsQuery"><i class="fa fa-comment fa-fw"></i> <wicket:message key="gb.mentionsMeTickets"></wicket:message></a></li>
<li class="divider"></li>
- <li><a wicket:id="resetQuery"><i class="fa fa-bolt"></i> <wicket:message key="gb.reset"></wicket:message></a></li>
+ <li><a wicket:id="resetQuery"><i class="fa fa-bolt fa-fw"></i> <wicket:message key="gb.reset"></wicket:message></a></li>
</ul>
</div>
<div wicket:id="dynamicQueries" class="hidden-phone"></div>
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketsPage.java b/src/main/java/com/gitblit/wicket/pages/TicketsPage.java index 658cddec..ecfed250 100644 --- a/src/main/java/com/gitblit/wicket/pages/TicketsPage.java +++ b/src/main/java/com/gitblit/wicket/pages/TicketsPage.java @@ -272,6 +272,16 @@ public class TicketsPage extends RepositoryPage { sortBy,
desc,
1)));
+
+ add(new BookmarkablePageLink<Void>("maintenanceQuery", TicketsPage.class,
+ queryParameters(
+ Lucene.type.matches(TicketModel.Type.Maintenance.name()),
+ milestoneParam,
+ statiiParam,
+ assignedToParam,
+ sortBy,
+ desc,
+ 1)));
add(new BookmarkablePageLink<Void>("resetQuery", TicketsPage.class,
queryParameters(
@@ -454,7 +464,11 @@ public class TicketsPage extends RepositoryPage { sortChoices.add(new TicketSort(getString("gb.sortLeastPatchsetRevisions"), Lucene.patchsets.name(), false));
sortChoices.add(new TicketSort(getString("gb.sortMostVotes"), Lucene.votes.name(), true));
sortChoices.add(new TicketSort(getString("gb.sortLeastVotes"), Lucene.votes.name(), false));
-
+ sortChoices.add(new TicketSort(getString("gb.sortHighestPriority"), Lucene.priority.name(), true));
+ sortChoices.add(new TicketSort(getString("gb.sortLowestPriority"), Lucene.priority.name(), false));
+ sortChoices.add(new TicketSort(getString("gb.sortHighestSeverity"), Lucene.severity.name(), true));
+ sortChoices.add(new TicketSort(getString("gb.sortLowestSeverity"), Lucene.severity.name(), false));
+
TicketSort currentSort = sortChoices.get(0);
for (TicketSort ts : sortChoices) {
if (ts.sortBy.equals(sortBy) && desc == ts.desc) {
diff --git a/src/main/java/com/gitblit/wicket/pages/TreePage.java b/src/main/java/com/gitblit/wicket/pages/TreePage.java index 9ddbecf6..d7899dcb 100644 --- a/src/main/java/com/gitblit/wicket/pages/TreePage.java +++ b/src/main/java/com/gitblit/wicket/pages/TreePage.java @@ -52,7 +52,7 @@ public class TreePage extends RepositoryPage { Repository r = getRepository();
RevCommit commit = getCommit();
- List<PathModel> paths = JGitUtils.getFilesInPath(r, path, commit);
+ List<PathModel> paths = JGitUtils.getFilesInPath2(r, path, commit);
// tree page links
add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
diff --git a/src/main/java/com/gitblit/wicket/pages/UserPage.java b/src/main/java/com/gitblit/wicket/pages/UserPage.java index 8931d5e3..6e7e7a76 100644 --- a/src/main/java/com/gitblit/wicket/pages/UserPage.java +++ b/src/main/java/com/gitblit/wicket/pages/UserPage.java @@ -104,7 +104,7 @@ public class UserPage extends RootPage { if (isMyProfile) {
addPreferences(user);
- if (app().gitblit().isServingSSH()) {
+ if (app().services().isServingSSH()) {
// show the SSH key management tab
addSshKeys(user);
} else {
@@ -248,14 +248,16 @@ public class UserPage extends RootPage { emailMeOnMyTicketChanges).setVisible(app().notifier().isSendingMail()));
List<Transport> availableTransports = new ArrayList<>();
- if (app().gitblit().isServingSSH()) {
+ if (app().services().isServingSSH()) {
availableTransports.add(Transport.SSH);
}
- if (app().gitblit().isServingHTTP()) {
- availableTransports.add(Transport.HTTPS);
+ if (app().services().isServingHTTP()) {
availableTransports.add(Transport.HTTP);
}
- if (app().gitblit().isServingGIT()) {
+ if (app().services().isServingHTTPS()) {
+ availableTransports.add(Transport.HTTPS);
+ }
+ if (app().services().isServingGIT()) {
availableTransports.add(Transport.GIT);
}
diff --git a/src/main/java/com/gitblit/wicket/pages/scripts/imgdiff.js b/src/main/java/com/gitblit/wicket/pages/scripts/imgdiff.js new file mode 100644 index 00000000..61575132 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/scripts/imgdiff.js @@ -0,0 +1,263 @@ +/* + * Copyright 2014 Tom <tw201207@gmail.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. + */ +(function($) { + +/** + * Sets up elem as a slider; returns an access object. Elem must be positioned! + * Note that the element may contain other elements; this is used for instance + * for the image diff overlay slider. + * + * The styling of the slider is to be done in CSS. Currently recognized options: + * - initial: <float> clipped to [0..1], default 0 + * - handleClass: <string> to assign to the handle span element created. + * If no handleClass is specified, a very plain default style is assigned. + */ +function rangeSlider(elem, options) { + options = $.extend({ initial : 0 }, options || {}); + options.initial = Math.min(1.0, Math.max(0, options.initial)); + + var $elem = $(elem); + var $handle = $('<span></span>').css({ position: 'absolute', left: 0, cursor: 'ew-resize' }); + var $root = $(document.documentElement); + var $doc = $(document); + var lastRatio = options.initial; + + /** Mousemove event handler to track the mouse and move the slider. Generates slider:pos events. */ + function track(e) { + var pos = $elem.offset().left; + var width = $elem.innerWidth(); + var handleWidth = $handle.outerWidth(false); + var range = width - handleWidth; + if (range <= 0) return; + var delta = Math.min(range, Math.max (0, e.pageX - pos - handleWidth / 2)); + lastRatio = delta / range; + $handle.css('left', "" + (delta * 100 / width) + '%'); + $elem.trigger('slider:pos', { ratio: lastRatio, handle: $handle[0] }); + } + + /** Mouseup event handler to stop mouse tracking. */ + function end(e) { + $doc.off('mousemove', track); + $doc.off('mouseup', end); + $root.removeClass('no-select'); + } + + /** Snaps the slider to the given ratio and generates a slider:pos event with the new ratio. */ + function setTo(ratio) { + var w = $elem.innerWidth(); + if (w <= 0 || $elem.is(':hidden')) return; + lastRatio = Math.min( 1.0, Math.max(0, ratio)); + $handle.css('left', "" + Math.max(0, 100 * (lastRatio * (w - $handle.outerWidth(false))) / w) + '%'); + $elem.trigger('slider:pos', { ratio: lastRatio, handle: $handle[0] }); + } + + /** + * Moves the slider to the given ratio, clipped to [0..1], in duration milliseconds. + * Generates slider:pos events during the animation. If duration <= 30, same as setTo. + * Default duration is 500ms. If a callback is given, it's called once the animation + * has completed. + */ + function moveTo(ratio, duration, callback) { + ratio = Math.min(1.0, Math.max(0, ratio)); + if (ratio === lastRatio) { + if (typeof callback == 'function') callback(); + return; + } + if (typeof duration == 'undefined') duration = 500; + if (duration <= 30) { + // Cinema is 24 or 48 frames/sec, so 20-40ms per frame. Makes no sense to animate for such a short duration. + setTo(ratio); + if (typeof callback == 'function') callback(); + } else { + var target = ratio * ($elem.innerWidth() - $handle.outerWidth(false)); + if (ratio > lastRatio) target--; else target++; + $handle.stop().animate({left: target}, + { 'duration' : duration, + 'step' : function() { + lastRatio = Math.min(1.0, Math.max(0, $handle.position().left / ($elem.innerWidth() - $handle.outerWidth(false)))); + $elem.trigger('slider:pos', { ratio : lastRatio, handle : $handle[0] }); + }, + 'complete' : function() { setTo(ratio); if (typeof callback == 'function') callback(); } // Ensure we have again a % value + } + ); + } + } + + /** + * As moveTo, but determines an appropriate duration in the range [0..maxDuration] on its own, + * depending on the distance the handle would move. If no maxDuration is given it defaults + * to 1500ms. + */ + function moveAuto(ratio, maxDuration, callback) { + if (typeof maxDuration == 'undefined') maxDuration = 1500; + var delta = ratio - lastRatio; + if (delta < 0) delta = -delta; + var speed = $elem.innerWidth() * delta * 2; + if (speed > maxDuration) speed = maxDuration; + moveTo(ratio, speed, callback); + } + + /** Returns the current ratio. */ + function getValue() { + return lastRatio; + } + + $elem.append($handle); + if (options.handleClass) { + $handle.addClass(options.handleClass); + } else { // Provide a default style so that it is at least visible + $handle.css({ width: '10px', height: '10px', background: 'white', border: '1px solid black' }); + } + if (options.initial) setTo(options.initial); + + /** Install mousedown handler to start mouse tracking. */ + $handle.on('mousedown', function(e) { + $root.addClass('no-select'); + $doc.on('mousemove', track); + $doc.on('mouseup', end); + e.stopPropagation(); + e.preventDefault(); + }); + + return { setRatio: setTo, moveRatio: moveTo, 'moveAuto': moveAuto, getRatio: getValue, handle: $handle[0] }; +} + +function setup() { + $('.imgdiff-container').each(function() { + var $this = $(this); + var $overlaySlider = $this.find('.imgdiff-ovr-slider').first(); + var $opacitySlider = $this.find('.imgdiff-opa-slider').first(); + var overlayAccess = rangeSlider($overlaySlider, {handleClass: 'imgdiff-ovr-handle'}); + var opacityAccess = rangeSlider($opacitySlider, {handleClass: 'imgdiff-opa-handle'}); + var $img = $('#' + this.id.substr(this.id.indexOf('-')+1)); // Here we change opacity + var $div = $img.parent(); // This controls visibility: here we change width. + var blinking = false; + + $overlaySlider.on('slider:pos', function(e, data) { + var pos = $(data.handle).offset().left; + var imgLeft = $img.offset().left; // Global + var imgW = $img.outerWidth(true); + var imgOff = $img.position().left; // From left edge of $div + if (pos <= imgLeft) { + $div.width(0); + } else if (pos <= imgLeft + imgW) { + $div.width(pos - imgLeft + imgOff); + } else if ($div.width() < imgW + imgOff) { + $div.width(imgW + imgOff); + } + }); + $overlaySlider.css('cursor', 'pointer'); + $overlaySlider.on('mousedown', function(e) { + var newRatio = (e.pageX - $overlaySlider.offset().left) / $overlaySlider.innerWidth(); + var oldRatio = overlayAccess.getRatio(); + if (newRatio !== oldRatio) { + overlayAccess.moveAuto(newRatio); + } + }); + + var autoShowing = false; + $opacitySlider.on('slider:pos', function(e, data) { + if ($div.width() <= 0 && !blinking) { + // Make old image visible in a nice way, *then* adjust opacity + autoShowing = true; + overlayAccess.moveAuto(1.0, 500, function() { + $img.stop().animate( + {opacity: 1.0 - opacityAccess.getRatio()}, + {duration: 400, + complete: function () { + // In case the opacity handle was moved while we were trying to catch up + $img.css('opacity', 1.0 - opacityAccess.getRatio()); + autoShowing = false; + } + } + ); + }); + } else if (!autoShowing) { + $img.css('opacity', 1.0 - data.ratio); + } + }); + $opacitySlider.on('click', function(e) { + var newRatio = (e.pageX - $opacitySlider.offset().left) / $opacitySlider.innerWidth(); + var oldRatio = opacityAccess.getRatio(); + if (newRatio !== oldRatio) { + if ($div.width() <= 0) { + overlayAccess.moveRatio(1.0, 500, function() {opacityAccess.moveAuto(newRatio);}); // Make old image visible in a nice way + } else { + opacityAccess.moveAuto(newRatio) + } + } + e.preventDefault(); + }); + + // Blinking before and after images is a good way for the human eye to catch differences. + var $blinker = $this.find('.imgdiff-blink'); + var initialOpacity = null; + $blinker.on('click', function(e) { + if (blinking) { + window.clearTimeout(blinking); + $blinker.children('img').first().css('border', '1px solid transparent'); + opacityAccess.setRatio(initialOpacity); + blinking = null; + } else { + $blinker.children('img').first().css('border', '1px solid #AAA'); + initialOpacity = opacityAccess.getRatio(); + var currentOpacity = 1.0; + function blink() { + opacityAccess.setRatio(currentOpacity); + currentOpacity = 1.0 - currentOpacity; + // Keep frequeny below 2Hz (i.e., delay above 500ms) + blinking = window.setTimeout(blink, 600); + } + if ($div.width() <= 0) { + overlayAccess.moveRatio(1.0, 500, blink); + } else { + blink(); + } + } + e.preventDefault(); + }); + + // Subtracting before and after images is another good way to detect differences. Result will be + // black where identical. + if (typeof $img[0].style.mixBlendMode != 'undefined') { + // Feature test: does the browser support the mix-blend-mode CSS property from the Compositing + // and Blending Level 1 spec (http://dev.w3.org/fxtf/compositing-1/#mix-blend-mode )? + // As of 2014-11, only Firefox >= 32 and Safari >= 7.1 support this. Other browsers will have to + // make do with the blink comparator only. + var $sub = $this.find('.imgdiff-subtract'); + $sub.css('display', 'inline-block'); + $sub.on('click', function (e) { + var curr = $img.css('mix-blend-mode'); + if (curr != 'difference') { + curr = 'difference'; + $sub.children('img').first().css('border', '1px solid #AAA'); + if ($div.width() <= 0) overlayAccess.moveRatio(1.0, 500); + opacityAccess.setRatio(0); + } else { + curr = 'normal'; + $sub.children('img').first().css('border', '1px solid transparent'); + + } + $img.css('mix-blend-mode', curr); + e.preventDefault(); + }); + } + }); +} + +$(setup); // Run on jQuery's dom-ready + +})(jQuery);
\ No newline at end of file |