{{this.getPrincipalName()}}
-
diff --git a/dmp-frontend/src/app/shared/components/navigation/navigation.component.ts b/dmp-frontend/src/app/shared/components/navigation/navigation.component.ts
index 16e3556a4..107b94530 100644
--- a/dmp-frontend/src/app/shared/components/navigation/navigation.component.ts
+++ b/dmp-frontend/src/app/shared/components/navigation/navigation.component.ts
@@ -1,8 +1,9 @@
import { sample } from 'rxjs/operators';
-import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { Component, EventEmitter, Input, Output, ElementRef } from '@angular/core';
import { Principal } from '../../../models/login/Principal';
import { AuthService } from '../../../services/auth/auth.service';
import { LanguageResolverService } from '../../../services/language-resolver/language-resolver.service';
+import { MatSidenav } from '@angular/material';
@Component({
selector: 'app-navigation',
@@ -26,7 +27,7 @@ export class NavigationComponent {
}
@Input()
- sidenavOpen = false;
+ sideNav: MatSidenav;
@Output()
sidenavOpenChanges = new EventEmitter();
@@ -60,7 +61,6 @@ export class NavigationComponent {
}
onSideNavClick() {
- this.sidenavOpen = !this.sidenavOpen;
- this.sidenavOpenChanges.emit(this.sidenavOpen);
+ this.sideNav.toggle();
}
}
diff --git a/dmp-frontend/src/app/shared/components/url-listing/url-listing.component.ts b/dmp-frontend/src/app/shared/components/url-listing/url-listing.component.ts
index 2dacfc038..1d55779c6 100644
--- a/dmp-frontend/src/app/shared/components/url-listing/url-listing.component.ts
+++ b/dmp-frontend/src/app/shared/components/url-listing/url-listing.component.ts
@@ -25,6 +25,6 @@ export class UrlListingComponent {
}
navigate(link: string) {
- this.router.navigate([link, this.parameters]);
+ this.router.navigate([link], { queryParams: this.parameters });
}
}
diff --git a/dmp-frontend/src/app/shared/componentsAdmin/combobox/combobox-component.html b/dmp-frontend/src/app/shared/componentsAdmin/combobox/combobox-component.html
index a3eb851a1..4ce4b2d4e 100644
--- a/dmp-frontend/src/app/shared/componentsAdmin/combobox/combobox-component.html
+++ b/dmp-frontend/src/app/shared/componentsAdmin/combobox/combobox-component.html
@@ -1,16 +1,16 @@
diff --git a/dmp-frontend/src/app/shared/componentsAdmin/radiobox/radiobox-component.html b/dmp-frontend/src/app/shared/componentsAdmin/radiobox/radiobox-component.html
index f33a91a9d..d0bbe6f72 100644
--- a/dmp-frontend/src/app/shared/componentsAdmin/radiobox/radiobox-component.html
+++ b/dmp-frontend/src/app/shared/componentsAdmin/radiobox/radiobox-component.html
@@ -1,29 +1,29 @@
diff --git a/dmp-frontend/src/app/user-management/login/login.component.scss b/dmp-frontend/src/app/user-management/login/login.component.scss
index e0e8af094..e2acfe0ae 100644
--- a/dmp-frontend/src/app/user-management/login/login.component.scss
+++ b/dmp-frontend/src/app/user-management/login/login.component.scss
@@ -1,19 +1,19 @@
-.container{
+.container {
padding: 15px 30px;
}
-.card{
+.card {
box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14);
border-radius: 6px;
- color: rgba(0,0,0, 0.87);
+ color: rgba(0, 0, 0, 0.87);
background: #fff;
}
-.card-raised{
+.card-raised {
box-shadow: 0 10px 30px -12px rgba(0, 0, 0, 0.42), 0 4px 25px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.2);
}
-.page-title{
+.page-title {
margin-top: 40px;
}
@@ -35,13 +35,13 @@
}
}
-.container{
+.container {
height: 100%;
position: relative;
z-index: 1;
}
-.card{
+.card {
position: relative;
padding: 20px;
display: flex;
@@ -64,16 +64,24 @@
}
@-webkit-keyframes card {
- from {top: -40px;}
- to {top: 0;}
+ from {
+ top: -40px;
+ }
+ to {
+ top: 0;
+ }
}
@keyframes card {
- from {top: -40px;}
- to {top: 0;}
+ from {
+ top: -40px;
+ }
+ to {
+ top: 0;
+ }
}
-.card-header{
+.card-header {
position: relative;
overflow: hidden;
top: -40px;
@@ -87,53 +95,55 @@
align-items: center;
}
-.card-header h4{
+.card-header h4 {
font-weight: 400;
color: #fff;
margin-bottom: 25px;
margin-top: 5px;
}
-.social-btns i{
+.social-btns i {
font-size: 21px;
color: #fff;
}
-.social-btns button{
+.social-btns button {
margin: 0 8px;
}
-.tip{
+.tip {
margin-top: -20px;
}
-.form-row, .card-form, .mat-input-container{
+.form-row,
+.card-form,
+.mat-form-field {
width: 100%;
}
-.card-form{
+.card-form {
padding: 5px;
}
-.form-row{
+.form-row {
position: relative;
display: flex;
align-items: center;
margin-top: 13px;
}
-.form-row i{
+.form-row i {
position: relative;
top: -5px;
margin-right: 15px;
color: #555;
}
-.card-footer{
+.card-footer {
margin: 10px;
}
-.card-footer button{
+.card-footer button {
color: #e91e63;
}
@@ -143,9 +153,10 @@
width: 45px;
height: 25px;
}
+
span.iconmedium {
background: url(img/b2access_medium.png) no-repeat;
float: left;
width: 100px;
height: 56px;
-}
\ No newline at end of file
+}
diff --git a/dmp-frontend/src/app/user-management/utilties/login-service.ts b/dmp-frontend/src/app/user-management/utilties/login-service.ts
index d1d0cd646..f8cb479c7 100644
--- a/dmp-frontend/src/app/user-management/utilties/login-service.ts
+++ b/dmp-frontend/src/app/user-management/utilties/login-service.ts
@@ -20,215 +20,213 @@ declare const IN: any;
@Injectable()
export class LoginService {
- private providers: LoginOptions[]
- private auth2: any;
- constructor(
- private router: Router,
- public authService: AuthService,
- public route: ActivatedRoute,
- public snackBar: MatSnackBar,
- public language: TranslateService,
- private zone: NgZone,
- private httpClient: HttpClient,
- @Optional() private config: LoginServiceConfiguration
- ) {
- if (config) {
- this.providers = config.loginProviders;
- }
- else this.providers = [LoginOptions.nativeLogin];
- }
-
- public initProviders() {
- if (this.hasProvider(LoginOptions.googleOauth)) this.initialiseGoogleOauth();
- if (this.hasProvider(LoginOptions.facebookOauth)) this.initialiseFacebookOauth();
- }
-
- public hasProvider(provider: LoginOptions) {
- for (let i = 0; i < this.providers.length; i++) {
- if (provider === this.providers[i]) return this.isProviderProperlyConfigured(provider)
- }
- return false;
- }
-
- private isProviderProperlyConfigured(provider: LoginOptions) {
- switch (provider) {
- case LoginOptions.facebookOauth: return this.hasAllRequiredFieldsConfigured(this.config.facebookConfiguration);
- case LoginOptions.googleOauth: return this.hasAllRequiredFieldsConfigured(this.config.googleConfiguration)
- case LoginOptions.linkedInOauth: return this.hasAllRequiredFieldsConfigured(this.config.linkedInConfiguration);
- case LoginOptions.twitterOauth: return this.hasAllRequiredFieldsConfigured(this.config.twitterConfiguration);
- case LoginOptions.b2Access: return this.hasAllRequiredFieldsConfigured(this.config.b2accessConfiguration);
- case LoginOptions.nativeLogin: return true;
- default: throw new Error("Unsupported Provider Type")
- }
- }
-
- private hasAllRequiredFieldsConfigured(configuration: LoginProviderConfiguration) {
- if (configuration != null && configuration.clientId != null) return true
- return false;
- }
-
- /*
- * GOOGLE SIGN IN
- */
-
- private initialiseGoogleOauth(): void {
- gapi.load('auth2', () => {
- this.auth2 = gapi.auth2.init({
- client_id: this.config.googleConfiguration.clientId,
- cookiepolicy: 'single_host_origin',
- scope: 'profile email'
- });
- this.attachGoogleSignin(document.getElementById('googleSignInButton'));
- });
- }
-
- public attachGoogleSignin(element) {
- if (!element) return
- this.auth2.attachClickHandler(element, {},
- (googleUser) => {
- var id_token = googleUser.getAuthResponse().id_token;
- if (id_token) {
- this.authService.login({ ticket: id_token, provider: LoginProviders.Google }).subscribe(
- res => this.onLogInSuccess(res),
- error => this.onLogInError(error)
- )
- }
- }, (error) => {
- alert(JSON.stringify(error, undefined, 2));
- });
- }
-
-
-
- /*
- * FACEBOOK SIGN IN
- */
-
-
- private initialiseFacebookOauth(): void {
- FB.init({
- appId: this.config.facebookConfiguration.clientId,
- cookie: false,
- xfbml: true,
- version: 'v2.8'
- });
- }
-
-
-
-
- public facebookLogin() {
- FB.login((response: any) => {
- if (response.status === 'connected' || 'not_authorized') {
- this.authService.login({ ticket: response.authResponse.accessToken, provider: LoginProviders.Facebook }).subscribe(
- res => this.onLogInSuccess(res),
- error => this.onLogInError(error)
- )
- }
- }, { scope: 'user_friends,email' });
- }
-
- /*
- * LINKEDIN SIGN IN
- */
-
- public linkedinAuthorize() {
- window.location.href = this.config.linkedInConfiguration.oauthUrl + "?response_type=code&client_id=" + this.config.linkedInConfiguration.clientId + "&redirect_uri=" + this.config.linkedInConfiguration.redirectUri + "&state=987654321"
- }
-
- public linkedInInitialiseLogin() {
- this.router.navigate(["/login/linkedin"])
- }
+ private providers: LoginOptions[]
+ private auth2: any;
+ constructor(
+ private router: Router,
+ public authService: AuthService,
+ public route: ActivatedRoute,
+ public snackBar: MatSnackBar,
+ public language: TranslateService,
+ private zone: NgZone,
+ private httpClient: HttpClient,
+ @Optional() private config: LoginServiceConfiguration
+ ) {
+ if (config) {
+ this.providers = config.loginProviders;
+ }
+ else this.providers = [LoginOptions.nativeLogin];
+ }
+
+ public initProviders() {
+ if (this.hasProvider(LoginOptions.googleOauth)) this.initialiseGoogleOauth();
+ if (this.hasProvider(LoginOptions.facebookOauth)) this.initialiseFacebookOauth();
+ }
+
+ public hasProvider(provider: LoginOptions) {
+ for (let i = 0; i < this.providers.length; i++) {
+ if (provider === this.providers[i]) return this.isProviderProperlyConfigured(provider)
+ }
+ return false;
+ }
+
+ private isProviderProperlyConfigured(provider: LoginOptions) {
+ switch (provider) {
+ case LoginOptions.facebookOauth: return this.hasAllRequiredFieldsConfigured(this.config.facebookConfiguration);
+ case LoginOptions.googleOauth: return this.hasAllRequiredFieldsConfigured(this.config.googleConfiguration)
+ case LoginOptions.linkedInOauth: return this.hasAllRequiredFieldsConfigured(this.config.linkedInConfiguration);
+ case LoginOptions.twitterOauth: return this.hasAllRequiredFieldsConfigured(this.config.twitterConfiguration);
+ case LoginOptions.b2Access: return this.hasAllRequiredFieldsConfigured(this.config.b2accessConfiguration);
+ case LoginOptions.nativeLogin: return true;
+ default: throw new Error("Unsupported Provider Type")
+ }
+ }
+
+ private hasAllRequiredFieldsConfigured(configuration: LoginProviderConfiguration) {
+ if (configuration != null && configuration.clientId != null) return true
+ return false;
+ }
+
+ /*
+ * GOOGLE SIGN IN
+ */
- public linkedInloginUser(code: string) {
- this.authService.login({ ticket: code, provider: LoginProviders.LinkedIn }).subscribe(
+ private initialiseGoogleOauth(): void {
+ gapi.load('auth2', () => {
+ this.auth2 = gapi.auth2.init({
+ client_id: this.config.googleConfiguration.clientId,
+ cookiepolicy: 'single_host_origin',
+ scope: 'profile email'
+ });
+ this.attachGoogleSignin(document.getElementById('googleSignInButton'));
+ });
+ }
+
+ public attachGoogleSignin(element) {
+ if (!element) return
+ this.auth2.attachClickHandler(element, {},
+ (googleUser) => {
+ var id_token = googleUser.getAuthResponse().id_token;
+ if (id_token) {
+ this.authService.login({ ticket: id_token, provider: LoginProviders.Google }).subscribe(
res => this.onLogInSuccess(res),
error => this.onLogInError(error)
- )
- }
-
- /*
- * TWITTER SIGN IN
- */
- public twitterInitialiseLogin() {
- this.router.navigate(["/login/twitter"])
- }
+ )
+ }
+ }, (error) => {
+ alert(JSON.stringify(error, undefined, 2));
+ });
+ }
- public twitterAuthorize() {
- let headers = new HttpHeaders();
- headers = headers.set('Content-Type', 'application/json');
- headers = headers.set('Accept', 'application/json');
- this.httpClient.get(HostConfiguration.Server + 'auth/twitterRequestToken', { headers: headers }).subscribe((data: any) => {
- window.location.href = this.config.twitterConfiguration.oauthUrl + "?oauth_token=" + data.payload.value
- })
- }
- public twitterLogin(token: string, verifier: string) {
- this.authService.login({ ticket: token, provider: LoginProviders.Twitter, data: verifier }).subscribe(
- res => this.onLogInSuccess(res),
- error => this.onLogInError(error)
- )
- }
- /*
- * B2ACCESS LOG IN
+ /*
+ * FACEBOOK SIGN IN
*/
- public b2AccessInitialiseLogin() {
- this.router.navigate(["/api/oauth/authorized/b2access"])
- }
- public b2AccessGetAuthCode() {
- window.location.href = this.config.b2accessConfiguration.oauthUrl + "?response_type=code&client_id=" + this.config.b2accessConfiguration.clientId + "&redirect_uri=" + this.config.b2accessConfiguration.redirectUri + "&state=987654321&scope=USER_PROFILE"
- }
+ private initialiseFacebookOauth(): void {
+ FB.init({
+ appId: this.config.facebookConfiguration.clientId,
+ cookie: false,
+ xfbml: true,
+ version: 'v2.8'
+ });
+ }
- public b2AccessLogin(code: String) {
- let headers = new HttpHeaders();
- headers = headers.set('Content-Type', 'application/json');
- headers = headers.set('Accept', 'application/json');
- this.httpClient.post(HostConfiguration.Server + "auth/b2AccessRequestToken", { code: code }, { headers: headers })
- .subscribe((data: any) => {
- this.authService.login({ ticket: data.payload.accessToken, provider: LoginProviders.B2Accesss, data: null }).subscribe(
- res => this.onLogInSuccess(res),
- error => this.onLogInError(error)
- )
- })
- }
- /*
- * NATIVE LOGIN
- */
- public nativeLogin(credentials: Credential) {
- this.authService.nativeLogin(credentials).subscribe(
- res => this.onLogInSuccess(res),
- error => this.onLogInError(error)
+ public facebookLogin() {
+ FB.login((response: any) => {
+ if (response.status === 'connected' || 'not_authorized') {
+ this.authService.login({ ticket: response.authResponse.accessToken, provider: LoginProviders.Facebook }).subscribe(
+ res => this.onLogInSuccess(res),
+ error => this.onLogInError(error)
)
- }
-
-
- /*
- * LOGIN HANDLERS
- */
-
-
- public onLogInSuccess(logoutMessage: any) {
- this.snackBar.openFromComponent(SnackBarNotificationComponent, {
- data: { message: 'GENERAL.SNACK-BAR.SUCCESSFUL-LOGIN', language: this.language },
- duration: 3000,
- extraClasses: ['snackbar-success']
- });
- let params = this.router["rawUrlTree"].queryParams;
- let redirectUrl = params['returnUrl'] ? params['returnUrl'] : '/';
- this.zone.run(() => this.router.navigate([redirectUrl]));
- }
-
- public onLogInError(errorMessage: string) {
- this.snackBar.openFromComponent(SnackBarNotificationComponent, {
- data: { message: 'GENERAL.SNACK-BAR.UNSUCCESSFUL-LOGIN', language: this.language },
- duration: 3000,
- extraClasses: ['snackbar-warning']
- })
- }
-}
\ No newline at end of file
+ }
+ }, { scope: 'user_friends,email' });
+ }
+
+ /*
+ * LINKEDIN SIGN IN
+ */
+
+ public linkedinAuthorize() {
+ window.location.href = this.config.linkedInConfiguration.oauthUrl + "?response_type=code&client_id=" + this.config.linkedInConfiguration.clientId + "&redirect_uri=" + this.config.linkedInConfiguration.redirectUri + "&state=987654321"
+ }
+
+ public linkedInInitialiseLogin() {
+ this.router.navigate(["/login/linkedin"])
+ }
+
+ public linkedInloginUser(code: string) {
+ this.authService.login({ ticket: code, provider: LoginProviders.LinkedIn }).subscribe(
+ res => this.onLogInSuccess(res),
+ error => this.onLogInError(error)
+ )
+ }
+
+ /*
+ * TWITTER SIGN IN
+ */
+ public twitterInitialiseLogin() {
+ this.router.navigate(["/login/twitter"])
+ }
+
+ public twitterAuthorize() {
+ let headers = new HttpHeaders();
+ headers = headers.set('Content-Type', 'application/json');
+ headers = headers.set('Accept', 'application/json');
+ this.httpClient.get(HostConfiguration.Server + 'auth/twitterRequestToken', { headers: headers }).subscribe((data: any) => {
+ window.location.href = this.config.twitterConfiguration.oauthUrl + "?oauth_token=" + data.payload.value
+ })
+ }
+
+ public twitterLogin(token: string, verifier: string) {
+ this.authService.login({ ticket: token, provider: LoginProviders.Twitter, data: verifier }).subscribe(
+ res => this.onLogInSuccess(res),
+ error => this.onLogInError(error)
+ )
+ }
+
+ /*
+* B2ACCESS LOG IN
+*/
+
+ public b2AccessInitialiseLogin() {
+ this.router.navigate(["/api/oauth/authorized/b2access"])
+ }
+
+ public b2AccessGetAuthCode() {
+ window.location.href = this.config.b2accessConfiguration.oauthUrl + "?response_type=code&client_id=" + this.config.b2accessConfiguration.clientId + "&redirect_uri=" + this.config.b2accessConfiguration.redirectUri + "&state=987654321&scope=USER_PROFILE"
+ }
+
+ public b2AccessLogin(code: String) {
+ let headers = new HttpHeaders();
+ headers = headers.set('Content-Type', 'application/json');
+ headers = headers.set('Accept', 'application/json');
+ this.httpClient.post(HostConfiguration.Server + "auth/b2AccessRequestToken", { code: code }, { headers: headers })
+ .subscribe((data: any) => {
+ this.authService.login({ ticket: data.payload.accessToken, provider: LoginProviders.B2Accesss, data: null }).subscribe(
+ res => this.onLogInSuccess(res),
+ error => this.onLogInError(error)
+ )
+ })
+ }
+
+
+ /*
+ * NATIVE LOGIN
+ */
+
+ public nativeLogin(credentials: Credential) {
+ this.authService.nativeLogin(credentials).subscribe(
+ res => this.onLogInSuccess(res),
+ error => this.onLogInError(error)
+ )
+ }
+
+
+ /*
+ * LOGIN HANDLERS
+ */
+
+
+ public onLogInSuccess(logoutMessage: any) {
+ this.snackBar.openFromComponent(SnackBarNotificationComponent, {
+ data: { message: 'GENERAL.SNACK-BAR.SUCCESSFUL-LOGIN', language: this.language },
+ duration: 3000,
+ });
+ let params = this.router["rawUrlTree"].queryParams;
+ let redirectUrl = params['returnUrl'] ? params['returnUrl'] : '/';
+ this.zone.run(() => this.router.navigate([redirectUrl]));
+ }
+
+ public onLogInError(errorMessage: string) {
+ this.snackBar.openFromComponent(SnackBarNotificationComponent, {
+ data: { message: 'GENERAL.SNACK-BAR.UNSUCCESSFUL-LOGIN', language: this.language },
+ duration: 3000,
+ })
+ }
+}
diff --git a/dmp-frontend/src/app/users/components/roles/user-role-editor.component.ts b/dmp-frontend/src/app/users/components/roles/user-role-editor.component.ts
index f1fcbaf5e..d5e06bb1c 100644
--- a/dmp-frontend/src/app/users/components/roles/user-role-editor.component.ts
+++ b/dmp-frontend/src/app/users/components/roles/user-role-editor.component.ts
@@ -19,14 +19,14 @@ import { MatSnackBar } from '@angular/material';
export class UserRoleEditorComponent implements OnInit {
@Input() public item: UserListingModel;
- private formGroup: FormGroup = null;
- private nowEditing = false;
- private errorModel: UserErrorModel;
+ public formGroup: FormGroup = null;
+ public nowEditing = false;
+ public errorModel: UserErrorModel;
constructor(
- private language: TranslateService,
- private userService: UserReferenceService,
- private formBuilder: FormBuilder,
- private snackBar: MatSnackBar
+ public language: TranslateService,
+ public userService: UserReferenceService,
+ public formBuilder: FormBuilder,
+ public snackBar: MatSnackBar
) {
}
@@ -133,7 +133,6 @@ export class UserRoleEditorComponent implements OnInit {
this.snackBar.openFromComponent(SnackBarNotificationComponent, {
data: { message: 'GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE', language: this.language },
duration: 3000,
- extraClasses: ['snackbar-success']
});
}
onCallbackError(error: any) {
@@ -142,7 +141,6 @@ export class UserRoleEditorComponent implements OnInit {
this.snackBar.openFromComponent(SnackBarNotificationComponent, {
data: { message: 'GENERAL.SNACK-BAR.UNSUCCESSFUL-UPDATE', language: this.language },
duration: 3000,
- extraClasses: ['snackbar-warning']
});
}
diff --git a/dmp-frontend/src/app/users/components/users.component.ts b/dmp-frontend/src/app/users/components/users.component.ts
index 256259f96..aad27d8cb 100644
--- a/dmp-frontend/src/app/users/components/users.component.ts
+++ b/dmp-frontend/src/app/users/components/users.component.ts
@@ -63,7 +63,6 @@ export class UsersDataSource extends DataSource
{
this._snackBar.openFromComponent(SnackBarNotificationComponent, {
data: { message: 'GENERAL.SNACK-BAR.FORMS-BAD-REQUEST', language: this._languageService },
duration: 3000,
- extraClasses: ['snackbar-warning']
});
this._criteria.onCallbackError(error);
return Observable.of(null);
diff --git a/dmp-frontend/src/app/utilities/cite-http-service-module/base-http.service.ts b/dmp-frontend/src/app/utilities/cite-http-service-module/base-http.service.ts
index 13b61ec56..7f9182cab 100644
--- a/dmp-frontend/src/app/utilities/cite-http-service-module/base-http.service.ts
+++ b/dmp-frontend/src/app/utilities/cite-http-service-module/base-http.service.ts
@@ -75,7 +75,6 @@ export class BaseHttpService {
this.snackBar.openFromComponent(SnackBarNotificationComponent, {
data: { message: 'GENERAL.SNACK-BAR.SUCCESSFUL-LOGOUT', language: this.language },
duration: 3000,
- extraClasses: ['snackbar-success']
})
let currentPage = this.router.url;
this.router.navigate(['/unauthorized'], { queryParams: { returnUrl: currentPage } });
@@ -87,7 +86,6 @@ export class BaseHttpService {
this.snackBar.openFromComponent(SnackBarNotificationComponent, {
data: { message: error.message, language: null },
duration: 3000,
- extraClasses: ['snackbar-warning']
})
return Observable.throw(errorResponse);
}
@@ -98,7 +96,6 @@ export class BaseHttpService {
this.snackBar.openFromComponent(SnackBarNotificationComponent, {
data: { message: 'GENERAL.ERRORS.HTTP-REQUEST-ERROR', language: this.language },
duration: 3000,
- extraClasses: ['snackbar-warning']
})
return Observable.throw(errorResponse);
}
@@ -106,21 +103,20 @@ export class BaseHttpService {
})
.map(response => {
if (response instanceof Blob) return response
- if (response.statusCode == ApiMessageCode.SUCCESS_MESSAGE) {
+ if (response["statusCode"] == ApiMessageCode.SUCCESS_MESSAGE) {
//throw new Error('Request failed');
this.snackBar.openFromComponent(SnackBarNotificationComponent, {
- data: { message: response.message, language: null },
+ data: { message: response["message"], language: null },
duration: 3000,
- extraClasses: ['snackbar-success']
})
- return response.payload;
+ return response["payload"];
}
- else if (response.statusCode == ApiMessageCode.NO_MESSAGE) {
- return response.payload;
+ else if (response["statusCode"] == ApiMessageCode.NO_MESSAGE) {
+ return response["payload"];
}
else {
- return response.payload;
+ return response["payload"];
}
});
}
diff --git a/dmp-frontend/src/app/welcomepage/welcomepage.component.ts b/dmp-frontend/src/app/welcomepage/welcomepage.component.ts
index c3a86acd2..89bce9cc9 100644
--- a/dmp-frontend/src/app/welcomepage/welcomepage.component.ts
+++ b/dmp-frontend/src/app/welcomepage/welcomepage.component.ts
@@ -4,7 +4,6 @@ import { DashboardService } from '../../app/services/dashboard/dashboard.service
import { DashboardStatisticsModel } from '../models/dashboard/DashboardStatisticsModel';
import { JsonSerializer } from '../utilities/JsonSerializer';
import { HomepageComponent } from '../homepage/homepage.component'
-import { IBreadcrumb } from 'ngx-breadcrumbs';
import { IBreadCrumbComponent } from '../shared/components/breadcrumb/definition/IBreadCrumbComponent';
import { Observable } from 'rxjs/Observable';
import { BreadcrumbItem } from '../shared/components/breadcrumb/definition/breadcrumb-item';
diff --git a/dmp-frontend/src/assets/lang/en.json b/dmp-frontend/src/assets/lang/en.json
index 3ce0c2cba..c84aade7e 100644
--- a/dmp-frontend/src/assets/lang/en.json
+++ b/dmp-frontend/src/assets/lang/en.json
@@ -185,7 +185,8 @@
"LIKE": "Search",
"PERIOD-FROM": "Start",
"PERIOD-TO": "End",
- "STATUS": "Status"
+ "STATUS": "Status",
+ "TAGS": "Tags"
},
"DMP": {
"LIKE": "Search",
diff --git a/dmp-frontend/src/environments/environment.ts b/dmp-frontend/src/environments/environment.ts
index 05a577f68..b60755433 100644
--- a/dmp-frontend/src/environments/environment.ts
+++ b/dmp-frontend/src/environments/environment.ts
@@ -5,7 +5,7 @@
export const environment = {
production: false,
- Server: 'http://devel-21.local.cite.gr:8080/api/',
+ Server: 'http://localhost:8080/api/',
App: 'http://localhost:4200/',
HelpServiceUrl: 'localhost:5000/'
};
diff --git a/dmp-frontend/tslint.json b/dmp-frontend/tslint.json
index 9963d6c39..d8d395663 100644
--- a/dmp-frontend/tslint.json
+++ b/dmp-frontend/tslint.json
@@ -18,7 +18,6 @@
"forin": true,
"import-blacklist": [
true,
- "rxjs",
"rxjs/Rx"
],
"import-spacing": true,
diff --git a/docker-compose.yml b/docker-compose.yml
index 8c3a4baa2..42fcd5682 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -17,6 +17,9 @@ services:
#
# ELASTIC_VERSION=6.0.0-beta1 TAG=6.0.0-beta1-3eab5b40 docker-compose up
#
+
+##########################DMP######################################################################
+
dmp-backend:
build:
context: ./dmp-backend
@@ -25,8 +28,9 @@ services:
container_name: dmp-backend
ports: ['0.0.0.0:8080:8080']
links:
- - logstash
- networks: ['stack']
+ - logstash:logstash
+ - elasticsearch-dmp:elasticsearch-dmp
+ networks: ['stack','elasticsearch-dmp']
dmp-frontend:
build:
@@ -38,12 +42,28 @@ services:
ports: ['0.0.0.0:80:80']
networks: ['stack']
+##########################ELASTIC######################################################################
+ elasticsearch-dmp:
+ image: docker.elastic.co/elasticsearch/elasticsearch:${TAG}
+ container_name: elasticsearch-dmp
+ volumes:
+ - ./elastic-config/elasticsearch-custom.yml:/usr/share/elasticsearch/config/elasticsearch.yml
+ environment: ['http.host=0.0.0.0','transport.host=0.0.0.0']
+ ports: ['0.0.0.0:9201:9200','0.0.0.0:9301:9300']
+ networks: ['elasticsearch-dmp']
+ volumes:
+ - esdata-dmp:/usr/share/elasticsearch/data
+
+##########################ELK-STACK######################################################################
+
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:${TAG}
container_name: elasticsearch
environment: ['http.host=0.0.0.0', 'transport.host=127.0.0.1', 'ELASTIC_PASSWORD=${ELASTIC_PASSWORD}']
ports: ['0.0.0.0:9200:9200']
networks: ['stack']
+ volumes:
+ - esdata:/usr/share/elasticsearch/data
kibana:
image: docker.elastic.co/kibana/kibana:${TAG}
@@ -62,27 +82,25 @@ services:
networks: ['stack']
depends_on: ['elasticsearch', 'setup_logstash']
- filebeat:
- image: docker.elastic.co/beats/filebeat:${TAG}
- container_name: filebeat
- command: -e -E 'output.elasticsearch.password=${ELASTIC_PASSWORD}'
- networks: ['stack']
- depends_on: ['elasticsearch', 'setup_filebeat']
+ #filebeat:
+ # image: docker.elastic.co/beats/filebeat:${TAG}
+ # container_name: filebeat
+ # command: -e -E 'output.elasticsearch.password=${ELASTIC_PASSWORD}'
+ # networks: ['stack']
+ # depends_on: ['elasticsearch', 'setup_filebeat']
- heartbeat:
- image: docker.elastic.co/beats/heartbeat:${TAG}
- container_name: heartbeat
- command: -e -E 'output.elasticsearch.password=${ELASTIC_PASSWORD}'
- networks: ['stack']
- depends_on: ['elasticsearch', 'setup_heartbeat']
+ #heartbeat:
+ # image: docker.elastic.co/beats/heartbeat:${TAG}
+ # container_name: heartbeat
+ # command: -e -E 'output.elasticsearch.password=${ELASTIC_PASSWORD}'
+ # networks: ['stack']
+ # depends_on: ['elasticsearch', 'setup_heartbeat']
# Run a short-lived container to set up Logstash.
setup_logstash:
image: centos:7
container_name: setup_logstash
volumes: ['./ELK.Docker/scripts/setup-logstash.sh:/usr/local/bin/setup-logstash.sh:ro']
- # The script may have CR/LF line endings if using Docker for Windows, so
- # make sure that they don't confuse Bash.
command: ['/bin/bash', '-c', 'cat /usr/local/bin/setup-logstash.sh | tr -d "\r" | bash']
environment: ['ELASTIC_PASSWORD=${ELASTIC_PASSWORD}']
networks: ['stack']
@@ -97,25 +115,95 @@ services:
networks: ['stack']
depends_on: ['elasticsearch']
- setup_filebeat:
- image: docker.elastic.co/beats/filebeat:${TAG}
- container_name: setup_filebeat
- volumes: ['./ELK.Docker/scripts/setup-beat.sh:/usr/local/bin/setup-beat.sh:ro']
- command: ['/bin/bash', '-c', 'cat /usr/local/bin/setup-beat.sh | tr -d "\r" | bash -s filebeat']
- environment: ['ELASTIC_PASSWORD=${ELASTIC_PASSWORD}']
- networks: ['stack']
- depends_on: ['kibana']
+ #setup_filebeat:
+ # image: docker.elastic.co/beats/filebeat:${TAG}
+ # container_name: setup_filebeat
+ # volumes: ['./ELK.Docker/scripts/setup-beat.sh:/usr/local/bin/setup-beat.sh:ro']
+ # command: ['/bin/bash', '-c', 'cat /usr/local/bin/setup-beat.sh | tr -d "\r" | bash -s filebeat']
+ # environment: ['ELASTIC_PASSWORD=${ELASTIC_PASSWORD}']
+ # networks: ['stack']
+ # depends_on: ['kibana']
+
+ #setup_heartbeat:
+ # image: docker.elastic.co/beats/heartbeat:${TAG}
+ # container_name: setup_heartbeat
+ # volumes: ['./ELK.Docker/scripts/setup-beat.sh:/usr/local/bin/setup-beat.sh:ro']
+ # command: ['/bin/bash', '-c', 'cat /usr/local/bin/setup-beat.sh | tr -d "\r" | bash -s heartbeat']
+ # environment: ['ELASTIC_PASSWORD=${ELASTIC_PASSWORD}']
+ # networks: ['stack']
+ # depends_on: ['kibana']
+
+##########################DOCSBOX######################################################################
+ # web:
+ # restart: always
+ # build: ./docsbox-master/docsbox
+ # expose:
+ # - "8000"
+ # links:
+ # - redis:redis
+ # volumes:
+ # - docsbox:/home/docsbox
+ # - media:/home/docsbox/media
+ # command: gunicorn -b :8000 docsbox:app
+ # networks: ['stack']
+
+ # rqworker:
+ # restart: always
+ # build: ./docsbox-master/docsbox
+ # links:
+ # - redis:redis
+ # volumes:
+ # - web
+ # command: rq worker -c docsbox.settings
+ # networks: ['stack']
+
+ # rqscheduler:
+ # restart: always
+ # build: ./docsbox-master/docsbox
+ # links:
+ # - redis:redis
+ # volumes:
+ # - web
+ # command: rqscheduler -H redis -p 6379 -d 0
+ # networks: ['stack']
+
+ # nginx:
+ # restart: always
+ # build: ./docsbox-master/nginx/
+ # ports:
+ # - "81:80"
+ # volumes:
+ # - web
+ # links:
+ # - web:web
+ # networks: ['stack']
+
+ # redis:
+ # restart: always
+ # image: redis:latest
+ # expose:
+ # - "6379"
+ # volumes:
+ # - redisdata:/data
+ # networks: ['stack']
+
+
+##########################SETTINGS######################################################################
+
+volumes:
+ esdata:
+ driver: local
+ esdata-dmp:
+ driver: local
+ #redisdata:
+ # driver: local
+ # docsbox:
+ # driver: local
+ # media:
+ # driver: local
+networks:
+ stack: {}
+ elasticsearch-dmp: {}
+
- setup_heartbeat:
- image: docker.elastic.co/beats/heartbeat:${TAG}
- container_name: setup_heartbeat
- volumes: ['./ELK.Docker/scripts/setup-beat.sh:/usr/local/bin/setup-beat.sh:ro']
- command: ['/bin/bash', '-c', 'cat /usr/local/bin/setup-beat.sh | tr -d "\r" | bash -s heartbeat']
- environment: ['ELASTIC_PASSWORD=${ELASTIC_PASSWORD}']
- networks: ['stack']
- depends_on: ['kibana']
-#volumes:
- #esdata:
- # driver: local
-networks: {stack: {}}
diff --git a/docsbox-master/.gitignore b/docsbox-master/.gitignore
new file mode 100644
index 000000000..4acaba3a2
--- /dev/null
+++ b/docsbox-master/.gitignore
@@ -0,0 +1,48 @@
+*.py[cod]
+
+# C extensions
+*.so
+
+# Packages
+*.egg
+*.egg-info
+dist
+build
+eggs
+parts
+bin
+var
+sdist
+develop-eggs
+.installed.cfg
+lib
+lib64
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+nosetests.xml
+
+# Translations
+*.mo
+
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
+
+# Complexity
+output/*.html
+output/*/index.html
+
+# Sphinx
+docs/_build
+
+/todo.txt
+/.ropeproject
+/example.db
+/_
+/.cache
diff --git a/docsbox-master/.travis.yml b/docsbox-master/.travis.yml
new file mode 100644
index 000000000..348bd2212
--- /dev/null
+++ b/docsbox-master/.travis.yml
@@ -0,0 +1,7 @@
+sudo: required
+language: python
+services:
+ - docker
+
+script:
+ - docker-compose run web nosetests
diff --git a/docsbox-master/LICENSE b/docsbox-master/LICENSE
new file mode 100644
index 000000000..e04e227f5
--- /dev/null
+++ b/docsbox-master/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Dmitry Veselov
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/docsbox-master/README.md b/docsbox-master/README.md
new file mode 100644
index 000000000..fda98191d
--- /dev/null
+++ b/docsbox-master/README.md
@@ -0,0 +1,134 @@
+# docsbox [![Build Status](https://travis-ci.org/dveselov/docsbox.svg?branch=master)](https://travis-ci.org/dveselov/docsbox)
+
+`docsbox` is a standalone service that allows you convert office documents, like .docx and .pptx, into more useful filetypes like PDF, for viewing it in browser with PDF.js, or HTML for organizing full-text search of document content.
+`docsbox` uses **LibreOffice** (via **LibreOfficeKit**) for document converting.
+
+```bash
+$ curl -F "file=@kittens.docx" http://localhost/api/v1/
+
+{
+ "id": "9b643d78-d0c8-4552-a0c5-111a89896176",
+ "status": "queued"
+}
+
+$ curl http://localhost/api/v1/9b643d78-d0c8-4552-a0c5-111a89896176
+
+{
+ "id": "9b643d78-d0c8-4552-a0c5-111a89896176",
+ "result_url": "/media/9b643d78-d0c8-4552-a0c5-111a89896176.zip",
+ "status": "finished"
+}
+
+$ curl -O http://localhost/media/9b643d78-d0c8-4552-a0c5-111a89896176.zip
+
+$ unzip -l 9b643d78-d0c8-4552-a0c5-111a89896176.zip
+
+Archive: 9b643d78-d0c8-4552-a0c5-111a89896176.zip
+ Length Date Time Name
+--------- ---------- ----- ----
+ 11135 2016-07-08 05:31 txt
+ 373984 2016-07-08 05:31 pdf
+ 147050 2016-07-08 05:31 html
+--------- -------
+ 532169 3 files
+```
+
+```bash
+$ cat options.json
+{
+ "formats": ["pdf"],
+ "thumbnails": {
+ "size": "640x480",
+ }
+}
+
+$ curl -i -F "file=@kittens.ppt" -F "options=80/tcp docsbox_nginx_1
+f6b55773c71d docsbox_rqworker "rq worker -c docsbox" 15 minutes ago Up 8 minutes docsbox_rqworker_1
+662b08daefea docsbox_rqscheduler "rqscheduler -H redis" 15 minutes ago Up 8 minutes docsbox_rqscheduler_1
+0364df126b36 docsbox_web "gunicorn -b :8000 do" 15 minutes ago Up 8 minutes 8000/tcp docsbox_web_1
+5e8c8481e288 redis:latest "docker-entrypoint.sh" 9 hours ago Up 8 minutes 0.0.0.0:6379->6379/tcp docsbox_redis_1
+```
+
+# Settings (env)
+
+```
+REDIS_URL - redis-server url (default: redis://redis:6379/0)
+REDIS_JOB_TIMEOUT - job timeout (default: 10 minutes)
+ORIGINAL_FILE_TTL - TTL for uploaded file in seconds (default: 10 minutes)
+RESULT_FILE_TTL - TTL for result file in seconds (default: 24 hours)
+THUMBNAILS_DPI - thumbnails dpi, for bigger thumbnails choice bigger values (default: 90)
+LIBREOFFICE_PATH - path to libreoffice (default: /usr/lib/libreoffice/program/)
+```
+
+# Scaling
+Within a single physical server, docsbox can be scaled by docker-compose:
+```bash
+$ docker-compose scale web=4 rqworker=8
+```
+For multi-host deployment you'll need to create global syncronized volume (e.g. with flocker), global redis-server and mount it at `docker-compose.yml` file.
+
+# Supported filetypes
+
+| Input | Output | Thumbnails |
+| ---------------------------------- | ------------------- | ---------- |
+| Document `doc` `docx` `odt` `rtf` | `pdf` `txt` `html` | `yes` |
+| Presentation `ppt` `pptx` `odp` | `pdf` `html` | `yes` |
+| Spreadsheet `xls` `xlsx` `ods` | `pdf` `csv` `html` | `yes` |
diff --git a/docsbox-master/docker-compose.yml b/docsbox-master/docker-compose.yml
new file mode 100644
index 000000000..60dca2149
--- /dev/null
+++ b/docsbox-master/docker-compose.yml
@@ -0,0 +1,67 @@
+---
+version: '3'
+services:
+ web:
+ restart: always
+ build: ./docsbox-master/docsbox
+ expose:
+ - "8000"
+ links:
+ - redis:redis
+ volumes:
+ - docsbox:/home/docsbox
+ - media:/home/docsbox/media
+ command: gunicorn -b :8000 docsbox:app
+ networks: ['stack']
+
+ rqworker:
+ restart: always
+ build: ./docsbox-master/docsbox
+ links:
+ - redis:redis
+ volumes:
+ - web
+ command: rq worker -c docsbox.settings
+ networks: ['stack']
+
+ rqscheduler:
+ restart: always
+ build: ./docsbox-master/docsbox
+ links:
+ - redis:redis
+ volumes:
+ - web
+ command: rqscheduler -H redis -p 6379 -d 0
+ networks: ['stack']
+
+ nginx:
+ restart: always
+ build: ./docsbox-master/nginx/
+ ports:
+ - "81:80"
+ volumes:
+ - web
+ links:
+ - web:web
+ networks: ['stack']
+
+ redis:
+ restart: always
+ image: redis:latest
+ expose:
+ - "6379"
+ volumes:
+ - redisdata:/data
+ networks: ['stack']
+
+volumes:
+ esdata:
+ driver: local
+ redisdata:
+ driver: local
+ docsbox:
+ driver: local
+ media:
+ driver: local
+
+networks: {stack: {}}
diff --git a/docsbox-master/docsbox/Dockerfile b/docsbox-master/docsbox/Dockerfile
new file mode 100644
index 000000000..4cbd1352f
--- /dev/null
+++ b/docsbox-master/docsbox/Dockerfile
@@ -0,0 +1,17 @@
+FROM ubuntu:16.04
+MAINTAINER Dmitry Veselov
+
+RUN apt-get update && apt-get upgrade -y
+RUN apt-get install -y libffi-dev libmagic-dev libmagickwand-dev
+RUN apt-get install -y libreoffice libreofficekit-dev
+RUN apt-get install -y python3-dev python3-pip git
+
+RUN apt-get clean && apt-get -y update && apt-get install -y locales && locale-gen en_US.UTF-8
+ENV LANG en_US.UTF-8
+ENV LANGUAGE en_US:en
+ENV LC_ALL en_US.UTF-8
+
+ADD . /home/docsbox
+WORKDIR /home/
+RUN pip3 install --upgrade pip
+RUN pip3 install -r docsbox/requirements.txt
diff --git a/docsbox-master/docsbox/__init__.py b/docsbox-master/docsbox/__init__.py
new file mode 100644
index 000000000..67ea852a9
--- /dev/null
+++ b/docsbox-master/docsbox/__init__.py
@@ -0,0 +1,32 @@
+from flask import Flask
+from flask.ext.rq2 import RQ
+from flask_restful import Api
+from flask_env_settings import Settings
+
+
+app = Flask(__name__)
+app.config.from_object("docsbox.settings")
+
+Settings(app, rules={
+ "REDIS_JOB_TIMEOUT": (int, 60 * 10),
+ "ORIGINAL_FILE_TTL": (int, 60 * 10),
+ "RESULT_FILE_TTL": (int, 60 * 60 * 24),
+
+ "LIBREOFFICE_PATH": (str, "/usr/lib/libreoffice/program/"),
+
+ "THUMBNAILS_DPI": (int, 90),
+ "THUMBNAILS_QUANTIZE": (bool, False),
+ "THUMBNAILS_QUANTIZE_COLORS": (int, 128),
+ "THUMBNAILS_QUANTIZE_COLORSPACE": (str, "rgb"),
+})
+
+api = Api(app)
+rq = RQ(app)
+
+from docsbox.docs.views import DocumentView, DocumentCreateView
+
+api.add_resource(DocumentView, "/api/v1/")
+api.add_resource(DocumentCreateView, "/api/v1/")
+
+if __name__ == "__main__":
+ app.run()
diff --git a/docsbox-master/docsbox/docs/__init__.py b/docsbox-master/docsbox/docs/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/docsbox-master/docsbox/docs/tasks.py b/docsbox-master/docsbox/docs/tasks.py
new file mode 100644
index 000000000..22c9b3f49
--- /dev/null
+++ b/docsbox-master/docsbox/docs/tasks.py
@@ -0,0 +1,56 @@
+import os
+import shutil
+import datetime
+
+from pylokit import Office
+from wand.image import Image
+from tempfile import NamedTemporaryFile, TemporaryDirectory
+
+from rq import get_current_job
+
+from docsbox import app, rq
+from docsbox.docs.utils import make_zip_archive, make_thumbnails
+
+
+
+@rq.job(timeout=app.config["REDIS_JOB_TIMEOUT"])
+def remove_file(path):
+ """
+ Just removes a file.
+ Used for deleting original files (uploaded by user) and result files (result of converting)
+ """
+ return os.remove(path)
+
+
+@rq.job(timeout=app.config["REDIS_JOB_TIMEOUT"])
+def process_document(path, options, meta):
+ current_task = get_current_job()
+ with Office(app.config["LIBREOFFICE_PATH"]) as office: # acquire libreoffice lock
+ with office.documentLoad(path) as original_document: # open original document
+ with TemporaryDirectory() as tmp_dir: # create temp dir where output'll be stored
+ for fmt in options["formats"]: # iterate over requested formats
+ current_format = app.config["SUPPORTED_FORMATS"][fmt]
+ output_path = os.path.join(tmp_dir, current_format["path"])
+ original_document.saveAs(output_path, fmt=current_format["fmt"])
+ if options.get("thumbnails", None):
+ is_created = False
+ if meta["mimetype"] == "application/pdf":
+ pdf_path = path
+ elif "pdf" in options["formats"]:
+ pdf_path = os.path.join(tmp_dir, "pdf")
+ else:
+ pdf_tmp_file = NamedTemporaryFile()
+ pdf_path = pdf_tmp_file.name
+ original_document.saveAs(pdf_tmp_file.name, fmt="pdf")
+ is_created = True
+ image = Image(filename=pdf_path,
+ resolution=app.config["THUMBNAILS_DPI"])
+ if is_created:
+ pdf_tmp_file.close()
+ thumbnails = make_thumbnails(image, tmp_dir, options["thumbnails"]["size"])
+ result_path, result_url = make_zip_archive(current_task.id, tmp_dir)
+ remove_file.schedule(
+ datetime.timedelta(seconds=app.config["RESULT_FILE_TTL"]),
+ result_path
+ )
+ return result_url
diff --git a/docsbox-master/docsbox/docs/tests/samples/sample.docx b/docsbox-master/docsbox/docs/tests/samples/sample.docx
new file mode 100644
index 000000000..8d78118f9
Binary files /dev/null and b/docsbox-master/docsbox/docs/tests/samples/sample.docx differ
diff --git a/docsbox-master/docsbox/docs/tests/test_views.py b/docsbox-master/docsbox/docs/tests/test_views.py
new file mode 100644
index 000000000..ced97be5a
--- /dev/null
+++ b/docsbox-master/docsbox/docs/tests/test_views.py
@@ -0,0 +1,149 @@
+import os
+import ujson
+import unittest
+import docsbox
+
+
+class BaseTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.app = docsbox.app
+ self.app.config["TESTING"] = True
+ self.app.config["RQ_ASYNC"] = False
+ self.samples = os.path.join(
+ self.app.config["BASE_DIR"],
+ "docs/tests/samples/"
+ )
+ self.client = docsbox.app.test_client()
+
+ def submit_file(self, filename, options):
+ with open(filename, "rb") as source:
+ response = self.client.post("/api/v1/", data={
+ "file": source,
+ "options": ujson.dumps(options),
+ })
+ return response
+
+class DocumentViewTestCase(BaseTestCase):
+
+ def test_get_task_by_valid_uuid(self):
+ filename = os.path.join(self.samples, "sample.docx")
+ response = self.submit_file(filename, {
+ "formats": ["txt"]
+ })
+ json = ujson.loads(response.data)
+ self.assertEqual(response.status_code, 200)
+ self.assertTrue(json.get("id"))
+ self.assertEqual(json.get("status"), "queued")
+ response = self.client.get("/api/v1/{0}".format(json.get("id")))
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(ujson.loads(response.data), {
+ "id": json.get("id"),
+ "status": "queued",
+ "result_url": None,
+ })
+
+ def test_get_task_by_invalid_uuid(self):
+ response = self.client.get("/api/v1/uuid-with-ponies")
+ self.assertEqual(response.status_code, 404)
+ self.assertEqual(ujson.loads(response.data), {
+ "message": "Unknown task_id"
+ })
+
+class DocumentCreateViewTestCase(BaseTestCase):
+
+ def test_submit_without_file(self):
+ response = self.client.post("/api/v1/", data={
+ "options": ujson.dumps(["pdf"])
+ })
+ json = ujson.loads(response.data)
+ self.assertEqual(response.status_code, 400)
+ self.assertEqual(json, {
+ "message": "file field is required"
+ })
+
+ def test_submit_invalid_mimetype(self):
+ response = self.submit_file("/bin/sh", {
+ "formats": ["pdf"],
+ })
+ json = ujson.loads(response.data)
+ self.assertEqual(response.status_code, 400)
+ self.assertEqual(json, {
+ "message": "Not supported mimetype: 'application/x-sharedlib'"
+ })
+
+ def test_submit_empty_formats(self):
+ filename = os.path.join(self.samples, "sample.docx")
+ response = self.submit_file(filename, {
+ "formats": []
+ })
+ json = ujson.loads(response.data)
+ self.assertEqual(response.status_code, 400)
+ self.assertEqual(json, {
+ "message": "Invalid 'formats' value"
+ })
+
+ def test_submit_invalid_formats(self):
+ filename = os.path.join(self.samples, "sample.docx")
+ response = self.submit_file(filename, {
+ "formats": ["csv"]
+ })
+ json = ujson.loads(response.data)
+ self.assertEqual(response.status_code, 400)
+ self.assertEqual(json, {
+ "message": "'application/vnd.openxmlformats-offi"
+ "cedocument.wordprocessingml.document'"
+ " mimetype can't be converted to 'csv'"
+ })
+
+ def test_submit_invalid_thumbnails_field(self):
+ filename = os.path.join(self.samples, "sample.docx")
+ response = self.submit_file(filename, {
+ "formats": ["pdf"],
+ "thumbnails": 1,
+ })
+ json = ujson.loads(response.data)
+ self.assertEqual(response.status_code, 400)
+ self.assertEqual(json, {
+ "message": "Invalid 'thumbnails' value"
+ })
+
+ def test_submit_invalid_thumbnails_size_field(self):
+ filename = os.path.join(self.samples, "sample.docx")
+ response = self.submit_file(filename, {
+ "formats": ["pdf"],
+ "thumbnails": {
+ "size": None,
+ },
+ })
+ json = ujson.loads(response.data)
+ self.assertEqual(response.status_code, 400)
+ self.assertEqual(json, {
+ "message": "Invalid 'size' value"
+ })
+
+ def test_submit_invalid_thumbnails_size_field_value(self):
+ filename = os.path.join(self.samples, "sample.docx")
+ response = self.submit_file(filename, {
+ "formats": ["pdf"],
+ "thumbnails": {
+ "size": "ZZx43",
+ },
+ })
+ json = ujson.loads(response.data)
+ self.assertEqual(response.status_code, 400)
+ self.assertEqual(json, {
+ "message": "Invalid 'size' value"
+ })
+
+ def test_submit_valid_thumbnails(self):
+ filename = os.path.join(self.samples, "sample.docx")
+ response = self.submit_file(filename, {
+ "formats": ["pdf"],
+ "thumbnails": {
+ "size": "640x480",
+ },
+ })
+ json = ujson.loads(response.data)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(json.get("status"), "queued")
diff --git a/docsbox-master/docsbox/docs/utils.py b/docsbox-master/docsbox/docs/utils.py
new file mode 100644
index 000000000..94aebf015
--- /dev/null
+++ b/docsbox-master/docsbox/docs/utils.py
@@ -0,0 +1,40 @@
+import os
+import zipfile
+
+from wand.image import Image
+
+from docsbox import app
+
+
+def make_zip_archive(uuid, tmp_dir):
+ """
+ Creates ZIP archive from given @tmp_dir.
+ """
+ zipname = "{0}.zip".format(uuid)
+ result_path = os.path.join(app.config["MEDIA_PATH"],
+ zipname)
+ result_url = os.path.join(app.config["MEDIA_URL"],
+ zipname)
+ with zipfile.ZipFile(result_path, "w") as output:
+ for dirname, subdirs, files in os.walk(tmp_dir):
+ for filename in files:
+ path = os.path.join(dirname, filename)
+ output.write(path, path.split(tmp_dir)[1])
+ return result_path, result_url
+
+
+def make_thumbnails(image, tmp_dir, size):
+ thumbnails_folder = os.path.join(tmp_dir, "thumbnails/")
+ os.mkdir(thumbnails_folder)
+ (width, height) = size
+ for index, page in enumerate(image.sequence):
+ with Image(page) as page:
+ filename = os.path.join(thumbnails_folder, "{0}.png".format(index))
+ page.resize(width, height)
+ if app.config["THUMBNAILS_QUANTIZE"]:
+ page.quantize(app.config["THUMBNAILS_QUANTIZE_COLORS"],
+ app.config["THUMBNAILS_QUANTIZE_COLORSPACE"], 0, True, True)
+ page.save(filename=filename)
+ else:
+ image.close()
+ return index
diff --git a/docsbox-master/docsbox/docs/views.py b/docsbox-master/docsbox/docs/views.py
new file mode 100644
index 000000000..87bbca0db
--- /dev/null
+++ b/docsbox-master/docsbox/docs/views.py
@@ -0,0 +1,93 @@
+import ujson
+import datetime
+
+from magic import Magic
+from tempfile import NamedTemporaryFile
+
+from flask import request
+from flask_restful import Resource, abort
+
+from docsbox import app, rq
+from docsbox.docs.tasks import remove_file, process_document
+
+
+class DocumentView(Resource):
+
+ def get(self, task_id):
+ """
+ Returns information about task status.
+ """
+ queue = rq.get_queue()
+ task = queue.fetch_job(task_id)
+ if task:
+ return {
+ "id": task.id,
+ "status": task.status,
+ "result_url": task.result
+ }
+ else:
+ return abort(404, message="Unknown task_id")
+
+
+class DocumentCreateView(Resource):
+
+ def post(self):
+ """
+ Recieves file and options, checks file mimetype,
+ validates options and creates converting task
+ """
+ if "file" not in request.files:
+ return abort(400, message="file field is required")
+ else:
+ with NamedTemporaryFile(delete=False, prefix=app.config["MEDIA_PATH"]) as tmp_file:
+ request.files["file"].save(tmp_file)
+ tmp_file.flush()
+ tmp_file.close()
+ remove_file.schedule(
+ datetime.timedelta(seconds=app.config["ORIGINAL_FILE_TTL"])
+ , tmp_file.name)
+ with Magic() as magic: # detect mimetype
+ mimetype = magic.from_file(tmp_file.name)
+ if mimetype not in app.config["SUPPORTED_MIMETYPES"]:
+ return abort(400, message="Not supported mimetype: '{0}'".format(mimetype))
+ options = request.form.get("options", None)
+ if options: # options validation
+ options = ujson.loads(options)
+ formats = options.get("formats", None)
+ if not isinstance(formats, list) or not formats:
+ return abort(400, message="Invalid 'formats' value")
+ else:
+ for fmt in formats:
+ supported = (fmt in app.config["SUPPORTED_MIMETYPES"][mimetype]["formats"])
+ if not supported:
+ message = "'{0}' mimetype can't be converted to '{1}'"
+ return abort(400, message=message.format(mimetype, fmt))
+ thumbnails = options.get("thumbnails", None)
+ if thumbnails:
+ if not isinstance(thumbnails, dict):
+ return abort(400, message="Invalid 'thumbnails' value")
+ else:
+ thumbnails_size = thumbnails.get("size", None)
+ if not isinstance(thumbnails_size, str) or not thumbnails_size:
+ return abort(400, message="Invalid 'size' value")
+ else:
+ try:
+ (width, height) = map(int, thumbnails_size.split("x"))
+ except ValueError:
+ return abort(400, message="Invalid 'size' value")
+ else:
+ options["thumbnails"]["size"] = (width, height)
+ else:
+ if mimetype == "application/pdf":
+ options = {
+ "formats": ["html"]
+ }
+ else:
+ options = app.config["DEFAULT_OPTIONS"]
+ task = process_document.queue(tmp_file.name, options, {
+ "mimetype": mimetype,
+ })
+ return {
+ "id": task.id,
+ "status": task.status,
+ }
diff --git a/docsbox-master/docsbox/requirements.txt b/docsbox-master/docsbox/requirements.txt
new file mode 100644
index 000000000..4b04efc50
--- /dev/null
+++ b/docsbox-master/docsbox/requirements.txt
@@ -0,0 +1,14 @@
+#git+https://github.com/nvie/rq.git#egg=rq
+#git+https://github.com/nvie/rq.git#egg=rq
+#rq
+rq-scheduler==0.7.0
+Flask==0.11.1
+Flask-RESTful==0.3.5
+Flask-RQ2==16.0.2
+flask-env-settings==0.1.0
+gunicorn==19.6.0
+pylokit==0.8.1
+python-libmagic==0.4.0
+wand==0.4.3
+ujson==1.35
+nose==1.3.7
diff --git a/docsbox-master/docsbox/settings.py b/docsbox-master/docsbox/settings.py
new file mode 100644
index 000000000..a66b45248
--- /dev/null
+++ b/docsbox-master/docsbox/settings.py
@@ -0,0 +1,98 @@
+import os
+
+REDIS_URL = os.environ.get("REDIS_URL", "redis://redis:6379/0")
+RQ_REDIS_URL = REDIS_URL
+
+BASE_DIR = os.path.abspath(os.path.dirname(__file__))
+MEDIA_PATH = os.path.join(BASE_DIR, "media/")
+MEDIA_URL = "/media/"
+
+SUPPORTED_FORMATS = {
+ "pdf": {
+ "path": "pdf",
+ "fmt": "pdf",
+ },
+ "txt": {
+ "path": "txt",
+ "fmt": "txt",
+ },
+ "html": {
+ "path": "html",
+ "fmt": "html",
+ },
+ "csv": {
+ "path": "csv",
+ "fmt": "csv",
+ }
+}
+
+DOCUMENT_EXPORT_FORMATS = ["pdf", "txt", "html"]
+SPREADSHEET_EXPORT_FORMATS = ["pdf", "csv", "html"]
+PRESENTATION_EXPORT_FORMATS = ["pdf", "html"]
+PDF_EXPORT_FORMATS = ["html"]
+
+SUPPORTED_MIMETYPES = {
+ # Microsoft Word 2003
+ "application/msword": {
+ "formats": DOCUMENT_EXPORT_FORMATS,
+ },
+
+ # Microsoft Word 2007
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": {
+ "formats": DOCUMENT_EXPORT_FORMATS,
+ },
+
+ # LibreOffice Writer
+ "application/vnd.oasis.opendocument.text": {
+ "formats": DOCUMENT_EXPORT_FORMATS,
+ },
+
+ # Portable Document Format
+ "application/pdf": {
+ "formats": PDF_EXPORT_FORMATS,
+ },
+
+# Portable Document Format
+ "application/octet-stream": {
+ "formats": DOCUMENT_EXPORT_FORMATS,
+ },
+
+ # Rich Text Format
+ "text/rtf": {
+ "formats": DOCUMENT_EXPORT_FORMATS,
+ },
+
+ # Microsoft Excel 2003
+ "application/vnd.ms-excel": {
+ "formats": SPREADSHEET_EXPORT_FORMATS,
+ },
+
+ # Microsoft Excel 2007
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
+ "formats": SPREADSHEET_EXPORT_FORMATS,
+ },
+
+ # LibreOffice Calc
+ "application/vnd.oasis.opendocument.spreadsheet": {
+ "formats": SPREADSHEET_EXPORT_FORMATS,
+ },
+
+ # Microsoft Powerpoint 2003
+ "application/vnd.ms-powerpoint": {
+ "formats": PRESENTATION_EXPORT_FORMATS,
+ },
+
+ # Microsoft Powerpoint 2007
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation": {
+ "formats": PRESENTATION_EXPORT_FORMATS,
+ },
+
+ # LibreOffice Impress
+ "application/vnd.oasis.opendocument.presentation": {
+ "formats": PRESENTATION_EXPORT_FORMATS,
+ },
+}
+
+DEFAULT_OPTIONS = {
+ "formats": ["pdf"]
+}
diff --git a/docsbox-master/nginx/Dockerfile b/docsbox-master/nginx/Dockerfile
new file mode 100644
index 000000000..2a996964e
--- /dev/null
+++ b/docsbox-master/nginx/Dockerfile
@@ -0,0 +1,3 @@
+FROM tutum/nginx
+RUN rm /etc/nginx/sites-enabled/default
+ADD sites-enabled/ /etc/nginx/sites-enabled
diff --git a/docsbox-master/nginx/sites-enabled/docsbox b/docsbox-master/nginx/sites-enabled/docsbox
new file mode 100644
index 000000000..6b0ddf60b
--- /dev/null
+++ b/docsbox-master/nginx/sites-enabled/docsbox
@@ -0,0 +1,20 @@
+server {
+
+ listen 80;
+
+ client_max_body_size 10m;
+
+ charset utf-8;
+
+ location /media {
+ alias /home/docsbox/media;
+ }
+
+ location / {
+ proxy_pass http://web:8000;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ }
+
+}
diff --git a/elastic-config/elasticsearch-custom.yml b/elastic-config/elasticsearch-custom.yml
new file mode 100644
index 000000000..62c2ff915
--- /dev/null
+++ b/elastic-config/elasticsearch-custom.yml
@@ -0,0 +1,3 @@
+transport.host: 0.0.0.0
+network.host: 0.0.0.0
+xpack.security.enabled: false
\ No newline at end of file