Project

General

Profile

« Previous | Next » 

Revision 57596

User Registration form

View differences:

modules/dnet-orgs-database-application/trunk/src/main/java/eu/dnetlib/organizations/repository/UserCountryRepository.java
1
package eu.dnetlib.organizations.repository;
2

  
3
import org.springframework.data.jpa.repository.JpaRepository;
4

  
5
import eu.dnetlib.organizations.model.UserCountry;
6
import eu.dnetlib.organizations.model.UserCountryPK;
7

  
8
public interface UserCountryRepository extends JpaRepository<UserCountry, UserCountryPK> {
9

  
10
	void deleteByEmail(String email);
11
}
modules/dnet-orgs-database-application/trunk/src/main/java/eu/dnetlib/organizations/model/UserCountry.java
1
package eu.dnetlib.organizations.model;
2

  
3
import java.io.Serializable;
4

  
5
import javax.persistence.Column;
6
import javax.persistence.Entity;
7
import javax.persistence.Id;
8
import javax.persistence.IdClass;
9
import javax.persistence.Table;
10

  
11
@Entity
12
@Table(name = "user_countries")
13
@IdClass(UserCountryPK.class)
14
public class UserCountry implements Serializable {
15

  
16
	/**
17
	 *
18
	 */
19
	private static final long serialVersionUID = 3493897777214583701L;
20

  
21
	@Id
22
	@Column(name = "email")
23
	private String email;
24

  
25
	@Id
26
	@Column(name = "country")
27
	private String country;
28

  
29
	public UserCountry() {}
30

  
31
	public UserCountry(final String email, final String country) {
32
		this.email = email;
33
		this.country = country;
34
	}
35

  
36
	public String getEmail() {
37
		return email;
38
	}
39

  
40
	public void setEmail(final String email) {
41
		this.email = email;
42
	}
43

  
44
	public String getCountry() {
45
		return country;
46
	}
47

  
48
	public void setCountry(final String country) {
49
		this.country = country;
50
	}
51

  
52
}
modules/dnet-orgs-database-application/trunk/src/main/java/eu/dnetlib/organizations/model/UserCountryPK.java
1
package eu.dnetlib.organizations.model;
2

  
3
import java.io.Serializable;
4
import java.util.Objects;
5

  
6
public class UserCountryPK implements Serializable {
7

  
8
	/**
9
	 *
10
	 */
11
	private static final long serialVersionUID = 2003686130380208901L;
12

  
13
	private String email;
14

  
15
	private String country;
16

  
17
	public String getEmail() {
18
		return email;
19
	}
20

  
21
	public void setEmail(final String email) {
22
		this.email = email;
23
	}
24

  
25
	public String getCountry() {
26
		return country;
27
	}
28

  
29
	public void setCountry(final String country) {
30
		this.country = country;
31
	}
32

  
33
	@Override
34
	public int hashCode() {
35
		return Objects.hash(country, email);
36
	}
37

  
38
	@Override
39
	public boolean equals(final Object obj) {
40
		if (this == obj) { return true; }
41
		if (obj == null) { return false; }
42
		if (!(obj instanceof UserCountryPK)) { return false; }
43
		final UserCountryPK other = (UserCountryPK) obj;
44
		return Objects.equals(country, other.country) && Objects.equals(email, other.email);
45
	}
46

  
47
	@Override
48
	public String toString() {
49
		return String.format("UserCountryPK [email=%s, country=%s]", email, country);
50
	}
51

  
52
}
modules/dnet-orgs-database-application/trunk/src/main/java/eu/dnetlib/organizations/utils/DatabaseUtils.java
9 9
import org.springframework.cache.annotation.Cacheable;
10 10
import org.springframework.jdbc.core.JdbcTemplate;
11 11
import org.springframework.stereotype.Component;
12
import org.springframework.web.bind.annotation.RequestBody;
12 13

  
13 14
import eu.dnetlib.organizations.model.Acronym;
14 15
import eu.dnetlib.organizations.model.Organization;
......
16 17
import eu.dnetlib.organizations.model.OtherName;
17 18
import eu.dnetlib.organizations.model.Relationship;
18 19
import eu.dnetlib.organizations.model.Url;
20
import eu.dnetlib.organizations.model.User;
21
import eu.dnetlib.organizations.model.UserCountry;
19 22
import eu.dnetlib.organizations.model.view.OrganizationView;
23
import eu.dnetlib.organizations.model.view.UserView;
20 24
import eu.dnetlib.organizations.repository.AcronymRepository;
21 25
import eu.dnetlib.organizations.repository.OrganizationRepository;
22 26
import eu.dnetlib.organizations.repository.OtherIdentifierRepository;
23 27
import eu.dnetlib.organizations.repository.OtherNameRepository;
24 28
import eu.dnetlib.organizations.repository.RelationshipRepository;
25 29
import eu.dnetlib.organizations.repository.UrlRepository;
30
import eu.dnetlib.organizations.repository.UserCountryRepository;
31
import eu.dnetlib.organizations.repository.UserRepository;
26 32

  
27 33
@Component
28 34
public class DatabaseUtils {
......
39 45
	private UrlRepository urlRepository;
40 46
	@Autowired
41 47
	private RelationshipRepository relationshipRepository;
48
	@Autowired
49
	private UserRepository userRepository;
50
	@Autowired
51
	private UserCountryRepository userCountryRepository;
42 52

  
43 53
	@Autowired
44 54
	private JdbcTemplate jdbcTemplate;
......
119 129
		return jdbcTemplate.queryForList("select country from user_countries where email = ?", String.class, name);
120 130
	}
