automatic project update

This commit is contained in:
Julien Dubois 2016-03-23 16:27:37 +01:00
parent 231b2d9297
commit ed41ad9816
1822 changed files with 66055 additions and 41755 deletions

6
.eslintignore Normal file
View File

@ -0,0 +1,6 @@
# more info here - http://eslint.org/docs/user-guide/configuring.html#ignoring-files-and-directories
# node_modules ignored by default
# ignore bower_components
src/main/webapp/bower_components

29
.eslintrc.json Normal file
View File

@ -0,0 +1,29 @@
// See: http://eslint.org/
// See: https://www.npmjs.com/package/eslint-plugin-angular
{
"extends": [
"eslint:recommended",
"angular"
],
"env": {
"node": true,
"browser": true
},
// severity for a rule should be one of the following: 0 = off, 1 = warning, 2 = error
"rules": {
// coding style
"wrap-iife": [2, "inside"],
"eqeqeq": 2,
"no-use-before-define": [2, "nofunc"],
"no-unused-vars": [2, {"vars": "local", "args": "none"}],
"no-multi-str": 2,
"no-irregular-whitespace": 2,
"semi": [2, "always"],
"indent": 2,
// os/git options
// we want to run on all OSes
"linebreak-style": 0,
"eol-last": 2
}
}

7
.gitignore vendored
View File

@ -1,7 +1,7 @@
###################### ######################
# Project Specific # Project Specific
###################### ######################
/src/main/webapp/dist /target/www/**
###################### ######################
# Node # Node
@ -126,3 +126,8 @@ Desktop.ini
# Maven Wrapper # Maven Wrapper
###################### ######################
!.mvn/wrapper/maven-wrapper.jar !.mvn/wrapper/maven-wrapper.jar
######################
# ESLint
######################
.eslintcache

View File

@ -1 +1 @@
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.3/apache-maven-3.3.3-bin.zip distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip

View File

@ -2,9 +2,9 @@ language: node_js
node_js: node_js:
- "0.12" - "0.12"
sudo: false sudo: false
before_install: npm install -g grunt-cli before_install: npm install -g gulp
install: npm install install: npm install
before_script: grunt build before_script: gulp build
notifications: notifications:
webhooks: webhooks:
on_success: change # options: [always|never|change] default: always on_success: change # options: [always|never|change] default: always

View File

@ -12,15 +12,20 @@
"prodDatabaseType": "mysql", "prodDatabaseType": "mysql",
"useCompass": false, "useCompass": false,
"buildTool": "maven", "buildTool": "maven",
"frontendBuilder": "grunt",
"javaVersion": "8",
"rememberMeKey": "5c37379956bd1242f5636c8cb322c2966ad81277", "rememberMeKey": "5c37379956bd1242f5636c8cb322c2966ad81277",
"searchEngine": "no", "searchEngine": "no",
"enableTranslation": true, "enableTranslation": true,
"useSass": false, "applicationType": "monolith",
"testFrameworks": [ "testFrameworks": [
"gatling" "gatling",
"protractor"
], ],
"enableSocialSignIn": false "languages": [
"en"
],
"serverPort": 8080,
"jhipsterVersion": "3.0.0",
"enableSocialSignIn": false,
"useSass": false
} }
} }

View File

@ -1,362 +0,0 @@
// Generated on 2016-03-07 using generator-jhipster 2.27.2
'use strict';
var fs = require('fs');
var parseString = require('xml2js').parseString;
// Returns the second occurence of the version number
var parseVersionFromPomXml = function() {
var version;
var pomXml = fs.readFileSync('pom.xml', "utf8");
parseString(pomXml, function (err, result){
if (result.project.version && result.project.version[0]) {
version = result.project.version[0];
} else if (result.project.parent && result.project.parent[0] && result.project.parent[0].version && result.project.parent[0].version[0]) {
version = result.project.parent[0].version[0]
} else {
throw new Error('pom.xml is malformed. No version is defined');
}
});
return version;
};
// usemin custom step
var useminAutoprefixer = {
name: 'autoprefixer',
createConfig: function(context, block) {
if(block.src.length === 0) {
return {};
} else {
return require('grunt-usemin/lib/config/cssmin').createConfig(context, block) // Reuse cssmins createConfig
}
}
};
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
require('time-grunt')(grunt);
grunt.initConfig({
yeoman: {
// configurable paths
app: require('./bower.json').appPath || 'app',
dist: 'src/main/webapp/dist'
},
watch: {
bower: {
files: ['bower.json'],
tasks: ['wiredep']
},
ngconstant: {
files: ['Gruntfile.js', 'pom.xml'],
tasks: ['ngconstant:dev']
}
},
autoprefixer: {
// src and dest is configured in a subtask called "generated" by usemin
},
wiredep: {
app: {
src: ['src/main/webapp/index.html'],
exclude: [
/angular-i18n/ // localizations are loaded dynamically
]
},
test: {
src: 'src/test/javascript/karma.conf.js',
exclude: [/angular-i18n/, /angular-scenario/],
ignorePath: /\.\.\/\.\.\//, // remove ../../ from paths of injected javascripts
devDependencies: true,
fileTypes: {
js: {
block: /(([\s\t]*)\/\/\s*bower:*(\S*))(\n|\r|.)*?(\/\/\s*endbower)/gi,
detect: {
js: /'(.*\.js)'/gi
},
replace: {
js: '\'{{filePath}}\','
}
}
}
}
},
browserSync: {
dev: {
bsFiles: {
src : [
'src/main/webapp/**/*.html',
'src/main/webapp/**/*.json',
'src/main/webapp/assets/styles/**/*.css',
'src/main/webapp/scripts/**/*.{js,html}',
'src/main/webapp/assets/images/**/*.{png,jpg,jpeg,gif,webp,svg}',
'tmp/**/*.{css,js}'
]
}
},
options: {
watchTask: true,
proxy: {
target: "localhost:8080",
proxyOptions: {
xfwd: true
}
}
}
},
clean: {
dist: {
files: [{
dot: true,
src: [
'.tmp',
'<%= yeoman.dist %>/*',
'!<%= yeoman.dist %>/.git*'
]
}]
},
server: '.tmp'
},
jshint: {
options: {
jshintrc: '.jshintrc'
},
all: [
'Gruntfile.js',
'src/main/webapp/scripts/app.js',
'src/main/webapp/scripts/app/**/*.js',
'src/main/webapp/scripts/components/**/*.js'
]
},
concat: {
// src and dest is configured in a subtask called "generated" by usemin
},
uglifyjs: {
// src and dest is configured in a subtask called "generated" by usemin
},
rev: {
dist: {
files: {
src: [
'<%= yeoman.dist %>/scripts/**/*.js',
'<%= yeoman.dist %>/assets/styles/**/*.css',
'<%= yeoman.dist %>/assets/images/**/*.{png,jpg,jpeg,gif,webp,svg}',
'<%= yeoman.dist %>/assets/fonts/*'
]
}
}
},
useminPrepare: {
html: 'src/main/webapp/index.html',
options: {
dest: '<%= yeoman.dist %>',
flow: {
html: {
steps: {
js: ['concat', 'uglifyjs'],
css: ['cssmin', useminAutoprefixer] // Let cssmin concat files so it corrects relative paths to fonts and images
},
post: {}
}
}
}
},
usemin: {
html: ['<%= yeoman.dist %>/**/*.html'],
css: ['<%= yeoman.dist %>/assets/styles/**/*.css'],
js: ['<%= yeoman.dist %>/scripts/**/*.js'],
options: {
assetsDirs: ['<%= yeoman.dist %>', '<%= yeoman.dist %>/assets/styles', '<%= yeoman.dist %>/assets/images', '<%= yeoman.dist %>/assets/fonts'],
patterns: {
js: [
[/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the JS to reference our revved images']
]
},
dirs: ['<%= yeoman.dist %>']
}
},
imagemin: {
dist: {
files: [{
expand: true,
cwd: 'src/main/webapp/assets/images',
src: '**/*.{jpg,jpeg}', // we don't optimize PNG files as it doesn't work on Linux. If you are not on Linux, feel free to use '**/*.{png,jpg,jpeg}'
dest: '<%= yeoman.dist %>/assets/images'
}]
}
},
svgmin: {
dist: {
files: [{
expand: true,
cwd: 'src/main/webapp/assets/images',
src: '**/*.svg',
dest: '<%= yeoman.dist %>/assets/images'
}]
}
},
cssmin: {
// src and dest is configured in a subtask called "generated" by usemin
},
ngtemplates: {
dist: {
cwd: 'src/main/webapp',
src: ['scripts/app/**/*.html', 'scripts/components/**/*.html',],
dest: '.tmp/templates/templates.js',
options: {
module: 'sampleapplicationApp',
usemin: 'scripts/app.js',
htmlmin: '<%= htmlmin.dist.options %>'
}
}
},
htmlmin: {
dist: {
options: {
removeCommentsFromCDATA: true,
// https://github.com/yeoman/grunt-usemin/issues/44
collapseWhitespace: true,
collapseBooleanAttributes: true,
conservativeCollapse: true,
removeAttributeQuotes: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
keepClosingSlash: true
},
files: [{
expand: true,
cwd: '<%= yeoman.dist %>',
src: ['*.html'],
dest: '<%= yeoman.dist %>'
}]
}
},
// Put files not handled in other tasks here
copy: {
fonts: {
files: [{
expand: true,
dot: true,
flatten: true,
cwd: 'src/main/webapp',
dest: '<%= yeoman.dist %>/assets/fonts',
src: [
'bower_components/bootstrap/fonts/*.*'
]
}]
},
dist: {
files: [{
expand: true,
dot: true,
cwd: 'src/main/webapp',
dest: '<%= yeoman.dist %>',
src: [
'*.html',
'scripts/**/*.html',
'assets/images/**/*.{png,gif,webp,jpg,jpeg,svg}',
'assets/fonts/*'
]
}, {
expand: true,
cwd: '.tmp/assets/images',
dest: '<%= yeoman.dist %>/assets/images',
src: [
'generated/*'
]
}]
}
},
karma: {
unit: {
configFile: 'src/test/javascript/karma.conf.js',
singleRun: true
}
},
ngAnnotate: {
dist: {
files: [{
expand: true,
cwd: '.tmp/concat/scripts',
src: '*.js',
dest: '.tmp/concat/scripts'
}]
}
},
buildcontrol: {
options: {
commit: true,
push: false,
connectCommits: false,
message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%'
}
},
ngconstant: {
options: {
name: 'sampleapplicationApp',
deps: false,
wrap: '"use strict";\n// DO NOT EDIT THIS FILE, EDIT THE GRUNT TASK NGCONSTANT SETTINGS INSTEAD WHICH GENERATES THIS FILE\n{%= __ngModule %}'
},
dev: {
options: {
dest: 'src/main/webapp/scripts/app/app.constants.js'
},
constants: {
ENV: 'dev',
VERSION: parseVersionFromPomXml()
}
},
prod: {
options: {
dest: '.tmp/scripts/app/app.constants.js'
},
constants: {
ENV: 'prod',
VERSION: parseVersionFromPomXml()
}
}
}
});
grunt.registerTask('serve', [
'clean:server',
'wiredep',
'ngconstant:dev',
'browserSync',
'watch'
]);
grunt.registerTask('server', function (target) {
grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
grunt.task.run([target ? ('serve:' + target) : 'serve']);
});
grunt.registerTask('test', [
'clean:server',
'wiredep:test',
'ngconstant:dev',
'karma'
]);
grunt.registerTask('build', [
'clean:dist',
'wiredep:app',
'ngconstant:prod',
'useminPrepare',
'ngtemplates',
'imagemin',
'svgmin',
'concat',
'copy:fonts',
'copy:dist',
'ngAnnotate',
'cssmin',
'autoprefixer',
'uglify',
'rev',
'usemin',
'htmlmin'
]);
grunt.registerTask('default', ['serve']);
};

View File

@ -2,6 +2,8 @@
This application was generated using JHipster, you can find documentation and help at [https://jhipster.github.io](https://jhipster.github.io). This application was generated using JHipster, you can find documentation and help at [https://jhipster.github.io](https://jhipster.github.io).
## Development
Before you can build this project, you must install and configure the following dependencies on your machine: Before you can build this project, you must install and configure the following dependencies on your machine:
1. [Node.js][]: We use Node to run a development web server and build the project. 1. [Node.js][]: We use Node to run a development web server and build the project.
@ -12,25 +14,26 @@ After installing Node, you should be able to run the following command to instal
npm install npm install
We use [Grunt][] as our build system. Install the grunt command-line tool globally with: We use [Gulp][] as our build system. Install the Gulp command-line tool globally with:
npm install -g grunt-cli npm install -g gulp
Run the following commands in two separate terminals to create a blissful development experience where your browser Run the following commands in two separate terminals to create a blissful development experience where your browser
auto-refreshes when files change on your hard drive. auto-refreshes when files change on your hard drive.
mvn ./mvnw
grunt gulp
Bower is used to manage CSS and JavaScript dependencies used in this application. You can upgrade dependencies by Bower is used to manage CSS and JavaScript dependencies used in this application. You can upgrade dependencies by
specifying a newer version in `bower.json`. You can also run `bower update` and `bower install` to manage dependencies. specifying a newer version in `bower.json`. You can also run `bower update` and `bower install` to manage dependencies.
Add the `-h` flag on any command to see how you can use it. For example, `bower update -h`. Add the `-h` flag on any command to see how you can use it. For example, `bower update -h`.
# Building for production
## Building for production
To optimize the sampleApplication client for production, run: To optimize the sampleApplication client for production, run:
mvn -Pprod clean package ./mvnw -Pprod clean package
This will concatenate and minify CSS and JavaScript files. It will also modify `index.html` so it references This will concatenate and minify CSS and JavaScript files. It will also modify `index.html` so it references
these new files. these new files.
@ -41,15 +44,16 @@ To ensure everything worked, run:
Then navigate to [http://localhost:8080](http://localhost:8080) in your browser. Then navigate to [http://localhost:8080](http://localhost:8080) in your browser.
# Testing ## Testing
Unit tests are run by [Karma][] and written with [Jasmine][]. They're located in `src/test/javascript` and can be run with: Unit tests are run by [Karma][] and written with [Jasmine][]. They're located in `src/test/javascript/` and can be run with:
grunt test gulp test
UI end-to-end tests are powered by [Protractor][], which is built on top of WebDriverJS. They're located in `src/test/javascript/e2e`
and can be run by starting Spring Boot in one terminal (`./mvnw spring-boot:run`) and running the tests (`gulp itest`) in a second one.
## Continuous Integration
# Continuous Integration
To setup this project in Jenkins, use the following configuration: To setup this project in Jenkins, use the following configuration:
@ -62,13 +66,21 @@ To setup this project in Jenkins, use the following configuration:
* Poll SCM / Schedule: `H/5 * * * *` * Poll SCM / Schedule: `H/5 * * * *`
* Build * Build
* Invoke Maven / Tasks: `-Pprod clean package` * Invoke Maven / Tasks: `-Pprod clean package`
* Execute Shell / Command:
````
./mvnw spring-boot:run &
bootPid=$!
sleep 30s
gulp itest
kill $bootPid
````
* Post-build Actions * Post-build Actions
* Publish JUnit test result report / Test Report XMLs: `build/test-results/*.xml` * Publish JUnit test result report / Test Report XMLs: `build/test-results/*.xml,build/reports/e2e/*.xml`
[JHipster]: https://jhipster.github.io/ [JHipster]: https://jhipster.github.io/
[Node.js]: https://nodejs.org/ [Node.js]: https://nodejs.org/
[Bower]: http://bower.io/ [Bower]: http://bower.io/
[Grunt]: http://gruntjs.com/ [Gulp]: http://gulpjs.com/
[BrowserSync]: http://www.browsersync.io/ [BrowserSync]: http://www.browsersync.io/
[Karma]: http://karma-runner.github.io/ [Karma]: http://karma-runner.github.io/
[Jasmine]: http://jasmine.github.io/2.0/introduction.html [Jasmine]: http://jasmine.github.io/2.0/introduction.html

View File

@ -1,49 +1,62 @@
{ {
"name": "sampleApplication",
"version": "0.0.0", "version": "0.0.0",
"appPath": "src/main/webapp", "name": "sampleApplication",
"appPath": "src/main/webapp/",
"testPath": "src/test/javascript/spec", "testPath": "src/test/javascript/spec",
"dependencies": { "dependencies": {
"angular": "1.4.8", "angular": "1.5.2",
"angular-aria": "1.4.8", "angular-aria": "1.5.2",
"angular-bootstrap": "0.14.3", "angular-bootstrap": "1.2.5",
"angular-cache-buster": "0.4.3", "angular-cache-buster": "0.4.3",
"angular-cookies": "1.4.8", "angular-cookies": "1.5.2",
"angular-dynamic-locale": "0.1.28", "angular-dynamic-locale": "0.1.30",
"angular-i18n": "1.4.8", "angular-i18n": "1.5.2",
"angular-local-storage": "0.2.3", "ngstorage": "0.3.10",
"angular-loading-bar": "0.8.0", "angular-loading-bar": "0.9.0",
"angular-resource": "1.4.8", "angular-resource": "1.5.2",
"angular-sanitize": "1.4.8", "angular-sanitize": "1.5.2",
"angular-translate": "2.8.1", "angular-translate": "2.11.0",
"angular-translate-interpolation-messageformat": "2.8.1", "angular-translate-interpolation-messageformat": "2.11.0",
"angular-translate-loader-partial": "2.8.1", "angular-translate-loader-partial": "2.11.0",
"angular-translate-storage-cookie": "2.8.1", "angular-translate-storage-cookie": "2.11.0",
"angular-ui-router": "0.2.15", "angular-ui-router": "0.2.18",
"bootstrap": "3.3.5", "bootstrap": "3.3.6",
"jquery": "2.1.4", "bootstrap-ui-datetime-picker": "2.2.3",
"jquery": "2.2.2",
"json3": "3.3.2", "json3": "3.3.2",
"modernizr": "3.2.0", "modernizr": "3.3.1",
"ng-file-upload": "10.0.2", "ng-file-upload": "12.0.4",
"ngInfiniteScroll": "1.2.1", "ngInfiniteScroll": "1.2.2",
"swagger-ui": "2.1.3" "swagger-ui": "2.1.4"
}, },
"devDependencies": { "devDependencies": {
"angular-mocks": "1.4.8", "angular-mocks": "1.5.2",
"angular-scenario": "1.4.8" "angular-scenario": "1.5.2"
}, },
"overrides": { "overrides": {
"angular": {
"dependencies": {
"jquery": "2.2.2"
}
},
"bootstrap": { "bootstrap": {
"main": [ "main": [
"dist/js/bootstrap.js", "dist/js/bootstrap.js",
"dist/css/bootstrap.css", "dist/css/bootstrap.css",
"less/bootstrap.less" "less/bootstrap.less"
] ]
},
"messageformat": {
"main": [
"messageformat.js",
"locale/en.js"
]
} }
}, },
"resolutions": { "resolutions": {
"angular": "1.4.8", "angular": "1.5.2",
"angular-cookies": "1.4.8", "angular-cookies": "1.5.2",
"jquery": "2.1.4" "angular-bootstrap": "1.2.5",
"jquery": "2.2.2"
} }
} }

50
gulp/build.js Normal file
View File

@ -0,0 +1,50 @@
var fs = require('fs'),
gulp = require('gulp'),
lazypipe = require('lazypipe'),
footer = require('gulp-footer'),
sourcemaps = require('gulp-sourcemaps'),
rev = require('gulp-rev'),
htmlmin = require('gulp-htmlmin'),
ngAnnotate = require('gulp-ng-annotate'),
prefix = require('gulp-autoprefixer'),
cssnano = require('gulp-cssnano'),
uglify = require('gulp-uglify'),
useref = require("gulp-useref"),
revReplace = require("gulp-rev-replace")
plumber = require('gulp-plumber'),
gulpIf = require('gulp-if'),
handleErrors = require('./handleErrors');
var config = require('./config');
var initTask = lazypipe()
.pipe(sourcemaps.init)
.pipe(footer, ';');
var jsTask = lazypipe()
.pipe(ngAnnotate)
.pipe(uglify);
var cssTask = lazypipe()
.pipe(prefix)
.pipe(cssnano);
module.exports = function() {
var templates = fs.readFileSync(config.tmp + '/templates.js');
var manifest = gulp.src(config.revManifest);
return gulp.src([config.app + '**/*.html',
'!' + config.app + 'app/**/*.html',
'!' + config.app + 'swagger-ui/**/*',
'!' + config.bower + '**/*.html'])
.pipe(plumber({errorHandler: handleErrors}))
//init sourcemaps and prepend semicolon
.pipe(useref({}, initTask))
//append html templates
.pipe(gulpIf('**/app.js', footer(templates)))
.pipe(gulpIf('*.js', jsTask()))
.pipe(gulpIf('*.css', cssTask()))
.pipe(gulpIf('*.html', htmlmin({collapseWhitespace: true})))
.pipe(gulpIf('**/*.!(html)', rev()))
.pipe(revReplace({manifest}))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(config.dist));
}

12
gulp/config.js Normal file
View File

@ -0,0 +1,12 @@
module.exports = {
app: 'src/main/webapp/',
dist: 'target/www/',
test: 'src/test/javascript/',
bower: 'src/main/webapp/bower_components/',
tmp: 'target/tmp',
revManifest: 'target/tmp/rev-manifest.json',
port: 9000,
apiPort: 8080,
liveReloadPort: 35729,
uri: 'http://localhost:'
};

20
gulp/handleErrors.js Normal file
View File

@ -0,0 +1,20 @@
var notify = require("gulp-notify");
var argv = require('yargs').argv;
module.exports = function() {
var args = Array.prototype.slice.call(arguments);
var notification = argv.notification === undefined ? true : argv.notification;
// Send error to notification center with gulp-notify
if(notification) {
notify.onError({
title: "JHipster Gulp Build",
subtitle: "Failure!",
message: "Error: <%= error.message %>",
sound: "Beep"
}).apply(this, args);
}
// Keep gulp from hanging on this task
this.emit('end');
};

60
gulp/serve.js Normal file
View File

@ -0,0 +1,60 @@
var gulp = require('gulp'),
util = require('./utils'),
url = require('url'),
browserSync = require('browser-sync'),
proxy = require('proxy-middleware');
var config = require('./config');
module.exports = function () {
var baseUri = config.uri + config.apiPort;
// Routes to proxy to the backend. Routes ending with a / will setup
// a redirect so that if accessed without a trailing slash, will
// redirect. This is required for some endpoints for proxy-middleware
// to correctly handle them.
var proxyRoutes = [
'/'
];
var requireTrailingSlash = proxyRoutes.filter(function (r) {
return util.endsWith(r, '/');
}).map(function (r) {
// Strip trailing slash so we can use the route to match requests
// with non trailing slash
return r.substr(0, r.length - 1);
});
var proxies = [
// Ensure trailing slash in routes that require it
function (req, res, next) {
requireTrailingSlash.forEach(function (route){
if (url.parse(req.url).path === route) {
res.statusCode = 301;
res.setHeader('Location', route + '/');
res.end();
}
});
next();
}
]
.concat(
// Build a list of proxies for routes: [route1_proxy, route2_proxy, ...]
proxyRoutes.map(function (r) {
var options = url.parse(baseUri + r);
options.route = r;
options.preserveHost = true;
return proxy(options);
}));
browserSync({
open: true,
port: config.port,
server: {
baseDir: config.app,
middleware: proxies
}
});
gulp.start('watch');
}

36
gulp/utils.js Normal file
View File

@ -0,0 +1,36 @@
'use strict';
var fs = require('fs');
module.exports = {
endsWith : endsWith,
parseVersion : parseVersion,
isLintFixed : isLintFixed
}
function endsWith(str, suffix) {
return str.indexOf('/', str.length - suffix.length) !== -1;
};
var parseString = require('xml2js').parseString;
// return the version number from `pom.xml` file
function parseVersion() {
var version;
var pomXml = fs.readFileSync('pom.xml', 'utf8');
parseString(pomXml, function (err, result) {
if (result.project.version && result.project.version[0]) {
version = result.project.version[0];
} else if (result.project.parent && result.project.parent[0] && result.project.parent[0].version && result.project.parent[0].version[0]) {
version = result.project.parent[0].version[0];
} else {
throw new Error('pom.xml is malformed. No version is defined');
}
});
return version;
}
function isLintFixed(file) {
// Has ESLint fixed the file contents?
return file.eslint !== null && file.eslint.fixed;
}

275
gulpfile.js Normal file
View File

@ -0,0 +1,275 @@
// Generated on 2016-03-23 using generator-jhipster 3.0.0
'use strict';
var gulp = require('gulp'),
rev = require('gulp-rev'),
templateCache = require('gulp-angular-templatecache'),
htmlmin = require('gulp-htmlmin'),
imagemin = require('gulp-imagemin'),
ngConstant = require('gulp-ng-constant-fork'),
eslint = require('gulp-eslint'),
argv = require('yargs').argv,
gutil = require('gulp-util'),
protractor = require('gulp-protractor').protractor,
es = require('event-stream'),
flatten = require('gulp-flatten'),
del = require('del'),
wiredep = require('wiredep').stream,
runSequence = require('run-sequence'),
browserSync = require('browser-sync'),
KarmaServer = require('karma').Server,
plumber = require('gulp-plumber'),
changed = require('gulp-changed'),
gulpIf = require('gulp-if'),
inject = require('gulp-inject'),
angularFilesort = require('gulp-angular-filesort');
var handleErrors = require('./gulp/handleErrors'),
serve = require('./gulp/serve'),
util = require('./gulp/utils'),
build = require('./gulp/build');
var yorc = require('./.yo-rc.json')['generator-jhipster'];
var config = require('./gulp/config');
gulp.task('clean', function () {
return del([config.dist], { dot: true });
});
gulp.task('copy', function () {
return es.merge(
gulp.src(config.app + 'i18n/**')
.pipe(plumber({errorHandler: handleErrors}))
.pipe(changed(config.dist + 'i18n/'))
.pipe(gulp.dest(config.dist + 'i18n/')),
gulp.src(config.bower + 'bootstrap/fonts/*.*')
.pipe(plumber({errorHandler: handleErrors}))
.pipe(changed(config.dist + 'content/fonts/'))
.pipe(rev())
.pipe(gulp.dest(config.dist + 'content/fonts/'))
.pipe(rev.manifest(config.revManifest, {
base: config.dist,
merge: true
}))
.pipe(gulp.dest(config.dist)),
gulp.src(config.app + 'content/**/*.{woff,woff2,svg,ttf,eot,otf}')
.pipe(plumber({errorHandler: handleErrors}))
.pipe(changed(config.dist + 'content/fonts/'))
.pipe(flatten())
.pipe(rev())
.pipe(gulp.dest(config.dist + 'content/fonts/'))
.pipe(rev.manifest(config.revManifest, {
base: config.dist,
merge: true
}))
.pipe(gulp.dest(config.dist)),
gulp.src([config.app + 'robots.txt', config.app + 'favicon.ico', config.app + '.htaccess'], { dot: true })
.pipe(plumber({errorHandler: handleErrors}))
.pipe(changed(config.dist))
.pipe(gulp.dest(config.dist))
);
});
gulp.task('images', function () {
return gulp.src(config.app + 'content/images/**')
.pipe(plumber({errorHandler: handleErrors}))
.pipe(changed(config.dist + 'content/images'))
.pipe(imagemin({optimizationLevel: 5, progressive: true, interlaced: true}))
.pipe(rev())
.pipe(gulp.dest(config.dist + 'content/images'))
.pipe(rev.manifest(config.revManifest, {
base: config.dist,
merge: true
}))
.pipe(gulp.dest(config.dist))
.pipe(browserSync.reload({stream: true}));
});
gulp.task('languages', function () {
var locales = yorc.languages.map(function (locale) {
return config.bower + 'angular-i18n/angular-locale_' + locale + '.js';
});
return gulp.src(locales)
.pipe(plumber({errorHandler: handleErrors}))
.pipe(changed(config.app + 'i18n/'))
.pipe(gulp.dest(config.app + 'i18n/'));
});
gulp.task('styles', [], function () {
return gulp.src(config.app + 'content/css')
.pipe(browserSync.reload({stream: true}));
});
gulp.task('inject', function () {
return gulp.src(config.app + 'index.html')
.pipe(inject(gulp.src(config.app + 'app/**/*.js').pipe(angularFilesort()), {relative: true}))
.pipe(gulp.dest(config.app));
});
gulp.task('wiredep', ['wiredep:test', 'wiredep:app']);
gulp.task('wiredep:app', function () {
var stream = gulp.src(config.app + 'index.html')
.pipe(plumber({errorHandler: handleErrors}))
.pipe(wiredep({
exclude: [
/angular-i18n/, // localizations are loaded dynamically
'bower_components/bootstrap/dist/js/' // exclude bootstrap js files as we use ui-bootstrap
]
}))
.pipe(gulp.dest(config.app));
return stream;
});
gulp.task('wiredep:test', function () {
return gulp.src(config.test + 'karma.conf.js')
.pipe(plumber({errorHandler: handleErrors}))
.pipe(wiredep({
exclude: [
/angular-i18n/, // localizations are loaded dynamically
/angular-scenario/,
'bower_components/bootstrap/dist/js/' // exclude Bootstrap js files as we use ui-bootstrap
],
ignorePath: /\.\.\/\.\.\//, // remove ../../ from paths of injected JavaScript files
devDependencies: true,
fileTypes: {
js: {
block: /(([\s\t]*)\/\/\s*bower:*(\S*))(\n|\r|.)*?(\/\/\s*endbower)/gi,
detect: {
js: /'(.*\.js)'/gi
},
replace: {
js: '\'src/{{filePath}}\','
}
}
}
}))
.pipe(gulp.dest(config.test));
});
gulp.task('assets:prod', ['images', 'styles', 'html'], build);
gulp.task('html', function () {
return gulp.src(config.app + 'app/**/*.html')
.pipe(htmlmin({collapseWhitespace: true}))
.pipe(templateCache({
module: 'sampleApplicationApp',
root: 'app/',
moduleSystem: 'IIFE'
}))
.pipe(gulp.dest(config.tmp));
});
gulp.task('ngconstant:dev', function () {
return ngConstant({
dest: 'app.constants.js',
name: 'sampleApplicationApp',
deps: false,
noFile: true,
interpolate: /\{%=(.+?)%\}/g,
wrap:
'(function () {\n' +
' "use strict";\n' +
' // DO NOT EDIT THIS FILE, EDIT THE GULP TASK NGCONSTANT SETTINGS INSTEAD WHICH GENERATES THIS FILE\n' +
' {%= __ngModule %}\n' +
'})();\n',
constants: {
ENV: 'dev',
VERSION: util.parseVersion()
}
})
.pipe(gulp.dest(config.app + 'app/'));
});
gulp.task('ngconstant:prod', function () {
return ngConstant({
dest: 'app.constants.js',
name: 'sampleApplicationApp',
deps: false,
noFile: true,
interpolate: /\{%=(.+?)%\}/g,
wrap:
'(function () {\n' +
' "use strict";\n' +
' // DO NOT EDIT THIS FILE, EDIT THE GULP TASK NGCONSTANT SETTINGS INSTEAD WHICH GENERATES THIS FILE\n' +
' {%= __ngModule %}\n' +
'})();\n',
constants: {
ENV: 'prod',
VERSION: util.parseVersion()
}
})
.pipe(gulp.dest(config.app + 'app/'));
});
// check app for eslint errors
gulp.task('eslint', function () {
return gulp.src(['gulpfile.js', config.app + 'app/**/*.js'])
.pipe(plumber({errorHandler: handleErrors}))
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failOnError());
});
// check app for eslint errors anf fix some of them
gulp.task('eslint:fix', function () {
return gulp.src(config.app + 'app/**/*.js')
.pipe(plumber({errorHandler: handleErrors}))
.pipe(eslint({
fix: true
}))
.pipe(eslint.format())
.pipe(gulpIf(util.isLintFixed, gulp.dest(config.app + 'app')));
});
gulp.task('test', ['wiredep:test', 'ngconstant:dev'], function (done) {
new KarmaServer({
configFile: __dirname + '/' + config.test + 'karma.conf.js',
singleRun: true
}, done).start();
});
/* to run individual suites pass `gulp itest --suite suiteName` */
gulp.task('protractor', function () {
var configObj = {
configFile: config.test + 'protractor.conf.js'
};
if (argv.suite) {
configObj['args'] = ['--suite', argv.suite];
}
return gulp.src([])
.pipe(plumber({errorHandler: handleErrors}))
.pipe(protractor(configObj))
.on('error', function () {
gutil.log('E2E Tests failed');
process.exit(1);
});
});
gulp.task('itest', ['protractor']);
gulp.task('watch', function () {
gulp.watch('bower.json', ['install']);
gulp.watch(['gulpfile.js', 'pom.xml'], ['ngconstant:dev']);
gulp.watch(config.app + 'content/css/**/*.css', ['styles']);
gulp.watch(config.app + 'content/images/**', ['images']);
gulp.watch(config.app + 'app/**/*.js', ['inject']);
gulp.watch([config.app + '*.html', config.app + 'app/**', config.app + 'i18n/**']).on('change', browserSync.reload);
});
gulp.task('install', function () {
runSequence(['wiredep', 'ngconstant:dev'], 'languages', 'inject');
});
gulp.task('serve', function () {
runSequence('install', serve);
});
gulp.task('build', ['clean'], function (cb) {
runSequence(['copy', 'wiredep:app', 'ngconstant:prod', 'languages'], 'inject', 'assets:prod', cb);
});
gulp.task('default', ['serve']);

