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 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 @@ + + +
+
+

Edit Team

+
+
+
+
+
+
+ \ 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 @@ + + +
+ +

Team Name:

+ +
+ + + + + + +
+ +
+ + +
+

Members

+
+ + + +
+
+
+

members total

+
+
+
+
+
+
+
+
+ + + + + +
+ \ 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; + } + } + +}