Skip to content

Commit

Permalink
Save from editor
Browse files Browse the repository at this point in the history
Fixes #599

Implement the save controller in the API
Use it from the menu
Make sure that we can deal with returned text instead of JSON
  • Loading branch information
dickschoeller committed Mar 29, 2018
1 parent 9caf396 commit bdbe6fe
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package org.schoellerfamily.gedbrowser.api.controller;

import java.util.Collections;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.schoellerfamily.gedbrowser.api.controller.exception.DataSetNotFoundException;
import org.schoellerfamily.gedbrowser.datamodel.Root;
import org.schoellerfamily.gedbrowser.persistence.mongo.loader.GedDocumentFileLoader;
import org.schoellerfamily.gedbrowser.writer.GedWriterLine;
import org.schoellerfamily.gedbrowser.writer.creator.GedObjectToGedWriterVisitor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/**
* Emits GEDCOM to the HTTP connection to download the GEDCOM state.
*
* @author Dick Schoeller
*/
@CrossOrigin(origins = {
"http://largo.schoellerfamily.org:4200", "http://localhost:4200" })
@RestController
public class SaveController {
/** Logger. */
private final transient Log logger = LogFactory.getLog(getClass());

/** */
@Autowired
private transient GedDocumentFileLoader loader;

/**
* Connects HTML template file with data for saving the GEDCOM file.
*
* @param db name of database for the lookup
* @param response the servlet response object, needed for tweaking headers
* @return a string identifying which HTML template to use.
*/
@GetMapping(value = "/v1/dbs/{db}/save",
produces = MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
public final ResponseEntity<String> save(@PathVariable final String db,
final HttpServletResponse response) {
logger.info("Starting save");
// check here whether authorized.
final Root root = fetchRoot(db);

setHeaders(response, root);

final String contents = renderGedFile(root);
final String filename = root.getDbName() + ".ged";
logger.info("filename: " + filename);
final HttpHeaders headers = new HttpHeaders();
headers.setAccessControlExposeHeaders(
Collections.singletonList("Content-Disposition"));
headers.set("Content-Disposition", "attachment; filename=" + filename);
headers.setContentType(MediaType.TEXT_PLAIN);
logger.info("Exiting save");
return new ResponseEntity<>(contents, headers, HttpStatus.OK);
}

/**
* @param dbName the name of the database
* @return the root object
*/
protected final Root fetchRoot(final String dbName) {
final Root root = loader.loadDocument(dbName).getGedObject();
if (root == null) {
throw new DataSetNotFoundException(
"Data set " + dbName + " not found", dbName);
}
return root;
}

/**
* @param root the dataset to render
* @return the gedcom file as a string
*/
private String renderGedFile(final Root root) {
final GedObjectToGedWriterVisitor visitor =
new GedObjectToGedWriterVisitor();
root.accept(visitor);
final StringBuilder builder = new StringBuilder();
for (final GedWriterLine line : visitor.getLines()) {
if (line.getLine().isEmpty()) {
continue;
}
builder.append(line.getLine()).append("\n");
}
return builder.toString();
}

/**
* Fill in response headers to make this save the file instead of
* displaying it.
*
* @param response the servlet response
* @param root the root of the dataset being saved
*/
private void setHeaders(final HttpServletResponse response,
final Root root) {
response.setHeader("content-type", "application/octet-stream");
response.setHeader("content-disposition",
"attachment; filename=" + getSaveFilename(root));
}

/**
* @param root the root object we are saving
* @return the href string with the base filename.
*/
private String getSaveFilename(final Root root) {
final String filename = root.getFilename();
final int lastIndexOf = filename.lastIndexOf("/");
if (lastIndexOf == -1) {
return filename;
}
return filename.substring(lastIndexOf + 1);
}
}
5 changes: 5 additions & 0 deletions gedbrowser-client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions gedbrowser-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,17 @@
"@schematics/schematics": "^0.4.8",
"angular-in-memory-web-api": "0.6.0",
"core-js": "^2.5.3",
"file-saver": "^1.3.8",
"font-awesome": "^4.7.0",
"hammerjs": "^2.0.8",
"https-proxy-agent": "^2.2.1",
"moment": "^2.21.0",
"ng2-page-scroll": "^4.0.0-beta.12",
"ngx-gallery": "^5.3.0",
"npm": "^5.6.0",
"rxjs": "^5.5.6",
"web-animations-js": "^2.3.1",
"zone.js": "^0.8.20",
"https-proxy-agent": "^2.2.1"
"zone.js": "^0.8.20"
},
"devDependencies": {
"@angular/cli": "^1.7.3",
Expand Down
4 changes: 4 additions & 0 deletions gedbrowser-client/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
<mat-icon>people_outline</mat-icon>
<span>Submitters</span>
</button>
<button mat-menu-item (click)="saveFile()">
<mat-icon>save</mat-icon>
<span>Save</span>
</button>
</mat-menu>

<mat-toolbar class="menubar" color="primary">
Expand Down
19 changes: 18 additions & 1 deletion gedbrowser-client/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
import { Component } from '@angular/core';
import {HttpResponse} from '@angular/common/http';
import {Component} from '@angular/core';
import {saveAs} from 'file-saver/FileSaver';
import {SaveService} from './shared/services';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'gedbrowser';

constructor(private saveService: SaveService) { }

saveFile() {
this.saveService.getTextFile('schoeller').subscribe(
results => this.saveToFileSystem(results)
);
}

private saveToFileSystem(response) {
const blob = new Blob([response], {type: 'text/plain'});
saveAs(blob, 'schoeller.ged');
}
}
2 changes: 2 additions & 0 deletions gedbrowser-client/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import {
SourceService,
SpouseService,
SubmitterService,
SaveService,
SharedModule
} from './shared';

Expand Down Expand Up @@ -132,6 +133,7 @@ const rootRouting: ModuleWithProviders = RouterModule.forRoot([], { useHash: tru
SpouseService,
SourceService,
SubmitterService,
SaveService
],
bootstrap: [AppComponent]
})
Expand Down
1 change: 1 addition & 0 deletions gedbrowser-client/src/app/shared/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './source.service';
export * from './submitter.service';
export * from './spouse.service';
export * from './parent.service';
export * from './save.service';
15 changes: 15 additions & 0 deletions gedbrowser-client/src/app/shared/services/save.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { TestBed, inject } from '@angular/core/testing';

import { PersonService } from './person.service';

describe('PersonService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [PersonService]
});
});

it('should be created', inject([PersonService], (service: PersonService) => {
expect(service).toBeTruthy();
}));
});
16 changes: 16 additions & 0 deletions gedbrowser-client/src/app/shared/services/save.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {Injectable} from '@angular/core';
import {HttpClient, HttpHeaders, HttpResponse} from '@angular/common/http';
import {Observable} from 'rxjs/Observable';

/**
* Service for obtaining saving a db.
*/
@Injectable()
export class SaveService {
constructor(private http: HttpClient) {}

getTextFile(db: string) {
return this.http.get(
'http://largo.schoellerfamily.org:9084/gedbrowser-api/v1/dbs/' + db + '/save', {responseType: 'text'});
}
}

0 comments on commit bdbe6fe

Please sign in to comment.