Merge branch 'develop'
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
package org.codiki.core.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller that catch errors from spring rest or spring security and others, and transform them to JSON response.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/error")
|
||||
public class CustomErrorController implements ErrorController {
|
||||
|
||||
private final ErrorAttributes errorAttributes;
|
||||
|
||||
@Autowired
|
||||
public CustomErrorController(ErrorAttributes errorAttributes) {
|
||||
Assert.notNull(errorAttributes, "ErrorAttributes must not be null");
|
||||
this.errorAttributes = errorAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorPath() {
|
||||
return "/error";
|
||||
}
|
||||
|
||||
@RequestMapping
|
||||
public Map<String, Object> error(HttpServletRequest request){
|
||||
Map<String, Object> body = getErrorAttributes(request, getTraceParameter(request));
|
||||
String trace = (String) body.get("trace");
|
||||
if(trace != null){
|
||||
String[] lines = trace.split("\n\t");
|
||||
body.put("trace", lines);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
private boolean getTraceParameter(HttpServletRequest request) {
|
||||
String parameter = request.getParameter("trace");
|
||||
if (parameter == null) {
|
||||
return false;
|
||||
}
|
||||
return !"false".equals(parameter.toLowerCase());
|
||||
}
|
||||
|
||||
private Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
|
||||
return errorAttributes.getErrorAttributes(new ServletWebRequest(request), includeStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.codiki.core.config;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
@RestController
|
||||
public class RobotsTxtController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RobotsTxtController.class);
|
||||
|
||||
@RequestMapping(value = "/robots.txt")
|
||||
public void robots(HttpServletResponse response) {
|
||||
try {
|
||||
response.getWriter().write("User-agent: *\nDisallow: /\n");
|
||||
} catch (IOException ex) {
|
||||
LOG.info("Error during robots.txt serving.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,6 @@ public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException authException) throws IOException {
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,27 +45,31 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests()
|
||||
// Permits all
|
||||
.antMatchers(
|
||||
"/api/account/login",
|
||||
"/api/account/logout",
|
||||
"/api/account/signin"
|
||||
"/api/account/signin",
|
||||
"/robots.txt"
|
||||
).permitAll()
|
||||
.antMatchers(
|
||||
HttpMethod.GET,
|
||||
"/api/categories",
|
||||
"/api/images",
|
||||
"/api/posts",
|
||||
"/api/categories/**",
|
||||
"/api/images/**",
|
||||
"/api/posts/**"
|
||||
).permitAll()
|
||||
.antMatchers(
|
||||
"/api/images/uploadAvatar",
|
||||
"/api/images/myImages",
|
||||
"/api/posts/myPosts",
|
||||
"/api/account/changePassword",
|
||||
"/api/account/"
|
||||
"/api/account/",
|
||||
"/api/posts/myPosts",
|
||||
"/api/posts/preview",
|
||||
"/api/posts/"
|
||||
).authenticated()
|
||||
.antMatchers(
|
||||
HttpMethod.GET,
|
||||
"/api/categories",
|
||||
"/api/images",
|
||||
"/api/posts",
|
||||
"/api/categories/**",
|
||||
"/api/images/**",
|
||||
"/api/posts/**"
|
||||
).permitAll()
|
||||
.anyRequest().permitAll()
|
||||
.and()
|
||||
// Allow to avoid login form at authentication failure from Angular app
|
||||
|
||||
@@ -21,6 +21,9 @@ logging:
|
||||
|
||||
server:
|
||||
# use-forward-headers=true
|
||||
error:
|
||||
whitelabel:
|
||||
enabled: false # Disable html error responses.
|
||||
port: 8080
|
||||
# ssl:
|
||||
# key-store: /home/takiguchi/Developpement/Java/codiki/keystore.p12
|
||||
|
||||
@@ -39,9 +39,9 @@
|
||||
<a routerLink="/accountSettings" class="indigo-text">
|
||||
Annuler
|
||||
</a>
|
||||
<button class="float-right waves-effect waves-light indigo btn"
|
||||
<button class="float-right waves-effect waves-light indigo white-text btn"
|
||||
type="submit" [disabled]="!profilEditionForm.form.valid">
|
||||
Suivant
|
||||
Enregistrer
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -80,7 +80,7 @@ export class ProfilEditionComponent implements OnInit {
|
||||
|
||||
onSubmit(): void {
|
||||
this.profilEditionService.updateUser(this.model).subscribe(() => {
|
||||
NotificationsComponent.success('Modification enregistrée.');
|
||||
NotificationsComponent.success('Modifications enregistrées.');
|
||||
}, error => {
|
||||
NotificationsComponent.error('L\'adresse mail saisie n\'est pas disponible.');
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/c
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { AuthService } from '../services/auth.service';
|
||||
import { NotificationsComponent } from '../notifications/notifications.component';
|
||||
|
||||
@Injectable()
|
||||
export class UnauthorizedInterceptor implements HttpInterceptor {
|
||||
@@ -17,6 +18,7 @@ export class UnauthorizedInterceptor implements HttpInterceptor {
|
||||
if (err.status === 401) {
|
||||
this.authService.setAnonymous();
|
||||
this.router.navigate(['/login']);
|
||||
window.setTimeout(() => NotificationsComponent.error('Veuillez vous authentifier pour réaliser cette action.'), 500);
|
||||
}
|
||||
|
||||
const error = (err.error && err.error.message) || err.statusText;
|
||||
|
||||
27
src/main/ts/src/app/core/post-card/post-card.component.html
Normal file
27
src/main/ts/src/app/core/post-card/post-card.component.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<div class="post-card hoverable" routerLink="/posts/{{post.key}}">
|
||||
<mdb-card>
|
||||
<!-- Picture -->
|
||||
<mdb-card-img [src]="post.image" alt="Article"></mdb-card-img>
|
||||
<!-- Body -->
|
||||
<mdb-card-body>
|
||||
<!-- Title -->
|
||||
<mdb-card-title>
|
||||
<h4>{{post.title}}</h4>
|
||||
</mdb-card-title>
|
||||
<!-- Description -->
|
||||
<mdb-card-text>
|
||||
{{post.description}}
|
||||
</mdb-card-text>
|
||||
</mdb-card-body>
|
||||
<!-- Footer -->
|
||||
<div class="card-footer text-muted mt-4">
|
||||
<img [src]="getAvatarUrl()"
|
||||
class="author-img"
|
||||
[alt]="post.author.name"
|
||||
[mdbTooltip]="post.author.name"
|
||||
placement="bottom"/>
|
||||
Article écrit par {{post.author.name}}
|
||||
<span class="creation-date-area">({{post.creationDate | date:'yyyy-MM-dd HH:mm:ss'}})</span>
|
||||
</div>
|
||||
</mdb-card>
|
||||
</div>
|
||||
@@ -3,35 +3,11 @@ import { Post } from '../entities';
|
||||
|
||||
@Component({
|
||||
selector: 'app-post-card',
|
||||
template: `
|
||||
<div class="card hoverable">
|
||||
<div class="view hm-white-slight waves-light" mdbRippleRadius>
|
||||
<img id="post-image" class="img-fluid" [src]="post.image" alt="Article" />
|
||||
<a routerLink="/posts/{{post.key}}">
|
||||
<div class="mask"></div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">{{post.title}}</h4>
|
||||
<p class="card-text">{{post.description}}</p>
|
||||
</div>
|
||||
<div class="card-data">
|
||||
<img [src]="getAvatarUrl()"
|
||||
class="author-img"
|
||||
[alt]="post.author.name"
|
||||
[mdbTooltip]="post.author.name"
|
||||
placement="bottom"/>
|
||||
Article écrit par {{post.author.name}}
|
||||
<span class="creation-date-area">({{post.creationDate | date:'yyyy-MM-dd HH:mm:ss'}})</span>
|
||||
</div>
|
||||
</div>`,
|
||||
templateUrl: './post-card.component.html',
|
||||
styles: [`
|
||||
div.card {
|
||||
.post-card {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.card .card-data {
|
||||
padding: 15px;
|
||||
background-color: #f5f5f5;
|
||||
cursor: pointer;
|
||||
}
|
||||
.creation-date-area {
|
||||
color: #bdbdbd;
|
||||
|
||||
@@ -159,11 +159,17 @@ export class CreateUpdatePostComponent implements OnInit {
|
||||
|
||||
if (this.model.key) {
|
||||
this.createUpdatePostService.updatePost(this.model).subscribe(post => {
|
||||
NotificationsComponent.error('Modification enregistrée');
|
||||
NotificationsComponent.success('Modification enregistrée');
|
||||
}, error => {
|
||||
console.log(error);
|
||||
NotificationsComponent.error('Une erreur est survenue lors de l\'enregistrement des modifications.');
|
||||
});
|
||||
} else {
|
||||
this.createUpdatePostService.addPost(this.model).subscribe(post => {
|
||||
this.router.navigate([`/posts/update/${post.key}`]);
|
||||
}, error => {
|
||||
console.log(error);
|
||||
NotificationsComponent.error('Une erreur est survenue lors de la création du post.');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
||||
BIN
src/main/ts/src/assets/doc/codiki_user_manual.pdf
Normal file
BIN
src/main/ts/src/assets/doc/codiki_user_manual.pdf
Normal file
Binary file not shown.
@@ -3,7 +3,8 @@
|
||||
<head>
|
||||
<title>Codiki</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<base href="/" />
|
||||
<link rel="icon" type="image/x-icon" href="assets/images/favicon.png" />
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||
|
||||
Reference in New Issue
Block a user