system configuration page: read-only mode, title, homepage message
This commit is contained in:
parent
92ae3fb533
commit
654d3fc397
|
@ -17,14 +17,16 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import eu.dnetlib.common.controller.AbstractDnetController;
|
||||
import eu.dnetlib.organizations.model.SystemConfiguration;
|
||||
import eu.dnetlib.organizations.model.utils.VocabularyTerm;
|
||||
import eu.dnetlib.organizations.model.view.UserView;
|
||||
import eu.dnetlib.organizations.repository.SystemConfigurationRepository;
|
||||
import eu.dnetlib.organizations.repository.UserRepository;
|
||||
import eu.dnetlib.organizations.repository.readonly.UserViewRepository;
|
||||
import eu.dnetlib.organizations.utils.DatabaseUtils;
|
||||
|
||||
@RestController
|
||||
public class UserController extends AbstractDnetController {
|
||||
public class AdminController extends AbstractDnetController {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
@ -32,6 +34,9 @@ public class UserController extends AbstractDnetController {
|
|||
@Autowired
|
||||
private UserViewRepository userViewRepository;
|
||||
|
||||
@Autowired
|
||||
private SystemConfigurationRepository systemConfigurationRepository;
|
||||
|
||||
@Autowired
|
||||
private DatabaseUtils dbUtils;
|
||||
|
||||
|
@ -76,17 +81,35 @@ public class UserController extends AbstractDnetController {
|
|||
}
|
||||
|
||||
@PostMapping("/api/users")
|
||||
public Iterable<UserView> save(@RequestBody final UserView userView, final Authentication authentication) {
|
||||
public Iterable<UserView> saveUser(@RequestBody final UserView userView, final Authentication authentication) {
|
||||
if (UserInfo.getEmail(authentication).equals(userView.getEmail())) { throw new RuntimeException("You can't edit your own user"); }
|
||||
dbUtils.saveUser(userView);
|
||||
return users(authentication);
|
||||
}
|
||||
|
||||
@DeleteMapping("/api/users")
|
||||
public Iterable<UserView> delete(final @RequestParam String email, final Authentication authentication) {
|
||||
public Iterable<UserView> deleteUser(final @RequestParam String email, final Authentication authentication) {
|
||||
if (UserInfo.getEmail(authentication).equals(email)) { throw new RuntimeException("You can't delete your own user"); }
|
||||
dbUtils.deleteUser(email);
|
||||
return users(authentication);
|
||||
}
|
||||
|
||||
@GetMapping("/api/sysconf")
|
||||
public SystemConfiguration sysConf(final Authentication authentication) {
|
||||
if (UserInfo.isSuperAdmin(authentication)) {
|
||||
return systemConfigurationRepository.findById(SystemConfiguration.DEFAULT_ID).get();
|
||||
} else {
|
||||
throw new RuntimeException("User not authorized");
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/api/sysconf")
|
||||
public SystemConfiguration saveSysConf(@RequestBody final SystemConfiguration sysConf, final Authentication authentication) {
|
||||
if (UserInfo.isSuperAdmin(authentication)) {
|
||||
sysConf.setId(SystemConfiguration.DEFAULT_ID);
|
||||
return systemConfigurationRepository.save(sysConf);
|
||||
} else {
|
||||
throw new RuntimeException("User not authorized");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,8 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
|
||||
import eu.dnetlib.common.controller.AbstractDnetController;
|
||||
import eu.dnetlib.organizations.model.SystemConfiguration;
|
||||
import eu.dnetlib.organizations.repository.SystemConfigurationRepository;
|
||||
|
||||
@Controller
|
||||
public class HomeController extends AbstractDnetController {
|
||||
|
@ -16,6 +18,9 @@ public class HomeController extends AbstractDnetController {
|
|||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
@Autowired
|
||||
private SystemConfigurationRepository systemConfigurationRepository;
|
||||
|
||||
@GetMapping("/")
|
||||
public String home() {
|
||||
return env.acceptsProfiles(Profiles.of("dev")) ? "redirect:main" : "home";
|
||||
|
@ -46,6 +51,11 @@ public class HomeController extends AbstractDnetController {
|
|||
return authentication != null ? UserInfo.getEmail(authentication) : null;
|
||||
}
|
||||
|
||||
@ModelAttribute("sysconf")
|
||||
public SystemConfiguration getSysConf(final Authentication authentication) {
|
||||
return systemConfigurationRepository.findById(SystemConfiguration.DEFAULT_ID).get();
|
||||
}
|
||||
|
||||
@GetMapping({
|
||||
"/doc", "/swagger"
|
||||
})
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package eu.dnetlib.organizations.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "sysconf")
|
||||
public class SystemConfiguration implements Serializable {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -7935575713678578023L;
|
||||
|
||||
public static final String DEFAULT_ID = "default";
|
||||
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
private String id;
|
||||
|
||||
@Column(name = "title")
|
||||
private String title;
|
||||
|
||||
@Column(name = "homepage_msg")
|
||||
private String homepageMessage;
|
||||
|
||||
@Column(name = "readonly")
|
||||
private Boolean readonly;
|
||||
|
||||
public SystemConfiguration() {}
|
||||
|
||||
public SystemConfiguration(final String id, final String title, final String homepageMessage, final Boolean readonly) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.homepageMessage = homepageMessage;
|
||||
this.readonly = readonly;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(final String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getHomepageMessage() {
|
||||
return homepageMessage;
|
||||
}
|
||||
|
||||
public void setHomepageMessage(final String homepageMessage) {
|
||||
this.homepageMessage = homepageMessage;
|
||||
}
|
||||
|
||||
public Boolean getReadonly() {
|
||||
return readonly;
|
||||
}
|
||||
|
||||
public void setReadonly(final Boolean readonly) {
|
||||
this.readonly = readonly;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package eu.dnetlib.organizations.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import eu.dnetlib.organizations.model.SystemConfiguration;
|
||||
|
||||
public interface SystemConfigurationRepository extends JpaRepository<SystemConfiguration, String> {
|
||||
|
||||
}
|
|
@ -6,6 +6,7 @@ DROP VIEW IF EXISTS conflict_groups_view;
|
|||
DROP VIEW IF EXISTS suggestions_info_by_country_view;
|
||||
DROP VIEW IF EXISTS duplicate_groups_view;
|
||||
|
||||
DROP TABLE IF EXISTS sysconf;
|
||||
DROP TABLE IF EXISTS other_ids;
|
||||
DROP TABLE IF EXISTS other_names;
|
||||
DROP TABLE IF EXISTS acronyms;
|
||||
|
@ -26,6 +27,14 @@ DROP TABLE IF EXISTS languages;
|
|||
|
||||
DROP SEQUENCE IF EXISTS organizations_id_seq;
|
||||
|
||||
CREATE TABLE sysconf (
|
||||
id text PRIMARY KEY DEFAULT 'default',
|
||||
title text NOT NULL DEFAULT 'OpenOrgs Database',
|
||||
homepage_msg text NOT NULL DEFAULT '',
|
||||
readonly boolean NOT NULL DEFAULT false
|
||||
);
|
||||
INSERT INTO sysconf(id) VALUES ('default');
|
||||
|
||||
CREATE TABLE org_types (val text PRIMARY KEY, name text);
|
||||
INSERT INTO org_types(val) VALUES ('Archive'), ('Company'), ('Education'), ('Facility'), ('Government'), ('Healthcare'), ('Nonprofit'), ('Other'), ('UNKNOWN');
|
||||
UPDATE org_types SET name = val;
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<h4>System configuration</h4>
|
||||
<br />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-lg-10">
|
||||
<form>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Title</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" ng-model="sysconf.title" placeholder="title here" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Homepage message</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea class="form-control" ng-model="sysconf.homepageMessage" placeholder="html message here" rows="5"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Read only</label>
|
||||
<div class="col-sm-10">
|
||||
<input class="form-control" type="checkbox" ng-model="sysconf.readonly" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="offset-sm-2 col-sm-10">
|
||||
<button type="submit" class="btn btn-primary" ng-click="saveConf()">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
|
@ -95,7 +95,7 @@
|
|||
<div class="card mb-3" ng-if="currentUser.valid && ((currentUser.role == 'USER') || (currentUser.role == 'NATIONAL_ADMIN'))">
|
||||
<div class="card-header bg-primary text-white py-1">Countries</div>
|
||||
<div class="card-body">
|
||||
<input type="text" class="form-control form-control-sm mb-3" ng-model="countryFilter" placeholder="Filter country...">
|
||||
<input type="text" class="form-control form-control-sm mb-3" ng-model="countryFilter" ng-show="vocabularies.countries.length > 10" placeholder="Filter country...">
|
||||
<div class="form-group row">
|
||||
<div class="col-xs-12 col-md-6 col-lg-4" ng-repeat="c in vocabularies.countries | filter:countryFilter">
|
||||
<div class="form-check form-check-inline">
|
||||
|
|
|
@ -380,6 +380,7 @@ orgsModule.config(function($routeProvider) {
|
|||
.when('/duplicates/:country', { templateUrl: 'resources/html/pages/advanced/duplicates.html', controller: 'duplicatesCtrl' })
|
||||
.when('/conflicts/:country', { templateUrl: 'resources/html/pages/advanced/conflicts.html', controller: 'conflictsCtrl' })
|
||||
.when('/users', { templateUrl: 'resources/html/pages/admin/users.html', controller: 'usersCtrl' })
|
||||
.when('/sysconf', { templateUrl: 'resources/html/pages/admin/sysConf.html', controller: 'sysConfCtrl' })
|
||||
.otherwise({ redirectTo: '/search' });
|
||||
});
|
||||
|
||||
|
@ -778,7 +779,21 @@ orgsModule.controller('conflictsCtrl', function ($scope, $http, $routeParams, $l
|
|||
$scope.refresh();
|
||||
});
|
||||
|
||||
|
||||
orgsModule.controller('sysConfCtrl', function ($scope, $http, $timeout, $route) {
|
||||
$scope.sysconf = {};
|
||||
|
||||
call_http_get($http, 'api/sysconf', function(res) { $scope.sysconf = res.data; });
|
||||
|
||||
$scope.saveConf = function() {
|
||||
// I use force_http_post because call_http_post could be disabled if the system is in readonly mode
|
||||
force_http_post($http, 'api/sysconf', $scope.sysconf, function(res) {
|
||||
$scope.sysconf = res.data;
|
||||
alert("Configuration saved !");
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
orgsModule.controller('usersCtrl', function ($scope, $http, $timeout, $route, vocabulariesService) {
|
||||
$scope.users = [];
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<!-- Icons CSS -->
|
||||
<link rel="stylesheet" href="common/css/fontawesome-all.min.css">
|
||||
|
||||
<title>Organizations Database: user already registered</title>
|
||||
<title th:text="${sysconf.title} + ': user already registered'"></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -21,7 +21,7 @@
|
|||
<div class="container">
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<a class="navbar-brand" href="#">
|
||||
<img src="resources/images/openaire_logo_small.png" width="30" height="30" alt="OpenOrgs Database"> OpenOrgs Database
|
||||
<img src="resources/images/openaire_logo_small.png" width="30" height="30" alt="OpenAIRE logo"> <span th:text="${sysconf.title}"></span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<!-- Icons CSS -->
|
||||
<link rel="stylesheet" href="common/css/fontawesome-all.min.css">
|
||||
|
||||
<title>Organizations Database: authorization request</title>
|
||||
<title th:text="${sysconf.title} + ': authorization request'"></title>
|
||||
</head>
|
||||
|
||||
<body ng-app="authReqApp" ng-controller="authReqCtrl">
|
||||
|
@ -21,8 +21,7 @@
|
|||
<div class="container">
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<a class="navbar-brand" href="#">
|
||||
<img src="resources/images/openaire_logo_small.png" width="30" height="30" alt="OpenOrgs Database"> OpenOrgs Database
|
||||
</a>
|
||||
<img src="resources/images/openaire_logo_small.png" width="30" height="30" alt="OpenAIRE logo"> <span th:text="${sysconf.title}"></span> </a>
|
||||
</nav>
|
||||
|
||||
<div ng-if="registrationMessage">
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="common/css/bootstrap.cerulean.min.css" />
|
||||
|
||||
<title>Organizations Database: Login</title>
|
||||
<title th:text="${sysconf.title} + ': login'"></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -23,8 +23,7 @@
|
|||
<div>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<a class="navbar-brand" href="#">
|
||||
<img src="resources/images/openaire_logo_small.png" width="30" height="30" alt="OpenOrgs Database"> OpenOrgs Database
|
||||
</a>
|
||||
<img src="resources/images/openaire_logo_small.png" width="30" height="30" alt="OpenAIRE logo"> <span th:text="${sysconf.title}"></span> </a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
@ -41,6 +40,10 @@
|
|||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active text-center p-5" id="tabLogin">
|
||||
<div th:utext="${sysconf.homepageMessage}"></div>
|
||||
|
||||
<hr th:if="${sysconf.homepageMessage != '' and sysconf.homepageMessage != null}" />
|
||||
|
||||
<a href="./main" class="btn btn-lg btn-primary">Access using OpenAIRE credentials</a>
|
||||
</div>
|
||||
<div class="tab-pane" id="tabRegister">
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="common/css/bootstrap.cerulean.min.css" />
|
||||
|
||||
<title>Organizations Database: Login</title>
|
||||
<title th:text="${sysconf.title} + ': login'"></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -23,8 +23,7 @@
|
|||
<div>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<a class="navbar-brand" href="#">
|
||||
<img src="resources/images/openaire_logo_small.png" width="30" height="30" alt="OpenOrgs Database"> OpenOrgs Database
|
||||
</a>
|
||||
<img src="resources/images/openaire_logo_small.png" width="30" height="30" alt="OpenAIRE logo"> <span th:text="${sysconf.title}"></span> </a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
@ -40,6 +39,10 @@
|
|||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="tabLogin">
|
||||
<div th:utext="${sysconf.homepageMessage}"></div>
|
||||
|
||||
<hr th:if="${sysconf.homepageMessage != '' and sysconf.homepageMessage != null}" />
|
||||
|
||||
<form th:action="@{/login}" method="post">
|
||||
<div th:if="${param.error}">
|
||||
<div class="alert alert-danger">Invalid username and password.</div>
|
||||
|
|
|
@ -45,7 +45,7 @@ fieldset > legend { font-size : 1.2rem !important; }
|
|||
</style>
|
||||
|
||||
|
||||
<title>Organizations Database</title>
|
||||
<title th:text="${sysconf.title}"></title>
|
||||
|
||||
</head>
|
||||
|
||||
|
@ -57,8 +57,8 @@ fieldset > legend { font-size : 1.2rem !important; }
|
|||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary" ng-controller="menuCtrl">
|
||||
<a class="navbar-brand" href="#"> <img
|
||||
src="resources/images/openaire_logo_small.png" width="30" height="30" alt="OpenOrgs Database">
|
||||
OpenOrgs Database
|
||||
src="resources/images/openaire_logo_small.png" width="30" height="30" alt="OpenAIRE logo">
|
||||
<span th:text="${sysconf.title}"></span>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse"
|
||||
data-target="#navbarSupportedContent">
|
||||
|
@ -109,6 +109,8 @@ fieldset > legend { font-size : 1.2rem !important; }
|
|||
<li class="nav-item dropdown" sec:authorize="hasRole('ROLE_OPENORGS_ADMIN') or hasRole('ROLE_OPENORGS_NATIONAL_ADMIN')">
|
||||
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown"><i class="fa fa-cog"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" href="#!/sysconf" sec:authorize="hasRole('ROLE_OPENORGS_ADMIN')">System configuration</a>
|
||||
<div class="dropdown-divider" sec:authorize="hasRole('ROLE_OPENORGS_ADMIN')"></div>
|
||||
<a class="dropdown-item" href="#!/users">Manage users</a>
|
||||
</div>
|
||||
</li>
|
||||
|
@ -137,6 +139,9 @@ fieldset > legend { font-size : 1.2rem !important; }
|
|||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
<div class="alert alert-danger text-center" th:if="${sysconf.readonly}">The portal is running in READ-ONLY mode.</div>
|
||||
|
||||
|
||||
<div class="container-fluid small mt-4" ng-view></div>
|
||||
|
||||
|
@ -192,7 +197,7 @@ fieldset > legend { font-size : 1.2rem !important; }
|
|||
spinnerTarget.style.visibility = 'hidden';
|
||||
spinner.stop();
|
||||
}
|
||||
|
||||
|
||||
function call_http_get($http, url, onSuccess) {
|
||||
showSpinner();
|
||||
|
||||
|
@ -208,7 +213,38 @@ fieldset > legend { font-size : 1.2rem !important; }
|
|||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
||||
});
|
||||
}
|
||||
|
||||
function force_http_post($http, url, obj, onSuccess) {
|
||||
showSpinner();
|
||||
$http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
|
||||
$http.post(url, obj).then(function successCallback(res) {
|
||||
hideSpinner();
|
||||
if ((typeof res.data) == 'string') {
|
||||
alert("Session expired !"); location.reload(true);
|
||||
} else {
|
||||
onSuccess(res);
|
||||
}
|
||||
}, function errorCallback(res) {
|
||||
hideSpinner();
|
||||
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<script th:if="${sysconf.readonly}">
|
||||
function call_http_post($http, url, obj, onSuccess) {
|
||||
alert("Method not permitted in READ-ONLY mode !")
|
||||
}
|
||||
|
||||
function call_http_delete($http, url, onSuccess) {
|
||||
alert("Method not permitted in READ-ONLY mode !")
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script th:if="${!sysconf.readonly}">
|
||||
function call_http_post($http, url, obj, onSuccess) {
|
||||
showSpinner();
|
||||
$http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
|
||||
|
|
Loading…
Reference in New Issue