121 131

  
132
	@Transactional
133
	public void saveUser(@RequestBody final UserView userView) {
134

  
135
		final User user = userRepository.findById(userView.getEmail()).orElseThrow(() -> new RuntimeException("User not found"));
136
		user.setRole(userView.getRole());
137
		user.setValid(userView.isValid());
138
		userRepository.save(user);
139

  
140
		userCountryRepository.deleteByEmail(userView.getEmail());
141
		for (final String country : userView.getCountries()) {
142
			userCountryRepository.save(new UserCountry(userView.getEmail(), country));
143
		}
144
	}
145

  
146
	@Transactional
147
	public void deleteUser(final String email) {
148
		userCountryRepository.deleteByEmail(email);
149
		userRepository.deleteById(email);
150
	}
151

  
122 152
}
modules/dnet-orgs-database-application/trunk/src/main/java/eu/dnetlib/organizations/controller/UserController.java
5 5

  
6 6
import org.apache.commons.codec.digest.DigestUtils;
7 7
import org.springframework.beans.factory.annotation.Autowired;
8
import org.springframework.security.core.Authentication;
9
import org.springframework.web.bind.annotation.DeleteMapping;
8 10
import org.springframework.web.bind.annotation.GetMapping;
9 11
import org.springframework.web.bind.annotation.PostMapping;
12
import org.springframework.web.bind.annotation.RequestBody;
10 13
import org.springframework.web.bind.annotation.RequestParam;
11 14
import org.springframework.web.bind.annotation.RestController;
12 15

  
......
14 17
import eu.dnetlib.organizations.model.view.UserView;
15 18
import eu.dnetlib.organizations.repository.UserRepository;
16 19
import eu.dnetlib.organizations.repository.readonly.UserViewRepository;
20
import eu.dnetlib.organizations.utils.DatabaseUtils;
17 21
import eu.dnetlib.organizations.utils.OpenOrgsConstants;
18 22

  
19 23
@RestController
......
25 29
	private UserRepository userRepository;
26 30
	@Autowired
27 31
	private UserViewRepository userViewRepository;
32
	@Autowired
33
	private DatabaseUtils dbUtils;
28 34

  
29 35
	@PostMapping(value = "/public_api/newUser")