View File

@ -1,5 +1,5 @@
{ {
"name": "sampleapplication", "name": "sample-application",
"version": "0.0.0", "version": "0.0.0",
"description": "Description for sampleApplication", "description": "Description for sampleApplication",
"private": true, "private": true,
@ -11,57 +11,62 @@
"bower": "^1.6.3" "bower": "^1.6.3"
}, },
"devDependencies": { "devDependencies": {
"grunt": "0.4.5", "browser-sync": "2.11.2",
"grunt-autoprefixer": "3.0.3", "del": "2.2.0",
"grunt-build-control": "0.6.1", "eslint-config-angular": "0.5.0",
"grunt-wiredep": "2.0.0", "eslint-plugin-angular": "1.0.0",
"grunt-browser-sync": "2.2.0",
"browser-sync": "2.10.0",
"grunt-contrib-copy": "0.8.2",
"grunt-contrib-clean": "0.7.0",
"grunt-contrib-concat": "0.5.1",
"grunt-contrib-cssmin": "0.14.0",
"grunt-contrib-htmlmin": "0.6.0",
"grunt-contrib-imagemin": "1.0.0",
"grunt-contrib-jshint": "0.11.3",
"grunt-contrib-uglify": "0.10.1",
"grunt-contrib-watch": "0.6.1",
"grunt-modernizr": "1.0.1",
"grunt-ng-annotate": "1.0.1",
"grunt-ng-constant": "1.1.0",
"grunt-rev": "0.1.0",
"grunt-svgmin": "3.1.0",
"grunt-text-replace": "0.4.0",
"grunt-usemin": "3.1.1",
"grunt-angular-templates":"0.5.7",
"load-grunt-tasks": "3.3.0",
"grunt-karma": "0.12.1",
"time-grunt": "1.2.2",
"event-stream": "3.3.2", "event-stream": "3.3.2",
"jshint-stylish": "2.1.0", "generator-jhipster": "3.0.0",
"karma-script-launcher": "0.1.0", "gulp": "3.9.1",
"karma-chrome-launcher": "0.2.1", "gulp-angular-filesort": "1.1.1",
"karma-html2js-preprocessor": "0.1.0", "gulp-angular-templatecache": "1.8.0",
"karma-jasmine": "0.3.6", "gulp-autoprefixer": "3.1.0",
"karma-requirejs": "0.2.2", "gulp-changed": "1.3.0",
"karma-phantomjs-launcher": "0.2.1", "gulp-cssnano": "2.1.1",
"phantomjs": "1.9.18", "gulp-eslint": "2.0.0",
"karma": "0.13.19", "gulp-flatten": "0.2.0",
"karma-coverage": "0.5.3", "gulp-footer": "1.0.5",
"gulp-htmlmin": "1.3.0",
"gulp-if": "2.0.0",
"gulp-imagemin": "2.4.0",
"gulp-inject": "3.0.0",
"gulp-ng-annotate": "2.0.0",
"gulp-ng-constant-fork": "0.4.1",
"gulp-notify": "2.2.0",
"gulp-plumber": "1.1.0",
"gulp-protractor": "2.1.0",
"gulp-util": "3.0.7",
"gulp-rev": "7.0.0",
"gulp-rev-replace": "^0.4.3",
"gulp-sourcemaps": "1.6.0",
"gulp-uglify": "1.5.3",
"gulp-useref": "3.0.7",
"jasmine-core": "2.4.1",
"jasmine-reporters": "2.1.1",
"karma": "0.13.22",
"karma-chrome-launcher": "0.2.3",
"karma-coverage": "0.5.5",
"karma-jasmine": "0.3.8",
"karma-jenkins-reporter": "0.0.2", "karma-jenkins-reporter": "0.0.2",
"generator-jhipster": "2.27.2", "karma-phantomjs-launcher": "1.0.0",
"lodash": "3.10.1", "karma-script-launcher": "0.2.0",
"xml2js": "0.4.15", "lazypipe": "1.0.1",
"yo": ">=1.3.0", "lodash": "4.6.1",
"requirejs": "2.1", "map-stream": "0.0.6",
"jasmine-core": "2.3.4", "phantomjs-prebuilt": "2.1.4",
"zeparser": "0.0.7", "protractor": "3.1.1",
"wiredep": "2.2.2" "protractor-jasmine2-screenshot-reporter": "0.3.0",
"proxy-middleware": "0.15.0",
"run-sequence": "1.1.5",
"wiredep": "4.0.0",
"xml2js": "0.4.16",
"yargs": "4.3.2"
}, },
"engines": { "engines": {
"node": "^0.12 || ^4.2" "node": "^4.3"
}, },
"scripts": { "scripts": {
"test": "grunt test" "test": "gulp test",
"postinstall": "webdriver-manager update"
} }
} }

163
pom.xml
View File

@ -5,12 +5,12 @@
<parent> <parent>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<version>1.3.1.RELEASE</version> <version>1.3.3.RELEASE</version>
<relativePath/> <relativePath/>
</parent> </parent>
<groupId>com.mycompany.myapp</groupId> <groupId>com.mycompany.myapp</groupId>
<artifactId>sampleapplication</artifactId> <artifactId>sample-application</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging> <packaging>war</packaging>
<name>sampleApplication</name> <name>sampleApplication</name>
@ -33,9 +33,9 @@
</prerequisites> </prerequisites>
<properties> <properties>
<hikaricp.version>2.4.1</hikaricp.version> <hikaricp.version>2.4.3</hikaricp.version>
<assertj-core.version>3.1.0</assertj-core.version> <assertj-core.version>3.3.0</assertj-core.version>
<awaitility.version>1.4.0</awaitility.version> <awaitility.version>1.7.0</awaitility.version>
<commons-io.version>2.4</commons-io.version> <commons-io.version>2.4</commons-io.version>
<commons-lang.version>2.6</commons-lang.version> <commons-lang.version>2.6</commons-lang.version>
<gatling.version>2.1.7</gatling.version> <gatling.version>2.1.7</gatling.version>
@ -50,18 +50,19 @@
<liquibase-hibernate4.version>3.5</liquibase-hibernate4.version> <liquibase-hibernate4.version>3.5</liquibase-hibernate4.version>
<mapstruct.version>1.0.0.Final</mapstruct.version> <mapstruct.version>1.0.0.Final</mapstruct.version>
<maven.build.timestamp.format>yyyyMMddHHmmss</maven.build.timestamp.format> <maven.build.timestamp.format>yyyyMMddHHmmss</maven.build.timestamp.format>
<maven-enforcer-plugin.version>1.3.1</maven-enforcer-plugin.version> <maven-enforcer-plugin.version>1.4.1</maven-enforcer-plugin.version>
<maven-sortpom-plugin.version>2.3.0</maven-sortpom-plugin.version> <sortpom-maven-plugin.version>2.5.0</sortpom-maven-plugin.version>
<metrics-spark-reporter.version>1.2</metrics-spark-reporter.version> <metrics-spark-reporter.version>1.2</metrics-spark-reporter.version>
<metrics-spring.version>3.1.2</metrics-spring.version> <metrics-spring.version>3.1.3</metrics-spring.version>
<logstash-logback-encoder.version>4.6</logstash-logback-encoder.version>
<run.addResources>false</run.addResources> <run.addResources>false</run.addResources>
<spring-security.version>4.0.2.RELEASE</spring-security.version> <spring-security.version>4.0.4.RELEASE</spring-security.version>
<springfox.version>2.0.3</springfox.version> <springfox.version>2.4.0</springfox.version>
<!-- Sonar properties --> <!-- Sonar properties -->
<project.testresult.directory>${project.build.directory}/test-results</project.testresult.directory> <project.testresult.directory>${project.build.directory}/test-results</project.testresult.directory>
<sonar-maven-plugin.version>2.6</sonar-maven-plugin.version> <sonar-maven-plugin.version>2.7.1</sonar-maven-plugin.version>
<jacoco-maven-plugin.version>0.7.4.201502262128</jacoco-maven-plugin.version> <jacoco-maven-plugin.version>0.7.6.201602180812</jacoco-maven-plugin.version>
<sonar.exclusions>src/main/webapp/assets/**/*.*, src/main/webapp/bower_components/**/*.*, src/main/webapp/dist/**/*.*</sonar.exclusions> <sonar.exclusions>src/main/webapp/content/**/*.*, src/main/webapp/bower_components/**/*.*, target/www/**/*.*</sonar.exclusions>
<sonar.java.codeCoveragePlugin>jacoco</sonar.java.codeCoveragePlugin> <sonar.java.codeCoveragePlugin>jacoco</sonar.java.codeCoveragePlugin>
<sonar.jacoco.itReportPath>${project.testresult.directory}/coverage/jacoco/jacoco-it.exec</sonar.jacoco.itReportPath> <sonar.jacoco.itReportPath>${project.testresult.directory}/coverage/jacoco/jacoco-it.exec</sonar.jacoco.itReportPath>
<sonar.jacoco.reportPath>${project.testresult.directory}/coverage/jacoco/jacoco.exec</sonar.jacoco.reportPath> <sonar.jacoco.reportPath>${project.testresult.directory}/coverage/jacoco/jacoco.exec</sonar.jacoco.reportPath>
@ -204,12 +205,13 @@
</exclusions> </exclusions>
</dependency> </dependency>
<!-- The HikariCP Java Agent is disabled by default, as it is experimental <!-- The HikariCP Java Agent is disabled by default, as it is experimental
<dependency> <dependency>
<groupId>com.zaxxer</groupId> <groupId>com.zaxxer</groupId>
<artifactId>HikariCP-agent</artifactId> <artifactId>HikariCP-agent</artifactId>
<version>${HikariCP.version}</version> <version>${HikariCP.version}</version>
</dependency> </dependency>
--> -->
<dependency> <dependency>
<groupId>commons-io</groupId> <groupId>commons-io</groupId>
<artifactId>commons-io</artifactId> <artifactId>commons-io</artifactId>
@ -336,6 +338,25 @@
</exclusions> </exclusions>
</dependency> </dependency>
<!-- Spring Cloud --> <!-- Spring Cloud -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>${logstash-logback-encoder.version}</version>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-cloudfoundry-connector</artifactId> <artifactId>spring-cloud-cloudfoundry-connector</artifactId>
@ -365,14 +386,14 @@
<defaultGoal>spring-boot:run</defaultGoal> <defaultGoal>spring-boot:run</defaultGoal>
<resources> <resources>
<resource> <resource>
<directory>src/main/resources</directory> <directory>src/main/resources/</directory>
<filtering>true</filtering> <filtering>true</filtering>
<includes> <includes>
<include>**/*.xml</include> <include>**/*.xml</include>
</includes> </includes>
</resource> </resource>
<resource> <resource>
<directory>src/main/resources</directory> <directory>src/main/resources/</directory>
<filtering>false</filtering> <filtering>false</filtering>
<excludes> <excludes>
<exclude>**/*.xml</exclude> <exclude>**/*.xml</exclude>
@ -381,9 +402,9 @@
</resources> </resources>
<plugins> <plugins>
<plugin> <plugin>
<groupId>com.google.code.sortpom</groupId> <groupId>com.github.ekryd.sortpom</groupId>
<artifactId>maven-sortpom-plugin</artifactId> <artifactId>sortpom-maven-plugin</artifactId>
<version>${maven-sortpom-plugin.version}</version> <version>${sortpom-maven-plugin.version}</version>
<executions> <executions>
<execution> <execution>
<phase>verify</phase> <phase>verify</phase>
@ -453,18 +474,11 @@
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<configuration> <configuration>
<argLine>-Xmx256m ${surefireArgLine}</argLine> <argLine>-Djava.security.egd=file:/dev/./urandom -Xmx256m ${surefireArgLine}</argLine>
<!-- Force alphabetical order to have a reproducible build --> <!-- Force alphabetical order to have a reproducible build -->
<runOrder>alphabetical</runOrder> <runOrder>alphabetical</runOrder>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<packagingExcludes>WEB-INF/lib/tomcat-*.jar</packagingExcludes>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.jacoco</groupId> <groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId> <artifactId>jacoco-maven-plugin</artifactId>
@ -546,7 +560,7 @@
<defaultSchemaName></defaultSchemaName> <defaultSchemaName></defaultSchemaName>
<username></username> <username></username>
<password></password> <password></password>
<referenceUrl>hibernate:spring:com.mycompany.myapp.domain?dialect=</referenceUrl> <referenceUrl>hibernate:spring:com.mycompany.myapp.domain?dialect=&amp;hibernate.ejb.naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy</referenceUrl>
<verbose>true</verbose> <verbose>true</verbose>
<logging>debug</logging> <logging>debug</logging>
</configuration> </configuration>
@ -573,12 +587,31 @@
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
<configuration> <configuration>
<executable>true</executable> <executable>true</executable>
<!--
Enable the line below to have remote debugging of your application on port 5005
<jvmArguments>-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005</jvmArguments> <jvmArguments>-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005</jvmArguments>
-->
<arguments> <arguments>
<argument>--spring.profiles.active=dev</argument> <argument>--spring.profiles.active=dev</argument>
</arguments> </arguments>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.4.1</version>
<configuration>
<imageName>sampleapplication</imageName>
<dockerDirectory>src/main/docker</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.war</include>
</resource>
</resources>
</configuration>
</plugin>
<!-- jhipster-needle-maven-add-plugin --> <!-- jhipster-needle-maven-add-plugin -->
</plugins> </plugins>
<pluginManagement> <pluginManagement>
@ -612,6 +645,23 @@
<ignore/> <ignore/>
</action> </action>
</pluginExecution> </pluginExecution>
<pluginExecution>
<pluginExecutionFilter>
<groupId>com.github.trecloux</groupId>
<artifactId>
yeoman-maven-plugin
</artifactId>
<versionRange>
[0.5,)
</versionRange>
<goals>
<goal>build</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore/>
</action>
</pluginExecution>
</pluginExecutions> </pluginExecutions>
</lifecycleMappingMetadata> </lifecycleMappingMetadata>
</configuration> </configuration>
@ -641,34 +691,14 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
</dependencies> </dependencies>
</profile>
<profile>
<id>fast</id>
<properties>
<!-- log configuration -->
<logback.loglevel>DEBUG</logback.loglevel>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>maven-war-plugin</artifactId>
<configuration> <configuration>
<executable>true</executable> <warSourceDirectory>src/main/webapp/</warSourceDirectory>
<arguments> <packagingExcludes>WEB-INF/lib/tomcat-*.jar</packagingExcludes>
<argument>--spring.profiles.active=dev,fast</argument>
</arguments>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
@ -687,7 +717,7 @@
<plugin> <plugin>
<groupId>com.github.trecloux</groupId> <groupId>com.github.trecloux</groupId>
<artifactId>yeoman-maven-plugin</artifactId> <artifactId>yeoman-maven-plugin</artifactId>
<version>0.4</version> <version>0.5</version>
<executions> <executions>
<execution> <execution>
<id>run-frontend-build</id> <id>run-frontend-build</id>
@ -696,8 +726,8 @@
<goal>build</goal> <goal>build</goal>
</goals> </goals>
<configuration> <configuration>
<buildTool>grunt</buildTool> <buildTool>gulp</buildTool>
<buildArgs>build --no-color</buildArgs> <buildArgs>build --no-notification</buildArgs>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
@ -711,10 +741,7 @@
<configuration> <configuration>
<filesets> <filesets>
<fileset> <fileset>
<directory>src/main/webapp/dist</directory> <directory>target/www/</directory>
</fileset>
<fileset>
<directory>.tmp</directory>
</fileset> </fileset>
<fileset> <fileset>
<directory>node_modules</directory> <directory>node_modules</directory>
@ -722,6 +749,14 @@
</filesets> </filesets>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<warSourceDirectory>target/www/</warSourceDirectory>
<packagingExcludes>WEB-INF/lib/tomcat-*.jar</packagingExcludes>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>

View File

@ -0,0 +1,10 @@
FROM java:openjdk-8-jdk-alpine
# add directly the war
ADD *.war /app.war
RUN sh -c 'touch /app.war'
VOLUME /tmp
CMD ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.war"]
EXPOSE 8080

15
src/main/docker/app.yml Normal file
View File

@ -0,0 +1,15 @@
version: '2'
services:
sampleapplication-app:
image: sampleapplication
external_links:
- sampleapplication-mysql:mysql
environment:
- SPRING_PROFILES_ACTIVE=prod
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/sampleapplication?useUnicode=true&characterEncoding=utf8&useSSL=false
ports:
- 8080:8080
sampleapplication-mysql:
extends:
file: mysql.yml
service: sampleapplication-mysql

14
src/main/docker/mysql.yml Normal file
View File

@ -0,0 +1,14 @@
version: '2'
services:
sampleapplication-mysql:
container_name: sampleapplication-mysql
image: mysql:5.7.11
# volumes:
# - ~/volumes/jhipster/sampleApplication/mysql/:/var/lib/mysql/
environment:
- MYSQL_USER=root
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
- MYSQL_DATABASE=sampleapplication
ports:
- 3306:3306
command: mysqld --lower_case_table_names=1

View File

@ -1,12 +0,0 @@
sampleapplication-mysql:
container_name: sampleapplication-mysql
image: mysql:5.7.9
# volumes:
# - ~/volumes/jhipster/sampleApplication/mysql/:/var/lib/mysql/
environment:
- MYSQL_USER=root
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
- MYSQL_DATABASE=sampleapplication
ports:
- "3306:3306"
command: mysqld --lower_case_table_names=1

View File

@ -1,6 +1,8 @@
sampleapplication-sonar: version: '2'
container_name: sampleapplication-sonar services:
image: sonarqube:4.5.6 sampleapplication-sonar:
ports: container_name: sampleapplication-sonar
- "9000:9000" image: sonarqube:5.3
- "9092:9092" ports:
- 9000:9000
- 9092:9092

View File

