diff --git a/commons.hibernate/src/main/java/com/pmease/commons/hibernate/dao/DefaultGeneralDao.java b/commons.hibernate/src/main/java/com/pmease/commons/hibernate/dao/DefaultGeneralDao.java
index bf8326199d..b7bde72023 100644
--- a/commons.hibernate/src/main/java/com/pmease/commons/hibernate/dao/DefaultGeneralDao.java
+++ b/commons.hibernate/src/main/java/com/pmease/commons/hibernate/dao/DefaultGeneralDao.java
@@ -72,7 +72,7 @@ public class DefaultGeneralDao implements GeneralDao, Serializable {
return null;
}
- return (Class) entityClass;
+ return entityClass;
}
@Sessional
@@ -99,7 +99,7 @@ public class DefaultGeneralDao implements GeneralDao, Serializable {
public int count(DetachedCriteria detachedCriteria) {
Criteria criteria = detachedCriteria.getExecutableCriteria(getSession());
criteria.setProjection(Projections.rowCount());
- return (Integer) criteria.uniqueResult();
+ return ((Long) criteria.uniqueResult()).intValue();
}
public Object writeReplace() throws ObjectStreamException {
diff --git a/gitop.web/pom.xml b/gitop.web/pom.xml
index 64d5628d6d..72d8296811 100644
--- a/gitop.web/pom.xml
+++ b/gitop.web/pom.xml
@@ -36,7 +36,13 @@
1.0.29
-
+
+
+ com.sun.jersey
+ jersey-client
+ 1.17.1
+
+
com.vaynberg.wicket.select2
@@ -66,7 +72,7 @@
imgscalr-lib
4.2
-
+
com.pmease
commons.jetty
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/GitopWebApp.java b/gitop.web/src/main/java/com/pmease/gitop/web/GitopWebApp.java
index dac1e9b778..009be12150 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/GitopWebApp.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/GitopWebApp.java
@@ -12,9 +12,11 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.wicket.Application;
import org.apache.wicket.Page;
+import org.apache.wicket.RuntimeConfigurationType;
import org.apache.wicket.Session;
import org.apache.wicket.bean.validation.BeanValidationConfiguration;
import org.apache.wicket.core.request.mapper.MountedMapper;
+import org.apache.wicket.devutils.stateless.StatelessChecker;
import org.apache.wicket.markup.html.IPackageResourceGuard;
import org.apache.wicket.markup.html.SecurePackageResourceGuard;
import org.apache.wicket.request.IRequestMapper;
@@ -39,6 +41,8 @@ import com.pmease.gitop.web.page.account.AccountHomePage;
import com.pmease.gitop.web.page.account.RegisterPage;
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;
+import com.pmease.gitop.web.page.account.setting.permission.EditTeamPage;
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;
@@ -102,15 +106,17 @@ public class GitopWebApp extends AbstractWicketConfig {
loadDefaultUserAvatarData();
- new ShiroWicketPlugin().mountLoginPage("login", LoginPage.class)
- .mountLogoutPage("logout", LogoutPage.class).install(this);
+ new ShiroWicketPlugin()
+ .mountLoginPage("login", LoginPage.class)
+ .mountLogoutPage("logout", LogoutPage.class)
+ .install(this);
mountPages();
configureResources();
-// if (getConfigurationType() == RuntimeConfigurationType.DEVELOPMENT) {
-// getComponentPreOnBeforeRenderListeners().add(new StatelessChecker());
-// }
+ if (getConfigurationType() == RuntimeConfigurationType.DEVELOPMENT) {
+ getComponentPreOnBeforeRenderListeners().add(new StatelessChecker());
+ }
}
public byte[] getDefaultUserAvatar() {
@@ -173,6 +179,8 @@ public class GitopWebApp extends AbstractWicketConfig {
mountPage("settings/password", AccountPasswordPage.class);
mountPage("settings/permission", AccountPermissionPage.class);
mountPage("settings/repos", AccountReposPage.class);
+ mountPage("teams/add", AddTeamPage.class);
+ mountPage("teams/edit/${teamId}", EditTeamPage.class);
mountPage("/test", TestPage.class);
mountPage("test2", TestPage2.class);
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/WebModule.java b/gitop.web/src/main/java/com/pmease/gitop/web/WebModule.java
index 79ed0d4f88..d78cd73599 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/WebModule.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/WebModule.java
@@ -2,10 +2,13 @@ package com.pmease.gitop.web;
import javax.inject.Singleton;
+import com.pmease.commons.jersey.JerseyConfigurator;
+import com.pmease.commons.jersey.JerseyEnvironment;
import com.pmease.commons.jetty.ServletConfigurator;
import com.pmease.commons.loader.AbstractPluginModule;
import com.pmease.commons.wicket.AbstractWicketConfig;
import com.pmease.gitop.core.validation.UserNameReservation;
+import com.pmease.gitop.web.resource.TestResource;
/**
* NOTE: Do not forget to rename moduleClass property defined in the pom if you've renamed this class.
@@ -23,6 +26,15 @@ public class WebModule extends AbstractPluginModule {
contribute(ServletConfigurator.class, WebServletConfigurator.class);
contribute(UserNameReservation.class, WebUserNameReservation.class);
+
+ contribute(JerseyConfigurator.class, new JerseyConfigurator() {
+
+ @Override
+ public void configure(JerseyEnvironment environment) {
+ environment.addComponentFromPackage(TestResource.class);
+ }
+
+ });
}
}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/assets/css/base.css b/gitop.web/src/main/java/com/pmease/gitop/web/assets/css/base.css
index d271017bd8..8079cabad0 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/assets/css/base.css
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/assets/css/base.css
@@ -38,11 +38,32 @@ ul.disc { list-style:disc outside; }
.piped:after { clear: both; }
ul.piped, ol.piped { margin: 0; padding: 0; }
+.action-links { white-space: nowrap; }
+.action-links > .link-item { display: inline-block; margin-left: 12px; }
+.action-links > .link-item:first-child { margin-left: 0;}
+
+/* LINKS */
+.checkable-link { color: #333; cursor: pointer; }
+.checkable-link:hover { text-decoration: none; }
+.checkable-link > .icon-checkbox:before { content: "\f096"; width: 16px; }
+.checkable-link.checked > .icon-checkbox:before { content: "\f046"; width: 16px; }
+.checkable-link.disabled { color: #888; }
+em.checkable-link { color: #888 !important; }
+
/** LIST GROUP */
-.list-navs > .list-group-item { border-width: 1px 0; }
+.list-navs > .list-group-item { border-width: 1px 0; positioin: relative; }
.list-navs > .list-group-item:first-child,
.list-navs > .list-group-item:last-child { border-radius: 0; }
+.list-group.list-group-hoverable .list-group-item:hover { background: #ffe; }
+.list-group-item .item-actions { position: absolute; right: 30px; top: 10px; }
+.list-group-item .item-actions a { color: #999; }
+.list-group-item .item-actions a:hover { color: #333; text-decoration: none; }
+
+.list-view .list-group-item { border-left: none; border-right: none; position: relative; }
+.list-view .list-group-item:first-child,
+.list-view .list-group-item:last-child { border-radius: 0; }
+
/* ADDITIONAL ICONS */
@font-face {
@@ -173,23 +194,32 @@ a > .icon-null, .icon-null { display: inline-block; width: 10px; }
background: #2ba6cb;
}
+.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; }
/**
* TABLE
*/
.table { width: 100%; }
.norecords-tr .norecords-td { background: #F9F7ED; font-size: 1.5em; line-height: 3em; font-weight: bold; text-align: center; }
.table th a { color: #333; text-decoration: underline; }
-.operations-td a { color: #999; }
-.operations-td a:hover { color: #333; }
+.table .operations-td { white-space: nowrap; }
+.operations-td a, .operations-td a.iconlink { color: #999; }
+.operations-td a:hover { color: #333; text-decoration: none; }
+
+.search-nav-td { font-size: 11px; }
+.search-nav-td form { position: relative; }
+.search-nav-td input.form-control, .search-nav-td select.form-control { padding: 2px 4px; height: 24px; }
+.search-nav-td .btn { padding: 2px 8px; height: 24px; }
+
+.form-inline.navigation-form select.form-control { width: 60px; }
+.form-inline.navigation-form input.form-control { width: 40px; }
+.form-inline.navigation-form .btn-group { margin-left: 6px; }
+.search-form button { position: absolute; right: 2px; top: 1px; border: none; height: 22px; background: transparent; color: #888; }
+
-/**
- * FORM COMPONENT
- */
/* FORM COMPONENT */
.select2-container .select2-choice { padding-top: 4px; padding-bottom: 4px; height: auto; }
-.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-highlighted .user-choice-row p.text-muted { color: #ccc; }
.required { color: #a00; margin-left: 3px;}
@@ -247,65 +277,13 @@ a > .icon-null, .icon-null { display: inline-block; width: 10px; }
transition: all 0.2s ease-in-out;
}
+.avatar.img-thumbnail { padding: 1px; }
.avatar > img { display: block; margin: 0 auto; width: 100%; height: 100%; border-radius: 3px; }
.avatar-circle { border-radius: 50%; }
.avatar-circle > img { border-radius: 50%; }
-
+.avatar-big { width: 32px; height: 32px; }
.avatar-xlarge { width: 128px; height: 128px; }
-/**
- * LAYOUT
- */
-#globalheader, #main, #globalfooter {position: relative;}
-#globalheader:before, #globalheader:after, #main:before, #main:after, #globalfooter:before, #globalfooter:after {
- display: table;
- content: " ";
-}
-#globalheader:after, #main:after, #globalfooter:after { clear: both; }
-
-/**
- * GLOBAL HEADER
- */
-#globalheader .navbar { border-width: 0 0 1px 0; border-color: #ddd; border-radius: 0; margin: 0;
- background: #f0f2f4; color: white;
-}
-#globalheader .navbar-brand { padding: 10px 10px 10px 20px; }
-#globalheader .search-form { margin-top: 12px; margin-bottom: 0;}
-.search-form input.text-field {
- height: 30px;
- width: 200px;
- line-height: normal;
- text-indent: 0;
- position: relative;
- padding: 3px 30px 3px 8px;
- font-size: 12px;
- -webkit-transition: width 200ms, background 200ms ease; transition: width 200ms, background 200ms ease;
- background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAEJWlDQ1BJQ0MgUHJvZmlsZQAAOBGFVd9v21QUPolvUqQWPyBYR4eKxa9VU1u5GxqtxgZJk6XpShal6dgqJJQ6N4mpmxjb6baqT3uBNwb8AUDZAw9IPCGNX2J72fYA2qSpoIpqEtIeOvFDiEl7QVX4rp3YDoqYo15/Pec753zn3GubqO+zkmkaUYVope5YhUxSOX1mUenboig9Sf00RP0lzTYT+fwc4RJccQ9dEaIHPxFWottjPfwhak/YX+a2RhR5BN5G2dZWgM8RxQzNtByi+F3YJ886JnDfU8CPWxAIrApc9XBK4CUPn3Y5xcI0OEKrrNVKZeB14NGlkL0awp4GMJAnw+vc0jVFzCJvNSq6wV2HtzzEHWL+H1wxmujZvYaxDtjL8ydwHxG916yZQht/rJXS88DPwX7DdJLC/gLw783lhQTwAaLoYxXr+ILHj768Viu+DrwH9rLuZItt+1p9KXcSGLHRjeXGCZFHcG5o9jRmSc8A36nxrNhj6JGozFNp4FHg4Vpzpp1fmrVX54XdzbNWm84BI49kvVWazQMPAb9rNQqiFjRLG9zIiFrIL10znXxbg7RTN3KiFvplMrfdHpFT2nFqxRkvlh12rGI7li1W9OPZNn/dNNyzCG3sktUsCG3Iz26VrHQGGHnY37y+IOYGHNtbLqXEbMeBj9GpSIk4NWgJq0Z12iWFCpShJO4mWfBUSCcDFg4vh0WPPErLsPXm5V2OFxMwqm70johGld4cr8K9NqfBBpnKDuHvCJtjR9kkmyKFvcJeZcdYCtYpdsRXkA/pVKhK96DUy/M2NVFZ6DhFyYtDzRE/RrlgvalrN9/7C2qCLhuBH3n8jqG5EZ4A2ZhAp7ux8Jyur3+71/com+zyG7cHrq/TyYfNN3Y3thPbxLoV2w7iY7/EtvHbogR2wHAVrUCV7u6E7fPGunq4CqZDJay/gteA1o7Srh2t1C8OBR4xCf5O7kGOLowGVvVn9Q91U/1EvaT+1lUjyNg1JelD6UvpO+kr6WvpB1KkK9JV6XvpmvSF9I2fs/fee1n8vXf7FTbRrdiFTr3wrDkZclLeJz8rp+Sn5eflOZ+lyIPyhDwj74dnn79vRuDv6kWnM5hVZ6q9a4knQKcFKNHpLLgWpiomXKfzyBp+TtpK2TCbYNn/nNpJcZY7KuLpeCqeICV+ID4Vn4jPCtx58uL74ZvCmvbVe0+Oz+jqgIdYmIqfZ8w9deKsiuhV6Dagmzv8HL4dRNMN87ylV2uOclBVX1IS+JRxJVvXxkeVkmEorstWLG5za5WXx0l8B0Uc0f2C+32L7LkZ2JzXiI7+iXfWrcC22CT63CYafDGwjeCd+MRHRJcPa01r1ctHkciPRHbl0EH3/8hAEu+mO63Wfbyv+j4g2n2/1fpno9Xa/RT5t4muGP8CYGVxecXFpEEAAAEiSURBVCgVhZG9SsRAFIVnkhBwSZ7AYGUXlDSCrYVYaCFKirBZCfgOamvrGySdkGwKEQRXO7XQxiKFINjrvsB2QoT4jThhENELh7nnnHvm5kd2XSeKohh4nncghBiBAEzRT9u2Pcmy7B3elx2G4ZzjODcoQynlA5jQu5z7tm2vNU0zjqLoQyccbjmErDKwkyTJhTbKsty1LOvMdV3lH2vdohkxfGkOKzNN03P0a9o9xXWpQMDzvmjBPNGf4AumpgJTEJmi0S/TvxlcWKwtEdbrut42DfUO8E2g/L5knucD3/dvWb9CeML5jLsEtgCSfOQrbsRxPFMpyYD+D0fwIZgHr2D8zRfN0FcA49eqqipg+A6zD/0ZULf8CN3/GzBCV2ybfQLGkGWtzcDkHAAAAABJRU5ErkJggg==') white no-repeat 180px 8px;
-}
-.search-form input.text-field:focus { width: 300px; background: white; }
-#globalheader .tooltip { white-space: nowrap; }
-#globalheader .navbar-right { margin-right: 20px; }
-#globalheader .user-links { margin: 0; }
-.user-links > li { margin: 0; }
-.user-links > li.separator { height: 50px; border-left: 1px solid #d5d5d5; border-right: 1px solid #fefefe; margin: 0 6px;}
-.user-links a { padding: 16px 10px 5px; line-height: 20px; }
-.user-links a > i { font-size: 16px; }
-.user-links a:hover { color: #333; text-decoration: none; }
-.user-links li .caret { border-top-color: #09f; }
-.user-links li a:hover .caret { border-top-color: #333; }
-.user-links > li.avatar-user { white-space: nowrap; }
-.user-links > li.avatar-user .avatar { margin-right: 3px; }
-
-/**
- * GLOBAL FOOTER
- */
-#globalfooter { padding: 20px; border-top: 1px solid #ddd; font-size: 11.9px; }
-
-/**
- * MAIN
- */
-#main { background: white; }
-
/** FAUX ROW */
.faux { display: table; width: 100%; border-collapse: collapse; table-layout: fixed; border-spacing: 0; }
.faux-row { display: table-row; }
@@ -314,24 +292,3 @@ a > .icon-null, .icon-null { display: inline-block; width: 10px; }
.sidebar-inner { position: relative; width: 240px; overflow-x: hidden; }
.faux .content { }
-.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; }
-
-.sidebar .list-navs > .list-group-item,
-.sidebar .list-navs > .list-group-item.active {
- padding: 0; margin: 0; border-top-width: 0; background: transparent;
-}
-.sidebar .list-navs > .list-group-item.active { border-color: #ccc; }
-
-.sidebar .list-navs > .list-group-item:first-child { border-top-width: 1px; }
-.sidebar .list-navs > .list-group-item a { padding: 10px 20px; display: block; color: #888; }
-.sidebar .list-navs > .list-group-item a:hover { color: #333; text-decoration: none; background: #f2f2f2; }
-.sidebar .list-navs > .list-group-item.active a,
-.sidebar .list-navs > .list-group-item.active a:hover { color: #222; background: #e5e5e5; font-weight: bold; }
-
-section .head { border-style: solid; border-width: 1px 0; border-color: #ddd; }
-section:first-child .head { border-top: none; }
-section .head h2, section .head h3 { margin: 0; font-size: 18px; line-height: 24px; color: #888; font-weight: 300; }
-section .padder, section > .body { padding: 0px 20px 20px; margin-bottom: 18px; }
-
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/assets/css/page.css b/gitop.web/src/main/java/com/pmease/gitop/web/assets/css/page.css
index d2f6fb44d8..c2066f853b 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/assets/css/page.css
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/assets/css/page.css
@@ -1,3 +1,100 @@
+/**
+ * LAYOUT
+ */
+#globalheader, #main, #globalfooter {position: relative;}
+#globalheader:before, #globalheader:after, #main:before, #main:after, #globalfooter:before, #globalfooter:after {
+ display: table;
+ content: " ";
+}
+#globalheader:after, #main:after, #globalfooter:after { clear: both; }
+
+/**
+ * GLOBAL HEADER
+ */
+#globalheader .navbar { border-width: 0 0 1px 0; border-color: #ddd; border-radius: 0; margin: 0;
+ background: #f0f2f4; color: white;
+}
+#globalheader .navbar-brand { padding: 10px 10px 10px 20px; }
+#globalheader .search-form { margin-top: 12px; margin-bottom: 0;}
+.search-form input.text-field {
+ height: 30px;
+ width: 200px;
+ line-height: normal;
+ text-indent: 0;
+ position: relative;
+ padding: 3px 30px 3px 8px;
+ font-size: 12px;
+ -webkit-transition: width 200ms, background 200ms ease; transition: width 200ms, background 200ms ease;
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAEJWlDQ1BJQ0MgUHJvZmlsZQAAOBGFVd9v21QUPolvUqQWPyBYR4eKxa9VU1u5GxqtxgZJk6XpShal6dgqJJQ6N4mpmxjb6baqT3uBNwb8AUDZAw9IPCGNX2J72fYA2qSpoIpqEtIeOvFDiEl7QVX4rp3YDoqYo15/Pec753zn3GubqO+zkmkaUYVope5YhUxSOX1mUenboig9Sf00RP0lzTYT+fwc4RJccQ9dEaIHPxFWottjPfwhak/YX+a2RhR5BN5G2dZWgM8RxQzNtByi+F3YJ886JnDfU8CPWxAIrApc9XBK4CUPn3Y5xcI0OEKrrNVKZeB14NGlkL0awp4GMJAnw+vc0jVFzCJvNSq6wV2HtzzEHWL+H1wxmujZvYaxDtjL8ydwHxG916yZQht/rJXS88DPwX7DdJLC/gLw783lhQTwAaLoYxXr+ILHj768Viu+DrwH9rLuZItt+1p9KXcSGLHRjeXGCZFHcG5o9jRmSc8A36nxrNhj6JGozFNp4FHg4Vpzpp1fmrVX54XdzbNWm84BI49kvVWazQMPAb9rNQqiFjRLG9zIiFrIL10znXxbg7RTN3KiFvplMrfdHpFT2nFqxRkvlh12rGI7li1W9OPZNn/dNNyzCG3sktUsCG3Iz26VrHQGGHnY37y+IOYGHNtbLqXEbMeBj9GpSIk4NWgJq0Z12iWFCpShJO4mWfBUSCcDFg4vh0WPPErLsPXm5V2OFxMwqm70johGld4cr8K9NqfBBpnKDuHvCJtjR9kkmyKFvcJeZcdYCtYpdsRXkA/pVKhK96DUy/M2NVFZ6DhFyYtDzRE/RrlgvalrN9/7C2qCLhuBH3n8jqG5EZ4A2ZhAp7ux8Jyur3+71/com+zyG7cHrq/TyYfNN3Y3thPbxLoV2w7iY7/EtvHbogR2wHAVrUCV7u6E7fPGunq4CqZDJay/gteA1o7Srh2t1C8OBR4xCf5O7kGOLowGVvVn9Q91U/1EvaT+1lUjyNg1JelD6UvpO+kr6WvpB1KkK9JV6XvpmvSF9I2fs/fee1n8vXf7FTbRrdiFTr3wrDkZclLeJz8rp+Sn5eflOZ+lyIPyhDwj74dnn79vRuDv6kWnM5hVZ6q9a4knQKcFKNHpLLgWpiomXKfzyBp+TtpK2TCbYNn/nNpJcZY7KuLpeCqeICV+ID4Vn4jPCtx58uL74ZvCmvbVe0+Oz+jqgIdYmIqfZ8w9deKsiuhV6Dagmzv8HL4dRNMN87ylV2uOclBVX1IS+JRxJVvXxkeVkmEorstWLG5za5WXx0l8B0Uc0f2C+32L7LkZ2JzXiI7+iXfWrcC22CT63CYafDGwjeCd+MRHRJcPa01r1ctHkciPRHbl0EH3/8hAEu+mO63Wfbyv+j4g2n2/1fpno9Xa/RT5t4muGP8CYGVxecXFpEEAAAEiSURBVCgVhZG9SsRAFIVnkhBwSZ7AYGUXlDSCrYVYaCFKirBZCfgOamvrGySdkGwKEQRXO7XQxiKFINjrvsB2QoT4jThhENELh7nnnHvm5kd2XSeKohh4nncghBiBAEzRT9u2Pcmy7B3elx2G4ZzjODcoQynlA5jQu5z7tm2vNU0zjqLoQyccbjmErDKwkyTJhTbKsty1LOvMdV3lH2vdohkxfGkOKzNN03P0a9o9xXWpQMDzvmjBPNGf4AumpgJTEJmi0S/TvxlcWKwtEdbrut42DfUO8E2g/L5knucD3/dvWb9CeML5jLsEtgCSfOQrbsRxPFMpyYD+D0fwIZgHr2D8zRfN0FcA49eqqipg+A6zD/0ZULf8CN3/GzBCV2ybfQLGkGWtzcDkHAAAAABJRU5ErkJggg==') white no-repeat 180px 8px;
+}
+.search-form input.text-field:focus { width: 300px; background: white; }
+#globalheader .tooltip { white-space: nowrap; }
+#globalheader .navbar-right { margin-right: 20px; }
+#globalheader .user-links { margin: 0; }
+.user-links > li { margin: 0; }
+.user-links > li.separator { height: 50px; border-left: 1px solid #d5d5d5; border-right: 1px solid #fefefe; margin: 0 6px;}
+.user-links a { padding: 16px 10px 5px; line-height: 20px; }
+.user-links a > i { font-size: 16px; }
+.user-links a:hover { color: #333; text-decoration: none; }
+.user-links li .caret { border-top-color: #09f; }
+.user-links li a:hover .caret { border-top-color: #333; }
+.user-links > li.avatar-user { white-space: nowrap; }
+.user-links > li.avatar-user .avatar { margin-right: 3px; }
+
+/**
+ * GLOBAL FOOTER
+ */
+#globalfooter { padding: 20px; border-top: 1px solid #ddd; font-size: 11.9px; }
+
+/**
+ * MAIN
+ */
+#main { background: white; }
+
+.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; }
+
+.sidebar .list-navs > .list-group-item,
+.sidebar .list-navs > .list-group-item.active {
+ padding: 0; margin: 0; border-top-width: 0; background: transparent;
+}
+.sidebar .list-navs > .list-group-item.active { border-color: #ccc; }
+
+.sidebar .list-navs > .list-group-item:first-child { border-top-width: 1px; }
+.sidebar .list-navs > .list-group-item a { padding: 10px 20px; display: block; color: #888; }
+.sidebar .list-navs > .list-group-item a:hover { color: #333; text-decoration: none; background: #f2f2f2; }
+.sidebar .list-navs > .list-group-item.active a,
+.sidebar .list-navs > .list-group-item.active a:hover { color: #222; background: #e5e5e5; font-weight: bold; }
+
+section .head { border-style: solid; border-width: 1px 0; border-color: #ddd; }
+section:first-child .head { border-top: none; }
+section .head h2, section .head h3 { margin: 0; font-size: 18px; line-height: 24px; color: #888; font-weight: 300; }
+section .padder, section > .body { padding: 0px 20px 20px; margin-bottom: 18px; }
+
+/** TABLE */
+
+th.wicket_orderUp, th.wicket_orderDown, th.wicket_orderNone { position: relative; }
+.wicket_orderUp:before, .wicket_orderDown:before, .wicket_orderNone:before {
+ font-family: pmease-icons;
+ font-weight: normal;
+ font-style: normal;
+ -webkit-font-smoothing: antialiased;
+ *margin-right: .3em;
+ text-decoration: none;
+ display: inline-block;
+ speak: none;
+ position: absolute;
+ left: 5px;
+ top: 5px;
+ color: #666;
+}
+th.wicket_orderUp a, th.wicket_orderDown a { padding-left: 20px; }
+
+.wicket_orderUp:before { content: "\f062"; }
+.wicket_orderDown:before { content: "\f063"; }
+
+/** SERVER INIT PAGE */
.server-init {
text-align: center;
width: 50%;
@@ -20,28 +117,22 @@
#login-form .btn { width: 100%;}
-#dropzone {
- background: white;
- width: 150px;
- height: 50px;
- line-height: 50px;
- text-align: center;
- font-weight: bold;
-}
-#dropzone.in {
- width: 600px;
- height: 200px;
- line-height: 200px;
- font-size: larger;
-}
-#dropzone.hover {
- background: lawngreen;
-}
-#dropzone.fade {
- -webkit-transition: all 0.3s ease-out;
- -moz-transition: all 0.3s ease-out;
- -ms-transition: all 0.3s ease-out;
- -o-transition: all 0.3s ease-out;
- transition: all 0.3s ease-out;
- opacity: 1;
-}
+/* TEAM EDIT PAGE */
+.member-list .members { width: 48%; }
+.member-list .members.first { float: left; }
+.member-list .members.last { float: right; }
+.members a { color: #999; font-size: 16px; }
+.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 .avatar { width: 32px; height: 32px; margin-right: 8px; }
+
+.permission-link { color: #666; }
+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; }
+
+.btn-group-permissions > a { width: 80px; }
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/common/bootstrap/AjaxIconLink.html b/gitop.web/src/main/java/com/pmease/gitop/web/common/bootstrap/AjaxIconLink.html
new file mode 100644
index 0000000000..b55a9b8796
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/common/bootstrap/AjaxIconLink.html
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/common/bootstrap/AjaxIconLink.java b/gitop.web/src/main/java/com/pmease/gitop/web/common/bootstrap/AjaxIconLink.java
new file mode 100644
index 0000000000..21e8283420
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/common/bootstrap/AjaxIconLink.java
@@ -0,0 +1,51 @@
+package com.pmease.gitop.web.common.bootstrap;
+
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+
+public abstract class AjaxIconLink extends Panel {
+
+ private static final long serialVersionUID = 1L;
+
+ private final IModel iconModel;
+ private final IModel labelModel;
+
+ public AjaxIconLink(String id, IModel iconModel) {
+ this(id, iconModel, Model.of(""));
+ }
+
+ public AjaxIconLink(String id, IModel iconModel,
+ IModel labelModel) {
+ super(id);
+ this.iconModel = iconModel;
+ this.labelModel = labelModel;
+ }
+
+ @Override
+ protected void onInitialize() {
+ super.onInitialize();
+
+ AjaxLink> link = createLink("link");
+ add(link);
+ link.add(new Icon("icon", iconModel));
+ link.add(new Label("label", labelModel));
+ }
+
+ @Override
+ protected void onDetach() {
+ super.onDetach();
+
+ if (iconModel != null) {
+ iconModel.detach();
+ }
+
+ if (labelModel != null) {
+ labelModel.detach();
+ }
+ }
+
+ abstract protected AjaxLink> createLink(String id);
+}
\ No newline at end of file
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/common/bootstrap/Icon.java b/gitop.web/src/main/java/com/pmease/gitop/web/common/bootstrap/Icon.java
new file mode 100644
index 0000000000..0e067cce6a
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/common/bootstrap/Icon.java
@@ -0,0 +1,29 @@
+package com.pmease.gitop.web.common.bootstrap;
+
+import org.apache.wicket.behavior.AttributeAppender;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.model.AbstractReadOnlyModel;
+import org.apache.wicket.model.IModel;
+
+public class Icon extends WebMarkupContainer {
+
+ private static final long serialVersionUID = 1L;
+
+ public Icon(String id, final IconType type) {
+ super(id);
+ add(type.newCssClassNameModifier());
+ }
+
+ @SuppressWarnings("serial")
+ public Icon(String id, final IModel type) {
+ super(id);
+ add(AttributeAppender.append("class",
+ new AbstractReadOnlyModel() {
+
+ @Override
+ public String getObject() {
+ return type.getObject().cssClassName();
+ }
+ }));
+ }
+}
\ No newline at end of file
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/common/bootstrap/IconType.java b/gitop.web/src/main/java/com/pmease/gitop/web/common/bootstrap/IconType.java
new file mode 100644
index 0000000000..77f3f1d438
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/common/bootstrap/IconType.java
@@ -0,0 +1,400 @@
+package com.pmease.gitop.web.common.bootstrap;
+
+import org.apache.wicket.behavior.AttributeAppender;
+
+public enum IconType {
+ NULL("icon-null"),
+ ADJUST("icon-adjust"),
+ ANCHOR("icon-anchor"),
+ ARCHIVE("icon-archive"),
+ ASTERISK("icon-asterisk"),
+ BAN_CIRCLE("icon-ban-circle"),
+ BAR_CHART("icon-bar-chart"),
+ BARCODE("icon-barcode"),
+ BEAKER("icon-beaker"),
+ BEER("icon-beer"),
+ BELL("icon-bell"),
+ BELL_ALT("icon-bell-alt"),
+ BOLT("icon-bolt"),
+ BOOK("icon-book"),
+ BOOKMARK("icon-bookmark"),
+ BOOKMARK_EMPTY("icon-bookmark-empty"),
+ BRIEFCASE("icon-briefcase"),
+ BUG("icon-bug"),
+ BUILDING("icon-building"),
+ BULLHORN("icon-bullhorn"),
+ BULLSEYE("icon-bullseye"),
+ CALENDAR("icon-calendar"),
+ CALENDAR_EMPTY("icon-calendar-empty"),
+ CAMERA("icon-camera"),
+ CAMERA_RETRO("icon-camera-retro"),
+ CERTIFICATE("icon-certificate"),
+ CHECK("icon-check"),
+ CHECK_EMPTY("icon-check-empty"),
+ CHECK_MINUS("icon-check-minus"),
+ CHECK_SIGN("icon-check-sign"),
+ CIRCLE("icon-circle"),
+ CIRCLE_BLANK("icon-circle-blank"),
+ CLOUD("icon-cloud"),
+ CLOUD_DOWNLOAD("icon-cloud-download"),
+ CLOUD_UPLOAD("icon-cloud-upload"),
+ CODE("icon-code"),
+ CODE_FORK("icon-code-fork"),
+ COFFEE("icon-coffee"),
+ COG("icon-cog"),
+ COGS("icon-cogs"),
+ COLLAPSE("icon-collapse"),
+ COLLAPSE_ALT("icon-collapse-alt"),
+ COLLAPSE_TOP("icon-collapse-top"),
+ COMMENT("icon-comment"),
+ COMMENT_ALT("icon-comment-alt"),
+ COMMENTS("icon-comments"),
+ COMMENTS_ALT("icon-comments-alt"),
+ COMPASS("icon-compass"),
+ CREDIT_CARD("icon-credit-card"),
+ CROP("icon-crop"),
+ DASHBOARD("icon-dashboard"),
+ DESKTOP("icon-desktop"),
+ DOWNLOAD("icon-download"),
+ DOWNLOAD_ALT("icon-download-alt"),
+ EDIT("icon-edit"),
+ EDIT_SIGN("icon-edit-sign"),
+ ELLIPSIS_HORIZONTAL("icon-ellipsis-horizontal"),
+ ELLIPSIS_VERTICAL("icon-ellipsis-vertical"),
+ ENVELOPE("icon-envelope"),
+ ENVELOPE_ALT("icon-envelope-alt"),
+ ERASER("icon-eraser"),
+ EXCHANGE("icon-exchange"),
+ EXCLAMATION("icon-exclamation"),
+ EXCLAMATION_SIGN("icon-exclamation-sign"),
+ EXPAND("icon-expand"),
+ EXPAND_ALT("icon-expand-alt"),
+ EXTERNAL_LINK("icon-external-link"),
+ EXTERNAL_LINK_SIGN("icon-external-link-sign"),
+ EYE_CLOSE("icon-eye-close"),
+ EYE_OPEN("icon-eye-open"),
+ FACETIME_VIDEO("icon-facetime-video"),
+ FEMALE("icon-female"),
+ FIGHTER_JET("icon-fighter-jet"),
+ FILM("icon-film"),
+ FILTER("icon-filter"),
+ FIRE("icon-fire"),
+ FIRE_EXTINGUISHER("icon-fire-extinguisher"),
+ FLAG("icon-flag"),
+ FLAG_ALT("icon-flag-alt"),
+ FLAG_CHECKERED("icon-flag-checkered"),
+ FOLDER_CLOSE("icon-folder-close"),
+ FOLDER_CLOSE_ALT("icon-folder-close-alt"),
+ FOLDER_OPEN("icon-folder-open"),
+ FOLDER_OPEN_ALT("icon-folder-open-alt"),
+ FOOD("icon-food"),
+ FROWN("icon-frown"),
+ GAMEPAD("icon-gamepad"),
+ GEAR("icon-gear"),
+ GEARS("icon-gears"),
+ GIFT("icon-gift"),
+ GLASS("icon-glass"),
+ GLOBE("icon-globe"),
+ GROUP("icon-group"),
+ HDD("icon-hdd"),
+ HEADPHONES("icon-headphones"),
+ HEART("icon-heart"),
+ HEART_EMPTY("icon-heart-empty"),
+ HOME("icon-home"),
+ INBOX("icon-inbox"),
+ INFO("icon-info"),
+ INFO_SIGN("icon-info-sign"),
+ KEY("icon-key"),
+ KEYBOARD("icon-keyboard"),
+ LAPTOP("icon-laptop"),
+ LEAF("icon-leaf"),
+ LEGAL("icon-legal"),
+ LEMON("icon-lemon"),
+ LEVEL_DOWN("icon-level-down"),
+ LEVEL_UP("icon-level-up"),
+ LIGHTBULB("icon-lightbulb"),
+ LOCATION_ARROW("icon-location-arrow"),
+ LOCK("icon-lock"),
+ MAGIC("icon-magic"),
+ MAGNET("icon-magnet"),
+ MAIL_FORWARD("icon-mail-forward"),
+ MAIL_REPLY("icon-mail-reply"),
+ MAIL_REPLY_ALL("icon-mail-reply-all"),
+ MALE("icon-male"),
+ MAP_MARKER("icon-map-marker"),
+ MEH("icon-meh"),
+ MICROPHONE("icon-microphone"),
+ MICROPHONE_OFF("icon-microphone-off"),
+ MINUS("icon-minus"),
+ MINUS_SIGN("icon-minus-sign"),
+ MINUS_SIGN_ALT("icon-minus-sign-alt"),
+ MOBILE_PHONE("icon-mobile-phone"),
+ MONEY("icon-money"),
+ MOON("icon-moon"),
+ MOVE("icon-move"),
+ MUSIC("icon-music"),
+ OFF("icon-off"),
+ OK("icon-ok"),
+ OK_CIRCLE("icon-ok-circle"),
+ OK_SIGN("icon-ok-sign"),
+ PENCIL("icon-pencil"),
+ PHONE("icon-phone"),
+ PHONE_SIGN("icon-phone-sign"),
+ PICTURE("icon-picture"),
+ PLANE("icon-plane"),
+ PLUS("icon-plus"),
+ PLUS_SIGN("icon-plus-sign"),
+ PLUS_SIGN_ALT("icon-plus-sign-alt"),
+ POWER_OFF("icon-power-off"),
+ PRINT("icon-print"),
+ PUSHPIN("icon-pushpin"),
+ PUZZLE_PIECE("icon-puzzle-piece"),
+ QRCODE("icon-qrcode"),
+ QUESTION("icon-question"),
+ QUESTION_SIGN("icon-question-sign"),
+ QUOTE_LEFT("icon-quote-left"),
+ QUOTE_RIGHT("icon-quote-right"),
+ RANDOM("icon-random"),
+ REFRESH("icon-refresh"),
+ REMOVE("icon-remove"),
+ REMOVE_CIRCLE("icon-remove-circle"),
+ REMOVE_SIGN("icon-remove-sign"),
+ REORDER("icon-reorder"),
+ REPLY("icon-reply"),
+ REPLY_ALL("icon-reply-all"),
+ RESIZE_HORIZONTAL("icon-resize-horizontal"),
+ RESIZE_VERTICAL("icon-resize-vertical"),
+ RETWEET("icon-retweet"),
+ ROAD("icon-road"),
+ ROCKET("icon-rocket"),
+ RSS("icon-rss"),
+ RSS_SIGN("icon-rss-sign"),
+ SCREENSHOT("icon-screenshot"),
+ SEARCH("icon-search"),
+ SHARE("icon-share"),
+ SHARE_ALT("icon-share-alt"),
+ SHARE_SIGN("icon-share-sign"),
+ SHIELD("icon-shield"),
+ SHOPPING_CART("icon-shopping-cart"),
+ SIGN_BLANK("icon-sign-blank"),
+ SIGNAL("icon-signal"),
+ SIGNIN("icon-signin"),
+ SIGNOUT("icon-signout"),
+ SITEMAP("icon-sitemap"),
+ SMILE("icon-smile"),
+ SORT("icon-sort"),
+ SORT_BY_ALPHABET("icon-sort-by-alphabet"),
+ SORT_BY_ALPHABET_ALT("icon-sort-by-alphabet-alt"),
+ SORT_BY_ATTRIBUTES("icon-sort-by-attributes"),
+ SORT_BY_ATTRIBUTES_ALT("icon-sort-by-attributes-alt"),
+ SORT_BY_ORDER("icon-sort-by-order"),
+ SORT_BY_ORDER_ALT("icon-sort-by-order-alt"),
+ SORT_DOWN("icon-sort-down"),
+ SORT_UP("icon-sort-up"),
+ SPINNER("icon-spinner"),
+ STAR("icon-star"),
+ STAR_EMPTY("icon-star-empty"),
+ STAR_HALF("icon-star-half"),
+ STAR_HALF_EMPTY("icon-star-half-empty"),
+ STAR_HALF_FULL("icon-star-half-full"),
+ SUBSCRIPT("icon-subscript"),
+ SUITCASE("icon-suitcase"),
+ SUN("icon-sun"),
+ SUPERSCRIPT("icon-superscript"),
+ TABLET("icon-tablet"),
+ TAG("icon-tag"),
+ TAGS("icon-tags"),
+ TASKS("icon-tasks"),
+ TERMINAL("icon-terminal"),
+ THUMBS_DOWN("icon-thumbs-down"),
+ THUMBS_DOWN_ALT("icon-thumbs-down-alt"),
+ THUMBS_UP("icon-thumbs-up"),
+ THUMBS_UP_ALT("icon-thumbs-up-alt"),
+ TICKET("icon-ticket"),
+ TIME("icon-time"),
+ TINT("icon-tint"),
+ TRASH("icon-trash"),
+ TROPHY("icon-trophy"),
+ TRUCK("icon-truck"),
+ UMBRELLA("icon-umbrella"),
+ UNCHECKED("icon-unchecked"),
+ UNLOCK("icon-unlock"),
+ UNLOCK_ALT("icon-unlock-alt"),
+ UPLOAD("icon-upload"),
+ UPLOAD_ALT("icon-upload-alt"),
+ USER("icon-user"),
+ VOLUME_DOWN("icon-volume-down"),
+ VOLUME_OFF("icon-volume-off"),
+ VOLUME_UP("icon-volume-up"),
+ WARNING_SIGN("icon-warning-sign"),
+ WRENCH("icon-wrench"),
+ ZOOM_IN("icon-zoom-in"),
+ ZOOM_OUT("icon-zoom-out"),
+ BITCOIN("icon-bitcoin"),
+ BTC("icon-btc"),
+ CNY("icon-cny"),
+ DOLLAR("icon-dollar"),
+ EUR("icon-eur"),
+ EURO("icon-euro"),
+ GBP("icon-gbp"),
+ INR("icon-inr"),
+ JPY("icon-jpy"),
+ KRW("icon-krw"),
+ RENMINBI("icon-renminbi"),
+ RUPEE("icon-rupee"),
+ USD("icon-usd"),
+ WON("icon-won"),
+ YEN("icon-yen"),
+ ALIGN_CENTER("icon-align-center"),
+ ALIGN_JUSTIFY("icon-align-justify"),
+ ALIGN_LEFT("icon-align-left"),
+ ALIGN_RIGHT("icon-align-right"),
+ BOLD("icon-bold"),
+ COLUMNS("icon-columns"),
+ COPY("icon-copy"),
+ CUT("icon-cut"),
+ FILE("icon-file"),
+ FILE_ALT("icon-file-alt"),
+ FILE_TEXT("icon-file-text"),
+ FILE_TEXT_ALT("icon-file-text-alt"),
+ FONT("icon-font"),
+ INDENT_LEFT("icon-indent-left"),
+ INDENT_RIGHT("icon-indent-right"),
+ ITALIC("icon-italic"),
+ LINK("icon-link"),
+ LIST("icon-list"),
+ LIST_ALT("icon-list-alt"),
+ LIST_OL("icon-list-ol"),
+ LIST_UL("icon-list-ul"),
+ PAPER_CLIP("icon-paper-clip"),
+ PAPERCLIP("icon-paperclip"),
+ PASTE("icon-paste"),
+ REPEAT("icon-repeat"),
+ ROTATE_LEFT("icon-rotate-left"),
+ ROTATE_RIGHT("icon-rotate-right"),
+ SAVE("icon-save"),
+ STRIKETHROUGH("icon-strikethrough"),
+ TABLE("icon-table"),
+ TEXT_HEIGHT("icon-text-height"),
+ TEXT_WIDTH("icon-text-width"),
+ TH("icon-th"),
+ TH_LARGE("icon-th-large"),
+ TH_LIST("icon-th-list"),
+ UNDERLINE("icon-underline"),
+ UNDO("icon-undo"),
+ UNLINK("icon-unlink"),
+ ANGLE_DOWN("icon-angle-down"),
+ ANGLE_LEFT("icon-angle-left"),
+ ANGLE_RIGHT("icon-angle-right"),
+ ANGLE_UP("icon-angle-up"),
+ ARROW_DOWN("icon-arrow-down"),
+ ARROW_LEFT("icon-arrow-left"),
+ ARROW_RIGHT("icon-arrow-right"),
+ ARROW_UP("icon-arrow-up"),
+ CARET_DOWN("icon-caret-down"),
+ CARET_LEFT("icon-caret-left"),
+ CARET_RIGHT("icon-caret-right"),
+ CARET_UP("icon-caret-up"),
+ CHEVRON_DOWN("icon-chevron-down"),
+ CHEVRON_LEFT("icon-chevron-left"),
+ CHEVRON_RIGHT("icon-chevron-right"),
+ CHEVRON_SIGN_DOWN("icon-chevron-sign-down"),
+ CHEVRON_SIGN_LEFT("icon-chevron-sign-left"),
+ CHEVRON_SIGN_RIGHT("icon-chevron-sign-right"),
+ CHEVRON_SIGN_UP("icon-chevron-sign-up"),
+ CHEVRON_UP("icon-chevron-up"),
+ CIRCLE_ARROW_DOWN("icon-circle-arrow-down"),
+ CIRCLE_ARROW_LEFT("icon-circle-arrow-left"),
+ CIRCLE_ARROW_RIGHT("icon-circle-arrow-right"),
+ CIRCLE_ARROW_UP("icon-circle-arrow-up"),
+ DOUBLE_ANGLE_DOWN("icon-double-angle-down"),
+ DOUBLE_ANGLE_LEFT("icon-double-angle-left"),
+ DOUBLE_ANGLE_RIGHT("icon-double-angle-right"),
+ DOUBLE_ANGLE_UP("icon-double-angle-up"),
+ HAND_DOWN("icon-hand-down"),
+ HAND_LEFT("icon-hand-left"),
+ HAND_RIGHT("icon-hand-right"),
+ HAND_UP("icon-hand-up"),
+ LONG_ARROW_DOWN("icon-long-arrow-down"),
+ LONG_ARROW_LEFT("icon-long-arrow-left"),
+ LONG_ARROW_RIGHT("icon-long-arrow-right"),
+ LONG_ARROW_UP("icon-long-arrow-up"),
+ BACKWARD("icon-backward"),
+ EJECT("icon-eject"),
+ FAST_BACKWARD("icon-fast-backward"),
+ FAST_FORWARD("icon-fast-forward"),
+ FORWARD("icon-forward"),
+ FULLSCREEN("icon-fullscreen"),
+ PAUSE("icon-pause"),
+ PLAY("icon-play"),
+ PLAY_CIRCLE("icon-play-circle"),
+ PLAY_SIGN("icon-play-sign"),
+ RESIZE_FULL("icon-resize-full"),
+ RESIZE_SMALL("icon-resize-small"),
+ STEP_BACKWARD("icon-step-backward"),
+ STEP_FORWARD("icon-step-forward"),
+ STOP("icon-stop"),
+ YOUTUBE_PLAY("icon-youtube-play"),
+ ADN("icon-adn"),
+ ANDROID("icon-android"),
+ APPLE("icon-apple"),
+ BITBUCKET("icon-bitbucket"),
+ BITBUCKET_SIGN("icon-bitbucket-sign"),
+ CSS3("icon-css3"),
+ DRIBBBLE("icon-dribbble"),
+ DROPBOX("icon-dropbox"),
+ FACEBOOK("icon-facebook"),
+ FACEBOOK_SIGN("icon-facebook-sign"),
+ FLICKR("icon-flickr"),
+ FOURSQUARE("icon-foursquare"),
+ GITHUB("icon-github"),
+ GITHUB_ALT("icon-github-alt"),
+ GITHUB_SIGN("icon-github-sign"),
+ GITTIP("icon-gittip"),
+ GOOGLE_PLUS("icon-google-plus"),
+ GOOGLE_PLUS_SIGN("icon-google-plus-sign"),
+ HTML5("icon-html5"),
+ INSTAGRAM("icon-instagram"),
+ LINKEDIN("icon-linkedin"),
+ LINKEDIN_SIGN("icon-linkedin-sign"),
+ LINUX("icon-linux"),
+ MAXCDN("icon-maxcdn"),
+ PINTEREST("icon-pinterest"),
+ PINTEREST_SIGN("icon-pinterest-sign"),
+ RENREN("icon-renren"),
+ SKYPE("icon-skype"),
+ STACKEXCHANGE("icon-stackexchange"),
+ TRELLO("icon-trello"),
+ TUMBLR("icon-tumblr"),
+ TUMBLR_SIGN("icon-tumblr-sign"),
+ TWITTER("icon-twitter"),
+ TWITTER_SIGN("icon-twitter-sign"),
+ VK("icon-vk"),
+ WEIBO("icon-weibo"),
+ WINDOWS("icon-windows"),
+ XING("icon-xing"),
+ XING_SIGN("icon-xing-sign"),
+ YOUTUBE("icon-youtube"),
+ YOUTUBE_SIGN("icon-youtube-sign"),
+ AMBULANCE("icon-ambulance"),
+ H_SIGN("icon-h-sign"),
+ HOSPITAL("icon-hospital"),
+ MEDKIT("icon-medkit"),
+ STETHOSCOPE("icon-stethoscope"),
+ USER_MD("icon-user-md");
+
+
+ private final String cssClassName;
+
+ IconType(String cssClassName) {
+ this.cssClassName = cssClassName;
+ }
+
+ public String cssClassName() {
+ return cssClassName;
+ }
+
+ public AttributeAppender newCssClassNameModifier() {
+ return AttributeAppender.append("class", cssClassName());
+ }
+}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/common/component/datagrid/hibernate/HibernateDataProvider.java b/gitop.web/src/main/java/com/pmease/gitop/web/common/component/datagrid/hibernate/HibernateDataProvider.java
index 3a7ed4adbd..1d6c478b89 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/common/component/datagrid/hibernate/HibernateDataProvider.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/common/component/datagrid/hibernate/HibernateDataProvider.java
@@ -28,15 +28,15 @@ public abstract class HibernateDataProvider extends So
public Iterator extends T> iterator(long first, long count) {
GeneralDao dao = getDao();
DetachedCriteria criteria = getCriteria();
+
SortParam param = getSort();
if (param != null) {
- DetachedCriteria crit = getCriteria();
Iterable it = Splitter.on(",").trimResults().split(param.getProperty());
for (String each : it) {
if (param.isAscending())
- crit.addOrder(Order.asc(each));
+ criteria.addOrder(Order.asc(each));
else
- crit.addOrder(Order.desc(each));
+ criteria.addOrder(Order.desc(each));
}
}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/common/component/datagrid/toolbar/SearchNavToolbar.java b/gitop.web/src/main/java/com/pmease/gitop/web/common/component/datagrid/toolbar/SearchNavToolbar.java
index 5e8446b5e8..026a881087 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/common/component/datagrid/toolbar/SearchNavToolbar.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/common/component/datagrid/toolbar/SearchNavToolbar.java
@@ -28,205 +28,217 @@ import com.pmease.gitop.web.common.component.datagrid.event.SearchStringChanged;
public class SearchNavToolbar extends AbstractToolbar {
- private static final long serialVersionUID = 1L;
+ private static final long serialVersionUID = 1L;
- public SearchNavToolbar(DataTable, ?> table) {
- super(table);
+ public SearchNavToolbar(DataTable, ?> table) {
+ super(table);
- setOutputMarkupId(true);
- }
+ setOutputMarkupId(true);
+ }
- @Override
- protected void onInitialize() {
- super.onInitialize();
+ @Override
+ protected void onInitialize() {
+ super.onInitialize();
- WebMarkupContainer span = new WebMarkupContainer("td");
- add(span);
- span.add(AttributeModifier.replace("colspan", new AbstractReadOnlyModel() {
- private static final long serialVersionUID = 1L;
+ WebMarkupContainer span = new WebMarkupContainer("td");
+ add(span);
+ span.add(AttributeModifier.replace("colspan",
+ new AbstractReadOnlyModel() {
+ private static final long serialVersionUID = 1L;
- @Override
- public String getObject() {
- return String.valueOf(getTable().getColumns().size());
- }
- }));
+ @Override
+ public String getObject() {
+ return String.valueOf(getTable().getColumns().size());
+ }
+ }));
- span.add(newSearchForm("search"));
- span.add(newNavigationForm("nav"));
- }
+ span.add(newSearchForm("search"));
+ span.add(newNavigationForm("nav"));
+ }
- @Override
- protected void onConfigure() {
- super.onConfigure();
-
- this.setVisibilityAllowed(getTable().getRowCount() > 0);
- }
-
- protected Component newSearchForm(String id) {
- Fragment frag = new Fragment(id, "searchFrag", this);
- frag.add(new SearchForm("searchForm"));
- return frag;
- }
-
- @SuppressWarnings({"serial"})
- private class SearchForm extends Form {
+ @Override
+ protected void onConfigure() {
+ super.onConfigure();
- private String pattern;
-
- public SearchForm(String id) {
- super(id);
- }
-
- @Override
- protected void onInitialize() {
- super.onInitialize();
-
- TextField input = new TextField("input", new PropertyModel(this, "pattern"));
- add(input);
- IndicatingAjaxButton submit = new IndicatingAjaxButton("submit", this) {
- @Override
- protected void onSubmit(AjaxRequestTarget target, Form> form) {
- send(getTable(), Broadcast.BREADTH, new SearchStringChanged(target, pattern));
- }
- };
- add(submit);
- setDefaultButton(submit);
- }
-
- @Override
- public void onEvent(IEvent> event) {
- if (event.getPayload() instanceof SearchStringChanged) {
- SearchStringChanged e = (SearchStringChanged) event.getPayload();
- this.pattern = e.getPattern();
- e.getTarget().add(this);
- }
- }
- }
+ this.setVisibilityAllowed(getTable().getRowCount() > 0);
+ }
- protected void onPageChanged(AjaxRequestTarget target) {
- target.add(getTable());
- target.add(this);
- }
-
- protected Component newNavigationForm(String id) {
- Fragment frag = new Fragment(id, "navFrag", this);
- frag.add(new NavigationForm("form"));
- return frag;
- }
+ protected Component newSearchForm(String id) {
+ Fragment frag = new Fragment(id, "searchFrag", this);
+ frag.add(new SearchForm("searchForm"));
+ return frag;
+ }
- static final List ROWS_PER_PAGE = ImmutableList.of(10, 25, 50, 100, 250, 500);
-
- @SuppressWarnings({"serial"})
- private class NavigationForm extends Form {
-
- private int rowsPerPage = 10;
- private int currentPage = 1;
-
- public NavigationForm(String id) {
- super(id);
- }
-
- @Override
- protected void onInitialize() {
- super.onInitialize();
-
- DropDownChoice rowsSelector = new DropDownChoice("rowsSelector",
- new PropertyModel(this, "rowsPerPage"), ROWS_PER_PAGE);
- add(rowsSelector);
- rowsSelector.add(new AjaxFormComponentUpdatingBehavior("onchange") {
+ @SuppressWarnings({ "serial" })
+ private class SearchForm extends Form {
- @Override
- protected void onUpdate(AjaxRequestTarget target) {
- currentPage = 1;
- getTable().setItemsPerPage(rowsPerPage);
- send(getTable(), Broadcast.BREADTH, new PageChanged(target, currentPage - 1));
- }
- });
-
- add(new AjaxLink("previousLink") {
+ private String pattern;
- @Override
- public void onClick(AjaxRequestTarget target) {
- long current = getTable().getCurrentPage();
- if (current == 0) {
- return;
- }
-
- current--;
- if (current < 0) {
- current = 0;
- }
-
- send(getTable(), Broadcast.BREADTH, new PageChanged(target, (int) current));
- }
- });
-
- add(new AjaxLink("nextLink") {
+ public SearchForm(String id) {
+ super(id);
+ }
- @Override
- public void onClick(AjaxRequestTarget target) {
- long current = getTable().getCurrentPage();
- long totals = getTable().getPageCount();
- if (current == totals) {
- return;
- }
-
- current++;
- if (current >= totals) {
- current = totals - 1;
- }
-
- send(getTable(), Broadcast.BREADTH, new PageChanged(target, (int) current));
- }
-
- });
-
- add(new TextField("pageInput", new PropertyModel(this, "currentPage")));
-
- AjaxButton btn = new AjaxButton("submit", this) {
- @Override
- protected void onSubmit(AjaxRequestTarget target, Form> form) {
- if (currentPage == getTable().getCurrentPage() + 1) {
- return;
- }
-
- long totals = getTable().getPageCount();
- currentPage = Math.min(currentPage, (int) totals);
- currentPage = Math.max(currentPage, 0);
-
- send(getTable(), Broadcast.BREADTH, new PageChanged(target, currentPage - 1));
- }
-
- @Override
- protected void onError(AjaxRequestTarget target, Form> form) {
- target.add(form);
- }
- };
- add(btn);
- setDefaultButton(btn);
-
- add(new Label("navlabel", new AbstractReadOnlyModel() {
+ @Override
+ protected void onInitialize() {
+ super.onInitialize();
- @Override
- public String getObject() {
- long totals = getTable().getRowCount();
- long items = getTable().getItemsPerPage();
- long start = (currentPage - 1) * items + 1;
- long end = start + items - 1;
- end = Math.min(end, totals);
- return start + " - " + end + " of " + totals;
- }
- }));
- }
-
- @Override
- public void onEvent(IEvent> event) {
- if (event.getPayload() instanceof PageChanged) {
- PageChanged e = (PageChanged) event.getPayload();
- currentPage = e.getPage() + 1;
- rowsPerPage = (int) getTable().getItemsPerPage();
- e.getTarget().add(this);
- }
- }
- }
+ TextField input = new TextField("input",
+ new PropertyModel(this, "pattern"));
+ add(input);
+ IndicatingAjaxButton submit = new IndicatingAjaxButton("submit",
+ this) {
+ @Override
+ protected void onSubmit(AjaxRequestTarget target, Form> form) {
+ send(getTable(), Broadcast.BREADTH,
+ new SearchStringChanged(target, pattern));
+ }
+ };
+ add(submit);
+ setDefaultButton(submit);
+ }
+
+ @Override
+ public void onEvent(IEvent> event) {
+ if (event.getPayload() instanceof SearchStringChanged) {
+ SearchStringChanged e = (SearchStringChanged) event
+ .getPayload();
+ this.pattern = e.getPattern();
+ e.getTarget().add(this);
+ }
+ }
+ }
+
+ protected void onPageChanged(AjaxRequestTarget target) {
+ target.add(getTable());
+ target.add(this);
+ }
+
+ protected Component newNavigationForm(String id) {
+ Fragment frag = new Fragment(id, "navFrag", this);
+ frag.add(new NavigationForm("form"));
+ return frag;
+ }
+
+ static final List ROWS_PER_PAGE = ImmutableList. of(10,
+ 25, 50, 100, 250, 500);
+
+ @SuppressWarnings({ "serial" })
+ private class NavigationForm extends Form {
+
+ private int rowsPerPage = 10;
+ private int currentPage = 1;
+
+ public NavigationForm(String id) {
+ super(id);
+ }
+
+ @Override
+ protected void onInitialize() {
+ super.onInitialize();
+
+ DropDownChoice rowsSelector = new DropDownChoice(
+ "rowsSelector", new PropertyModel(this,
+ "rowsPerPage"), ROWS_PER_PAGE);
+ add(rowsSelector);
+ rowsSelector.add(new AjaxFormComponentUpdatingBehavior("onchange") {
+
+ @Override
+ protected void onUpdate(AjaxRequestTarget target) {
+ currentPage = 1;
+ getTable().setItemsPerPage(rowsPerPage);
+ send(getTable(), Broadcast.BREADTH, new PageChanged(target,
+ currentPage - 1));
+ }
+ });
+
+ add(new AjaxLink("previousLink") {
+
+ @Override
+ public void onClick(AjaxRequestTarget target) {
+ long current = getTable().getCurrentPage();
+ if (current == 0) {
+ return;
+ }
+
+ current--;
+ if (current < 0) {
+ current = 0;
+ }
+
+ send(getTable(), Broadcast.BREADTH, new PageChanged(target,
+ (int) current));
+ }
+ });
+
+ add(new AjaxLink("nextLink") {
+
+ @Override
+ public void onClick(AjaxRequestTarget target) {
+ long current = getTable().getCurrentPage();
+ long totals = getTable().getPageCount();
+ if (current == totals) {
+ return;
+ }
+
+ current++;
+ if (current >= totals) {
+ current = totals - 1;
+ }
+
+ send(getTable(), Broadcast.BREADTH, new PageChanged(target,
+ (int) current));
+ }
+
+ });
+
+ add(new TextField("pageInput", new PropertyModel(
+ this, "currentPage")));
+
+ AjaxButton btn = new AjaxButton("submit", this) {
+ @Override
+ protected void onSubmit(AjaxRequestTarget target, Form> form) {
+ if (currentPage == getTable().getCurrentPage() + 1) {
+ return;
+ }
+
+ long totals = getTable().getPageCount();
+ currentPage = Math.min(currentPage, (int) totals);
+ currentPage = Math.max(currentPage, 0);
+
+ send(getTable(), Broadcast.BREADTH, new PageChanged(target,
+ currentPage - 1));
+ }
+
+ @Override
+ protected void onError(AjaxRequestTarget target, Form> form) {
+ target.add(form);
+ }
+ };
+ add(btn);
+ setDefaultButton(btn);
+
+ add(new Label("navlabel", new AbstractReadOnlyModel() {
+
+ @Override
+ public String getObject() {
+ long totals = getTable().getRowCount();
+ long items = getTable().getItemsPerPage();
+ long start = (currentPage - 1) * items + 1;
+ long end = start + items - 1;
+ end = Math.min(end, totals);
+ return start + " - " + end + " of " + totals;
+ }
+ }));
+ }
+
+ @Override
+ public void onEvent(IEvent> event) {
+ if (event.getPayload() instanceof PageChanged) {
+ PageChanged e = (PageChanged) event.getPayload();
+ currentPage = e.getPage() + 1;
+ rowsPerPage = (int) getTable().getItemsPerPage();
+ e.getTarget().add(this);
+ }
+ }
+ }
}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/common/component/messenger/Messenger.java b/gitop.web/src/main/java/com/pmease/gitop/web/common/component/messenger/Messenger.java
index d4e0c580e1..bab0fcebb9 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/common/component/messenger/Messenger.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/common/component/messenger/Messenger.java
@@ -7,7 +7,7 @@ import com.pmease.gitop.web.common.util.Options;
public class Messenger {
static enum Type {
- INFO, SUCCESS, ERROR
+ INFO, SUCCESS, WARNING, ERROR
}
private final String jsFunc;
@@ -31,6 +31,10 @@ public class Messenger {
public static Messenger error(String message) {
return message(message, Type.ERROR);
}
+
+ public static Messenger warn(String message) {
+ return message(message, Type.WARNING);
+ }
private static Messenger message(String message, Type type) {
return post(new Options()
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/common/component/messenger/res/css/messenger-theme-flat.css b/gitop.web/src/main/java/com/pmease/gitop/web/common/component/messenger/res/css/messenger-theme-flat.css
index 3b39da42f3..3129462992 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/common/component/messenger/res/css/messenger-theme-flat.css
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/common/component/messenger/res/css/messenger-theme-flat.css
@@ -356,6 +356,9 @@ ul.messenger-theme-flat .messenger-message.alert-success .messenger-message-inne
ul.messenger-theme-flat .messenger-message.alert-info .messenger-message-inner:before {
background: #00B5AD;
}
+ul.messenger-theme-flat .messenger-message.alert-warning .messenger-message-inner:before {
+ background: #ff9500;
+}
/* line 103, ../../src/sass/messenger-theme-flat.sass */
ul.messenger-theme-flat .messenger-message.alert-error .messenger-message-inner:before {
background: #ff2a68;
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/common/component/vex/AjaxConfirmButton.java b/gitop.web/src/main/java/com/pmease/gitop/web/common/component/vex/AjaxConfirmButton.java
index b2fc0ea3e3..fb7d59ac8a 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/common/component/vex/AjaxConfirmButton.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/common/component/vex/AjaxConfirmButton.java
@@ -4,9 +4,7 @@ import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
-import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.markup.head.IHeaderResponse;
-import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.model.IModel;
@@ -20,13 +18,11 @@ public class AjaxConfirmButton extends AjaxButton {
}
public AjaxConfirmButton(String id, Form> form, IModel textModel,
- IModel iconModel, IModel yesLabelModel,
+ final IModel iconModel, IModel yesLabelModel,
IModel noLabelModel, IModel confirmCssClassModel) {
super(id, form);
add(new VexLinkBehavior(textModel, iconModel, yesLabelModel,
noLabelModel, confirmCssClassModel));
-
- add(AttributeAppender.append("class", "confirm-link"));
}
@Override
@@ -54,8 +50,5 @@ public class AjaxConfirmButton extends AjaxButton {
@Override
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
-
- response.render(JavaScriptHeaderItem.forReference(VexConfirmJavaScriptResourceReference.get()));
- response.render(JavaScriptHeaderItem.forScript("vex.defaultOptions.className = 'vex-theme-wireframe'", "vex-theme-options"));
}
}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/common/component/vex/VexLinkBehavior.java b/gitop.web/src/main/java/com/pmease/gitop/web/common/component/vex/VexLinkBehavior.java
index 25665d4d71..884b3c7ddd 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/common/component/vex/VexLinkBehavior.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/common/component/vex/VexLinkBehavior.java
@@ -4,6 +4,7 @@ import org.apache.wicket.Component;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.model.IComponentAssignedModel;
import org.apache.wicket.model.IModel;
@@ -44,6 +45,9 @@ public class VexLinkBehavior extends Behavior {
public void renderHead(Component component, IHeaderResponse response) {
super.renderHead(component, response);
+ response.render(JavaScriptHeaderItem.forReference(VexConfirmJavaScriptResourceReference.get()));
+ response.render(JavaScriptHeaderItem.forScript("vex.defaultOptions.className = 'vex-theme-wireframe'", "vex-theme-options"));
+
String markupId = component.getMarkupId(true);
response.render(OnDomReadyHeaderItem.forScript(String.format(
"$('#%s').on('click', function(e){e.preventDefault(); $(this).confirm(); });",
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/MultipleUserChoice.java b/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/MultipleUserChoice.java
new file mode 100644
index 0000000000..b39a7f63d6
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/MultipleUserChoice.java
@@ -0,0 +1,41 @@
+package com.pmease.gitop.web.component.choice;
+
+import java.util.Collection;
+
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.resource.PackageResourceReference;
+import org.apache.wicket.request.resource.ResourceReference;
+
+import com.pmease.gitop.core.model.User;
+import com.vaynberg.wicket.select2.Select2MultiChoice;
+
+public class MultipleUserChoice extends Select2MultiChoice {
+
+ private static final long serialVersionUID = 1L;
+
+ public MultipleUserChoice(String id, IModel> model) {
+ super(id, model, new UserChoiceProvider());
+ }
+
+ @Override
+ protected void onInitialize() {
+ super.onInitialize();
+ // getSettings().setMinimumInputLength(1);
+ getSettings().setPlaceholder("Choose an user ...");
+ getSettings().setFormatResult("UserChoice.formatter.formatResult");
+ getSettings()
+ .setFormatSelection("UserChoice.formatter.formatSelection");
+ getSettings().setEscapeMarkup("UserChoice.formatter.escapeMarkup");
+ }
+
+ private ResourceReference userChoiceReference = new PackageResourceReference(
+ SingleUserChoice.class, "userchoice.js");
+
+ @Override
+ public void renderHead(IHeaderResponse response) {
+ super.renderHead(response);
+ response.render(JavaScriptHeaderItem.forReference(userChoiceReference));
+ }
+}
\ No newline at end of file
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/SingleTeamChoice.java b/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/SingleTeamChoice.java
new file mode 100644
index 0000000000..63c7e0c126
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/SingleTeamChoice.java
@@ -0,0 +1,39 @@
+package com.pmease.gitop.web.component.choice;
+
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.request.resource.ResourceReference;
+
+import com.pmease.gitop.core.model.Team;
+import com.vaynberg.wicket.select2.ChoiceProvider;
+import com.vaynberg.wicket.select2.Select2Choice;
+
+@SuppressWarnings("serial")
+public class SingleTeamChoice extends Select2Choice {
+
+ public SingleTeamChoice(String id, IModel model,
+ ChoiceProvider teamsProvider) {
+ super(id, model, teamsProvider);
+ }
+
+ @Override
+ protected void onInitialize() {
+ super.onInitialize();
+ getSettings().setPlaceholder("Choose a team ...");
+ getSettings().setFormatResult("TeamChoice.formatter.formatResult");
+ getSettings()
+ .setFormatSelection("TeamChoice.formatter.formatSelection");
+ getSettings().setEscapeMarkup("TeamChoice.formatter.escapeMarkup");
+ }
+
+ private ResourceReference teamChoiceReference =
+ new JavaScriptResourceReference(SingleUserChoice.class, "teamchoice.js");
+
+ @Override
+ public void renderHead(IHeaderResponse response) {
+ super.renderHead(response);
+ response.render(JavaScriptHeaderItem.forReference(teamChoiceReference));
+ }
+}
\ No newline at end of file
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/SingleUserChoice.java b/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/SingleUserChoice.java
new file mode 100644
index 0000000000..d1243bc3d4
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/SingleUserChoice.java
@@ -0,0 +1,39 @@
+package com.pmease.gitop.web.component.choice;
+
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.request.resource.ResourceReference;
+
+import com.pmease.gitop.core.model.User;
+import com.vaynberg.wicket.select2.Select2Choice;
+
+@SuppressWarnings("serial")
+public class SingleUserChoice extends Select2Choice {
+
+ public SingleUserChoice(String id, IModel model) {
+ super(id, model, new UserChoiceProvider());
+ }
+
+ @Override
+ protected void onInitialize() {
+ super.onInitialize();
+ // getSettings().setMinimumInputLength(1);
+ getSettings().setPlaceholder("Choose an user ...");
+ getSettings().setFormatResult("UserChoice.formatter.formatResult");
+ getSettings()
+ .setFormatSelection("UserChoice.formatter.formatSelection");
+ getSettings().setEscapeMarkup("UserChoice.formatter.escapeMarkup");
+ }
+
+ private ResourceReference userChoiceReference =
+ new JavaScriptResourceReference(SingleUserChoice.class, "userchoice.js");
+
+ @Override
+ public void renderHead(IHeaderResponse response) {
+ super.renderHead(response);
+ response.render(JavaScriptHeaderItem.forReference(userChoiceReference));
+ }
+
+}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/TeamChoiceProvider.java b/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/TeamChoiceProvider.java
new file mode 100644
index 0000000000..013b1c4b24
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/TeamChoiceProvider.java
@@ -0,0 +1,75 @@
+package com.pmease.gitop.web.component.choice;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.wicket.model.IModel;
+import org.hibernate.criterion.DetachedCriteria;
+import org.hibernate.criterion.MatchMode;
+import org.hibernate.criterion.Order;
+import org.hibernate.criterion.Restrictions;
+import org.json.JSONException;
+import org.json.JSONWriter;
+
+import com.google.common.collect.Lists;
+import com.pmease.commons.hibernate.dao.GeneralDao;
+import com.pmease.gitop.core.Gitop;
+import com.pmease.gitop.core.manager.TeamManager;
+import com.pmease.gitop.core.model.Team;
+import com.vaynberg.wicket.select2.ChoiceProvider;
+import com.vaynberg.wicket.select2.Response;
+
+public class TeamChoiceProvider extends ChoiceProvider {
+
+ private static final long serialVersionUID = 1L;
+
+ IModel criteria;
+
+ public TeamChoiceProvider(IModel criteria) {
+ this.criteria = criteria;
+ }
+
+ @Override
+ public void query(String term, int page, Response response) {
+ DetachedCriteria crit = criteria == null ? null : criteria.getObject();
+ if (crit == null) {
+ crit = DetachedCriteria.forClass(Team.class);
+ }
+
+ crit.add(Restrictions.ilike("name", term, MatchMode.START));
+ crit.addOrder(Order.asc("name"));
+ int first = page * 10;
+ @SuppressWarnings("unchecked")
+ List teams = (List) Gitop.getInstance(GeneralDao.class).query(crit, first, 10);
+
+ response.addAll(teams);
+ }
+
+ @Override
+ public void toJson(Team choice, JSONWriter writer) throws JSONException {
+ writer.key("id").value(choice.getId())
+ .key("name").value(choice.getName())
+ .key("permission").value(choice.getAuthorizedOperation());
+ }
+
+ @Override
+ public Collection toChoices(Collection ids) {
+ List teams = Lists.newArrayList();
+ TeamManager tm = Gitop.getInstance(TeamManager.class);
+ for (String each : ids) {
+ Long id = Long.valueOf(each);
+ teams.add(tm.get(id));
+ }
+
+ return teams;
+ }
+
+ @Override
+ public void detach() {
+ super.detach();
+
+ if (criteria != null) {
+ criteria.detach();
+ }
+ }
+}
\ No newline at end of file
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/UserChoiceProvider.java b/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/UserChoiceProvider.java
new file mode 100644
index 0000000000..5464da7be4
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/UserChoiceProvider.java
@@ -0,0 +1,81 @@
+package com.pmease.gitop.web.component.choice;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.hibernate.criterion.Criterion;
+import org.hibernate.criterion.MatchMode;
+import org.hibernate.criterion.Order;
+import org.hibernate.criterion.Restrictions;
+import org.json.JSONException;
+import org.json.JSONWriter;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+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.GitopWebApp;
+import com.pmease.gitop.web.component.avatar.AvatarImageResourceReference;
+import com.pmease.gitop.web.util.Gravatar;
+import com.pmease.gitop.web.util.WicketUtils;
+import com.vaynberg.wicket.select2.ChoiceProvider;
+import com.vaynberg.wicket.select2.Response;
+
+public class UserChoiceProvider extends ChoiceProvider {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void query(String term, int page, Response response) {
+ if (Strings.isNullOrEmpty(term)) {
+ return;
+ }
+
+ UserManager um = Gitop.getInstance(UserManager.class);
+ int first = page * 10;
+ List users = um.query(new Criterion[] {
+ Restrictions.ilike("name", term, MatchMode.START),
+ Restrictions.ilike("displayName", term, MatchMode.START)
+ }, new Order[0], first, 10);
+
+ response.addAll(users);
+ }
+
+ @Override
+ public void toJson(User choice, JSONWriter writer) throws JSONException {
+ writer.key("id").value(choice.getId())
+ .key("name").value(choice.getName())
+ .key("displayName").value(choice.getDisplayName())
+ .key("email").value(choice.getEmail())
+ .key("avatar").value(getAvatarUrl(choice));
+ }
+
+ @Override
+ public Collection toChoices(Collection ids) {
+ List users = Lists.newArrayList();
+ UserManager um = Gitop.getInstance(UserManager.class);
+ for (String each : ids) {
+ Long id = Long.valueOf(each);
+ users.add(um.get(id));
+ }
+
+ return users;
+ }
+
+ private String getAvatarUrl(User user) {
+ String path = user.getAvatarUrl();
+ if (GitopWebApp.get().isGravatarEnabled() && Strings.isNullOrEmpty(path)) {
+ return Gravatar.getURL(user.getEmail());
+ } else {
+ CharSequence url = RequestCycle.get().urlFor(
+ new AvatarImageResourceReference(),
+ WicketUtils.newPageParams(
+ "id",String.valueOf(user.getId()),
+ "type", "user"));
+
+ return url.toString() + "?antiCache=" + System.currentTimeMillis();
+ }
+ }
+}
\ No newline at end of file
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/teamchoice.js b/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/teamchoice.js
new file mode 100644
index 0000000000..c35e8b5949
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/teamchoice.js
@@ -0,0 +1,14 @@
+var TeamChoice = TeamChoice || {};
+TeamChoice.formatter = {
+ formatSelection: function(team) {
+ return team.name;
+ },
+
+ formatResult: function(team) {
+ return team.name;
+ },
+
+ escapeMarkup: function(m) {
+ return m;
+ }
+};
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/userchoice.js b/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/userchoice.js
new file mode 100644
index 0000000000..fd0cf4a378
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/component/choice/userchoice.js
@@ -0,0 +1,17 @@
+var UserChoice = UserChoice || {};
+UserChoice.formatter = {
+ formatSelection: function(user) {
+ return " " + user.name + " (" + user.displayName + ")";
+ },
+
+ formatResult: function(user) {
+ return ""
+ + "
"+ user.name + " (" + user.displayName + ")" + "
"
+ + "
" + user.email + "
"
+ + "
";
+ },
+
+ escapeMarkup: function(m) {
+ return m;
+ }
+};
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/model/EntityModel.java b/gitop.web/src/main/java/com/pmease/gitop/web/model/EntityModel.java
index b51cb2b691..5666f4adf8 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/model/EntityModel.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/model/EntityModel.java
@@ -1,36 +1,37 @@
package com.pmease.gitop.web.model;
-
import org.apache.wicket.model.LoadableDetachableModel;
+import com.google.common.base.Preconditions;
import com.pmease.commons.hibernate.AbstractEntity;
import com.pmease.commons.hibernate.dao.GeneralDao;
import com.pmease.commons.loader.AppLoader;
public class EntityModel extends LoadableDetachableModel {
- private static final long serialVersionUID = 1L;
-
- protected T entity;
- private final Class entityClass;
-
- protected GeneralDao getDao() {
- return AppLoader.getInstance(GeneralDao.class);
- }
-
- @SuppressWarnings("unchecked")
-public EntityModel(T entity) {
- this.entity = entity;
- this.entityClass = (Class) entity.getClass();
- }
-
- @Override
- protected T load() {
- if (entity.isNew()) {
- return entity;
- } else {
- return getDao().get(entityClass, entity.getId());
- }
- }
+ private static final long serialVersionUID = 1L;
+
+ protected T entity;
+ private final Class entityClass;
+
+ protected GeneralDao getDao() {
+ return AppLoader.getInstance(GeneralDao.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ public EntityModel(T entity) {
+ Preconditions.checkNotNull(entity, "entity");
+ this.entity = entity;
+ this.entityClass = (Class) entity.getClass();
+ }
+
+ @Override
+ protected T load() {
+ if (entity.isNew()) {
+ return entity;
+ } else {
+ return getDao().get(entityClass, entity.getId());
+ }
+ }
}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/model/TeamModel.java b/gitop.web/src/main/java/com/pmease/gitop/web/model/TeamModel.java
new file mode 100644
index 0000000000..809bab1e47
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/model/TeamModel.java
@@ -0,0 +1,12 @@
+package com.pmease.gitop.web.model;
+
+import com.pmease.gitop.core.model.Team;
+
+public class TeamModel extends EntityModel {
+ private static final long serialVersionUID = 1L;
+
+ public TeamModel(Team team) {
+ super(team);
+ }
+
+}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/BasePage.java b/gitop.web/src/main/java/com/pmease/gitop/web/page/BasePage.java
index 25b75490d4..8751fa1e1a 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/page/BasePage.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/BasePage.java
@@ -183,7 +183,7 @@ public abstract class BasePage extends WebPage {
protected void onPageInitialize() {
if (!isPermitted()) {
- throw new AccessDeniedException();
+ throw new AccessDeniedException("Access denied");
}
add(new Label("title", getPageTitle()));
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/PageSpec.java b/gitop.web/src/main/java/com/pmease/gitop/web/page/PageSpec.java
index 03e4bd7fe3..0087ecf934 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/page/PageSpec.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/PageSpec.java
@@ -1,9 +1,14 @@
package com.pmease.gitop.web.page;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.link.Link;
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.project.ProjectHomePage;
import com.pmease.gitop.web.util.WicketUtils;
public class PageSpec {
@@ -11,11 +16,30 @@ public class PageSpec {
public static final String ID = "id";
public static final String TYPE = "type";
public static final String USER = "user";
+ public static final String PROJECT = "project";
public static final String REPO = "repo";
public static final String TAB = "tab";
-
+
public static PageParameters avatarOfUser(User user) {
- return WicketUtils.newPageParams(TYPE, AvatarImageType.USER.name().toLowerCase(),
- ID, String.valueOf(user.getId()));
+ return WicketUtils.newPageParams(TYPE, AvatarImageType.USER.name()
+ .toLowerCase(), ID, String.valueOf(user.getId()));
}
+
+ public static PageParameters forUser(User user) {
+ return WicketUtils.newPageParams(USER, user.getName());
+ }
+
+ public static PageParameters forProject(Project project) {
+ return WicketUtils.newPageParams(USER, project.getOwner().getName(),
+ PROJECT, project.getName());
+ }
+
+ public static Link> newUserHomeLink(String id, User user) {
+ return new BookmarkablePageLink(id, AccountHomePage.class, forUser(user));
+ }
+
+ public static Link> newProjectHomeLink(String id, Project project) {
+ return new BookmarkablePageLink(id, ProjectHomePage.class, forProject(project));
+ }
+
}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/RegisterPage.java b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/RegisterPage.java
index 9df2b560df..0425586cdc 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/RegisterPage.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/RegisterPage.java
@@ -51,9 +51,7 @@ public class RegisterPage extends AbstractLayoutPage {
@Override
protected void onInitialize() {
super.onInitialize();
-
}
-
};
add(form);
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 a556d8d5fa..724257cd71 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
@@ -6,15 +6,24 @@
Anonymous Users
-
Anonymous users can browse and pull any repositories under this account
+
Anonymous users can browse and pull any repositories under this account
Logged-in Users
-
Logged-in users can access any repositories under this account with permission:
+
Logged-in users can access any repositories under this account with permission:
+
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.
+
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/AccountPermissionPage.java b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/AccountPermissionPage.java
index e853ff40b9..88faa65133 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/AccountPermissionPage.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/AccountPermissionPage.java
@@ -1,5 +1,20 @@
package com.pmease.gitop.web.page.account.setting.permission;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.behavior.AttributeAppender;
+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.model.AbstractReadOnlyModel;
+
+import com.google.common.collect.ImmutableList;
+import com.pmease.gitop.core.Gitop;
+import com.pmease.gitop.core.manager.UserManager;
+import com.pmease.gitop.core.model.User;
+import com.pmease.gitop.core.permission.operation.GeneralOperation;
+import com.pmease.gitop.web.model.UserModel;
import com.pmease.gitop.web.page.account.setting.AccountSettingPage;
@SuppressWarnings("serial")
@@ -15,4 +30,81 @@ public class AccountPermissionPage extends AccountSettingPage {
return Category.PERMISSION;
}
+ @Override
+ protected void onPageInitialize() {
+ super.onPageInitialize();
+
+ AjaxLink> link = new AjaxLink("anonymouslink") {
+
+ @Override
+ public void onClick(AjaxRequestTarget target) {
+ User account = getAccount();
+ account.setPubliclyAccessible(!account.isPubliclyAccessible());
+ Gitop.getInstance(UserManager.class).save(account);
+
+ target.add(this);
+ }
+
+ };
+
+ add(link);
+ link.setOutputMarkupId(true);
+ link.add(AttributeAppender.append("class", new AbstractReadOnlyModel() {
+
+ @Override
+ public String getObject() {
+ return getAccount().isPubliclyAccessible() ?
+ "checked" : "";
+ }
+
+ }));
+
+ WebMarkupContainer loggedInPermissions = new WebMarkupContainer("loggedInPermissions");
+ add(loggedInPermissions);
+ loggedInPermissions.setOutputMarkupId(true);
+ loggedInPermissions.add(new ListView("permissions",
+ ImmutableList.of(GeneralOperation.NO_ACCESS, GeneralOperation.READ, GeneralOperation.WRITE)) {
+
+ @Override
+ protected void populateItem(ListItem item) {
+ AjaxLink> link = new PermissionLink("permission", item.getModelObject());
+ item.add(link);
+ link.add(new Label("name", item.getModelObject().toString()));
+ }
+
+ });
+
+ add(new TeamsPanel("teams", new UserModel(getAccount())));
+ }
+
+ class PermissionLink extends AjaxLink {
+ final GeneralOperation permssion;
+
+ PermissionLink(String id, final GeneralOperation permission) {
+ super(id);
+ this.permssion = permission;
+
+ add(AttributeAppender.append("class", new AbstractReadOnlyModel() {
+
+ @Override
+ public String getObject() {
+ return getAccount().getDefaultAuthorizedOperation() == permission ?
+ "btn-default active" : "btn-default";
+ }
+ }));
+ }
+
+ @Override
+ public void onClick(AjaxRequestTarget target) {
+ User account = getAccount();
+ if (account.getDefaultAuthorizedOperation() == permssion) {
+ return;
+ }
+
+ account.setDefaultAuthorizedOperation(permssion);
+ Gitop.getInstance(UserManager.class).save(account);
+
+ target.add(AccountPermissionPage.this.get("loggedInPermissions"));
+ }
+ }
}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/AddTeamPage.java b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/AddTeamPage.java
new file mode 100644
index 0000000000..9039a043b9
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/AddTeamPage.java
@@ -0,0 +1,15 @@
+package com.pmease.gitop.web.page.account.setting.permission;
+
+import com.pmease.gitop.core.model.Team;
+
+@SuppressWarnings("serial")
+public class AddTeamPage extends EditTeamPage {
+
+ public AddTeamPage() {
+ }
+
+ @Override
+ protected Team getTeam() {
+ return new Team();
+ }
+}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/EditTeamPage.html b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/EditTeamPage.html
new file mode 100644
index 0000000000..5dd510c4cd
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/EditTeamPage.html
@@ -0,0 +1,12 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/EditTeamPage.java b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/EditTeamPage.java
new file mode 100644
index 0000000000..e40d308b34
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/EditTeamPage.java
@@ -0,0 +1,59 @@
+package com.pmease.gitop.web.page.account.setting.permission;
+
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.model.AbstractReadOnlyModel;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+import com.pmease.gitop.core.Gitop;
+import com.pmease.gitop.core.manager.TeamManager;
+import com.pmease.gitop.core.model.Team;
+import com.pmease.gitop.web.model.TeamModel;
+import com.pmease.gitop.web.model.UserModel;
+import com.pmease.gitop.web.page.account.setting.AccountSettingPage;
+
+@SuppressWarnings("serial")
+public class EditTeamPage extends AccountSettingPage {
+
+ protected final Long teamId;
+
+ public EditTeamPage() {
+ this.teamId = null;
+ }
+
+ public EditTeamPage(PageParameters params) {
+ this.teamId = params.get("teamId").toLongObject();
+ }
+
+ @Override
+ protected Category getSettingCategory() {
+ return Category.PERMISSION;
+ }
+
+ @Override
+ protected String getPageTitle() {
+ return "Edit Team";
+ }
+
+ @Override
+ protected void onPageInitialize() {
+ super.onPageInitialize();
+
+ add(new TeamEditor("editor", new UserModel(getAccount()), new TeamModel(getTeam())));
+ add(new Label("head", new AbstractReadOnlyModel() {
+
+ @Override
+ public String getObject() {
+ return getTeam().isNew() ? "Create Team" : "Edit Team";
+ }
+
+ }));
+ }
+
+ protected Team getTeam() {
+ if (teamId == null) {
+ throw new IllegalStateException("Team id cannot be null when editing team");
+ } else {
+ return Gitop.getInstance(TeamManager.class).get(teamId);
+ }
+ }
+}
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
new file mode 100644
index 0000000000..3074a93100
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/TeamEditor.html
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+ Members
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/TeamEditor.java b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/TeamEditor.java
new file mode 100644
index 0000000000..d396eb4a98
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/TeamEditor.java
@@ -0,0 +1,344 @@
+package com.pmease.gitop.web.page.account.setting.permission;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.behavior.AttributeAppender;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.markup.html.link.Link;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.AbstractReadOnlyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.LoadableDetachableModel;
+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 org.hibernate.criterion.Restrictions;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.pmease.gitop.core.Gitop;
+import com.pmease.gitop.core.manager.MembershipManager;
+import com.pmease.gitop.core.manager.TeamManager;
+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.core.permission.operation.GeneralOperation;
+import com.pmease.gitop.web.common.component.messenger.Messenger;
+import com.pmease.gitop.web.common.form.FeedbackPanel;
+import com.pmease.gitop.web.component.avatar.AvatarImage;
+import com.pmease.gitop.web.component.choice.SingleUserChoice;
+import com.pmease.gitop.web.model.UserModel;
+import com.pmease.gitop.web.page.PageSpec;
+
+@SuppressWarnings("serial")
+public class TeamEditor extends Panel {
+
+ private final IModel userModel;
+
+ private WebMarkupContainer membersContainer;
+
+ public TeamEditor(String id, IModel userModel, IModel model) {
+ super(id, model);
+ this.userModel = userModel;
+ this.setOutputMarkupId(true);
+ }
+
+ private User getAccount() {
+ return userModel.getObject();
+ }
+
+ private Team getTeam() {
+ return (Team) getDefaultModelObject();
+ }
+
+ @Override
+ protected void onInitialize() {
+ super.onInitialize();
+
+ this.name = getTeam().getName();
+ this.operation = getTeam().getAuthorizedOperation();
+
+ add(createInfoForm());
+
+ membersContainer = new WebMarkupContainer("span") {
+ @Override
+ protected void onConfigure() {
+ super.onConfigure();
+ this.setVisibilityAllowed(!getTeam().isNew());
+ }
+ };
+ add(membersContainer);
+ membersContainer.setOutputMarkupId(true);
+
+ membersContainer.add(createMembersForm());
+
+ final IModel> relationModel = new LoadableDetachableModel>() {
+
+ @Override
+ protected List load() {
+ Team team = getTeam();
+ if (team.isNew()) {
+ return Collections.emptyList();
+ }
+
+ List r = Gitop.getInstance(MembershipManager.class)
+ .query(Restrictions.eq("team", getTeam()));
+
+ Collections.sort(r, new java.util.Comparator() {
+
+ @Override
+ public int compare(Membership o1, Membership o2) {
+ return o1.getUser().getName()
+ .compareTo(o2.getUser().getName());
+ }
+ });
+
+ return r;
+ }
+ };
+
+ membersContainer.add(createMemberList("oddlist", relationModel, true));
+ membersContainer.add(createMemberList("evenlist", relationModel, false));
+ membersContainer.add(new Label("total", new AbstractReadOnlyModel() {
+
+ @Override
+ public Integer getObject() {
+ return relationModel.getObject().size();
+ }
+ }).setOutputMarkupId(true));
+ }
+
+ private GeneralOperation operation;
+ private String name;
+
+ private Form> createInfoForm() {
+ operation = getTeam().getAuthorizedOperation();
+ Form> infoForm = new Form("infoForm");
+ add(infoForm);
+ infoForm.add(new FeedbackPanel("feedback"));
+ infoForm.add(new TextField("name", new PropertyModel(
+ this, "name")).add(new IValidator() {
+
+ @Override
+ public void validate(IValidatable validatable) {
+ TeamManager tm = Gitop.getInstance(TeamManager.class);
+ Team team = tm.find(getAccount(), validatable.getValue());
+ Team current = getTeam();
+ if (team != null && !Objects.equal(team, current)) {
+ validatable.error(new ValidationError()
+ .setMessage("The team is already exist."));
+ }
+ }
+ }).setRequired(true));
+ WebMarkupContainer permissionContainer = new WebMarkupContainer(
+ "permissionContainer");
+ permissionContainer.setOutputMarkupId(true);
+ infoForm.add(permissionContainer);
+ permissionContainer.add(new ListView("permissions",
+ ImmutableList. of(
+ GeneralOperation.READ,
+ GeneralOperation.WRITE,
+ GeneralOperation.ADMIN)) {
+
+ @Override
+ protected void populateItem(ListItem item) {
+ GeneralOperation permission = item.getModelObject();
+ AjaxLink> link = new PermissionLink("permission", permission);
+ link.add(new Label("name", permission.toString()));
+ item.add(link);
+ }
+
+ });
+
+ AjaxButton btn = new AjaxButton("submit", infoForm) {
+ @Override
+ protected void onError(AjaxRequestTarget target, Form> form) {
+ target.add(form);
+ }
+
+ @Override
+ protected void onSubmit(AjaxRequestTarget target, Form> form) {
+ Team team = getTeam();
+ boolean isNew = team.isNew();
+ User owner = getAccount();
+ Preconditions.checkNotNull(owner, "owner");
+
+ team.setName(name);
+ team.setAuthorizedOperation(operation);
+ team.setOwner(owner);
+
+ Gitop.getInstance(TeamManager.class).save(team);
+ target.add(TeamEditor.this);
+
+ Messenger.success(
+ String.format("Team has been %s successfully.",
+ isNew ? "created" : "updated")).run(target);
+ }
+ };
+
+ btn.add(new Label("label", new AbstractReadOnlyModel() {
+
+ @Override
+ public String getObject() {
+ return getTeam().isNew() ? "Create Team" : "Update Team";
+ }
+
+ }));
+
+ infoForm.add(btn);
+
+ return infoForm;
+ }
+
+ class PermissionLink extends AjaxLink {
+ final GeneralOperation permission;
+
+ PermissionLink(String id, final GeneralOperation permission) {
+ super(id);
+ this.permission = permission;
+
+ add(AttributeAppender.append("class",
+ new AbstractReadOnlyModel() {
+
+ @Override
+ public String getObject() {
+ return Objects.equal(operation, permission) ?
+ "active" : "";
+ }
+ }));
+ }
+
+ @Override
+ public void onClick(AjaxRequestTarget target) {
+ if (Objects.equal(operation, permission)) {
+ return;
+ }
+
+ operation = permission;
+ target.add(TeamEditor.this.get("infoForm").get("permissionContainer"));
+ }
+ }
+
+ User userToAdd;
+
+ private Form> createMembersForm() {
+ Form> form = new Form("membersForm");
+ form.add(new FeedbackPanel("feedback"));
+ form.add(new SingleUserChoice("userchoice", new PropertyModel(
+ this, "userToAdd")));
+
+ form.add(new AjaxButton("submit", form) {
+ @Override
+ protected void onError(AjaxRequestTarget target, Form> form) {
+ form.error("Please fix errors below.");
+ }
+
+ @Override
+ protected void onSubmit(AjaxRequestTarget target, Form> form) {
+ if (userToAdd == null) {
+ form.error("Please select an user first");
+ target.add(form);
+ return;
+ }
+
+ Team team = getTeam();
+ MembershipManager mm = Gitop
+ .getInstance(MembershipManager.class);
+ Membership m = mm.find(Restrictions.eq("team", team),
+ Restrictions.eq("user", userToAdd));
+ if (m != null) {
+ form.warn("User has been added already");
+ target.add(form);
+ return;
+ }
+
+ m = new Membership();
+ m.setUser(userToAdd);
+ m.setTeam(team);
+ mm.save(m);
+
+ Messenger.success(String.format("User [%s] is added to team [%s]",
+ userToAdd.getName(), team.getName()))
+ .run(target);
+ userToAdd = null;
+ target.add(form);
+ onMembersChanged(target);
+ }
+ });
+
+ return form;
+ }
+
+ private void onMembersChanged(AjaxRequestTarget target) {
+ target.add(membersContainer.get("oddlist"));
+ target.add(membersContainer.get("evenlist"));
+ target.add(membersContainer.get("total"));
+ }
+
+ private Component createMemberList(String id,
+ IModel> model, final boolean expected) {
+ Fragment frag = new Fragment(id, "membersview", this);
+ frag.setOutputMarkupId(true);
+ frag.add(new ListView("member", model) {
+
+ @Override
+ protected void populateItem(ListItem item) {
+ int index = item.getIndex();
+ boolean odd = index % 2 == 0;
+ if (odd != expected) {
+ item.setVisibilityAllowed(false);
+ return;
+ }
+
+ Membership membership = item.getModelObject();
+ User user = membership.getUser();
+ item.add(new AvatarImage("avatar", new UserModel(user)));
+ Link> link = PageSpec.newUserHomeLink("link", user);
+ link.add(new Label("name", user.getName()));
+ link.add(new Label("fullname", user.getDisplayName()));
+ item.add(link);
+
+ final Long id = membership.getId();
+ item.add(new AjaxLink("remove") {
+
+ @Override
+ public void onClick(AjaxRequestTarget target) {
+ MembershipManager mm = Gitop.getInstance(MembershipManager.class);
+ Membership membership = mm.get(id);
+ Gitop.getInstance(MembershipManager.class).delete(membership);
+ Messenger.warn(String.format("User [%s] is removed from team [%s]",
+ membership.getUser().getName(),
+ membership.getTeam().getName()))
+ .run(target);
+
+ onMembersChanged(target);
+ }
+ });
+ }
+ });
+
+ return frag;
+ }
+
+ @Override
+ public void onDetach() {
+ if (userModel != null) {
+ userModel.detach();
+ }
+
+ super.onDetach();
+ }
+}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/TeamsPanel.html b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/TeamsPanel.html
new file mode 100644
index 0000000000..467bea3306
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/TeamsPanel.html
@@ -0,0 +1,19 @@
+
+
+
+ Create a team
+
+
+
+
+
+
+
+
+ ( members)
+
+
+
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/TeamsPanel.java b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/TeamsPanel.java
new file mode 100644
index 0000000000..1c210cc8cd
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/permission/TeamsPanel.java
@@ -0,0 +1,214 @@
+package com.pmease.gitop.web.page.account.setting.permission;
+
+import java.util.List;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.behavior.AttributeAppender;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackHeadersToolbar;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.NoRecordsToolbar;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.AbstractReadOnlyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.hibernate.criterion.Criterion;
+import org.hibernate.criterion.DetachedCriteria;
+import org.hibernate.criterion.MatchMode;
+import org.hibernate.criterion.Restrictions;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.pmease.gitop.core.Gitop;
+import com.pmease.gitop.core.manager.TeamManager;
+import com.pmease.gitop.core.model.Team;
+import com.pmease.gitop.core.model.User;
+import com.pmease.gitop.core.permission.operation.GeneralOperation;
+import com.pmease.gitop.web.common.bootstrap.AjaxIconLink;
+import com.pmease.gitop.web.common.bootstrap.IconType;
+import com.pmease.gitop.web.common.component.datagrid.DataGrid;
+import com.pmease.gitop.web.common.component.datagrid.event.SearchStringChanged;
+import com.pmease.gitop.web.common.component.datagrid.hibernate.HibernateDataProvider;
+import com.pmease.gitop.web.common.component.datagrid.toolbar.SearchNavToolbar;
+import com.pmease.gitop.web.common.component.vex.AjaxConfirmLink;
+import com.pmease.gitop.web.util.WicketUtils;
+
+@SuppressWarnings("serial")
+public class TeamsPanel extends Panel {
+
+ public TeamsPanel(String id, IModel user) {
+ super(id, user);
+ }
+
+ @Override
+ protected void onInitialize() {
+ super.onInitialize();
+
+ add(new BookmarkablePageLink("addTeam", AddTeamPage.class));
+ add(createTeamPermissionsTable("teams"));
+ }
+
+ private Component createTeamPermissionsTable(String id) {
+ List> columns = Lists.newArrayList();
+ columns.add(new AbstractColumn(Model.of("Name"), "name") {
+
+ @Override
+ public void populateItem(Item> cellItem,
+ String componentId, IModel rowModel) {
+ Team team = rowModel.getObject();
+ Fragment frag = new Fragment(componentId, "namefrag",
+ TeamsPanel.this);
+ frag.add(new Label("name", team.getName()));
+ frag.add(new Label("members", team.getMemberships().size()));
+ cellItem.add(frag);
+ }
+ });
+
+ columns.add(new TeamPermissionColumn(Model.of("Read"), GeneralOperation.READ));
+ columns.add(new TeamPermissionColumn(Model.of("Write"), GeneralOperation.WRITE));
+ columns.add(new TeamPermissionColumn(Model.of("Admin"), GeneralOperation.ADMIN));
+
+ columns.add(new AbstractColumn(Model.of("")) {
+
+ @Override
+ public void populateItem(Item> cellItem,
+ String componentId, final IModel rowModel) {
+ Fragment frag = new Fragment(componentId, "ops", TeamsPanel.this);
+ frag.add(new BookmarkablePageLink("edit",
+ EditTeamPage.class, WicketUtils.newPageParams("teamId", rowModel.getObject().getId())));
+
+ Team team = rowModel.getObject();
+ frag.add(new AjaxConfirmLink("remove",
+ Model.of("Are you sure you want to remove team " + team.getName())) {
+
+ @Override
+ public void onClick(AjaxRequestTarget target) {
+ Team p = rowModel.getObject();
+ Gitop.getInstance(TeamManager.class).delete(p);
+ onTeamTableChanged(target);
+ }
+ });
+ cellItem.add(frag);
+ }
+
+ @Override
+ public String getCssClass() {
+ return "operations-td";
+ }
+ });
+
+ final List criterions = Lists.newArrayList();
+
+ HibernateDataProvider teamProvider = new HibernateDataProvider() {
+
+ @Override
+ protected DetachedCriteria getCriteria() {
+ DetachedCriteria criteria = DetachedCriteria.forClass(Team.class);
+ criteria.add(Restrictions.eq("owner", getUser()));
+ for (Criterion each : criterions) {
+ criteria.add(each);
+ }
+
+ return criteria;
+ }
+ };
+
+ DataGrid dataTable = new DataGrid(id, columns,
+ teamProvider, 10) {
+ @Override
+ public void onEvent(IEvent> event) {
+ if (event.getPayload() instanceof SearchStringChanged) {
+ SearchStringChanged e = (SearchStringChanged) event.getPayload();
+
+ criterions.clear();
+
+ if (!Strings.isNullOrEmpty(e.getPattern())) {
+ criterions.add(Restrictions.ilike("name", e.getPattern(), MatchMode.ANYWHERE));
+ }
+
+ e.getTarget().add(this);
+ }
+ }
+ };
+
+ dataTable.setOutputMarkupId(true);
+ dataTable.addTopToolbar(new AjaxFallbackHeadersToolbar(dataTable, teamProvider));
+ dataTable.addBottomToolbar(new NoRecordsToolbar(dataTable, Model.of("No Teams Found")));
+ dataTable.addBottomToolbar(new SearchNavToolbar(dataTable));
+
+ return dataTable;
+ }
+
+ private class TeamPermissionColumn extends AbstractColumn {
+
+ private GeneralOperation expected;
+
+ public TeamPermissionColumn(IModel displayModel,
+ GeneralOperation expected) {
+ super(displayModel);
+ this.expected = expected;
+ }
+
+ @Override
+ public void populateItem(Item> cellItem,
+ String componentId, final IModel rowModel) {
+ IModel iconModel = new AbstractReadOnlyModel() {
+
+ @Override
+ public IconType getObject() {
+ return rowModel.getObject().getAuthorizedOperation().can(expected) ?
+ IconType.CHECK : IconType.CHECK_EMPTY;
+ }
+ };
+
+ cellItem.add(new AjaxIconLink(componentId, iconModel) {
+
+ @Override
+ protected AjaxLink> createLink(String id) {
+ AjaxLink> link = new AjaxLink(id) {
+
+ @Override
+ public void onClick(AjaxRequestTarget target) {
+ GeneralOperation old = rowModel.getObject().getAuthorizedOperation();
+ Team team = rowModel.getObject();
+ if (old == expected) {
+ int ordinal = expected.ordinal() - 1;
+ if (ordinal < 0) {
+ ordinal = 0;
+ }
+
+ team.setAuthorizedOperation(GeneralOperation.values()[ordinal]);
+ } else {
+ team.setAuthorizedOperation(expected);
+ }
+
+ Gitop.getInstance(TeamManager.class).save(team);
+ onTeamTableChanged(target);
+ }
+
+ };
+
+ link.add(AttributeAppender.append("class", "permission-link"));
+ return link;
+ }
+ });
+ }
+ }
+
+ private void onTeamTableChanged(AjaxRequestTarget target) {
+ target.add(get("teams"));
+ }
+
+ private User getUser() {
+ return (User) getDefaultModelObject();
+ }
+
+}
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/profile/AccountProfilePage.html b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/profile/AccountProfilePage.html
index 83b4d0544c..7872b2b128 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/profile/AccountProfilePage.html
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/profile/AccountProfilePage.html
@@ -28,7 +28,7 @@
- Supported image types are png, gif, jpg and jpeg. The image size should be less than 128K bytes.
+ Supported image types are png, gif, jpg and jpeg. The image size should be less than 2M bytes.
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/profile/AccountProfilePage.java b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/profile/AccountProfilePage.java
index dd8835828d..9ae5e843df 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/profile/AccountProfilePage.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/account/setting/profile/AccountProfilePage.java
@@ -13,6 +13,7 @@ import org.apache.wicket.markup.html.form.upload.FileUploadField;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.util.lang.Bytes;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
@@ -106,8 +107,13 @@ public class AccountProfilePage extends AccountSettingPage {
protected void onInitialize() {
super.onInitialize();
+ // limit avatar size to 2M bytes
+ setMaxSize(Bytes.megabytes(2));
+ setMultiPart(true);
+
final FileUploadField uploadField = new FileUploadField("fileInput");
uploadField.setRequired(false);
+
add(uploadField);
add(new FeedbackPanel("feedback"));
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/page/home/HomePage.java b/gitop.web/src/main/java/com/pmease/gitop/web/page/home/HomePage.java
index fd12f4a035..28951c9669 100644
--- a/gitop.web/src/main/java/com/pmease/gitop/web/page/home/HomePage.java
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/page/home/HomePage.java
@@ -5,6 +5,7 @@ import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.RadioGroup;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
@@ -60,7 +61,14 @@ public class HomePage extends AbstractLayoutPage {
form.add(new AjaxConfirmButton("submit", form,
Model.of("Are you sure you want to submit the form?"),
- Model.of(VexIcon.ERROR),
+ new AbstractReadOnlyModel() {
+
+ @Override
+ public VexIcon getObject() {
+ return vexIcon;
+ }
+
+ },
Model.of("Yes"),
Model.of("No"),
null) {
diff --git a/gitop.web/src/main/java/com/pmease/gitop/web/resource/GitHubUserResource.java b/gitop.web/src/main/java/com/pmease/gitop/web/resource/GitHubUserResource.java
new file mode 100644
index 0000000000..9d9208ff76
--- /dev/null
+++ b/gitop.web/src/main/java/com/pmease/gitop/web/resource/GitHubUserResource.java
@@ -0,0 +1,135 @@
+package com.pmease.gitop.web.resource;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+import org.apache.shiro.authc.credential.PasswordService;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Strings;
+import com.google.common.base.Throwables;
+import com.google.common.collect.Lists;
+import com.pmease.gitop.core.manager.UserManager;
+import com.pmease.gitop.core.model.User;
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
+
+@Path("/github")
+public class GitHubUserResource {
+
+ @Inject ObjectMapper objectMapper;
+ @Inject UserManager userManager;
+ @Inject PasswordService passwordService;
+
+ @GET
+ public Response get() {
+ try {
+ updateDatabase("github");
+ } catch (Exception e) {
+ throw Throwables.propagate(e);
+ }
+
+ return Response.ok("Everything is ok").build();
+ }
+
+ private void updateDatabase(String org) throws JsonParseException,
+ JsonMappingException, UniformInterfaceException,
+ ClientHandlerException, IOException {
+ Client client = Client.create();
+ client.addFilter(new HTTPBasicAuthFilter("zhenyuluo", "hongmei9"));
+ WebResource r = client.resource("https://api.github.com/orgs/" + org
+ + "/public_members");
+ String content = r.get(String.class);
+ objectMapper.configure(
+ DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ List users = objectMapper.readValue(content,
+ new TypeReference>() {
+ });
+ users = users.subList(102, users.size());
+ for (HubUser each : users) {
+ try {
+ r = client.resource("https://api.github.com/users/"
+ + each.login);
+ HubUser u = objectMapper.readValue(r.get(String.class),
+ HubUser.class);
+ User user = new User();
+ user.setName(u.login);
+ user.setDisplayName(u.name);
+
+ if (Strings.isNullOrEmpty(u.email)) {
+ user.setEmail(u.login + "@github.com");
+ } else {
+ user.setEmail(u.email);
+ }
+
+ user.setPasswordHash(passwordService.encryptPassword("12345"));
+ userManager.save(user);
+ System.out.println(user.getId() + " " + user);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public static class HubUsers {
+ @JsonProperty
+ List users = Lists.newArrayList();
+
+ public List getUsers() {
+ return users;
+ }
+
+ public void setUsers(List users) {
+ this.users = users;
+ }
+ }
+
+ public static class HubUser {
+ @JsonProperty
+ private String login;
+
+ @JsonProperty
+ private String name;
+
+ @JsonProperty
+ private String email;
+
+ public String getLogin() {
+ return login;
+ }
+
+ public void setLogin(String login) {
+ this.login = login;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+ }
+
+}