30 36
	public Map<String, Integer> newUser(final @RequestParam String email) {
......
48 54
		return userViewRepository.findAll();
49 55
	}
50 56

  
57
	@PostMapping("/api/users")
58
	public Iterable<UserView> save(@RequestBody final UserView userView, final Authentication authentication) {
59
		if (authentication.getName().equals(userView.getEmail())) { throw new RuntimeException("You can't edit your own user"); }
60
		dbUtils.saveUser(userView);
61
		return users();
62
	}
63

  
64
	@DeleteMapping("/api/users")
65
	public Iterable<UserView> delete(final @RequestParam String email, final Authentication authentication) {
66
		if (authentication.getName().equals(email)) { throw new RuntimeException("You can't delete your own user"); }
67
		dbUtils.deleteUser(email);
68
		return users();
69
	}
70

  
51 71
}
modules/dnet-orgs-database-application/trunk/src/main/resources/sql/schema.sql
188 188
	u.email,
189 189
	u.valid,
190 190
	u.role,
191
	array_agg(uc.country) AS countries
191
	array_agg(uc.country) FILTER (WHERE uc.country IS NOT NULL) AS countries
192 192
FROM
193 193
	users u
194 194
	LEFT OUTER JOIN user_countries uc ON (u.email = uc.email)
modules/dnet-orgs-database-application/trunk/src/main/resources/static/resources/html/users.html
1 1
<table class="table table-sm table-hover col-sm-12 col-md-10 col-lg-8">
2 2
	<thead class="thead-light">
3 3
		<tr class="d-flex">
4
			<th class="col-4">User</th>
5
			<th class="col-2 text-center">Valid</th>
6
			<th class="col-2 text-center">Role</th>
4
			<th class="col-5">User</th>
5
			<th class="col-1 text-center">Enabled</th>
6
			<th class="col-1 text-center">Is admin</th>
7 7
			<th class="col-3">Countries</th>
8
			<th class="col-1"></th>
8
			<th class="col-2"></th>
9 9
		</tr>
10 10
	</thead>
11 11
	<tbody>
12
		<tr ng-repeat="u in users" class="d-flex">
13
			<td class="col-4">{{u.email}}</td>
14
			<td class="col-2 text-center">{{u.valid}}</td>
15
			<td class="col-2 text-center">{{u.role}}</td>
16
			<td class="col-3">{{u.countries}}</td>
17
			<td class="col-1 text-right"><button type="button" class="btn btn-sm  btn-outline-danger"><i class="fa fa-trash"></i></button></td>
12
		<tr ng-repeat="u in users" class="d-flex" ng-class="{'table-danger' : u.role == 'PENDING'}">
13
			<th class="col-5" ng-class="{'text-secondary': !u.valid}">{{u.email}}</th>
14
			<td class="col-1 text-center text-success">
15
				<i class="fa fa-check-circle" ng-if="u.valid"></i>
16
			</td>
17
			<td class="col-1 text-center">
18
				<i class="fa fa-check-circle" ng-if="u.role == 'SUPERUSER'"></i>
19
				<span class="text-warning" ng-if="u.role == 'PENDING'">not configured</span>
20
			</td>
21
			<td class="col-3">
22
				<span ng-if="u.role == 'USER'">
23
					<img ng-src="resources/images/flags/{{c}}.gif" title="{{c}}" class="mr-1" ng-repeat="c in u.countries" />
24
					<span class="text-warning" ng-if="u.countries.length == 0"><i class="fa fa-exclamation-triangle"></i> no countries</span>
25
				</span>
26
				<span class="text-muted" ng-if="u.role == 'SUPERUSER'">All countries</span>
27
				<span class="text-warning" ng-if="u.role == 'PENDING'">not configured</span>
28
				
29
			</td>
30
			<td class="col-2 text-right">
31
				<a href="mailto:{{u.email}}" class="btn btn-sm  btn-outline-info"><i class="fa fa-at"></i></a>
