Add user home page

This commit is contained in:
steve 2013-10-16 01:29:03 +08:00
parent 081104ae0e
commit 26e934079d
52 changed files with 1942 additions and 108 deletions

View File

@ -66,6 +66,12 @@
<artifactId>commons-fileupload</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.imgscalr</groupId>

View File

@ -37,8 +37,8 @@ import com.pmease.gitop.core.manager.UserManager;
import com.pmease.gitop.web.assets.AssetLocator;
import com.pmease.gitop.web.component.avatar.AvatarImageResource;
import com.pmease.gitop.web.component.avatar.AvatarImageResourceReference;
import com.pmease.gitop.web.page.account.AccountHomePage;
import com.pmease.gitop.web.page.account.RegisterPage;
import com.pmease.gitop.web.page.account.home.AccountHomePage;
import com.pmease.gitop.web.page.account.setting.password.AccountPasswordPage;
import com.pmease.gitop.web.page.account.setting.permission.AccountPermissionPage;
import com.pmease.gitop.web.page.account.setting.permission.AddTeamPage;
@ -47,6 +47,7 @@ import com.pmease.gitop.web.page.account.setting.profile.AccountProfilePage;
import com.pmease.gitop.web.page.account.setting.repos.AccountReposPage;
import com.pmease.gitop.web.page.home.HomePage;
import com.pmease.gitop.web.page.init.ServerInitPage;
import com.pmease.gitop.web.page.project.CreateProjectPage;
import com.pmease.gitop.web.page.project.ProjectHomePage;
import com.pmease.gitop.web.page.test.TestPage;
import com.pmease.gitop.web.page.test.TestPage2;
@ -181,6 +182,9 @@ public class GitopWebApp extends AbstractWicketConfig {
mountPage("settings/repos", AccountReposPage.class);
mountPage("teams/add", AddTeamPage.class);
mountPage("teams/edit/${teamId}", EditTeamPage.class);
// project related
mountPage("new", CreateProjectPage.class);
mountPage("/test", TestPage.class);
mountPage("test2", TestPage2.class);

View File

@ -194,6 +194,47 @@ a > .icon-null, .icon-null { display: inline-block; width: 10px; }
background: #2ba6cb;
}
.btn-gray {
color: #333333;
background-color: #eaeaea;
border-color: #cccccc;
}
.btn-gray:hover,
.btn-gray:focus,
.btn-gray:active,
.btn-gray.active,
.open .dropdown-toggle.btn-gray {
color: #333333;
background-color: #d5d5d5;
border-color: #adadad;
}
.btn-gray:active,
.btn-gray.active,
.open .dropdown-toggle.btn-gray {
background-image: none;
}
.btn-gray.disabled,
.btn-gray[disabled],
fieldset[disabled] .btn-gray,
.btn-gray.disabled:hover,
.btn-gray[disabled]:hover,
fieldset[disabled] .btn-gray:hover,
.btn-gray.disabled:focus,
.btn-gray[disabled]:focus,
fieldset[disabled] .btn-gray:focus,
.btn-gray.disabled:active,
.btn-gray[disabled]:active,
fieldset[disabled] .btn-gray:active,
.btn-gray.disabled.active,
.btn-gray[disabled].active,
fieldset[disabled] .btn-gray.active {
background-color: #eaeaea;
border-color: #cccccc;
}
.btn-radio-group .btn .icon-ok { display: none; }
.btn-radio-group .btn.active { background: #999; color: white; }
.btn-radio-group .btn.active .icon-ok, .btn-radio-group .btn:hover .icon-ok { display: inline-block; }

View File

@ -51,6 +51,8 @@
*/
#main { background: white; }
.padder { padding: 20px; }
.sidebar .head, .content .head { padding: 15px 20px; white-space: nowrap; overflow: hidden; border-bottom: 1px solid #ddd; margin-bottom: 20px; }
.sidebar .head a { color: #666; font-weight:bold; line-height: 24px; }
.sidebar .head a:hover { text-decoration: none; color: #09F; }
@ -106,8 +108,8 @@ th.wicket_orderUp a, th.wicket_orderDown a { padding-left: 20px; }
}
/* LOGIN PAGE */
#register-form, #login-form { padding: 50px 0; width: 500px; margin: 0 auto; }
#login-form h1, #register-form h1 { font-size: 32px; margin: 0 0 30px; font-weight: 300; }
.small-section, #register-form, #login-form { padding: 50px 0; width: 500px; margin: 0 auto; }
.small-section h1, #login-form h1, #register-form h1 { font-size: 32px; margin: 0 0 30px; font-weight: 300; }
#login-form .input-group-addon { font-size: 18px; color: #666; }
#login-form .user-group .input-group-addon { border-bottom-left-radius: 0; }
#login-form .user-group input.form-control { border-bottom-right-radius: 0; }
@ -125,7 +127,7 @@ th.wicket_orderUp a, th.wicket_orderDown a { padding-left: 20px; }
.members .list-group-item:hover a { color: #333; }
.members .item-actions {top: 12px;}
.members .item-actions .img-link { color: #ddd; font-size: 20px; }
.members .list-group-item:hover .img-link, .members .item-actions .img-link:hover { color: #990000; }
.members .list-group-item:hover .remove-link, .members .item-actions .remove-link:hover { color: #990000; }
.members .avatar { width: 32px; height: 32px; margin-right: 8px; }
.permission-link { color: #666; }
@ -134,5 +136,63 @@ a.permission-link:hover { color: #333; text-decoration: none; }
.user-choice-row .img-thumbnail { float: left; }
.user-choice-row p { margin-left: 44px; margin-bottom: 0; }
.user-choice-row p.text-muted { font-size: 0.85em; }
.select2-container.user-choice .select2-choice { height: 34px; line-height: 34px;}
.btn-group-permissions > a { width: 80px; }
.link-item a.confirm-link:hover { color: #990000; }
/* ACCOUNT HOME */
.vcard-column { padding: 20px; }
.vcard-column .avatar { width: 200px; height: 200px; margin-bottom: 18px; }
.vcard-column .avatar > img { border-radius: 8px; }
.vcard-name { margin: 0 0 18px; }
.vcard-name b, .vcard-name em { display: block; white-space: nowrap; overflow: hidden; }
.vcard-name b { font-size: 26px; }
.vcard-name em { font-size: 24px; color: #888; font-style: normal; font-weight: 300; }
.vcard-stats { padding: 20px 0; border-top: 1px solid #ddd; overflow: hidden; }
.vcard-stats a { display: inline-block; color: #999; font-size: 12px; line-height: 1.123; width: 66px; overflow: hidden; text-align: center; }
.vcard-stats a > b { display: block; color: #333; font-size: 28px; }
.vcard-stats a:hover { text-decoration: none; color: #09f; }
.vcard-stats a:hover > b { color: #09f; }
.nav-piped > li { float: left; }
.page-nav { border-bottom: 1px solid #ddd; padding: 10px 20px 0; }
.page-nav ul { margin: 0; padding: 0; list-style: none; }
.page-nav .nav-piped > li > a { color: #999; padding: 12px 15px;}
.page-nav .nav-piped > li > a:hover { background: transparent; color: #09f; }
.page-nav .nav-piped > li.active > a { color: #333; font-weight: bold; }
.page-nav .nav-piped > li.active > a:after{
position: absolute;
bottom: -1px;
right: 50%;
margin-right: -6px;
display: inline-block;
width: 0;
height: 0;
border-bottom: 6px solid #333;
border-right: 6px solid transparent;
border-top: 0 dotted;
border-left: 6px solid transparent;
content: "";
}
ul.btn-list { list-style: none; margin: 0; }
.btn-list > li { float: left; list-style: none; padding: 0; margin: 0 6px;}
.page-nav .btn-list > li { padding: 7px 0; }
.page-nav .btn-list .btn { font-weight: bold; }
.project-list .list-group-item { position: relative; padding-left: 44px; }
.project-list .list-group-item i { position: absolute; left: 0; top: 10px; font-size: 32px; width: 32px; color: #bbb; }
.project-list h4 { margin: 0 0 9px; }
.project-list p { margin: 0 0 6px; font-size: 12px;}
.project-list .last-updated { color: #999; }
ul.members-list { list-style: none; margin: 0; padding: 0; }
.members-list > li.member { width: 220px; display: inline-block; list-style: none; padding-left: 60px; margin-right: 18px; margin-bottom: 18px; position: relative; font-size: 18px; white-space: nowrap; overflow: hidden;}
.member > .avatar { width: 48px; height: 48px; position: absolute; left: 0; top: 0; }
.member > .avatar > img { border-radius: 50%; }
.member > h4 { margin-top: 0; }
.member > em { font-style: normal; display: block; color: #999; }

View File

@ -0,0 +1,5 @@
<html xmlns:wicket>
<wicket:panel>
<a wicket:id="link"><span wicket:id="text"></span></a>
</wicket:panel>
</html>

View File

@ -0,0 +1,25 @@
package com.pmease.gitop.web.common.component.link;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.AbstractLink;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
@SuppressWarnings("serial")
public abstract class LinkPanel extends Panel {
public LinkPanel(String id, IModel<String> label) {
super(id, label);
}
@Override
protected void onInitialize() {
super.onInitialize();
AbstractLink link = createLink("link");
add(link);
link.add(new Label("text", getDefaultModel()));
}
protected abstract AbstractLink createLink(String id);
}

View File

@ -0,0 +1,31 @@
package com.pmease.gitop.web.common.datatype;
import java.util.Locale;
public abstract class AbstractDataType implements DataType {
@Override
public String asString(Object from, String pattern) {
return asString(from, pattern, Locale.getDefault());
}
@Override
public String asString(Object from) {
return asString(from, null);
}
@Override
public Object fromString(String value, String pattern) {
return fromString(value, pattern, Locale.getDefault());
}
@Override
public Object fromString(String value) {
return fromString(value, null);
}
@Override
public boolean isNumericType() {
return false;
}
}

View File

@ -0,0 +1,38 @@
package com.pmease.gitop.web.common.datatype;
import java.util.Locale;
import org.apache.commons.validator.routines.BigDecimalValidator;
public abstract class AbstractNumericType extends AbstractDataType {
@Override
public boolean isNumericType() {
return true;
}
protected Number toNumber(Object from) {
if (from == null) {
return 0;
}
if (from instanceof Number) {
return (Number) from;
}
if (from instanceof String) {
String s = (String) from;
if ("NaN".equalsIgnoreCase(s)) {
return Double.NaN;
}
return BigDecimalValidator.getInstance().validate(s, null, Locale.getDefault());
}
if (from instanceof Boolean) {
return ((Boolean) from) ? 1 : 0;
}
throw new TypeCastException(this, from);
}
}

View File

@ -0,0 +1,80 @@
package com.pmease.gitop.web.common.datatype;
import java.util.Locale;
import com.google.common.base.Objects;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
public class BooleanType extends AbstractDataType {
@Override
public String asString(Object from, String pattern, Locale locale) {
Boolean b = (Boolean) typeCast(from);
if (Strings.isNullOrEmpty(pattern)) {
return b.toString();
}
String[] tokens = Iterables.toArray(Splitter.on(":").limit(2).omitEmptyStrings().split(pattern), String.class);
return b ? tokens[0] : tokens[1];
}
@Override
public Object fromString(String value, String pattern, Locale locale) {
if (Strings.isNullOrEmpty(value)) {
return null;
}
if (Strings.isNullOrEmpty(pattern)) {
if ("TRUE".equalsIgnoreCase(value)) {
return Boolean.TRUE;
} else {
return Boolean.FALSE;
}
}
String[] tokens = Iterables.toArray(Splitter.on(":").limit(2).omitEmptyStrings().split(pattern), String.class);
if (Objects.equal(value, tokens[0])) {
return Boolean.TRUE;
} else {
return Boolean.FALSE;
}
}
@Override
public Class<?> getReturnClass() {
return Boolean.class;
}
@Override
public Object typeCast(Object from) {
if (from == null){
return Boolean.FALSE;
}
if (from instanceof Boolean) {
return from;
}
if (from instanceof Number) {
return ((Number) from).intValue() > 0;
}
if (from instanceof String) {
if ("TRUE".equalsIgnoreCase((String) from)) {
return Boolean.TRUE;
} else {
return Boolean.FALSE;
}
}
throw new TypeCastException(this, from);
}
@Override
public String getTypeName() {
return "BOOLEAN";
}
}

View File

@ -0,0 +1,36 @@
package com.pmease.gitop.web.common.datatype;
import java.util.Locale;
import org.apache.commons.validator.routines.ByteValidator;
public class ByteType extends AbstractNumericType {
@Override
public String asString(Object from, String pattern, Locale locale) {
Byte b = (Byte) typeCast(from);
return ByteValidator.getInstance().format(b, pattern, locale);
}
@Override
public Object fromString(String value, String pattern, Locale locale) {
return ByteValidator.getInstance().validate(value, pattern, locale);
}
@Override
public Object typeCast(Object from) {
Number number = toNumber(from);
return number.byteValue();
}
@Override
public Class<?> getReturnClass() {
return Byte.class;
}
@Override
public String getTypeName() {
return "BYTE";
}
}

View File

@ -0,0 +1,26 @@
package com.pmease.gitop.web.common.datatype;
import java.util.Locale;
public interface DataType {
String asString(Object from, String pattern, Locale locale);
String asString(Object from, String pattern);
String asString(Object from);
Object fromString(String value, String pattern, Locale locale);
Object fromString(String value, String pattern);
Object fromString(String value);
Class<?> getReturnClass();
boolean isNumericType();
Object typeCast(Object from);
String getTypeName();
}

View File

@ -0,0 +1,33 @@
package com.pmease.gitop.web.common.datatype;
public class DataTypes {
private static enum NULLE {
}
public static final DataType BOOLEAN = new BooleanType();
public static final DataType BYTE = new ByteType();
public static final DataType SHORT = new ShortType();
public static final DataType INTEGER = new IntegerType();
public static final DataType LONG = new LongType();
public static final DataType FLOAT = new FloatType();
public static final DataType DOUBLE = new DoubleType();
public static final DataType PERCENT = new PercentType();
public static final DataType DATE = new DateType();
public static final DataType ENUM = new EnumType(NULLE.class);
public static final DataType STRING = new StringType();
static final DataType[] PRIMITIVES = new DataType[] {
BOOLEAN, BYTE, SHORT, INTEGER, LONG, FLOAT, DOUBLE, PERCENT, ENUM, STRING
};
public static DataType getType(Class<?> clazz) {
for (DataType each : PRIMITIVES) {
if (each.getReturnClass().isAssignableFrom(clazz)) {
return each;
}
}
throw new UnsupportedOperationException("Unable to find the data type for class [" + clazz + "]");
}
}

View File

@ -0,0 +1,82 @@
package com.pmease.gitop.web.common.datatype;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import org.apache.commons.validator.routines.DateValidator;
import org.joda.time.DateTime;
import org.joda.time.ReadableDateTime;
import org.joda.time.format.ISODateTimeFormat;
import com.google.common.base.Strings;
public class DateType extends AbstractDataType {
@Override
public String asString(Object from, String pattern, Locale locale) {
Date date = (Date) typeCast(from);
if (date == null) {
return null;
}
if (Strings.isNullOrEmpty(pattern)) {
return ISODateTimeFormat.dateTime().withZoneUTC().print(new DateTime(date));
}
return DateValidator.getInstance().format(date, pattern, locale);
}
@Override
public Object fromString(String value, String pattern, Locale locale) {
if (Strings.isNullOrEmpty(pattern)) {
return ISODateTimeFormat.dateTime().withZoneUTC().parseDateTime(value).toDate();
}
return DateValidator.getInstance().validate(value, pattern, locale);
}
@Override
public Class<?> getReturnClass() {
return Date.class;
}
@Override
public Object typeCast(Object from) {
if (from == null || (from instanceof Date)) {
return from;
}
if (from instanceof Long) {
return new Date((Long) from);
}
if (from instanceof Calendar) {
return new Date(((Calendar) from).getTimeInMillis());
}
if (from instanceof java.sql.Timestamp) {
return new Date(((java.sql.Timestamp) from).getTime());
}
if (from instanceof ReadableDateTime) {
return ((ReadableDateTime) from).toDateTime().toDate();
}
if (from instanceof String) {
DateTime dt = ISODateTimeFormat.dateTime().parseDateTime((String) from);
if (dt != null) {
return dt.toDate();
}
}
throw new TypeCastException(this, from);
}
@Override
public String getTypeName() {
return "DATE";
}
}

View File

@ -0,0 +1,35 @@
package com.pmease.gitop.web.common.datatype;
import java.util.Locale;
import org.apache.commons.validator.routines.DoubleValidator;
public class DoubleType extends AbstractNumericType {
@Override
public String asString(Object from, String pattern, Locale locale) {
Double d = (Double) typeCast(from);
return DoubleValidator.getInstance().format(d, pattern, locale);
}
@Override
public Object fromString(String value, String pattern, Locale locale) {
return DoubleValidator.getInstance().validate(value, pattern, locale);
}
@Override
public Class<?> getReturnClass() {
return Double.class;
}
@Override
public Object typeCast(Object from) {
return toNumber(from).doubleValue();
}
@Override
public String getTypeName() {
return "DOUBLE";
}
}

View File

@ -0,0 +1,158 @@
package com.pmease.gitop.web.common.datatype;
import org.joda.time.format.ISOPeriodFormat;
import org.joda.time.format.PeriodFormatter;
import org.joda.time.format.PeriodFormatterBuilder;
public class DurationFormatUtils {
// public static final String ISO8601 = "yyyy-MM-dd'T'HH:mm:ssZ";
public static PeriodFormatter createPeriodFormatter(String pattern) {
PeriodFormatterBuilder builder = new PeriodFormatterBuilder();
parsePatternTo(builder, pattern);
return builder.toFormatter();
}
private static void parsePatternTo(PeriodFormatterBuilder builder, String pattern) {
int length = pattern.length();
int[] indexRef = new int[1];
builder.printZeroAlways();
for (int i = 0; i < length; i++) {
indexRef[0] = i;
String token = parseToken(pattern, indexRef);
i = indexRef[0];
int tokenLen = token.length();
if (tokenLen == 0) {
break;
}
char c = token.charAt(0);
switch (c) {
case 'y': // year (number)
builder.appendYears();
break;
case 'd': // days
builder.minimumPrintedDigits(tokenLen);
builder.appendDays();
break;
case 'H': // hours
builder.minimumPrintedDigits(tokenLen);
builder.appendHours();
break;
case 'm': // minutes
builder.minimumPrintedDigits(tokenLen);
builder.appendMinutes();
break;
case 's': // seconds
builder.minimumPrintedDigits(tokenLen);
builder.appendSeconds();
break;
case 'S': // milliseconds
builder.minimumPrintedDigits(tokenLen);
builder.appendMillis();
break;
case '\'': // literal text
String sub = token.substring(1);
builder.appendLiteral(new String(sub));
break;
default:
throw new IllegalArgumentException("Illegal pattern component: " + token);
}
}
}
/**
* Parses an individual token.
*
* @param pattern the pattern string
* @param indexRef a single element array, where the input is the start location and the output is
* the location after parsing the token
* @return the parsed token
*/
private static String parseToken(String pattern, int[] indexRef) {
StringBuffer buf = new StringBuffer();
int i = indexRef[0];
int length = pattern.length();
char c = pattern.charAt(i);
if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') {
// Scan a run of the same character, which indicates a time
// pattern.
buf.append(c);
while (i + 1 < length) {
char peek = pattern.charAt(i + 1);
if (peek == c) {
buf.append(c);
i++;
} else {
break;
}
}
} else {
// This will identify token as text.
buf.append('\'');
boolean inLiteral = false;
for (; i < length; i++) {
c = pattern.charAt(i);
if (c == '\'') {
if (i + 1 < length && pattern.charAt(i + 1) == '\'') {
// '' is treated as escaped '
i++;
buf.append(c);
} else {
inLiteral = !inLiteral;
}
} else if (!inLiteral && (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z')) {
i--;
break;
} else {
buf.append(c);
}
}
}
indexRef[0] = i;
return buf.toString();
}
public static PeriodFormatter isoFormatter() {
return ISOPeriodFormat.standard();
}
static PeriodFormatter wordFormatter = new PeriodFormatterBuilder().appendYears()
.appendSuffix(" year", " years").appendSeparator(" ").appendMonths()
.appendSuffix(" month", " months").appendSeparator(" ").appendDays()
.appendSuffix(" day", " days").appendSeparator(" ").appendHours()
.appendSuffix(" hour", " hours").appendSeparator(" ").appendMinutes()
.appendSuffix(" minute", " minutes").appendSeparator(" ").appendSeconds()
.appendSuffix(" second", " seconds").appendSeparator(" ").appendMillis3Digit().toFormatter();
public static PeriodFormatter wordFormatter() {
return wordFormatter;
}
static PeriodFormatter shortWordFormatter = new PeriodFormatterBuilder().appendDays()
.appendSuffix("d").appendSeparator(", ").appendHours().appendSuffix("h").appendSeparator(":")
.appendMinutes().appendSuffix("m").appendSeparator(":").appendSeconds().appendSuffix("s")
.appendSeparator(", ").appendMillis().toFormatter();
public static PeriodFormatter shortWordFormatter() {
return shortWordFormatter;
}
static PeriodFormatter timeFormatter = new PeriodFormatterBuilder().printZeroAlways()
.appendHours().appendSuffix(":").appendMinutes().appendSuffix(":").appendSeconds()
.toFormatter();
public static PeriodFormatter getTimeFormatter() {
return timeFormatter;
}
}

View File

@ -0,0 +1,91 @@
package com.pmease.gitop.web.common.datatype;
import java.util.Locale;
import org.apache.commons.validator.routines.BigDecimalValidator;
import org.joda.time.DateTimeConstants;
import org.joda.time.Duration;
import org.joda.time.Period;
import org.joda.time.PeriodType;
import org.joda.time.chrono.ISOChronology;
import org.joda.time.format.PeriodFormatter;
import com.google.common.base.Strings;
public class DurationType extends LongType {
public static final String FMT_WORD = "WORD";
public static final String FMT_ISO = "ISO";
public static final String FMT_SHORT_WORD = "SHORT";
@Override
public Object fromString(String str, String pattern, Locale locale) {
if (Strings.isNullOrEmpty(str)) {
return null;
}
if ("NaN".equalsIgnoreCase(str)) {
return new Long(0);
}
if (Strings.isNullOrEmpty(pattern)) {
try {
return Long.valueOf(str);
} catch (NumberFormatException e) {
Number number = BigDecimalValidator.getInstance().validate(str, locale);
if (number != null) {
return number.longValue();
} else {
return 0;
}
}
}
PeriodFormatter formatter = getFormatter(pattern, locale);
Period period = formatter.parsePeriod(str);
return Long.valueOf(period.toStandardDuration().getMillis());
}
@Override
public String asString(Object value, String pattern, Locale locale) {
Long mills = (Long) typeCast(value);
if (mills == null) {
return null;
}
if (Strings.isNullOrEmpty(pattern)) {
return mills.toString();
}
if (mills == 0) {
return "0 mil";
} else if (mills < DateTimeConstants.MILLIS_PER_SECOND) {
return mills + " mils";
}
PeriodFormatter fmt = getFormatter(pattern, locale);
Duration duration = new Duration(mills);
return fmt.print(duration.toPeriod(PeriodType.yearMonthDayTime(),
ISOChronology.getInstanceUTC()));
}
protected PeriodFormatter getFormatter(String pattern, Locale locale) {
PeriodFormatter fmt = null;
if (FMT_WORD.equalsIgnoreCase(pattern)) {
fmt = DurationFormatUtils.wordFormatter();
} else if (FMT_ISO.equalsIgnoreCase(pattern)) {
fmt = DurationFormatUtils.isoFormatter();
} else if (FMT_SHORT_WORD.equalsIgnoreCase(pattern)) {
fmt = DurationFormatUtils.shortWordFormatter();
} else {
fmt = DurationFormatUtils.createPeriodFormatter(pattern);
}
if (locale != null) {
fmt = fmt.withLocale(locale);
}
return fmt;
}
}

View File

@ -0,0 +1,62 @@
package com.pmease.gitop.web.common.datatype;
import java.util.Locale;
@SuppressWarnings("rawtypes")
public class EnumType extends AbstractDataType {
private final Class<? extends Enum> enumClass;
public EnumType(Class<? extends Enum> enumClass) {
this.enumClass = enumClass;
}
public static EnumType of(Class<? extends Enum> clazz) {
return new EnumType(clazz);
}
@Override
public String asString(Object from, String pattern, Locale locale) {
Enum<?> e = (Enum<?>) typeCast(from);
if (e == null) {
return null;
} else {
return e.toString();
}
}
@SuppressWarnings("unchecked")
@Override
public Object fromString(String value, String pattern, Locale locale) {
return Enum.valueOf(enumClass, value);
}
@Override
public Class<?> getReturnClass() {
return Enum.class;
}
@Override
public Object typeCast(Object from) {
if (from == null || from instanceof Enum) {
return from;
}
if (from instanceof String) {
return fromString((String) from);
}
if (from instanceof Integer) {
int i = (Integer) from;
return enumClass.getEnumConstants()[i];
}
throw new TypeCastException(this, from);
}
@Override
public String getTypeName() {
return "ENUM";
}
}

View File

@ -0,0 +1,35 @@
package com.pmease.gitop.web.common.datatype;
import java.util.Locale;
import org.apache.commons.validator.routines.FloatValidator;
public class FloatType extends AbstractNumericType {
@Override
public String asString(Object from, String pattern, Locale locale) {
Float f = (Float) typeCast(from);
return FloatValidator.getInstance().format(f, pattern, locale);
}
@Override
public Object fromString(String value, String pattern, Locale locale) {
return FloatValidator.getInstance().validate(value, pattern, locale);
}
@Override
public Class<?> getReturnClass() {
return Float.class;
}
@Override
public Object typeCast(Object from) {
return toNumber(from).floatValue();
}
@Override
public String getTypeName() {
return "FLOAT";
}
}

View File

@ -0,0 +1,35 @@
package com.pmease.gitop.web.common.datatype;
import java.util.Locale;
import org.apache.commons.validator.routines.IntegerValidator;
public class IntegerType extends AbstractNumericType {
@Override
public String asString(Object from, String pattern, Locale locale) {
Integer i = (Integer) typeCast(from);
return IntegerValidator.getInstance().format(i, pattern, locale);
}
@Override
public Object fromString(String value, String pattern, Locale locale) {
return IntegerValidator.getInstance().validate(value, pattern, locale);
}
@Override
public Class<?> getReturnClass() {
return Integer.class;
}
@Override
public Object typeCast(Object from) {
return toNumber(from).intValue();
}
@Override
public String getTypeName() {
return "INTEGER";
}
}

View File

@ -0,0 +1,35 @@
package com.pmease.gitop.web.common.datatype;
import java.util.Locale;
import org.apache.commons.validator.routines.LongValidator;
public class LongType extends AbstractNumericType {
@Override
public String asString(Object from, String pattern, Locale locale) {
Long l = (Long) typeCast(from);
return LongValidator.getInstance().format(l, pattern, locale);
}
@Override
public Object fromString(String value, String pattern, Locale locale) {
return LongValidator.getInstance().validate(value, pattern, locale);
}
@Override
public Class<?> getReturnClass() {
return Long.class;
}
@Override
public Object typeCast(Object from) {
return toNumber(from).longValue();
}
@Override
public String getTypeName() {
return "LONG";
}
}

View File

@ -0,0 +1,35 @@
package com.pmease.gitop.web.common.datatype;
import java.util.Locale;
import org.apache.commons.validator.routines.PercentValidator;
public class PercentType extends AbstractNumericType {
@Override
public String asString(Object from, String pattern, Locale locale) {
Double d = (Double) typeCast(from);
return PercentValidator.getInstance().format(d, pattern, locale);
}
@Override
public Object fromString(String value, String pattern, Locale locale) {
return PercentValidator.getInstance().validate(value, pattern, locale).doubleValue();
}
@Override
public Class<?> getReturnClass() {
return Double.class;
}
@Override
public Object typeCast(Object from) {
return toNumber(from).doubleValue();
}
@Override
public String getTypeName() {
return "PERCENT";
}
}

View File

@ -0,0 +1,41 @@
package com.pmease.gitop.web.common.datatype;
import java.util.Locale;
import org.apache.commons.validator.routines.ShortValidator;
import com.google.common.base.Strings;
public class ShortType extends AbstractNumericType {
@Override
public String asString(Object from, String pattern, Locale locale) {
Short s = (Short) typeCast(from);
return ShortValidator.getInstance().format(s, pattern, locale);
}
@Override
public Object fromString(String value, String pattern, Locale locale) {
if (Strings.isNullOrEmpty(value)) {
return 0;
}
return ShortValidator.getInstance().validate(value, pattern, locale);
}
@Override
public Class<?> getReturnClass() {
return Short.class;
}
@Override
public Object typeCast(Object from) {
return toNumber(from).shortValue();
}
@Override
public String getTypeName() {
return "SHORT";
}
}

View File

@ -0,0 +1,40 @@
package com.pmease.gitop.web.common.datatype;
import java.util.Locale;
public class StringType extends AbstractDataType {
@Override
public String asString(Object from, String pattern, Locale locale) {
if (from == null || (from instanceof String)) {
return (String) from;
}
return from.toString();
}
@Override
public Object fromString(String value, String pattern, Locale locale) {
return value;
}
@Override
public Class<?> getReturnClass() {
return String.class;
}
@Override
public Object typeCast(Object from) {
if (from == null || (from instanceof String)) {
return from;
}
return from.toString();
}
@Override
public String getTypeName() {
return "STRING";
}
}

View File

@ -0,0 +1,14 @@
package com.pmease.gitop.web.common.datatype;
public class TypeCastException extends RuntimeException {
private static final long serialVersionUID = 1L;
public TypeCastException(String msg) {
super(msg);
}
public TypeCastException(DataType dataType, Object value) {
this("Cannot cast object [" + value + "] to data type [" + dataType + "]");
}
}

View File

@ -0,0 +1,44 @@
package com.pmease.gitop.web.component.link;
import org.apache.wicket.markup.html.link.AbstractLink;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import com.pmease.gitop.core.model.Project;
import com.pmease.gitop.web.common.component.link.LinkPanel;
import com.pmease.gitop.web.page.PageSpec;
public class ProjectHomeLink extends LinkPanel {
private static final long serialVersionUID = 1L;
private final IModel<Project> projectModel;
@SuppressWarnings("serial")
public ProjectHomeLink(String id, final IModel<Project> projectModel) {
super(id, new AbstractReadOnlyModel<String>() {
@Override
public String getObject() {
Project project = projectModel.getObject();
return project.getOwner().getName() + " / " + project.getName();
}
});
this.projectModel = projectModel;
}
@Override
protected AbstractLink createLink(String id) {
return PageSpec.newProjectHomeLink(id, projectModel.getObject());
}
@Override
public void onDetach() {
if (projectModel != null) {
projectModel.detach();
}
super.onDetach();
}
}

View File

@ -11,7 +11,7 @@ import com.google.common.base.Preconditions;
import com.pmease.gitop.core.model.User;
import com.pmease.gitop.web.component.avatar.AvatarImage;
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")

View File

@ -19,7 +19,7 @@
</wicket:enclosure>
<wicket:enclosure child="logoutLink">
<li><a href="#" data-toggle="tooltip" title="Create a new repository"><i class="icon-plus icon-fixed-width"></i></a></li>
<li><a href="#" data-toggle="tooltip" title="Create a new repository" wicket:id="newlink"><i class="icon-plus icon-fixed-width"></i></a></li>
<li><a wicket:id="profileLink" data-toggle="tooltip" title="Account settings"><i class="icon-cog"></i></a></li>
<li><a href="#" data-toggle="tooltip" title="Sign out" wicket:id="logoutLink"><i class="icon-signout"></i></a></li>
<li class="separator"></li>

View File

@ -10,6 +10,7 @@ import com.pmease.gitop.web.component.link.UserAvatarLink;
import com.pmease.gitop.web.model.UserModel;
import com.pmease.gitop.web.page.account.RegisterPage;
import com.pmease.gitop.web.page.account.setting.profile.AccountProfilePage;
import com.pmease.gitop.web.page.project.CreateProjectPage;
import com.pmease.gitop.web.shiro.LoginPage;
import com.pmease.gitop.web.shiro.LogoutPage;
@ -28,6 +29,7 @@ public class GlobalHeaderPanel extends Panel {
if (isSignedIn()) {
add(new UserAvatarLink("userlink", new UserModel(currentUser().get())));
add(new BookmarkablePageLink<Void>("profileLink", AccountProfilePage.class));
add(new BookmarkablePageLink<Void>("newlink", CreateProjectPage.class));
} else {
add(new WebMarkupContainer("userlink").setVisibilityAllowed(false));
}

View File

@ -7,7 +7,7 @@ import org.apache.wicket.request.mapper.parameter.PageParameters;
import com.pmease.gitop.core.model.Project;
import com.pmease.gitop.core.model.User;
import com.pmease.gitop.web.component.avatar.AvatarImage.AvatarImageType;
import com.pmease.gitop.web.page.account.AccountHomePage;
import com.pmease.gitop.web.page.account.home.AccountHomePage;
import com.pmease.gitop.web.page.project.ProjectHomePage;
import com.pmease.gitop.web.util.WicketUtils;

View File

@ -0,0 +1,46 @@
package com.pmease.gitop.web.page.account;
import javax.persistence.EntityNotFoundException;
import org.apache.wicket.model.IModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.pmease.gitop.core.Gitop;
import com.pmease.gitop.core.manager.UserManager;
import com.pmease.gitop.core.model.User;
import com.pmease.gitop.web.model.UserModel;
import com.pmease.gitop.web.page.AbstractLayoutPage;
import com.pmease.gitop.web.page.PageSpec;
@SuppressWarnings("serial")
public abstract class AbstractAccountPage extends AbstractLayoutPage {
protected final IModel<User> accountModel;
public AbstractAccountPage(PageParameters params) {
String name = params.get(PageSpec.USER).toString();
Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
User user = Gitop.getInstance(UserManager.class).find(name);
if (user == null) {
throw new EntityNotFoundException("User " + name + " not found");
}
this.accountModel = new UserModel(user);
}
@Override
public void onDetach() {
if (accountModel != null) {
accountModel.detach();
}
super.onDetach();
}
protected User getAccount() {
return accountModel.getObject();
}
}

View File

@ -1,7 +0,0 @@
<html xmlns:wicket>
<wicket:extend>
<h1>Welcome, Account home</h1>
<div wicket:id="accountName"></div>
<a wicket:id="link">link</a>
</wicket:extend>
</html>

View File

@ -1,78 +0,0 @@
package com.pmease.gitop.web.page.account;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import com.pmease.commons.util.GeneralException;
import com.pmease.gitop.core.Gitop;
import com.pmease.gitop.core.manager.UserManager;
import com.pmease.gitop.core.model.User;
import com.pmease.gitop.web.page.AbstractLayoutPage;
@SuppressWarnings("serial")
public class AccountHomePage extends AbstractLayoutPage {
private final IModel<User> accountModel;
public AccountHomePage(PageParameters params) {
String accountName = params.get("user").toString();
User account = Gitop.getInstance(UserManager.class).find(accountName);
if (account == null)
throw new GeneralException("Account %s does not exist!", accountName);
final Long accountId = account.getId();
accountModel = new LoadableDetachableModel<User>() {
@Override
protected User load() {
return Gitop.getInstance(UserManager.class).load(accountId);
}
};
}
@Override
protected void onPageInitialize() {
super.onPageInitialize();
add(new Label("accountName", getAccount().getName()));
add(new Link<Void>("link") {
@Override
public void onClick() {
}
});
}
@Override
protected String getPageTitle() {
return "Gitop";
}
public User getAccount() {
return accountModel.getObject();
}
@Override
public void detachModels() {
if (accountModel != null)
accountModel.detach();
super.detachModels();
}
public static PageParameters paramsOf(User account) {
PageParameters params = new PageParameters();
params.set("user", account.getName());
return params;
}
}

View File

@ -0,0 +1,39 @@
<html xmlns:wicket>
<wicket:extend>
<div class="faux">
<div class="faux-row">
<div class="faux-cell sidebar">
<div class="sidebar-inner">
<div class="vcard-column">
<div wicket:id="avatar" class="avatar"></div>
<h1 class="vcard-name">
<b wicket:id="fullname"></b>
<em wicket:id="username"></em>
</h1>
<div class="vcard-stats">
<a href="#"><b>12</b> repos</a>
<a href="#"><b>188</b> members</a>
</div>
</div>
</div>
</div>
<div class="faux-cell content">
<div class="content-inner">
<div class="page-nav clearfix">
<ul class="nav nav-piped pull-left">
<li wicket:id="category"><a wicket:id="link"><wicket:container wicket:id="name"></wicket:container></a></li>
</ul>
<ul class="btn-list pull-right">
<li><a class="btn btn-sm btn-gray"><i class="icon-pencil"></i> Edit your profile</a></li>
</ul>
</div>
<div class="padder">
<wicket:container wicket:id="content"></wicket:container>
</div>
</div>
</div>
</div>
</div>
</wicket:extend>
</html>

View File

@ -0,0 +1,96 @@
package com.pmease.gitop.web.page.account.home;
import org.apache.shiro.SecurityUtils;
import org.apache.wicket.Component;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.AbstractLink;
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.PropertyModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.pmease.gitop.core.permission.ObjectPermission;
import com.pmease.gitop.web.component.avatar.AvatarImage;
import com.pmease.gitop.web.page.PageSpec;
import com.pmease.gitop.web.page.account.AbstractAccountPage;
@SuppressWarnings("serial")
public class AccountHomePage extends AbstractAccountPage {
public static enum Category {
PROJECTS("Projects"),
MEMBERS("Members");
final String displayName;
Category(String displayName) {
this.displayName = displayName;
}
}
private Category category = Category.PROJECTS;
public AccountHomePage(PageParameters params) {
super(params);
String tab = params.get("tab").toString();
if (!Strings.isNullOrEmpty(tab)) {
category = Category.valueOf(tab.toUpperCase());
}
}
@Override
protected void onPageInitialize() {
super.onPageInitialize();
add(new AvatarImage("avatar", accountModel));
add(new Label("fullname", new PropertyModel<String>(accountModel, "displayName")));
add(new Label("username", new PropertyModel<String>(accountModel, "name")));
add(new ListView<Category>("category", Lists.newArrayList(Category.values())) {
@Override
protected void populateItem(ListItem<Category> item) {
Category current = item.getModelObject();
PageParameters params = PageSpec.forUser(accountModel.getObject());
if (current != Category.PROJECTS) {
params.add(PageSpec.TAB, current.name().toLowerCase());
}
AbstractLink link = new BookmarkablePageLink<Void>("link",
AccountHomePage.class, params);
link.add(new Label("name", current.displayName));
item.add(link);
item.add(AttributeAppender.append("class", category == current ? "active" : ""));
}
});
add(createContent("content"));
}
private Component createContent(String id) {
switch (category) {
case PROJECTS:
return new ProjectListPanel(id, accountModel);
case MEMBERS:
return new MemberListPanel(id, accountModel);
default:
throw new IllegalArgumentException("tab " + category);
}
}
@Override
protected boolean isPermitted() {
return SecurityUtils.getSubject().isPermitted(ObjectPermission.ofUserRead(getAccount()));
}
@Override
protected String getPageTitle() {
return getAccount().getName() + " (" + getAccount().getDisplayName() + ")";
}
}

View File

@ -0,0 +1,11 @@
<html xmlns:wicket>
<wicket:panel>
<ul class="members-list">
<li class="member" wicket:id="member">
<span class="avatar" wicket:id="avatar"></span>
<h4><a wicket:id="namelink"><wicket:container wicket:id="name"></wicket:container></a></h4>
<em><wicket:container wicket:id="displayname"></wicket:container></em>
</li>
</ul>
</wicket:panel>
</html>

View File

@ -0,0 +1,79 @@
package com.pmease.gitop.web.page.account.home;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.AbstractLink;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.PropertyModel;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.pmease.gitop.core.model.Membership;
import com.pmease.gitop.core.model.Team;
import com.pmease.gitop.core.model.User;
import com.pmease.gitop.web.component.avatar.AvatarImage;
import com.pmease.gitop.web.model.UserModel;
import com.pmease.gitop.web.page.PageSpec;
@SuppressWarnings("serial")
public class MemberListPanel extends Panel {
public MemberListPanel(String id, IModel<User> model) {
super(id, model);
}
@Override
protected void onInitialize() {
super.onInitialize();
IModel<List<User>> model = new LoadableDetachableModel<List<User>>() {
@Override
protected List<User> load() {
User account = getThisAccount();
Collection<Team> teams = account.getTeams();
Set<User> users = Sets.newHashSet();
for (Team each : teams) {
for (Membership membership : each.getMemberships()) {
users.add(membership.getUser());
}
}
List<User> result = Lists.newArrayList(users);
Collections.sort(result);
return result;
}
};
ListView<User> membersView = new ListView<User>("member", model) {
@Override
protected void populateItem(ListItem<User> item) {
User user = item.getModelObject();
IModel<User> model = new UserModel(user);
item.add(new AvatarImage("avatar", model));
AbstractLink link = PageSpec.newUserHomeLink("namelink", user);
link.add(new Label("name", new PropertyModel<String>(model, "name")));
item.add(link);
item.add(new Label("displayname", new PropertyModel<String>(model, "displayName")));
}
};
add(membersView);
}
private User getThisAccount() {
return (User) getDefaultModelObject();
}
}

View File

@ -0,0 +1,30 @@
<html xmlns:wicket>
<wicket:panel>
<div class="clearfix">
<div class="pull-left">
<form class="form-inline form-search">
<div class="form-group">
<input type="search" class="form-control" placeholder="Find a project ..." />
</div>
<button class="btn btn-gray">Search</button>
</form>
</div>
<div class="pull-right">
<a class="btn btn-info btn-colorful-text"><i class="icon-repo-plus"></i> New</a>
</div>
</div>
<p></p>
<ul class="list-group list-view project-list">
<li class="list-group-item" wicket:id="project">
<i class="icon-repo-book"></i>
<h4><a wicket:id="projectlink"><span wicket:id="name"></span></a></h4>
<wicket:enclosure>
forked from <span wicket:id="forklink"></span>
</wicket:enclosure>
<p wicket:id="description"></p>
<p class="last-updated">Last updated 6 hours ago</p>
</li>
</ul>
</wicket:panel>
</html>

View File

@ -0,0 +1,76 @@
package com.pmease.gitop.web.page.account.home;
import java.util.List;
import org.apache.shiro.SecurityUtils;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Panel;
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.core.model.User;
import com.pmease.gitop.core.permission.ObjectPermission;
import com.pmease.gitop.web.component.link.ProjectHomeLink;
import com.pmease.gitop.web.model.ProjectModel;
import com.pmease.gitop.web.page.PageSpec;
@SuppressWarnings("serial")
public class ProjectListPanel extends Panel {
public ProjectListPanel(String id, IModel<User> model) {
super(id, model);
}
@Override
protected void onInitialize() {
super.onInitialize();
IModel<List<Project>> model = new LoadableDetachableModel<List<Project>>() {
@Override
protected List<Project> load() {
User account = getThisAccount();
List<Project> projects = Lists.newArrayList();
for (Project each : account.getRepositories()) {
if (SecurityUtils.getSubject().isPermitted(ObjectPermission.ofProjectRead(each))) {
projects.add(each);
}
}
return projects;
}
};
ListView<Project> projectsView = new ListView<Project>("project", model) {
@Override
protected void populateItem(ListItem<Project> item) {
Project project = item.getModelObject();
// IModel<Project> 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();
}
}

View File

@ -22,7 +22,7 @@
<hr/>
<h4>Teams</h4>
<p class="text-muted">Manage teams and grant the permissions so that all members in the team can have the permission to access any repositories under this account.</p>
<p class="text-muted">Manage teams and grant the permissions so that all members in the team can have the permissions to access any repositories under this account.</p>
<wicket:container wicket:id="teams"></wicket:container>
</div>
</section>

View File

@ -44,7 +44,7 @@
(<em wicket:id="fullname"></em>)
</a>
<div class="item-actions">
<a class="img-link" wicket:id="remove"><i class="icon-remove-sign"></i></a>
<a class="img-link remove-link" wicket:id="remove"><i class="icon-remove-sign"></i></a>
</div>
</li>
</ul>

View File

@ -0,0 +1,19 @@
<html xmlns:wicket>
<wicket:extend>
<section>
<div class="head">
<h2>Repositories</h2>
</div>
<div class="body">
<ul class="list-group list-view list-group-hoverable repos-list">
<li class="list-group-item" wicket:id="projects">
<div wicket:id="info"></div>
<ul class="piped item-actions">
<li><a class="first" wicket:id="admin"><i class="icon-cog"></i> Settings</a></li>
</ul>
</li>
</ul>
</div>
</section>
</wicket:extend>
</html>

View File

@ -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<List<Project>> model = new LoadableDetachableModel<List<Project>>() {
@Override
protected List<Project> load() {
return Lists.newArrayList(getAccount().getRepositories());
}
};
ListView<Project> view = new ListView<Project>("projects", model) {
@Override
protected void populateItem(ListItem<Project> item) {
Project project = item.getModelObject();
item.add(new SimpleProjectInfo("info", new ProjectModel(project)));
item.add(new BookmarkablePageLink<Void>("admin", ProjectAdminPage.class,
PageSpec.forProject(project)));
}
};
add(view);
}
}

View File

@ -0,0 +1,5 @@
<html xmlns:wicket>
<wicket:panel>
<a>steve/repo</a>
</wicket:panel>
</html>

View File

@ -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<Project> model) {
super(id, model);
}
@Override
protected void onInitialize() {
super.onInitialize();
// add(new UserProjectLink("project", (IModel<Project>) getDefaultModel()));
// add(new Label("age", new AbstractReadOnlyModel<String>() {
//
// @Override
// public String getObject() {
// return DateUtils.formatAge(getProject().getCreatedAt());
// }
//
// }).add(AttributeModifier.replace("title",
// new AbstractReadOnlyModel<String>() {
//
// @Override
// public String getObject() {
// return DataTypes.DATE
// .asString(getProject().getCreatedAt(),
// Constants.DATETIME_FULL_FORMAT);
// }
//
// })));
//
// if (getProject().isForked()) {
// add(new UserProjectLink("forkedFrom",
// new LoadableDetachableModel<Project>() {
//
// @Override
// protected Project load() {
// return getProject().getParentFork().getForkedFrom();
// }
// }));
// } else {
// add(new WebMarkupContainer("forkedFrom")
// .setVisibilityAllowed(false));
// }
}
protected Project getProject() {
return (Project) getDefaultModelObject();
}
}

View File

@ -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<Project> 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()));
}
}

View File

@ -0,0 +1,17 @@
<html xmlns:wicket>
<wicket:extend>
<div class="small-section">
<h1>Create Project</h1>
<form wicket:id="form">
<div wicket:id="feedback" class="form-feedback"></div>
<div wicket:id="name"></div>
<div wicket:id="description"></div>
<hr/>
<div wicket:id="public"></div>
<hr />
<button class="btn btn-info" wicket:id="submit">Create project</button>
</form>
</div>
</wicket:extend>
</html>

View File

@ -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<Project> projectModel = new ProjectModel(new Project());
Form<Project> form = new Form<Project>("form", projectModel);
add(form);
form.add(new FeedbackPanel("feedback"));
form.add(new TextFieldElement<String>("name", "Project Name",
new PropertyModel<String>(projectModel, "name"))
.add(new PropertyValidator<String>())
.add(new IValidator<String>() {
@Override
public void validate(IValidatable<String> 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<String>("description", "Description",
new PropertyModel<String>(projectModel, "description"))
.add(new PropertyValidator<String>())
.setRequired(false));
form.add(new FlatCheckBoxElement("public", "Public Accessible",
new PropertyModel<Boolean>(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));
}
});
}
}

View File

@ -0,0 +1,5 @@
<html xmlns:wicket>
<wicket:extend>
<h1>Project Administration</h1>
</wicket:extend>
</html>

View File

@ -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}";
}
}

View File

@ -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<Project> 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;
}
}

View File

@ -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")

View File

@ -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();
}