Commit b9ef974b by Tuomas Riihimäki

Merge branch 'sessions-for-frontend' into jsf-locale-from-rest

2 parents fbe7b22d c091283f
# devausnoteja (tmp ohjeet)
# devausnoteja
## Devaus
......@@ -12,12 +12,13 @@ Kehittäminen on nopeampaa jos saa muutokset suoraan käyttöön, tämän takia
ng-palvelimessa on bugi, ja se ei osaa jakaa tavaraa muusta hakemistosta kuin /.
moyaproxy.conf.json sisältää proxyasetukset localhostin porttiin 8080.
* `moyaproxy.conf.json` sisältää proxyasetukset localhostin porttiin 8080.
* `test-moya-proxy.conf.json` sisältää proxyasetukset ng.test.moya.fi:n
Käyttö:
`npm run start`
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 sisään moyaan: `http://localhost:4200/MoyaWeb/`
### HMR
......@@ -26,18 +27,15 @@ HMR -asetukset löytyy, niistä ei nykyisellään ole mitään hyötyä. Saa poi
## 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.
### Muuta
Jos tulee muita järkeviä kokonaisuuksia, joita voi käyttää muualla, jaa omaan moduuliinsa.
## WIP: Hakemistohierarkia
## Hakemistohierarkia
* app
* modules
* modules `as`
* viplist
* viplist-manager.module
* viplist-manager
......
......@@ -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>
......@@ -27,8 +27,8 @@
"@angular/platform-browser": "6.1.1",
"@angular/platform-browser-dynamic": "6.1.1",
"@angular/router": "6.1.1",
"@ngx-translate/core": "^9.0.0",
"@ngx-translate/http-loader": "^2.0.0",
"@ngx-translate/core": "^10.0.0",
"@ngx-translate/http-loader": "^3.0.0",
"apollo-angular": "^1.1.2",
"apollo-angular-link-http": "^1.1.1",
"apollo-cache-inmemory": "^1.2.6",
......@@ -37,7 +37,7 @@
"core-js": "^2.5.1",
"graphql": "^0.13.2",
"graphql-tag": "^2.9.2",
"ngx-bootstrap": "^2.0.2",
"ngx-bootstrap": "^3.0.0",
"rxjs": "^6.2.2",
"ts-helpers": "^1.1.1",
"zone.js": "^0.8.26"
......
......@@ -35,6 +35,7 @@ import {createApollo} from './shared/config/moya.config';
LoginModule,
LeftMenuModule,
ApolloModule,
HttpLinkModule,
......
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,8 +27,8 @@ export class MENU {
{
'name': 'users',
'items': [
{ 'path': '/old/info', 'name': 'listusers', 'params': {p: 'useradmin/list.jsf'} },
{ 'path': '/viplist/info', 'name': 'viplist'}
{ 'path': '/old/info', 'name': 'listusers', 'perm': _GP({'SITE': 'INFO'}), 'params': {p: 'useradmin/list.jsf'} },
{ 'path': '/viplist/info', 'perm': _GP({'VIPLIST': 'INFO'}), 'name': 'viplist'}
]}
];
......@@ -29,54 +36,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;
}
.loginarea {
display: flex;
flex-direction: column;
}
<div>Login works!</div>
\ No newline at end of file
<div class="loginarea">
<mat-form-field>
<input matInput [(ngModel)]="username" placeholder="{{ 'user.username' | translate }}">
</mat-form-field>
<mat-form-field>
<input matInput type="password" [(ngModel)]="password" placeholder="{{ 'user.password' | translate }}">
</mat-form-field>
<button mat-button (click)="login()" >{{ 'login.login' | translate }}</button>
<span *ngIf="loginError">ERROR</span>
<span *ngIf="loginOk">OK</span>
</div>
import { Component, OnInit } from '@angular/core';
import {TranslatePipe} from '@ngx-translate/core';
import {SessionServiceService} from '../../shared/services/session-service.service';
@Component({
selector: 'moya-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
styleUrls: ['./login.component.css'],
providers: [TranslatePipe]
})
export class LoginComponent implements OnInit {
constructor() { }
username: string = "";
password: string = "";
loginError = false;
loginOk = false;
constructor(private sessionService: SessionServiceService) { }
ngOnInit() {
}
login() {
this.sessionService.doLogin(this.username, this.password).subscribe((x) => {
this.loginError = false;
this.loginOk = true;
}, (error) => {
this.loginError = true;
});
}
}
......@@ -2,12 +2,26 @@ import { LoginRoutingModule } from './login-routing.module';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {LoginComponent} from './login.component';
import {MatButtonModule, MatFormFieldModule, MatInputModule} from '@angular/material';
import {TranslateModule} from '@ngx-translate/core';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {FormsModule} from "@angular/forms";
@NgModule({
imports: [
CommonModule,
LoginRoutingModule
LoginRoutingModule,
MatFormFieldModule,
MatInputModule,
MatButtonModule,
TranslateModule,
BrowserAnimationsModule,
FormsModule,
],
declarations: [LoginComponent]
declarations: [LoginComponent],
exports: [
LoginComponent
]
})
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;
};
}
}
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 {Observable} from 'rxjs';
import {MOYA_BASE_URL, MOYA_REST_URL} from '../config/moya.config';
import {map, share} from 'rxjs/operators';
import {Vip} from '../../modules/viplist/models/vip.model';
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';
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>;
constructor(private apollo: Apollo, private http: HttpClient) {
// 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));
}
public getPermissions(): Observable<Permissions> {
return this.permissionsObservable;
}
/**
* 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();
});
return apolloObservable;
}
}
......@@ -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,16 @@
"test": {
"user": "Käyttäjä"
},
"user": {
"username": "Käyttäjänimi",
"password": "Salasana"
},
"login": {
"login": "Kirjaudu"
},
"general": {
"yes": "Kyllä",
"no": "Ei",
......
{
"/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"
],
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!