32
				<button type="button" class="btn btn-sm  btn-outline-info" data-toggle="modal" data-target="#editUserModal" ng-click="setCurrentUser(u)"><i class="fa fa-edit"></i></button>
33
				<button type="button" class="btn btn-sm  btn-outline-danger" ng-click="deleteUser(u.email)"><i class="fa fa-trash"></i></button>
34
			</td>
18 35
		</tr>
19 36
	</tbody>
20 37
</table>
21 38

  
22 39

  
40
<div class="modal fade" id="editUserModal" tabindex="-1" role="dialog">
41
	<div class="modal-dialog modal-xl" role="document">
42
		<div class="modal-content">
43
			<div class="modal-header">
44
				<h5 class="modal-title">Edit user</h5>
45
				<button type="button" class="close" data-dismiss="modal">&times;</button>
46
			</div>
47
			<div class="modal-body" style="min-height: 300px; max-height: 500px; overflow-y: auto;">
48
				<form>
49
					<div class="form-group row">
50
						<label class="col-sm-2 col-form-label">Email</label>
51
						<div class="col-sm-10"><a href="mailto:{{currentUser.email}}">{{currentUser.email}}</a></div>
52
					</div>
53
					<div class="form-group row">
54
						<label class="col-sm-2 col-form-label">Enabled</label>
55
						<div class="col-sm-10">
56
							<div class="form-check">
57
								<input class="form-check-input" type="checkbox" ng-model="currentUser.valid" />
58
							</div>
59
						</div>
60
					</div>
61
					<div class="form-group row" ng-if="currentUser.valid">
62
						<label class="col-sm-2 col-form-label">Role</label>
63
						<div class="col-sm-10">
64
							<div class="form-check form-check-inline">
65
								<input class="form-check-input" type="radio" value="USER" ng-model="currentUser.role">
66
								<label class="form-check-label">USER</label>
67
							</div>
68
							<div class="form-check form-check-inline">
69
								<input class="form-check-input" type="radio" value="SUPERUSER" ng-model="currentUser.role">
70
								<label class="form-check-label">SUPERUSER</label>
71
							</div>
72
						</div>
73
					</div>
74
					
75
					<div class="card mb-3"  ng-if="currentUser.valid && currentUser.role == 'USER'">
76
						<div class="card-header bg-primary text-white py-1">Countries</div>
77
						<div class="card-body">
78
							<div class="form-group row">
79
								<div class="col-sm-2" ng-repeat="c in vocs.countries">
80
									<div class="form-check form-check-inline">
81
										<input class="form-check-input" type="checkbox" checklist-model="currentUser.countries" checklist-value="c"/>
82
										<label class="form-check-label">{{c}}</label>
83
									</div>
84
								</div>
85
							</div>
86
						</div>
87
					</div>
88
					
89
				</form>
90
			</div>
91
			<div class="modal-footer">
92
				<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
93
				<button type="button" class="btn btn-primary" data-dismiss="modal" ng-click="saveUser(currentUser)">Save changes</button>
94
			</div>
95
		</div>
96
	</div>
