Commit dd8c49b3 by Liv Haapala

merge master

2 parents 02859a50 d3f8f960
Showing with 1579 additions and 397 deletions
# devausnoteja (tmp ohjeet)
# devausnoteja
## Devaus
### devaus workflow
### Testaus
Kehittäminen on nopeampaa jos saa muutokset suoraan käyttöön, tämän takia kannattaa käynnistää lasikala ja webback-palvelin erikseen, ja proxyttää webbakista moya-jutut lasikalaan.
Kehittämisen nopeuttamiseksi kannattaa frontti käynnistää erikseen ja ajaa sitä proxyn läpi halutulle backendille.
ng-palvelimessa on bugi, ja se ei osaa jakaa tavaraa muusta hakemistosta kuin /.
moyaproxy.conf.json sisältää proxyasetukset localhostin porttiin 8080.
Käyttö:
`npm run start`
* `npm run localhost` : Käynnistä frontend paikallista palvelinta vasten (localhost:8080)
* `npm run testserver` : Käynnistä frontend meidän testipalvelinta vasten (https://ng.test.moya.fi)
Tämän jälkeen kirjaa selaimesi sisään moyaan: http://localhost:4200/MoyaWeb/ ja surffata softaan: http://localhost:4200/
Tämän jälkeen surffaa selaimellasi moyaan: `http://localhost:4200/`
### HMR
### Esteettömyys
HMR -asetukset löytyy, niistä ei nykyisellään ole mitään hyötyä. Saa poistaa.
#### Tabindex
Tehtyjä sivuja pitää kokeilla myös surffaamalla niitä läpi tabilla. Jos järjestys on outo, niin tabindex -elementti on avuksi.
Sivun menuvalikot käyttävät indexejä 10:n alta. (8: Login & kieli, 9: ylämenu, 10: vasenmenu).
#### click
Jos pistät linkkiin pelkän (click) -tagin, sitä ei voi painaa näppäimistöllä. (esim: `<a (click)=...><img>...</>`), Tyhjällä `[routerLink]=""` -tagilla saat kerrottua selaimelle "tähän voi mennä ja tätä voi painaa".
## Koodijuttuja
### moya-rest
Oma moduulinsa, joka tullaan jossain vaiheessa eriyttämään ja käyttämään myös mahdollisessa nativescript-kännyappiksessa. Eli sen alle tulee vain servisejä, ei komponentteja.
## Koodijuttuja
### Muuta
Jos tulee muita järkeviä kokonaisuuksia, joita voi käyttää muualla, jaa omaan moduuliinsa.
### Koodaustyyli
## WIP: Hakemistohierarkia
Meillä on käytössä kohtuu tiukka lintteri, ennen committamista kannattaa ajaa `ng lint` ja korjata virheet. `ng lint moya-angular --fix` on ystävämme.
## Hakemistohierarkia
* app
* modules
* viplist
* viplist-manager.module
* viplist-manager
* viplist-info.component
* viplist-admin.component
* components `Componentit joidenka ei ole järkeä olla omia lazyloadattavia moduuleita`
* modules `Kokonaisuuksia jotka on järkeä jakaa omaksi lazyloadattavaksi kokonaisuudekseen.`
* viplist `Esimerkki vain info ja admin -puoella näkyvästä vippilista -moduulista`
* viplist.module
* info
* info-viplist-page.component `Infopuolen landing -sivu. Tämä on routing component info -vippilistasta`
* admin
* admin-viplist-page.component
* models
* viplist.model
* pizza
* pizza-user
* pizza `Esimerkki moduulista joka näkyy kaikilla puolilla`
* pizza-user
* pizza-user.component
* pizza-user.service
* pizza-user.module
......@@ -59,13 +63,14 @@ Jos tulee muita järkeviä kokonaisuuksia, joita voi käyttää muualla, jaa oma
* pizza-list.component (shared for user / admin pizza)
* models
* pizza.model
* shared
* shared `Resurssit jotka ovat käytössä vähän kaikkialla. Esim. käyttäjää on turha jakaa minkään erillisen moduulin alle`
* components
* services
* models
* auth
##Routing
Tämä pitää vielä miettä läpi
* info
* info routing johon tulee pizza-user.module LL
......@@ -76,58 +81,6 @@ Jos tulee muita järkeviä kokonaisuuksia, joita voi käyttää muualla, jaa oma
* pizza-user.component & pizza-user.service
## OLD PLAN
* app
* pages `tämän alla on käytännössä "älykkäät" komponentit (stateful), eli componentit jotka tietää mitä dataa haetaan ja mistä. Käytännössä route -targetit`
* user `Loppukäyttäjälle näkyvät jutut, ohjleman ns. main moduuli`
* pizzatilaus
* listOpen.component.ts `avoimet pizzatilaukset joista voi tilata`
* own_orders.component.ts
* testit yms.
* admin `moya asetukset, esim. tuotteet, korttipohjat, oma moduulinsa joka lazyloadataan`
* admin.module.ts
* pizzatilaus
* pizzaedit.component.ts `hallitse mitä pizzoja voi tilata ylipäätänsä`
* info `tapahtumanaikana "infolaisen" toimesta tehtävät jutut, sisäänotto, käyttäjänhallinta jne. Oma moduulinsa joka lazyloadataan`
* info.module.ts
* viplist
* info-viplist-page.component.ts,html,jne
* viplist.service.ts
* pizzatilaus
* list.component.ts `listaa kaikki, ja lisää uusi pizzailaus`
* old-moya `Monessa paikassa käytettävät target -componentit tulevat omiksi kokonaisuudiksiin`
* old-moya.component.ts
* pizzatilaus `Jos halutaan lazyloadata jokin osa moyasta, tehdään siitä uusi päätason komponentti. Joka sitten laitetetaan user / admin yms. puolille route -targetiksi`
* pizzatilaus-route.module.ts
* pizza-user-order.component.ts
* menu `menu on täällä`
* shared `geneeriset, mihinkään osaan kuulumattomat jaetut modellit ja servicet tulevat tänne`
* models
* user.model.ts
* services
* moya-rest.service.ts
* session.service.ts
* widgets `tyhmät componponentit tulevat tänne`
* button
* html
* ts
* scss
* components `servicet sekä componenttien geneeriset tyhmät osat tulevat tämän hakemiston alle. Tänne voi myös pistää älykkäitä pieniä komponentteja `
* viplist
* list.component.ts
* add.component.ts
* moya-locale
* moya-locale.service.ts
* moya-locale.model.ts `model -tiedosto joka on käytössä vaan moya-locale -servicessä`
* modules `Kokonaiset, monessa paikassa olevat kokonaisuuksien tyhmät osat tulevat tänne. Sen verta isot että ne kannattaa tehdä moduuleiksi`
* pizzatilaus
* anna-pizza.component.ts
* list-pizzat.component.ts,html,jne
* listItem.component.ts
* edit.component.ts
* pizzatilaus.service.ts
* pizza-tilaus.module.ts
......@@ -159,38 +112,10 @@ Integraatio jne. testit, aloitteliat ei riko asioita niin todennäköisesti.
### WebPack ja buildin minimointi
aot-kääntäminen ja siihen comperssointia jne.
### HMR
HMR -asetukset löytyy, niistä ei nykyisellään ole mitään hyötyä. Saa poistaa.
# MoyaAngular, generoidut ohjeet
This project was generated with [angular-cli](https://github.com/angular/angular-cli) version 1.0.0-beta.24.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
Before running the tests make sure you are serving the app via `ng serve`.
## Deploying to Github Pages
Run `ng github-pages:deploy` to deploy to Github Pages.
## Further help
To get more help on the `angular-cli` use `ng help` or go check out the [Angular-CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
......@@ -3,6 +3,6 @@
xsi:schemaLocation="http://ocpsoft.org/schema/rewrite-config-prettyfaces
http://ocpsoft.org/xml/ns/prettyfaces/rewrite-config-prettyfaces.xsd">
<rewrite match="^(?!.*\.(js|css|jpg|gif|png|ico|html|svg)$).*$" substitute="/index.html" redirect="chain" />
<rewrite match="^(?!.*\.(js|css|jpg|gif|png|ico|html|svg|json)$).*$" substitute="/index.html" redirect="chain" />
</pretty-config>
......@@ -5,7 +5,8 @@
"angular-cli": {},
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config=moyaproxy.conf.json --base-href=/ && ng lint",
"localhost": "ng serve --proxy-config=localhost-moyaproxy.conf.json --base-href=/",
"testserver": "ng serve --proxy-config=test-moyaproxy.conf.json --base-href=/",
"build": "ng build",
"aot": "ng build --aot",
"test": "ng test",
......
import { NgModule } from '@angular/core';
import {Routes, RouterModule, PreloadAllModules, NoPreloading} from '@angular/router';
import { LoginComponent } from './modules/login/login.component';
import {FrontpageComponent} from './components/frontpage/frontpage.component';
import {LoginPageComponent} from './modules/login/login-page/login-page.component';
const APP_ROUTES: Routes = [
{ path: 'login', component: LoginComponent },
{ path: 'viplist', loadChildren: 'app/modules/viplist/viplist.module#ViplistModule' },
{ path: 'usersearch', loadChildren: 'app/modules/usersearch/usersearch.module#UsersearchModule' },
{ path: 'viplist', loadChildren: 'app/modules/viplist/viplist.module#ViplistModule' },
{ path: 'old', loadChildren: 'app/modules/old-moya/old-moya.module#OldMoyaModule' },
{ path: 'index', component: FrontpageComponent },
{ path: '', pathMatch: 'full' , redirectTo: '/index', }
......
......@@ -6,10 +6,12 @@
<div id="contentarea">
<div id="topbar">
<moya-top-menu></moya-top-menu>
<moya-locale></moya-locale>
<div id="loginlocale">
<moya-login-button></moya-login-button>
<moya-locale></moya-locale>
</div>
</div>
<div id="content">
<router-outlet></router-outlet>
</div>
</div>
......@@ -29,3 +29,8 @@
padding: 20px;
flex-grow: 1;
}
#loginlocale {
display: flex;
}
import {Component, NgZone} from '@angular/core';
import {Router} from '@angular/router';
import {MatDialog, MatDialogConfig} from '@angular/material';
import {LoginComponent} from './modules/login/login-component/login.component';
declare var window: any;
......@@ -10,7 +12,7 @@ declare var window: any;
})
export class AppComponent {
constructor(private router: Router, private zone: NgZone) {
constructor(private router: Router, private zone: NgZone, private dialog: MatDialog) {
window.angularRoute = (url => {
zone.run(() => {
......
......@@ -13,20 +13,22 @@ import { MoyaLocaleComponent } from './components/moya-locale/moya-locale.compon
import { TopMenuComponent } from './menu/top-menu/top-menu.component';
import { AppComponent } from './app.component';
import { NgModule } from '@angular/core';
import {LoginModule} from './modules/login/login.module';
import {LeftMenuModule} from './menu/left-menu/left-menu.module';
import { LoginModule } from './modules/login/login.module';
import { LeftMenuModule } from './menu/left-menu/left-menu.module';
import { FrontpageComponent } from './components/frontpage/frontpage.component';
import {HttpLink, HttpLinkModule} from 'apollo-angular-link-http';
import {APOLLO_OPTIONS, ApolloModule} from 'apollo-angular';
import {createApollo} from './shared/config/moya.config';
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
import { HttpLink, HttpLinkModule } from 'apollo-angular-link-http';
import { APOLLO_OPTIONS, ApolloModule } from 'apollo-angular';
import { createApollo } from './shared/config/moya.config';
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { MatButtonModule } from '@angular/material';
import { LoginComponent } from './modules/login/login-component/login.component';
@NgModule({
declarations: [
AppComponent,
TopMenuComponent,
MoyaLocaleComponent,
FrontpageComponent
FrontpageComponent,
],
imports: [
BrowserModule,
......@@ -35,6 +37,8 @@ import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
HttpClientModule,
LoginModule,
LeftMenuModule,
MatButtonModule,
ApolloModule,
HttpLinkModule,
......
<p>
frontpage works!
</p>
<marquee behavior="scroll" direction="left"> frontpage works! </marquee>
<div id="flagBond">
<img src="assets/Flag_of_Finland.svg" [className]="(fiSelected)? 'disabledFlag':'flag' "height="20px" (click)="selectFinnish()" />
<img src="assets/Flag_of_Britain.svg" [className]="(enSelected)? 'disabledFlag':'flag'" height="20px" (click)="selectEnglish()" />
<img src="assets/Flag_of_Sweden.svg" [className]="(svSelected)? 'disabledFlag':'flag'" height="20px" (click)="selectSwedish()" />
<div class="locales">
<a (click)="selectFinnish()" [routerLink]="" tabindex="8"><img src="assets/Flag_of_Finland.svg" [className]="(fiSelected)? 'disabledFlag':'flag' "height="20px" alt="Suomeksi {{ (fiSelected)? 'valittu':'' }}" /></a>
<a (click)="selectEnglish()" [routerLink]="" tabindex="8"><img src="assets/Flag_of_Britain.svg" [className]="(enSelected)? 'disabledFlag':'flag'" height="20px" alt="In english {{ (enSelected)? 'selected':'' }}" /></a>
<a (click)="selectSwedish()" [routerLink]="" tabindex="8" ><img src="assets/Flag_of_Sweden.svg" [className]="(svSelected)? 'disabledFlag':'flag'" height="20px" alt="Svenska {{ (svSelected)? 'vald':'' }}" /></a>
</div>
......@@ -11,7 +11,11 @@
border: none;
}
/* http://flexboxfroggy.com/ */
#flagBond {
.locales {
display: flex;
height: 100%;
a {
align-self: center;
}
}
import {MenuGroup} from '../models/menu-group.model';
import {OldMoyaComponent} from '../../modules/old-moya/old-moya.component';
import {Permissions} from '../../shared/models/permissions.model';
/**
* Created by tuukka on 13/05/17.
*/
// We use GeneratePermissionFunction to make permission check functions
// This way if we can check permission names on software intialize, and will get exception if there is typo
const _GP = Permissions.GeneratePermissionFunction;
export class MENU {
static ADMIN: MenuGroup[] = [
{
'name': 'shop',
'items': [
{ 'path': '/old/admin', 'name': 'products', 'params': {p: 'product/list.jsf'} }
{ 'path': '/old/admin', 'name': 'products', 'perm': _GP({'VIPLIST': 'ADMIN'}), 'params': {p: 'product/list.jsf'} }
]}
];
......@@ -20,9 +27,9 @@ export class MENU {
{
'name': 'users',
'items': [
{ 'path': '/old/info', 'name': 'listusers', 'params': {p: 'useradmin/list.jsf'} },
{ 'path': '/viplist/info', 'name': 'viplist'},
{ 'path': '/usersearch/info', 'name': 'usersearch'}
{ 'path': '/old/info', 'name': 'listusers', 'perm': _GP({'SITE': 'INFO'}), 'params': {p: 'useradmin/list.jsf'} },
{ 'path': '/viplist/info', 'perm': _GP({'VIPLIST': 'INFO'}), 'name': 'viplist'}
]}
];
......@@ -30,54 +37,53 @@ export class MENU {
{
'name': 'frontpage',
'items': [
{ 'path': '/index', 'name': 'frontpage' },
{ 'path': '/index', 'name': 'frontpage', 'perm': _GP({'SITE': 'USER'}) },
]},
{
'name': 'event',
'items': [
{ 'path': '/old/user', 'name': 'poll', 'params': {p: 'poll/index.jsf'} },
{ 'path': '/old/user', 'name': 'invite', 'params': {p: 'user/invite.jsf'} },
{ 'path': '/old/user', 'name': 'poll', 'perm': _GP({'POLL': 'USER'}), 'params': {p: 'poll/index.jsf'} },
{ 'path': '/old/user', 'name': 'invite', 'perm': _GP({'INVITE': 'USER'}), 'params': {p: 'user/invite.jsf'} },
]},
{
'name': 'shop',
'items': [
{ 'path': '/old/user', 'name': 'buytickets', 'params': {p: 'shop/createBill.jsf'} },
{ 'path': '/old/user', 'name': 'reserveplaces', 'params': {p: 'neomap/reserve.jsf'} },
{ 'path': '/old/user', 'name': 'changeplaces', 'params': {p: 'neomap/moveplaces.jsf'} },
{ 'path': '/old/user', 'name': 'foodorders', 'params': {p: 'foodwave/list.jsf'} },
{ 'path': '/old/user', 'name': 'ownorders', 'params': {p: 'bill/list.jsf'} },
{ 'path': '/old/user', 'name': 'accountevents', 'params': {p: 'user/accountEvents.jsf'} },
{ 'path': '/old/user', 'name': 'buytickets', 'perm': _GP({'SHOP': 'USER'}), 'params': {p: 'shop/createBill.jsf'} },
{ 'path': '/old/user', 'name': 'reserveplaces', 'perm': _GP({'MAP': 'USER'}), 'params': {p: 'neomap/reserve.jsf'} },
{ 'path': '/old/user', 'name': 'changeplaces', 'perm': _GP({'MAP': 'USER'}), 'params': {p: 'neomap/moveplaces.jsf'} },
{ 'path': '/old/user', 'name': 'foodorders', 'perm': _GP({'FOOD': 'USER'}), 'params': {p: 'foodwave/list.jsf'} },
{ 'path': '/old/user', 'name': 'ownorders', 'perm': _GP({'SHOP': 'USER', 'CREDIT': 'USER'}), 'params': {p: 'bill/list.jsf'} },
]},
{
'name': 'computerplaces',
'items': [
{ 'path': '/old/user', 'name': 'ownplaces', 'params': {p: 'place/myGroups.jsf'} },
{ 'path': '/old/user', 'name': 'placemap', 'params': {p: 'neomap/view.jsf'} },
{ 'path': '/old/user', 'name': 'ownplaces', 'perm': _GP({'MAP': 'USER'}), 'params': {p: 'place/myGroups.jsf'} },
{ 'path': '/old/user', 'name': 'placemap', 'perm': _GP({'MAP': 'USER'}), 'params': {p: 'neomap/view.jsf'} },
]},
{
'name': 'competitions',
'items': [
{ 'path': '/old/user', 'name': 'compos', 'params': {p: 'voting/compolist.jsf'} },
{ 'path': '/old/user', 'name': 'myentries', 'params': {p: 'voting/myEntries.jsf'} },
{ 'path': '/old/user', 'name': 'compos', 'perm': _GP({'COMPO': 'USER'}), 'params': {p: 'voting/compolist.jsf'} },
{ 'path': '/old/user', 'name': 'myentries', 'perm': _GP({'COMPO': 'USER'}), 'params': {p: 'voting/myEntries.jsf'} },
]},
{
'name': 'profile',
'items': [
{ 'path': '/old/user', 'name': 'myinformation', 'params': {p: 'user/edit.jsf'} },
{ 'path': '/old/user', 'name': 'requestrole', 'params': {p: 'orgrole/requestRole.jsf'} },
{ 'path': '/old/user', 'name': 'changepassword', 'params': {p: 'user/changePassword.jsf'} },
{ 'path': '/old/user', 'name': 'setgameids', 'params': {p: 'user/gameids.jsf'} },
{ 'path': '/old/user', 'name': 'myinformation', 'perm': _GP({'USER': 'USER'}), 'params': {p: 'user/edit.jsf'} },
{ 'path': '/old/user', 'name': 'requestrole', 'perm': _GP({'USER': 'USER'}), 'params': {p: 'orgrole/requestRole.jsf'} },
{ 'path': '/old/user', 'name': 'changepassword', 'perm': _GP({'USER': 'USER'}), 'params': {p: 'user/changePassword.jsf'} },
{ 'path': '/old/user', 'name': 'setgameids', 'perm': _GP({'USER': 'USER'}), 'params': {p: 'user/gameids.jsf'} },
]},
{
'name': 'tournaments',
'items': [
{ 'path': '/old/user', 'name': 'tournamentslist', 'params': {p: 'tournaments/index.jsf'} },
{ 'path': '/old/user', 'name': 'myparticipations', 'params': {p: 'tournaments/myparticipations.jsf'} },
{ 'path': '/old/user', 'name': 'tournamentslist', 'perm': _GP({'TOURNAMENT': 'USER'}), 'params': {p: 'tournaments/index.jsf'} },
{ 'path': '/old/user', 'name': 'myparticipations', 'perm': _GP({'TOURNAMENT': 'USER'}), 'params': {p: 'tournaments/myparticipations.jsf'} },
]},
{
'name': 'lecturesandcourses',
'items': [
{ 'path': '/old/user', 'name': 'participate', 'params': {p: 'lectures/viewLectures.jsf'} },
{ 'path': '/old/user', 'name': 'participate', 'perm': _GP({'LECTURE': 'USER'}), 'params': {p: 'lectures/viewLectures.jsf'} },
]},
];
......
import {Permissions} from '../../shared/models/permissions.model';
export interface MenuItem {
path: string;
name: string;
perm: (permissions: Permissions) => boolean;
params?: any;
}
<nav id="topNav">
<a class="topNavItem" id="userTopNav" routerLink="/index" routerLinkActive="active" translate>menu.top.user</a>
<a class="topNavItem" id="infoTopNav" routerLink="/old/info" routerLinkActive="active" [queryParams]="{p: 'useradmin/list.jsf'}" translate>menu.top.info</a>
<a class="topNavItem" id="adminTopNav" routerLink="/old/admin" routerLinkActive="active" [queryParams]="{p: 'product/list.jsf'}" translate>menu.top.admin</a>
<a tabindex="9" class="topNavItem" id="userTopNav" routerLink="/index" routerLinkActive="active" translate>menu.top.user</a>
<a tabindex="9" class="topNavItem" id="infoTopNav" routerLink="/old/info" routerLinkActive="active" [queryParams]="{p: 'useradmin/list.jsf'}" translate>menu.top.info</a>
<a tabindex="9" class="topNavItem" id="adminTopNav" routerLink="/old/admin" routerLinkActive="active" [queryParams]="{p: 'product/list.jsf'}" translate>menu.top.admin</a>
</nav>
<moya-login *ngIf="!loggedIn"></moya-login>
<span tabindex="8" *ngIf="loggedIn">{{currentUser.firstname}} ({{currentUser.nick}}) {{currentUser.lastname}}</span>
<button tabindex="8" *ngIf="loggedIn" mat-button color="primary" (click)="logout()" >{{ "login.logout" | translate}}</button>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginButtonComponent } from './login-button.component';
describe('LoginButtonComponent', () => {
let component: LoginButtonComponent;
let fixture: ComponentFixture<LoginButtonComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginButtonComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginButtonComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import {MatDialog, MatDialogConfig} from '@angular/material';
import {LoginComponent} from '../login-component/login.component';
import {Router} from '@angular/router';
import {SessionServiceService} from '../../../shared/services/session-service.service';
import {EventUser} from '../../../shared/models/event-user.model';
import {User} from '../../../shared/models/user.model';
@Component({
selector: 'moya-login-button',
templateUrl: './login-button.component.html',
styleUrls: ['./login-button.component.css']
})
export class LoginButtonComponent implements OnInit {
loggedIn = false;
currentUser: User = null;
constructor(private router: Router, private dialog: MatDialog, private sessionService: SessionServiceService) {
this.sessionService.getCurrentUser().subscribe((user: User) => {
this.loggedIn = !user.isAnonymous();
this.currentUser = user;
});
}
logout() {
this.sessionService.logout();
}
ngOnInit() { }
openLoginDialog(): void {
const conf = new MatDialogConfig();
conf.autoFocus = true;
/*conf.height = "800px";
conf.width = "500px";*/
const dialogRef = this.dialog.open(LoginComponent, conf);
dialogRef.componentInstance.loginEvent.subscribe((success) => {
if (success) {
dialogRef.close();
} else {
dialogRef.close();
this.router.navigateByUrl('/login');
}
});
}
}
.loginarea {
display: flex;
}
.loginarea > mat-form-field {
margin-left: 10px;
}
<form class="loginarea" #logindetails="ngForm" (ngSubmit)="login(logindetails)" >
<mat-form-field>
<input tabindex="8" matInput ngModel name="username" placeholder="{{ 'user.username' | translate }}">
</mat-form-field>
<mat-form-field>
<input tabindex="8" matInput type="password" ngModel name="password" placeholder="{{ 'user.password' | translate }}">
</mat-form-field>
<button tabindex="8" type="submit" mat-button color="primary" >{{ 'login.login' | translate }}</button>
</form>
<span *ngIf="loginError" class="loginError">{{ 'login.error'|translate }}</span>
<span *ngIf="loginOk" class="loginOk"> {{ 'login.ok'|translate }}</span>
<span *ngIf="submitting" class="loginInProgress"> {{ 'login.submitting'|translate }}</span>
import {Component, EventEmitter, OnInit, Output} from '@angular/core';
import {TranslatePipe} from '@ngx-translate/core';
import {SessionServiceService} from '../../../shared/services/session-service.service';
import {FormGroup, NgForm} from '@angular/forms';
@Component({
selector: 'moya-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css'],
providers: [TranslatePipe]
})
export class LoginComponent implements OnInit {
@Output()
loginEvent = new EventEmitter<boolean>();
loginError = false;
loginOk = false;
submitting = false;
constructor(private sessionService: SessionServiceService) { }
ngOnInit() {
this.loginError = this.loginOk = this.submitting = false;
}
login(userDetails: NgForm) {
this.loginError = this.loginOk = false;
this.submitting = true;
this.sessionService.doLogin(userDetails.value.username, userDetails.value.password).subscribe((x) => {
this.loginError = this.loginOk = this.submitting = false;
this.loginOk = true;
this.loginEvent.emit(true);
}, (error) => {
this.loginError = this.loginOk = this.submitting = false;
this.loginError = true;
this.loginEvent.emit(false);
});
}
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginDialogComponent } from './login-dialog.component';
describe('LoginDialogComponent', () => {
let component: LoginDialogComponent;
let fixture: ComponentFixture<LoginDialogComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginDialogComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, NgZone, OnInit} from '@angular/core';
import {MatDialog, MatDialogConfig} from '@angular/material';
import {LoginComponent} from '../login-component/login.component';
import {Router} from '@angular/router';
@Component({
selector: 'moya-login-dialog',
templateUrl: './login-dialog.component.html',
styleUrls: ['./login-dialog.component.css']
})
export class LoginDialogComponent implements OnInit {
constructor(private router: Router, private dialog: MatDialog) { }
ngOnInit() {
}
}
<mat-card>
<mat-card-title>
<mat-toolbar color="primary">
<h1>{{ 'login.title' | translate }}</h1>
</mat-toolbar>
</mat-card-title>
<mat-card-content>
<moya-login (loginEvent)="onLoginEvent($event)" ></moya-login>
</mat-card-content>
</mat-card>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginPageComponent } from './login-page.component';
describe('LoginPageComponent', () => {
let component: LoginPageComponent;
let fixture: ComponentFixture<LoginPageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginPageComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import {Router} from '@angular/router';
@Component({
selector: 'moya-login-page',
templateUrl: './login-page.component.html',
styleUrls: ['./login-page.component.css']
})
export class LoginPageComponent implements OnInit {
constructor(private router: Router) { }
ngOnInit() {
}
onLoginEvent(success: boolean): void {
if (success) {
this.router.navigateByUrl('/');
}
}
}
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LoginComponent } from './login.component';
const routes: Routes = [
{ path: '', component: LoginComponent }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class LoginRoutingModule { }
<div>Login works!</div>
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'moya-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
import { LoginRoutingModule } from './login-routing.module';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {LoginComponent} from './login.component';
import {
MatButtonModule,
MatCardModule,
MatDialogModule,
MatFormFieldModule,
MatInputModule,
MatMenuModule,
MatToolbarModule
} from '@angular/material';
import {TranslateModule} from '@ngx-translate/core';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {FormsModule} from '@angular/forms';
import { LoginPageComponent } from './login-page/login-page.component';
import {LoginComponent} from './login-component/login.component';
import { LoginDialogComponent } from './login-dialog/login-dialog.component';
import { LoginButtonComponent } from './login-button/login-button.component';
@NgModule({
imports: [
CommonModule,
LoginRoutingModule
MatFormFieldModule,
MatInputModule,
MatButtonModule,
TranslateModule,
BrowserAnimationsModule,
FormsModule,
MatCardModule,
MatDialogModule,
MatToolbarModule,
MatMenuModule
],
declarations: [LoginComponent]
entryComponents: [LoginComponent],
declarations: [LoginComponent, LoginPageComponent, LoginDialogComponent, LoginButtonComponent],
exports: [
LoginComponent,
LoginButtonComponent
]
})
export class LoginModule { }
<iframe id="oldMoyaFrame" [src]="frameUrl | safe" [height]="height" #iframe (load)="changeUrl()" frameborder="0"></iframe>
<iframe id="oldMoyaFrame" [src]="frameUrl | safe" [height]="height" #iframe (load)="changeUrl()" frameborder="0" ></iframe>
import {map} from 'rxjs/operators';
import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import {Component, OnInit, ViewChild, ElementRef, ApplicationRef, ChangeDetectorRef} from '@angular/core';
import {Location, LocationStrategy, HashLocationStrategy, PathLocationStrategy} from '@angular/common';
import {NavigationExtras, ActivatedRoute, Router} from '@angular/router';
import {angularCoreEnv} from '@angular/core/src/render3/jit/environment';
@Component({
selector: 'moya-old',
......@@ -22,10 +23,9 @@ export class OldMoyaComponent implements OnInit {
constructor(private location: Location, private route: ActivatedRoute) {
constructor(private location: Location, private route: ActivatedRoute, private changeDetectionRef: ChangeDetectorRef) {
route.queryParamMap.pipe(map(a => a.get('p'))).subscribe(x => {
console.log(x);
if (x) {
this.frameUrl = '/MoyaWeb/' + x.replace('::', '?');
}
......@@ -42,12 +42,18 @@ export class OldMoyaComponent implements OnInit {
this.location.replaceState(this.location.path(false).split('?', 1)[0] + '?p=' + tmpFrameUrl[1].replace('?', '::') );
}
this.iframe.nativeElement.contentWindow.onscroll = (() => {this.fixHeight(this); });
console.log(this.iframe.nativeElement.contentWindow.document.body.height);
this.height = (this.iframe.nativeElement.contentWindow.document.body.scrollHeight + 50) + 'px';
this.fixHeight(this);
}
fixHeight(self) {
console.log(self.iframe.nativeElement.contentWindow.document.body.scrollHeight);
if (self.height !== (self.iframe.nativeElement.contentWindow.document.body.scrollHeight + 50) + 'px') {
self.height = (self.iframe.nativeElement.contentWindow.document.body.scrollHeight + 50) + 'px';
this.changeDetectionRef.detectChanges();
}
}
......
......@@ -2,7 +2,7 @@ import {InMemoryCache} from 'apollo-cache-inmemory';
import {HttpLink} from 'apollo-angular-link-http';
export const MOYA_BASE_URL = '/MoyaWeb/';
export const MOYA_REST_URL = MOYA_BASE_URL + 'rest/';
export const MOYA_REST_URL = MOYA_BASE_URL + 'rest';
export function createApollo(httpLink: HttpLink) {
......
/**
* Created by tuukka on 22/09/18.
*/
class PermissionFields {
USER = false;
INFO = false;
ADMIN = false;
}
export class Permissions {
static referencePermission = new Permissions();
VIPLIST: PermissionFields = new PermissionFields();
BILLING: PermissionFields = new PermissionFields();
SITE: PermissionFields = new PermissionFields();
SHOP: PermissionFields = new PermissionFields();
POLL: PermissionFields = new PermissionFields();
INVITE: PermissionFields = new PermissionFields();
MAP: PermissionFields = new PermissionFields();
COMPO: PermissionFields = new PermissionFields();
FOOD: PermissionFields = new PermissionFields();
USER: PermissionFields = new PermissionFields();
TOURNAMENT: PermissionFields = new PermissionFields();
CREDIT: PermissionFields = new PermissionFields();
LECTURE: PermissionFields = new PermissionFields();
constructor() {
}
/**
*
* Returns function, which will check user permissions against desired roles.
*
* Returned function can be used as a permission checked.
*
*
* @param requiredPermissions Permissions which are allowed. Example: {VIPLIST: 'ADMIN', BILLING: 'INFO'}
* @constructor
*/
static GeneratePermissionFunction(allowedPermissions: any): ((permissions: Permissions) => boolean) {
// no defines, always true
if (!allowedPermissions) {
return function (permissions: Permissions): boolean {
return true;
};
}
// Check if all permissions exists
// Think this as a runtime typecheck
for (const target in allowedPermissions) {
if (!allowedPermissions.hasOwnProperty(target)) {
continue;
}
if (!(target in Permissions.referencePermission)) {
throw Error('Invalid permission key ' + target);
}
switch (allowedPermissions[target]) {
default:
throw Error('Invalid permission role ' + allowedPermissions[target]);
case 'ADMIN':
case 'INFO':
case 'USER':
}
}
return function (permissions: Permissions): boolean {
// Go trought allowed permissions. If permissions contains allowed permission, return true.
for (const target in allowedPermissions) {
if (permissions[target][allowedPermissions[target]]) {
return true;
}
}
// no permission found, return false
return false;
};
}
}
......@@ -16,6 +16,8 @@ export class User {
static fragments = gql`
fragment userPrimitives on User {
address
......@@ -56,5 +58,18 @@ export class User {
town: string;
constructor() { }
public static NewUser(user) {
if (user === null) {
return new User();
}
return Object.assign(new User(), user);
}
public isAnonymous(): boolean {
return !this.login || this.login === 'anonymous';
}
}
import { TestBed, inject } from '@angular/core/testing';
import { SessionServiceService } from './session-service.service';
describe('SessionServiceService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [SessionServiceService]
});
});
it('should be created', inject([SessionServiceService], (service: SessionServiceService) => {
expect(service).toBeTruthy();
}));
});
import {Injectable} from '@angular/core';
import gql from 'graphql-tag';
import {Permissions} from '../models/permissions.model';
import {Apollo, QueryRef} from 'apollo-angular';
import {BehaviorSubject, Observable, Observer, Subject} from 'rxjs';
import {MOYA_BASE_URL, MOYA_REST_URL} from '../config/moya.config';
import {map, share} from 'rxjs/operators';
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';
import {EventUser} from '../models/event-user.model';
import {User} from '../models/user.model';
import {Router} from '@angular/router';
const __REFRESH_TIME_MINUTES = 5;
export const Q_USER_PERMISSIONS = gql`
{
user(id: 0) {
permissions {
feature
admin
info
user
}
}
}
`;
class ApiPermissionType {
feature: string;
admin: boolean;
user: boolean;
info: boolean;
}
class ApiPermissionRoot {
currentuser: { permissions: ApiPermissionType[] };
}
@Injectable({
providedIn: 'root'
})
export class SessionServiceService {
private permissionsObservable: Observable<Permissions>;
private permissionsApollo: QueryRef<ApiPermissionRoot>;
private currentuserSubject: BehaviorSubject<User>;
constructor(private apollo: Apollo, private http: HttpClient, private router: Router) {
// Permissions apollo call.
// rewrite permissions from API -format to moya-angular -format
//
// From:
// [{feature: 'VIPLIST', user:true, admin: false, info:false}, {feature: 'USERLIST', user:true, admin: false, info:false}]
// To:
// {VIPLIST: {USER: true, ADMIN: false, INFO: false}, USERLIST: {USER: true, ADMIN: false, INFO: false}}
this.permissionsApollo = this.apollo.watchQuery<ApiPermissionRoot>({query: Q_USER_PERMISSIONS, pollInterval: __REFRESH_TIME_MINUTES * 60000});
this.permissionsObservable = this.permissionsApollo.valueChanges
.pipe(map((perms) => perms.data.currentuser.permissions
.reduce((permObject, perm) => (permObject[perm.feature] = {'ADMIN': perm.admin, 'USER': perm.user, 'INFO': perm.info}), {}) as Permissions));
// make subject to share the current user.
// This way we can later on update it to everyone who is listening this beautiful observable
this.currentuserSubject = new BehaviorSubject<User>(null);
this.updateCurrentUser();
}
public getPermissions(): Observable<Permissions> {
return this.permissionsObservable;
}
/**
* Returns observable with current user. If we are not logged in, current user is Anonymous.
*
* If there is any change of user status, new user is automatically pushed into this observable.
*
* TODO: Change User to EventUser, and use prober currentUser -api
*/
public getCurrentUser(): Observable<User> {
return this.currentuserSubject.pipe(map(user => User.NewUser(user)));
}
/**
* Request update of current user.
*
* This will push currently logged in user into currentUser -observable.
*/
public updateCurrentUser() {
this.http.get<User>(MOYA_REST_URL + '/v2/user/current').subscribe(
(currentUser) => {
this.currentuserSubject.next(currentUser);
});
}
/**
* Check loginerrors from returned observable
*
* @param username
* @param password
*/
public doLogin(username: string, password: string): Observable<Object> {
let params = new HttpParams();
params = params.set('username', username);
params = params.set('password', password);
const apolloObservable = this.http.post(MOYA_REST_URL + '/user/auth', params, {
headers: new HttpHeaders()
.set('Content-Type', 'application/x-www-form-urlencoded')
})
.pipe(map(x => x as boolean),
share()
);
apolloObservable.subscribe((x) => {
this.permissionsApollo.refetch();
this.updateCurrentUser();
});
return apolloObservable;
}
public logout() {
// Do login with unexiting user and password, this does also logout
let params = new HttpParams();
params = params.set('username', 'anonymous');
params = params.set('password', 'notValidPassword');
const apolloObservable = this.http.post(MOYA_REST_URL + '/user/auth', params, {
headers: new HttpHeaders()
.set('Content-Type', 'application/x-www-form-urlencoded')
}).subscribe(x => x, x => x, () => {
this.permissionsApollo.refetch();
this.updateCurrentUser();
this.router.navigateByUrl('/');
});
}
}
......@@ -23,5 +23,4 @@ export class UserService {
return this.cacheService.cacheObservable('moya:UserService', path,
this.http.get<User>(path).pipe(tap(v => {console.log('getting user outside of cache', path); })));
}
}
......@@ -2,6 +2,22 @@
"test": {
"user": "User"
},
"user": {
"username": "Username",
"password": "Password"
},
"login": {
"login": "Login",
"error": "Error on login",
"ok": "Login success",
"title": "Login",
"submitting": "Loggin in",
"logout": "Logout"
},
"general": {
"yes": "Yes",
"no": "No",
......
......@@ -2,6 +2,21 @@
"test": {
"user": "Käyttäjä"
},
"user": {
"username": "Käyttäjänimi",
"password": "Salasana"
},
"login": {
"login": "Kirjaudu",
"error": "Virhe sisäänkirjautumisessa",
"ok": "Kirjautuminen onnistui",
"title": "Kirjaudu",
"submitting": "Kirjaudutaan",
"logout": "Uloskirjaudu"
},
"general": {
"yes": "Kyllä",
"no": "Ei",
......
......@@ -2,6 +2,24 @@
"test": {
"user": "Användare"
},
"user": {
"username": "Användarnamn",
"password": "Lösenord"
},
"login": {
"login": "Logga in",
"error": "Fel vid inloggning",
"ok": "Logga in framgång",
"title": "Titel",
"submitting": "Skicka in",
"logout": "Logga ut"
},
"general": {
"yes": "Ja",
"no": "Nej",
......
......@@ -2,6 +2,7 @@
@import url('https://fonts.googleapis.com/css?family=Roboto');
@import '~@angular/material/prebuilt-themes/indigo-pink.css';
/*@import '~@angular/material/prebuilt-themes/deeppurple-amber.css';*/
body {
background-color: #f2f3f9;
......
{
"/MoyaWeb": {
"target": "https://localproxy.test.moya.fi",
"secure": true,
"headers": {"host":"localproxy.test.moya.fi" }
}
}
......@@ -13,6 +13,7 @@
],
"lib": [
"es2018",
"es2016",
"dom",
"esnext.asynciterable"
],
......
......@@ -29,10 +29,7 @@ import javax.ejb.Local;
import fi.codecrew.moya.bortal.views.BillSummary;
import fi.codecrew.moya.entitysearch.BillSearchQuery;
import fi.codecrew.moya.exceptions.BillException;
import fi.codecrew.moya.model.Bill;
import fi.codecrew.moya.model.EventUser;
import fi.codecrew.moya.model.FoodWave;
import fi.codecrew.moya.model.Product;
import fi.codecrew.moya.model.*;
import fi.codecrew.moya.utilities.SearchResult;
@Local
......@@ -50,7 +47,7 @@ public interface BillBeanLocal {
Collection<BillSummary> getBillLineSummary();
Bill markPaid(Bill bill, Calendar when, boolean useCredits) throws BillException;
Bill markPaid(Bill bill, Calendar when, boolean useCredits, Bill.BillPaymentSource paymentSource) throws BillException;
void getPdfBillStream(Bill bill, OutputStream ostream);
......@@ -70,4 +67,5 @@ public interface BillBeanLocal {
SearchResult<Bill> findUsers(BillSearchQuery q);
List<BillLine> getBillLinesForProduct(Integer productId);
}
......@@ -22,18 +22,27 @@ import java.io.Serializable;
import java.math.BigDecimal;
import fi.codecrew.moya.model.BillLine;
import fi.codecrew.moya.model.Product;
public class BillSummary implements Serializable{
public class BillSummary implements Serializable {
public BillSummary(String name) {
this.name = name;
}
private final Product product;
private final String name;
private String name;
private BigDecimal active = BigDecimal.ZERO;
private BigDecimal paid = BigDecimal.ZERO;
private BigDecimal expired = BigDecimal.ZERO;
public BillSummary(BillLine line) {
this.name = line.getName();
this.product = line.getLineProduct();
}
public BillSummary(Product product) {
this.name = product.getName();
this.product = product;
}
public String getName() {
return name;
}
......@@ -61,4 +70,7 @@ public class BillSummary implements Serializable{
return expired;
}
public Product getProduct() {
return product;
}
}
......@@ -36,6 +36,8 @@ public class UserSearchQuery extends SearchQuery {
private boolean onlyThisEvent = true;
private boolean placeAssoc = false;
private boolean usersWithUnusedCodes = false;
private boolean usersWithUnusedPlaceslots = false;
private DatabaseHasCompare requireImage = DatabaseHasCompare.NONE;
private List<CardState> hasCardState = new ArrayList<>();
......@@ -46,8 +48,7 @@ public class UserSearchQuery extends SearchQuery {
super();
}
public CardState[] getCardStates()
{
public CardState[] getCardStates() {
return CardState.values();
}
......@@ -55,8 +56,7 @@ public class UserSearchQuery extends SearchQuery {
super(page, pagesize, sort, search, direction);
}
public DatabaseValueCompare[] getAccountCompareValues()
{
public DatabaseValueCompare[] getAccountCompareValues() {
return DatabaseValueCompare.values();
}
......@@ -124,4 +124,11 @@ public class UserSearchQuery extends SearchQuery {
this.hasCardState = hasCardState;
}
public boolean isUsersWithUnusedPlaceslots() {
return usersWithUnusedPlaceslots;
}
public void setUsersWithUnusedPlaceslots(boolean usersWithUnusedPlaceslots) {
this.usersWithUnusedPlaceslots = usersWithUnusedPlaceslots;
}
}
......@@ -20,11 +20,7 @@ package fi.codecrew.moya.beans;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.*;
import javax.annotation.security.DeclareRoles;
import javax.annotation.security.RolesAllowed;
......@@ -55,7 +51,7 @@ import fi.codecrew.moya.utilities.moyamessage.MoyaEventType;
*/
@Stateless
@LocalBean
@DeclareRoles({ BillPermission.S_CREATE_BILL, BillPermission.S_READ_ALL, BillPermission.S_VIEW_OWN, BillPermission.S_WRITE_ALL, SpecialPermission.S_USER, })
@DeclareRoles({BillPermission.S_CREATE_BILL, BillPermission.S_READ_ALL, BillPermission.S_VIEW_OWN, BillPermission.S_WRITE_ALL, SpecialPermission.S_USER,})
public class BillBean implements BillBeanLocal {
private static final Logger logger = LoggerFactory.getLogger(BillBean.class);
......@@ -113,7 +109,7 @@ public class BillBean implements BillBeanLocal {
logger.debug("bill {} user {}", bill, currentuser);
if (bill != null && !currentuser.equals(bill.getUser())
&& !permbean.hasPermission(BillPermission.READ_ALL)) {
&& !permbean.hasPermission(BillPermission.READ_ALL)) {
bill = null;
}
return bill;
......@@ -218,10 +214,14 @@ public class BillBean implements BillBeanLocal {
@Override
@RolesAllowed(BillPermission.S_READ_ALL)
public Collection<BillSummary> getBillLineSummary() {
Collection<BillSummary> ret = billLineFacade.getLineSummary(eventbean
.getCurrentEvent());
return ret;
Map<String, BillSummary> retmap = new HashMap<String, BillSummary>();
for (BillLine bl : billLineFacade.getAllLines(eventbean.getCurrentEvent())) {
retmap.computeIfAbsent(bl.getName(), (k) -> new BillSummary(bl)).addLine(bl);
}
return retmap.values();
}
/**
......@@ -237,9 +237,9 @@ public class BillBean implements BillBeanLocal {
@Override
@RolesAllowed({ BillPermission.S_WRITE_ALL })
public Bill markPaid(Bill bill, Calendar when, boolean useCredits) throws BillException {
public Bill markPaid(Bill bill, Calendar when, boolean useCredits, Bill.BillPaymentSource paymentSource) throws BillException {
return billpbean.markPaid(bill, when, useCredits);
return billpbean.markPaid(bill, when, useCredits, paymentSource);
}
@Override
......@@ -276,7 +276,7 @@ public class BillBean implements BillBeanLocal {
// we will mark them immediately as paid.
if (bill.getTotalPrice().compareTo(BigDecimal.ZERO) == 0
&& eventbean.getPropertyBoolean(LanEventPropertyKey.ALLOW_FREE_BILLS)) {
bill = billpbean.markPaid(bill, Calendar.getInstance(), false);
bill = billpbean.markPaid(bill, Calendar.getInstance(), false, Bill.BillPaymentSource.UNKNOWN);
}
return bill;
......@@ -297,6 +297,12 @@ public class BillBean implements BillBeanLocal {
// Not a place product.
continue;
}
// If product is created when bill is paid, there is no need to create a placeslot for the product.
if (prod.getProductFlags().contains(ProductFlag.CREATE_NEW_PLACE_WHEN_BOUGHT)) {
continue;
}
int count = bl.getQuantity().intValue();
Date now = new Date();
......@@ -398,4 +404,11 @@ public class BillBean implements BillBeanLocal {
return billFacade.find(q);
}
@Override
@RolesAllowed(BillPermission.S_READ_ALL)
public List<BillLine> getBillLinesForProduct(Integer productId) {
return billLineFacade.getLinesForProduct(productBean.findById(productId));
}
}
......@@ -52,7 +52,7 @@ public class BillPBean {
private static final Logger logger = LoggerFactory.getLogger(BillPBean.class);
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
private Bill markPaidSafeTransaction(Bill bill, Calendar when, boolean useCredits) throws BillException {
private Bill markPaidSafeTransaction(Bill bill, Calendar when, boolean useCredits, Bill.BillPaymentSource paymentSource) throws BillException {
bill = billFacade.reload(bill);
if (bill.getAccountEvent() != null || bill.getPaidDate() != null) {
......@@ -87,13 +87,14 @@ public class BillPBean {
}
bill.setPaidDate(when.getTime());
bill.setPaymentSource(paymentSource);
return bill;
}
public Bill markPaid(Bill bill, Calendar when, boolean useCredits) throws BillException {
public Bill markPaid(Bill bill, Calendar when, boolean useCredits, Bill.BillPaymentSource paymentSource) throws BillException {
bill = markPaidSafeTransaction(bill, when, useCredits);
bill = markPaidSafeTransaction(bill, when, useCredits, paymentSource);
EventUser user = bill.getUser();
......
......@@ -640,8 +640,16 @@ public class BootstrapBean implements BootstrapBeanLocal {
"ALTER TABLE event_role_features ADD CONSTRAINT FK_event_role_features_role_id FOREIGN KEY (role_id) REFERENCES roles (id)"
});
dbUpdates.add(new String[]{
"ALTER TABLE bills ADD COLUMN payment_source TEXT NOT NULL default 'UNKNOWN'",
});
dbUpdates.add(new String[]{
"UPDATE place_slots SET used = now() WHERE used is null AND product_id in (SELECT product_id FROM product_productflags WHERE productflags = 'CREATE_NEW_PLACE_WHEN_BOUGHT')"
});
}
public BootstrapBean() {
}
......
......@@ -380,7 +380,7 @@ public class CheckoutFiBean implements CheckoutFiBeanLocal {
if (bill.getAccountEvent() == null
&& bill.getPaidDate() == null) {
logger.info("Trying to mark bill {} paid", bill);
billpbean.markPaid(bill, Calendar.getInstance(), false);
billpbean.markPaid(bill, Calendar.getInstance(), false, Bill.BillPaymentSource.CHECKOUT);
logbean.sendMessage(MoyaEventType.BANKING_MESSAGE, permbean.getCurrentUser(), "Marking bill paid from checkout. Received bill status ", statusInt, " for bill ", bill, " stamp ", stamp, " payment: ", payment, " reference ", reference);
} else {
logbean.sendMessage(MoyaEventType.BANKING_MESSAGE, permbean.getCurrentUser(), "Bill already marked paid: ", bill, " status ", status, " stamp ", stamp, " payment ", payment);
......
......@@ -400,7 +400,6 @@ public class PlaceBean implements PlaceBeanLocal {
if (prod.getProductFlags().contains(ProductFlag.CREATE_NEW_PLACE_WHEN_BOUGHT)) {
freePlace = new Place();
freePlace.setProduct(prod);
freePlace.setProvidesRole(prod.getProvides());
freePlace.setName("-");
placeFacade.create(freePlace);
......
......@@ -281,6 +281,20 @@ public class UserBean implements UserBeanLocal {
}
}
// double check that we are handling only roles for this event
// It is not trivial to create checks in db to verify validity of event matches for example:
// place -> provided_role -> event_id == place -> product -> event_id
// We do have checks in place in code for these, but this is a precaution for
// accidental and malicious db changes.
for (Role r: checkedRoles.keySet()){
if(!r.getEvent().equals(event)){
logger.warn("Tried to return roles for a wrong event. Role {}, expected event {}, got event: {}", r, r.getEvent(), event);
throw new EJBAccessException("Trying to return roles for a wrong avent!");
}
}
// currentEventuser = u;
// currentEventuserRoles = new ArrayList<Role>(checkedRoles);
// logger.debug("Returning parsed eventUser roles for user {}: {} ", u,
......@@ -929,9 +943,11 @@ public class UserBean implements UserBeanLocal {
newSearchList.addAll(returnUsers.getResults());
returnUsers = new SearchResult<EventUser>(newSearchList, (long) newSearchList.size());
return new SearchResult<EventUser>(newSearchList, (long) newSearchList.size());
}
} else if (searchQuery.isUsersWithUnusedCodes()) {
if (searchQuery.isUsersWithUnusedCodes()) {
List<EventUser> newSearchList = new ArrayList<EventUser>();
List<GroupMembership> groupMembers = new ArrayList<GroupMembership>();
groupMembers = gmfacade.findAllWithoutUser();
......
......@@ -135,7 +135,7 @@ public class VerkkomaksutFiBean implements VerkkomaksutFiBeanLocal {
} else if (bill.getAccountEvent() == null
&& bill.getPaidDate() == null
&& (SvmReturnType.NOTIFICATION.equals(type) || SvmReturnType.SUCCESS.equals(type))) {
billpbean.markPaid(bill, Calendar.getInstance(), false);
billpbean.markPaid(bill, Calendar.getInstance(), false, Bill.BillPaymentSource.PAYTRAIL);
logbean.sendMessage(MoyaEventType.BANKING_MESSAGE, permbean.getCurrentUser(), "Validated order number ", orderNumber, " bill ", bill == null ? "null" : bill.toString(), " with authcode: ", authcode);
ret = true;
......
/*
* Copyright Codecrew Ry
*
*
* All rights reserved.
*
* This license applies to any software containing a notice placed by the
* copyright holder. Such software is herein referred to as the Software.
* This license covers modification, distribution and use of the Software.
*
* Any distribution and use in source and binary forms, with or without
* modification is not permitted without explicit written permission from the
* copyright owner.
*
* A non-exclusive royalty-free right is granted to the copyright owner of the
* Software to use, modify and distribute all modifications to the Software in
* future versions of the Software.
*
*
* This license applies to any software containing a notice placed by the
* copyright holder. Such software is herein referred to as the Software.
* This license covers modification, distribution and use of the Software.
*
* Any distribution and use in source and binary forms, with or without
* modification is not permitted without explicit written permission from the
* copyright owner.
*
* A non-exclusive royalty-free right is granted to the copyright owner of the
* Software to use, modify and distribute all modifications to the Software in
* future versions of the Software.
*
*/
package fi.codecrew.moya.facade;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import fi.codecrew.moya.beans.EventBeanLocal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -43,37 +42,25 @@ import fi.codecrew.moya.model.Product;
@Stateless
@LocalBean
public class BillLineFacade extends IntegerPkGenericFacade<BillLine> {
@SuppressWarnings("unused")
private static final Logger logger = LoggerFactory.getLogger(BillLineFacade.class);
@EJB
private EventBeanLocal eventbean;
public BillLineFacade() {
super(BillLine.class);
}
// @Override
// public void create(BillLine entity) {
// super.create(entity);
// billfacade.evict(entity.getBill());
// }
public Collection<BillSummary> getLineSummary(LanEvent event) {
public List<BillLine> getAllLines(LanEvent event) {
CriteriaBuilder cb = getEm().getCriteriaBuilder();
CriteriaQuery<BillLine> cq = cb.createQuery(BillLine.class);
Root<BillLine> root = cq.from(BillLine.class);
cq.where(cb.equal(root.get(BillLine_.bill).get(Bill_.event), event));
List<BillLine> lines = getEm().createQuery(cq).getResultList();
Map<String, BillSummary> retmap = new HashMap<String, BillSummary>();
return getEm().createQuery(cq).getResultList();
for (BillLine bl : lines) {
if (!retmap.containsKey(bl.getName())) {
retmap.put(bl.getName(), new BillSummary(bl.getName()));
}
retmap.get(bl.getName()).addLine(bl);
}
return retmap.values();
}
public BillSummary getLineSummary(Product list, LanEvent event) {
......@@ -81,10 +68,10 @@ public class BillLineFacade extends IntegerPkGenericFacade<BillLine> {
CriteriaQuery<BillLine> cq = cb.createQuery(BillLine.class);
Root<BillLine> root = cq.from(BillLine.class);
cq.where(cb.equal(root.get(BillLine_.bill).get(Bill_.event), event),
cb.equal(root.get(BillLine_.lineProduct), list));
cb.equal(root.get(BillLine_.lineProduct), list));
List<BillLine> lines = getEm().createQuery(cq).getResultList();
BillSummary ret = new BillSummary(list.getName());
BillSummary ret = new BillSummary(list);
for (BillLine bl : lines) {
ret.addLine(bl);
......@@ -92,17 +79,18 @@ public class BillLineFacade extends IntegerPkGenericFacade<BillLine> {
return ret;
}
public BillSummary getLineSummary(Product list, LanEvent event, EventUser user) {
public BillSummary getLineSummary(Product product, LanEvent event, EventUser user) {
CriteriaBuilder cb = getEm().getCriteriaBuilder();
CriteriaQuery<BillLine> cq = cb.createQuery(BillLine.class);
Root<BillLine> root = cq.from(BillLine.class);
cq.where(cb.equal(root.get(BillLine_.bill).get(Bill_.event), event),
cb.equal(root.get(BillLine_.lineProduct), list),
cb.equal(root.get(BillLine_.bill).get(Bill_.user), user)
);
cb.equal(root.get(BillLine_.lineProduct), product),
cb.equal(root.get(BillLine_.bill).get(Bill_.user), user)
);
List<BillLine> lines = getEm().createQuery(cq).getResultList();
BillSummary ret = new BillSummary(list.getName());
BillSummary ret = new BillSummary(product);
for (BillLine bl : lines) {
ret.addLine(bl);
......@@ -110,4 +98,16 @@ public class BillLineFacade extends IntegerPkGenericFacade<BillLine> {
return ret;
}
public List<BillLine> getLinesForProduct(Product product) {
if (product == null) {
return null;
}
CriteriaBuilder cb = getEm().getCriteriaBuilder();
CriteriaQuery<BillLine> cq = cb.createQuery(BillLine.class);
Root<BillLine> root = cq.from(BillLine.class);
cq.where(cb.equal(root.get(BillLine_.lineProduct), product),
cb.equal(root.get(BillLine_.bill).get(Bill_.event), eventbean.getCurrentEvent()));
return getEm().createQuery(cq).getResultList();
}
}
......@@ -33,6 +33,7 @@ import javax.persistence.criteria.From;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.SingularAttribute;
import fi.codecrew.moya.facade.callbacks.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -40,14 +41,6 @@ import fi.codecrew.moya.beans.EventBeanLocal;
import fi.codecrew.moya.entitysearch.UserSearchQuery;
import fi.codecrew.moya.enums.DatabaseHasCompare;
import fi.codecrew.moya.enums.DatabaseValueCompare;
import fi.codecrew.moya.facade.callbacks.EventLimiter;
import fi.codecrew.moya.facade.callbacks.EventUserAccountSaldoPredicate;
import fi.codecrew.moya.facade.callbacks.EventUserCardStateFilter;
import fi.codecrew.moya.facade.callbacks.EventUserPlacegroupPredicate;
import fi.codecrew.moya.facade.callbacks.EventUserRolefilter;
import fi.codecrew.moya.facade.callbacks.EventUserSearchPredicate;
import fi.codecrew.moya.facade.callbacks.EventuserToUserWrapper;
import fi.codecrew.moya.facade.callbacks.OrderCallback;
import fi.codecrew.moya.model.EventUser;
import fi.codecrew.moya.model.EventUser_;
import fi.codecrew.moya.model.LanEvent;
......@@ -145,6 +138,7 @@ public class EventUserFacade extends IntegerPkGenericFacade<EventUser> {
CriteriaQuery<EventUser> listCQuery = cb.createQuery(EventUser.class);
CriteriaQuery<Long> countCQuery = cb.createQuery(Long.class);
final LanEvent currentEvent = eventBean.getCurrentEvent();
if (query.getSort() != null)
{
......@@ -159,7 +153,7 @@ public class EventUserFacade extends IntegerPkGenericFacade<EventUser> {
callbacks.add(new OrderCallback<EventUser>(false, EventUser_.id));
}
callbacks.add(new EventLimiter(eventBean.getCurrentEvent()));
callbacks.add(new EventLimiter(currentEvent));
if (query.getSearch() != null && !query.getSearch().isEmpty()) {
callbacks.add(new EventUserSearchPredicate(query.getSearch(), UserFacade.getAttrlist()));
......@@ -181,6 +175,11 @@ public class EventUserFacade extends IntegerPkGenericFacade<EventUser> {
{
callbacks.add(new EventUserAccountSaldoPredicate(query.getAccountSaldo(), query.getAccountSaldoCompare()));
}
if(query.isUsersWithUnusedPlaceslots()){
callbacks.add(new UnusedPlaceSlotFilter(currentEvent));
}
if (query.getRequireImage() != null && !query.getRequireImage().equals(DatabaseHasCompare.NONE))
{
......
......@@ -57,7 +57,9 @@ public class RoleFacade extends IntegerPkGenericFacade<Role> {
CriteriaBuilder cb = getEm().getCriteriaBuilder();
CriteriaQuery<Role> cq = cb.createQuery(Role.class);
Root<Role> root = cq.from(Role.class);
cq.where(cb.equal(root.get(Role_.event), user.getEvent()), cb.isMember(user, root.get(Role_.users)));
cq.where(
cb.equal(root.get(Role_.event), user.getEvent()),
cb.isMember(user, root.get(Role_.users)));
return getEm().createQuery(cq).getResultList();
}
......
package fi.codecrew.moya.facade.callbacks;
import fi.codecrew.moya.model.*;
import javax.persistence.criteria.*;
import java.util.List;
public class UnusedPlaceSlotFilter implements fi.codecrew.moya.utilities.jpa.FacadeCallback<fi.codecrew.moya.model.EventUser> {
private final LanEvent currentEvent;
public UnusedPlaceSlotFilter(LanEvent currentEvent) {
this.currentEvent = currentEvent;
}
@Override
public void exec(CriteriaBuilder cb, CriteriaQuery<?> cq, Path<EventUser> root, List<Predicate> predicates, boolean isFullQuery) {
Subquery<Integer> subq = cq.subquery(Integer.class);
Root<PlaceSlot> slotRoot = subq.from(PlaceSlot.class);
Path<Bill> bill = slotRoot.get(PlaceSlot_.bill);
subq.select(bill.get(Bill_.user).get(EventUser_.id));
subq.where(
cb.isNotNull(bill.get(Bill_.paidDate)),
cb.equal(bill.get(Bill_.event), currentEvent),
cb.isNull(slotRoot.get(PlaceSlot_.used))
);
predicates.add(root.get(EventUser_.id).in(subq));
}
}
......@@ -25,20 +25,7 @@ import java.util.Calendar;
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import javax.persistence.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -54,6 +41,16 @@ import fi.codecrew.moya.utilities.BillUtils;
@Table(name = "bills", uniqueConstraints = { @UniqueConstraint(columnNames = { Bill.EVENT_ID_COLUMN, "bill_number" }) })
public class Bill extends GenericEntity {
public enum BillPaymentSource{
UNKNOWN,
PAYTRAIL,
CHECKOUT,
// expected to be paid by cash
CASH,
// Marked paid by admin ui
ADMIN,
CREDITS,
}
/**
* <p>
* With how many decimals we want to calculate prices, discounts, etc. If we
......@@ -176,6 +173,11 @@ public class Bill extends GenericEntity {
@JoinColumn(updatable = false, name = "eventuser_id")
private EventUser user;
@Column(nullable=false, name = "payment_source")
@Enumerated(EnumType.STRING)
private BillPaymentSource paymentSource = BillPaymentSource.UNKNOWN;
@SuppressWarnings("unused")
private static final Logger logger = LoggerFactory.getLogger(Bill.class);
......@@ -504,4 +506,12 @@ public class Bill extends GenericEntity {
return summary;
}
public BillPaymentSource getPaymentSource() {
return paymentSource;
}
public void setPaymentSource(BillPaymentSource paymentSource) {
this.paymentSource = paymentSource;
}
}
......@@ -292,6 +292,10 @@ public class Place extends GenericEntity implements Comparable<Place> {
return (u.equals(getCurrentUser()) && getGroup() == null);
}
public boolean isReservedButNotLocked(){
return getGroup() == null && getCurrentUser() != null;
}
public void setBuyable(boolean buyable) {
this.buyable = buyable;
}
......
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:bills="http://java.sun.com/jsf/composite/tools/bills"
xmlns:f="http://java.sun.com/jsf/core">
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core" xmlns:p="http://primefaces.org/ui">
<h:body>
<ui:composition template="#{sessionHandler.template}">
<f:metadata>
<f:event type="preRenderView" listener="#{billListView.initSummaryView}" />
<f:event type="preRenderView" listener="#{billListView.initSummaryView}"/>
</f:metadata>
<ui:define name="content">
<h:form id="billsummary" styleClass="moya_datatable2">
<h:dataTable border="0" id="billSummary" value="#{billListView.billsummary}" var="sumline" styleClass="moya_datatable2">
<h:column>
<f:facet name="header">
<h:outputText value="${i18n['product.name']}" />
</f:facet>
<h:outputText value="#{sumline.name}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="${i18n['product.boughtTotal']}" />
</f:facet>
<p:dataTable border="0" id="billSummary" value="#{billListView.billsummary}" var="sumline"
styleClass="moya_datatable2">
<p:column headerText="${i18n['product.name']}">
<h:outputText value="#{sumline.name}"/>
</p:column>
<p:column headerText="${i18n['product.name']}">
<p:link rendered="#{!empty sumline.product}" outcome="/bill/productSummary"
value="#{sumline.product.name}">
<f:param name="productId" value="#{sumline.product.id}"/>
</p:link>
</p:column>
<p:column headerText="${i18n['product.boughtTotal']}">
<h:outputText value="#{sumline.active}">
<f:convertNumber />
<f:convertNumber/>
</h:outputText>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="${i18n['product.paid']}" />
</f:facet>
</p:column>
<p:column headerText="${i18n['product.paid']}">
<h:outputText value="#{sumline.paid}">
<f:convertNumber />
<f:convertNumber/>
</h:outputText>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="${i18n['product.expired']}" />
</f:facet>
</p:column>
<p:column headerText="${i18n['product.expired']}">
<h:outputText value="#{sumline.expired}">
<f:convertNumber />
<f:convertNumber/>
</h:outputText>
</h:column>
</h:dataTable>
</p:column>
</p:dataTable>
</h:form>
</ui:define>
</ui:composition>
......
......@@ -29,6 +29,9 @@
<h:outputLabel for="paidDate" value="#{i18n['bill.paidDate']}:" />
<p:calendar id="paidDate" value="#{billEditView.bill.paidDate}" pattern="#{sessionHandler.datetimeFormat}" timeZone="#{sessionHandler.timezone}" />
<h:outputLabel for="paymentSource" value="#{i18n['bill.paymentSource']}:" />
<h:outputText id="paymentSource" value="#{billEditView.bill.paymentSource}" />
<h:outputLabel for="billnr" value="#{i18n['bill.billNumber']}:" />
<h:inputText id="billnr" value="#{billEditView.bill.billNumber}" />
......
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core" xmlns:p="http://primefaces.org/ui">
<h:body>
<ui:composition template="#{sessionHandler.template}">
<f:metadata>
<f:viewParam value="#{productSummaryView.productId}" name="productId"/>
<f:event type="preRenderView" listener="#{productSummaryView.initProductSummary}"/>
</f:metadata>
<ui:define name="content">
<h:form id="billsummary" styleClass="moya_datatable2">
<p:dataTable id="lines" value="#{productSummaryView.billLines}" var="line" widgetVar="lineTable">
<p:column headerText="${i18n['bill.id_str']}" sortBy="#{line.bill.id }" sortable="true"
filterBy="#{line.bill.paid}" filterMatchMode="exact">
<f:facet name="filter">
<p:selectOneButton onchange="PF('lineTable').filter()" styleClass="custom-filter">
<f:converter converterId="javax.faces.Boolean"/>
<f:selectItem itemLabel="#{i18n['bill.filter_all']}" itemValue=""/>
<f:selectItem itemLabel="#{i18n['bill.isPaid']}" itemValue="true"/>
<f:selectItem itemLabel="#{i18n['bill.isNotPaid']}" itemValue="false"/>
</p:selectOneButton>
</f:facet>
<p:link outcome="/bill/edit" value="#{line.bill.id} (#{line.bill.paid?i18n['bill.isPaid']:i18n['bill.isNotPaid']})">
<f:param name="billid" value="#{line.bill.id}"/>
</p:link>
</p:column>
<p:column sortable="true" sortBy="#{line.bill.paidDate}" headerText="#{i18n['bill.paidDate']}">
<h:outputText value="#{line.bill.paidDate}">
<f:convertDateTime pattern="#{sessionHandler.datetimeFormat}"
timeZone="#{sessionHandler.timezone}"/>
</h:outputText>
</p:column>
<p:column headerText="Purchaser" sortBy="#{line.bill.user.wholeName}" sortable="true">
<p:link outcome="/user/edit" value="#{line.bill.user.wholeName}">
<f:param name="userid" value="#{line.bill.user.user.id}"/>
</p:link>
</p:column>
<p:column headerText="${i18n['billLine.product']}" sortBy="#{line.name}" sortable="true">
<p:link outcome="/product/edit" value="#{line.name}">
<f:param name="productid" value="#{line.lineProduct.id}"/>
</p:link>
</p:column>
<p:column headerText="${i18n['billLine.quantity']}" sortBy="#{line.quantity}" sortable="true">
<h:outputText value="#{line.quantity}">
<f:convertNumber/>
</h:outputText>
</p:column>
</p:dataTable>
</h:form>
</ui:define>
</ui:composition>
</h:body>
</html>
\ No newline at end of file
<!DOCTYPE html
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html" xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:users="http://java.sun.com/jsf/composite/tools/user" xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui"
>
<h:body>
<ui:composition template="#{sessionHandler.template}">
......@@ -43,27 +45,43 @@
<h:outputLabel value="#{i18n['place.product']}:" />
<h:outputText value="#{placeView.place.product.name}" />
<h:outputLabel value="#{i18n['place.buyable']}" />
<h:selectBooleanCheckbox value="#{placeView.place.buyable}"/>
<h:outputLabel value="#{i18n['place.description']}:" />
<h:inputTextarea value="#{placeView.place.description}" />
<h:outputText value="#{i18n['place.assocUser']}" />
<p:link value="#{placeView.place.currentUser.wholeName}" outcome="/useradmin/edit" >
<f:param name="userid" value="#{placeView.place.currentUser.user.id}"/>
</p:link>
</h:panelGrid>
<h:commandButton id="commitbtn" action="#{placeView.save()}" value="#{i18n['place.commit']}" />
<p:commandButton ajax="false" id="commitbtn" action="#{placeView.save()}" value="#{i18n['place.commit']}" />
<h:commandButton rendered="#{placeView.place.isTaken() and (empty placeView.place.group)}"
<p:commandButton ajax="false" rendered="#{placeView.place.isTaken() and (empty placeView.place.group)}"
action="#{placeView.releasePlace()}" value="#{i18n['place.release']}"
/>
<h:commandButton rendered="#{placeView.place.isTaken() and (!empty placeView.place.group)}"
<p:commandButton ajax="false" rendered="#{placeView.place.isTaken() and (!empty placeView.place.group)}"
action="#{placeView.removePlaceFromGroup()}" value="#{i18n['place.groupremove']}"
onclick="return confirm('#{i18n['place.groupremove.confirm']}')"
/>
</h:form>
<h2>#{i18n['editplace.placegroup.title']}</h2>
<ui:fragment rendered="#{placeView.place.reservedButNotLocked}">
<h2><h:outputText value="#{i18n['place.warning.reservedNotLocked']}" /></h2>
<h:form>
<p:commandButton action="#{placeView.lockPlace()}" value="#{i18n['place.lockPlace']}" ajax="false"/>
<p:commandButton action="#{placeView.releasePlace()}" value="#{i18n['place.releasePlace']}" ajax="false"/>
</h:form>
</ui:fragment>
<h2>#{i18n['editplace.placegroup.title']}</h2>
<h:panelGrid columns="2" rendered="#{empty placeView.place.group}">
<h:form id="usersearchform">
<h:outputText value="#{i18n['user.searchUser']}" />
......@@ -79,31 +97,31 @@
<f:facet name="header">
<h:outputText value="Id" />
</f:facet>
<h:outputText value="#{user.id}" />
<h:outputText value="#{user.user.id}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{i18n['user.login']}" />
</f:facet>
<h:outputText value="#{user.login}" />
<h:outputText value="#{user.user.login}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{i18n['user.nick']}" />
</f:facet>
<h:outputText value="#{user.nick}" />
<h:outputText value="#{user.user.nick}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{i18n['user.firstNames']}" />
</f:facet>
<h:outputText value="#{user.firstnames}" />
<h:outputText value="#{user.user.firstnames}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{i18n['user.lastName']}" />
</f:facet>
<h:outputText value="#{user.lastname}"/>
<h:outputText value="#{user.user.lastname}"/>
</h:column>
<h:column>
<h:commandButton action="#{placeView.reserveForUser()}" value="#{i18n['place.reserveForUser']}" />
......
......@@ -35,43 +35,45 @@
<f:selectItems var="role" itemLabel="#{role.name}" value="#{cardView.templatesWithNull}"/>
</h:selectOneMenu>
<h:outputText value="#{i18n['role.permissions']}"></h:outputText>
<p:dataTable style="width: auto" value="#{roleView.role.features}" var="feature">
<p:column headerText="#{i18n['feature.name']}">
<h:outputText value="#{feature.feature.name()}"/>
</p:column>
<p:column headerText="#{i18n['feature.user_permission']}">
<p:selectBooleanCheckbox value="#{feature.user}" />
</p:column>
<p:column headerText="#{i18n['feature.info_permission']}">
<p:selectBooleanCheckbox value="#{feature.info}" />
</p:column>
<p:column headerText="#{i18n['feature.admin_permission']}">
<p:selectBooleanCheckbox value="#{feature.admin}" />
</p:column>
</p:dataTable>
</h:panelGrid>
<h:commandButton rendered="#{roleView.canWriteRoles}" id="saverole" value="#{i18n['roleView.save']}" action="#{roleView.save}"/>
</h:panelGrid>
<p:fieldset id="featuregroup" legend="#{i18n['role.features']}" collapsed="true" toggleable="true">
<p:dataTable id="featuretable" tableStyle="width: auto;" value="#{roleView.role.features}" var="feature">
<p:column headerText="#{i18n['feature.name']}">
<h:outputText value="#{feature.feature.name()}"/>
</p:column>
<p:column headerText="#{i18n['feature.user_permission']}">
<p:selectBooleanCheckbox value="#{feature.user}"/>
</p:column>
<p:column headerText="#{i18n['feature.info_permission']}">
<p:selectBooleanCheckbox value="#{feature.info}"/>
</p:column>
<p:column headerText="#{i18n['feature.admin_permission']}">
<p:selectBooleanCheckbox value="#{feature.admin}"/>
</p:column>
</p:dataTable>
</p:fieldset>
<p:commandButton update="roleform" rendered="#{roleView.canWriteRoles}" id="saverole" value="#{i18n['roleView.save']}" action="#{roleView.save}"/>
</h:form>
</p>
<h:form id="addmember">
<h:outputText value="#{i18n['roleView.adduser']}"/>
<p:autoComplete id="useradder" value="#{roleView.addableUser}" completeMethod="#{roleView.searchUser}" converter="#{userConverter}" var="usr" itemValue="#{usr}"
itemLabel="#{usr.shortUserDescriptor}">
<p:ajax onerror="location.reload(true);" update=":editor:addmember,:editor:memberlist" event="itemSelect" listener="#{roleView.addUser}"/>
</p:autoComplete>
</h:form>
<p:fieldset id="userfield" legend="#{i18n['roleView.members']}" toggleable="true" collapsed="true">
<h:form id="addmember">
<h:outputText value="#{i18n['roleView.adduser']}"/>
<p:autoComplete id="useradder" value="#{roleView.addableUser}" completeMethod="#{roleView.searchUser}"
converter="#{eventUserConverter}" var="usr" itemValue="#{usr}" itemLabel="#{usr.shortUserDescriptor}">
<p:ajax onerror="location.reload(true);" update=":editor:addmember,:editor:memberlist" event="itemSelect" listener="#{roleView.addUser}"/>
</p:autoComplete>
</h:form>
<p:dataTable id="memberlist" value="#{roleView.role.users}" var="usr">
<p:column>
<h:outputText value="#{usr.login}"/>
......
......@@ -57,3 +57,8 @@
100% { opacity: 1.0; }
}
.ui-filter-column .ui-column-customfilter .custom-filter {
width: 100%;
box-sizing: border-box;
}
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:p="http://primefaces.org/ui"
>
<h:body>
<ui:composition template="#{sessionHandler.template}">
<f:metadata>
<f:event type="preRenderView" listener="#{cardHolderizeView.init}"/>
</f:metadata>
<ui:define name="content">
<h2><h:outputText value="#{i18n['holderize.header']}" /></h2>
<h:form id="holderform">
<h2><h:outputText value="#{i18n['holderize.parametersHeader']}"/></h2>
<h:panelGrid columns="2">
<h:outputLabel value="#{i18n['holderize.pageColumns']}"/>
<p:inputText value="#{cardHolderizeView.cols}"/>
<h:outputLabel value="#{i18n['holderize.pageRows']}"/>
<p:inputText value="#{cardHolderizeView.rows}"/>
</h:panelGrid>
<h2><h:outputText value="#{i18n['holderize.nextCodeHeader']}" /></h2>
<p:focus for="code"/>
<h:panelGrid columns="2">
<h:outputLabel value="#{i18n['holderize.folderName']}"/>
<p:inputText value="#{cardHolderizeView.currentFolder}"/>
<h:outputLabel value="#{i18n['holderize.page']}"/>
<p:inputText value="#{cardHolderizeView.currentPage}">
<f:convertNumber maxFractionDigits="0" minFractionDigits="0"/>
</p:inputText>
<h:outputLabel value="#{i18n['holderize.column']}"/>
<p:inputText value="#{cardHolderizeView.currentCol}">
<f:convertNumber maxFractionDigits="0" minFractionDigits="0"/>
</p:inputText>
<h:outputLabel value="#{i18n['holderize.row']}"/>
<p:inputText value="#{cardHolderizeView.currentRow}">
<f:convertNumber maxFractionDigits="0" minFractionDigits="0"/>
</p:inputText>
<h:outputLabel value="#{i18n['holderize.barcode']}"/>
<p:inputText id="code" value="#{cardHolderizeView.code}">
</p:inputText>
</h:panelGrid>
<p:commandButton update="holderform" action="#{cardHolderizeView.holderizeCard()}" value="Holderize"/>
<h3>History</h3>
<p:dataTable value="#{cardHolderizeView.queue}" var="card">
<p:column>
<p:link outcome="/useradmin/edit" value="#{card.user.wholeName}">
<f:param name="userid" value="#{card.user.user.id}"/>
</p:link>
</p:column>
<p:column>
<h:outputText value="#{card.meta.getJsonObject('card-filing').getString('cardplace')}"/>
</p:column>
</p:dataTable>
</h:form>
</ui:define>
</ui:composition>
</h:body>
</html>
\ No newline at end of file
......@@ -69,6 +69,10 @@
<p:selectBooleanCheckbox id="userswithunusedcodes" value="#{userSearchView.searchQuery.usersWithUnusedCodes}" />
<p:outputLabel for="userswithunusedcodes" value="#{i18n['userlist.usersWithUnusedCodes']}" />
<br />
<p:selectBooleanCheckbox id="usersWithUnusedPlaceslots" value="#{userSearchView.searchQuery.usersWithUnusedPlaceslots}" />
<p:outputLabel for="usersWithUnusedPlaceslots" value="#{i18n['userlist.usersWithUnusedPlaceslots']}" />
</div>
<br />
......
......@@ -283,7 +283,7 @@ public class MoyaGraphQLServlet extends HttpServlet {
b.addField(Role_.name);
b.addField(Role_.parents);
b.addField(Role_.children);
b.addField(Place_.meta);
b.addField(Role_.meta);
}
......
......@@ -3,15 +3,19 @@ package fi.codecrew.moya.rest.v3;
import fi.codecrew.moya.beans.EventBeanLocal;
import fi.codecrew.moya.beans.UserBeanLocal;
import fi.codecrew.moya.handler.SessionHandler;
import fi.codecrew.moya.handler.SessionStore;
import fi.codecrew.moya.rest.v3.pojo.LocalePojoV3;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
import javax.ejb.EJB;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Locale;
@RequestScoped
@Path("/v3/locale")
......@@ -27,6 +31,9 @@ public class LocaleRestViewV3 {
@EJB
private EventBeanLocal eventBean;
@Inject
private SessionStore sessionStore;
@GET
@Path("/")
public Response getCurrentLocale() {
......@@ -36,11 +43,22 @@ public class LocaleRestViewV3 {
@POST
@Path("/")
public Response setCurrentLocale(LocalePojoV3 locale) {
if(userBean.setUsersLocale(locale.userLocale)) {
public Response setCurrentLocale(LocalePojoV3 localePojo) {
Locale locale = Locale.forLanguageTag(localePojo.userLocale);
if(userBean.setUsersLocale(localePojo.userLocale)) {
sessionStore.setLocale(locale);
return Response.ok().build();
}
return Response.status(420).build();
}
public SessionStore getSessionStore() {
return sessionStore;
}
public void setSessionStore(SessionStore sessionStore) {
this.sessionStore = sessionStore;
}
}
......@@ -19,6 +19,7 @@
package fi.codecrew.moya.web.cdiview.map;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
......@@ -30,6 +31,7 @@ import javax.faces.model.ListDataModel;
import javax.inject.Inject;
import javax.inject.Named;
import fi.codecrew.moya.entitysearch.UserSearchQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -76,7 +78,7 @@ public class PlaceView extends GenericCDIView {
private EventMap currentMap;
private String searchuser;
private ListDataModel<User> userlist;
private ListDataModel<EventUser> userlist;
public boolean canEdit() {
return permbean.hasPermission(MapPermission.MANAGE_MAPS);
......@@ -170,7 +172,7 @@ public class PlaceView extends GenericCDIView {
public String reserveForUser() {
try {
EventUser user = userbean.getEventUser(userlist.getRowData(), true);
EventUser user = userlist.getRowData();
if (placebean.reservePlace(place, user)) {
PlaceGroup newgroup = placebean.buySelectedPlaces(user);
for (Place p : newgroup.getPlaces()) {
......@@ -193,7 +195,8 @@ public class PlaceView extends GenericCDIView {
public String searchUser() {
super.beginConversation();
userlist = new ListDataModel<User>(userbean.getUsers(new SearchQuery(0, 20, null, searchuser, QuerySortOrder.DESCENDING)).getResults());
userlist = new ListDataModel<EventUser>(userbean.getThisEventsUsers(new UserSearchQuery(0, 20, null, searchuser, QuerySortOrder.DESCENDING)).getResults());
return null;
}
......@@ -206,6 +209,8 @@ public class PlaceView extends GenericCDIView {
if (!placebean.releasePlace(place)) {
logger.debug("Error releasing place.");
}
place = placebean.find(place.getId());
return null;
}
......@@ -223,6 +228,17 @@ public class PlaceView extends GenericCDIView {
return null;
}
public String lockPlace(){
place = placebean.find(place.getId());
if(!place.isReservedButNotLocked()){
super.addFaceMessage("place.alreadyLocked");
return null;
}
PlaceGroup pgroup = placebean.buySelectedPlaces(Arrays.asList(place), place.getCurrentUser());
place = pgroup.getPlaces().stream().filter(p -> p.equals(place)).findFirst().get();
return null;
}
public void setSearchuser(String searchuser) {
this.searchuser = searchuser;
}
......@@ -231,11 +247,11 @@ public class PlaceView extends GenericCDIView {
return searchuser;
}
public void setUserlist(ListDataModel<User> userlist) {
public void setUserlist(ListDataModel<EventUser> userlist) {
this.userlist = userlist;
}
public ListDataModel<User> getUserlist() {
public ListDataModel<EventUser> getUserlist() {
return userlist;
}
......
......@@ -145,7 +145,7 @@ public class BillEditView extends GenericCDIView {
public void buyWithCredits() {
try {
bill = billbean.markPaid(bill, Calendar.getInstance(), true);
bill = billbean.markPaid(bill, Calendar.getInstance(), true, Bill.BillPaymentSource.ADMIN);
} catch (BillException e) {
throw new RuntimeException(e);
// TODO: error handling
......
......@@ -151,7 +151,7 @@ public class BillListView extends GenericCDIView {
try {
Bill bill = lazyBillList.getRowData();
billbean.markPaid(bill, Calendar.getInstance(), false);
billbean.markPaid(bill, Calendar.getInstance(), false, Bill.BillPaymentSource.ADMIN);
} catch (BillException x) {
throw new RuntimeException(x);
}
......
......@@ -172,7 +172,7 @@ public class FoodWaveFoodView extends GenericCDIView {
Bill b = createBillFromShoppingcart();
try {
billBean.markPaid(b, Calendar.getInstance(), true);
billBean.markPaid(b, Calendar.getInstance(), true, Bill.BillPaymentSource.CREDITS);
} catch (BillException e) {
throw new RuntimeException(e);
}
......@@ -186,7 +186,7 @@ public class FoodWaveFoodView extends GenericCDIView {
Bill b = createBillFromShoppingcart();
try {
billBean.markPaid(b, Calendar.getInstance(), false);
billBean.markPaid(b, Calendar.getInstance(), false, Bill.BillPaymentSource.CASH);
} catch (BillException e) {
throw new RuntimeException(e);
}
......
......@@ -265,7 +265,7 @@ public class FoodWaveView extends GenericCDIView {
Bill b = bills.getRowData();
try {
b = billbean.markPaid(b, Calendar.getInstance(), false);
b = billbean.markPaid(b, Calendar.getInstance(), false, Bill.BillPaymentSource.CASH);
} catch (BillException e) {
throw new RuntimeException(e);
}
......@@ -288,7 +288,7 @@ public class FoodWaveView extends GenericCDIView {
Bill b = bills.getRowData();
try {
b = billbean.markPaid(b, Calendar.getInstance(), true);
b = billbean.markPaid(b, Calendar.getInstance(), true, Bill.BillPaymentSource.CREDITS);
} catch (BillException e) {
throw new RuntimeException(e);
}
......
/*
* Copyright Codecrew Ry
*
* All rights reserved.
*
* This license applies to any software containing a notice placed by the
* copyright holder. Such software is herein referred to as the Software.
* This license covers modification, distribution and use of the Software.
*
* Any distribution and use in source and binary forms, with or without
* modification is not permitted without explicit written permission from the
* copyright owner.
*
* A non-exclusive royalty-free right is granted to the copyright owner of the
* Software to use, modify and distribute all modifications to the Software in
* future versions of the Software.
*
*/
package fi.codecrew.moya.web.cdiview.shop;
import fi.codecrew.moya.beans.BillBeanLocal;
import fi.codecrew.moya.beans.EventBeanLocal;
import fi.codecrew.moya.beans.VerkkomaksutFiBeanLocal;
import fi.codecrew.moya.bortal.views.BillSummary;
import fi.codecrew.moya.entitysearch.BillSearchQuery;
import fi.codecrew.moya.enums.apps.BillPermission;
import fi.codecrew.moya.exceptions.BillException;
import fi.codecrew.moya.model.Bill;
import fi.codecrew.moya.model.BillLine;
import fi.codecrew.moya.model.EventUser;
import fi.codecrew.moya.utilities.SearchQuery.QuerySortOrder;
import fi.codecrew.moya.utilities.SearchResult;
import fi.codecrew.moya.utilities.jsf.MessageHelper;
import fi.codecrew.moya.web.annotations.SelectedUser;
import fi.codecrew.moya.web.cdiview.GenericCDIView;
import fi.codecrew.moya.web.helpers.LazyEntityDataModel;
import org.primefaces.model.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ejb.EJB;
import javax.enterprise.context.ConversationScoped;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
@Named
@ConversationScoped
public class ProductSummaryView extends GenericCDIView {
private static final long serialVersionUID = -4668803787903428161L;
private static final Logger logger = LoggerFactory.getLogger(ProductSummaryView.class);
@EJB
private transient BillBeanLocal billbean;
private Integer productId;
private List<BillLine> billLines;
public void initProductSummary() {
if (super.requirePermissions(BillPermission.READ_ALL) && billLines == null) {
beginConversation();
logger.info("Initializing lazyList");
billLines = billbean.getBillLinesForProduct(productId);
}
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public List<BillLine> getBillLines() {
return billLines;
}
public void setBillLines(List<BillLine> billLines) {
this.billLines = billLines;
}
}
/*
* Copyright Codecrew Ry
*
* All rights reserved.
*
* This license applies to any software containing a notice placed by the
* copyright holder. Such software is herein referred to as the Software.
* This license covers modification, distribution and use of the Software.
*
* Any distribution and use in source and binary forms, with or without
* modification is not permitted without explicit written permission from the
* copyright owner.
*
* A non-exclusive royalty-free right is granted to the copyright owner of the
* Software to use, modify and distribute all modifications to the Software in
* future versions of the Software.
*
*/
package fi.codecrew.moya.web.cdiview.user;
import fi.codecrew.moya.beans.*;
import fi.codecrew.moya.enums.CardState;
import fi.codecrew.moya.enums.apps.UserPermission;
import fi.codecrew.moya.model.*;
import fi.codecrew.moya.rest.meta.v1.PrintedCardRestViewV1;
import fi.codecrew.moya.util.MassPrintResult;
import fi.codecrew.moya.utilities.jsf.MessageHelper;
import fi.codecrew.moya.web.annotations.LoggedIn;
import fi.codecrew.moya.web.annotations.SelectedUser;
import fi.codecrew.moya.web.cdiview.GenericCDIView;
import fi.codecrew.moya.web.cdiview.reader.ReaderView;
import fi.codecrew.moya.web.helper.LayoutView;
import org.primefaces.event.CaptureEvent;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.CroppedImage;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;
import org.primefaces.model.UploadedFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ejb.EJB;
import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.enterprise.inject.Produces;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;
import javax.inject.Inject;
import javax.inject.Named;
import javax.json.*;
import javax.json.stream.JsonGenerator;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Named()
@ConversationScoped
public class CardHolderizeView extends GenericCDIView {
@EJB
private CardPrintBeanLocal cardbean;
@EJB
private CardTemplateBeanLocal tplbean;
@EJB
private UserBeanLocal userbean;
@EJB
private ReaderBeanLocal readerbean;
private int cols = 1;
private int rows = 5;
private String code;
private String currentFolder = "A";
private int currentPage = 1;
private int currentCol = 1;
private int currentRow = 1;
private static final Logger logger = LoggerFactory.getLogger(CardHolderizeView.class);
private List<PrintedCard> queue;
private int QUE_HISTORY_SIZE = 10;
public void init() {
if (queue == null) {
queue = new ArrayList<>();
super.beginConversation();
}
}
public void holderizeCard() {
logger.warn("Holderizing card {}", code);
ReaderEvent event = readerbean.checkCode(code);
logger.warn("got event from card code {}, card {}", code, event.getPrintedCard());
if (event == null || event.getPrintedCard() == null) {
super.addFaceMessage("printecard.not-found");
return;
}
StringBuilder sb = new StringBuilder();
sb.append(currentFolder);
sb.append(" / ").append(currentPage);
sb.append(" / ").append(currentCol);
sb.append(" / ").append(currentRow);
PrintedCard card = event.getPrintedCard();
JsonObjectBuilder metaBuilder = Json.createObjectBuilder();
JsonObject meta = card.getMeta();
JsonObjectBuilder filingBuilder = Json.createObjectBuilder();
if (meta != null) {
meta.entrySet().forEach(e -> metaBuilder.add(e.getKey(), e.getValue()));
JsonObject filingObj = meta.getJsonObject("card-filing");
if (filingObj != null) {
filingObj.entrySet().forEach(e -> filingBuilder.add(e.getKey(), e.getValue()));
}
}
filingBuilder.add("cardplace", sb.toString());
metaBuilder.add("card-filing", filingBuilder);
card.setMeta(metaBuilder.build());
PrintedCard saved = tplbean.saveCard(card);
queue.add(0, saved);
while (queue.size() > QUE_HISTORY_SIZE) {
queue.remove(queue.size() - 1);
}
increaseSlot();
code = "";
}
private void increaseSlot() {
if (++currentRow > rows) {
currentRow = 1;
if (++currentCol > cols) {
currentCol = 1;
++currentPage;
}
}
}
public int getCols() {
return cols;
}
public void setCols(int cols) {
this.cols = cols;
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
this.rows = rows;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getCurrentFolder() {
return currentFolder;
}
public void setCurrentFolder(String currentFolder) {
this.currentFolder = currentFolder;
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getCurrentCol() {
return currentCol;
}
public void setCurrentCol(int currentCol) {
this.currentCol = currentCol;
}
public int getCurrentRow() {
return currentRow;
}
public void setCurrentRow(int currentRow) {
this.currentRow = currentRow;
}
public List<PrintedCard> getQueue() {
return queue;
}
public void setQueue(List<PrintedCard> queue) {
this.queue = queue;
}
}
......@@ -61,7 +61,7 @@ public class RoleView extends GenericCDIView {
@EJB
private UserBeanLocal userbean;
private User addableUser;
private EventUser addableUser;
private static final Logger logger = LoggerFactory.getLogger(RoleView.class);
......@@ -82,19 +82,16 @@ public class RoleView extends GenericCDIView {
public void addUser()
{
EventUser eu = userbean.getEventUser(addableUser, false);
role = rolebean.addRole(eu, role);
role = rolebean.addRole(addableUser, role);
addableUser = null;
}
public List<User> searchUser(String user)
public List<EventUser> searchUser(String user)
{
// By default this returns only 20 first results.
UserSearchQuery usq = new UserSearchQuery();
usq.setSearch(user);
usq.setOnlyThisEvent(true);
SearchResult<User> ret = userbean.getUsers(usq);
return ret.getResults();
return userbean.getThisEventsUsers(usq).getResults();
}
public boolean isCanReadRoles() {
......@@ -218,11 +215,11 @@ public class RoleView extends GenericCDIView {
initPermissions();
}
public User getAddableUser() {
public EventUser getAddableUser() {
return addableUser;
}
public void setAddableUser(User addableUser) {
public void setAddableUser(EventUser addableUser) {
this.addableUser = addableUser;
}
......
......@@ -1928,4 +1928,26 @@ user.allroles = All users
voting.create.entrysubmitrole = Role needed to submit entries
compo.filetype.name = Filetype name
compo.filetype.sort = Sort number
compo.filetype.filetype = Type
\ No newline at end of file
compo.filetype.filetype = Type
feature.name = Feature
feature.user_permission = User
feature.info_permission = Organizer
feature.admin_permission = Administrator
role.features = Event feature permissions
holderize.header = Printed card foldering
holderize.pageRows = Rows on page
holderize.pageColumns = Columns on page
holderize.folderName = Folder name
holderize.page = Page
holderize.column = Column
holderize.row = Row
holderize.barcode = Barcode
holderize.nextCodeHeader = Next card position
holderize.parametersHeader = Folder parameters
bill.id_str=Bill id
bill.filter_all=All
foodwave.updateList = Update list
place.assocUser = Associated user
place.lockPlace = Lock place
place.releasePlace = Release place
userlist.usersWithUnusedPlaceslots=Only users with unused placeslots
......@@ -1916,7 +1916,29 @@ role.source.ACCOUNTEVENT = Tilitapah
role.source.PLACE_PRODUCT = Tuote paikan kautta
role.source.EVENT_DEFAULT = Tapahtuman oletusrooli
user.allroles = Kaikki k\u00E4ytt\u00E4j\u00E4n roolit
voting.create.entrysubmitrole = Teosten lhettmiseen tarvittava rooli
voting.create.entrysubmitrole = Teosten l\u00E4hett\u00E4miseen tarvittava rooli
compo.filetype.name = Tiedostotyypin nimi
compo.filetype.sort = Jrjestysnumero
compo.filetype.filetype = Tyyppi
\ No newline at end of file
compo.filetype.sort = J\u00E4rjestysnumero
compo.filetype.filetype = Tyyppi
feature.name = Ominaisuus
feature.user_permission = K\u00E4ytt\u00E4j\u00E4
feature.info_permission = J\u00E4rjest\u00E4j\u00E4
feature.admin_permission = P\u00E4\u00E4k\u00E4ytt\u00E4j\u00E4
role.features = Tapahtuman ominaisuuksien oikeudet
bill.id_str=Laskun ID
bill.filter_all=Kaikki
holderize.header = Tulostettujen korttien kansiotus
holderize.pageRows = Rivej‰ sivulla
holderize.pageColumns = Sarakkeita sivulla
holderize.folderName = Kansion nimi
holderize.page = Sivu
holderize.column = Sarake
holderize.row = Rivi
holderize.barcode = Viivakoodi
holderize.nextCodeHeader = Seuraavan kortin paikka
holderize.parametersHeader = Kansion asetukset
foodwave.updateList = Pivit lista
place.assocUser = Liitetty kyttj
place.lockPlace = Lukitse paikka
place.releasePlace = Vapauta paikka
userlist.usersWithUnusedPlaceslots=Vain käyttäjät joilla on käyttämättömiä paikkaslotteja
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!