model = new ProjectModel(project);
+ item.add(PageSpec.newProjectHomeLink("projectlink", project)
+ .add(new Label("name", project.getName())));
+
+ if (project.getForkedFrom() != null) {
+ item.add(new ProjectHomeLink("forklink", new ProjectModel(project.getForkedFrom())));
+ } else {
+ item.add(new WebMarkupContainer("forklink").setVisibilityAllowed(false));
+ }
+
+ item.add(new Label("description", project.getDescription()));
+ }
+
+ };
+
+ add(projectsView);
+ }
+
+ private User getThisAccount() {
+ return (User) getDefaultModelObject();
+ }
+}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/AccountPermissionPage.html b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/AccountPermissionPage.html
index 724257cd71..770790b5ce 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/AccountPermissionPage.html
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/AccountPermissionPage.html
@@ -22,7 +22,7 @@
Teams
- Manage teams and grant the permissions so that all members in the team can have the permission to access any repositories under this account.
+ Manage teams and grant the permissions so that all members in the team can have the permissions to access any repositories under this account.
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/TeamEditor.html b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/TeamEditor.html
index 3074a93100..da2ee2633b 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/TeamEditor.html
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/TeamEditor.html
@@ -44,7 +44,7 @@
()
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/repos/AccountReposPage.html b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/repos/AccountReposPage.html
new file mode 100644
index 0000000000..2c2d47b9fd
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/repos/AccountReposPage.html
@@ -0,0 +1,19 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/repos/AccountReposPage.java b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/repos/AccountReposPage.java
index 4e4e3ce217..441530a814 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/repos/AccountReposPage.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/repos/AccountReposPage.java
@@ -1,6 +1,19 @@
package com.pmease.gitop.web.page.account.setting.repos;
+import java.util.List;
+
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.LoadableDetachableModel;
+
+import com.google.common.collect.Lists;
+import com.pmease.gitop.core.model.Project;
+import com.pmease.gitop.web.model.ProjectModel;
+import com.pmease.gitop.web.page.PageSpec;
import com.pmease.gitop.web.page.account.setting.AccountSettingPage;
+import com.pmease.gitop.web.page.project.ProjectAdminPage;
@SuppressWarnings("serial")
public class AccountReposPage extends AccountSettingPage {
@@ -14,4 +27,32 @@ public class AccountReposPage extends AccountSettingPage {
protected Category getSettingCategory() {
return Category.REPOS;
}
+
+ @Override
+ protected void onPageInitialize() {
+ super.onPageInitialize();
+
+ IModel> model = new LoadableDetachableModel>() {
+
+ @Override
+ protected List load() {
+ return Lists.newArrayList(getAccount().getRepositories());
+ }
+
+ };
+
+ ListView view = new ListView("projects", model) {
+
+ @Override
+ protected void populateItem(ListItem item) {
+ Project project = item.getModelObject();
+ item.add(new SimpleProjectInfo("info", new ProjectModel(project)));
+ item.add(new BookmarkablePageLink("admin", ProjectAdminPage.class,
+ PageSpec.forProject(project)));
+ }
+
+ };
+
+ add(view);
+ }
}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/repos/SimpleProjectInfo.html b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/repos/SimpleProjectInfo.html
new file mode 100644
index 0000000000..673f6de3d4
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/repos/SimpleProjectInfo.html
@@ -0,0 +1,5 @@
+
+
+ steve/repo
+
+
\ No newline at end of file
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/repos/SimpleProjectInfo.java b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/repos/SimpleProjectInfo.java
new file mode 100644
index 0000000000..3a17d90b5a
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/repos/SimpleProjectInfo.java
@@ -0,0 +1,58 @@
+package com.pmease.gitop.web.page.account.setting.repos;
+
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+
+import com.pmease.gitop.core.model.Project;
+
+public class SimpleProjectInfo extends Panel {
+
+ private static final long serialVersionUID = 1L;
+
+ public SimpleProjectInfo(String id, IModel model) {
+ super(id, model);
+ }
+
+ @Override
+ protected void onInitialize() {
+ super.onInitialize();
+
+// add(new UserProjectLink("project", (IModel) getDefaultModel()));
+// add(new Label("age", new AbstractReadOnlyModel() {
+//
+// @Override
+// public String getObject() {
+// return DateUtils.formatAge(getProject().getCreatedAt());
+// }
+//
+// }).add(AttributeModifier.replace("title",
+// new AbstractReadOnlyModel() {
+//
+// @Override
+// public String getObject() {
+// return DataTypes.DATE
+// .asString(getProject().getCreatedAt(),
+// Constants.DATETIME_FULL_FORMAT);
+// }
+//
+// })));
+//
+// if (getProject().isForked()) {
+// add(new UserProjectLink("forkedFrom",
+// new LoadableDetachableModel() {
+//
+// @Override
+// protected Project load() {
+// return getProject().getParentFork().getForkedFrom();
+// }
+// }));
+// } else {
+// add(new WebMarkupContainer("forkedFrom")
+// .setVisibilityAllowed(false));
+// }
+ }
+
+ protected Project getProject() {
+ return (Project) getDefaultModelObject();
+ }
+}
\ No newline at end of file
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/project/AbstractProjectPage.java b/gitop.web/src/main/java/com/pmease/gitop/web/page/project/AbstractProjectPage.java
new file mode 100644
index 0000000000..1db6c62f72
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/project/AbstractProjectPage.java
@@ -0,0 +1,47 @@
+package com.pmease.gitop.web.page.project;
+
+import javax.persistence.EntityNotFoundException;
+
+import org.apache.shiro.SecurityUtils;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+import com.google.common.base.Preconditions;
+import com.pmease.gitop.core.Gitop;
+import com.pmease.gitop.core.manager.ProjectManager;
+import com.pmease.gitop.core.model.Project;
+import com.pmease.gitop.core.model.User;
+import com.pmease.gitop.core.permission.ObjectPermission;
+import com.pmease.gitop.web.model.ProjectModel;
+import com.pmease.gitop.web.page.PageSpec;
+import com.pmease.gitop.web.page.account.AbstractAccountPage;
+
+@SuppressWarnings("serial")
+public abstract class AbstractProjectPage extends AbstractAccountPage {
+
+ protected IModel projectModel;
+
+ public AbstractProjectPage(PageParameters params) {
+ super(params);
+
+ String projectName = params.get(PageSpec.PROJECT).toString();
+ Preconditions.checkNotNull(projectName);
+
+ User user = accountModel.getObject();
+ Project project = Gitop.getInstance(ProjectManager.class).find(
+ user, projectName);
+
+ if (project == null) {
+ throw new EntityNotFoundException("Unable to find project "
+ + user.getName() + " / " + projectName);
+ }
+
+ projectModel = new ProjectModel(project);
+ }
+
+ @Override
+ protected boolean isPermitted() {
+ return SecurityUtils.getSubject().isPermitted(
+ ObjectPermission.ofProjectRead(projectModel.getObject()));
+ }
+}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/project/CreateProjectPage.html b/gitop.web/src/main/java/com/pmease/gitop/web/page/project/CreateProjectPage.html
new file mode 100644
index 0000000000..86c2edd5d0
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/project/CreateProjectPage.html
@@ -0,0 +1,17 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/project/CreateProjectPage.java b/gitop.web/src/main/java/com/pmease/gitop/web/page/project/CreateProjectPage.java
new file mode 100644
index 0000000000..7b3b66deac
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/project/CreateProjectPage.java
@@ -0,0 +1,88 @@
+package com.pmease.gitop.web.page.project;
+
+import org.apache.shiro.authz.annotation.RequiresUser;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.bean.validation.PropertyValidator;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.validation.IValidatable;
+import org.apache.wicket.validation.IValidator;
+import org.apache.wicket.validation.ValidationError;
+
+import com.pmease.gitop.core.Gitop;
+import com.pmease.gitop.core.manager.ProjectManager;
+import com.pmease.gitop.core.model.Project;
+import com.pmease.gitop.core.model.User;
+import com.pmease.gitop.web.common.form.FeedbackPanel;
+import com.pmease.gitop.web.common.form.flatcheckbox.FlatCheckBoxElement;
+import com.pmease.gitop.web.common.form.textfield.TextFieldElement;
+import com.pmease.gitop.web.model.ProjectModel;
+import com.pmease.gitop.web.page.AbstractLayoutPage;
+import com.pmease.gitop.web.page.PageSpec;
+
+@SuppressWarnings("serial")
+@RequiresUser
+public class CreateProjectPage extends AbstractLayoutPage {
+
+ @Override
+ protected String getPageTitle() {
+ return "Create a new project";
+ }
+
+ @Override
+ protected void onPageInitialize() {
+ super.onPageInitialize();
+
+ final IModel projectModel = new ProjectModel(new Project());
+ Form form = new Form("form", projectModel);
+ add(form);
+
+ form.add(new FeedbackPanel("feedback"));
+ form.add(new TextFieldElement("name", "Project Name",
+ new PropertyModel(projectModel, "name"))
+ .add(new PropertyValidator())
+ .add(new IValidator() {
+
+ @Override
+ public void validate(IValidatable validatable) {
+ String name = validatable.getValue();
+ User owner = User.getCurrent();
+
+ for (Project each : owner.getRepositories()) {
+ if (each.getName().equalsIgnoreCase(name)) {
+ validatable.error(new ValidationError().setMessage("This project already exists"));
+ return;
+ }
+ }
+ }
+
+ }));
+
+ form.add(new TextFieldElement("description", "Description",
+ new PropertyModel(projectModel, "description"))
+ .add(new PropertyValidator())
+ .setRequired(false));
+
+ form.add(new FlatCheckBoxElement("public", "Public Accessible",
+ new PropertyModel(projectModel, "publiclyAccessible"),
+ Model.of("Anyone can browse and pull this repository")));
+
+ form.add(new AjaxButton("submit", form) {
+ @Override
+ protected void onError(AjaxRequestTarget target, Form> form) {
+ target.add(form);
+ }
+
+ @Override
+ protected void onSubmit(AjaxRequestTarget target, Form> form) {
+ Project project = projectModel.getObject();
+ project.setOwner(User.getCurrent());
+ Gitop.getInstance(ProjectManager.class).save(project);
+ setResponsePage(ProjectHomePage.class, PageSpec.forProject(project));
+ }
+ });
+ }
+}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/project/ProjectAdminPage.html b/gitop.web/src/main/java/com/pmease/gitop/web/page/project/ProjectAdminPage.html
new file mode 100644
index 0000000000..25fa8688af
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/project/ProjectAdminPage.html
@@ -0,0 +1,5 @@
+
+
+ Project Administration
+
+
\ No newline at end of file
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/project/ProjectAdminPage.java b/gitop.web/src/main/java/com/pmease/gitop/web/page/project/ProjectAdminPage.java
new file mode 100644
index 0000000000..6bbf95a8a5
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/project/ProjectAdminPage.java
@@ -0,0 +1,13 @@
+package com.pmease.gitop.web.page.project;
+
+import com.pmease.gitop.web.page.AbstractLayoutPage;
+
+@SuppressWarnings("serial")
+public class ProjectAdminPage extends AbstractLayoutPage {
+
+ @Override
+ protected String getPageTitle() {
+ return "Administration - {Project}";
+ }
+
+}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/project/ProjectHomePage.java b/gitop.web/src/main/java/com/pmease/gitop/web/page/project/ProjectHomePage.java
index 262455d866..c6387cfdfc 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/page/project/ProjectHomePage.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/project/ProjectHomePage.java
@@ -1,6 +1,6 @@
package com.pmease.gitop.web.page.project;
-import org.apache.shiro.authz.annotation.RequiresAuthentication;
+import org.apache.shiro.SecurityUtils;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
@@ -10,10 +10,11 @@ import com.pmease.commons.util.GeneralException;
import com.pmease.gitop.core.Gitop;
import com.pmease.gitop.core.manager.ProjectManager;
import com.pmease.gitop.core.model.Project;
+import com.pmease.gitop.core.permission.ObjectPermission;
import com.pmease.gitop.web.page.AbstractLayoutPage;
+import com.pmease.gitop.web.page.PageSpec;
@SuppressWarnings("serial")
-@RequiresAuthentication
public class ProjectHomePage extends AbstractLayoutPage {
private final IModel projectModel;
@@ -24,8 +25,8 @@ public class ProjectHomePage extends AbstractLayoutPage {
}
public ProjectHomePage(PageParameters params) {
- String userName = params.get("user").toString();
- String projectName = params.get("project").toString();
+ String userName = params.get(PageSpec.USER).toString();
+ String projectName = params.get(PageSpec.PROJECT).toString();
if (projectName.endsWith(".git"))
projectName = projectName.substring(0, projectName.length() - ".git".length());
@@ -47,6 +48,11 @@ public class ProjectHomePage extends AbstractLayoutPage {
};
}
+ @Override
+ protected boolean isPermitted() {
+ return SecurityUtils.getSubject().isPermitted(ObjectPermission.ofProjectRead(getProject()));
+ }
+
@Override
protected void onPageInitialize() {
super.onPageInitialize();
@@ -65,13 +71,4 @@ public class ProjectHomePage extends AbstractLayoutPage {
projectModel.detach();
super.detachModels();
}
-
- public static PageParameters paramsOf(Project project) {
- PageParameters params = new PageParameters();
- params.set("user", project.getOwner().getName());
- params.set("project", project.getName());
-
- return params;
- }
-
}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/shiro/LoginPage.java b/gitop.web/src/main/java/com/pmease/gitop/web/shiro/LoginPage.java
index 8b29ecb954..92a806868f 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/shiro/LoginPage.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/shiro/LoginPage.java
@@ -17,7 +17,7 @@ import com.pmease.gitop.web.common.form.checkbox.CheckBoxElement;
import com.pmease.gitop.web.common.form.flatcheckbox.FlatCheckBoxElement;
import com.pmease.gitop.web.page.AbstractLayoutPage;
import com.pmease.gitop.web.page.PageSpec;
-import com.pmease.gitop.web.page.account.AccountHomePage;
+import com.pmease.gitop.web.page.account.home.AccountHomePage;
import com.pmease.gitop.web.util.WicketUtils;
@SuppressWarnings("serial")
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/util/DateUtils.java b/gitop.web/src/main/java/com/pmease/gitop/web/util/DateUtils.java
new file mode 100644
index 0000000000..3657f13ce8
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/util/DateUtils.java
@@ -0,0 +1,158 @@
+package com.pmease.gitop.web.util;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import org.apache.commons.lang3.time.DurationFormatUtils;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeConstants;
+import org.joda.time.DateTimeZone;
+import org.joda.time.Days;
+import org.joda.time.Duration;
+import org.joda.time.Hours;
+import org.joda.time.Period;
+import org.joda.time.PeriodType;
+import org.joda.time.chrono.ISOChronology;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.PeriodFormatter;
+import org.joda.time.format.PeriodFormatterBuilder;
+
+import com.google.common.base.Preconditions;
+import com.pmease.gitop.web.Constants;
+
+public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
+
+ public static final String AGE = "age";
+
+ static enum DurationUnit {
+ year, month, week, day, hour, minute, second
+ }
+
+ static String formatAge(DateTime dtBefore, DateTime dtNow) {
+ return formatAge(dtBefore, dtNow, Constants.DATETIME_FORMAT);
+ }
+
+ static String formatAge(DateTime dtBefore, DateTime dtNow,
+ String fullDateFormat) {
+ Preconditions.checkArgument(dtBefore != null && dtNow != null);
+
+ Period period = new Period(dtBefore, dtNow);
+
+ int years = period.getYears();
+ int months = period.getMonths();
+ int weeks = period.getWeeks();
+ int days = Days.daysBetween(dtBefore.toDateMidnight(),
+ dtNow.toDateMidnight()).getDays();
+
+ if (years > 0 || months > 0 || weeks > 0 || days > 6) {
+ return DateUtils.formatDate(dtBefore.toDate(), fullDateFormat);
+ }
+
+ if (days > 1) {
+ return formatDurationPart(days, DurationUnit.day);
+ }
+
+ // acutal hours
+ int hours = Hours.hoursBetween(
+ dtBefore.hourOfDay().roundHalfFloorCopy(),
+ dtNow.hourOfDay().roundHalfFloorCopy()).getHours();
+
+ if ((hours >= DateTimeConstants.HOURS_PER_DAY)
+ || (days == 1 && hours > 12)) {
+ return formatDurationPart(days, DurationUnit.day);
+ }
+
+ if (hours > 0) {
+ return formatDurationPart(hours, DurationUnit.hour);
+ }
+
+ int minutes = period.getMinutes();
+ if (minutes > 0) {
+ return formatDurationPart(minutes, DurationUnit.minute);
+ }
+ return formatDurationPart(period.getSeconds(), DurationUnit.second);
+ }
+
+ private static String formatDurationPart(int dur, DurationUnit unit) {
+ if (unit == DurationUnit.second && dur < 5) { // < 30 seconds
+ return "just now";
+ }
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("about ").append(dur).append(" ").append(unit);
+ ;
+ if (dur > 1) {
+ sb.append("s");
+ }
+ sb.append(" ago");
+
+ return sb.toString();
+ }
+
+ public static String formatAge(Date date) {
+ return formatAge(new DateTime(date), DateTime.now());
+ }
+
+ public static String formatDate(Date date) {
+ return formatDate(date, Constants.DATE_FORMAT);
+ }
+
+ public static String formatDateTime(Date date) {
+ return formatDate(date, Constants.DATETIME_FORMAT);
+ }
+
+ public static String formatDate(Date date, String pattern) {
+ return DateTimeFormat.forPattern(pattern)
+ .withZone(DateTimeZone.UTC)
+ .print(new DateTime(date));
+ }
+
+ public static String formatDuration(long durationMillis) {
+ if (durationMillis < 1000) {
+ return durationMillis + " ms";
+ } else {
+ return DurationFormatUtils.formatDurationWords(durationMillis,
+ true, true);
+ }
+ }
+
+ public static String formatDurationShortWords(long durationMills) {
+ if (durationMills < DateTimeConstants.MILLIS_PER_SECOND) {
+ return durationMills + " ms";
+ } else {
+ Duration duration = new Duration(durationMills);
+ return shortWordFormatter.print(duration.toPeriod(
+ PeriodType.yearMonthDayTime(),
+ ISOChronology.getInstanceUTC()));
+ }
+ }
+
+ public static void main(String[] argv) {
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.YEAR, 2012);
+ cal.set(Calendar.MONTH, 9); // Oct.
+ cal.set(Calendar.DAY_OF_MONTH, 21);
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.setTimeZone(DateTimeZone.forID("America/Sao_Paulo").toTimeZone());
+ DateTime dt = new DateTime(cal);
+ try {
+ dt.withMillisOfDay(0);
+ } catch (IllegalArgumentException e) {
+ // Illegal instant due to time zone offset transition
+ e.printStackTrace();
+ }
+
+ System.out.println(dt.toDateMidnight());
+ }
+
+ static PeriodFormatter shortWordFormatter = new PeriodFormatterBuilder()
+ .appendDays().appendSuffix("d").appendSeparator(", ").appendHours()
+ .appendSuffix("h").appendSeparator(":").appendMinutes()
+ .appendSuffix("m").appendSeparator(":").appendSeconds()
+ .appendSuffix("s")
+ // .appendSeparator(", ")
+ // .appendMillis()
+ .toFormatter();
+}
\ No newline at end of file