@ -17,7 +17,7 @@ public class ApplicationWebXml extends SpringBootServletInitializer {
@Override @Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.profiles(addDefaultProfile()) return application.profiles(addDefaultProfile())
.sources(Application.class); .sources(SampleApplicationApp.class);
} }
/** /**

View File

@ -6,8 +6,7 @@ import com.mycompany.myapp.config.JHipsterProperties;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.*;
import org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ -17,7 +16,6 @@ import org.springframework.core.env.SimpleCommandLinePropertySource;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.inject.Inject; import javax.inject.Inject;
import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.Arrays; import java.util.Arrays;
@ -26,24 +24,22 @@ import java.util.Collection;
@ComponentScan @ComponentScan
@EnableAutoConfiguration(exclude = { MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class }) @EnableAutoConfiguration(exclude = { MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class })
@EnableConfigurationProperties({ JHipsterProperties.class, LiquibaseProperties.class }) @EnableConfigurationProperties({ JHipsterProperties.class, LiquibaseProperties.class })
public class Application { public class SampleApplicationApp {
private static final Logger log = LoggerFactory.getLogger(Application.class); private static final Logger log = LoggerFactory.getLogger(SampleApplicationApp.class);
@Inject @Inject
private Environment env; private Environment env;
/** /**
* Initializes sampleApplication. * Initializes sampleApplication.
* <p/>
* Spring profiles can be configured with a program arguments --spring.profiles.active=your-active-profile
* <p/>
* <p> * <p>
* You can find more information on how profiles work with JHipster on <a href="http://jhipster.github.io/profiles.html">http://jhipster.github.io/profiles.html</a>. * Spring profiles can be configured with a program arguments --spring.profiles.active=your-active-profile
* </p> * <p>
* You can find more information on how profiles work with JHipster on <a href="http://jhipster.github.io/profiles/">http://jhipster.github.io/profiles/</a>.
*/ */
@PostConstruct @PostConstruct
public void initApplication() throws IOException { public void initApplication() {
if (env.getActiveProfiles().length == 0) { if (env.getActiveProfiles().length == 0) {
log.warn("No Spring profile configured, running with default configuration"); log.warn("No Spring profile configured, running with default configuration");
} else { } else {
@ -53,10 +49,6 @@ public class Application {
log.error("You have misconfigured your application! " + log.error("You have misconfigured your application! " +
"It should not run with both the 'dev' and 'prod' profiles at the same time."); "It should not run with both the 'dev' and 'prod' profiles at the same time.");
} }
if (activeProfiles.contains(Constants.SPRING_PROFILE_PRODUCTION) && activeProfiles.contains(Constants.SPRING_PROFILE_FAST)) {
log.error("You have misconfigured your application! " +
"It should not run with both the 'prod' and 'fast' profiles at the same time.");
}
if (activeProfiles.contains(Constants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(Constants.SPRING_PROFILE_CLOUD)) { if (activeProfiles.contains(Constants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(Constants.SPRING_PROFILE_CLOUD)) {
log.error("You have misconfigured your application! " + log.error("You have misconfigured your application! " +
"It should not run with both the 'dev' and 'cloud' profiles at the same time."); "It should not run with both the 'dev' and 'cloud' profiles at the same time.");
@ -66,15 +58,20 @@ public class Application {
/** /**
* Main method, used to run the application. * Main method, used to run the application.
*
* @param args the command line arguments
* @throws UnknownHostException if the local host name could not be resolved into an address
*/ */
public static void main(String[] args) throws UnknownHostException { public static void main(String[] args) throws UnknownHostException {
SpringApplication app = new SpringApplication(Application.class); SpringApplication app = new SpringApplication(SampleApplicationApp.class);
SimpleCommandLinePropertySource source = new SimpleCommandLinePropertySource(args); SimpleCommandLinePropertySource source = new SimpleCommandLinePropertySource(args);
addDefaultProfile(app, source); addDefaultProfile(app, source);
Environment env = app.run(args).getEnvironment(); Environment env = app.run(args).getEnvironment();
log.info("Access URLs:\n----------------------------------------------------------\n\t" + log.info("\n----------------------------------------------------------\n\t" +
"Application '{}' is running! Access URLs:\n\t" +
"Local: \t\thttp://127.0.0.1:{}\n\t" + "Local: \t\thttp://127.0.0.1:{}\n\t" +
"External: \thttp://{}:{}\n----------------------------------------------------------", "External: \thttp://{}:{}\n----------------------------------------------------------",
env.getProperty("spring.application.name"),
env.getProperty("server.port"), env.getProperty("server.port"),
InetAddress.getLocalHost().getHostAddress(), InetAddress.getLocalHost().getHostAddress(),
env.getProperty("server.port")); env.getProperty("server.port"));

View File

@ -22,7 +22,7 @@ public class ExceptionHandlingAsyncTaskExecutor implements AsyncTaskExecutor,
@Override @Override
public void execute(Runnable task) { public void execute(Runnable task) {
executor.execute(task); executor.execute(createWrappedRunnable(task));
} }
@Override @Override

View File

@ -33,7 +33,7 @@ public class AsyncConfiguration implements AsyncConfigurer {
executor.setCorePoolSize(jHipsterProperties.getAsync().getCorePoolSize()); executor.setCorePoolSize(jHipsterProperties.getAsync().getCorePoolSize());
executor.setMaxPoolSize(jHipsterProperties.getAsync().getMaxPoolSize()); executor.setMaxPoolSize(jHipsterProperties.getAsync().getMaxPoolSize());
executor.setQueueCapacity(jHipsterProperties.getAsync().getQueueCapacity()); executor.setQueueCapacity(jHipsterProperties.getAsync().getQueueCapacity());
executor.setThreadNamePrefix("sampleapplication-Executor-"); executor.setThreadNamePrefix("sample-application-Executor-");
return new ExceptionHandlingAsyncTaskExecutor(executor); return new ExceptionHandlingAsyncTaskExecutor(executor);
} }

View File

@ -22,7 +22,6 @@ import java.util.SortedSet;
@Configuration @Configuration
@EnableCaching @EnableCaching
@AutoConfigureAfter(value = { MetricsConfiguration.class, DatabaseConfiguration.class }) @AutoConfigureAfter(value = { MetricsConfiguration.class, DatabaseConfiguration.class })
@Profile("!" + Constants.SPRING_PROFILE_FAST)
public class CacheConfiguration { public class CacheConfiguration {
private final Logger log = LoggerFactory.getLogger(CacheConfiguration.class); private final Logger log = LoggerFactory.getLogger(CacheConfiguration.class);

View File

@ -5,14 +5,17 @@ package com.mycompany.myapp.config;
*/ */
public final class Constants { public final class Constants {
// Spring profile for development, production and "fast", see http://jhipster.github.io/profiles.html // Spring profile for development and production, see http://jhipster.github.io/profiles/
public static final String SPRING_PROFILE_DEVELOPMENT = "dev"; public static final String SPRING_PROFILE_DEVELOPMENT = "dev";
public static final String SPRING_PROFILE_PRODUCTION = "prod"; public static final String SPRING_PROFILE_PRODUCTION = "prod";
public static final String SPRING_PROFILE_FAST = "fast";
// Spring profile used when deploying with Spring Cloud (used when deploying to CloudFoundry) // Spring profile used when deploying with Spring Cloud (used when deploying to CloudFoundry)
public static final String SPRING_PROFILE_CLOUD = "cloud"; public static final String SPRING_PROFILE_CLOUD = "cloud";
// Spring profile used when deploying to Heroku // Spring profile used when deploying to Heroku
public static final String SPRING_PROFILE_HEROKU = "heroku"; public static final String SPRING_PROFILE_HEROKU = "heroku";
// Spring profile used to disable swagger
public static final String SPRING_PROFILE_NO_SWAGGER = "no-swagger";
// Spring profile used to disable running liquibase
public static final String SPRING_PROFILE_NO_LIQUIBASE = "no-liquibase";
public static final String SYSTEM_ACCOUNT = "system"; public static final String SYSTEM_ACCOUNT = "system";

View File

@ -4,7 +4,6 @@ import com.mycompany.myapp.config.liquibase.AsyncSpringLiquibase;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module; import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import liquibase.integration.spring.SpringLiquibase; import liquibase.integration.spring.SpringLiquibase;
import org.h2.tools.Server; import org.h2.tools.Server;
@ -12,8 +11,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContextException; import org.springframework.context.ApplicationContextException;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -43,8 +44,9 @@ public class DatabaseConfiguration {
private MetricRegistry metricRegistry; private MetricRegistry metricRegistry;
@Bean(destroyMethod = "close") @Bean(destroyMethod = "close")
@ConditionalOnExpression("#{!environment.acceptsProfiles('cloud') && !environment.acceptsProfiles('heroku')}") @ConditionalOnExpression("#{!environment.acceptsProfiles('" + Constants.SPRING_PROFILE_CLOUD + "') && !environment.acceptsProfiles('" + Constants.SPRING_PROFILE_HEROKU + "')}")
public DataSource dataSource(DataSourceProperties dataSourceProperties, JHipsterProperties jHipsterProperties) { @ConfigurationProperties(prefix = "spring.datasource.hikari")
public DataSource dataSource(DataSourceProperties dataSourceProperties) {
log.debug("Configuring Datasource"); log.debug("Configuring Datasource");
if (dataSourceProperties.getUrl() == null) { if (dataSourceProperties.getUrl() == null) {
log.error("Your database connection pool configuration is incorrect! The application" + log.error("Your database connection pool configuration is incorrect! The application" +
@ -53,34 +55,26 @@ public class DatabaseConfiguration {
throw new ApplicationContextException("Database connection pool is not configured correctly"); throw new ApplicationContextException("Database connection pool is not configured correctly");
} }
HikariConfig config = new HikariConfig(); HikariDataSource hikariDataSource = (HikariDataSource) DataSourceBuilder
config.setDataSourceClassName(dataSourceProperties.getDriverClassName()); .create(dataSourceProperties.getClassLoader())
config.addDataSourceProperty("url", dataSourceProperties.getUrl()); .type(HikariDataSource.class)
if (dataSourceProperties.getUsername() != null) { .driverClassName(dataSourceProperties.getDriverClassName())
config.addDataSourceProperty("user", dataSourceProperties.getUsername()); .url(dataSourceProperties.getUrl())
} else { .username(dataSourceProperties.getUsername())
config.addDataSourceProperty("user", ""); // HikariCP doesn't allow null user .password(dataSourceProperties.getPassword())
} .build();
if (dataSourceProperties.getPassword() != null) {
config.addDataSourceProperty("password", dataSourceProperties.getPassword());
} else {
config.addDataSourceProperty("password", ""); // HikariCP doesn't allow null password
}
//MySQL optimizations, see https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration
if ("com.mysql.jdbc.jdbc2.optional.MysqlDataSource".equals(dataSourceProperties.getDriverClassName())) {
config.addDataSourceProperty("cachePrepStmts", jHipsterProperties.getDatasource().isCachePrepStmts());
config.addDataSourceProperty("prepStmtCacheSize", jHipsterProperties.getDatasource().getPrepStmtCacheSize());
config.addDataSourceProperty("prepStmtCacheSqlLimit", jHipsterProperties.getDatasource().getPrepStmtCacheSqlLimit());
}
if (metricRegistry != null) { if (metricRegistry != null) {
config.setMetricRegistry(metricRegistry); hikariDataSource.setMetricRegistry(metricRegistry);
} }
return new HikariDataSource(config); return hikariDataSource;
} }
/** /**
* Open the TCP port for the H2 database, so it is available remotely. * Open the TCP port for the H2 database, so it is available remotely.
*
* @return the H2 database TCP server
* @throws SQLException if the server failed to start
*/ */
@Bean(initMethod = "start", destroyMethod = "stop") @Bean(initMethod = "start", destroyMethod = "stop")
@Profile(Constants.SPRING_PROFILE_DEVELOPMENT) @Profile(Constants.SPRING_PROFILE_DEVELOPMENT)
@ -99,18 +93,13 @@ public class DatabaseConfiguration {
liquibase.setContexts(liquibaseProperties.getContexts()); liquibase.setContexts(liquibaseProperties.getContexts());
liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema()); liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema());
liquibase.setDropFirst(liquibaseProperties.isDropFirst()); liquibase.setDropFirst(liquibaseProperties.isDropFirst());
liquibase.setShouldRun(liquibaseProperties.isEnabled()); if (env.acceptsProfiles(Constants.SPRING_PROFILE_NO_LIQUIBASE)) {
if (env.acceptsProfiles(Constants.SPRING_PROFILE_FAST)) { liquibase.setShouldRun(false);
if ("org.h2.jdbcx.JdbcDataSource".equals(dataSourceProperties.getDriverClassName())) {
liquibase.setShouldRun(true);
log.warn("Using '{}' profile with H2 database in memory is not optimal, you should consider switching to" +
" MySQL or Postgresql to avoid rebuilding your database upon each start.", Constants.SPRING_PROFILE_FAST);
} else {
liquibase.setShouldRun(false);
}
} else { } else {
liquibase.setShouldRun(liquibaseProperties.isEnabled());
log.debug("Configuring Liquibase"); log.debug("Configuring Liquibase");
} }
return liquibase; return liquibase;
} }

View File

@ -5,6 +5,7 @@ import javax.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
/** /**
* Properties specific to JHipster. * Properties specific to JHipster.
* *
@ -19,8 +20,6 @@ public class JHipsterProperties {
private final Http http = new Http(); private final Http http = new Http();
private final Datasource datasource = new Datasource();
private final Cache cache = new Cache(); private final Cache cache = new Cache();
private final Mail mail = new Mail(); private final Mail mail = new Mail();
@ -33,8 +32,6 @@ public class JHipsterProperties {
private final CorsConfiguration cors = new CorsConfiguration(); private final CorsConfiguration cors = new CorsConfiguration();
public Async getAsync() { public Async getAsync() {
return async; return async;
} }
@ -43,10 +40,6 @@ public class JHipsterProperties {
return http; return http;
} }
public Datasource getDatasource() {
return datasource;
}
public Cache getCache() { public Cache getCache() {
return cache; return cache;
} }
@ -71,7 +64,6 @@ public class JHipsterProperties {
return cors; return cors;
} }
public static class Async { public static class Async {
private int corePoolSize = 2; private int corePoolSize = 2;
@ -115,7 +107,7 @@ public class JHipsterProperties {
public static class Cache { public static class Cache {
private int timeToLiveInDays = 31; private int timeToLiveInDays = 1461;
public int getTimeToLiveInDays() { public int getTimeToLiveInDays() {
return timeToLiveInDays; return timeToLiveInDays;
@ -127,49 +119,6 @@ public class JHipsterProperties {
} }
} }
public static class Datasource {
private boolean cachePrepStmts = true;
private int prepStmtCacheSize = 250;
private int prepStmtCacheSqlLimit = 2048;
private boolean useServerPrepStmts = true;
public boolean isCachePrepStmts() {
return cachePrepStmts;
}
public void setCachePrepStmts(boolean cachePrepStmts) {
this.cachePrepStmts = cachePrepStmts;
}
public int getPrepStmtCacheSize() {
return prepStmtCacheSize;
}
public void setPrepStmtCacheSize(int prepStmtCacheSize) {
this.prepStmtCacheSize = prepStmtCacheSize;
}
public int getPrepStmtCacheSqlLimit() {
return prepStmtCacheSqlLimit;
}
public void setPrepStmtCacheSqlLimit(int prepStmtCacheSqlLimit) {
this.prepStmtCacheSqlLimit = prepStmtCacheSqlLimit;
}
public boolean isUseServerPrepStmts() {
return useServerPrepStmts;
}
public void setUseServerPrepStmts(boolean useServerPrepStmts) {
this.useServerPrepStmts = useServerPrepStmts;
}
}
public static class Cache { public static class Cache {
private int timeToLiveSeconds = 3600; private int timeToLiveSeconds = 3600;
@ -217,13 +166,13 @@ public class JHipsterProperties {
public static class Security { public static class Security {
private final Rememberme rememberme = new Rememberme(); private final RememberMe rememberMe = new RememberMe();
public Rememberme getRememberme() { public RememberMe getRememberMe() {
return rememberme; return rememberMe;
} }
public static class Rememberme { public static class RememberMe {
@NotNull @NotNull
private String key; private String key;
@ -248,7 +197,11 @@ public class JHipsterProperties {
private String termsOfServiceUrl; private String termsOfServiceUrl;
private String contact; private String contactName;
private String contactUrl;
private String contactEmail;
private String license; private String license;
@ -286,12 +239,28 @@ public class JHipsterProperties {
this.termsOfServiceUrl = termsOfServiceUrl; this.termsOfServiceUrl = termsOfServiceUrl;
} }
public String getContact() { public String getContactName() {
return contact; return contactName;
} }
public void setContact(String contact) { public void setContactName(String contactName) {
this.contact = contact; this.contactName = contactName;
}
public String getContactUrl() {
return contactUrl;
}
public void setContactUrl(String contactUrl) {
this.contactUrl = contactUrl;
}
public String getContactEmail() {
return contactEmail;
}
public void setContactEmail(String contactEmail) {
this.contactEmail = contactEmail;
} }
public String getLicense() { public String getLicense() {
@ -319,6 +288,8 @@ public class JHipsterProperties {
private final Graphite graphite = new Graphite(); private final Graphite graphite = new Graphite();
private final Logs logs = new Logs();
public Jmx getJmx() { public Jmx getJmx() {
return jmx; return jmx;
} }
@ -331,6 +302,11 @@ public class JHipsterProperties {
return graphite; return graphite;
} }
public Logs getLogs() {
return logs;
}
public static class Jmx { public static class Jmx {
private boolean enabled = true; private boolean enabled = true;
@ -419,5 +395,67 @@ public class JHipsterProperties {
this.prefix = prefix; this.prefix = prefix;
} }
} }
public static class Logs {
private boolean enabled = false;
private long reportFrequency = 60;
public long getReportFrequency() {
return reportFrequency;
}
public void setReportFrequency(int reportFrequency) {
this.reportFrequency = reportFrequency;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
} }
private final Logging logging = new Logging();
public Logging getLogging() { return logging; }
public static class Logging {
private final Logstash logstash = new Logstash();
public Logstash getLogstash() { return logstash; }
public static class Logstash {
private boolean enabled = false;
private String host = "localhost";
private int port = 5000;
private int queueSize = 512;
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public String getHost() { return host; }
public void setHost(String host) { this.host = host; }
public int getPort() { return port; }
public void setPort(int port) { this.port = port; }
public int getQueueSize() { return queueSize; }
public void setQueueSize(int queueSize) { this.queueSize = queueSize; }
}
}
} }

View File

@ -4,10 +4,8 @@ import com.mycompany.myapp.config.locale.AngularCookieLocaleResolver;
import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware; import org.springframework.context.EnvironmentAware;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
@ -31,15 +29,6 @@ public class LocaleConfiguration extends WebMvcConfigurerAdapter implements Envi
return cookieLocaleResolver; return cookieLocaleResolver;
} }
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:/i18n/messages");
messageSource.setDefaultEncoding("UTF-8");
messageSource.setCacheSeconds(propertyResolver.getProperty("cache-seconds", Integer.class, -1));
return messageSource;
}
@Override @Override
public void addInterceptors(InterceptorRegistry registry) { public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();

View File

@ -0,0 +1,62 @@
package com.mycompany.myapp.config;
import ch.qos.logback.classic.AsyncAppender;
import ch.qos.logback.classic.LoggerContext;
import net.logstash.logback.appender.LogstashSocketAppender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
@Configuration
public class LoggingConfiguration {
private final Logger log = LoggerFactory.getLogger(LoggingConfiguration.class);
private LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
@Value("${spring.application.name}")
private String appName;
@Value("${server.port}")
private String serverPort;
@Inject
private JHipsterProperties jHipsterProperties;
@PostConstruct
private void init() {
if (jHipsterProperties.getLogging().getLogstash().isEnabled()) {
addLogstashAppender();
}
}
public void addLogstashAppender() {
log.info("Initializing Logstash logging");
LogstashSocketAppender logstashAppender = new LogstashSocketAppender();
logstashAppender.setName("LOGSTASH");
logstashAppender.setContext(context);
String customFields = "{\"app_name\":\"" + appName + "\",\"app_port\":\"" + serverPort + "\"}";
// Set the Logstash appender config from JHipster properties
logstashAppender.setSyslogHost(jHipsterProperties.getLogging().getLogstash().getHost());
logstashAppender.setPort(jHipsterProperties.getLogging().getLogstash().getPort());
logstashAppender.setCustomFields(customFields);
logstashAppender.start();
// Wrap the appender in an Async appender for performance
AsyncAppender asyncLogstashAppender = new AsyncAppender();
asyncLogstashAppender.setContext(context);
asyncLogstashAppender.setName("ASYNC_LOGSTASH");
asyncLogstashAppender.setQueueSize(jHipsterProperties.getLogging().getLogstash().getQueueSize());
asyncLogstashAppender.addAppender(logstashAppender);
asyncLogstashAppender.start();
context.getLogger("ROOT").addAppender(asyncLogstashAppender);
}
}

View File

@ -2,6 +2,7 @@ package com.mycompany.myapp.config;
import com.codahale.metrics.JmxReporter; import com.codahale.metrics.JmxReporter;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Slf4jReporter;
import com.codahale.metrics.graphite.Graphite; import com.codahale.metrics.graphite.Graphite;
import com.codahale.metrics.graphite.GraphiteReporter; import com.codahale.metrics.graphite.GraphiteReporter;
import com.codahale.metrics.health.HealthCheckRegistry; import com.codahale.metrics.health.HealthCheckRegistry;
@ -22,7 +23,6 @@ import java.util.concurrent.TimeUnit;
@Configuration @Configuration
@EnableMetrics(proxyTargetClass = true) @EnableMetrics(proxyTargetClass = true)
@Profile("!" + Constants.SPRING_PROFILE_FAST)
public class MetricsConfiguration extends MetricsConfigurerAdapter { public class MetricsConfiguration extends MetricsConfigurerAdapter {
private static final String PROP_METRIC_REG_JVM_MEMORY = "jvm.memory"; private static final String PROP_METRIC_REG_JVM_MEMORY = "jvm.memory";
@ -65,11 +65,20 @@ public class MetricsConfiguration extends MetricsConfigurerAdapter {
JmxReporter jmxReporter = JmxReporter.forRegistry(metricRegistry).build(); JmxReporter jmxReporter = JmxReporter.forRegistry(metricRegistry).build();
jmxReporter.start(); jmxReporter.start();
} }
if (jHipsterProperties.getMetrics().getLogs().isEnabled()) {
log.info("Initializing Metrics Log reporting");
final Slf4jReporter reporter = Slf4jReporter.forRegistry(metricRegistry)
.outputTo(LoggerFactory.getLogger("metrics"))
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build();
reporter.start(jHipsterProperties.getMetrics().getLogs().getReportFrequency(), TimeUnit.SECONDS);
}
} }
@Configuration @Configuration
@ConditionalOnClass(Graphite.class) @ConditionalOnClass(Graphite.class)
@Profile("!" + Constants.SPRING_PROFILE_FAST)
public static class GraphiteRegistry { public static class GraphiteRegistry {
private final Logger log = LoggerFactory.getLogger(GraphiteRegistry.class); private final Logger log = LoggerFactory.getLogger(GraphiteRegistry.class);
@ -100,7 +109,6 @@ public class MetricsConfiguration extends MetricsConfigurerAdapter {
@Configuration @Configuration
@ConditionalOnClass(SparkReporter.class) @ConditionalOnClass(SparkReporter.class)
@Profile("!" + Constants.SPRING_PROFILE_FAST)
public static class SparkRegistry { public static class SparkRegistry {
private final Logger log = LoggerFactory.getLogger(SparkRegistry.class); private final Logger log = LoggerFactory.getLogger(SparkRegistry.class);

View File

@ -2,9 +2,11 @@ package com.mycompany.myapp.config;
import com.mycompany.myapp.security.*; import com.mycompany.myapp.security.*;
import com.mycompany.myapp.web.filter.CsrfCookieGeneratorFilter; import com.mycompany.myapp.web.filter.CsrfCookieGeneratorFilter;
import com.mycompany.myapp.config.JHipsterProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment; import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@ -27,7 +29,7 @@ import javax.inject.Inject;
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Inject @Inject
private Environment env; private JHipsterProperties jHipsterProperties;
@Inject @Inject
private AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler; private AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler;
@ -62,10 +64,11 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override @Override
public void configure(WebSecurity web) throws Exception { public void configure(WebSecurity web) throws Exception {
web.ignoring() web.ignoring()
.antMatchers("/scripts/**/*.{js,html}") .antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/bower_components/**") .antMatchers("/bower_components/**")
.antMatchers("/i18n/**") .antMatchers("/i18n/**")
.antMatchers("/assets/**") .antMatchers("/content/**")
.antMatchers("/swagger-ui/index.html") .antMatchers("/swagger-ui/index.html")
.antMatchers("/test/**") .antMatchers("/test/**")
.antMatchers("/h2-console/**"); .antMatchers("/h2-console/**");
@ -84,7 +87,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
.rememberMe() .rememberMe()
.rememberMeServices(rememberMeServices) .rememberMeServices(rememberMeServices)
.rememberMeParameter("remember-me") .rememberMeParameter("remember-me")
.key(env.getProperty("jhipster.security.rememberme.key")) .key(jHipsterProperties.getSecurity().getRememberMe().getKey())
.and() .and()
.formLogin() .formLogin()
.loginProcessingUrl("/api/authentication") .loginProcessingUrl("/api/authentication")
@ -123,7 +126,6 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
.antMatchers("/info/**").hasAuthority(AuthoritiesConstants.ADMIN) .antMatchers("/info/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/autoconfig/**").hasAuthority(AuthoritiesConstants.ADMIN) .antMatchers("/autoconfig/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/env/**").hasAuthority(AuthoritiesConstants.ADMIN) .antMatchers("/env/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/trace/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/mappings/**").hasAuthority(AuthoritiesConstants.ADMIN) .antMatchers("/mappings/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/liquibase/**").hasAuthority(AuthoritiesConstants.ADMIN) .antMatchers("/liquibase/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/v2/api-docs/**").permitAll() .antMatchers("/v2/api-docs/**").permitAll()

View File

@ -3,9 +3,7 @@ package com.mycompany.myapp.config;
import org.apache.commons.lang.CharEncoding; import org.apache.commons.lang.CharEncoding;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.*; import org.springframework.context.annotation.*;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
@Configuration @Configuration
@ -24,14 +22,4 @@ public class ThymeleafConfiguration {
emailTemplateResolver.setOrder(1); emailTemplateResolver.setOrder(1);
return emailTemplateResolver; return emailTemplateResolver;
} }
@Bean
@Description("Spring mail message resolver")
public MessageSource emailMessageSource() {
log.info("loading non-reloadable mail messages resources");
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:/mails/messages/messages");
messageSource.setDefaultEncoding(CharEncoding.UTF_8);
return messageSource;
}
} }

View File

@ -4,7 +4,6 @@ import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.servlet.InstrumentedFilter; import com.codahale.metrics.servlet.InstrumentedFilter;
import com.codahale.metrics.servlets.MetricsServlet; import com.codahale.metrics.servlets.MetricsServlet;
import com.mycompany.myapp.web.filter.CachingHttpHeadersFilter; import com.mycompany.myapp.web.filter.CachingHttpHeadersFilter;
import com.mycompany.myapp.web.filter.StaticResourcesProductionFilter;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -19,6 +18,7 @@ import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter; import org.springframework.web.filter.CorsFilter;
import java.io.File;
import java.util.*; import java.util.*;
import javax.inject.Inject; import javax.inject.Inject;
import javax.servlet.*; import javax.servlet.*;
@ -35,7 +35,7 @@ public class WebConfigurer implements ServletContextInitializer, EmbeddedServlet
private Environment env; private Environment env;
@Inject @Inject
private JHipsterProperties props; private JHipsterProperties jHipsterProperties;
@Autowired(required = false) @Autowired(required = false)
private MetricRegistry metricRegistry; private MetricRegistry metricRegistry;
@ -44,12 +44,9 @@ public class WebConfigurer implements ServletContextInitializer, EmbeddedServlet
public void onStartup(ServletContext servletContext) throws ServletException { public void onStartup(ServletContext servletContext) throws ServletException {
log.info("Web application configuration, using profiles: {}", Arrays.toString(env.getActiveProfiles())); log.info("Web application configuration, using profiles: {}", Arrays.toString(env.getActiveProfiles()));
EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC); EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
if (!env.acceptsProfiles(Constants.SPRING_PROFILE_FAST)) { initMetrics(servletContext, disps);
initMetrics(servletContext, disps);
}
if (env.acceptsProfiles(Constants.SPRING_PROFILE_PRODUCTION)) { if (env.acceptsProfiles(Constants.SPRING_PROFILE_PRODUCTION)) {
initCachingHttpHeadersFilter(servletContext, disps); initCachingHttpHeadersFilter(servletContext, disps);
initStaticResourcesProductionFilter(servletContext, disps);
} }
if (env.acceptsProfiles(Constants.SPRING_PROFILE_DEVELOPMENT)) { if (env.acceptsProfiles(Constants.SPRING_PROFILE_DEVELOPMENT)) {
initH2Console(servletContext); initH2Console(servletContext);
@ -58,7 +55,7 @@ public class WebConfigurer implements ServletContextInitializer, EmbeddedServlet
} }
/** /**
* Set up Mime types. * Set up Mime types and, if needed, set the document root.
*/ */
@Override @Override
public void customize(ConfigurableEmbeddedServletContainer container) { public void customize(ConfigurableEmbeddedServletContainer container) {
@ -68,24 +65,17 @@ public class WebConfigurer implements ServletContextInitializer, EmbeddedServlet
// CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64 // CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64
mappings.add("json", "text/html;charset=utf-8"); mappings.add("json", "text/html;charset=utf-8");
container.setMimeMappings(mappings); container.setMimeMappings(mappings);
}
/** // When running in an IDE or with ./mvnw spring-boot:run, set location of the static web assets.
* Initializes the static resources production Filter. File root;
*/ if (env.acceptsProfiles(Constants.SPRING_PROFILE_PRODUCTION)) {
private void initStaticResourcesProductionFilter(ServletContext servletContext, root = new File("target/www/");
EnumSet<DispatcherType> disps) { } else {
root = new File("src/main/webapp/");
log.debug("Registering static resources production Filter"); }
FilterRegistration.Dynamic staticResourcesProductionFilter = if (root.exists() && root.isDirectory()) {
servletContext.addFilter("staticResourcesProductionFilter", container.setDocumentRoot(root);
new StaticResourcesProductionFilter()); }
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/index.html");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/assets/*");
staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/scripts/*");
staticResourcesProductionFilter.setAsyncSupported(true);
} }
/** /**
@ -96,10 +86,10 @@ public class WebConfigurer implements ServletContextInitializer, EmbeddedServlet
log.debug("Registering Caching HTTP Headers Filter"); log.debug("Registering Caching HTTP Headers Filter");
FilterRegistration.Dynamic cachingHttpHeadersFilter = FilterRegistration.Dynamic cachingHttpHeadersFilter =
servletContext.addFilter("cachingHttpHeadersFilter", servletContext.addFilter("cachingHttpHeadersFilter",
new CachingHttpHeadersFilter(env)); new CachingHttpHeadersFilter(jHipsterProperties));
cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/dist/assets/*"); cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/content/*");
cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/dist/scripts/*"); cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/app/*");
cachingHttpHeadersFilter.setAsyncSupported(true); cachingHttpHeadersFilter.setAsyncSupported(true);
} }
@ -132,7 +122,7 @@ public class WebConfigurer implements ServletContextInitializer, EmbeddedServlet
@Bean @Bean
public CorsFilter corsFilter() { public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = props.getCors(); CorsConfiguration config = jHipsterProperties.getCors();
if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) { if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) {
source.registerCorsConfiguration("/api/**", config); source.registerCorsConfiguration("/api/**", config);
source.registerCorsConfiguration("/v2/api-docs", config); source.registerCorsConfiguration("/v2/api-docs", config);
@ -148,7 +138,7 @@ public class WebConfigurer implements ServletContextInitializer, EmbeddedServlet
log.debug("Initialize H2 console"); log.debug("Initialize H2 console");
ServletRegistration.Dynamic h2ConsoleServlet = servletContext.addServlet("H2Console", new org.h2.server.web.WebServlet()); ServletRegistration.Dynamic h2ConsoleServlet = servletContext.addServlet("H2Console", new org.h2.server.web.WebServlet());
h2ConsoleServlet.addMapping("/h2-console/*"); h2ConsoleServlet.addMapping("/h2-console/*");
h2ConsoleServlet.setInitParameter("-properties", "src/main/resources"); h2ConsoleServlet.setInitParameter("-properties", "src/main/resources/");
h2ConsoleServlet.setLoadOnStartup(1); h2ConsoleServlet.setLoadOnStartup(1);
} }
} }

View File

@ -6,11 +6,13 @@ import com.mycompany.myapp.config.JHipsterProperties;
import java.util.Date; import java.util.Date;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.*; import org.springframework.context.annotation.*;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.util.StopWatch; import org.springframework.util.StopWatch;
import springfox.documentation.service.ApiInfo; import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2; import springfox.documentation.swagger2.annotations.EnableSwagger2;
@ -26,7 +28,7 @@ import static springfox.documentation.builders.PathSelectors.regex;
*/ */
@Configuration @Configuration
@EnableSwagger2 @EnableSwagger2
@Profile("!" + Constants.SPRING_PROFILE_PRODUCTION) @ConditionalOnExpression("#{!environment.acceptsProfiles('" + Constants.SPRING_PROFILE_NO_SWAGGER + "') && !environment.acceptsProfiles('" + Constants.SPRING_PROFILE_PRODUCTION + "')}")
public class SwaggerConfiguration { public class SwaggerConfiguration {
private final Logger log = LoggerFactory.getLogger(SwaggerConfiguration.class); private final Logger log = LoggerFactory.getLogger(SwaggerConfiguration.class);
@ -35,29 +37,36 @@ public class SwaggerConfiguration {
/** /**
* Swagger Springfox configuration. * Swagger Springfox configuration.
*
* @param jHipsterProperties the properties of the application
* @return the Swagger Springfox configuration
*/ */
@Bean @Bean
@Profile("!" + Constants.SPRING_PROFILE_FAST)
public Docket swaggerSpringfoxDocket(JHipsterProperties jHipsterProperties) { public Docket swaggerSpringfoxDocket(JHipsterProperties jHipsterProperties) {
log.debug("Starting Swagger"); log.debug("Starting Swagger");
StopWatch watch = new StopWatch(); StopWatch watch = new StopWatch();
watch.start(); watch.start();
Contact contact = new Contact(
jHipsterProperties.getSwagger().getContactName(),
jHipsterProperties.getSwagger().getContactUrl(),
jHipsterProperties.getSwagger().getContactEmail());
ApiInfo apiInfo = new ApiInfo( ApiInfo apiInfo = new ApiInfo(
jHipsterProperties.getSwagger().getTitle(), jHipsterProperties.getSwagger().getTitle(),
jHipsterProperties.getSwagger().getDescription(), jHipsterProperties.getSwagger().getDescription(),
jHipsterProperties.getSwagger().getVersion(), jHipsterProperties.getSwagger().getVersion(),
jHipsterProperties.getSwagger().getTermsOfServiceUrl(), jHipsterProperties.getSwagger().getTermsOfServiceUrl(),
jHipsterProperties.getSwagger().getContact(), contact,
jHipsterProperties.getSwagger().getLicense(), jHipsterProperties.getSwagger().getLicense(),
jHipsterProperties.getSwagger().getLicenseUrl()); jHipsterProperties.getSwagger().getLicenseUrl());
Docket docket = new Docket(DocumentationType.SWAGGER_2) Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo) .apiInfo(apiInfo)
.genericModelSubstitutes(ResponseEntity.class)
.forCodeGeneration(true) .forCodeGeneration(true)
.genericModelSubstitutes(ResponseEntity.class) .genericModelSubstitutes(ResponseEntity.class)
.ignoredParameterTypes(Pageable.class) .ignoredParameterTypes(Pageable.class)
.directModelSubstitute(java.time.LocalDate.class, String.class) .ignoredParameterTypes(java.sql.Date.class)
.directModelSubstitute(java.time.LocalDate.class, java.sql.Date.class)
.directModelSubstitute(java.time.ZonedDateTime.class, Date.class) .directModelSubstitute(java.time.ZonedDateTime.class, Date.class)
.directModelSubstitute(java.time.LocalDateTime.class, Date.class) .directModelSubstitute(java.time.LocalDateTime.class, Date.class)
.select() .select()

View File

@ -16,7 +16,7 @@ import liquibase.integration.spring.SpringLiquibase;
/** /**
* Specific liquibase.integration.spring.SpringLiquibase that will update the database asynchronously. * Specific liquibase.integration.spring.SpringLiquibase that will update the database asynchronously.
* <p> * <p>
* By default, this asynchronous version only works when using the "dev" profile.<br/> * By default, this asynchronous version only works when using the "dev" profile.<p>
* The standard liquibase.integration.spring.SpringLiquibase starts Liquibase in the current thread: * The standard liquibase.integration.spring.SpringLiquibase starts Liquibase in the current thread:
* <ul> * <ul>
* <li>This is needed if you want to do some database requests at startup</li> * <li>This is needed if you want to do some database requests at startup</li>
@ -27,7 +27,6 @@ import liquibase.integration.spring.SpringLiquibase;
* <li>On a recent MacBook Pro, start-up time is down from 14 seconds to 8 seconds</li> * <li>On a recent MacBook Pro, start-up time is down from 14 seconds to 8 seconds</li>
* <li>In production, this can help your application run on platforms like Heroku, where it must start/restart very quickly</li> * <li>In production, this can help your application run on platforms like Heroku, where it must start/restart very quickly</li>
* </ul> * </ul>
* </p>
*/ */
public class AsyncSpringLiquibase extends SpringLiquibase { public class AsyncSpringLiquibase extends SpringLiquibase {
@ -42,18 +41,22 @@ public class AsyncSpringLiquibase extends SpringLiquibase {
@Override @Override
public void afterPropertiesSet() throws LiquibaseException { public void afterPropertiesSet() throws LiquibaseException {
if (env.acceptsProfiles(Constants.SPRING_PROFILE_DEVELOPMENT, Constants.SPRING_PROFILE_HEROKU)) { if (!env.acceptsProfiles(Constants.SPRING_PROFILE_NO_LIQUIBASE)) {
taskExecutor.execute(() -> { if (env.acceptsProfiles(Constants.SPRING_PROFILE_DEVELOPMENT, Constants.SPRING_PROFILE_HEROKU)) {
try { taskExecutor.execute(() -> {
log.warn("Starting Liquibase asynchronously, your database might not be ready at startup!"); try {
initDb(); log.warn("Starting Liquibase asynchronously, your database might not be ready at startup!");
} catch (LiquibaseException e) { initDb();
log.error("Liquibase could not start correctly, your database is NOT ready: {}", e.getMessage(), e); } catch (LiquibaseException e) {
} log.error("Liquibase could not start correctly, your database is NOT ready: {}", e.getMessage(), e);
}); }
});
} else {
log.debug("Starting Liquibase synchronously");
initDb();
}
} else { } else {
log.debug("Starting Liquibase synchronously"); log.debug("Liquibase is disabled");
initDb();
} }
} }

View File

@ -28,13 +28,11 @@ public abstract class AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@CreatedBy @CreatedBy
@NotNull
@Column(name = "created_by", nullable = false, length = 50, updatable = false) @Column(name = "created_by", nullable = false, length = 50, updatable = false)
@JsonIgnore @JsonIgnore
private String createdBy; private String createdBy;
@CreatedDate @CreatedDate
@NotNull
@Column(name = "created_date", nullable = false) @Column(name = "created_date", nullable = false)
@JsonIgnore @JsonIgnore
private ZonedDateTime createdDate = ZonedDateTime.now(); private ZonedDateTime createdDate = ZonedDateTime.now();

View File

@ -18,6 +18,8 @@ import java.io.Serializable;
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Authority implements Serializable { public class Authority implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull @NotNull
@Size(min = 0, max = 50) @Size(min = 0, max = 50)
@Id @Id

View File

@ -20,6 +20,8 @@ import java.util.Objects;
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class BankAccount implements Serializable { public class BankAccount implements Serializable {
private static final long serialVersionUID = 1L;
@Id @Id
@GeneratedValue(strategy = GenerationType.AUTO) @GeneratedValue(strategy = GenerationType.AUTO)
private Long id; private Long id;
@ -27,13 +29,12 @@ public class BankAccount implements Serializable {
@NotNull @NotNull
@Column(name = "name", nullable = false) @Column(name = "name", nullable = false)
private String name; private String name;
@NotNull @NotNull
@Column(name = "balance", precision=10, scale=2, nullable = false) @Column(name = "balance", precision=10, scale=2, nullable = false)
private BigDecimal balance; private BigDecimal balance;
@ManyToOne @ManyToOne
@JoinColumn(name = "user_id")
private User user; private User user;
@OneToMany(mappedBy = "bankAccount") @OneToMany(mappedBy = "bankAccount")
@ -52,7 +53,7 @@ public class BankAccount implements Serializable {
public String getName() { public String getName() {
return name; return name;
} }
public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
} }
@ -60,7 +61,7 @@ public class BankAccount implements Serializable {
public BigDecimal getBalance() { public BigDecimal getBalance() {
return balance; return balance;
} }
public void setBalance(BigDecimal balance) { public void setBalance(BigDecimal balance) {
this.balance = balance; this.balance = balance;
} }

View File

@ -19,6 +19,8 @@ import java.util.Objects;
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Label implements Serializable { public class Label implements Serializable {
private static final long serialVersionUID = 1L;
@Id @Id
@GeneratedValue(strategy = GenerationType.AUTO) @GeneratedValue(strategy = GenerationType.AUTO)
private Long id; private Long id;
@ -27,7 +29,7 @@ public class Label implements Serializable {
@Size(min = 3) @Size(min = 3)
@Column(name = "label", nullable = false) @Column(name = "label", nullable = false)
private String label; private String label;
@ManyToMany(mappedBy = "labels") @ManyToMany(mappedBy = "labels")
@JsonIgnore @JsonIgnore
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@ -44,7 +46,7 @@ public class Label implements Serializable {
public String getLabel() { public String getLabel() {
return label; return label;
} }
public void setLabel(String label) { public void setLabel(String label) {
this.label = label; this.label = label;
} }

View File

@ -2,12 +2,12 @@ package com.mycompany.myapp.domain;
import org.hibernate.annotations.Cache; import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.CacheConcurrencyStrategy;
import java.time.ZonedDateTime;
import javax.persistence.*; import javax.persistence.*;
import javax.validation.constraints.*; import javax.validation.constraints.*;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.ZonedDateTime;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.Objects; import java.util.Objects;
@ -20,6 +20,8 @@ import java.util.Objects;
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Operation implements Serializable { public class Operation implements Serializable {
private static final long serialVersionUID = 1L;
@Id @Id
@GeneratedValue(strategy = GenerationType.AUTO) @GeneratedValue(strategy = GenerationType.AUTO)
private Long id; private Long id;
@ -27,16 +29,15 @@ public class Operation implements Serializable {
@NotNull @NotNull
@Column(name = "date", nullable = false) @Column(name = "date", nullable = false)
private ZonedDateTime date; private ZonedDateTime date;
@Column(name = "description") @Column(name = "description")
private String description; private String description;
@NotNull @NotNull
@Column(name = "amount", precision=10, scale=2, nullable = false) @Column(name = "amount", precision=10, scale=2, nullable = false)
private BigDecimal amount; private BigDecimal amount;
@ManyToOne @ManyToOne
@JoinColumn(name = "bank_account_id")
private BankAccount bankAccount; private BankAccount bankAccount;
@ManyToMany @ManyToMany
@ -57,7 +58,7 @@ public class Operation implements Serializable {
public ZonedDateTime getDate() { public ZonedDateTime getDate() {
return date; return date;
} }
public void setDate(ZonedDateTime date) { public void setDate(ZonedDateTime date) {
this.date = date; this.date = date;
} }
@ -65,7 +66,7 @@ public class Operation implements Serializable {
public String getDescription() { public String getDescription() {
return description; return description;
} }
public void setDescription(String description) { public void setDescription(String description) {
this.description = description; this.description = description;
} }
@ -73,7 +74,7 @@ public class Operation implements Serializable {
public BigDecimal getAmount() { public BigDecimal getAmount() {
return amount; return amount;
} }
public void setAmount(BigDecimal amount) { public void setAmount(BigDecimal amount) {
this.amount = amount; this.amount = amount;
} }

View File

@ -22,10 +22,9 @@ import java.io.Serializable;
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class PersistentToken implements Serializable { public class PersistentToken implements Serializable {
private static final long serialVersionUID = 1L;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("d MMMM yyyy"); private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("d MMMM yyyy");
private static final int MAX_USER_AGENT_LEN = 255; private static final int MAX_USER_AGENT_LEN = 255;

View File

@ -22,6 +22,8 @@ import java.time.ZonedDateTime;
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class User extends AbstractAuditingEntity implements Serializable { public class User extends AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id @Id
@GeneratedValue(strategy = GenerationType.AUTO) @GeneratedValue(strategy = GenerationType.AUTO)
private Long id; private Long id;
@ -46,11 +48,13 @@ public class User extends AbstractAuditingEntity implements Serializable {
@Column(name = "last_name", length = 50) @Column(name = "last_name", length = 50)
private String lastName; private String lastName;
@NotNull
@Email @Email
@Size(max = 100) @Size(max = 100)
@Column(length = 100, unique = true) @Column(length = 100, unique = true)
private String email; private String email;
@NotNull
@Column(nullable = false) @Column(nullable = false)
private boolean activated = false; private boolean activated = false;

View File

@ -12,7 +12,7 @@ import com.fasterxml.jackson.databind.SerializerProvider;
public final class JSR310DateTimeSerializer extends JsonSerializer<TemporalAccessor> { public final class JSR310DateTimeSerializer extends JsonSerializer<TemporalAccessor> {
private static final DateTimeFormatter ISOFormatter = private static final DateTimeFormatter ISOFormatter =
DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.of("Z")); DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").withZone(ZoneId.of("Z"));
public static final JSR310DateTimeSerializer INSTANCE = new JSR310DateTimeSerializer(); public static final JSR310DateTimeSerializer INSTANCE = new JSR310DateTimeSerializer();

View File

@ -5,7 +5,6 @@ import com.mycompany.myapp.domain.PersistentAuditEvent;
import org.springframework.boot.actuate.audit.AuditEvent; import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.boot.actuate.audit.AuditEventRepository; import org.springframework.boot.actuate.audit.AuditEventRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -18,54 +17,48 @@ import java.util.Date;
import java.util.List; import java.util.List;
/** /**
* Wraps an implementation of Spring Boot's AuditEventRepository. * An implementation of Spring Boot's AuditEventRepository.
*/ */
@Repository @Repository
public class CustomAuditEventRepository { public class CustomAuditEventRepository implements AuditEventRepository {
private static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE";
private static final String ANONYMOUS_USER = "anonymousUser";
@Inject @Inject
private PersistenceAuditEventRepository persistenceAuditEventRepository; private PersistenceAuditEventRepository persistenceAuditEventRepository;
@Bean @Inject
public AuditEventRepository auditEventRepository() { private AuditEventConverter auditEventConverter;
return new AuditEventRepository() {
private static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE"; @Override
public List<AuditEvent> find(String principal, Date after) {
Iterable<PersistentAuditEvent> persistentAuditEvents;
if (principal == null && after == null) {
persistentAuditEvents = persistenceAuditEventRepository.findAll();
} else if (after == null) {
persistentAuditEvents = persistenceAuditEventRepository.findByPrincipal(principal);
} else {
persistentAuditEvents =
persistenceAuditEventRepository.findByPrincipalAndAuditEventDateAfter(principal, LocalDateTime.from(after.toInstant()));
}
return auditEventConverter.convertToAuditEvent(persistentAuditEvents);
}
private static final String ANONYMOUS_USER = "anonymousUser"; @Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void add(AuditEvent event) {
if (!AUTHORIZATION_FAILURE.equals(event.getType()) &&
!ANONYMOUS_USER.equals(event.getPrincipal().toString())) {
@Inject PersistentAuditEvent persistentAuditEvent = new PersistentAuditEvent();
private AuditEventConverter auditEventConverter; persistentAuditEvent.setPrincipal(event.getPrincipal());
persistentAuditEvent.setAuditEventType(event.getType());
@Override Instant instant = Instant.ofEpochMilli(event.getTimestamp().getTime());
public List<AuditEvent> find(String principal, Date after) { persistentAuditEvent.setAuditEventDate(LocalDateTime.ofInstant(instant, ZoneId.systemDefault()));
Iterable<PersistentAuditEvent> persistentAuditEvents; persistentAuditEvent.setData(auditEventConverter.convertDataToStrings(event.getData()));
if (principal == null && after == null) { persistenceAuditEventRepository.save(persistentAuditEvent);
persistentAuditEvents = persistenceAuditEventRepository.findAll(); }
} else if (after == null) {
persistentAuditEvents = persistenceAuditEventRepository.findByPrincipal(principal);
} else {
persistentAuditEvents =
persistenceAuditEventRepository.findByPrincipalAndAuditEventDateAfter(principal, LocalDateTime.from(after.toInstant()));
}
return auditEventConverter.convertToAuditEvent(persistentAuditEvents);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void add(AuditEvent event) {
if (!AUTHORIZATION_FAILURE.equals(event.getType()) &&
!ANONYMOUS_USER.equals(event.getPrincipal().toString())) {
PersistentAuditEvent persistentAuditEvent = new PersistentAuditEvent();
persistentAuditEvent.setPrincipal(event.getPrincipal());
persistentAuditEvent.setAuditEventType(event.getType());
Instant instant = Instant.ofEpochMilli(event.getTimestamp().getTime());
persistentAuditEvent.setAuditEventDate(LocalDateTime.ofInstant(instant, ZoneId.systemDefault()));
persistentAuditEvent.setData(auditEventConverter.convertDataToStrings(event.getData()));
persistenceAuditEventRepository.save(persistentAuditEvent);
}
}
};
} }
} }

View File

@ -4,6 +4,8 @@ import com.mycompany.myapp.domain.PersistentAuditEvent;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List; import java.util.List;
@ -16,5 +18,5 @@ public interface PersistenceAuditEventRepository extends JpaRepository<Persisten
List<PersistentAuditEvent> findByPrincipalAndAuditEventDateAfter(String principal, LocalDateTime after); List<PersistentAuditEvent> findByPrincipalAndAuditEventDateAfter(String principal, LocalDateTime after);
List<PersistentAuditEvent> findAllByAuditEventDateBetween(LocalDateTime fromDate, LocalDateTime toDate); Page<PersistentAuditEvent> findAllByAuditEventDateBetween(LocalDateTime fromDate, LocalDateTime toDate, Pageable pageable);
} }

View File

@ -4,6 +4,7 @@ import com.mycompany.myapp.domain.PersistentToken;
import com.mycompany.myapp.domain.User; import com.mycompany.myapp.domain.User;
import com.mycompany.myapp.repository.PersistentTokenRepository; import com.mycompany.myapp.repository.PersistentTokenRepository;
import com.mycompany.myapp.repository.UserRepository; import com.mycompany.myapp.repository.UserRepository;
import com.mycompany.myapp.config.JHipsterProperties;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
@ -25,9 +26,9 @@ import java.util.Arrays;
/** /**
* Custom implementation of Spring Security's RememberMeServices. * Custom implementation of Spring Security's RememberMeServices.
* <p/> * <p>
* Persistent tokens are used by Spring Security to automatically log in users. * Persistent tokens are used by Spring Security to automatically log in users.
* <p/> * <p>
* This is a specific implementation of Spring Security's remember-me authentication, but it is much * This is a specific implementation of Spring Security's remember-me authentication, but it is much
* more powerful than the standard implementations: * more powerful than the standard implementations:
* <ul> * <ul>
@ -35,17 +36,16 @@ import java.util.Arrays;
* <li>It stores more information, such as the IP address and the user agent, for audit purposes<li> * <li>It stores more information, such as the IP address and the user agent, for audit purposes<li>
* <li>When a user logs out, only his current session is invalidated, and not all of his sessions</li> * <li>When a user logs out, only his current session is invalidated, and not all of his sessions</li>
* </ul> * </ul>
* <p/> * <p>
* This is inspired by: * This is inspired by:
* <ul> * <ul>
* <li><a href="http://jaspan.com/improved_persistent_login_cookie_best_practice">Improved Persistent Login Cookie * <li><a href="http://jaspan.com/improved_persistent_login_cookie_best_practice">Improved Persistent Login Cookie
* Best Practice</a></li> * Best Practice</a></li>
* <li><a href="https://github.com/blog/1661-modeling-your-app-s-user-session">Github's "Modeling your App's User Session"</a></li></li> * <li><a href="https://github.com/blog/1661-modeling-your-app-s-user-session">Github's "Modeling your App's User Session"</a></li>
* </ul> * </ul>
* <p/> * <p>
* The main algorithm comes from Spring Security's PersistentTokenBasedRememberMeServices, but this class * The main algorithm comes from Spring Security's PersistentTokenBasedRememberMeServices, but this class
* couldn't be cleanly extended. * couldn't be cleanly extended.
* <p/>
*/ */
@Service @Service
public class CustomPersistentRememberMeServices extends public class CustomPersistentRememberMeServices extends
@ -71,15 +71,14 @@ public class CustomPersistentRememberMeServices extends
private UserRepository userRepository; private UserRepository userRepository;
@Inject @Inject
public CustomPersistentRememberMeServices(Environment env, org.springframework.security.core.userdetails public CustomPersistentRememberMeServices(JHipsterProperties jHipsterProperties, org.springframework.security.core.userdetails
.UserDetailsService userDetailsService) { .UserDetailsService userDetailsService) {
super(env.getProperty("jhipster.security.rememberme.key"), userDetailsService); super(jHipsterProperties.getSecurity().getRememberMe().getKey(), userDetailsService);
random = new SecureRandom(); random = new SecureRandom();
} }
@Override @Override
@Transactional
protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request, protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,
HttpServletResponse response) { HttpServletResponse response) {
@ -129,7 +128,7 @@ public class CustomPersistentRememberMeServices extends
/** /**
* When logout occurs, only invalidate the current token, and not all user sessions. * When logout occurs, only invalidate the current token, and not all user sessions.
* <p/> * <p>
* The standard Spring Security implementations are too basic: they invalidate all tokens for the * The standard Spring Security implementations are too basic: they invalidate all tokens for the
* current user, so when he logs out from one browser, all his other sessions are destroyed. * current user, so when he logs out from one browser, all his other sessions are destroyed.
*/ */

View File

@ -5,7 +5,6 @@ import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection; import java.util.Collection;
@ -20,6 +19,8 @@ public final class SecurityUtils {
/** /**
* Get the login of the current user. * Get the login of the current user.
*
* @return the login of the current user
*/ */
public static String getCurrentUserLogin() { public static String getCurrentUserLogin() {
SecurityContext securityContext = SecurityContextHolder.getContext(); SecurityContext securityContext = SecurityContextHolder.getContext();
@ -54,27 +55,13 @@ public final class SecurityUtils {
return true; return true;
} }
/**
* Return the current user, or throws an exception, if the user is not
* authenticated yet.
*
* @return the current user
*/
public static User getCurrentUser() {
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
if (authentication != null) {
if (authentication.getPrincipal() instanceof User) {
return (User) authentication.getPrincipal();
}
}
throw new IllegalStateException("User not found!");
}
/** /**
* If the current user has a specific authority (security role). * If the current user has a specific authority (security role).
* *
* <p>The name of this method comes from the isUserInRole() method in the Servlet API</p> * <p>The name of this method comes from the isUserInRole() method in the Servlet API</p>
*
* @param authority the authorithy to check
* @return true if the current user has the authority, false otherwise
*/ */
public static boolean isCurrentUserInRole(String authority) { public static boolean isCurrentUserInRole(String authority) {
SecurityContext securityContext = SecurityContextHolder.getContext(); SecurityContext securityContext = SecurityContextHolder.getContext();

View File

@ -1,10 +1,11 @@
package com.mycompany.myapp.service; package com.mycompany.myapp.service;
import com.mycompany.myapp.config.audit.AuditEventConverter; import com.mycompany.myapp.config.audit.AuditEventConverter;
import com.mycompany.myapp.domain.PersistentAuditEvent;
import com.mycompany.myapp.repository.PersistenceAuditEventRepository; import com.mycompany.myapp.repository.PersistenceAuditEventRepository;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import org.springframework.boot.actuate.audit.AuditEvent; import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -14,7 +15,6 @@ import java.util.Optional;
/** /**
* Service for managing audit events. * Service for managing audit events.
* <p/>
* <p> * <p>
* This is the default implementation to support SpringBoot Actuator AuditEventRepository * This is the default implementation to support SpringBoot Actuator AuditEventRepository
* </p> * </p>
@ -36,15 +36,14 @@ public class AuditEventService {
this.auditEventConverter = auditEventConverter; this.auditEventConverter = auditEventConverter;
} }
public List<AuditEvent> findAll() { public Page<AuditEvent> findAll(Pageable pageable) {
return auditEventConverter.convertToAuditEvent(persistenceAuditEventRepository.findAll()); return persistenceAuditEventRepository.findAll(pageable)
.map(persistentAuditEvents -> auditEventConverter.convertToAuditEvent(persistentAuditEvents));
} }
public List<AuditEvent> findByDates(LocalDateTime fromDate, LocalDateTime toDate) { public Page<AuditEvent> findByDates(LocalDateTime fromDate, LocalDateTime toDate, Pageable pageable) {
List<PersistentAuditEvent> persistentAuditEvents = return persistenceAuditEventRepository.findAllByAuditEventDateBetween(fromDate, toDate, pageable)
persistenceAuditEventRepository.findAllByAuditEventDateBetween(fromDate, toDate); .map(persistentAuditEvents -> auditEventConverter.convertToAuditEvent(persistentAuditEvents));
return auditEventConverter.convertToAuditEvent(persistentAuditEvents);
} }
public Optional<AuditEvent> find(Long id) { public Optional<AuditEvent> find(Long id) {

View File

@ -22,7 +22,6 @@ import java.util.Locale;
/** /**
* Service for sending e-mails. * Service for sending e-mails.
* <p/>
* <p> * <p>
* We use the @Async annotation to send e-mails asynchronously. * We use the @Async annotation to send e-mails asynchronously.
* </p> * </p>

View File

@ -31,12 +31,14 @@ public class UserService {
private final Logger log = LoggerFactory.getLogger(UserService.class); private final Logger log = LoggerFactory.getLogger(UserService.class);
@Inject @Inject
private PasswordEncoder passwordEncoder; private PasswordEncoder passwordEncoder;
@Inject @Inject
private UserRepository userRepository; private UserRepository userRepository;
@Inject @Inject
private PersistentTokenRepository persistentTokenRepository; private PersistentTokenRepository persistentTokenRepository;
@ -45,7 +47,7 @@ public class UserService {
public Optional<User> activateRegistration(String key) { public Optional<User> activateRegistration(String key) {
log.debug("Activating user for activation key {}", key); log.debug("Activating user for activation key {}", key);
userRepository.findOneByActivationKey(key) return userRepository.findOneByActivationKey(key)
.map(user -> { .map(user -> {
// activate given user for the registration key. // activate given user for the registration key.
user.setActivated(true); user.setActivated(true);
@ -54,7 +56,6 @@ public class UserService {
log.debug("Activated user: {}", user); log.debug("Activated user: {}", user);
return user; return user;
}); });
return Optional.empty();
} }
public Optional<User> completePasswordReset(String newPassword, String key) { public Optional<User> completePasswordReset(String newPassword, String key) {
@ -117,7 +118,7 @@ public class UserService {
user.setLastName(managedUserDTO.getLastName()); user.setLastName(managedUserDTO.getLastName());
user.setEmail(managedUserDTO.getEmail()); user.setEmail(managedUserDTO.getEmail());
if (managedUserDTO.getLangKey() == null) { if (managedUserDTO.getLangKey() == null) {
user.setLangKey("en"); // default language is English user.setLangKey("en"); // default language
} else { } else {
user.setLangKey(managedUserDTO.getLangKey()); user.setLangKey(managedUserDTO.getLangKey());
} }
@ -139,7 +140,7 @@ public class UserService {
} }
public void updateUserInformation(String firstName, String lastName, String email, String langKey) { public void updateUserInformation(String firstName, String lastName, String email, String langKey) {
userRepository.findOneByLogin(SecurityUtils.getCurrentUser().getUsername()).ifPresent(u -> { userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).ifPresent(u -> {
u.setFirstName(firstName); u.setFirstName(firstName);
u.setLastName(lastName); u.setLastName(lastName);
u.setEmail(email); u.setEmail(email);
@ -157,7 +158,7 @@ public class UserService {
} }
public void changePassword(String password) { public void changePassword(String password) {
userRepository.findOneByLogin(SecurityUtils.getCurrentUser().getUsername()).ifPresent(u -> { userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).ifPresent(u -> {
String encryptedPassword = passwordEncoder.encode(password); String encryptedPassword = passwordEncoder.encode(password);
u.setPassword(encryptedPassword); u.setPassword(encryptedPassword);
userRepository.save(u); userRepository.save(u);
@ -182,7 +183,7 @@ public class UserService {
@Transactional(readOnly = true) @Transactional(readOnly = true)
public User getUserWithAuthorities() { public User getUserWithAuthorities() {
User user = userRepository.findOneByLogin(SecurityUtils.getCurrentUser().getUsername()).get(); User user = userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).get();
user.getAuthorities().size(); // eagerly load the association user.getAuthorities().size(); // eagerly load the association
return user; return user;
} }
@ -190,7 +191,6 @@ public class UserService {
/** /**
* Persistent Token are used for providing automatic authentication, they should be automatically deleted after * Persistent Token are used for providing automatic authentication, they should be automatically deleted after
* 30 days. * 30 days.
* <p/>
* <p> * <p>
* This is scheduled to get fired everyday, at midnight. * This is scheduled to get fired everyday, at midnight.
* </p> * </p>
@ -208,7 +208,6 @@ public class UserService {
/** /**
* Not activated users should be automatically deleted after 3 days. * Not activated users should be automatically deleted after 3 days.
* <p/>
* <p> * <p>
* This is scheduled to get fired everyday, at 01:00 (am). * This is scheduled to get fired everyday, at 01:00 (am).
* </p> * </p>

View File

@ -1,6 +1,6 @@
package com.mycompany.myapp.web.filter; package com.mycompany.myapp.web.filter;
import org.springframework.core.env.Environment; import com.mycompany.myapp.config.JHipsterProperties;
import javax.servlet.*; import javax.servlet.*;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -15,18 +15,17 @@ public class CachingHttpHeadersFilter implements Filter {
// We consider the last modified date is the start up time of the server // We consider the last modified date is the start up time of the server
private final static long LAST_MODIFIED = System.currentTimeMillis(); private final static long LAST_MODIFIED = System.currentTimeMillis();
private long CACHE_TIME_TO_LIVE = TimeUnit.DAYS.toMillis(31L); private long CACHE_TIME_TO_LIVE = TimeUnit.DAYS.toMillis(1461L);
private Environment env; private JHipsterProperties jHipsterProperties;;
public CachingHttpHeadersFilter(Environment env) { public CachingHttpHeadersFilter(JHipsterProperties jHipsterProperties) {
this.env = env; this.jHipsterProperties = jHipsterProperties;
} }
@Override @Override
public void init(FilterConfig filterConfig) throws ServletException { public void init(FilterConfig filterConfig) throws ServletException {
CACHE_TIME_TO_LIVE = TimeUnit.DAYS.toMillis(env.getProperty("jhipster.http.cache.timeToLiveInDays", CACHE_TIME_TO_LIVE = TimeUnit.DAYS.toMillis(jHipsterProperties.getHttp().getCache().getTimeToLiveInDays());
Long.class, 31L));
} }
@Override @Override

View File

@ -1,42 +0,0 @@
package com.mycompany.myapp.web.filter;
import org.apache.commons.lang.StringUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* This filter is used in production, to serve static resources generated by "grunt build".
* <p/>
* <p>
* It is configured to serve resources from the "dist" directory, which is the Grunt
* destination directory.
* </p>
*/
public class StaticResourcesProductionFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// Nothing to initialize
}
@Override
public void destroy() {
// Nothing to destroy
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String contextPath = ((HttpServletRequest) request).getContextPath();
String requestURI = httpRequest.getRequestURI();
requestURI = StringUtils.substringAfter(requestURI, contextPath);
if (StringUtils.equals("/", requestURI)) {
requestURI = "/index.html";
}
String newURI = "/dist" + requestURI;
request.getRequestDispatcher(newURI).forward(request, response);
}
}

View File

@ -16,6 +16,7 @@ import com.mycompany.myapp.web.rest.util.HeaderUtil;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@ -50,17 +51,25 @@ public class AccountResource {
private MailService mailService; private MailService mailService;
/** /**
* POST /register -> register the user. * POST /register : register the user.
*
* @param userDTO the user DTO
* @param request the HTTP request
* @return the ResponseEntity with status 201 (Created) if the user is registred or 400 (Bad Request) if the login or e-mail is already in use
*/ */
@RequestMapping(value = "/register", @RequestMapping(value = "/register",
method = RequestMethod.POST, method = RequestMethod.POST,
produces = MediaType.TEXT_PLAIN_VALUE) produces={MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE})
@Timed @Timed
public ResponseEntity<?> registerAccount(@Valid @RequestBody UserDTO userDTO, HttpServletRequest request) { public ResponseEntity<?> registerAccount(@Valid @RequestBody UserDTO userDTO, HttpServletRequest request) {
HttpHeaders textPlainHeaders = new HttpHeaders();
textPlainHeaders.setContentType(MediaType.TEXT_PLAIN);
return userRepository.findOneByLogin(userDTO.getLogin()) return userRepository.findOneByLogin(userDTO.getLogin())
.map(user -> new ResponseEntity<>("login already in use", HttpStatus.BAD_REQUEST)) .map(user -> new ResponseEntity<>("login already in use", textPlainHeaders, HttpStatus.BAD_REQUEST))
.orElseGet(() -> userRepository.findOneByEmail(userDTO.getEmail()) .orElseGet(() -> userRepository.findOneByEmail(userDTO.getEmail())
.map(user -> new ResponseEntity<>("e-mail address already in use", HttpStatus.BAD_REQUEST)) .map(user -> new ResponseEntity<>("e-mail address already in use", textPlainHeaders, HttpStatus.BAD_REQUEST))
.orElseGet(() -> { .orElseGet(() -> {
User user = userService.createUserInformation(userDTO.getLogin(), userDTO.getPassword(), User user = userService.createUserInformation(userDTO.getLogin(), userDTO.getPassword(),
userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail().toLowerCase(), userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail().toLowerCase(),
@ -79,20 +88,26 @@ public class AccountResource {
} }
/** /**
* GET /activate -> activate the registered user. * GET /activate : activate the registered user.
*
* @param key the activation key
* @return the ResponseEntity with status 200 (OK) and the activated user in body, or status 500 (Internal Server Error) if the user couldn't be activated
*/ */
@RequestMapping(value = "/activate", @RequestMapping(value = "/activate",
method = RequestMethod.GET, method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.APPLICATION_JSON_VALUE)
@Timed @Timed
public ResponseEntity<String> activateAccount(@RequestParam(value = "key") String key) { public ResponseEntity<String> activateAccount(@RequestParam(value = "key") String key) {
return Optional.ofNullable(userService.activateRegistration(key)) return userService.activateRegistration(key)
.map(user -> new ResponseEntity<String>(HttpStatus.OK)) .map(user -> new ResponseEntity<String>(HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
} }
/** /**
* GET /authenticate -> check if the user is authenticated, and return its login. * GET /authenticate : check if the user is authenticated, and return its login.
*
* @param request the HTTP request
* @return the login if the user is authenticated
*/ */
@RequestMapping(value = "/authenticate", @RequestMapping(value = "/authenticate",
method = RequestMethod.GET, method = RequestMethod.GET,
@ -104,7 +119,9 @@ public class AccountResource {
} }
/** /**
* GET /account -> get the current user. * GET /account : get the current user.
*
* @return the ResponseEntity with status 200 (OK) and the current user in body, or status 500 (Internal Server Error) if the user couldn't be returned
*/ */
@RequestMapping(value = "/account", @RequestMapping(value = "/account",
method = RequestMethod.GET, method = RequestMethod.GET,
@ -117,7 +134,10 @@ public class AccountResource {
} }
/** /**
* POST /account -> update the current user information. * POST /account : update the current user information.
*
* @param userDTO the current user information
* @return the ResponseEntity with status 200 (OK), or status 400 (Bad Request) or 500 (Internal Server Error) if the user couldn't be updated
*/ */
@RequestMapping(value = "/account", @RequestMapping(value = "/account",
method = RequestMethod.POST, method = RequestMethod.POST,
@ -129,7 +149,7 @@ public class AccountResource {
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("user-management", "emailexists", "Email already in use")).body(null); return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("user-management", "emailexists", "Email already in use")).body(null);
} }
return userRepository return userRepository
.findOneByLogin(SecurityUtils.getCurrentUser().getUsername()) .findOneByLogin(SecurityUtils.getCurrentUserLogin())
.map(u -> { .map(u -> {
userService.updateUserInformation(userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail(), userService.updateUserInformation(userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail(),
userDTO.getLangKey()); userDTO.getLangKey());
@ -139,11 +159,14 @@ public class AccountResource {
} }
/** /**
* POST /change_password -> changes the current user's password * POST /account/change_password : changes the current user's password
*
* @param password the new password
* @return the ResponseEntity with status 200 (OK), or status 400 (Bad Request) if the new password is not strong enough
*/ */
@RequestMapping(value = "/account/change_password", @RequestMapping(value = "/account/change_password",
method = RequestMethod.POST, method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.TEXT_PLAIN_VALUE)
@Timed @Timed
public ResponseEntity<?> changePassword(@RequestBody String password) { public ResponseEntity<?> changePassword(@RequestBody String password) {
if (!checkPasswordLength(password)) { if (!checkPasswordLength(password)) {
@ -154,14 +177,17 @@ public class AccountResource {
} }
/** /**
* GET /account/sessions -> get the current open sessions. * GET /account/sessions : get the current open sessions.
*
* @return the ResponseEntity with status 200 (OK) and the current open sessions in body,
* or status 500 (Internal Server Error) if the current open sessions couldn't be retrieved
*/ */
@RequestMapping(value = "/account/sessions", @RequestMapping(value = "/account/sessions",
method = RequestMethod.GET, method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.APPLICATION_JSON_VALUE)
@Timed @Timed
public ResponseEntity<List<PersistentToken>> getCurrentSessions() { public ResponseEntity<List<PersistentToken>> getCurrentSessions() {
return userRepository.findOneByLogin(SecurityUtils.getCurrentUser().getUsername()) return userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin())
.map(user -> new ResponseEntity<>( .map(user -> new ResponseEntity<>(
persistentTokenRepository.findByUser(user), persistentTokenRepository.findByUser(user),
HttpStatus.OK)) HttpStatus.OK))
@ -169,7 +195,7 @@ public class AccountResource {
} }
/** /**
* DELETE /account/sessions?series={series} -> invalidate an existing session. * DELETE /account/sessions?series={series} : invalidate an existing session.
* *
* - You can only delete your own sessions, not any other user's session * - You can only delete your own sessions, not any other user's session
* - If you delete one of your existing sessions, and that you are currently logged in on that session, you will * - If you delete one of your existing sessions, and that you are currently logged in on that session, you will
@ -180,19 +206,29 @@ public class AccountResource {
* anymore. * anymore.
* There is an API to invalidate the current session, but there is no API to check which session uses which * There is an API to invalidate the current session, but there is no API to check which session uses which
* cookie. * cookie.
*
* @param series the series of an existing session
* @throws UnsupportedEncodingException if the series couldnt be URL decoded
*/ */
@RequestMapping(value = "/account/sessions/{series}", @RequestMapping(value = "/account/sessions/{series}",
method = RequestMethod.DELETE) method = RequestMethod.DELETE)
@Timed @Timed
public void invalidateSession(@PathVariable String series) throws UnsupportedEncodingException { public void invalidateSession(@PathVariable String series) throws UnsupportedEncodingException {
String decodedSeries = URLDecoder.decode(series, "UTF-8"); String decodedSeries = URLDecoder.decode(series, "UTF-8");
userRepository.findOneByLogin(SecurityUtils.getCurrentUser().getUsername()).ifPresent(u -> { userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).ifPresent(u -> {
persistentTokenRepository.findByUser(u).stream() persistentTokenRepository.findByUser(u).stream()
.filter(persistentToken -> StringUtils.equals(persistentToken.getSeries(), decodedSeries)) .filter(persistentToken -> StringUtils.equals(persistentToken.getSeries(), decodedSeries))
.findAny().ifPresent(t -> persistentTokenRepository.delete(decodedSeries)); .findAny().ifPresent(t -> persistentTokenRepository.delete(decodedSeries));
}); });
} }
/**
* POST /account/reset_password/init : Send an e-mail to reset the password of the user
*
* @param mail the mail of the user
* @param request the HTTP request
* @return the ResponseEntity with status 200 (OK) if the e-mail was sent, or status 400 (Bad Request) if the e-mail address is not registred
*/
@RequestMapping(value = "/account/reset_password/init", @RequestMapping(value = "/account/reset_password/init",
method = RequestMethod.POST, method = RequestMethod.POST,
produces = MediaType.TEXT_PLAIN_VALUE) produces = MediaType.TEXT_PLAIN_VALUE)
@ -211,16 +247,24 @@ public class AccountResource {
}).orElse(new ResponseEntity<>("e-mail address not registered", HttpStatus.BAD_REQUEST)); }).orElse(new ResponseEntity<>("e-mail address not registered", HttpStatus.BAD_REQUEST));
} }
/**
* POST /account/reset_password/finish : Finish to reset the password of the user
*
* @param keyAndPassword the generated key and the new password
* @return the ResponseEntity with status 200 (OK) if the password has been reset,
* or status 400 (Bad Request) or 500 (Internal Server Error) if the password could not be reset
*/
@RequestMapping(value = "/account/reset_password/finish", @RequestMapping(value = "/account/reset_password/finish",
method = RequestMethod.POST, method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.TEXT_PLAIN_VALUE)
@Timed @Timed
public ResponseEntity<String> finishPasswordReset(@RequestBody KeyAndPasswordDTO keyAndPassword) { public ResponseEntity<String> finishPasswordReset(@RequestBody KeyAndPasswordDTO keyAndPassword) {
if (!checkPasswordLength(keyAndPassword.getNewPassword())) { if (!checkPasswordLength(keyAndPassword.getNewPassword())) {
return new ResponseEntity<>("Incorrect password", HttpStatus.BAD_REQUEST); return new ResponseEntity<>("Incorrect password", HttpStatus.BAD_REQUEST);
} }
return userService.completePasswordReset(keyAndPassword.getNewPassword(), keyAndPassword.getKey()) return userService.completePasswordReset(keyAndPassword.getNewPassword(), keyAndPassword.getKey())
.map(user -> new ResponseEntity<String>(HttpStatus.OK)).orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); .map(user -> new ResponseEntity<String>(HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
} }
private boolean checkPasswordLength(String password) { private boolean checkPasswordLength(String password) {

View File

@ -3,13 +3,18 @@ package com.mycompany.myapp.web.rest;
import com.mycompany.myapp.service.AuditEventService; import com.mycompany.myapp.service.AuditEventService;
import java.time.LocalDate; import java.time.LocalDate;
import com.mycompany.myapp.web.rest.util.PaginationUtil;
import org.springframework.boot.actuate.audit.AuditEvent; import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import java.net.URISyntaxException;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.List; import java.util.List;
@ -27,20 +32,48 @@ public class AuditResource {
this.auditEventService = auditEventService; this.auditEventService = auditEventService;
} }
/**
* GET /audits : get a page of AuditEvents.
*
* @param pageable the pagination information
* @return the ResponseEntity with status 200 (OK) and the list of AuditEvents in body
* @throws URISyntaxException if there is an error to generate the pagination HTTP headers
*/
@RequestMapping(method = RequestMethod.GET) @RequestMapping(method = RequestMethod.GET)
public List<AuditEvent> getAll() { public ResponseEntity<List<AuditEvent>> getAll(Pageable pageable) throws URISyntaxException {
return auditEventService.findAll(); Page<AuditEvent> page = auditEventService.findAll(pageable);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/audits");
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
} }
/**
* GET /audits : get a page of AuditEvents between the fromDate and toDate.
*
* @param fromDate the start of the time period of AuditEvents to get
* @param toDate the end of the time period of AuditEvents to get
* @param pageable the pagination information
* @return the ResponseEntity with status 200 (OK) and the list of AuditEvents in body
* @throws URISyntaxException if there is an error to generate the pagination HTTP headers
*/
@RequestMapping(method = RequestMethod.GET, @RequestMapping(method = RequestMethod.GET,
params = {"fromDate", "toDate"}) params = {"fromDate", "toDate"})
public List<AuditEvent> getByDates( public ResponseEntity<List<AuditEvent>> getByDates(
@RequestParam(value = "fromDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate fromDate, @RequestParam(value = "fromDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate fromDate,
@RequestParam(value = "toDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate toDate) { @RequestParam(value = "toDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate toDate,
Pageable pageable) throws URISyntaxException {
return auditEventService.findByDates(fromDate.atTime(0, 0), toDate.atTime(23, 59)); Page<AuditEvent> page = auditEventService.findByDates(fromDate.atTime(0, 0), toDate.atTime(23, 59), pageable);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/audits");
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
} }
/**
* GET /audits/:id : get an AuditEvent by id.
*
* @param id the id of the entity to get
* @return the ResponseEntity with status 200 (OK) and the AuditEvent in body, or status 404 (Not Found)
*/
@RequestMapping(value = "/{id:.+}", @RequestMapping(value = "/{id:.+}",
method = RequestMethod.GET) method = RequestMethod.GET)
public ResponseEntity<AuditEvent> get(@PathVariable Long id) { public ResponseEntity<AuditEvent> get(@PathVariable Long id) {

View File

@ -32,9 +32,13 @@ public class BankAccountResource {
private BankAccountRepository bankAccountRepository; private BankAccountRepository bankAccountRepository;
/** /**
* POST /bankAccounts -> Create a new bankAccount. * POST /bank-accounts : Create a new bankAccount.
*
* @param bankAccount the bankAccount to create
* @return the ResponseEntity with status 201 (Created) and with body the new bankAccount, or with status 400 (Bad Request) if the bankAccount has already an ID
* @throws URISyntaxException if the Location URI syntax is incorrect
*/ */
@RequestMapping(value = "/bankAccounts", @RequestMapping(value = "/bank-accounts",
method = RequestMethod.POST, method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.APPLICATION_JSON_VALUE)
@Timed @Timed
@ -44,15 +48,21 @@ public class BankAccountResource {
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("bankAccount", "idexists", "A new bankAccount cannot already have an ID")).body(null); return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("bankAccount", "idexists", "A new bankAccount cannot already have an ID")).body(null);
} }
BankAccount result = bankAccountRepository.save(bankAccount); BankAccount result = bankAccountRepository.save(bankAccount);
return ResponseEntity.created(new URI("/api/bankAccounts/" + result.getId())) return ResponseEntity.created(new URI("/api/bank-accounts/" + result.getId()))
.headers(HeaderUtil.createEntityCreationAlert("bankAccount", result.getId().toString())) .headers(HeaderUtil.createEntityCreationAlert("bankAccount", result.getId().toString()))
.body(result); .body(result);
} }
/** /**
* PUT /bankAccounts -> Updates an existing bankAccount. * PUT /bank-accounts : Updates an existing bankAccount.
*
* @param bankAccount the bankAccount to update
* @return the ResponseEntity with status 200 (OK) and with body the updated bankAccount,
* or with status 400 (Bad Request) if the bankAccount is not valid,
* or with status 500 (Internal Server Error) if the bankAccount couldnt be updated
* @throws URISyntaxException if the Location URI syntax is incorrect
*/ */
@RequestMapping(value = "/bankAccounts", @RequestMapping(value = "/bank-accounts",
method = RequestMethod.PUT, method = RequestMethod.PUT,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.APPLICATION_JSON_VALUE)
@Timed @Timed
@ -68,21 +78,27 @@ public class BankAccountResource {
} }
/** /**
* GET /bankAccounts -> get all the bankAccounts. * GET /bank-accounts : get all the bankAccounts.
*
* @return the ResponseEntity with status 200 (OK) and the list of bankAccounts in body
*/ */
@RequestMapping(value = "/bankAccounts", @RequestMapping(value = "/bank-accounts",
method = RequestMethod.GET, method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.APPLICATION_JSON_VALUE)
@Timed @Timed
public List<BankAccount> getAllBankAccounts() { public List<BankAccount> getAllBankAccounts() {
log.debug("REST request to get all BankAccounts"); log.debug("REST request to get all BankAccounts");
return bankAccountRepository.findAll(); List<BankAccount> bankAccounts = bankAccountRepository.findAll();
} return bankAccounts;
}
/** /**
* GET /bankAccounts/:id -> get the "id" bankAccount. * GET /bank-accounts/:id : get the "id" bankAccount.
*
* @param id the id of the bankAccount to retrieve
* @return the ResponseEntity with status 200 (OK) and with body the bankAccount, or with status 404 (Not Found)
*/ */
@RequestMapping(value = "/bankAccounts/{id}", @RequestMapping(value = "/bank-accounts/{id}",
method = RequestMethod.GET, method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.APPLICATION_JSON_VALUE)
@Timed @Timed
@ -97,9 +113,12 @@ public class BankAccountResource {
} }
/** /**
* DELETE /bankAccounts/:id -> delete the "id" bankAccount. * DELETE /bank-accounts/:id : delete the "id" bankAccount.
*
* @param id the id of the bankAccount to delete
* @return the ResponseEntity with status 200 (OK)
*/ */
@RequestMapping(value = "/bankAccounts/{id}", @RequestMapping(value = "/bank-accounts/{id}",
method = RequestMethod.DELETE, method = RequestMethod.DELETE,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.APPLICATION_JSON_VALUE)
@Timed @Timed
@ -108,4 +127,5 @@ public class BankAccountResource {
bankAccountRepository.delete(id); bankAccountRepository.delete(id);
return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert("bankAccount", id.toString())).build(); return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert("bankAccount", id.toString())).build();
} }
} }

View File

@ -32,7 +32,11 @@ public class LabelResource {
private LabelRepository labelRepository; private LabelRepository labelRepository;
/** /**
* POST /labels -> Create a new label. * POST /labels : Create a new label.
*
* @param label the label to create
* @return the ResponseEntity with status 201 (Created) and with body the new label, or with status 400 (Bad Request) if the label has already an ID
* @throws URISyntaxException if the Location URI syntax is incorrect
*/ */
@RequestMapping(value = "/labels", @RequestMapping(value = "/labels",
method = RequestMethod.POST, method = RequestMethod.POST,
@ -50,7 +54,13 @@ public class LabelResource {
} }
/** /**
* PUT /labels -> Updates an existing label. * PUT /labels : Updates an existing label.
*
* @param label the label to update
* @return the ResponseEntity with status 200 (OK) and with body the updated label,
* or with status 400 (Bad Request) if the label is not valid,
* or with status 500 (Internal Server Error) if the label couldnt be updated
* @throws URISyntaxException if the Location URI syntax is incorrect
*/ */
@RequestMapping(value = "/labels", @RequestMapping(value = "/labels",
method = RequestMethod.PUT, method = RequestMethod.PUT,
@ -68,7 +78,9 @@ public class LabelResource {
} }
/** /**
* GET /labels -> get all the labels. * GET /labels : get all the labels.
*
* @return the ResponseEntity with status 200 (OK) and the list of labels in body
*/ */
@RequestMapping(value = "/labels", @RequestMapping(value = "/labels",
method = RequestMethod.GET, method = RequestMethod.GET,
@ -76,11 +88,15 @@ public class LabelResource {
@Timed @Timed
public List<Label> getAllLabels() { public List<Label> getAllLabels() {
log.debug("REST request to get all Labels"); log.debug("REST request to get all Labels");
return labelRepository.findAll(); List<Label> labels = labelRepository.findAll();
} return labels;
}
/** /**
* GET /labels/:id -> get the "id" label. * GET /labels/:id : get the "id" label.
*
* @param id the id of the label to retrieve
* @return the ResponseEntity with status 200 (OK) and with body the label, or with status 404 (Not Found)
*/ */
@RequestMapping(value = "/labels/{id}", @RequestMapping(value = "/labels/{id}",
method = RequestMethod.GET, method = RequestMethod.GET,
@ -97,7 +113,10 @@ public class LabelResource {
} }
/** /**
* DELETE /labels/:id -> delete the "id" label. * DELETE /labels/:id : delete the "id" label.
*
* @param id the id of the label to delete
* @return the ResponseEntity with status 200 (OK)
*/ */
@RequestMapping(value = "/labels/{id}", @RequestMapping(value = "/labels/{id}",
method = RequestMethod.DELETE, method = RequestMethod.DELETE,
@ -108,4 +127,5 @@ public class LabelResource {
labelRepository.delete(id); labelRepository.delete(id);
return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert("label", id.toString())).build(); return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert("label", id.toString())).build();
} }
} }

View File

@ -35,7 +35,11 @@ public class OperationResource {
private OperationRepository operationRepository; private OperationRepository operationRepository;
/** /**
* POST /operations -> Create a new operation. * POST /operations : Create a new operation.
*
* @param operation the operation to create
* @return the ResponseEntity with status 201 (Created) and with body the new operation, or with status 400 (Bad Request) if the operation has already an ID
* @throws URISyntaxException if the Location URI syntax is incorrect
*/ */
@RequestMapping(value = "/operations", @RequestMapping(value = "/operations",
method = RequestMethod.POST, method = RequestMethod.POST,
@ -53,7 +57,13 @@ public class OperationResource {
} }
/** /**
* PUT /operations -> Updates an existing operation. * PUT /operations : Updates an existing operation.
*
* @param operation the operation to update
* @return the ResponseEntity with status 200 (OK) and with body the updated operation,
* or with status 400 (Bad Request) if the operation is not valid,
* or with status 500 (Internal Server Error) if the operation couldnt be updated
* @throws URISyntaxException if the Location URI syntax is incorrect
*/ */
@RequestMapping(value = "/operations", @RequestMapping(value = "/operations",
method = RequestMethod.PUT, method = RequestMethod.PUT,
@ -71,7 +81,11 @@ public class OperationResource {
} }
/** /**
* GET /operations -> get all the operations. * GET /operations : get all the operations.
*
* @param pageable the pagination information
* @return the ResponseEntity with status 200 (OK) and the list of operations in body
* @throws URISyntaxException if there is an error to generate the pagination HTTP headers
*/ */
@RequestMapping(value = "/operations", @RequestMapping(value = "/operations",
method = RequestMethod.GET, method = RequestMethod.GET,
@ -86,7 +100,10 @@ public class OperationResource {
} }
/** /**
* GET /operations/:id -> get the "id" operation. * GET /operations/:id : get the "id" operation.
*
* @param id the id of the operation to retrieve
* @return the ResponseEntity with status 200 (OK) and with body the operation, or with status 404 (Not Found)
*/ */
@RequestMapping(value = "/operations/{id}", @RequestMapping(value = "/operations/{id}",
method = RequestMethod.GET, method = RequestMethod.GET,
@ -103,7 +120,10 @@ public class OperationResource {
} }
/** /**
* DELETE /operations/:id -> delete the "id" operation. * DELETE /operations/:id : delete the "id" operation.
*
* @param id the id of the operation to delete
* @return the ResponseEntity with status 200 (OK)
*/ */
@RequestMapping(value = "/operations/{id}", @RequestMapping(value = "/operations/{id}",
method = RequestMethod.DELETE, method = RequestMethod.DELETE,
@ -114,4 +134,5 @@ public class OperationResource {
operationRepository.delete(id); operationRepository.delete(id);
return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert("operation", id.toString())).build(); return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert("operation", id.toString())).build();
} }
} }

View File

@ -52,7 +52,7 @@ import java.util.stream.Collectors;
* but then all authorities come from the cache, so in fact it's much better than doing an outer join * but then all authorities come from the cache, so in fact it's much better than doing an outer join
* (which will get lots of data from the database, for each HTTP call).</li> * (which will get lots of data from the database, for each HTTP call).</li>
* <li> As this manages users, for security reasons, we'd rather have a DTO layer.</li> * <li> As this manages users, for security reasons, we'd rather have a DTO layer.</li>
* </p> * </ul>
* <p>Another option would be to have a specific JPA entity graph to handle this case.</p> * <p>Another option would be to have a specific JPA entity graph to handle this case.</p>
*/ */
@RestController @RestController
@ -75,12 +75,17 @@ public class UserResource {
private UserService userService; private UserService userService;
/** /**
* POST /users -> Creates a new user. * POST /users : Creates a new user.
* <p> * <p>
* Creates a new user if the login and email are not already used, and sends an * Creates a new user if the login and email are not already used, and sends an
* mail with an activation link. * mail with an activation link.
* The user needs to be activated on creation. * The user needs to be activated on creation.
* </p> * </p>
*
* @param managedUserDTO the user to create
* @param request the HTTP request
* @return the ResponseEntity with status 201 (Created) and with body the new user, or with status 400 (Bad Request) if the login or email is already in use
* @throws URISyntaxException if the Location URI syntaxt is incorrect
*/ */
@RequestMapping(value = "/users", @RequestMapping(value = "/users",
method = RequestMethod.POST, method = RequestMethod.POST,
@ -91,11 +96,11 @@ public class UserResource {
log.debug("REST request to save User : {}", managedUserDTO); log.debug("REST request to save User : {}", managedUserDTO);
if (userRepository.findOneByLogin(managedUserDTO.getLogin()).isPresent()) { if (userRepository.findOneByLogin(managedUserDTO.getLogin()).isPresent()) {
return ResponseEntity.badRequest() return ResponseEntity.badRequest()
.headers(HeaderUtil.createFailureAlert("user-management", "userexists", "Login already in use")) .headers(HeaderUtil.createFailureAlert("userManagement", "userexists", "Login already in use"))
.body(null); .body(null);
} else if (userRepository.findOneByEmail(managedUserDTO.getEmail()).isPresent()) { } else if (userRepository.findOneByEmail(managedUserDTO.getEmail()).isPresent()) {
return ResponseEntity.badRequest() return ResponseEntity.badRequest()
.headers(HeaderUtil.createFailureAlert("user-management", "emailexists", "Email already in use")) .headers(HeaderUtil.createFailureAlert("userManagement", "emailexists", "Email already in use"))
.body(null); .body(null);
} else { } else {
User newUser = userService.createUser(managedUserDTO); User newUser = userService.createUser(managedUserDTO);
@ -107,13 +112,18 @@ public class UserResource {
request.getContextPath(); // "/myContextPath" or "" if deployed in root context request.getContextPath(); // "/myContextPath" or "" if deployed in root context
mailService.sendCreationEmail(newUser, baseUrl); mailService.sendCreationEmail(newUser, baseUrl);
return ResponseEntity.created(new URI("/api/users/" + newUser.getLogin())) return ResponseEntity.created(new URI("/api/users/" + newUser.getLogin()))
.headers(HeaderUtil.createAlert( "user-management.created", newUser.getLogin())) .headers(HeaderUtil.createAlert( "userManagement.created", newUser.getLogin()))
.body(newUser); .body(newUser);
} }
} }
/** /**
* PUT /users -> Updates an existing User. * PUT /users : Updates an existing User.
*
* @param managedUserDTO the user to update
* @return the ResponseEntity with status 200 (OK) and with body the updated user,
* or with status 400 (Bad Request) if the login or email is already in use,
* or with status 500 (Internal Server Error) if the user couldnt be updated
*/ */
@RequestMapping(value = "/users", @RequestMapping(value = "/users",
method = RequestMethod.PUT, method = RequestMethod.PUT,
@ -121,15 +131,15 @@ public class UserResource {
@Timed @Timed
@Transactional @Transactional
@Secured(AuthoritiesConstants.ADMIN) @Secured(AuthoritiesConstants.ADMIN)
public ResponseEntity<ManagedUserDTO> updateUser(@RequestBody ManagedUserDTO managedUserDTO) throws URISyntaxException { public ResponseEntity<ManagedUserDTO> updateUser(@RequestBody ManagedUserDTO managedUserDTO) {
log.debug("REST request to update User : {}", managedUserDTO); log.debug("REST request to update User : {}", managedUserDTO);
Optional<User> existingUser = userRepository.findOneByEmail(managedUserDTO.getEmail()); Optional<User> existingUser = userRepository.findOneByEmail(managedUserDTO.getEmail());
if (existingUser.isPresent() && (!existingUser.get().getId().equals(managedUserDTO.getId()))) { if (existingUser.isPresent() && (!existingUser.get().getId().equals(managedUserDTO.getId()))) {
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("user-management", "emailexists", "E-mail already in use")).body(null); return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("userManagement", "emailexists", "E-mail already in use")).body(null);
} }
existingUser = userRepository.findOneByLogin(managedUserDTO.getLogin()); existingUser = userRepository.findOneByLogin(managedUserDTO.getLogin());
if (existingUser.isPresent() && (!existingUser.get().getId().equals(managedUserDTO.getId()))) { if (existingUser.isPresent() && (!existingUser.get().getId().equals(managedUserDTO.getId()))) {
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("user-management", "userexists", "Login already in use")).body(null); return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("userManagement", "userexists", "Login already in use")).body(null);
} }
return userRepository return userRepository
.findOneById(managedUserDTO.getId()) .findOneById(managedUserDTO.getId())
@ -146,7 +156,7 @@ public class UserResource {
authority -> authorities.add(authorityRepository.findOne(authority)) authority -> authorities.add(authorityRepository.findOne(authority))
); );
return ResponseEntity.ok() return ResponseEntity.ok()
.headers(HeaderUtil.createAlert("user-management.updated", managedUserDTO.getLogin())) .headers(HeaderUtil.createAlert("userManagement.updated", managedUserDTO.getLogin()))
.body(new ManagedUserDTO(userRepository .body(new ManagedUserDTO(userRepository
.findOne(managedUserDTO.getId()))); .findOne(managedUserDTO.getId())));
}) })
@ -155,7 +165,11 @@ public class UserResource {
} }
/** /**
* GET /users -> get all users. * GET /users : get all users.
*
* @param pageable the pagination information
* @return the ResponseEntity with status 200 (OK) and with body all users
* @throws URISyntaxException if the pagination headers couldnt be generated
*/ */
@RequestMapping(value = "/users", @RequestMapping(value = "/users",
method = RequestMethod.GET, method = RequestMethod.GET,
@ -166,14 +180,17 @@ public class UserResource {
throws URISyntaxException { throws URISyntaxException {
Page<User> page = userRepository.findAll(pageable); Page<User> page = userRepository.findAll(pageable);
List<ManagedUserDTO> managedUserDTOs = page.getContent().stream() List<ManagedUserDTO> managedUserDTOs = page.getContent().stream()
.map(user -> new ManagedUserDTO(user)) .map(ManagedUserDTO::new)
.collect(Collectors.toList()); .collect(Collectors.toList());
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/users"); HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/users");
return new ResponseEntity<>(managedUserDTOs, headers, HttpStatus.OK); return new ResponseEntity<>(managedUserDTOs, headers, HttpStatus.OK);
} }
/** /**
* GET /users/:login -> get the "login" user. * GET /users/:login : get the "login" user.
*
* @param login the login of the user to find
* @return the ResponseEntity with status 200 (OK) and with body the "login" user, or with status 404 (Not Found)
*/ */
@RequestMapping(value = "/users/{login:[_'.@a-z0-9-]+}", @RequestMapping(value = "/users/{login:[_'.@a-z0-9-]+}",
method = RequestMethod.GET, method = RequestMethod.GET,
@ -187,9 +204,12 @@ public class UserResource {
.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND)); .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
} }
/** /**
* DELETE USER :login -> delete the "login" User. * DELETE USER :login : delete the "login" User.
*
* @param login the login of the user to delete
* @return the ResponseEntity with status 200 (OK)
*/ */
@RequestMapping(value = "/users/{login}", @RequestMapping(value = "/users/{login:[_'.@a-z0-9-]+}",
method = RequestMethod.DELETE, method = RequestMethod.DELETE,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.APPLICATION_JSON_VALUE)
@Timed @Timed
@ -197,6 +217,6 @@ public class UserResource {
public ResponseEntity<Void> deleteUser(@PathVariable String login) { public ResponseEntity<Void> deleteUser(@PathVariable String login) {
log.debug("REST request to delete User: {}", login); log.debug("REST request to delete User: {}", login);
userService.deleteUserInformation(login); userService.deleteUserInformation(login);
return ResponseEntity.ok().headers(HeaderUtil.createAlert( "user-management.deleted", login)).build(); return ResponseEntity.ok().headers(HeaderUtil.createAlert( "userManagement.deleted", login)).build();
} }
} }

View File

@ -13,7 +13,7 @@ import java.util.stream.Collectors;
*/ */
public class UserDTO { public class UserDTO {
public static final int PASSWORD_MIN_LENGTH = 5; public static final int PASSWORD_MIN_LENGTH = 4;
public static final int PASSWORD_MAX_LENGTH = 100; public static final int PASSWORD_MAX_LENGTH = 100;
@Pattern(regexp = "^[a-z0-9]*$") @Pattern(regexp = "^[a-z0-9]*$")

View File

@ -6,6 +6,7 @@ public final class ErrorConstants {
public static final String ERR_ACCESS_DENIED = "error.accessDenied"; public static final String ERR_ACCESS_DENIED = "error.accessDenied";
public static final String ERR_VALIDATION = "error.validation"; public static final String ERR_VALIDATION = "error.validation";
public static final String ERR_METHOD_NOT_SUPPORTED = "error.methodNotSupported"; public static final String ERR_METHOD_NOT_SUPPORTED = "error.methodNotSupported";
public static final String ERR_INTERNAL_SERVER_ERROR = "error.internalServerError";
private ErrorConstants() { private ErrorConstants() {
} }

View File

@ -16,16 +16,16 @@ public class ErrorDTO implements Serializable {
private List<FieldErrorDTO> fieldErrors; private List<FieldErrorDTO> fieldErrors;
ErrorDTO(String message) { public ErrorDTO(String message) {
this(message, null); this(message, null);
} }
ErrorDTO(String message, String description) { public ErrorDTO(String message, String description) {
this.message = message; this.message = message;
this.description = description; this.description = description;
} }
ErrorDTO(String message, String description, List<FieldErrorDTO> fieldErrors) { public ErrorDTO(String message, String description, List<FieldErrorDTO> fieldErrors) {
this.message = message; this.message = message;
this.description = description; this.description = description;
this.fieldErrors = fieldErrors; this.fieldErrors = fieldErrors;

View File

@ -2,8 +2,11 @@ package com.mycompany.myapp.web.rest.errors;
import java.util.List; import java.util.List;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.dao.ConcurrencyFailureException; import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.ResponseEntity.BodyBuilder;
import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError; import org.springframework.validation.FieldError;
@ -64,4 +67,19 @@ public class ExceptionTranslator {
public ErrorDTO processMethodNotSupportedException(HttpRequestMethodNotSupportedException exception) { public ErrorDTO processMethodNotSupportedException(HttpRequestMethodNotSupportedException exception) {
return new ErrorDTO(ErrorConstants.ERR_METHOD_NOT_SUPPORTED, exception.getMessage()); return new ErrorDTO(ErrorConstants.ERR_METHOD_NOT_SUPPORTED, exception.getMessage());
} }
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorDTO> processRuntimeException(Exception ex) throws Exception {
BodyBuilder builder;
ErrorDTO errorDTO;
ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
if (responseStatus != null) {
builder = ResponseEntity.status(responseStatus.value());
errorDTO = new ErrorDTO("error." + responseStatus.value().value(), responseStatus.reason());
} else {
builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
errorDTO = new ErrorDTO(ErrorConstants.ERR_INTERNAL_SERVER_ERROR, "Internal server error");
}
return builder.body(errorDTO);
}
} }

View File

@ -12,7 +12,7 @@ public class FieldErrorDTO implements Serializable {
private final String message; private final String message;
FieldErrorDTO(String dto, String field, String message) { public FieldErrorDTO(String dto, String field, String message) {
this.objectName = dto; this.objectName = dto;
this.field = field; this.field = field;
this.message = message; this.message = message;

View File

@ -0,0 +1,56 @@
package com.mycompany.myapp.web.rest.mapper;
import com.mycompany.myapp.domain.Authority;
import com.mycompany.myapp.domain.User;
import com.mycompany.myapp.web.rest.dto.UserDTO;
import org.mapstruct.*;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Mapper for the entity User and its DTO UserDTO.
*/
@Mapper(componentModel = "spring", uses = {})
public interface UserMapper {
UserDTO userToUserDTO(User user);
List<UserDTO> usersToUserDTOs(List<User> users);
@Mapping(target = "createdBy", ignore = true)
@Mapping(target = "createdDate", ignore = true)
@Mapping(target = "lastModifiedBy", ignore = true)
@Mapping(target = "lastModifiedDate", ignore = true)
@Mapping(target = "persistentTokens", ignore = true)
@Mapping(target = "id", ignore = true)
@Mapping(target = "activationKey", ignore = true)
@Mapping(target = "resetKey", ignore = true)
@Mapping(target = "resetDate", ignore = true)
User userDTOToUser(UserDTO userDTO);
List<User> userDTOsToUsers(List<UserDTO> userDTOs);
default User userFromId(Long id) {
if (id == null) {
return null;
}
User user = new User();
user.setId(id);
return user;
}
default Set<String> stringsFromAuthorities (Set < Authority > authorities) {
return authorities.stream().map(Authority::getName)
.collect(Collectors.toSet());
}
default Set<Authority> authoritiesFromStrings(Set<String> strings) {
return strings.stream().map(string -> {
Authority auth = new Authority();
auth.setName(string);
return auth;
}).collect(Collectors.toSet());
}
}

View File

@ -3,34 +3,34 @@ package com.mycompany.myapp.web.rest.util;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
/** /**
* Utility class for http header creation. * Utility class for HTTP headers creation.
* *
*/ */
public class HeaderUtil { public class HeaderUtil {
public static HttpHeaders createAlert(String message, String param) { public static HttpHeaders createAlert(String message, String param) {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.add("X-sampleapplicationApp-alert", message); headers.add("X-sampleApplicationApp-alert", message);
headers.add("X-sampleapplicationApp-params", param); headers.add("X-sampleApplicationApp-params", param);
return headers; return headers;
} }
public static HttpHeaders createEntityCreationAlert(String entityName, String param) { public static HttpHeaders createEntityCreationAlert(String entityName, String param) {
return createAlert("sampleapplicationApp." + entityName + ".created", param); return createAlert("sampleApplicationApp." + entityName + ".created", param);
} }
public static HttpHeaders createEntityUpdateAlert(String entityName, String param) { public static HttpHeaders createEntityUpdateAlert(String entityName, String param) {
return createAlert("sampleapplicationApp." + entityName + ".updated", param); return createAlert("sampleApplicationApp." + entityName + ".updated", param);
} }
public static HttpHeaders createEntityDeletionAlert(String entityName, String param) { public static HttpHeaders createEntityDeletionAlert(String entityName, String param) {
return createAlert("sampleapplicationApp." + entityName + ".deleted", param); return createAlert("sampleApplicationApp." + entityName + ".deleted", param);
} }
public static HttpHeaders createFailureAlert(String entityName, String errorKey, String defaultMessage) { public static HttpHeaders createFailureAlert(String entityName, String errorKey, String defaultMessage) {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.add("X-sampleapplicationApp-error", "error." + errorKey); headers.add("X-sampleApplicationApp-error", "error." + errorKey);
headers.add("X-sampleapplicationApp-params", entityName); headers.add("X-sampleApplicationApp-params", entityName);
return headers; return headers;
} }
} }

View File

@ -10,9 +10,8 @@ import java.net.URISyntaxException;
* Utility class for handling pagination. * Utility class for handling pagination.
* *
* <p> * <p>
* Pagination uses the same principles as the <a href="https://developer.github.com/v3/#pagination">Github API</api>, * Pagination uses the same principles as the <a href="https://developer.github.com/v3/#pagination">Github API</a>,
* and follow <a href="http://tools.ietf.org/html/rfc5988">RFC 5988 (Link header)</a>. * and follow <a href="http://tools.ietf.org/html/rfc5988">RFC 5988 (Link header)</a>.
* </p>
*/ */
public class PaginationUtil { public class PaginationUtil {
@ -23,19 +22,19 @@ public class PaginationUtil {
headers.add("X-Total-Count", "" + page.getTotalElements()); headers.add("X-Total-Count", "" + page.getTotalElements());
String link = ""; String link = "";
if ((page.getNumber() + 1) < page.getTotalPages()) { if ((page.getNumber() + 1) < page.getTotalPages()) {
link = "<" + (new URI(baseUrl +"?page=" + (page.getNumber() + 1) + "&size=" + page.getSize())).toString() + ">; rel=\"next\","; link = "<" + (new URI(baseUrl + "?page=" + (page.getNumber() + 1) + "&size=" + page.getSize())).toString() + ">; rel=\"next\",";
} }
// prev link // prev link
if ((page.getNumber()) > 0) { if ((page.getNumber()) > 0) {
link += "<" + (new URI(baseUrl +"?page=" + (page.getNumber() - 1) + "&size=" + page.getSize())).toString() + ">; rel=\"prev\","; link += "<" + (new URI(baseUrl + "?page=" + (page.getNumber() - 1) + "&size=" + page.getSize())).toString() + ">; rel=\"prev\",";
} }
// last and first link // last and first link
int lastPage = 0; int lastPage = 0;
if (page.getTotalPages() > 0) { if (page.getTotalPages() > 0) {
lastPage = page.getTotalPages() - 1; lastPage = page.getTotalPages() - 1;
} }
link += "<" + (new URI(baseUrl +"?page=" + lastPage + "&size=" + page.getSize())).toString() + ">; rel=\"last\","; link += "<" + (new URI(baseUrl + "?page=" + lastPage + "&size=" + page.getSize())).toString() + ">; rel=\"last\",";
link += "<" + (new URI(baseUrl +"?page=" + 0 + "&size=" + page.getSize())).toString() + ">; rel=\"first\""; link += "<" + (new URI(baseUrl + "?page=" + 0 + "&size=" + page.getSize())).toString() + ">; rel=\"first\"";
headers.add(HttpHeaders.LINK, link); headers.add(HttpHeaders.LINK, link);
return headers; return headers;
} }

View File

@ -10,16 +10,14 @@
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html # http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
# =================================================================== # ===================================================================
spring: spring:
profiles:
active: dev
devtools: devtools:
restart: restart:
enabled: true enabled: true
livereload: livereload:
enabled: false # we use Grunt + BrowserSync for livereload enabled: false # we use gulp + BrowserSync for livereload
datasource: datasource:
driver-class-name: org.h2.jdbcx.JdbcDataSource
url: jdbc:h2:mem:sampleapplication;DB_CLOSE_DELAY=-1 url: jdbc:h2:mem:sampleapplication;DB_CLOSE_DELAY=-1
name: name:
username: sampleApplication username: sampleApplication
@ -71,3 +69,12 @@ jhipster:
host: localhost host: localhost
port: 2003 port: 2003
prefix: sampleApplication prefix: sampleApplication
logs: # report metrics in the logs
enabled: false
reportFrequency: 60 # in seconds
logging:
logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
enabled: false
host: localhost
port: 5000
queueSize: 512

View File

@ -10,20 +10,24 @@
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html # http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
# =================================================================== # ===================================================================
spring: spring:
profiles:
active: prod
devtools: devtools:
restart: restart:
enabled: false enabled: false
livereload: livereload:
enabled: false enabled: false
datasource: datasource:
driver-class-name: com.mysql.jdbc.jdbc2.optional.MysqlDataSource url: jdbc:mysql://localhost:3306/sampleApplication?useUnicode=true&characterEncoding=utf8&useSSL=false
url: jdbc:mysql://localhost:3306/sampleApplication?useUnicode=true&characterEncoding=utf8
name: name:
username: root username: root
password: password:
hikari:
data-source-properties:
cachePrepStmts: true
prepStmtCacheSize: 250
prepStmtCacheSqlLimit: 2048
useServerPrepStmts: true
jpa: jpa:
database-platform: org.hibernate.dialect.MySQLInnoDBDialect database-platform: org.hibernate.dialect.MySQLInnoDBDialect
database: MYSQL database: MYSQL
@ -57,7 +61,7 @@ server:
jhipster: jhipster:
http: http:
cache: # Used by the CachingHttpHeadersFilter cache: # Used by the CachingHttpHeadersFilter
timeToLiveInDays: 31 timeToLiveInDays: 1461
cache: # Hibernate 2nd level cache, used by CacheConfiguration cache: # Hibernate 2nd level cache, used by CacheConfiguration
timeToLiveSeconds: 3600 timeToLiveSeconds: 3600
ehcache: ehcache:
@ -75,3 +79,12 @@ jhipster:
host: localhost host: localhost
port: 2003 port: 2003
prefix: sampleApplication prefix: sampleApplication
logs: # report metrics in the logs
enabled: false
reportFrequency: 60 # in seconds
logging:
logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
enabled: false
host: localhost
port: 5000
queueSize: 512

View File

@ -11,14 +11,17 @@
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html # http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
# =================================================================== # ===================================================================
spring: spring:
application:
name: sampleApplication
jpa: jpa:
open-in-view: false open-in-view: false
hibernate: hibernate:
ddl-auto: none ddl-auto: none
naming-strategy: org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy naming-strategy: org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy
messages: messages:
basename: classpath:/i18n/messages basename: i18n/messages
mvc: mvc:
favicon: favicon:
enabled: false enabled: false
@ -48,7 +51,7 @@ jhipster:
mail: mail:
from: sampleApplication@localhost from: sampleApplication@localhost
security: security:
rememberme: rememberMe:
# security key (this key should be unique for your application, and kept secret) # security key (this key should be unique for your application, and kept secret)
key: 5c37379956bd1242f5636c8cb322c2966ad81277 key: 5c37379956bd1242f5636c8cb322c2966ad81277
swagger: swagger:
@ -56,6 +59,8 @@ jhipster:
description: sampleApplication API documentation description: sampleApplication API documentation
version: 0.0.1 version: 0.0.1
termsOfServiceUrl: termsOfServiceUrl:
contact: contactName:
contactUrl:
contactEmail:
license: license:
licenseUrl: licenseUrl:

View File

@ -19,6 +19,7 @@
The initial schema has the '00000000000001' id, so that it is over-written if we re-generate it. The initial schema has the '00000000000001' id, so that it is over-written if we re-generate it.
--> -->
<changeSet id="00000000000001" author="jhipster"> <changeSet id="00000000000001" author="jhipster">
<createTable tableName="jhi_user"> <createTable tableName="jhi_user">
<column name="id" type="bigint" autoIncrement="${autoIncrement}"> <column name="id" type="bigint" autoIncrement="${autoIncrement}">
<constraints primaryKey="true" nullable="false"/> <constraints primaryKey="true" nullable="false"/>
@ -30,7 +31,7 @@
<column name="first_name" type="varchar(50)"/> <column name="first_name" type="varchar(50)"/>
<column name="last_name" type="varchar(50)"/> <column name="last_name" type="varchar(50)"/>
<column name="email" type="varchar(100)"> <column name="email" type="varchar(100)">
<constraints unique="true"/> <constraints unique="true" nullable="false"/>
</column> </column>
<column name="activated" type="boolean" valueBoolean="false"> <column name="activated" type="boolean" valueBoolean="false">
<constraints nullable="false" /> <constraints nullable="false" />
@ -110,7 +111,6 @@
constraintName="fk_user_persistent_token" constraintName="fk_user_persistent_token"
referencedColumnNames="id" referencedColumnNames="id"
referencedTableName="jhi_user"/> referencedTableName="jhi_user"/>
<loadData encoding="UTF-8" <loadData encoding="UTF-8"
file="config/liquibase/users.csv" file="config/liquibase/users.csv"
separator=";" separator=";"
@ -129,7 +129,7 @@
file="config/liquibase/users_authorities.csv" file="config/liquibase/users_authorities.csv"
separator=";" separator=";"
tableName="jhi_user_authority"/> tableName="jhi_user_authority"/>
<createTable tableName="jhi_persistent_audit_event"> <createTable tableName="jhi_persistent_audit_event">
<column name="event_id" type="bigint" autoIncrement="${autoIncrement}"> <column name="event_id" type="bigint" autoIncrement="${autoIncrement}">
<constraints primaryKey="true" nullable="false"/> <constraints primaryKey="true" nullable="false"/>

View File

@ -25,7 +25,9 @@
<constraints nullable="false" /> <constraints nullable="false" />
</column> </column>
<column name="description" type="varchar(255)"/> <column name="description" type="varchar(255)">
<constraints nullable="true" />
</column>
<column name="amount" type="decimal(10,2)"> <column name="amount" type="decimal(10,2)">
<constraints nullable="false" /> <constraints nullable="false" />
@ -35,14 +37,12 @@
<!-- jhipster-needle-liquibase-add-column - Jhipster will add columns here, do not remove--> <!-- jhipster-needle-liquibase-add-column - Jhipster will add columns here, do not remove-->
</createTable> </createTable>
<dropDefaultValue tableName="operation" columnName="date" columnDataType="datetime"/> <dropDefaultValue tableName="operation" columnName="date" columnDataType="datetime"/>
<addForeignKeyConstraint baseColumnNames="bank_account_id" <addForeignKeyConstraint baseColumnNames="bank_account_id"
baseTableName="operation" baseTableName="operation"
constraintName="fk_operation_bankaccount_id" constraintName="fk_operation_bankaccount_id"
referencedColumnNames="id" referencedColumnNames="id"
referencedTableName="bank_account"/> referencedTableName="bank_account"/>
<createTable tableName="operation_label"> <createTable tableName="operation_label">
<column name="labels_id" type="bigint"> <column name="labels_id" type="bigint">
<constraints nullable="false"/> <constraints nullable="false"/>

View File

@ -0,0 +1,22 @@
# Error page
error.title=Your request cannot be processed
error.subtitle=Sorry, an error has occurred.
error.status=Status:
error.message=Message:
# Activation e-mail
email.activation.title=sampleApplication account activation
email.activation.greeting=Dear {0}
email.activation.text1=Your sampleApplication account has been created, please click on the URL below to activate it:
email.activation.text2=Regards,
email.signature=sampleApplication Team.
# Creation email
email.creation.text1=Your sampleApplication account has been created, please click on the URL below to access it:
# Reset e-mail
email.reset.title=sampleApplication password reset
email.reset.greeting=Dear {0}
email.reset.text1=For your sampleApplication account a password reset was requested, please click on the URL below to reset it:
email.reset.text2=Regards,

View File

@ -11,6 +11,9 @@ email.activation.text1=Your sampleApplication account has been created, please c
email.activation.text2=Regards, email.activation.text2=Regards,
email.signature=sampleApplication Team. email.signature=sampleApplication Team.
# Creation email
email.creation.text1=Your sampleApplication account has been created, please click on the URL below to access it:
# Reset e-mail # Reset e-mail
email.reset.title=sampleApplication password reset email.reset.title=sampleApplication password reset
email.reset.greeting=Dear {0} email.reset.greeting=Dear {0}

View File

@ -1,19 +0,0 @@
# Error page
error.title=Votre demande ne peut être traitée
error.subtitle=Désolé, une erreur s'est produite.
error.status=Status:
error.message=Message:
# Activation e-mail
email.activation.title=Activation de votre compte sampleApplication
email.activation.greeting=Cher {0}
email.activation.text1=Votre compte sampleApplication a été créé, pour l'activer merci de cliquer sur le lien ci-dessous:
email.activation.text2=Cordialement,
email.signature=sampleApplication.
# Reset e-mail
email.reset.title=sampleApplication Réinitialisation de mot de passe
email.reset.greeting=Cher {0}
email.reset.text1=Un nouveau mot de passe pour votre compte sampleApplication a été demandé, veuillez cliquer sur le lien ci-dessous pour le réinitialiser:
email.reset.text2=Cordialement,

View File

@ -30,9 +30,8 @@
<logger name="ch.qos.logback" level="WARN"/> <logger name="ch.qos.logback" level="WARN"/>
<logger name="com.codahale.metrics" level="WARN"/> <logger name="com.codahale.metrics" level="WARN"/>
<logger name="com.ryantenney" level="WARN"/> <logger name="com.ryantenney" level="WARN"/>
<logger name="com.sun.xml.internal.bind" level="WARN"/> <logger name="com.sun" level="WARN"/>
<logger name="com.zaxxer" level="WARN"/> <logger name="com.zaxxer" level="WARN"/>
<logger name="io.undertow" level="WARN"/>
<logger name="net.sf.ehcache" level="WARN"/> <logger name="net.sf.ehcache" level="WARN"/>
<logger name="org.apache" level="WARN"/> <logger name="org.apache" level="WARN"/>
<logger name="org.apache.catalina.startup.DigesterFactory" level="OFF"/> <logger name="org.apache.catalina.startup.DigesterFactory" level="OFF"/>

View File

@ -8,8 +8,8 @@
<p th:text="#{email.activation.greeting(${user.login})}"> <p th:text="#{email.activation.greeting(${user.login})}">
Dear Dear
</p> </p>
<p th:text="#{email.activation.text1}"> <p th:text="#{email.creation.text1}">
Your JHipster account has been created, please click on the URL below to activate it: Your JHipster account has been created, please click on the URL below to access it:
</p> </p>
<p> <p>
<a th:href="@{|${baseUrl}/#/reset/finish?key=${user.resetKey}|}" <a th:href="@{|${baseUrl}/#/reset/finish?key=${user.resetKey}|}"

View File

@ -1,543 +0,0 @@
# Apache Configuration File
# (!) Using `.htaccess` files slows down Apache, therefore, if you have access
# to the main server config file (usually called `httpd.conf`), you should add
# this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html.
# ##############################################################################
# # CROSS-ORIGIN RESOURCE SHARING (CORS) #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Cross-domain AJAX requests |
# ------------------------------------------------------------------------------
# Enable cross-origin AJAX requests.
# http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
# http://enable-cors.org/
# <IfModule mod_headers.c>
# Header set Access-Control-Allow-Origin "*"
# </IfModule>
# ------------------------------------------------------------------------------
# | CORS-enabled images |
# ------------------------------------------------------------------------------
# Send the CORS header for images when browsers request it.
# https://developer.mozilla.org/en/CORS_Enabled_Image
# http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
# http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
<FilesMatch "\.(gif|ico|jpe?g|png|svg|svgz|webp)$">
SetEnvIf Origin ":" IS_CORS
Header set Access-Control-Allow-Origin "*" env=IS_CORS
</FilesMatch>
</IfModule>
</IfModule>
# ------------------------------------------------------------------------------
# | Web fonts access |
# ------------------------------------------------------------------------------
# Allow access from all domains for web fonts
<IfModule mod_headers.c>
<FilesMatch "\.(eot|font.css|otf|ttc|ttf|woff)$">
Header set Access-Control-Allow-Origin "*"
</FilesMatch>
</IfModule>
# ##############################################################################
# # ERRORS #
# ##############################################################################
# ------------------------------------------------------------------------------
# | 404 error prevention for non-existing redirected folders |
# ------------------------------------------------------------------------------
# Prevent Apache from returning a 404 error for a rewrite if a directory
# with the same name does not exist.
# http://httpd.apache.org/docs/current/content-negotiation.html#multiviews
# http://www.webmasterworld.com/apache/3808792.htm
Options -MultiViews
# ------------------------------------------------------------------------------
# | Custom error messages / pages |
# ------------------------------------------------------------------------------
# You can customize what Apache returns to the client in case of an error (see
# http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.:
ErrorDocument 404 /404.html
# ##############################################################################
# # INTERNET EXPLORER #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Better website experience |
# ------------------------------------------------------------------------------
# Force IE to render pages in the highest available mode in the various
# cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf.
<IfModule mod_headers.c>
Header set X-UA-Compatible "IE=edge"
# `mod_headers` can't match based on the content-type, however, we only
# want to send this header for HTML pages and not for the other resources
<FilesMatch "\.(appcache|crx|css|eot|gif|htc|ico|jpe?g|js|m4a|m4v|manifest|mp4|oex|oga|ogg|ogv|otf|pdf|png|safariextz|svg|svgz|ttf|vcf|webapp|webm|webp|woff|xml|xpi)$">
Header unset X-UA-Compatible
</FilesMatch>
</IfModule>
# ------------------------------------------------------------------------------
# | Cookie setting from iframes |
# ------------------------------------------------------------------------------
# Allow cookies to be set from iframes in IE.
# <IfModule mod_headers.c>
# Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
# </IfModule>
# ------------------------------------------------------------------------------
# | Screen flicker |
# ------------------------------------------------------------------------------
# Stop screen flicker in IE on CSS rollovers (this only works in
# combination with the `ExpiresByType` directives for images from below).
# BrowserMatch "MSIE" brokenvary=1
# BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
# BrowserMatch "Opera" !brokenvary
# SetEnvIf brokenvary 1 force-no-vary
# ##############################################################################
# # MIME TYPES AND ENCODING #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Proper MIME types for all files |
# ------------------------------------------------------------------------------
<IfModule mod_mime.c>
# Audio
AddType audio/mp4 m4a f4a f4b
AddType audio/ogg oga ogg
# JavaScript
# Normalize to standard type (it's sniffed in IE anyways):
# http://tools.ietf.org/html/rfc4329#section-7.2
AddType application/javascript js jsonp
AddType application/json json
# Video
AddType video/mp4 mp4 m4v f4v f4p
AddType video/ogg ogv
AddType video/webm webm
AddType video/x-flv flv
# Web fonts
AddType application/font-woff woff
AddType application/vnd.ms-fontobject eot
# Browsers usually ignore the font MIME types and sniff the content,
# however, Chrome shows a warning if other MIME types are used for the
# following fonts.
AddType application/x-font-ttf ttc ttf
AddType font/opentype otf
# Make SVGZ fonts work on iPad:
# https://twitter.com/FontSquirrel/status/14855840545
AddType image/svg+xml svg svgz
AddEncoding gzip svgz
# Other
AddType application/octet-stream safariextz
AddType application/x-chrome-extension crx
AddType application/x-opera-extension oex
AddType application/x-shockwave-flash swf
AddType application/x-web-app-manifest+json webapp
AddType application/x-xpinstall xpi
AddType application/xml atom rdf rss xml
AddType image/webp webp
AddType image/x-icon ico
AddType text/cache-manifest appcache manifest
AddType text/vtt vtt
AddType text/x-component htc
AddType text/x-vcard vcf
</IfModule>
# ------------------------------------------------------------------------------
# | UTF-8 encoding |
# ------------------------------------------------------------------------------
# Use UTF-8 encoding for anything served as `text/html` or `text/plain`.
AddDefaultCharset utf-8
# Force UTF-8 for certain file formats.
<IfModule mod_mime.c>
AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml
</IfModule>
# ##############################################################################
# # URL REWRITES #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Rewrite engine |
# ------------------------------------------------------------------------------
# Turning on the rewrite engine and enabling the `FollowSymLinks` option is
# necessary for the following directives to work.
# If your web host doesn't allow the `FollowSymlinks` option, you may need to
# comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the
# performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks
# Also, some cloud hosting services require `RewriteBase` to be set:
# http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site
<IfModule mod_rewrite.c>
Options +FollowSymlinks
# Options +SymLinksIfOwnerMatch
RewriteEngine On
# RewriteBase /
</IfModule>
# ------------------------------------------------------------------------------
# | Suppressing / Forcing the "www." at the beginning of URLs |
# ------------------------------------------------------------------------------
# The same content should never be available under two different URLs especially
# not with and without "www." at the beginning. This can cause SEO problems
# (duplicate content), therefore, you should choose one of the alternatives and
# redirect the other one.
# By default option 1 (no "www.") is activated:
# http://no-www.org/faq.php?q=class_b
# If you'd prefer to use option 2, just comment out all the lines from option 1
# and uncomment the ones from option 2.
# IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Option 1: rewrite www.example.com → example.com
<IfModule mod_rewrite.c>
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
</IfModule>
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Option 2: rewrite example.com → www.example.com
# Be aware that the following might not be a good idea if you use "real"
# subdomains for certain parts of your website.
# <IfModule mod_rewrite.c>
# RewriteCond %{HTTPS} !=on
# RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
# RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# </IfModule>
# ##############################################################################
# # SECURITY #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Content Security Policy (CSP) |
# ------------------------------------------------------------------------------
# You can mitigate the risk of cross-site scripting and other content-injection
# attacks by setting a Content Security Policy which whitelists trusted sources
# of content for your site.
# The example header below allows ONLY scripts that are loaded from the current
# site's origin (no inline scripts, no CDN, etc). This almost certainly won't
# work as-is for your site!
# To get all the details you'll need to craft a reasonable policy for your site,
# read: http://html5rocks.com/en/tutorials/security/content-security-policy (or
# see the specification: http://w3.org/TR/CSP).
# <IfModule mod_headers.c>
# Header set Content-Security-Policy "script-src 'self'; object-src 'self'"
# <FilesMatch "\.(appcache|crx|css|eot|gif|htc|ico|jpe?g|js|m4a|m4v|manifest|mp4|oex|oga|ogg|ogv|otf|pdf|png|safariextz|svg|svgz|ttf|vcf|webapp|webm|webp|woff|xml|xpi)$">
# Header unset Content-Security-Policy
# </FilesMatch>
# </IfModule>
# ------------------------------------------------------------------------------
# | File access |
# ------------------------------------------------------------------------------
# Block access to directories without a default document.
# Usually you should leave this uncommented because you shouldn't allow anyone
# to surf through every directory on your server (which may includes rather
# private places like the CMS's directories).
<IfModule mod_autoindex.c>
Options -Indexes
</IfModule>
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Block access to hidden files and directories.
# This includes directories used by version control systems such as Git and SVN.
<IfModule mod_rewrite.c>
RewriteCond %{SCRIPT_FILENAME} -d [OR]
RewriteCond %{SCRIPT_FILENAME} -f
RewriteRule "(^|/)\." - [F]
</IfModule>
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Block access to backup and source files.
# These files may be left by some text editors and can pose a great security
# danger when anyone has access to them.
<FilesMatch "(^#.*#|\.(bak|config|dist|fla|inc|ini|log|psd|sh|sql|sw[op])|~)$">
Order allow,deny
Deny from all
Satisfy All
</FilesMatch>
# ------------------------------------------------------------------------------
# | Secure Sockets Layer (SSL) |
# ------------------------------------------------------------------------------
# Rewrite secure requests properly to prevent SSL certificate warnings, e.g.:
# prevent `https://www.example.com` when your certificate only allows
# `https://secure.example.com`.
# <IfModule mod_rewrite.c>
# RewriteCond %{SERVER_PORT} !^443
# RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L]
# </IfModule>
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Force client-side SSL redirection.
# If a user types "example.com" in his browser, the above rule will redirect him
# to the secure version of the site. That still leaves a window of opportunity
# (the initial HTTP connection) for an attacker to downgrade or redirect the
# request. The following header ensures that browser will ONLY connect to your
# server via HTTPS, regardless of what the users type in the address bar.
# http://www.html5rocks.com/en/tutorials/security/transport-layer-security/
# <IfModule mod_headers.c>
# Header set Strict-Transport-Security max-age=16070400;
# </IfModule>
# ------------------------------------------------------------------------------
# | Server software information |
# ------------------------------------------------------------------------------
# Avoid displaying the exact Apache version number, the description of the
# generic OS-type and the information about Apache's compiled-in modules.
# ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`!
# ServerTokens Prod
# ##############################################################################
# # WEB PERFORMANCE #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Compression |
# ------------------------------------------------------------------------------
<IfModule mod_deflate.c>
# Force compression for mangled headers.
# http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
</IfModule>
</IfModule>
# Compress all output labeled with one of the following MIME-types
# (for Apache versions below 2.3.7, you don't need to enable `mod_filter`
# and can remove the `<IfModule mod_filter.c>` and `</IfModule>` lines
# as `AddOutputFilterByType` is still in the core directives).
<IfModule mod_filter.c>
AddOutputFilterByType DEFLATE application/atom+xml \
application/javascript \
application/json \
application/rss+xml \
application/vnd.ms-fontobject \
application/x-font-ttf \
application/x-web-app-manifest+json \
application/xhtml+xml \
application/xml \
font/opentype \
image/svg+xml \
image/x-icon \
text/css \
text/html \
text/plain \
text/x-component \
text/xml
</IfModule>
</IfModule>
# ------------------------------------------------------------------------------
# | Content transformations |
# ------------------------------------------------------------------------------
# Prevent some of the mobile network providers from modifying the content of
# your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5.
# <IfModule mod_headers.c>
# Header set Cache-Control "no-transform"
# </IfModule>
# ------------------------------------------------------------------------------
# | ETag removal |
# ------------------------------------------------------------------------------
# Since we're sending far-future expires headers (see below), ETags can
# be removed: http://developer.yahoo.com/performance/rules.html#etags.
# `FileETag None` is not enough for every server.
<IfModule mod_headers.c>
Header unset ETag
</IfModule>
FileETag None
# ------------------------------------------------------------------------------
# | Expires headers (for better cache control) |
# ------------------------------------------------------------------------------
# The following expires headers are set pretty far in the future. If you don't
# control versioning with filename-based cache busting, consider lowering the
# cache time for resources like CSS and JS to something like 1 week.
<IfModule mod_expires.c>
ExpiresActive on
ExpiresDefault "access plus 1 month"
# CSS
ExpiresByType text/css "access plus 1 year"
# Data interchange
ExpiresByType application/json "access plus 0 seconds"
ExpiresByType application/xml "access plus 0 seconds"
ExpiresByType text/xml "access plus 0 seconds"
# Favicon (cannot be renamed!)
ExpiresByType image/x-icon "access plus 1 week"
# HTML components (HTCs)
ExpiresByType text/x-component "access plus 1 month"
# HTML
ExpiresByType text/html "access plus 0 seconds"
# JavaScript
ExpiresByType application/javascript "access plus 1 year"
# Manifest files
ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"
ExpiresByType text/cache-manifest "access plus 0 seconds"
# Media
ExpiresByType audio/ogg "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType video/mp4 "access plus 1 month"
ExpiresByType video/ogg "access plus 1 month"
ExpiresByType video/webm "access plus 1 month"
# Web feeds
ExpiresByType application/atom+xml "access plus 1 hour"
ExpiresByType application/rss+xml "access plus 1 hour"
# Web fonts
ExpiresByType application/font-woff "access plus 1 month"
ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
ExpiresByType application/x-font-ttf "access plus 1 month"
ExpiresByType font/opentype "access plus 1 month"
ExpiresByType image/svg+xml "access plus 1 month"
</IfModule>
# ------------------------------------------------------------------------------
# | Filename-based cache busting |
# ------------------------------------------------------------------------------
# If you're not using a build process to manage your filename version revving,
# you might want to consider enabling the following directives to route all
# requests such as `/css/style.12345.css` to `/css/style.css`.
# To understand why this is important and a better idea than `*.css?v231`, read:
# http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring
# <IfModule mod_rewrite.c>
# RewriteCond %{REQUEST_FILENAME} !-f
# RewriteCond %{REQUEST_FILENAME} !-d
# RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L]
# </IfModule>
# ------------------------------------------------------------------------------
# | File concatenation |
# ------------------------------------------------------------------------------
# Allow concatenation from within specific CSS and JS files, e.g.:
# Inside of `script.combined.js` you could have
# <!--#include file="libs/jquery.js" -->
# <!--#include file="plugins/jquery.idletimer.js" -->
# and they would be included into this single file.
# <IfModule mod_include.c>
# <FilesMatch "\.combined\.js$">
# Options +Includes
# AddOutputFilterByType INCLUDES application/javascript application/json
# SetOutputFilter INCLUDES
# </FilesMatch>
# <FilesMatch "\.combined\.css$">
# Options +Includes
# AddOutputFilterByType INCLUDES text/css
# SetOutputFilter INCLUDES
# </FilesMatch>
# </IfModule>
# ------------------------------------------------------------------------------
# | Persistent connections |
# ------------------------------------------------------------------------------
# Allow multiple requests to be sent over the same TCP connection:
# http://httpd.apache.org/docs/current/en/mod/core.html#keepalive.
# Enable if you serve a lot of static content but, be aware of the
# possible disadvantages!
# <IfModule mod_headers.c>
# Header set Connection Keep-Alive
# </IfModule>

View File

@ -0,0 +1,16 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.config(stateConfig);
stateConfig.$inject = ['$stateProvider'];
function stateConfig($stateProvider) {
$stateProvider.state('account', {
abstract: true,
parent: 'app'
});
}
})();

View File

@ -0,0 +1,23 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.controller('ActivationController', ActivationController);
ActivationController.$inject = ['$stateParams', 'Auth', 'LoginService'];
function ActivationController ($stateParams, Auth, LoginService) {
var vm = this;
Auth.activateAccount({key: $stateParams.key}).then(function () {
vm.error = null;
vm.success = 'OK';
}).catch(function () {
vm.success = null;
vm.error = 'ERROR';
});
vm.login = LoginService.open;
}
})();

View File

@ -3,11 +3,11 @@
<div class="col-md-8 col-md-offset-2"> <div class="col-md-8 col-md-offset-2">
<h1 translate="activate.title">Activation</h1> <h1 translate="activate.title">Activation</h1>
<div class="alert alert-success" ng-show="success" translate="activate.messages.success"> <div class="alert alert-success" ng-show="vm.success" translate="activate.messages.success" translate-compile>
<strong>Your user has been activated.</strong> Please <a class="alert-link" href="#/login">sign in</a>. <strong>Your user has been activated.</strong> Please <a class="alert-link" href="" ng-click="vm.login">sign in</a>.
</div> </div>
<div class="alert alert-danger" ng-show="error" translate="activate.messages.error"> <div class="alert alert-danger" ng-show="vm.error" translate="activate.messages.error">
<strong>Your user could not be activated.</strong> Please use the registration form to sign up. <strong>Your user could not be activated.</strong> Please use the registration form to sign up.
</div> </div>

View File

@ -0,0 +1,33 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.config(stateConfig);
stateConfig.$inject = ['$stateProvider'];
function stateConfig($stateProvider) {
$stateProvider.state('activate', {
parent: 'account',
url: '/activate?key',
data: {
authorities: [],
pageTitle: 'activate.title'
},
views: {
'content@': {
templateUrl: 'app/account/activate/activate.html',
controller: 'ActivationController',
controllerAs: 'vm'
}
},
resolve: {
translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) {
$translatePartialLoader.addPart('activate');
return $translate.refresh();
}]
}
});
}
})();

View File

@ -0,0 +1,95 @@
/* globals $ */
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.directive('passwordStrengthBar', passwordStrengthBar);
function passwordStrengthBar () {
var directive = {
replace: true,
restrict: 'E',
template: '<div id="strength">' +
'<small translate="global.messages.validate.newpassword.strength">Password strength:</small>' +
'<ul id="strengthBar">' +
'<li class="point"></li><li class="point"></li><li class="point"></li><li class="point"></li><li class="point"></li>' +
'</ul>' +
'</div>',
scope: {
passwordToCheck: '='
},
link: linkFunc
};
return directive;
/* private helper methods*/
function linkFunc(scope, iElement) {
var strength = {
colors: ['#F00', '#F90', '#FF0', '#9F0', '#0F0'],
mesureStrength: function (p) {
var _force = 0;
var _regex = /[$-/:-?{-~!"^_`\[\]]/g; // "
var _lowerLetters = /[a-z]+/.test(p);
var _upperLetters = /[A-Z]+/.test(p);
var _numbers = /[0-9]+/.test(p);
var _symbols = _regex.test(p);
var _flags = [_lowerLetters, _upperLetters, _numbers, _symbols];
var _passedMatches = $.grep(_flags, function (el) {
return el === true;
}).length;
_force += 2 * p.length + ((p.length >= 10) ? 1 : 0);
_force += _passedMatches * 10;
// penality (short password)
_force = (p.length <= 6) ? Math.min(_force, 10) : _force;
// penality (poor variety of characters)
_force = (_passedMatches === 1) ? Math.min(_force, 10) : _force;
_force = (_passedMatches === 2) ? Math.min(_force, 20) : _force;
_force = (_passedMatches === 3) ? Math.min(_force, 40) : _force;
return _force;
},
getColor: function (s) {
var idx = 0;
if (s <= 10) {
idx = 0;
}
else if (s <= 20) {
idx = 1;
}
else if (s <= 30) {
idx = 2;
}
else if (s <= 40) {
idx = 3;
}
else {
idx = 4;
}
return { idx: idx + 1, col: this.colors[idx] };
}
};
scope.$watch('passwordToCheck', function (password) {
if (password) {
var c = strength.getColor(strength.mesureStrength(password));
iElement.removeClass('ng-hide');
iElement.find('ul').children('li')
.css({ 'background-color': '#DDD' })
.slice(0, c.idx)
.css({ 'background-color': c.col });
}
});
}
}
})();

View File

@ -0,0 +1,39 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.controller('PasswordController', PasswordController);
PasswordController.$inject = ['Auth', 'Principal'];
function PasswordController (Auth, Principal) {
var vm = this;
vm.changePassword = changePassword;
vm.doNotMatch = null;
vm.error = null;
vm.success = null;
Principal.identity().then(function(account) {
vm.account = account;
});
function changePassword () {
if (vm.password !== vm.confirmPassword) {
vm.error = null;
vm.success = null;
vm.doNotMatch = 'ERROR';
} else {
vm.doNotMatch = null;
Auth.changePassword(vm.password).then(function () {
vm.error = null;
vm.success = 'OK';
}).catch(function () {
vm.success = null;
vm.error = 'ERROR';
});
}
}
}
})();

View File

@ -1,25 +1,25 @@
<div> <div>
<div class="row"> <div class="row">
<div class="col-md-8 col-md-offset-2"> <div class="col-md-8 col-md-offset-2">
<h2 translate="password.title" translate-values="{username: '{{account.login}}'}">Password for [<b>{{account.login}}</b>]</h2> <h2 translate="password.title" translate-values="{username: '{{vm.account.login}}'}">Password for [<b>{{vm.account.login}}</b>]</h2>
<div class="alert alert-success" ng-show="success" translate="password.messages.success"> <div class="alert alert-success" ng-show="vm.success" translate="password.messages.success">
<strong>Password changed!</strong> <strong>Password changed!</strong>
</div> </div>
<div class="alert alert-danger" ng-show="error" translate="password.messages.error"> <div class="alert alert-danger" ng-show="vm.error" translate="password.messages.error">
<strong>An error has occurred!</strong> The password could not be changed. <strong>An error has occurred!</strong> The password could not be changed.
</div> </div>
<div class="alert alert-danger" ng-show="doNotMatch" translate="global.messages.error.dontmatch"> <div class="alert alert-danger" ng-show="vm.doNotMatch" translate="global.messages.error.dontmatch">
The password and its confirmation do not match! The password and its confirmation do not match!
</div> </div>
<form name="form" role="form" novalidate ng-submit="changePassword()" show-validation> <form name="form" role="form" novalidate ng-submit="vm.changePassword()" show-validation>
<div class="form-group"> <div class="form-group">
<label class="control-label" for="password" translate="global.form.newpassword">New password</label> <label class="control-label" for="password" translate="global.form.newpassword">New password</label>
<input type="password" class="form-control" id="password" name="password" placeholder="{{'global.form.newpassword.placeholder' | translate}}" <input type="password" class="form-control" id="password" name="password" placeholder="{{'global.form.newpassword.placeholder' | translate}}"
ng-model="password" ng-minlength=5 ng-maxlength=50 required> ng-model="vm.password" ng-minlength=5 ng-maxlength=50 required>
<div ng-show="form.password.$dirty && form.password.$invalid"> <div ng-show="form.password.$dirty && form.password.$invalid">
<p class="help-block" <p class="help-block"
ng-show="form.password.$error.required" translate="global.messages.validate.newpassword.required"> ng-show="form.password.$error.required" translate="global.messages.validate.newpassword.required">
@ -34,12 +34,12 @@
Your password cannot be longer than 50 characters. Your password cannot be longer than 50 characters.
</p> </p>
</div> </div>
<password-strength-bar password-to-check="password"></password-strength-bar> <password-strength-bar password-to-check="vm.password"></password-strength-bar>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="control-label" for="confirmPassword" translate="global.form.confirmpassword">New password confirmation</label> <label class="control-label" for="confirmPassword" translate="global.form.confirmpassword">New password confirmation</label>
<input type="password" class="form-control" id="confirmPassword" name="confirmPassword" placeholder="{{'global.form.confirmpassword.placeholder' | translate}}" <input type="password" class="form-control" id="confirmPassword" name="confirmPassword" placeholder="{{'global.form.confirmpassword.placeholder' | translate}}"
ng-model="confirmPassword" ng-minlength=5 ng-maxlength=50 required> ng-model="vm.confirmPassword" ng-minlength=5 ng-maxlength=50 required>
<div ng-show="form.confirmPassword.$dirty && form.confirmPassword.$invalid"> <div ng-show="form.confirmPassword.$dirty && form.confirmPassword.$invalid">
<p class="help-block" <p class="help-block"
ng-show="form.confirmPassword.$error.required" translate="global.messages.validate.confirmpassword.required"> ng-show="form.confirmPassword.$error.required" translate="global.messages.validate.confirmpassword.required">

View File

@ -0,0 +1,33 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.config(stateConfig);
stateConfig.$inject = ['$stateProvider'];
function stateConfig($stateProvider) {
$stateProvider.state('password', {
parent: 'account',
url: '/password',
data: {
authorities: ['ROLE_USER'],
pageTitle: 'global.menu.account.password'
},
views: {
'content@': {
templateUrl: 'app/account/password/password.html',
controller: 'PasswordController',
controllerAs: 'vm'
}
},
resolve: {
translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) {
$translatePartialLoader.addPart('password');
return $translate.refresh();
}]
}
});
}
})();

View File

@ -0,0 +1,49 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.controller('RegisterController', RegisterController);
RegisterController.$inject = ['$translate', '$timeout', 'Auth', 'LoginService'];
function RegisterController ($translate, $timeout, Auth, LoginService) {
var vm = this;
vm.doNotMatch = null;
vm.error = null;
vm.errorUserExists = null;
vm.login = LoginService.open;
vm.register = register;
vm.registerAccount = {};
vm.success = null;
$timeout(function (){angular.element('[ng-model="vm.registerAccount.login"]').focus();});
function register () {
if (vm.registerAccount.password !== vm.confirmPassword) {
vm.doNotMatch = 'ERROR';
} else {
vm.registerAccount.langKey = $translate.use();
vm.doNotMatch = null;
vm.error = null;
vm.errorUserExists = null;
vm.errorEmailExists = null;
Auth.createAccount(vm.registerAccount).then(function () {
vm.success = 'OK';
}).catch(function (response) {
vm.success = null;
if (response.status === 400 && response.data === 'login already in use') {
vm.errorUserExists = 'ERROR';
} else if (response.status === 400 && response.data === 'e-mail address already in use') {
vm.errorEmailExists = 'ERROR';
} else {
vm.error = 'ERROR';
}
});
}
}
}
})();

View File

@ -3,33 +3,33 @@
<div class="col-md-8 col-md-offset-2"> <div class="col-md-8 col-md-offset-2">
<h1 translate="register.title">Registration</h1> <h1 translate="register.title">Registration</h1>
<div class="alert alert-success" ng-show="success" translate="register.messages.success"> <div class="alert alert-success" ng-show="vm.success" translate="register.messages.success">
<strong>Registration saved!</strong> Please check your email for confirmation. <strong>Registration saved!</strong> Please check your email for confirmation.
</div> </div>
<div class="alert alert-danger" ng-show="error" translate="register.messages.error.fail"> <div class="alert alert-danger" ng-show="vm.error" translate="register.messages.error.fail">
<strong>Registration failed!</strong> Please try again later. <strong>Registration failed!</strong> Please try again later.
</div> </div>
<div class="alert alert-danger" ng-show="errorUserExists" translate="register.messages.error.userexists"> <div class="alert alert-danger" ng-show="vm.errorUserExists" translate="register.messages.error.userexists">
<strong>Login name already registered!</strong> Please choose another one. <strong>Login name already registered!</strong> Please choose another one.
</div> </div>
<div class="alert alert-danger" ng-show="errorEmailExists" translate="register.messages.error.emailexists"> <div class="alert alert-danger" ng-show="vm.errorEmailExists" translate="register.messages.error.emailexists">
<strong>E-mail is already in use!</strong> Please choose another one. <strong>E-mail is already in use!</strong> Please choose another one.
</div> </div>
<div class="alert alert-danger" ng-show="doNotMatch" translate="global.messages.error.dontmatch"> <div class="alert alert-danger" ng-show="vm.doNotMatch" translate="global.messages.error.dontmatch">
The password and its confirmation do not match! The password and its confirmation do not match!
</div> </div>
</div> </div>
<div class="col-md-8 col-md-offset-2"> <div class="col-md-8 col-md-offset-2">
<form ng-show="!success" name="form" role="form" novalidate ng-submit="register()" show-validation> <form ng-show="!vm.success" name="form" role="form" novalidate ng-submit="vm.register()" show-validation>
<div class="form-group"> <div class="form-group">
<label class="control-label" for="login" translate="global.form.username">Username</label> <label class="control-label" for="login" translate="global.form.username">Username</label>
<input type="text" class="form-control" id="login" name="login" placeholder="{{'global.form.username.placeholder' | translate}}" <input type="text" class="form-control" id="login" name="login" placeholder="{{'global.form.username.placeholder' | translate}}"
ng-model="registerAccount.login" ng-minlength=1 ng-maxlength=50 ng-pattern="/^[a-z0-9]*$/" required> ng-model="vm.registerAccount.login" ng-minlength=1 ng-maxlength=50 ng-pattern="/^[a-z0-9]*$/" required>
<div ng-show="form.login.$dirty && form.login.$invalid"> <div ng-show="form.login.$dirty && form.login.$invalid">
<p class="help-block" <p class="help-block"
ng-show="form.login.$error.required" translate="register.messages.validate.login.required"> ng-show="form.login.$error.required" translate="register.messages.validate.login.required">
@ -52,7 +52,7 @@
<div class="form-group"> <div class="form-group">
<label class="control-label" for="email" translate="global.form.email">E-mail</label> <label class="control-label" for="email" translate="global.form.email">E-mail</label>
<input type="email" class="form-control" id="email" name="email" placeholder="{{'global.form.email.placeholder' | translate}}" <input type="email" class="form-control" id="email" name="email" placeholder="{{'global.form.email.placeholder' | translate}}"
ng-model="registerAccount.email" ng-minlength=5 ng-maxlength=100 required> ng-model="vm.registerAccount.email" ng-minlength=5 ng-maxlength=100 required>
<div ng-show="form.email.$dirty && form.email.$invalid"> <div ng-show="form.email.$dirty && form.email.$invalid">
<p class="help-block" <p class="help-block"
ng-show="form.email.$error.required" translate="global.messages.validate.email.required"> ng-show="form.email.$error.required" translate="global.messages.validate.email.required">
@ -75,7 +75,7 @@
<div class="form-group"> <div class="form-group">
<label class="control-label" for="password" translate="global.form.newpassword">New password</label> <label class="control-label" for="password" translate="global.form.newpassword">New password</label>
<input type="password" class="form-control" id="password" name="password" placeholder="{{'global.form.newpassword.placeholder' | translate}}" <input type="password" class="form-control" id="password" name="password" placeholder="{{'global.form.newpassword.placeholder' | translate}}"
ng-model="registerAccount.password" ng-minlength=5 ng-maxlength=50 required> ng-model="vm.registerAccount.password" ng-minlength=5 ng-maxlength=50 required>
<div ng-show="form.password.$dirty && form.password.$invalid"> <div ng-show="form.password.$dirty && form.password.$invalid">
<p class="help-block" <p class="help-block"
ng-show="form.password.$error.required" translate="global.messages.validate.newpassword.required"> ng-show="form.password.$error.required" translate="global.messages.validate.newpassword.required">
@ -90,12 +90,12 @@
Your password cannot be longer than 50 characters. Your password cannot be longer than 50 characters.
</p> </p>
</div> </div>
<password-strength-bar password-to-check="registerAccount.password"></password-strength-bar> <password-strength-bar password-to-check="vm.registerAccount.password"></password-strength-bar>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="control-label" for="confirmPassword" translate="global.form.confirmpassword">New password confirmation</label> <label class="control-label" for="confirmPassword" translate="global.form.confirmpassword">New password confirmation</label>
<input type="password" class="form-control" id="confirmPassword" name="confirmPassword" placeholder="{{'global.form.confirmpassword.placeholder' | translate}}" <input type="password" class="form-control" id="confirmPassword" name="confirmPassword" placeholder="{{'global.form.confirmpassword.placeholder' | translate}}"
ng-model="confirmPassword" ng-minlength=5 ng-maxlength=50 required> ng-model="vm.confirmPassword" ng-minlength=5 ng-maxlength=50 required>
<div ng-show="form.confirmPassword.$dirty && form.confirmPassword.$invalid"> <div ng-show="form.confirmPassword.$dirty && form.confirmPassword.$invalid">
<p class="help-block" <p class="help-block"
ng-show="form.confirmPassword.$error.required" translate="global.messages.validate.confirmpassword.required"> ng-show="form.confirmPassword.$error.required" translate="global.messages.validate.confirmpassword.required">
@ -115,8 +115,8 @@
<button type="submit" ng-disabled="form.$invalid" class="btn btn-primary" translate="register.form.button">Register</button> <button type="submit" ng-disabled="form.$invalid" class="btn btn-primary" translate="register.form.button">Register</button>
</form> </form>
<p></p> <p></p>
<div class="alert alert-warning" translate="global.messages.info.authenticated"> <div class="alert alert-warning" translate="global.messages.info.authenticated" translate-compile>
If you want to <a class="alert-link" href="#/login">sign in</a>, you can try the default accounts:<br/>- Administrator (login="admin" and password="admin") <br/>- User (login="user" and password="user"). If you want to <a class="alert-link" href="" ng-click="login()">sign in</a>, you can try the default accounts:<br/>- Administrator (login="admin" and password="admin") <br/>- User (login="user" and password="user").
</div> </div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,33 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.config(stateConfig);
stateConfig.$inject = ['$stateProvider'];
function stateConfig($stateProvider) {
$stateProvider.state('register', {
parent: 'account',
url: '/register',
data: {
authorities: [],
pageTitle: 'register.title'
},
views: {
'content@': {
templateUrl: 'app/account/register/register.html',
controller: 'RegisterController',
controllerAs: 'vm'
}
},
resolve: {
translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) {
$translatePartialLoader.addPart('register');
return $translate.refresh();
}]
}
});
}
})();

View File

@ -0,0 +1,37 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.controller('ResetFinishController', ResetFinishController);
ResetFinishController.$inject = ['$stateParams', '$timeout', 'Auth', 'LoginService'];
function ResetFinishController ($stateParams, $timeout, Auth, LoginService) {
var vm = this;
vm.keyMissing = angular.isUndefined($stateParams.key);
vm.confirmPassword = null;
vm.doNotMatch = null;
vm.error = null;
vm.finishReset = finishReset;
vm.login = LoginService.open;
vm.resetAccount = {};
vm.success = null;
$timeout(function (){angular.element('[ng-model="vm.resetAccount.password"]').focus();});
function finishReset() {
if (vm.resetAccount.password !== vm.confirmPassword) {
vm.doNotMatch = 'ERROR';
} else {
Auth.resetPasswordFinish({key: $stateParams.key, newPassword: vm.resetAccount.password}).then(function () {
vm.success = 'OK';
}).catch(function () {
vm.success = null;
vm.error = 'ERROR';
});
}
}
}
})();

View File

@ -3,32 +3,32 @@
<div class="col-md-4 col-md-offset-4"> <div class="col-md-4 col-md-offset-4">
<h1 translate="reset.finish.title">Reset password</h1> <h1 translate="reset.finish.title">Reset password</h1>
<div class="alert alert-danger" translate="reset.finish.messages.keymissing" ng-show="keyMissing"> <div class="alert alert-danger" translate="reset.finish.messages.keymissing" ng-show="vm.keyMissing">
<strong>The password reset key is missing.</strong> <strong>The password reset key is missing.</strong>
</div> </div>
<div class="alert alert-warning" ng-hide="success || keyMissing"> <div class="alert alert-warning" ng-hide="vm.success || vm.keyMissing">
<p translate="reset.finish.messages.info">Choose a new password</p> <p translate="reset.finish.messages.info">Choose a new password</p>
</div> </div>
<div class="alert alert-danger" ng-show="error"> <div class="alert alert-danger" ng-show="vm.error">
<p translate="reset.finish.messages.error">Your password couldn't be reset. Remember a password request is only valid for 24 hours.</p> <p translate="reset.finish.messages.error">Your password couldn't be reset. Remember a password request is only valid for 24 hours.</p>
</div> </div>
<div class="alert alert-success" ng-show="success"> <div class="alert alert-success" ng-show="vm.success">
<p translate="reset.finish.messages.success"><strong>Your password has been reset.</strong> Please <a class="alert-link" href="#/login">sign in</a>.</p> <p translate="reset.finish.messages.success" translate-compile><strong>Your password has been reset.</strong> Please <a class="alert-link" href="" ng-click="login">sign in</a>.</p>
</div> </div>
<div class="alert alert-danger" ng-show="doNotMatch" translate="global.messages.error.dontmatch"> <div class="alert alert-danger" ng-show="vm.doNotMatch" translate="global.messages.error.dontmatch">
The password and its confirmation do not match! The password and its confirmation do not match!
</div> </div>
<div ng-hide="keyMissing"> <div ng-hide="vm.keyMissing">
<form ng-show="!success" name="form" role="form" novalidate ng-submit="finishReset()" show-validation> <form ng-show="!vm.success" name="form" role="form" novalidate ng-submit="vm.finishReset()" show-validation>
<div class="form-group"> <div class="form-group">
<label class="control-label" for="password" translate="global.form.newpassword">New password</label> <label class="control-label" for="password" translate="global.form.newpassword">New password</label>
<input type="password" class="form-control" id="password" name="password" placeholder="{{'global.form.newpassword.placeholder' | translate}}" <input type="password" class="form-control" id="password" name="password" placeholder="{{'global.form.newpassword.placeholder' | translate}}"
ng-model="resetAccount.password" ng-minlength=5 ng-maxlength=50 required> ng-model="vm.resetAccount.password" ng-minlength=5 ng-maxlength=50 required>
<div ng-show="form.password.$dirty && form.password.$invalid"> <div ng-show="form.password.$dirty && form.password.$invalid">
<p class="help-block" <p class="help-block"
ng-show="form.password.$error.required" translate="global.messages.validate.newpassword.required"> ng-show="form.password.$error.required" translate="global.messages.validate.newpassword.required">
@ -43,13 +43,13 @@
Your password cannot be longer than 50 characters. Your password cannot be longer than 50 characters.
</p> </p>
</div> </div>
<password-strength-bar password-to-check="resetAccount.password"></password-strength-bar> <password-strength-bar password-to-check="vm.resetAccount.password"></password-strength-bar>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="control-label" for="confirmPassword" translate="global.form.confirmpassword">New password confirmation</label> <label class="control-label" for="confirmPassword" translate="global.form.confirmpassword">New password confirmation</label>
<input type="password" class="form-control" id="confirmPassword" name="confirmPassword" placeholder="{{'global.form.confirmpassword.placeholder' | translate}}" <input type="password" class="form-control" id="confirmPassword" name="confirmPassword" placeholder="{{'global.form.confirmpassword.placeholder' | translate}}"
ng-model="confirmPassword" ng-minlength=5 ng-maxlength=50 required> ng-model="vm.confirmPassword" ng-minlength=5 ng-maxlength=50 required>
<div ng-show="form.confirmPassword.$dirty && form.confirmPassword.$invalid"> <div ng-show="form.confirmPassword.$dirty && form.confirmPassword.$invalid">
<p class="help-block" <p class="help-block"
ng-show="form.confirmPassword.$error.required" translate="global.messages.validate.confirmpassword.required"> ng-show="form.confirmPassword.$error.required" translate="global.messages.validate.confirmpassword.required">

View File

@ -0,0 +1,32 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.config(stateConfig);
stateConfig.$inject = ['$stateProvider'];
function stateConfig($stateProvider) {
$stateProvider.state('finishReset', {
parent: 'account',
url: '/reset/finish?key',
data: {
authorities: []
},
views: {
'content@': {
templateUrl: 'app/account/reset/finish/reset.finish.html',
controller: 'ResetFinishController',
controllerAs: 'vm'
}
},
resolve: {
translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) {
$translatePartialLoader.addPart('reset');
return $translate.refresh();
}]
}
});
}
})();

View File

@ -0,0 +1,38 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.controller('RequestResetController', RequestResetController);
RequestResetController.$inject = ['$timeout', 'Auth'];
function RequestResetController ($timeout, Auth) {
var vm = this;
vm.error = null;
vm.errorEmailNotExists = null;
vm.requestReset = requestReset;
vm.resetAccount = {};
vm.success = null;
$timeout(function (){angular.element('[ng-model="resetAccount.email"]').focus();});
function requestReset () {
vm.error = null;
vm.errorEmailNotExists = null;
Auth.resetPasswordInit(vm.resetAccount.email).then(function () {
vm.success = 'OK';
}).catch(function (response) {
vm.success = null;
if (response.status === 400 && response.data === 'e-mail address not registered') {
vm.errorEmailNotExists = 'ERROR';
} else {
vm.error = 'ERROR';
}
});
}
}
})();

View File

@ -3,23 +3,23 @@
<div class="col-md-8 col-md-offset-2"> <div class="col-md-8 col-md-offset-2">
<h1 translate="reset.request.title">Reset your password</h1> <h1 translate="reset.request.title">Reset your password</h1>
<div class="alert alert-danger" translate="reset.request.messages.notfound" ng-show="errorEmailNotExists"> <div class="alert alert-danger" translate="reset.request.messages.notfound" ng-show="vm.errorEmailNotExists">
<strong>E-Mail address isn't registered!</strong> Please check and try again. <strong>E-Mail address isn't registered!</strong> Please check and try again.
</div> </div>
<div class="alert alert-warning" ng-hide="success"> <div class="alert alert-warning" ng-hide="vm.success">
<p translate="reset.request.messages.info">Enter the e-mail address you used to register.</p> <p translate="reset.request.messages.info">Enter the e-mail address you used to register.</p>
</div> </div>
<div class="alert alert-success" ng-show="success == 'OK'"> <div class="alert alert-success" ng-show="vm.success == 'OK'">
<p translate="reset.request.messages.success">Check your e-mails for details on how to reset your password.</p> <p translate="reset.request.messages.success">Check your e-mails for details on how to reset your password.</p>
</div> </div>
<form ng-show="!success" name="form" role="form" novalidate ng-submit="requestReset()" show-validation> <form ng-show="!vm.success" name="form" role="form" novalidate ng-submit="vm.requestReset()" show-validation>
<div class="form-group"> <div class="form-group">
<label class="control-label" for="email" translate="global.form.email">E-mail</label> <label class="control-label" for="email" translate="global.form.email">E-mail</label>
<input type="email" class="form-control" id="email" name="email" placeholder="{{'global.form.email.placeholder' | translate}}" <input type="email" class="form-control" id="email" name="email" placeholder="{{'global.form.email.placeholder' | translate}}"
ng-model="resetAccount.email" ng-minlength=5 ng-maxlength=100 required> ng-model="vm.resetAccount.email" ng-minlength=5 ng-maxlength=100 required>
<div ng-show="form.email.$dirty && form.email.$invalid"> <div ng-show="form.email.$dirty && form.email.$invalid">
<p class="help-block" <p class="help-block"
ng-show="form.email.$error.required" translate="global.messages.validate.email.required"> ng-show="form.email.$error.required" translate="global.messages.validate.email.required">

View File

@ -0,0 +1,32 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.config(stateConfig);
stateConfig.$inject = ['$stateProvider'];
function stateConfig($stateProvider) {
$stateProvider.state('requestReset', {
parent: 'account',
url: '/reset/request',
data: {
authorities: []
},
views: {
'content@': {
templateUrl: 'app/account/reset/request/reset.request.html',
controller: 'RequestResetController',
controllerAs: 'vm'
}
},
resolve: {
translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) {
$translatePartialLoader.addPart('reset');
return $translate.refresh();
}]
}
});
}
})();

View File

@ -0,0 +1,37 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.controller('SessionsController', SessionsController);
SessionsController.$inject = ['Sessions', 'Principal'];
function SessionsController (Sessions, Principal) {
var vm = this;
vm.account = null;
vm.error = null;
vm.invalidate = invalidate;
vm.sessions = Sessions.getAll();
vm.success = null;
Principal.identity().then(function(account) {
vm.account = account;
});
function invalidate (series) {
Sessions.delete({series: encodeURIComponent(series)},
function () {
vm.error = null;
vm.success = 'OK';
vm.sessions = Sessions.getAll();
},
function () {
vm.success = null;
vm.error = 'ERROR';
});
}
}
})();

View File

@ -1,11 +1,11 @@
<div> <div>
<h2 translate="sessions.title" translate-values="{username: '{{account.login}}'}">Active sessions for [<b>{{account.login}}</b>]</h2> <h2 translate="sessions.title" translate-values="{username: '{{vm.account.login}}'}">Active sessions for [<b>{{vm.account.login}}</b>]</h2>
<div class="alert alert-success" ng-show="success" translate="sessions.messages.success"> <div class="alert alert-success" ng-show="vm.success" translate="sessions.messages.success">
<strong>Session invalidated!</strong> <strong>Session invalidated!</strong>
</div> </div>
<div class="alert alert-danger" ng-show="error" translate="sessions.messages.error"> <div class="alert alert-danger" ng-show="vm.error" translate="sessions.messages.error">
<strong>An error has occured!</strong> The session could not be invalidated. <strong>An error has occured!</strong> The session could not be invalidated.
</div> </div>
@ -20,14 +20,14 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="session in sessions"> <tr ng-repeat="session in vm.sessions">
<td>{{session.ipAddress}}</td> <td>{{session.ipAddress}}</td>
<td>{{session.userAgent}}</td> <td>{{session.userAgent}}</td>
<td>{{session.formattedTokenDate}}</td> <td>{{session.formattedTokenDate}}</td>
<td> <td>
<button type="submit" <button type="submit"
class="btn btn-primary" class="btn btn-primary"
ng-click="invalidate(session.series)" translate="sessions.table.button"> ng-click="vm.invalidate(session.series)" translate="sessions.table.button">
Invalidate Invalidate
</button> </button>
</td> </td>

View File

@ -0,0 +1,33 @@
(function() {
'use strict';
angular
.module('sampleApplicationApp')
.config(stateConfig);
stateConfig.$inject = ['$stateProvider'];
function stateConfig($stateProvider) {
$stateProvider.state('sessions', {
parent: 'account',
url: '/sessions',
data: {
authorities: ['ROLE_USER'],
pageTitle: 'global.menu.account.sessions'
},
views: {
'content@': {
templateUrl: 'app/account/sessions/sessions.html',
controller: 'SessionsController',
controllerAs: 'vm'
}
},
resolve: {
translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) {
$translatePartialLoader.addPart('sessions');
return $translate.refresh();
}]
}
});
}
})();

Some files were not shown because too many files have changed in this diff Show More