97
</div>
23 98

  
99

  
100

  
101

  
modules/dnet-orgs-database-application/trunk/src/main/resources/static/resources/js/organizations.js
1
var orgsModule = angular.module('orgs', ['ngRoute']);
1
var orgsModule = angular.module('orgs', ['ngRoute', 'checklist-model']);
2 2

  
3 3
orgsModule.config(function($routeProvider) {
4 4
	$routeProvider
......
268 268

  
269 269

  
270 270

  
271
orgsModule.controller('usersCtrl', function ($scope, $http) {
271
orgsModule.controller('usersCtrl', function ($scope, $http, $timeout, $route) {
272 272
	$scope.users = [];
273
	$scope.vocabularies = {};
273
	$scope.vocs = {};
274
	$scope.currentUser = {};
274 275
	
275 276
	$http.get('api/vocabularies').then(function successCallback(res) {
276
		$scope.vocabularies = res.data;
277
		$scope.vocs = res.data;
277 278

  
278 279
		$http.get('api/users').then(function successCallback(res) {
279 280
			$scope.users = res.data;
......
284 285
		alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
285 286
	});
286 287
	
288
	$scope.setCurrentUser = function(user) {
289
		angular.copy(user, $scope.currentUser);
290
	}
291
	
292
	$scope.saveUser = function(user) {
293
		$http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
294
		$http.post('api/users', user).then(function successCallback(res) {
295
			$scope.users = res.data;
296
		}, function errorCallback(res) {
297
			alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
298
		});
299
	}
300
	
301
	$scope.deleteUser = function(email) {
302
		if (confirm("Are you sure ?")) {
303
			$http.delete('api/users?email=' + email).then(function successCallback(res) {
304
				$scope.users = res.data;
305
			}, function errorCallback(res) {
306
				alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
307
			});
308
		}
309
	}
287 310
});
modules/dnet-orgs-database-application/trunk/src/main/resources/static/resources/js/checklist-model.js
1
/**
2
 * Checklist-model
3
 * AngularJS directive for list of checkboxes
4
 * https://github.com/vitalets/checklist-model
5
 * License: MIT http://opensource.org/licenses/MIT
6
 */
7

  
8
 /* commonjs package manager support (eg componentjs) */
9
 if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
10
   module.exports = 'checklist-model';
11
 }
12

  
13
angular.module('checklist-model', [])
14
.directive('checklistModel', ['$parse', '$compile', function($parse, $compile) {
15
  // contains
16
  function contains(arr, item, comparator) {
17
    if (angular.isArray(arr)) {
18
      for (var i = arr.length; i--;) {
19
        if (comparator(arr[i], item)) {
20
          return true;
21
        }
22
      }
23
    }
24
    return false;
25
  }
26

  
27
  // add
28
  function add(arr, item, comparator) {
29
    arr = angular.isArray(arr) ? arr : [];
30
      if(!contains(arr, item, comparator)) {
31
          arr.push(item);
32
      }
33
    return arr;
34
  }
35

  
36
  // remove
37
  function remove(arr, item, comparator) {
38
    if (angular.isArray(arr)) {
39
      for (var i = arr.length; i--;) {
40
        if (comparator(arr[i], item)) {
41
          arr.splice(i, 1);
42
          break;
43
        }
44
      }
45
    }
46
    return arr;
47
  }
48

  
49
  // http://stackoverflow.com/a/19228302/1458162
50
  function postLinkFn(scope, elem, attrs) {
51
     // exclude recursion, but still keep the model
52
    var checklistModel = attrs.checklistModel;
53
    attrs.$set("checklistModel", null);
54
    // compile with `ng-model` pointing to `checked`
55
    $compile(elem)(scope);
56
    attrs.$set("checklistModel", checklistModel);
57

  
58
    // getter for original model
59
    var checklistModelGetter = $parse(checklistModel);
60
    var checklistChange = $parse(attrs.checklistChange);
61
    var checklistBeforeChange = $parse(attrs.checklistBeforeChange);
62
    var ngModelGetter = $parse(attrs.ngModel);
63

  
64

  
65

  
66
    var comparator = function (a, b) {
67
      if(!isNaN(a) && !isNaN(b)) {
68
        return String(a) === String(b);
69
      } else {
70
        return angular.equals(a,b);
71
      }
72
    };
73

  
74
    if (attrs.hasOwnProperty('checklistComparator')){
75
      if (attrs.checklistComparator[0] == '.') {
76
        var comparatorExpression = attrs.checklistComparator.substring(1);
77
        comparator = function (a, b) {
78
          return a[comparatorExpression] === b[comparatorExpression];
79
        };
80

  
81
      } else {
82
        comparator = $parse(attrs.checklistComparator)(scope.$parent);
83
      }
84
    }
85

  
86
    // watch UI checked change
87
    var unbindModel = scope.$watch(attrs.ngModel, function(newValue, oldValue) {
88
      if (newValue === oldValue) {
89
        return;
90
      }
91

  
92
      if (checklistBeforeChange && (checklistBeforeChange(scope) === false)) {
93
        ngModelGetter.assign(scope, contains(checklistModelGetter(scope.$parent), getChecklistValue(), comparator));
94
        return;
95
      }
96

  
97
      setValueInChecklistModel(getChecklistValue(), newValue);
98

  
99
      if (checklistChange) {
100
        checklistChange(scope);
101
      }
102
    });
103

  
104
    // watches for value change of checklistValue
105
    var unbindCheckListValue = scope.$watch(getChecklistValue, function(newValue, oldValue) {
106
      if( newValue != oldValue && angular.isDefined(oldValue) && scope[attrs.ngModel] === true ) {
107
        var current = checklistModelGetter(scope.$parent);
108
        checklistModelGetter.assign(scope.$parent, remove(current, oldValue, comparator));
109
        checklistModelGetter.assign(scope.$parent, add(current, newValue, comparator));
110
      }
111
    }, true);
112

  
113
    var unbindDestroy = scope.$on('$destroy', destroy);
114

  
115
    function destroy() {
116
      unbindModel();
117
      unbindCheckListValue();
118
      unbindDestroy();
119
    }
120

  
121
    function getChecklistValue() {
122
      return attrs.checklistValue ? $parse(attrs.checklistValue)(scope.$parent) : attrs.value;
123
    }
124

  
125
    function setValueInChecklistModel(value, checked) {
126
      var current = checklistModelGetter(scope.$parent);
127
      if (angular.isFunction(checklistModelGetter.assign)) {
128
        if (checked === true) {
129
          checklistModelGetter.assign(scope.$parent, add(current, value, comparator));
130
        } else {
131
          checklistModelGetter.assign(scope.$parent, remove(current, value, comparator));
132
        }
133
      }
134

  
135
    }
136

  
137
    // declare one function to be used for both $watch functions
138
    function setChecked(newArr, oldArr) {
139
      if (checklistBeforeChange && (checklistBeforeChange(scope) === false)) {
140
        setValueInChecklistModel(getChecklistValue(), ngModelGetter(scope));
141
        return;
142
      }
143
      ngModelGetter.assign(scope, contains(newArr, getChecklistValue(), comparator));
144
    }
145

  
146
    // watch original model change
147
    // use the faster $watchCollection method if it's available
148
    if (angular.isFunction(scope.$parent.$watchCollection)) {
149
        scope.$parent.$watchCollection(checklistModel, setChecked);
150
    } else {
151
        scope.$parent.$watch(checklistModel, setChecked, true);
152
    }
153
  }
154

  
155
  return {
156
    restrict: 'A',
157
    priority: 1000,
158
    terminal: true,
159
    scope: true,
160
    compile: function(tElement, tAttrs) {
161

  
162
      if (!tAttrs.checklistValue && !tAttrs.value) {
163
        throw 'You should provide `value` or `checklist-value`.';
164
      }
165

  
166
      // by default ngModel is 'checked', so we set it if not specified
167
      if (!tAttrs.ngModel) {
168
        // local scope var storing individual checkbox model
169
        tAttrs.$set("ngModel", "checked");
170
      }
171

  
172
      return postLinkFn;
173
    }
174
  };
175
}]);
modules/dnet-orgs-database-application/trunk/src/main/resources/templates/home.html
25 25
fieldset > legend {
26 26
	font-size: 1.2rem !important;  
27 27
}
28

  
29 28
</style>
30 29

  
31 30

  
......
90 89
	<script src="resources/js/bootstrap.min.js"></script>
91 90
	<script src="resources/js/angular.min.js"></script>
92 91
	<script src="resources/js/angular-route.min.js"></script>
92
	<script src='resources/js/checklist-model.js'></script>
93 93
	<script src="resources/js/organizations.js"></script>
94 94
</body>
95 95

  

Also available in: Unified diff